Project import generated by Copybara.

GitOrigin-RevId: bcecee014ba3ad28befeacbb53623051ffc6d209
Change-Id: I2fa3967852cdbf2618afa0a4ee0a6e4b00dcd53c
diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 0000000..1522857
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1 @@
+build --action_env=BAZEL_CXXOPTS=-std=c++20
\ No newline at end of file
diff --git a/.dockerignore b/.dockerignore
index d8f642c..bc549d9 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,5 +1,7 @@
 **
 
+!common/
+common/target/
 !nearby/.cargo
 !nearby/connections/
 !nearby/crypto/
@@ -12,3 +14,4 @@
 !nearby/deny.toml
 !nearby/rustfmt.toml
 !third_party
+third_party/boringssl/build
diff --git a/.gitignore b/.gitignore
index c1baf38..8a38822 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@
 nearby/connections/ukey2/ukey2_c_ffi/cpp/build/
 nearby/presence/ldt_np_jni/java/LdtNpJni/build/
 **/auth_token.txt
+/bazel-*
diff --git a/.gitmodules b/.gitmodules
index ed737f5..f3a15fb 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,6 +7,7 @@
 [submodule "third_party/boringssl"]
 	path = third_party/boringssl
 	url = https://boringssl.googlesource.com/boringssl
+	branch = master
 [submodule "third_party/fuzztest"]
 	path = third_party/fuzztest
 	url = https://github.com/google/fuzztest.git
diff --git a/BUILD b/BUILD
new file mode 100644
index 0000000..eb6e7aa
--- /dev/null
+++ b/BUILD
@@ -0,0 +1,297 @@
+# 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
+#
+#     https://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.
+
+load("@crate_index//:defs.bzl", "aliases")
+load("@rules_rust//rust:defs.bzl", "rust_library")
+
+cc_binary(
+    name = "np_cpp_sample",
+    srcs = ["nearby/presence/np_cpp_ffi/sample/main.cc"],
+    copts = [
+        "-Inearby/presence/np_c_ffi/include/cpp",
+        "-Inearby/presence/np_cpp_ffi/include",
+    ],
+    deps = [
+        ":np_c_ffi_types",
+        ":np_cpp_ffi",
+        "@absl//absl/strings",
+    ],
+)
+
+cc_library(
+    name = "np_cpp_ffi",
+    srcs = ["nearby/presence/np_cpp_ffi/nearby_protocol.cc"],
+    hdrs = ["nearby/presence/np_cpp_ffi/include/nearby_protocol.h"],
+    copts = [
+        "-Inearby/presence/np_cpp_ffi/include",
+        "-Inearby/presence/np_c_ffi/include/cpp",
+    ],
+    deps = [
+        ":np_c_ffi",
+        "@absl//absl/status",
+        "@absl//absl/status:statusor",
+        "@absl//absl/strings",
+        "@absl//absl/strings:str_format",
+    ],
+)
+
+cc_library(
+    name = "np_c_ffi",
+    hdrs = [
+        "nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h",
+        "nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_types.h",
+    ],
+    deps = [":np_c_ffi_rust"],
+)
+
+cc_library(
+    name = "np_c_ffi_types",
+    hdrs = ["nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_types.h"],
+)
+
+rust_library(
+    name = "np_c_ffi_rust",
+    srcs = glob(include = ["nearby/presence/np_c_ffi/src/**/*.rs"]),
+    crate_features = ["std"],
+    deps = [
+        ":lock_adapter",
+        ":np_ffi_core",
+    ],
+)
+
+rust_library(
+    name = "np_ffi_core",
+    srcs = glob(include = ["nearby/presence/np_ffi_core/src/**/*.rs"]),
+    crate_features = ["crypto_provider/raw_private_key_permit"],
+    proc_macro_deps = ["@crate_index//:strum_macros"],
+    deps = [
+        ":array_view",
+        ":crypto_provider",
+        ":crypto_provider_default",
+        ":handle_map",
+        ":ldt_np_adv",
+        ":lock_adapter",
+        ":np_adv",
+        ":np_adv_dynamic",
+        ":np_hkdf",
+        "@crate_index//:lazy_static",
+        "@crate_index//:strum",
+    ],
+)
+
+rust_library(
+    name = "np_adv_dynamic",
+    srcs = glob(include = ["nearby/presence/np_adv_dynamic/src/**/*.rs"]),
+    deps = [
+        ":array_view",
+        ":crypto_provider",
+        ":np_adv",
+        ":sink",
+        "@crate_index//:thiserror",
+    ],
+)
+
+rust_library(
+    name = "handle_map",
+    srcs = glob(include = ["common/handle_map/src/**/*.rs"]),
+    deps = [":lock_adapter"],
+)
+
+rust_library(
+    name = "lock_adapter",
+    srcs = glob(include = ["common/lock_adapter/src/**/*.rs"]),
+    crate_features = ["std"],
+)
+
+rust_library(
+    name = "np_adv",
+    srcs = glob(include = ["nearby/presence/np_adv/src/**/*.rs"]),
+    crate_features = ["alloc"],
+    proc_macro_deps = ["@crate_index//:strum_macros"],
+    deps = [
+        ":array_view",
+        ":crypto_provider",
+        ":ldt",
+        ":ldt_np_adv",
+        ":np_ed25519",
+        ":np_hkdf",
+        ":sink",
+        ":xts_aes",
+        "@crate_index//:lazy_static",
+        "@crate_index//:nom",
+        "@crate_index//:strum",
+        "@crate_index//:tinyvec",
+    ],
+)
+
+rust_library(
+    name = "np_ed25519",
+    srcs = glob(
+        include = ["nearby/presence/np_ed25519/src/**/*.rs"],
+    ),
+    deps = [
+        ":array_view",
+        ":crypto_provider",
+        ":ldt",
+        ":sink",
+        ":xts_aes",
+        "@crate_index//:tinyvec",
+    ],
+)
+
+cc_binary(
+    name = "main",
+    srcs = ["nearby/presence/ldt_np_adv_ffi/c/sample/main.c"],
+    deps = [":ldt_np_adv_ffi"],
+)
+
+cc_library(
+    name = "ldt_np_adv_ffi",
+    hdrs = ["nearby/presence/ldt_np_adv_ffi/c/include/np_ldt.h"],
+    includes = ["nearby/presence/ldt_np_adv_ffi/c/include"],
+    deps = [":ldt_np_adv_ffi_rust"],
+)
+
+rust_library(
+    name = "ldt_np_adv_ffi_rust",
+    srcs = glob(["nearby/presence/ldt_np_adv_ffi/src/**/*.rs"]),
+    crate_features = ["std"],
+    deps = [
+        ":crypto_provider",
+        ":crypto_provider_default",
+        ":ldt",
+        ":ldt_np_adv",
+        ":np_hkdf",
+        "@crate_index//:cfg-if",
+        "@crate_index//:lazy_static",
+    ],
+)
+
+rust_library(
+    name = "ldt_np_adv",
+    srcs = glob(["nearby/presence/ldt_np_adv/src/**/*.rs"]),
+    deps = [
+        ":array_view",
+        ":crypto_provider",
+        ":ldt",
+        ":np_hkdf",
+        ":xts_aes",
+    ],
+)
+
+rust_library(
+    name = "np_hkdf",
+    srcs = glob(["nearby/presence/np_hkdf/src/**/*.rs"]),
+    deps = [
+        ":crypto_provider",
+        ":ldt",
+        ":xts_aes",
+    ],
+)
+
+rust_library(
+    name = "crypto_provider",
+    srcs = glob(["nearby/crypto/crypto_provider/src/**/*.rs"]),
+    crate_features = [
+        "raw_private_key_permit",
+        "std",
+        "alloc",
+    ],
+    deps = ["@crate_index//:tinyvec"],
+)
+
+rust_library(
+    name = "crypto_provider_default",
+    srcs = glob(["nearby/crypto/crypto_provider_default/src/**/*.rs"]),
+    crate_features = [
+        "rustcrypto",
+        "std",
+    ],
+    deps = [
+        ":crypto_provider",
+        ":crypto_provider_rustcrypto",
+        "@crate_index//:cfg-if",
+    ],
+)
+
+rust_library(
+    name = "crypto_provider_rustcrypto",
+    srcs = glob(["nearby/crypto/crypto_provider_rustcrypto/src/**/*.rs"]),
+    crate_features = [
+        "std",
+        "alloc",
+    ],
+    deps = [
+        ":crypto_provider",
+        "@crate_index//:aead",
+        "@crate_index//:aes",
+        "@crate_index//:aes-gcm",
+        "@crate_index//:aes-gcm-siv",
+        "@crate_index//:cbc",
+        "@crate_index//:cfg-if",
+        "@crate_index//:ctr",
+        "@crate_index//:ed25519-dalek",
+        "@crate_index//:hkdf",
+        "@crate_index//:hmac",
+        "@crate_index//:p256",
+        "@crate_index//:rand",
+        "@crate_index//:rand_chacha",
+        "@crate_index//:rand_core",
+        "@crate_index//:sec1",
+        "@crate_index//:sha2",
+        "@crate_index//:subtle",
+        "@crate_index//:x25519-dalek",
+    ],
+)
+
+rust_library(
+    name = "ldt_tbc",
+    srcs = glob(["nearby/presence/ldt_tbc/src/**/*.rs"]),
+    deps = [":crypto_provider"],
+)
+
+rust_library(
+    name = "ldt",
+    srcs = glob(["nearby/presence/ldt/src/**/*.rs"]),
+    deps = [
+        ":crypto_provider",
+        ":ldt_tbc",
+    ],
+)
+
+rust_library(
+    name = "xts_aes",
+    srcs = glob(["nearby/presence/xts_aes/src/**/*.rs"]),
+    deps = [
+        ":array_ref",
+        ":crypto_provider",
+        ":ldt_tbc",
+    ],
+)
+
+rust_library(
+    name = "array_view",
+    srcs = glob(["nearby/presence/array_view/src/**/*.rs"]),
+)
+
+rust_library(
+    name = "array_ref",
+    srcs = glob(["nearby/presence/array_ref/src/**/*.rs"]),
+)
+
+rust_library(
+    name = "sink",
+    srcs = glob(["nearby/presence/sink/src/**/*.rs"]),
+    deps = ["@crate_index//:tinyvec"],
+)
diff --git a/Dockerfile b/Dockerfile
index 1f0462e..9122d14 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,31 +12,40 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM ubuntu:22.10
+FROM ubuntu:22.04
 
 # install system deps
-RUN apt-get update && apt-get install -y build-essential cmake gcc wget vim \
-clang git checkinstall zlib1g-dev libjsoncpp-dev libbenchmark-dev curl \
-protobuf-compiler pkg-config libdbus-1-dev libssl-dev ninja-build
+RUN apt-get update && apt-get install -y build-essential wget vim libstdc++-10-dev \
+clang git checkinstall zlib1g-dev curl protobuf-compiler cmake \
+pkg-config libdbus-1-dev ninja-build libssl-dev default-jre
 RUN apt upgrade -y
 
+RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
+RUN apt-get -y install ./google-chrome-stable_current_amd64.deb
+RUN export CHROME_BIN="/usr/bin/google-chrome-stable"
+
+ENV CC=/usr/bin/clang
+ENV CXX=/usr/bin/clang++
+
 # install cargo with default settings
-RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.72.0
+RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.77.1
 ENV PATH="/root/.cargo/bin:${PATH}"
+RUN rustup install stable
+RUN rustup toolchain install nightly --force
+RUN rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
+RUN rustup target add wasm32-unknown-unknown thumbv7m-none-eabi
+
 RUN cargo install --locked cargo-deny --color never 2>&1
-RUN cargo install cargo-fuzz --color never 2>&1
+RUN cargo install --locked cargo-llvm-cov --color never 2>&1
+RUN cargo install bindgen-cli --version 0.69.4 --color never 2>&1
+RUN cargo install wasm-pack --color never 2>&1
+
 # unreleased PR https://github.com/ehuss/cargo-prefetch/pull/6
 RUN cargo install cargo-prefetch \
     --git https://github.com/marshallpierce/cargo-prefetch.git \
     --rev f6affa68e950275f9fd773f2646ab7ee4db82897 \
     --color never 2>&1
-# needed for generating boringssl bindings
-# Must use 0.64.0, as version >= 0.65.0 removes the option "--size_t-is-usize", an option
-# used by boringssl when generating rust bindings
-RUN cargo install bindgen-cli --version 0.64.0
-RUN cargo install wasm-pack --color never 2>&1
-RUN rustup toolchain add nightly
-RUN rustup target add wasm32-unknown-unknown
+
 # boringssl build wants go
 RUN curl -L https://go.dev/dl/go1.20.2.linux-amd64.tar.gz | tar -C /usr/local -xz
 ENV PATH="$PATH:/usr/local/go/bin"
@@ -45,13 +54,20 @@
 RUN git config --global user.email "docker@example.com"
 RUN git config --global user.name "NP Docker"
 
-RUN mkdir -p /google
-COPY . /google
+# Download google-java-format
+RUN mkdir /opt/google-java-format
+WORKDIR /opt/google-java-format
+ENV GOOGLE_JAVA_FORMAT_VERSION="1.19.2"
+RUN wget "https://github.com/google/google-java-format/releases/download/v${GOOGLE_JAVA_FORMAT_VERSION}/google-java-format-${GOOGLE_JAVA_FORMAT_VERSION}-all-deps.jar" -q -O "google-java-format-all-deps.jar"
 
-WORKDIR /google/nearby
+RUN mkdir -p /beto-core
+COPY . /beto-core
+WORKDIR /beto-core
 
 # prefetch dependencies so later build steps don't re-download on source changes
-RUN cargo prefetch --lockfile Cargo.lock
+RUN cargo prefetch --lockfile nearby/Cargo.lock 2>&1
+RUN cargo prefetch --lockfile common/Cargo.lock 2>&1
 
 # when the image runs build and test everything to ensure env is setup correctly
+WORKDIR /beto-core/nearby
 CMD ["cargo", "run", "--", "check-everything"]
diff --git a/MODULE.bazel b/MODULE.bazel
new file mode 100644
index 0000000..1a49310
--- /dev/null
+++ b/MODULE.bazel
@@ -0,0 +1,6 @@
+###############################################################################
+# Bazel now uses Bzlmod by default to manage external dependencies.
+# Please consider migrating your external dependencies from WORKSPACE to MODULE.bazel.
+#
+# For more details, please check https://github.com/bazelbuild/bazel/issues/18958
+###############################################################################
\ No newline at end of file
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock
new file mode 100644
index 0000000..7490be4
--- /dev/null
+++ b/MODULE.bazel.lock
@@ -0,0 +1,1632 @@
+{
+  "lockFileVersion": 6,
+  "moduleFileHash": "1bed0cff67b36277151f008b059508a5316fa37a0baa62d447f3e14a517b8e77",
+  "flags": {
+    "cmdRegistries": [
+      "https://bcr.bazel.build/"
+    ],
+    "cmdModuleOverrides": {},
+    "allowedYankedVersions": [],
+    "envVarAllowedYankedVersions": "",
+    "ignoreDevDependency": false,
+    "directDependenciesMode": "WARNING",
+    "compatibilityMode": "ERROR"
+  },
+  "localOverrideHashes": {
+    "bazel_tools": "1ae69322ac3823527337acf02016e8ee95813d8d356f47060255b8956fa642f0"
+  },
+  "moduleDepGraph": {
+    "<root>": {
+      "name": "",
+      "version": "",
+      "key": "<root>",
+      "repoName": "",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [],
+      "deps": {
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      }
+    },
+    "bazel_tools@_": {
+      "name": "bazel_tools",
+      "version": "",
+      "key": "bazel_tools@_",
+      "repoName": "bazel_tools",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [
+        "@local_config_cc_toolchains//:all",
+        "@local_config_sh//:local_sh_toolchain"
+      ],
+      "extensionUsages": [
+        {
+          "extensionBzlFile": "@bazel_tools//tools/cpp:cc_configure.bzl",
+          "extensionName": "cc_configure_extension",
+          "usingModule": "bazel_tools@_",
+          "location": {
+            "file": "@@bazel_tools//:MODULE.bazel",
+            "line": 18,
+            "column": 29
+          },
+          "imports": {
+            "local_config_cc": "local_config_cc",
+            "local_config_cc_toolchains": "local_config_cc_toolchains"
+          },
+          "devImports": [],
+          "tags": [],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        },
+        {
+          "extensionBzlFile": "@bazel_tools//tools/osx:xcode_configure.bzl",
+          "extensionName": "xcode_configure_extension",
+          "usingModule": "bazel_tools@_",
+          "location": {
+            "file": "@@bazel_tools//:MODULE.bazel",
+            "line": 22,
+            "column": 32
+          },
+          "imports": {
+            "local_config_xcode": "local_config_xcode"
+          },
+          "devImports": [],
+          "tags": [],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        },
+        {
+          "extensionBzlFile": "@rules_java//java:extensions.bzl",
+          "extensionName": "toolchains",
+          "usingModule": "bazel_tools@_",
+          "location": {
+            "file": "@@bazel_tools//:MODULE.bazel",
+            "line": 25,
+            "column": 32
+          },
+          "imports": {
+            "local_jdk": "local_jdk",
+            "remote_java_tools": "remote_java_tools",
+            "remote_java_tools_linux": "remote_java_tools_linux",
+            "remote_java_tools_windows": "remote_java_tools_windows",
+            "remote_java_tools_darwin_x86_64": "remote_java_tools_darwin_x86_64",
+            "remote_java_tools_darwin_arm64": "remote_java_tools_darwin_arm64"
+          },
+          "devImports": [],
+          "tags": [],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        },
+        {
+          "extensionBzlFile": "@bazel_tools//tools/sh:sh_configure.bzl",
+          "extensionName": "sh_configure_extension",
+          "usingModule": "bazel_tools@_",
+          "location": {
+            "file": "@@bazel_tools//:MODULE.bazel",
+            "line": 36,
+            "column": 39
+          },
+          "imports": {
+            "local_config_sh": "local_config_sh"
+          },
+          "devImports": [],
+          "tags": [],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        },
+        {
+          "extensionBzlFile": "@bazel_tools//tools/test:extensions.bzl",
+          "extensionName": "remote_coverage_tools_extension",
+          "usingModule": "bazel_tools@_",
+          "location": {
+            "file": "@@bazel_tools//:MODULE.bazel",
+            "line": 40,
+            "column": 48
+          },
+          "imports": {
+            "remote_coverage_tools": "remote_coverage_tools"
+          },
+          "devImports": [],
+          "tags": [],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        },
+        {
+          "extensionBzlFile": "@bazel_tools//tools/android:android_extensions.bzl",
+          "extensionName": "remote_android_tools_extensions",
+          "usingModule": "bazel_tools@_",
+          "location": {
+            "file": "@@bazel_tools//:MODULE.bazel",
+            "line": 43,
+            "column": 42
+          },
+          "imports": {
+            "android_gmaven_r8": "android_gmaven_r8",
+            "android_tools": "android_tools"
+          },
+          "devImports": [],
+          "tags": [],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        },
+        {
+          "extensionBzlFile": "@buildozer//:buildozer_binary.bzl",
+          "extensionName": "buildozer_binary",
+          "usingModule": "bazel_tools@_",
+          "location": {
+            "file": "@@bazel_tools//:MODULE.bazel",
+            "line": 47,
+            "column": 33
+          },
+          "imports": {
+            "buildozer_binary": "buildozer_binary"
+          },
+          "devImports": [],
+          "tags": [],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        }
+      ],
+      "deps": {
+        "rules_cc": "rules_cc@0.0.9",
+        "rules_java": "rules_java@7.4.0",
+        "rules_license": "rules_license@0.0.7",
+        "rules_proto": "rules_proto@5.3.0-21.7",
+        "rules_python": "rules_python@0.22.1",
+        "buildozer": "buildozer@6.4.0.2",
+        "platforms": "platforms@0.0.7",
+        "com_google_protobuf": "protobuf@21.7",
+        "zlib": "zlib@1.3",
+        "build_bazel_apple_support": "apple_support@1.5.0",
+        "local_config_platform": "local_config_platform@_"
+      }
+    },
+    "local_config_platform@_": {
+      "name": "local_config_platform",
+      "version": "",
+      "key": "local_config_platform@_",
+      "repoName": "local_config_platform",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [],
+      "deps": {
+        "platforms": "platforms@0.0.7",
+        "bazel_tools": "bazel_tools@_"
+      }
+    },
+    "rules_cc@0.0.9": {
+      "name": "rules_cc",
+      "version": "0.0.9",
+      "key": "rules_cc@0.0.9",
+      "repoName": "rules_cc",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [
+        "@local_config_cc_toolchains//:all"
+      ],
+      "extensionUsages": [
+        {
+          "extensionBzlFile": "@bazel_tools//tools/cpp:cc_configure.bzl",
+          "extensionName": "cc_configure_extension",
+          "usingModule": "rules_cc@0.0.9",
+          "location": {
+            "file": "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel",
+            "line": 9,
+            "column": 29
+          },
+          "imports": {
+            "local_config_cc_toolchains": "local_config_cc_toolchains"
+          },
+          "devImports": [],
+          "tags": [],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        }
+      ],
+      "deps": {
+        "platforms": "platforms@0.0.7",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/bazelbuild/rules_cc/releases/download/0.0.9/rules_cc-0.0.9.tar.gz"
+          ],
+          "integrity": "sha256-IDeHW5pEVtzkp50RKorohbvEqtlo5lh9ym5k86CQDN8=",
+          "strip_prefix": "rules_cc-0.0.9",
+          "remote_patches": {
+            "https://bcr.bazel.build/modules/rules_cc/0.0.9/patches/module_dot_bazel_version.patch": "sha256-mM+qzOI0SgAdaJBlWOSMwMPKpaA9b7R37Hj/tp5bb4g="
+          },
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "rules_java@7.4.0": {
+      "name": "rules_java",
+      "version": "7.4.0",
+      "key": "rules_java@7.4.0",
+      "repoName": "rules_java",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [
+        "//toolchains:all",
+        "@local_jdk//:runtime_toolchain_definition",
+        "@local_jdk//:bootstrap_runtime_toolchain_definition",
+        "@remotejdk11_linux_toolchain_config_repo//:all",
+        "@remotejdk11_linux_aarch64_toolchain_config_repo//:all",
+        "@remotejdk11_linux_ppc64le_toolchain_config_repo//:all",
+        "@remotejdk11_linux_s390x_toolchain_config_repo//:all",
+        "@remotejdk11_macos_toolchain_config_repo//:all",
+        "@remotejdk11_macos_aarch64_toolchain_config_repo//:all",
+        "@remotejdk11_win_toolchain_config_repo//:all",
+        "@remotejdk11_win_arm64_toolchain_config_repo//:all",
+        "@remotejdk17_linux_toolchain_config_repo//:all",
+        "@remotejdk17_linux_aarch64_toolchain_config_repo//:all",
+        "@remotejdk17_linux_ppc64le_toolchain_config_repo//:all",
+        "@remotejdk17_linux_s390x_toolchain_config_repo//:all",
+        "@remotejdk17_macos_toolchain_config_repo//:all",
+        "@remotejdk17_macos_aarch64_toolchain_config_repo//:all",
+        "@remotejdk17_win_toolchain_config_repo//:all",
+        "@remotejdk17_win_arm64_toolchain_config_repo//:all",
+        "@remotejdk21_linux_toolchain_config_repo//:all",
+        "@remotejdk21_linux_aarch64_toolchain_config_repo//:all",
+        "@remotejdk21_macos_toolchain_config_repo//:all",
+        "@remotejdk21_macos_aarch64_toolchain_config_repo//:all",
+        "@remotejdk21_win_toolchain_config_repo//:all"
+      ],
+      "extensionUsages": [
+        {
+          "extensionBzlFile": "@rules_java//java:extensions.bzl",
+          "extensionName": "toolchains",
+          "usingModule": "rules_java@7.4.0",
+          "location": {
+            "file": "https://bcr.bazel.build/modules/rules_java/7.4.0/MODULE.bazel",
+            "line": 19,
+            "column": 27
+          },
+          "imports": {
+            "remote_java_tools": "remote_java_tools",
+            "remote_java_tools_linux": "remote_java_tools_linux",
+            "remote_java_tools_windows": "remote_java_tools_windows",
+            "remote_java_tools_darwin_x86_64": "remote_java_tools_darwin_x86_64",
+            "remote_java_tools_darwin_arm64": "remote_java_tools_darwin_arm64",
+            "local_jdk": "local_jdk",
+            "remotejdk11_linux_toolchain_config_repo": "remotejdk11_linux_toolchain_config_repo",
+            "remotejdk11_linux_aarch64_toolchain_config_repo": "remotejdk11_linux_aarch64_toolchain_config_repo",
+            "remotejdk11_linux_ppc64le_toolchain_config_repo": "remotejdk11_linux_ppc64le_toolchain_config_repo",
+            "remotejdk11_linux_s390x_toolchain_config_repo": "remotejdk11_linux_s390x_toolchain_config_repo",
+            "remotejdk11_macos_toolchain_config_repo": "remotejdk11_macos_toolchain_config_repo",
+            "remotejdk11_macos_aarch64_toolchain_config_repo": "remotejdk11_macos_aarch64_toolchain_config_repo",
+            "remotejdk11_win_toolchain_config_repo": "remotejdk11_win_toolchain_config_repo",
+            "remotejdk11_win_arm64_toolchain_config_repo": "remotejdk11_win_arm64_toolchain_config_repo",
+            "remotejdk17_linux_toolchain_config_repo": "remotejdk17_linux_toolchain_config_repo",
+            "remotejdk17_linux_aarch64_toolchain_config_repo": "remotejdk17_linux_aarch64_toolchain_config_repo",
+            "remotejdk17_linux_ppc64le_toolchain_config_repo": "remotejdk17_linux_ppc64le_toolchain_config_repo",
+            "remotejdk17_linux_s390x_toolchain_config_repo": "remotejdk17_linux_s390x_toolchain_config_repo",
+            "remotejdk17_macos_toolchain_config_repo": "remotejdk17_macos_toolchain_config_repo",
+            "remotejdk17_macos_aarch64_toolchain_config_repo": "remotejdk17_macos_aarch64_toolchain_config_repo",
+            "remotejdk17_win_toolchain_config_repo": "remotejdk17_win_toolchain_config_repo",
+            "remotejdk17_win_arm64_toolchain_config_repo": "remotejdk17_win_arm64_toolchain_config_repo",
+            "remotejdk21_linux_toolchain_config_repo": "remotejdk21_linux_toolchain_config_repo",
+            "remotejdk21_linux_aarch64_toolchain_config_repo": "remotejdk21_linux_aarch64_toolchain_config_repo",
+            "remotejdk21_macos_toolchain_config_repo": "remotejdk21_macos_toolchain_config_repo",
+            "remotejdk21_macos_aarch64_toolchain_config_repo": "remotejdk21_macos_aarch64_toolchain_config_repo",
+            "remotejdk21_win_toolchain_config_repo": "remotejdk21_win_toolchain_config_repo"
+          },
+          "devImports": [],
+          "tags": [],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        }
+      ],
+      "deps": {
+        "platforms": "platforms@0.0.7",
+        "rules_cc": "rules_cc@0.0.9",
+        "bazel_skylib": "bazel_skylib@1.3.0",
+        "rules_proto": "rules_proto@5.3.0-21.7",
+        "rules_license": "rules_license@0.0.7",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/bazelbuild/rules_java/releases/download/7.4.0/rules_java-7.4.0.tar.gz"
+          ],
+          "integrity": "sha256-l27wi0nJKXQfIBeQ5Z44B8cq2B9CjIvJU82+/1/tFes=",
+          "strip_prefix": "",
+          "remote_patches": {},
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "rules_license@0.0.7": {
+      "name": "rules_license",
+      "version": "0.0.7",
+      "key": "rules_license@0.0.7",
+      "repoName": "rules_license",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [],
+      "deps": {
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/bazelbuild/rules_license/releases/download/0.0.7/rules_license-0.0.7.tar.gz"
+          ],
+          "integrity": "sha256-RTHezLkTY5ww5cdRKgVNXYdWmNrrddjPkPKEN1/nw2A=",
+          "strip_prefix": "",
+          "remote_patches": {},
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "rules_proto@5.3.0-21.7": {
+      "name": "rules_proto",
+      "version": "5.3.0-21.7",
+      "key": "rules_proto@5.3.0-21.7",
+      "repoName": "rules_proto",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [],
+      "deps": {
+        "bazel_skylib": "bazel_skylib@1.3.0",
+        "com_google_protobuf": "protobuf@21.7",
+        "rules_cc": "rules_cc@0.0.9",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/bazelbuild/rules_proto/archive/refs/tags/5.3.0-21.7.tar.gz"
+          ],
+          "integrity": "sha256-3D+yBqLLNEG0heseQjFlsjEjWh6psDG0Qzz3vB+kYN0=",
+          "strip_prefix": "rules_proto-5.3.0-21.7",
+          "remote_patches": {},
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "rules_python@0.22.1": {
+      "name": "rules_python",
+      "version": "0.22.1",
+      "key": "rules_python@0.22.1",
+      "repoName": "rules_python",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [
+        "@bazel_tools//tools/python:autodetecting_toolchain"
+      ],
+      "extensionUsages": [
+        {
+          "extensionBzlFile": "@rules_python//python/extensions/private:internal_deps.bzl",
+          "extensionName": "internal_deps",
+          "usingModule": "rules_python@0.22.1",
+          "location": {
+            "file": "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel",
+            "line": 14,
+            "column": 30
+          },
+          "imports": {
+            "pypi__build": "pypi__build",
+            "pypi__click": "pypi__click",
+            "pypi__colorama": "pypi__colorama",
+            "pypi__importlib_metadata": "pypi__importlib_metadata",
+            "pypi__installer": "pypi__installer",
+            "pypi__more_itertools": "pypi__more_itertools",
+            "pypi__packaging": "pypi__packaging",
+            "pypi__pep517": "pypi__pep517",
+            "pypi__pip": "pypi__pip",
+            "pypi__pip_tools": "pypi__pip_tools",
+            "pypi__setuptools": "pypi__setuptools",
+            "pypi__tomli": "pypi__tomli",
+            "pypi__wheel": "pypi__wheel",
+            "pypi__zipp": "pypi__zipp",
+            "pypi__coverage_cp310_aarch64-apple-darwin": "pypi__coverage_cp310_aarch64-apple-darwin",
+            "pypi__coverage_cp310_aarch64-unknown-linux-gnu": "pypi__coverage_cp310_aarch64-unknown-linux-gnu",
+            "pypi__coverage_cp310_x86_64-apple-darwin": "pypi__coverage_cp310_x86_64-apple-darwin",
+            "pypi__coverage_cp310_x86_64-unknown-linux-gnu": "pypi__coverage_cp310_x86_64-unknown-linux-gnu",
+            "pypi__coverage_cp311_aarch64-unknown-linux-gnu": "pypi__coverage_cp311_aarch64-unknown-linux-gnu",
+            "pypi__coverage_cp311_x86_64-apple-darwin": "pypi__coverage_cp311_x86_64-apple-darwin",
+            "pypi__coverage_cp311_x86_64-unknown-linux-gnu": "pypi__coverage_cp311_x86_64-unknown-linux-gnu",
+            "pypi__coverage_cp38_aarch64-apple-darwin": "pypi__coverage_cp38_aarch64-apple-darwin",
+            "pypi__coverage_cp38_aarch64-unknown-linux-gnu": "pypi__coverage_cp38_aarch64-unknown-linux-gnu",
+            "pypi__coverage_cp38_x86_64-apple-darwin": "pypi__coverage_cp38_x86_64-apple-darwin",
+            "pypi__coverage_cp38_x86_64-unknown-linux-gnu": "pypi__coverage_cp38_x86_64-unknown-linux-gnu",
+            "pypi__coverage_cp39_aarch64-apple-darwin": "pypi__coverage_cp39_aarch64-apple-darwin",
+            "pypi__coverage_cp39_aarch64-unknown-linux-gnu": "pypi__coverage_cp39_aarch64-unknown-linux-gnu",
+            "pypi__coverage_cp39_x86_64-apple-darwin": "pypi__coverage_cp39_x86_64-apple-darwin",
+            "pypi__coverage_cp39_x86_64-unknown-linux-gnu": "pypi__coverage_cp39_x86_64-unknown-linux-gnu"
+          },
+          "devImports": [],
+          "tags": [
+            {
+              "tagName": "install",
+              "attributeValues": {},
+              "devDependency": false,
+              "location": {
+                "file": "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel",
+                "line": 15,
+                "column": 22
+              }
+            }
+          ],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        },
+        {
+          "extensionBzlFile": "@rules_python//python/extensions:python.bzl",
+          "extensionName": "python",
+          "usingModule": "rules_python@0.22.1",
+          "location": {
+            "file": "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel",
+            "line": 50,
+            "column": 23
+          },
+          "imports": {
+            "pythons_hub": "pythons_hub"
+          },
+          "devImports": [],
+          "tags": [],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        }
+      ],
+      "deps": {
+        "platforms": "platforms@0.0.7",
+        "bazel_skylib": "bazel_skylib@1.3.0",
+        "rules_proto": "rules_proto@5.3.0-21.7",
+        "com_google_protobuf": "protobuf@21.7",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/bazelbuild/rules_python/releases/download/0.22.1/rules_python-0.22.1.tar.gz"
+          ],
+          "integrity": "sha256-pWQP3dS+sD6MH95e1xYMC6a9R359BIZhwwwGk2om/WM=",
+          "strip_prefix": "rules_python-0.22.1",
+          "remote_patches": {
+            "https://bcr.bazel.build/modules/rules_python/0.22.1/patches/module_dot_bazel_version.patch": "sha256-3+VLDH9gYDzNI4eOW7mABC/LKxh1xqF6NhacLbNTucs="
+          },
+          "remote_patch_strip": 1
+        }
+      }
+    },
+    "buildozer@6.4.0.2": {
+      "name": "buildozer",
+      "version": "6.4.0.2",
+      "key": "buildozer@6.4.0.2",
+      "repoName": "buildozer",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [
+        {
+          "extensionBzlFile": "@buildozer//:buildozer_binary.bzl",
+          "extensionName": "buildozer_binary",
+          "usingModule": "buildozer@6.4.0.2",
+          "location": {
+            "file": "https://bcr.bazel.build/modules/buildozer/6.4.0.2/MODULE.bazel",
+            "line": 7,
+            "column": 33
+          },
+          "imports": {
+            "buildozer_binary": "buildozer_binary"
+          },
+          "devImports": [],
+          "tags": [
+            {
+              "tagName": "buildozer",
+              "attributeValues": {
+                "sha256": {
+                  "darwin-amd64": "d29e347ecd6b5673d72cb1a8de05bf1b06178dd229ff5eb67fad5100c840cc8e",
+                  "darwin-arm64": "9b9e71bdbec5e7223871e913b65d12f6d8fa026684daf991f00e52ed36a6978d",
+                  "linux-amd64": "8dfd6345da4e9042daa738d7fdf34f699c5dfce4632f7207956fceedd8494119",
+                  "linux-arm64": "6559558fded658c8fa7432a9d011f7c4dcbac6b738feae73d2d5c352e5f605fa",
+                  "windows-amd64": "e7f05bf847f7c3689dd28926460ce6e1097ae97380ac8e6ae7147b7b706ba19b"
+                },
+                "version": "6.4.0"
+              },
+              "devDependency": false,
+              "location": {
+                "file": "https://bcr.bazel.build/modules/buildozer/6.4.0.2/MODULE.bazel",
+                "line": 8,
+                "column": 27
+              }
+            }
+          ],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        }
+      ],
+      "deps": {
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/fmeum/buildozer/releases/download/v6.4.0.2/buildozer-v6.4.0.2.tar.gz"
+          ],
+          "integrity": "sha256-k7tFKQMR2AygxpmZfH0yEPnQmF3efFgD9rBPkj+Yz/8=",
+          "strip_prefix": "buildozer-6.4.0.2",
+          "remote_patches": {
+            "https://bcr.bazel.build/modules/buildozer/6.4.0.2/patches/module_dot_bazel_version.patch": "sha256-gKANF2HMilj7bWmuXs4lbBIAAansuWC4IhWGB/CerjU="
+          },
+          "remote_patch_strip": 1
+        }
+      }
+    },
+    "platforms@0.0.7": {
+      "name": "platforms",
+      "version": "0.0.7",
+      "key": "platforms@0.0.7",
+      "repoName": "platforms",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [],
+      "deps": {
+        "rules_license": "rules_license@0.0.7",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/bazelbuild/platforms/releases/download/0.0.7/platforms-0.0.7.tar.gz"
+          ],
+          "integrity": "sha256-OlYcmee9vpFzqmU/1Xn+hJ8djWc5V4CrR3Cx84FDHVE=",
+          "strip_prefix": "",
+          "remote_patches": {},
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "protobuf@21.7": {
+      "name": "protobuf",
+      "version": "21.7",
+      "key": "protobuf@21.7",
+      "repoName": "protobuf",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [
+        {
+          "extensionBzlFile": "@rules_jvm_external//:extensions.bzl",
+          "extensionName": "maven",
+          "usingModule": "protobuf@21.7",
+          "location": {
+            "file": "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel",
+            "line": 22,
+            "column": 22
+          },
+          "imports": {
+            "maven": "maven"
+          },
+          "devImports": [],
+          "tags": [
+            {
+              "tagName": "install",
+              "attributeValues": {
+                "name": "maven",
+                "artifacts": [
+                  "com.google.code.findbugs:jsr305:3.0.2",
+                  "com.google.code.gson:gson:2.8.9",
+                  "com.google.errorprone:error_prone_annotations:2.3.2",
+                  "com.google.j2objc:j2objc-annotations:1.3",
+                  "com.google.guava:guava:31.1-jre",
+                  "com.google.guava:guava-testlib:31.1-jre",
+                  "com.google.truth:truth:1.1.2",
+                  "junit:junit:4.13.2",
+                  "org.mockito:mockito-core:4.3.1"
+                ]
+              },
+              "devDependency": false,
+              "location": {
+                "file": "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel",
+                "line": 24,
+                "column": 14
+              }
+            }
+          ],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        }
+      ],
+      "deps": {
+        "bazel_skylib": "bazel_skylib@1.3.0",
+        "rules_python": "rules_python@0.22.1",
+        "rules_cc": "rules_cc@0.0.9",
+        "rules_proto": "rules_proto@5.3.0-21.7",
+        "rules_java": "rules_java@7.4.0",
+        "rules_pkg": "rules_pkg@0.7.0",
+        "com_google_abseil": "abseil-cpp@20211102.0",
+        "zlib": "zlib@1.3",
+        "upb": "upb@0.0.0-20220923-a547704",
+        "rules_jvm_external": "rules_jvm_external@4.4.2",
+        "com_google_googletest": "googletest@1.11.0",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/protocolbuffers/protobuf/releases/download/v21.7/protobuf-all-21.7.zip"
+          ],
+          "integrity": "sha256-VJOiH17T/FAuZv7GuUScBqVRztYwAvpIkDxA36jeeko=",
+          "strip_prefix": "protobuf-21.7",
+          "remote_patches": {
+            "https://bcr.bazel.build/modules/protobuf/21.7/patches/add_module_dot_bazel.patch": "sha256-q3V2+eq0v2XF0z8z+V+QF4cynD6JvHI1y3kI/+rzl5s=",
+            "https://bcr.bazel.build/modules/protobuf/21.7/patches/add_module_dot_bazel_for_examples.patch": "sha256-O7YP6s3lo/1opUiO0jqXYORNHdZ/2q3hjz1QGy8QdIU=",
+            "https://bcr.bazel.build/modules/protobuf/21.7/patches/relative_repo_names.patch": "sha256-RK9RjW8T5UJNG7flIrnFiNE9vKwWB+8uWWtJqXYT0w4=",
+            "https://bcr.bazel.build/modules/protobuf/21.7/patches/add_missing_files.patch": "sha256-Hyne4DG2u5bXcWHNxNMirA2QFAe/2Cl8oMm1XJdkQIY="
+          },
+          "remote_patch_strip": 1
+        }
+      }
+    },
+    "zlib@1.3": {
+      "name": "zlib",
+      "version": "1.3",
+      "key": "zlib@1.3",
+      "repoName": "zlib",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [],
+      "deps": {
+        "platforms": "platforms@0.0.7",
+        "rules_cc": "rules_cc@0.0.9",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/madler/zlib/releases/download/v1.3/zlib-1.3.tar.gz"
+          ],
+          "integrity": "sha256-/wukwpIBPbwnUws6geH5qBPNOd4Byl4Pi/NVcC76WT4=",
+          "strip_prefix": "zlib-1.3",
+          "remote_patches": {
+            "https://bcr.bazel.build/modules/zlib/1.3/patches/add_build_file.patch": "sha256-Ei+FYaaOo7A3jTKunMEodTI0Uw5NXQyZEcboMC8JskY=",
+            "https://bcr.bazel.build/modules/zlib/1.3/patches/module_dot_bazel.patch": "sha256-fPWLM+2xaF/kuy+kZc1YTfW6hNjrkG400Ho7gckuyJk="
+          },
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "apple_support@1.5.0": {
+      "name": "apple_support",
+      "version": "1.5.0",
+      "key": "apple_support@1.5.0",
+      "repoName": "build_bazel_apple_support",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [
+        "@local_config_apple_cc_toolchains//:all"
+      ],
+      "extensionUsages": [
+        {
+          "extensionBzlFile": "@build_bazel_apple_support//crosstool:setup.bzl",
+          "extensionName": "apple_cc_configure_extension",
+          "usingModule": "apple_support@1.5.0",
+          "location": {
+            "file": "https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel",
+            "line": 17,
+            "column": 35
+          },
+          "imports": {
+            "local_config_apple_cc": "local_config_apple_cc",
+            "local_config_apple_cc_toolchains": "local_config_apple_cc_toolchains"
+          },
+          "devImports": [],
+          "tags": [],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        }
+      ],
+      "deps": {
+        "bazel_skylib": "bazel_skylib@1.3.0",
+        "platforms": "platforms@0.0.7",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/bazelbuild/apple_support/releases/download/1.5.0/apple_support.1.5.0.tar.gz"
+          ],
+          "integrity": "sha256-miM41vja0yRPgj8txghKA+TQ+7J8qJLclw5okNW0gYQ=",
+          "strip_prefix": "",
+          "remote_patches": {},
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "bazel_skylib@1.3.0": {
+      "name": "bazel_skylib",
+      "version": "1.3.0",
+      "key": "bazel_skylib@1.3.0",
+      "repoName": "bazel_skylib",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [
+        "//toolchains/unittest:cmd_toolchain",
+        "//toolchains/unittest:bash_toolchain"
+      ],
+      "extensionUsages": [],
+      "deps": {
+        "platforms": "platforms@0.0.7",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz"
+          ],
+          "integrity": "sha256-dNVE2W9KW7Yw1GXKi7z+Ix41lOWq5X4e2/F6brPKJQY=",
+          "strip_prefix": "",
+          "remote_patches": {},
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "rules_pkg@0.7.0": {
+      "name": "rules_pkg",
+      "version": "0.7.0",
+      "key": "rules_pkg@0.7.0",
+      "repoName": "rules_pkg",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [],
+      "deps": {
+        "rules_python": "rules_python@0.22.1",
+        "bazel_skylib": "bazel_skylib@1.3.0",
+        "rules_license": "rules_license@0.0.7",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz"
+          ],
+          "integrity": "sha256-iimOgydi7aGDBZfWT+fbWBeKqEzVkm121bdE1lWJQcI=",
+          "strip_prefix": "",
+          "remote_patches": {
+            "https://bcr.bazel.build/modules/rules_pkg/0.7.0/patches/module_dot_bazel.patch": "sha256-4OaEPZwYF6iC71ZTDg6MJ7LLqX7ZA0/kK4mT+4xKqiE="
+          },
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "abseil-cpp@20211102.0": {
+      "name": "abseil-cpp",
+      "version": "20211102.0",
+      "key": "abseil-cpp@20211102.0",
+      "repoName": "abseil-cpp",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [],
+      "deps": {
+        "rules_cc": "rules_cc@0.0.9",
+        "platforms": "platforms@0.0.7",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/abseil/abseil-cpp/archive/refs/tags/20211102.0.tar.gz"
+          ],
+          "integrity": "sha256-3PcbnLqNwMqZQMSzFqDHlr6Pq0KwcLtrfKtitI8OZsQ=",
+          "strip_prefix": "abseil-cpp-20211102.0",
+          "remote_patches": {
+            "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/patches/module_dot_bazel.patch": "sha256-4izqopgGCey4jVZzl/w3M2GVPNohjh2B5TmbThZNvPY="
+          },
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "upb@0.0.0-20220923-a547704": {
+      "name": "upb",
+      "version": "0.0.0-20220923-a547704",
+      "key": "upb@0.0.0-20220923-a547704",
+      "repoName": "upb",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [],
+      "deps": {
+        "bazel_skylib": "bazel_skylib@1.3.0",
+        "rules_proto": "rules_proto@5.3.0-21.7",
+        "com_google_protobuf": "protobuf@21.7",
+        "com_google_absl": "abseil-cpp@20211102.0",
+        "platforms": "platforms@0.0.7",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/protocolbuffers/upb/archive/a5477045acaa34586420942098f5fecd3570f577.tar.gz"
+          ],
+          "integrity": "sha256-z39x6v+QskwaKLSWRan/A6mmwecTQpHOcJActj5zZLU=",
+          "strip_prefix": "upb-a5477045acaa34586420942098f5fecd3570f577",
+          "remote_patches": {
+            "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/patches/module_dot_bazel.patch": "sha256-wH4mNS6ZYy+8uC0HoAft/c7SDsq2Kxf+J8dUakXhaB0="
+          },
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "rules_jvm_external@4.4.2": {
+      "name": "rules_jvm_external",
+      "version": "4.4.2",
+      "key": "rules_jvm_external@4.4.2",
+      "repoName": "rules_jvm_external",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [
+        {
+          "extensionBzlFile": "@rules_jvm_external//:non-module-deps.bzl",
+          "extensionName": "non_module_deps",
+          "usingModule": "rules_jvm_external@4.4.2",
+          "location": {
+            "file": "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel",
+            "line": 9,
+            "column": 32
+          },
+          "imports": {
+            "io_bazel_rules_kotlin": "io_bazel_rules_kotlin"
+          },
+          "devImports": [],
+          "tags": [],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        },
+        {
+          "extensionBzlFile": "@rules_jvm_external//:extensions.bzl",
+          "extensionName": "maven",
+          "usingModule": "rules_jvm_external@4.4.2",
+          "location": {
+            "file": "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel",
+            "line": 16,
+            "column": 22
+          },
+          "imports": {
+            "rules_jvm_external_deps": "rules_jvm_external_deps"
+          },
+          "devImports": [],
+          "tags": [
+            {
+              "tagName": "install",
+              "attributeValues": {
+                "name": "rules_jvm_external_deps",
+                "artifacts": [
+                  "com.google.cloud:google-cloud-core:1.93.10",
+                  "com.google.cloud:google-cloud-storage:1.113.4",
+                  "com.google.code.gson:gson:2.9.0",
+                  "org.apache.maven:maven-artifact:3.8.6",
+                  "software.amazon.awssdk:s3:2.17.183"
+                ],
+                "lock_file": "@rules_jvm_external//:rules_jvm_external_deps_install.json"
+              },
+              "devDependency": false,
+              "location": {
+                "file": "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel",
+                "line": 18,
+                "column": 14
+              }
+            }
+          ],
+          "hasDevUseExtension": false,
+          "hasNonDevUseExtension": true
+        }
+      ],
+      "deps": {
+        "bazel_skylib": "bazel_skylib@1.3.0",
+        "io_bazel_stardoc": "stardoc@0.5.1",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/bazelbuild/rules_jvm_external/archive/refs/tags/4.4.2.zip"
+          ],
+          "integrity": "sha256-c1YC9QgT6y6pPKP15DsZWb2AshO4NqB6YqKddXZwt3s=",
+          "strip_prefix": "rules_jvm_external-4.4.2",
+          "remote_patches": {},
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "googletest@1.11.0": {
+      "name": "googletest",
+      "version": "1.11.0",
+      "key": "googletest@1.11.0",
+      "repoName": "googletest",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [],
+      "deps": {
+        "com_google_absl": "abseil-cpp@20211102.0",
+        "platforms": "platforms@0.0.7",
+        "rules_cc": "rules_cc@0.0.9",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz"
+          ],
+          "integrity": "sha256-tIcL8SH/d5W6INILzdhie44Ijy0dqymaAxwQNO3ck9U=",
+          "strip_prefix": "googletest-release-1.11.0",
+          "remote_patches": {
+            "https://bcr.bazel.build/modules/googletest/1.11.0/patches/module_dot_bazel.patch": "sha256-HuahEdI/n8KCI071sN3CEziX+7qP/Ec77IWayYunLP0="
+          },
+          "remote_patch_strip": 0
+        }
+      }
+    },
+    "stardoc@0.5.1": {
+      "name": "stardoc",
+      "version": "0.5.1",
+      "key": "stardoc@0.5.1",
+      "repoName": "stardoc",
+      "executionPlatformsToRegister": [],
+      "toolchainsToRegister": [],
+      "extensionUsages": [],
+      "deps": {
+        "bazel_skylib": "bazel_skylib@1.3.0",
+        "rules_java": "rules_java@7.4.0",
+        "bazel_tools": "bazel_tools@_",
+        "local_config_platform": "local_config_platform@_"
+      },
+      "repoSpec": {
+        "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+        "ruleClassName": "http_archive",
+        "attributes": {
+          "urls": [
+            "https://github.com/bazelbuild/stardoc/releases/download/0.5.1/stardoc-0.5.1.tar.gz"
+          ],
+          "integrity": "sha256-qoFNrgrEALurLoiB+ZFcb0fElmS/CHxAmhX5BDjSwj4=",
+          "strip_prefix": "",
+          "remote_patches": {
+            "https://bcr.bazel.build/modules/stardoc/0.5.1/patches/module_dot_bazel.patch": "sha256-UAULCuTpJE7SG0YrR9XLjMfxMRmbP+za3uW9ONZ5rjI="
+          },
+          "remote_patch_strip": 0
+        }
+      }
+    }
+  },
+  "moduleExtensions": {
+    "@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": {
+      "general": {
+        "bzlTransitiveDigest": "pMLFCYaRPkgXPQ8vtuNkMfiHfPmRBy6QJfnid4sWfv0=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "local_config_apple_cc": {
+            "bzlFile": "@@apple_support~//crosstool:setup.bzl",
+            "ruleClassName": "_apple_cc_autoconf",
+            "attributes": {}
+          },
+          "local_config_apple_cc_toolchains": {
+            "bzlFile": "@@apple_support~//crosstool:setup.bzl",
+            "ruleClassName": "_apple_cc_autoconf_toolchains",
+            "attributes": {}
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "apple_support~",
+            "bazel_tools",
+            "bazel_tools"
+          ]
+        ]
+      }
+    },
+    "@@bazel_tools//tools/cpp:cc_configure.bzl%cc_configure_extension": {
+      "general": {
+        "bzlTransitiveDigest": "PHpT2yqMGms2U4L3E/aZ+WcQalmZWm+ILdP3yiLsDhA=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "local_config_cc": {
+            "bzlFile": "@@bazel_tools//tools/cpp:cc_configure.bzl",
+            "ruleClassName": "cc_autoconf",
+            "attributes": {}
+          },
+          "local_config_cc_toolchains": {
+            "bzlFile": "@@bazel_tools//tools/cpp:cc_configure.bzl",
+            "ruleClassName": "cc_autoconf_toolchains",
+            "attributes": {}
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "bazel_tools",
+            "bazel_tools",
+            "bazel_tools"
+          ]
+        ]
+      }
+    },
+    "@@bazel_tools//tools/osx:xcode_configure.bzl%xcode_configure_extension": {
+      "general": {
+        "bzlTransitiveDigest": "Qh2bWTU6QW6wkrd87qrU4YeY+SG37Nvw3A0PR4Y0L2Y=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "local_config_xcode": {
+            "bzlFile": "@@bazel_tools//tools/osx:xcode_configure.bzl",
+            "ruleClassName": "xcode_autoconf",
+            "attributes": {
+              "xcode_locator": "@bazel_tools//tools/osx:xcode_locator.m",
+              "remote_xcode": ""
+            }
+          }
+        },
+        "recordedRepoMappingEntries": []
+      }
+    },
+    "@@bazel_tools//tools/sh:sh_configure.bzl%sh_configure_extension": {
+      "general": {
+        "bzlTransitiveDigest": "hp4NgmNjEg5+xgvzfh6L83bt9/aiiWETuNpwNuF1MSU=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "local_config_sh": {
+            "bzlFile": "@@bazel_tools//tools/sh:sh_configure.bzl",
+            "ruleClassName": "sh_config",
+            "attributes": {}
+          }
+        },
+        "recordedRepoMappingEntries": []
+      }
+    },
+    "@@rules_java~//java:extensions.bzl%toolchains": {
+      "general": {
+        "bzlTransitiveDigest": "tJHbmWnq7m+9eUBnUdv7jZziQ26FmcGL9C5/hU3Q9UQ=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "remotejdk21_linux_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_21\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"21\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk21_linux//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk21_linux//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk17_linux_s390x_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_17\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"17\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_linux_s390x//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_linux_s390x//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk17_macos_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_17\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"17\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_macos//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_macos//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk21_macos_aarch64_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_21\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"21\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk21_macos_aarch64//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk21_macos_aarch64//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk17_linux_aarch64_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_17\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"17\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_linux_aarch64//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_linux_aarch64//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk21_macos_aarch64": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 21,\n)\n",
+              "sha256": "e8260516de8b60661422a725f1df2c36ef888f6fb35393566b00e7325db3d04e",
+              "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-macosx_aarch64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-macosx_aarch64.tar.gz",
+                "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-macosx_aarch64.tar.gz"
+              ]
+            }
+          },
+          "remotejdk17_linux_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_17\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"17\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_linux//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_linux//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk17_macos_aarch64": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 17,\n)\n",
+              "sha256": "314b04568ec0ae9b36ba03c9cbd42adc9e1265f74678923b19297d66eb84dcca",
+              "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-macosx_aarch64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_aarch64.tar.gz",
+                "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_aarch64.tar.gz"
+              ]
+            }
+          },
+          "remote_java_tools_windows": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "sha256": "fe2f88169696d6c6fc6e90ba61bb46be7d0ae3693cbafdf336041bf56679e8d1",
+              "urls": [
+                "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools_windows-v13.4.zip",
+                "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools_windows-v13.4.zip"
+              ]
+            }
+          },
+          "remotejdk11_win": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 11,\n)\n",
+              "sha256": "43408193ce2fa0862819495b5ae8541085b95660153f2adcf91a52d3a1710e83",
+              "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-win_x64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-win_x64.zip",
+                "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-win_x64.zip"
+              ]
+            }
+          },
+          "remotejdk11_win_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_11\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"11\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_win//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_win//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk11_linux_aarch64": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 11,\n)\n",
+              "sha256": "54174439f2b3fddd11f1048c397fe7bb45d4c9d66d452d6889b013d04d21c4de",
+              "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-linux_aarch64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_aarch64.tar.gz",
+                "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_aarch64.tar.gz"
+              ]
+            }
+          },
+          "remotejdk17_linux": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 17,\n)\n",
+              "sha256": "b9482f2304a1a68a614dfacddcf29569a72f0fac32e6c74f83dc1b9a157b8340",
+              "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-linux_x64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_x64.tar.gz",
+                "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_x64.tar.gz"
+              ]
+            }
+          },
+          "remotejdk11_linux_s390x_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_11\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"11\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_linux_s390x//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_linux_s390x//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk11_linux_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_11\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"11\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_linux//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_linux//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk11_macos": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 11,\n)\n",
+              "sha256": "bcaab11cfe586fae7583c6d9d311c64384354fb2638eb9a012eca4c3f1a1d9fd",
+              "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-macosx_x64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_x64.tar.gz",
+                "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_x64.tar.gz"
+              ]
+            }
+          },
+          "remotejdk11_win_arm64": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 11,\n)\n",
+              "sha256": "b8a28e6e767d90acf793ea6f5bed0bb595ba0ba5ebdf8b99f395266161e53ec2",
+              "strip_prefix": "jdk-11.0.13+8",
+              "urls": [
+                "https://mirror.bazel.build/aka.ms/download-jdk/microsoft-jdk-11.0.13.8.1-windows-aarch64.zip"
+              ]
+            }
+          },
+          "remotejdk17_macos": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 17,\n)\n",
+              "sha256": "640453e8afe8ffe0fb4dceb4535fb50db9c283c64665eebb0ba68b19e65f4b1f",
+              "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-macosx_x64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_x64.tar.gz",
+                "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_x64.tar.gz"
+              ]
+            }
+          },
+          "remotejdk21_macos": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 21,\n)\n",
+              "sha256": "3ad8fe288eb57d975c2786ae453a036aa46e47ab2ac3d81538ebae2a54d3c025",
+              "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-macosx_x64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-macosx_x64.tar.gz",
+                "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-macosx_x64.tar.gz"
+              ]
+            }
+          },
+          "remotejdk21_macos_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_21\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"21\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk21_macos//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk21_macos//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk17_macos_aarch64_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_17\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"17\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_macos_aarch64//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_macos_aarch64//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk17_win": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 17,\n)\n",
+              "sha256": "192f2afca57701de6ec496234f7e45d971bf623ff66b8ee4a5c81582054e5637",
+              "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-win_x64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_x64.zip",
+                "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_x64.zip"
+              ]
+            }
+          },
+          "remotejdk11_macos_aarch64_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_11\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"11\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_macos_aarch64//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_macos_aarch64//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk11_linux_ppc64le_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_11\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"11\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_linux_ppc64le//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_linux_ppc64le//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk21_linux": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 21,\n)\n",
+              "sha256": "5ad730fbee6bb49bfff10bf39e84392e728d89103d3474a7e5def0fd134b300a",
+              "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-linux_x64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_x64.tar.gz",
+                "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_x64.tar.gz"
+              ]
+            }
+          },
+          "remote_java_tools_linux": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "sha256": "ba10f09a138cf185d04cbc807d67a3da42ab13d618c5d1ce20d776e199c33a39",
+              "urls": [
+                "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools_linux-v13.4.zip",
+                "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools_linux-v13.4.zip"
+              ]
+            }
+          },
+          "remotejdk21_win": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 21,\n)\n",
+              "sha256": "f7cc15ca17295e69c907402dfe8db240db446e75d3b150da7bf67243cded93de",
+              "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-win_x64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-win_x64.zip",
+                "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-win_x64.zip"
+              ]
+            }
+          },
+          "remotejdk21_linux_aarch64": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 21,\n)\n",
+              "sha256": "ce7df1af5d44a9f455617c4b8891443fbe3e4b269c777d8b82ed66f77167cfe0",
+              "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-linux_aarch64",
+              "urls": [
+                "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_aarch64.tar.gz",
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_aarch64.tar.gz"
+              ]
+            }
+          },
+          "remotejdk11_linux_aarch64_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_11\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"11\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_linux_aarch64//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_linux_aarch64//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk11_linux_s390x": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 11,\n)\n",
+              "sha256": "a58fc0361966af0a5d5a31a2d8a208e3c9bb0f54f345596fd80b99ea9a39788b",
+              "strip_prefix": "jdk-11.0.15+10",
+              "urls": [
+                "https://mirror.bazel.build/github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_s390x_linux_hotspot_11.0.15_10.tar.gz",
+                "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_s390x_linux_hotspot_11.0.15_10.tar.gz"
+              ]
+            }
+          },
+          "remotejdk17_linux_aarch64": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 17,\n)\n",
+              "sha256": "6531cef61e416d5a7b691555c8cf2bdff689201b8a001ff45ab6740062b44313",
+              "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-linux_aarch64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_aarch64.tar.gz",
+                "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_aarch64.tar.gz"
+              ]
+            }
+          },
+          "remotejdk17_win_arm64_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_17\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"17\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_win_arm64//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_win_arm64//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk11_linux": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 11,\n)\n",
+              "sha256": "a34b404f87a08a61148b38e1416d837189e1df7a040d949e743633daf4695a3c",
+              "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-linux_x64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_x64.tar.gz",
+                "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_x64.tar.gz"
+              ]
+            }
+          },
+          "remotejdk11_macos_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_11\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"11\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_macos//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_macos//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk17_linux_ppc64le_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_17\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"17\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_linux_ppc64le//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_linux_ppc64le//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk17_win_arm64": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 17,\n)\n",
+              "sha256": "6802c99eae0d788e21f52d03cab2e2b3bf42bc334ca03cbf19f71eb70ee19f85",
+              "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-win_aarch64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_aarch64.zip",
+                "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_aarch64.zip"
+              ]
+            }
+          },
+          "remote_java_tools_darwin_arm64": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "sha256": "076a7e198ad077f8c7d997986ef5102427fae6bbfce7a7852d2e080ed8767528",
+              "urls": [
+                "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools_darwin_arm64-v13.4.zip",
+                "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools_darwin_arm64-v13.4.zip"
+              ]
+            }
+          },
+          "remotejdk17_linux_ppc64le": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 17,\n)\n",
+              "sha256": "00a4c07603d0218cd678461b5b3b7e25b3253102da4022d31fc35907f21a2efd",
+              "strip_prefix": "jdk-17.0.8.1+1",
+              "urls": [
+                "https://mirror.bazel.build/github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_ppc64le_linux_hotspot_17.0.8.1_1.tar.gz",
+                "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_ppc64le_linux_hotspot_17.0.8.1_1.tar.gz"
+              ]
+            }
+          },
+          "remotejdk21_linux_aarch64_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_21\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"21\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk21_linux_aarch64//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk21_linux_aarch64//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk11_win_arm64_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_11\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"11\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_win_arm64//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk11_win_arm64//:jdk\",\n)\n"
+            }
+          },
+          "local_jdk": {
+            "bzlFile": "@@rules_java~//toolchains:local_java_repository.bzl",
+            "ruleClassName": "_local_java_repository_rule",
+            "attributes": {
+              "java_home": "",
+              "version": "",
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = {RUNTIME_VERSION},\n)\n"
+            }
+          },
+          "remote_java_tools_darwin_x86_64": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "sha256": "4523aec4d09c587091a2dae6f5c9bc6922c220f3b6030e5aba9c8f015913cc65",
+              "urls": [
+                "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools_darwin_x86_64-v13.4.zip",
+                "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools_darwin_x86_64-v13.4.zip"
+              ]
+            }
+          },
+          "remote_java_tools": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "sha256": "e025fd260ac39b47c111f5212d64ec0d00d85dec16e49368aae82fc626a940cf",
+              "urls": [
+                "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools-v13.4.zip",
+                "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools-v13.4.zip"
+              ]
+            }
+          },
+          "remotejdk17_linux_s390x": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 17,\n)\n",
+              "sha256": "ffacba69c6843d7ca70d572489d6cc7ab7ae52c60f0852cedf4cf0d248b6fc37",
+              "strip_prefix": "jdk-17.0.8.1+1",
+              "urls": [
+                "https://mirror.bazel.build/github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_s390x_linux_hotspot_17.0.8.1_1.tar.gz",
+                "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_s390x_linux_hotspot_17.0.8.1_1.tar.gz"
+              ]
+            }
+          },
+          "remotejdk17_win_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_17\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"17\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_win//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk17_win//:jdk\",\n)\n"
+            }
+          },
+          "remotejdk11_linux_ppc64le": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 11,\n)\n",
+              "sha256": "a8fba686f6eb8ae1d1a9566821dbd5a85a1108b96ad857fdbac5c1e4649fc56f",
+              "strip_prefix": "jdk-11.0.15+10",
+              "urls": [
+                "https://mirror.bazel.build/github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_ppc64le_linux_hotspot_11.0.15_10.tar.gz",
+                "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_ppc64le_linux_hotspot_11.0.15_10.tar.gz"
+              ]
+            }
+          },
+          "remotejdk11_macos_aarch64": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n    name = \"jre\",\n    srcs = glob(\n        [\n            \"jre/bin/**\",\n            \"jre/lib/**\",\n        ],\n        allow_empty = True,\n        # In some configurations, Java browser plugin is considered harmful and\n        # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n        # so do not include it in JRE on Windows.\n        exclude = [\"jre/bin/plugin2/**\"],\n    ),\n)\n\nfilegroup(\n    name = \"jdk-bin\",\n    srcs = glob(\n        [\"bin/**\"],\n        # The JDK on Windows sometimes contains a directory called\n        # \"%systemroot%\", which is not a valid label.\n        exclude = [\"**/*%*/**\"],\n    ),\n)\n\n# This folder holds security policies.\nfilegroup(\n    name = \"jdk-conf\",\n    srcs = glob(\n        [\"conf/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-include\",\n    srcs = glob(\n        [\"include/**\"],\n        allow_empty = True,\n    ),\n)\n\nfilegroup(\n    name = \"jdk-lib\",\n    srcs = glob(\n        [\"lib/**\", \"release\"],\n        allow_empty = True,\n        exclude = [\n            \"lib/missioncontrol/**\",\n            \"lib/visualvm/**\",\n        ],\n    ),\n)\n\njava_runtime(\n    name = \"jdk\",\n    srcs = [\n        \":jdk-bin\",\n        \":jdk-conf\",\n        \":jdk-include\",\n        \":jdk-lib\",\n        \":jre\",\n    ],\n    # Provide the 'java` binary explicitly so that the correct path is used by\n    # Bazel even when the host platform differs from the execution platform.\n    # Exactly one of the two globs will be empty depending on the host platform.\n    # When --incompatible_disallow_empty_glob is enabled, each individual empty\n    # glob will fail without allow_empty = True, even if the overall result is\n    # non-empty.\n    java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n    version = 11,\n)\n",
+              "sha256": "7632bc29f8a4b7d492b93f3bc75a7b61630894db85d136456035ab2a24d38885",
+              "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-macosx_aarch64",
+              "urls": [
+                "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_aarch64.tar.gz",
+                "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_aarch64.tar.gz"
+              ]
+            }
+          },
+          "remotejdk21_win_toolchain_config_repo": {
+            "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
+            "ruleClassName": "_toolchain_config",
+            "attributes": {
+              "build_file": "\nconfig_setting(\n    name = \"prefix_version_setting\",\n    values = {\"java_runtime_version\": \"remotejdk_21\"},\n    visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n    name = \"version_setting\",\n    values = {\"java_runtime_version\": \"21\"},\n    visibility = [\"//visibility:private\"],\n)\nalias(\n    name = \"version_or_prefix_version_setting\",\n    actual = select({\n        \":version_setting\": \":version_setting\",\n        \"//conditions:default\": \":prefix_version_setting\",\n    }),\n    visibility = [\"//visibility:private\"],\n)\ntoolchain(\n    name = \"toolchain\",\n    target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n    toolchain = \"@remotejdk21_win//:jdk\",\n)\ntoolchain(\n    name = \"bootstrap_runtime_toolchain\",\n    # These constraints are not required for correctness, but prevent fetches of remote JDK for\n    # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n    # the same configuration, this constraint will not result in toolchain resolution failures.\n    exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n    target_settings = [\":version_or_prefix_version_setting\"],\n    toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n    toolchain = \"@remotejdk21_win//:jdk\",\n)\n"
+            }
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "rules_java~",
+            "bazel_tools",
+            "bazel_tools"
+          ],
+          [
+            "rules_java~",
+            "remote_java_tools",
+            "rules_java~~toolchains~remote_java_tools"
+          ]
+        ]
+      }
+    }
+  }
+}
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..f96b8c2
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,51 @@
+workspace(name = "nearby-rust")
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+    name = "bazel_skylib",
+    sha256 = "66ffd9315665bfaafc96b52278f57c7e2dd09f5ede279ea6d39b2be471e7e3aa",
+    urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz"],
+)
+
+load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
+
+bazel_skylib_workspace()
+
+http_archive(
+    name = "rules_rust",
+    integrity = "sha256-JLN47ZcAbx9wEr5Jiib4HduZATGLiDgK7oUi/fvotzU=",
+    urls = ["https://github.com/bazelbuild/rules_rust/releases/download/0.42.1/rules_rust-v0.42.1.tar.gz"],
+)
+
+load("@rules_rust//crate_universe:defs.bzl", "crate", "crates_repository", "render_config", "splicing_config")
+load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_register_toolchains")
+
+rules_rust_dependencies()
+
+rust_register_toolchains(
+    edition = "2021",
+    versions = [
+        "1.77.1",
+    ],
+)
+
+crates_repository(
+    name = "crate_index",
+    cargo_lockfile = "//:bazel_placeholder/Cargo.lock",
+    lockfile = "//:bazel_placeholder/Cargo.Bazel.lock",
+    manifests = [
+        "//:bazel_placeholder/Cargo.toml",
+    ],
+)
+
+load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
+
+local_repository(
+    name = "absl",
+    path = "third_party/abseil-cpp",
+)
+
+load("@crate_index//:defs.bzl", "crate_repositories")
+
+crate_repositories()
diff --git a/bazel_placeholder/Cargo.Bazel.lock b/bazel_placeholder/Cargo.Bazel.lock
new file mode 100644
index 0000000..61c368f
--- /dev/null
+++ b/bazel_placeholder/Cargo.Bazel.lock
@@ -0,0 +1,4318 @@
+{
+  "checksum": "8252b2836647756486e1f6f409834897441b4ab161b9310b2c30f0a2ecf13ef6",
+  "crates": {
+    "aead 0.5.2": {
+      "name": "aead",
+      "version": "0.5.2",
+      "package_url": "https://github.com/RustCrypto/traits",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/aead/0.5.2/download",
+          "sha256": "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "aead",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "aead",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "alloc",
+            "default",
+            "rand_core"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "crypto-common 0.1.6",
+              "target": "crypto_common"
+            },
+            {
+              "id": "generic-array 0.14.7",
+              "target": "generic_array"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.5.2"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "aes 0.8.4": {
+      "name": "aes",
+      "version": "0.8.4",
+      "package_url": "https://github.com/RustCrypto/block-ciphers",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/aes/0.8.4/download",
+          "sha256": "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "aes",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "aes",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "cfg-if 1.0.0",
+              "target": "cfg_if"
+            },
+            {
+              "id": "cipher 0.4.4",
+              "target": "cipher"
+            }
+          ],
+          "selects": {
+            "cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))": [
+              {
+                "id": "cpufeatures 0.2.12",
+                "target": "cpufeatures"
+              }
+            ]
+          }
+        },
+        "edition": "2021",
+        "version": "0.8.4"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "aes-gcm 0.10.3": {
+      "name": "aes-gcm",
+      "version": "0.10.3",
+      "package_url": "https://github.com/RustCrypto/AEADs",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/aes-gcm/0.10.3/download",
+          "sha256": "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "aes_gcm",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "aes_gcm",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "aes"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "aead 0.5.2",
+              "target": "aead"
+            },
+            {
+              "id": "aes 0.8.4",
+              "target": "aes"
+            },
+            {
+              "id": "cipher 0.4.4",
+              "target": "cipher"
+            },
+            {
+              "id": "ctr 0.9.2",
+              "target": "ctr"
+            },
+            {
+              "id": "ghash 0.5.1",
+              "target": "ghash"
+            },
+            {
+              "id": "subtle 2.5.0",
+              "target": "subtle"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.10.3"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "aes-gcm-siv 0.11.1": {
+      "name": "aes-gcm-siv",
+      "version": "0.11.1",
+      "package_url": "https://github.com/RustCrypto/AEADs",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/aes-gcm-siv/0.11.1/download",
+          "sha256": "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "aes_gcm_siv",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "aes_gcm_siv",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "aes"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "aead 0.5.2",
+              "target": "aead"
+            },
+            {
+              "id": "aes 0.8.4",
+              "target": "aes"
+            },
+            {
+              "id": "cipher 0.4.4",
+              "target": "cipher"
+            },
+            {
+              "id": "ctr 0.9.2",
+              "target": "ctr"
+            },
+            {
+              "id": "polyval 0.6.2",
+              "target": "polyval"
+            },
+            {
+              "id": "subtle 2.5.0",
+              "target": "subtle"
+            },
+            {
+              "id": "zeroize 1.7.0",
+              "target": "zeroize"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.11.1"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "base16ct 0.2.0": {
+      "name": "base16ct",
+      "version": "0.2.0",
+      "package_url": "https://github.com/RustCrypto/formats/tree/master/base16ct",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/base16ct/0.2.0/download",
+          "sha256": "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "base16ct",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "base16ct",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2021",
+        "version": "0.2.0"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "block-buffer 0.10.4": {
+      "name": "block-buffer",
+      "version": "0.10.4",
+      "package_url": "https://github.com/RustCrypto/utils",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/block-buffer/0.10.4/download",
+          "sha256": "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "block_buffer",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "block_buffer",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "generic-array 0.14.7",
+              "target": "generic_array"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "0.10.4"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "block-padding 0.3.3": {
+      "name": "block-padding",
+      "version": "0.3.3",
+      "package_url": "https://github.com/RustCrypto/utils",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/block-padding/0.3.3/download",
+          "sha256": "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "block_padding",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "block_padding",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "generic-array 0.14.7",
+              "target": "generic_array"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.3.3"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "cbc 0.1.2": {
+      "name": "cbc",
+      "version": "0.1.2",
+      "package_url": "https://github.com/RustCrypto/block-modes",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/cbc/0.1.2/download",
+          "sha256": "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "cbc",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "cbc",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "alloc",
+            "block-padding"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "cipher 0.4.4",
+              "target": "cipher"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.1.2"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "cfg-if 1.0.0": {
+      "name": "cfg-if",
+      "version": "1.0.0",
+      "package_url": "https://github.com/alexcrichton/cfg-if",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/cfg-if/1.0.0/download",
+          "sha256": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "cfg_if",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "cfg_if",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2018",
+        "version": "1.0.0"
+      },
+      "license": "MIT/Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "cipher 0.4.4": {
+      "name": "cipher",
+      "version": "0.4.4",
+      "package_url": "https://github.com/RustCrypto/traits",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/cipher/0.4.4/download",
+          "sha256": "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "cipher",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "cipher",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "alloc",
+            "block-padding"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "crypto-common 0.1.6",
+              "target": "crypto_common"
+            },
+            {
+              "id": "inout 0.1.3",
+              "target": "inout"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.4.4"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "const-oid 0.9.6": {
+      "name": "const-oid",
+      "version": "0.9.6",
+      "package_url": "https://github.com/RustCrypto/formats/tree/master/const-oid",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/const-oid/0.9.6/download",
+          "sha256": "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "const_oid",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "const_oid",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2021",
+        "version": "0.9.6"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "cpufeatures 0.2.12": {
+      "name": "cpufeatures",
+      "version": "0.2.12",
+      "package_url": "https://github.com/RustCrypto/utils",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/cpufeatures/0.2.12/download",
+          "sha256": "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "cpufeatures",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "cpufeatures",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [],
+          "selects": {
+            "aarch64-linux-android": [
+              {
+                "id": "libc 0.2.153",
+                "target": "libc"
+              }
+            ],
+            "cfg(all(target_arch = \"aarch64\", target_os = \"linux\"))": [
+              {
+                "id": "libc 0.2.153",
+                "target": "libc"
+              }
+            ],
+            "cfg(all(target_arch = \"aarch64\", target_vendor = \"apple\"))": [
+              {
+                "id": "libc 0.2.153",
+                "target": "libc"
+              }
+            ],
+            "cfg(all(target_arch = \"loongarch64\", target_os = \"linux\"))": [
+              {
+                "id": "libc 0.2.153",
+                "target": "libc"
+              }
+            ]
+          }
+        },
+        "edition": "2018",
+        "version": "0.2.12"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "crypto-bigint 0.5.5": {
+      "name": "crypto-bigint",
+      "version": "0.5.5",
+      "package_url": "https://github.com/RustCrypto/crypto-bigint",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/crypto-bigint/0.5.5/download",
+          "sha256": "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "crypto_bigint",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "crypto_bigint",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "generic-array",
+            "rand_core",
+            "zeroize"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "generic-array 0.14.7",
+              "target": "generic_array"
+            },
+            {
+              "id": "rand_core 0.6.4",
+              "target": "rand_core"
+            },
+            {
+              "id": "subtle 2.5.0",
+              "target": "subtle"
+            },
+            {
+              "id": "zeroize 1.7.0",
+              "target": "zeroize"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.5.5"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "crypto-common 0.1.6": {
+      "name": "crypto-common",
+      "version": "0.1.6",
+      "package_url": "https://github.com/RustCrypto/traits",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/crypto-common/0.1.6/download",
+          "sha256": "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "crypto_common",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "crypto_common",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "rand_core"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "generic-array 0.14.7",
+              "target": "generic_array"
+            },
+            {
+              "id": "rand_core 0.6.4",
+              "target": "rand_core"
+            },
+            {
+              "id": "typenum 1.17.0",
+              "target": "typenum"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "0.1.6"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "ctr 0.9.2": {
+      "name": "ctr",
+      "version": "0.9.2",
+      "package_url": "https://github.com/RustCrypto/block-modes",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/ctr/0.9.2/download",
+          "sha256": "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "ctr",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "ctr",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "cipher 0.4.4",
+              "target": "cipher"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.9.2"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "curve25519-dalek 4.1.2": {
+      "name": "curve25519-dalek",
+      "version": "4.1.2",
+      "package_url": "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/curve25519-dalek",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/curve25519-dalek/4.1.2/download",
+          "sha256": "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "curve25519_dalek",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        },
+        {
+          "BuildScript": {
+            "crate_name": "build_script_build",
+            "crate_root": "build.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "curve25519_dalek",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "digest"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "cfg-if 1.0.0",
+              "target": "cfg_if"
+            },
+            {
+              "id": "curve25519-dalek 4.1.2",
+              "target": "build_script_build"
+            },
+            {
+              "id": "digest 0.10.7",
+              "target": "digest"
+            },
+            {
+              "id": "subtle 2.5.0",
+              "target": "subtle"
+            }
+          ],
+          "selects": {
+            "cfg(curve25519_dalek_backend = \"fiat\")": [
+              {
+                "id": "fiat-crypto 0.2.7",
+                "target": "fiat_crypto"
+              }
+            ],
+            "cfg(target_arch = \"x86_64\")": [
+              {
+                "id": "cpufeatures 0.2.12",
+                "target": "cpufeatures"
+              }
+            ]
+          }
+        },
+        "edition": "2021",
+        "proc_macro_deps": {
+          "common": [],
+          "selects": {
+            "cfg(all(not(curve25519_dalek_backend = \"fiat\"), not(curve25519_dalek_backend = \"serial\"), target_arch = \"x86_64\"))": [
+              {
+                "id": "curve25519-dalek-derive 0.1.1",
+                "target": "curve25519_dalek_derive"
+              }
+            ]
+          }
+        },
+        "version": "4.1.2"
+      },
+      "build_script_attrs": {
+        "data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "platforms 3.4.0",
+              "target": "platforms"
+            },
+            {
+              "id": "rustc_version 0.4.0",
+              "target": "rustc_version"
+            }
+          ],
+          "selects": {}
+        }
+      },
+      "license": "BSD-3-Clause",
+      "license_ids": [
+        "BSD-3-Clause"
+      ],
+      "license_file": "LICENSE"
+    },
+    "curve25519-dalek-derive 0.1.1": {
+      "name": "curve25519-dalek-derive",
+      "version": "0.1.1",
+      "package_url": "https://github.com/dalek-cryptography/curve25519-dalek",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/curve25519-dalek-derive/0.1.1/download",
+          "sha256": "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+        }
+      },
+      "targets": [
+        {
+          "ProcMacro": {
+            "crate_name": "curve25519_dalek_derive",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "curve25519_dalek_derive",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "proc-macro2 1.0.81",
+              "target": "proc_macro2"
+            },
+            {
+              "id": "quote 1.0.36",
+              "target": "quote"
+            },
+            {
+              "id": "syn 2.0.59",
+              "target": "syn"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.1.1"
+      },
+      "license": "MIT/Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "der 0.7.9": {
+      "name": "der",
+      "version": "0.7.9",
+      "package_url": "https://github.com/RustCrypto/formats/tree/master/der",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/der/0.7.9/download",
+          "sha256": "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "der",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "der",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "oid",
+            "zeroize"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "const-oid 0.9.6",
+              "target": "const_oid"
+            },
+            {
+              "id": "zeroize 1.7.0",
+              "target": "zeroize"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.7.9"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "digest 0.10.7": {
+      "name": "digest",
+      "version": "0.10.7",
+      "package_url": "https://github.com/RustCrypto/traits",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/digest/0.10.7/download",
+          "sha256": "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "digest",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "digest",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "block-buffer",
+            "core-api",
+            "default",
+            "mac",
+            "subtle"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "block-buffer 0.10.4",
+              "target": "block_buffer"
+            },
+            {
+              "id": "crypto-common 0.1.6",
+              "target": "crypto_common"
+            },
+            {
+              "id": "subtle 2.5.0",
+              "target": "subtle"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "0.10.7"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "ed25519 2.2.3": {
+      "name": "ed25519",
+      "version": "2.2.3",
+      "package_url": "https://github.com/RustCrypto/signatures/tree/master/ed25519",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/ed25519/2.2.3/download",
+          "sha256": "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "ed25519",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "ed25519",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "signature 2.2.0",
+              "target": "signature"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "2.2.3"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "ed25519-dalek 2.1.1": {
+      "name": "ed25519-dalek",
+      "version": "2.1.1",
+      "package_url": "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/ed25519-dalek",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/ed25519-dalek/2.1.1/download",
+          "sha256": "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "ed25519_dalek",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "ed25519_dalek",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "rand_core"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "curve25519-dalek 4.1.2",
+              "target": "curve25519_dalek"
+            },
+            {
+              "id": "ed25519 2.2.3",
+              "target": "ed25519"
+            },
+            {
+              "id": "rand_core 0.6.4",
+              "target": "rand_core"
+            },
+            {
+              "id": "sha2 0.10.8",
+              "target": "sha2"
+            },
+            {
+              "id": "subtle 2.5.0",
+              "target": "subtle"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "2.1.1"
+      },
+      "license": "BSD-3-Clause",
+      "license_ids": [
+        "BSD-3-Clause"
+      ],
+      "license_file": "LICENSE"
+    },
+    "elliptic-curve 0.13.8": {
+      "name": "elliptic-curve",
+      "version": "0.13.8",
+      "package_url": "https://github.com/RustCrypto/traits/tree/master/elliptic-curve",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/elliptic-curve/0.13.8/download",
+          "sha256": "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "elliptic_curve",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "elliptic_curve",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "arithmetic",
+            "digest",
+            "ecdh",
+            "ff",
+            "group",
+            "hazmat",
+            "sec1"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "base16ct 0.2.0",
+              "target": "base16ct"
+            },
+            {
+              "id": "crypto-bigint 0.5.5",
+              "target": "crypto_bigint"
+            },
+            {
+              "id": "digest 0.10.7",
+              "target": "digest"
+            },
+            {
+              "id": "ff 0.13.0",
+              "target": "ff"
+            },
+            {
+              "id": "generic-array 0.14.7",
+              "target": "generic_array"
+            },
+            {
+              "id": "group 0.13.0",
+              "target": "group"
+            },
+            {
+              "id": "hkdf 0.12.4",
+              "target": "hkdf"
+            },
+            {
+              "id": "rand_core 0.6.4",
+              "target": "rand_core"
+            },
+            {
+              "id": "sec1 0.7.3",
+              "target": "sec1"
+            },
+            {
+              "id": "subtle 2.5.0",
+              "target": "subtle"
+            },
+            {
+              "id": "zeroize 1.7.0",
+              "target": "zeroize"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.13.8"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "ff 0.13.0": {
+      "name": "ff",
+      "version": "0.13.0",
+      "package_url": "https://github.com/zkcrypto/ff",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/ff/0.13.0/download",
+          "sha256": "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "ff",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "ff",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "rand_core 0.6.4",
+              "target": "rand_core"
+            },
+            {
+              "id": "subtle 2.5.0",
+              "target": "subtle"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.13.0"
+      },
+      "license": "MIT/Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "fiat-crypto 0.2.7": {
+      "name": "fiat-crypto",
+      "version": "0.2.7",
+      "package_url": "https://github.com/mit-plv/fiat-crypto",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/fiat-crypto/0.2.7/download",
+          "sha256": "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "fiat_crypto",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "fiat_crypto",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2018",
+        "version": "0.2.7"
+      },
+      "license": "MIT OR Apache-2.0 OR BSD-1-Clause",
+      "license_ids": [
+        "Apache-2.0",
+        "BSD-1-Clause",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "generic-array 0.14.7": {
+      "name": "generic-array",
+      "version": "0.14.7",
+      "package_url": "https://github.com/fizyk20/generic-array.git",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/generic-array/0.14.7/download",
+          "sha256": "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "generic_array",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        },
+        {
+          "BuildScript": {
+            "crate_name": "build_script_build",
+            "crate_root": "build.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "generic_array",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "more_lengths",
+            "zeroize"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "generic-array 0.14.7",
+              "target": "build_script_build"
+            },
+            {
+              "id": "typenum 1.17.0",
+              "target": "typenum"
+            },
+            {
+              "id": "zeroize 1.7.0",
+              "target": "zeroize"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2015",
+        "version": "0.14.7"
+      },
+      "build_script_attrs": {
+        "data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "version_check 0.9.4",
+              "target": "version_check"
+            }
+          ],
+          "selects": {}
+        }
+      },
+      "license": "MIT",
+      "license_ids": [
+        "MIT"
+      ],
+      "license_file": "LICENSE"
+    },
+    "getrandom 0.2.14": {
+      "name": "getrandom",
+      "version": "0.2.14",
+      "package_url": "https://github.com/rust-random/getrandom",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/getrandom/0.2.14/download",
+          "sha256": "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "getrandom",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "getrandom",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "std"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "cfg-if 1.0.0",
+              "target": "cfg_if"
+            }
+          ],
+          "selects": {
+            "cfg(target_os = \"wasi\")": [
+              {
+                "id": "wasi 0.11.0+wasi-snapshot-preview1",
+                "target": "wasi"
+              }
+            ],
+            "cfg(unix)": [
+              {
+                "id": "libc 0.2.153",
+                "target": "libc"
+              }
+            ]
+          }
+        },
+        "edition": "2018",
+        "version": "0.2.14"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "ghash 0.5.1": {
+      "name": "ghash",
+      "version": "0.5.1",
+      "package_url": "https://github.com/RustCrypto/universal-hashes",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/ghash/0.5.1/download",
+          "sha256": "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "ghash",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "ghash",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "opaque-debug 0.3.1",
+              "target": "opaque_debug"
+            },
+            {
+              "id": "polyval 0.6.2",
+              "target": "polyval"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.5.1"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "group 0.13.0": {
+      "name": "group",
+      "version": "0.13.0",
+      "package_url": "https://github.com/zkcrypto/group",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/group/0.13.0/download",
+          "sha256": "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "group",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "group",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "ff 0.13.0",
+              "target": "ff"
+            },
+            {
+              "id": "rand_core 0.6.4",
+              "target": "rand_core"
+            },
+            {
+              "id": "subtle 2.5.0",
+              "target": "subtle"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.13.0"
+      },
+      "license": "MIT/Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "heck 0.4.1": {
+      "name": "heck",
+      "version": "0.4.1",
+      "package_url": "https://github.com/withoutboats/heck",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/heck/0.4.1/download",
+          "sha256": "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "heck",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "heck",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "default"
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "0.4.1"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "hkdf 0.12.4": {
+      "name": "hkdf",
+      "version": "0.12.4",
+      "package_url": "https://github.com/RustCrypto/KDFs/",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/hkdf/0.12.4/download",
+          "sha256": "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "hkdf",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "hkdf",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "hmac 0.12.1",
+              "target": "hmac"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "0.12.4"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "hmac 0.12.1": {
+      "name": "hmac",
+      "version": "0.12.1",
+      "package_url": "https://github.com/RustCrypto/MACs",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/hmac/0.12.1/download",
+          "sha256": "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "hmac",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "hmac",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "digest 0.10.7",
+              "target": "digest"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "0.12.1"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "inout 0.1.3": {
+      "name": "inout",
+      "version": "0.1.3",
+      "package_url": "https://github.com/RustCrypto/utils",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/inout/0.1.3/download",
+          "sha256": "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "inout",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "inout",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "block-padding"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "block-padding 0.3.3",
+              "target": "block_padding"
+            },
+            {
+              "id": "generic-array 0.14.7",
+              "target": "generic_array"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.1.3"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "lazy_static 1.4.0": {
+      "name": "lazy_static",
+      "version": "1.4.0",
+      "package_url": "https://github.com/rust-lang-nursery/lazy-static.rs",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/lazy_static/1.4.0/download",
+          "sha256": "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "lazy_static",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "lazy_static",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "spin",
+            "spin_no_std"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "spin 0.5.2",
+              "target": "spin"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2015",
+        "version": "1.4.0"
+      },
+      "license": "MIT/Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "libc 0.2.153": {
+      "name": "libc",
+      "version": "0.2.153",
+      "package_url": "https://github.com/rust-lang/libc",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/libc/0.2.153/download",
+          "sha256": "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "libc",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        },
+        {
+          "BuildScript": {
+            "crate_name": "build_script_build",
+            "crate_root": "build.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "libc",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [],
+          "selects": {
+            "aarch64-apple-darwin": [
+              "default",
+              "std"
+            ],
+            "aarch64-apple-ios": [
+              "default",
+              "std"
+            ],
+            "aarch64-apple-ios-sim": [
+              "default",
+              "std"
+            ],
+            "aarch64-linux-android": [
+              "default",
+              "std"
+            ],
+            "aarch64-unknown-linux-gnu": [
+              "default",
+              "std"
+            ],
+            "aarch64-unknown-nixos-gnu": [
+              "default",
+              "std"
+            ]
+          }
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "libc 0.2.153",
+              "target": "build_script_build"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2015",
+        "version": "0.2.153"
+      },
+      "build_script_attrs": {
+        "data_glob": [
+          "**"
+        ]
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "memchr 2.7.2": {
+      "name": "memchr",
+      "version": "2.7.2",
+      "package_url": "https://github.com/BurntSushi/memchr",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/memchr/2.7.2/download",
+          "sha256": "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "memchr",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "memchr",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2021",
+        "version": "2.7.2"
+      },
+      "license": "Unlicense OR MIT",
+      "license_ids": [
+        "MIT",
+        "Unlicense"
+      ],
+      "license_file": "LICENSE-MIT"
+    },
+    "minimal-lexical 0.2.1": {
+      "name": "minimal-lexical",
+      "version": "0.2.1",
+      "package_url": "https://github.com/Alexhuszagh/minimal-lexical",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/minimal-lexical/0.2.1/download",
+          "sha256": "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "minimal_lexical",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "minimal_lexical",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2018",
+        "version": "0.2.1"
+      },
+      "license": "MIT/Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "nom 7.1.3": {
+      "name": "nom",
+      "version": "7.1.3",
+      "package_url": "https://github.com/Geal/nom",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/nom/7.1.3/download",
+          "sha256": "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "nom",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "nom",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "memchr 2.7.2",
+              "target": "memchr"
+            },
+            {
+              "id": "minimal-lexical 0.2.1",
+              "target": "minimal_lexical"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "7.1.3"
+      },
+      "license": "MIT",
+      "license_ids": [
+        "MIT"
+      ],
+      "license_file": "LICENSE"
+    },
+    "opaque-debug 0.3.1": {
+      "name": "opaque-debug",
+      "version": "0.3.1",
+      "package_url": "https://github.com/RustCrypto/utils",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/opaque-debug/0.3.1/download",
+          "sha256": "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "opaque_debug",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "opaque_debug",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2018",
+        "version": "0.3.1"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "p256 0.13.2": {
+      "name": "p256",
+      "version": "0.13.2",
+      "package_url": "https://github.com/RustCrypto/elliptic-curves/tree/master/p256",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/p256/0.13.2/download",
+          "sha256": "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "p256",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "p256",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "arithmetic",
+            "ecdh"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "elliptic-curve 0.13.8",
+              "target": "elliptic_curve"
+            },
+            {
+              "id": "primeorder 0.13.6",
+              "target": "primeorder"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.13.2"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "place_holder 0.1.0": {
+      "name": "place_holder",
+      "version": "0.1.0",
+      "package_url": null,
+      "repository": null,
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "place_holder",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "place_holder",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "aead 0.5.2",
+              "target": "aead"
+            },
+            {
+              "id": "aes 0.8.4",
+              "target": "aes"
+            },
+            {
+              "id": "aes-gcm 0.10.3",
+              "target": "aes_gcm"
+            },
+            {
+              "id": "aes-gcm-siv 0.11.1",
+              "target": "aes_gcm_siv"
+            },
+            {
+              "id": "cbc 0.1.2",
+              "target": "cbc"
+            },
+            {
+              "id": "cfg-if 1.0.0",
+              "target": "cfg_if"
+            },
+            {
+              "id": "ctr 0.9.2",
+              "target": "ctr"
+            },
+            {
+              "id": "ed25519-dalek 2.1.1",
+              "target": "ed25519_dalek"
+            },
+            {
+              "id": "hkdf 0.12.4",
+              "target": "hkdf"
+            },
+            {
+              "id": "hmac 0.12.1",
+              "target": "hmac"
+            },
+            {
+              "id": "lazy_static 1.4.0",
+              "target": "lazy_static"
+            },
+            {
+              "id": "nom 7.1.3",
+              "target": "nom"
+            },
+            {
+              "id": "p256 0.13.2",
+              "target": "p256"
+            },
+            {
+              "id": "rand 0.8.5",
+              "target": "rand"
+            },
+            {
+              "id": "rand_chacha 0.3.1",
+              "target": "rand_chacha"
+            },
+            {
+              "id": "rand_core 0.6.4",
+              "target": "rand_core"
+            },
+            {
+              "id": "sec1 0.7.3",
+              "target": "sec1"
+            },
+            {
+              "id": "sha2 0.10.8",
+              "target": "sha2"
+            },
+            {
+              "id": "strum 0.25.0",
+              "target": "strum"
+            },
+            {
+              "id": "subtle 2.5.0",
+              "target": "subtle"
+            },
+            {
+              "id": "thiserror 1.0.58",
+              "target": "thiserror"
+            },
+            {
+              "id": "tinyvec 1.6.0",
+              "target": "tinyvec"
+            },
+            {
+              "id": "x25519-dalek 2.0.1",
+              "target": "x25519_dalek"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "proc_macro_deps": {
+          "common": [
+            {
+              "id": "strum_macros 0.25.3",
+              "target": "strum_macros"
+            }
+          ],
+          "selects": {}
+        },
+        "version": "0.1.0"
+      },
+      "license": null,
+      "license_ids": [],
+      "license_file": null
+    },
+    "platforms 3.4.0": {
+      "name": "platforms",
+      "version": "3.4.0",
+      "package_url": "https://github.com/rustsec/rustsec/tree/main/platforms",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/platforms/3.4.0/download",
+          "sha256": "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "platforms",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "platforms",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "default",
+            "std"
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "3.4.0"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "polyval 0.6.2": {
+      "name": "polyval",
+      "version": "0.6.2",
+      "package_url": "https://github.com/RustCrypto/universal-hashes",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/polyval/0.6.2/download",
+          "sha256": "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "polyval",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "polyval",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "cfg-if 1.0.0",
+              "target": "cfg_if"
+            },
+            {
+              "id": "opaque-debug 0.3.1",
+              "target": "opaque_debug"
+            },
+            {
+              "id": "universal-hash 0.5.1",
+              "target": "universal_hash"
+            }
+          ],
+          "selects": {
+            "cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))": [
+              {
+                "id": "cpufeatures 0.2.12",
+                "target": "cpufeatures"
+              }
+            ]
+          }
+        },
+        "edition": "2021",
+        "version": "0.6.2"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "ppv-lite86 0.2.17": {
+      "name": "ppv-lite86",
+      "version": "0.2.17",
+      "package_url": "https://github.com/cryptocorrosion/cryptocorrosion",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/ppv-lite86/0.2.17/download",
+          "sha256": "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "ppv_lite86",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "ppv_lite86",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "simd",
+            "std"
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "0.2.17"
+      },
+      "license": "MIT/Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "primeorder 0.13.6": {
+      "name": "primeorder",
+      "version": "0.13.6",
+      "package_url": "https://github.com/RustCrypto/elliptic-curves/tree/master/primeorder",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/primeorder/0.13.6/download",
+          "sha256": "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "primeorder",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "primeorder",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "elliptic-curve 0.13.8",
+              "target": "elliptic_curve"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.13.6"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "proc-macro2 1.0.81": {
+      "name": "proc-macro2",
+      "version": "1.0.81",
+      "package_url": "https://github.com/dtolnay/proc-macro2",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/proc-macro2/1.0.81/download",
+          "sha256": "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "proc_macro2",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        },
+        {
+          "BuildScript": {
+            "crate_name": "build_script_build",
+            "crate_root": "build.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "proc_macro2",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "default",
+            "proc-macro"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "proc-macro2 1.0.81",
+              "target": "build_script_build"
+            },
+            {
+              "id": "unicode-ident 1.0.12",
+              "target": "unicode_ident"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "1.0.81"
+      },
+      "build_script_attrs": {
+        "data_glob": [
+          "**"
+        ]
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "quote 1.0.36": {
+      "name": "quote",
+      "version": "1.0.36",
+      "package_url": "https://github.com/dtolnay/quote",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/quote/1.0.36/download",
+          "sha256": "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "quote",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "quote",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "default",
+            "proc-macro"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "proc-macro2 1.0.81",
+              "target": "proc_macro2"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "1.0.36"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "rand 0.8.5": {
+      "name": "rand",
+      "version": "0.8.5",
+      "package_url": "https://github.com/rust-random/rand",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/rand/0.8.5/download",
+          "sha256": "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "rand",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "rand",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "alloc",
+            "default",
+            "getrandom",
+            "libc",
+            "rand_chacha",
+            "std",
+            "std_rng"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "rand_chacha 0.3.1",
+              "target": "rand_chacha"
+            },
+            {
+              "id": "rand_core 0.6.4",
+              "target": "rand_core"
+            }
+          ],
+          "selects": {
+            "cfg(unix)": [
+              {
+                "id": "libc 0.2.153",
+                "target": "libc"
+              }
+            ]
+          }
+        },
+        "edition": "2018",
+        "version": "0.8.5"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "rand_chacha 0.3.1": {
+      "name": "rand_chacha",
+      "version": "0.3.1",
+      "package_url": "https://github.com/rust-random/rand",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/rand_chacha/0.3.1/download",
+          "sha256": "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "rand_chacha",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "rand_chacha",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "std"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "ppv-lite86 0.2.17",
+              "target": "ppv_lite86"
+            },
+            {
+              "id": "rand_core 0.6.4",
+              "target": "rand_core"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "0.3.1"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "rand_core 0.6.4": {
+      "name": "rand_core",
+      "version": "0.6.4",
+      "package_url": "https://github.com/rust-random/rand",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/rand_core/0.6.4/download",
+          "sha256": "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "rand_core",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "rand_core",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "alloc",
+            "getrandom",
+            "std"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "getrandom 0.2.14",
+              "target": "getrandom"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "0.6.4"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "rustc_version 0.4.0": {
+      "name": "rustc_version",
+      "version": "0.4.0",
+      "package_url": "https://github.com/Kimundi/rustc-version-rs",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/rustc_version/0.4.0/download",
+          "sha256": "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "rustc_version",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "rustc_version",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "semver 1.0.22",
+              "target": "semver"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "0.4.0"
+      },
+      "license": "MIT/Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "rustversion 1.0.15": {
+      "name": "rustversion",
+      "version": "1.0.15",
+      "package_url": "https://github.com/dtolnay/rustversion",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/rustversion/1.0.15/download",
+          "sha256": "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
+        }
+      },
+      "targets": [
+        {
+          "ProcMacro": {
+            "crate_name": "rustversion",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        },
+        {
+          "BuildScript": {
+            "crate_name": "build_script_build",
+            "crate_root": "build/build.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "rustversion",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "rustversion 1.0.15",
+              "target": "build_script_build"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "1.0.15"
+      },
+      "build_script_attrs": {
+        "data_glob": [
+          "**"
+        ]
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "sec1 0.7.3": {
+      "name": "sec1",
+      "version": "0.7.3",
+      "package_url": "https://github.com/RustCrypto/formats/tree/master/sec1",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/sec1/0.7.3/download",
+          "sha256": "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "sec1",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "sec1",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "default",
+            "der",
+            "point",
+            "subtle",
+            "zeroize"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "base16ct 0.2.0",
+              "target": "base16ct"
+            },
+            {
+              "id": "der 0.7.9",
+              "target": "der"
+            },
+            {
+              "id": "generic-array 0.14.7",
+              "target": "generic_array"
+            },
+            {
+              "id": "subtle 2.5.0",
+              "target": "subtle"
+            },
+            {
+              "id": "zeroize 1.7.0",
+              "target": "zeroize"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.7.3"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "semver 1.0.22": {
+      "name": "semver",
+      "version": "1.0.22",
+      "package_url": "https://github.com/dtolnay/semver",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/semver/1.0.22/download",
+          "sha256": "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "semver",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        },
+        {
+          "BuildScript": {
+            "crate_name": "build_script_build",
+            "crate_root": "build.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "semver",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "default",
+            "std"
+          ],
+          "selects": {}
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "semver 1.0.22",
+              "target": "build_script_build"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "1.0.22"
+      },
+      "build_script_attrs": {
+        "data_glob": [
+          "**"
+        ]
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "sha2 0.10.8": {
+      "name": "sha2",
+      "version": "0.10.8",
+      "package_url": "https://github.com/RustCrypto/hashes",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/sha2/0.10.8/download",
+          "sha256": "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "sha2",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "sha2",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "cfg-if 1.0.0",
+              "target": "cfg_if"
+            },
+            {
+              "id": "digest 0.10.7",
+              "target": "digest"
+            }
+          ],
+          "selects": {
+            "cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))": [
+              {
+                "id": "cpufeatures 0.2.12",
+                "target": "cpufeatures"
+              }
+            ]
+          }
+        },
+        "edition": "2018",
+        "version": "0.10.8"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "signature 2.2.0": {
+      "name": "signature",
+      "version": "2.2.0",
+      "package_url": "https://github.com/RustCrypto/traits/tree/master/signature",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/signature/2.2.0/download",
+          "sha256": "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "signature",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "signature",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2021",
+        "version": "2.2.0"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "spin 0.5.2": {
+      "name": "spin",
+      "version": "0.5.2",
+      "package_url": "https://github.com/mvdnes/spin-rs.git",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/spin/0.5.2/download",
+          "sha256": "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "spin",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "spin",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2015",
+        "version": "0.5.2"
+      },
+      "license": "MIT",
+      "license_ids": [
+        "MIT"
+      ],
+      "license_file": "LICENSE"
+    },
+    "strum 0.25.0": {
+      "name": "strum",
+      "version": "0.25.0",
+      "package_url": "https://github.com/Peternator7/strum",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/strum/0.25.0/download",
+          "sha256": "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "strum",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "strum",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2018",
+        "version": "0.25.0"
+      },
+      "license": "MIT",
+      "license_ids": [
+        "MIT"
+      ],
+      "license_file": "LICENSE"
+    },
+    "strum_macros 0.25.3": {
+      "name": "strum_macros",
+      "version": "0.25.3",
+      "package_url": "https://github.com/Peternator7/strum",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/strum_macros/0.25.3/download",
+          "sha256": "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
+        }
+      },
+      "targets": [
+        {
+          "ProcMacro": {
+            "crate_name": "strum_macros",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "strum_macros",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "heck 0.4.1",
+              "target": "heck"
+            },
+            {
+              "id": "proc-macro2 1.0.81",
+              "target": "proc_macro2"
+            },
+            {
+              "id": "quote 1.0.36",
+              "target": "quote"
+            },
+            {
+              "id": "syn 2.0.59",
+              "target": "syn"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "proc_macro_deps": {
+          "common": [
+            {
+              "id": "rustversion 1.0.15",
+              "target": "rustversion"
+            }
+          ],
+          "selects": {}
+        },
+        "version": "0.25.3"
+      },
+      "license": "MIT",
+      "license_ids": [
+        "MIT"
+      ],
+      "license_file": "LICENSE"
+    },
+    "subtle 2.5.0": {
+      "name": "subtle",
+      "version": "2.5.0",
+      "package_url": "https://github.com/dalek-cryptography/subtle",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/subtle/2.5.0/download",
+          "sha256": "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "subtle",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "subtle",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "i128"
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "2.5.0"
+      },
+      "license": "BSD-3-Clause",
+      "license_ids": [
+        "BSD-3-Clause"
+      ],
+      "license_file": "LICENSE"
+    },
+    "syn 2.0.59": {
+      "name": "syn",
+      "version": "2.0.59",
+      "package_url": "https://github.com/dtolnay/syn",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/syn/2.0.59/download",
+          "sha256": "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "syn",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "syn",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "clone-impls",
+            "default",
+            "derive",
+            "extra-traits",
+            "parsing",
+            "printing",
+            "proc-macro"
+          ],
+          "selects": {
+            "x86_64-apple-darwin": [
+              "full"
+            ],
+            "x86_64-apple-ios": [
+              "full"
+            ],
+            "x86_64-fuchsia": [
+              "full"
+            ],
+            "x86_64-linux-android": [
+              "full"
+            ],
+            "x86_64-pc-windows-msvc": [
+              "full"
+            ],
+            "x86_64-unknown-freebsd": [
+              "full"
+            ],
+            "x86_64-unknown-linux-gnu": [
+              "full"
+            ],
+            "x86_64-unknown-nixos-gnu": [
+              "full"
+            ],
+            "x86_64-unknown-none": [
+              "full"
+            ]
+          }
+        },
+        "deps": {
+          "common": [
+            {
+              "id": "proc-macro2 1.0.81",
+              "target": "proc_macro2"
+            },
+            {
+              "id": "quote 1.0.36",
+              "target": "quote"
+            },
+            {
+              "id": "unicode-ident 1.0.12",
+              "target": "unicode_ident"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "2.0.59"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "thiserror 1.0.58": {
+      "name": "thiserror",
+      "version": "1.0.58",
+      "package_url": "https://github.com/dtolnay/thiserror",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/thiserror/1.0.58/download",
+          "sha256": "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "thiserror",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        },
+        {
+          "BuildScript": {
+            "crate_name": "build_script_build",
+            "crate_root": "build.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "thiserror",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "thiserror 1.0.58",
+              "target": "build_script_build"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "proc_macro_deps": {
+          "common": [
+            {
+              "id": "thiserror-impl 1.0.58",
+              "target": "thiserror_impl"
+            }
+          ],
+          "selects": {}
+        },
+        "version": "1.0.58"
+      },
+      "build_script_attrs": {
+        "data_glob": [
+          "**"
+        ]
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "thiserror-impl 1.0.58": {
+      "name": "thiserror-impl",
+      "version": "1.0.58",
+      "package_url": "https://github.com/dtolnay/thiserror",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/thiserror-impl/1.0.58/download",
+          "sha256": "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
+        }
+      },
+      "targets": [
+        {
+          "ProcMacro": {
+            "crate_name": "thiserror_impl",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "thiserror_impl",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "proc-macro2 1.0.81",
+              "target": "proc_macro2"
+            },
+            {
+              "id": "quote 1.0.36",
+              "target": "quote"
+            },
+            {
+              "id": "syn 2.0.59",
+              "target": "syn"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "1.0.58"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "tinyvec 1.6.0": {
+      "name": "tinyvec",
+      "version": "1.6.0",
+      "package_url": "https://github.com/Lokathor/tinyvec",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/tinyvec/1.6.0/download",
+          "sha256": "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "tinyvec",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "tinyvec",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "crate_features": {
+          "common": [
+            "default",
+            "rustc_1_40",
+            "rustc_1_55"
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "1.6.0"
+      },
+      "license": "Zlib OR Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT",
+        "Zlib"
+      ],
+      "license_file": "LICENSE-APACHE.md"
+    },
+    "typenum 1.17.0": {
+      "name": "typenum",
+      "version": "1.17.0",
+      "package_url": "https://github.com/paholg/typenum",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/typenum/1.17.0/download",
+          "sha256": "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "typenum",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        },
+        {
+          "BuildScript": {
+            "crate_name": "build_script_main",
+            "crate_root": "build/main.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "typenum",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "typenum 1.17.0",
+              "target": "build_script_main"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2018",
+        "version": "1.17.0"
+      },
+      "build_script_attrs": {
+        "data_glob": [
+          "**"
+        ]
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE"
+    },
+    "unicode-ident 1.0.12": {
+      "name": "unicode-ident",
+      "version": "1.0.12",
+      "package_url": "https://github.com/dtolnay/unicode-ident",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/unicode-ident/1.0.12/download",
+          "sha256": "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "unicode_ident",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "unicode_ident",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2018",
+        "version": "1.0.12"
+      },
+      "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT",
+        "Unicode-DFS-2016"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "universal-hash 0.5.1": {
+      "name": "universal-hash",
+      "version": "0.5.1",
+      "package_url": "https://github.com/RustCrypto/traits",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/universal-hash/0.5.1/download",
+          "sha256": "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "universal_hash",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "universal_hash",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "crypto-common 0.1.6",
+              "target": "crypto_common"
+            },
+            {
+              "id": "subtle 2.5.0",
+              "target": "subtle"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "0.5.1"
+      },
+      "license": "MIT OR Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "version_check 0.9.4": {
+      "name": "version_check",
+      "version": "0.9.4",
+      "package_url": "https://github.com/SergioBenitez/version_check",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/version_check/0.9.4/download",
+          "sha256": "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "version_check",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "version_check",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2015",
+        "version": "0.9.4"
+      },
+      "license": "MIT/Apache-2.0",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "wasi 0.11.0+wasi-snapshot-preview1": {
+      "name": "wasi",
+      "version": "0.11.0+wasi-snapshot-preview1",
+      "package_url": "https://github.com/bytecodealliance/wasi",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/wasi/0.11.0+wasi-snapshot-preview1/download",
+          "sha256": "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "wasi",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "wasi",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2018",
+        "version": "0.11.0+wasi-snapshot-preview1"
+      },
+      "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    },
+    "x25519-dalek 2.0.1": {
+      "name": "x25519-dalek",
+      "version": "2.0.1",
+      "package_url": "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/x25519-dalek",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/x25519-dalek/2.0.1/download",
+          "sha256": "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "x25519_dalek",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "x25519_dalek",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "deps": {
+          "common": [
+            {
+              "id": "curve25519-dalek 4.1.2",
+              "target": "curve25519_dalek"
+            },
+            {
+              "id": "rand_core 0.6.4",
+              "target": "rand_core"
+            }
+          ],
+          "selects": {}
+        },
+        "edition": "2021",
+        "version": "2.0.1"
+      },
+      "license": "BSD-3-Clause",
+      "license_ids": [
+        "BSD-3-Clause"
+      ],
+      "license_file": "LICENSE"
+    },
+    "zeroize 1.7.0": {
+      "name": "zeroize",
+      "version": "1.7.0",
+      "package_url": "https://github.com/RustCrypto/utils/tree/master/zeroize",
+      "repository": {
+        "Http": {
+          "url": "https://static.crates.io/crates/zeroize/1.7.0/download",
+          "sha256": "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+        }
+      },
+      "targets": [
+        {
+          "Library": {
+            "crate_name": "zeroize",
+            "crate_root": "src/lib.rs",
+            "srcs": {
+              "allow_empty": false,
+              "include": [
+                "**/*.rs"
+              ]
+            }
+          }
+        }
+      ],
+      "library_target_name": "zeroize",
+      "common_attrs": {
+        "compile_data_glob": [
+          "**"
+        ],
+        "edition": "2021",
+        "version": "1.7.0"
+      },
+      "license": "Apache-2.0 OR MIT",
+      "license_ids": [
+        "Apache-2.0",
+        "MIT"
+      ],
+      "license_file": "LICENSE-APACHE"
+    }
+  },
+  "binary_crates": [],
+  "workspace_members": {
+    "place_holder 0.1.0": ""
+  },
+  "conditions": {
+    "aarch64-apple-darwin": [
+      "aarch64-apple-darwin"
+    ],
+    "aarch64-apple-ios": [
+      "aarch64-apple-ios"
+    ],
+    "aarch64-apple-ios-sim": [
+      "aarch64-apple-ios-sim"
+    ],
+    "aarch64-fuchsia": [
+      "aarch64-fuchsia"
+    ],
+    "aarch64-linux-android": [
+      "aarch64-linux-android"
+    ],
+    "aarch64-pc-windows-msvc": [
+      "aarch64-pc-windows-msvc"
+    ],
+    "aarch64-unknown-linux-gnu": [
+      "aarch64-unknown-linux-gnu"
+    ],
+    "aarch64-unknown-nixos-gnu": [
+      "aarch64-unknown-nixos-gnu"
+    ],
+    "aarch64-unknown-nto-qnx710": [
+      "aarch64-unknown-nto-qnx710"
+    ],
+    "arm-unknown-linux-gnueabi": [
+      "arm-unknown-linux-gnueabi"
+    ],
+    "armv7-linux-androideabi": [
+      "armv7-linux-androideabi"
+    ],
+    "armv7-unknown-linux-gnueabi": [
+      "armv7-unknown-linux-gnueabi"
+    ],
+    "cfg(all(not(curve25519_dalek_backend = \"fiat\"), not(curve25519_dalek_backend = \"serial\"), target_arch = \"x86_64\"))": [
+      "x86_64-apple-darwin",
+      "x86_64-apple-ios",
+      "x86_64-fuchsia",
+      "x86_64-linux-android",
+      "x86_64-pc-windows-msvc",
+      "x86_64-unknown-freebsd",
+      "x86_64-unknown-linux-gnu",
+      "x86_64-unknown-nixos-gnu",
+      "x86_64-unknown-none"
+    ],
+    "cfg(all(target_arch = \"aarch64\", target_os = \"linux\"))": [
+      "aarch64-unknown-linux-gnu",
+      "aarch64-unknown-nixos-gnu"
+    ],
+    "cfg(all(target_arch = \"aarch64\", target_vendor = \"apple\"))": [
+      "aarch64-apple-darwin",
+      "aarch64-apple-ios",
+      "aarch64-apple-ios-sim"
+    ],
+    "cfg(all(target_arch = \"loongarch64\", target_os = \"linux\"))": [],
+    "cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))": [
+      "aarch64-apple-darwin",
+      "aarch64-apple-ios",
+      "aarch64-apple-ios-sim",
+      "aarch64-fuchsia",
+      "aarch64-linux-android",
+      "aarch64-pc-windows-msvc",
+      "aarch64-unknown-linux-gnu",
+      "aarch64-unknown-nixos-gnu",
+      "aarch64-unknown-nto-qnx710",
+      "i686-apple-darwin",
+      "i686-linux-android",
+      "i686-pc-windows-msvc",
+      "i686-unknown-freebsd",
+      "i686-unknown-linux-gnu",
+      "x86_64-apple-darwin",
+      "x86_64-apple-ios",
+      "x86_64-fuchsia",
+      "x86_64-linux-android",
+      "x86_64-pc-windows-msvc",
+      "x86_64-unknown-freebsd",
+      "x86_64-unknown-linux-gnu",
+      "x86_64-unknown-nixos-gnu",
+      "x86_64-unknown-none"
+    ],
+    "cfg(curve25519_dalek_backend = \"fiat\")": [],
+    "cfg(target_arch = \"x86_64\")": [
+      "x86_64-apple-darwin",
+      "x86_64-apple-ios",
+      "x86_64-fuchsia",
+      "x86_64-linux-android",
+      "x86_64-pc-windows-msvc",
+      "x86_64-unknown-freebsd",
+      "x86_64-unknown-linux-gnu",
+      "x86_64-unknown-nixos-gnu",
+      "x86_64-unknown-none"
+    ],
+    "cfg(target_os = \"wasi\")": [
+      "wasm32-wasi"
+    ],
+    "cfg(unix)": [
+      "aarch64-apple-darwin",
+      "aarch64-apple-ios",
+      "aarch64-apple-ios-sim",
+      "aarch64-fuchsia",
+      "aarch64-linux-android",
+      "aarch64-unknown-linux-gnu",
+      "aarch64-unknown-nixos-gnu",
+      "aarch64-unknown-nto-qnx710",
+      "arm-unknown-linux-gnueabi",
+      "armv7-linux-androideabi",
+      "armv7-unknown-linux-gnueabi",
+      "i686-apple-darwin",
+      "i686-linux-android",
+      "i686-unknown-freebsd",
+      "i686-unknown-linux-gnu",
+      "powerpc-unknown-linux-gnu",
+      "s390x-unknown-linux-gnu",
+      "x86_64-apple-darwin",
+      "x86_64-apple-ios",
+      "x86_64-fuchsia",
+      "x86_64-linux-android",
+      "x86_64-unknown-freebsd",
+      "x86_64-unknown-linux-gnu",
+      "x86_64-unknown-nixos-gnu"
+    ],
+    "i686-apple-darwin": [
+      "i686-apple-darwin"
+    ],
+    "i686-linux-android": [
+      "i686-linux-android"
+    ],
+    "i686-pc-windows-msvc": [
+      "i686-pc-windows-msvc"
+    ],
+    "i686-unknown-freebsd": [
+      "i686-unknown-freebsd"
+    ],
+    "i686-unknown-linux-gnu": [
+      "i686-unknown-linux-gnu"
+    ],
+    "powerpc-unknown-linux-gnu": [
+      "powerpc-unknown-linux-gnu"
+    ],
+    "riscv32imc-unknown-none-elf": [
+      "riscv32imc-unknown-none-elf"
+    ],
+    "riscv64gc-unknown-none-elf": [
+      "riscv64gc-unknown-none-elf"
+    ],
+    "s390x-unknown-linux-gnu": [
+      "s390x-unknown-linux-gnu"
+    ],
+    "thumbv7em-none-eabi": [
+      "thumbv7em-none-eabi"
+    ],
+    "thumbv8m.main-none-eabi": [
+      "thumbv8m.main-none-eabi"
+    ],
+    "wasm32-unknown-unknown": [
+      "wasm32-unknown-unknown"
+    ],
+    "wasm32-wasi": [
+      "wasm32-wasi"
+    ],
+    "x86_64-apple-darwin": [
+      "x86_64-apple-darwin"
+    ],
+    "x86_64-apple-ios": [
+      "x86_64-apple-ios"
+    ],
+    "x86_64-fuchsia": [
+      "x86_64-fuchsia"
+    ],
+    "x86_64-linux-android": [
+      "x86_64-linux-android"
+    ],
+    "x86_64-pc-windows-msvc": [
+      "x86_64-pc-windows-msvc"
+    ],
+    "x86_64-unknown-freebsd": [
+      "x86_64-unknown-freebsd"
+    ],
+    "x86_64-unknown-linux-gnu": [
+      "x86_64-unknown-linux-gnu"
+    ],
+    "x86_64-unknown-nixos-gnu": [
+      "x86_64-unknown-nixos-gnu"
+    ],
+    "x86_64-unknown-none": [
+      "x86_64-unknown-none"
+    ]
+  },
+  "direct_deps": [
+    "aead 0.5.2",
+    "aes 0.8.4",
+    "aes-gcm 0.10.3",
+    "aes-gcm-siv 0.11.1",
+    "cbc 0.1.2",
+    "cfg-if 1.0.0",
+    "ctr 0.9.2",
+    "ed25519-dalek 2.1.1",
+    "hkdf 0.12.4",
+    "hmac 0.12.1",
+    "lazy_static 1.4.0",
+    "nom 7.1.3",
+    "p256 0.13.2",
+    "rand 0.8.5",
+    "rand_chacha 0.3.1",
+    "rand_core 0.6.4",
+    "sec1 0.7.3",
+    "sha2 0.10.8",
+    "strum 0.25.0",
+    "strum_macros 0.25.3",
+    "subtle 2.5.0",
+    "thiserror 1.0.58",
+    "tinyvec 1.6.0",
+    "x25519-dalek 2.0.1"
+  ],
+  "direct_dev_deps": []
+}
diff --git a/bazel_placeholder/Cargo.lock b/bazel_placeholder/Cargo.lock
new file mode 100644
index 0000000..bed8428
--- /dev/null
+++ b/bazel_placeholder/Cargo.lock
@@ -0,0 +1,664 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aead"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+dependencies = [
+ "crypto-common",
+ "generic-array",
+]
+
+[[package]]
+name = "aes"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
+name = "aes-gcm"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
+[[package]]
+name = "aes-gcm-siv"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "polyval",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "base16ct"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "block-padding"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "cbc"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crypto-bigint"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
+dependencies = [
+ "generic-array",
+ "rand_core",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "rand_core",
+ "typenum",
+]
+
+[[package]]
+name = "ctr"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "4.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest",
+ "fiat-crypto",
+ "platforms",
+ "rustc_version",
+ "subtle",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "der"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
+dependencies = [
+ "const-oid",
+ "zeroize",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "rand_core",
+ "sha2",
+ "subtle",
+]
+
+[[package]]
+name = "elliptic-curve"
+version = "0.13.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
+dependencies = [
+ "base16ct",
+ "crypto-bigint",
+ "digest",
+ "ff",
+ "generic-array",
+ "group",
+ "hkdf",
+ "rand_core",
+ "sec1",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "ff"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
+dependencies = [
+ "rand_core",
+ "subtle",
+]
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f"
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+ "zeroize",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "ghash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
+[[package]]
+name = "group"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
+dependencies = [
+ "ff",
+ "rand_core",
+ "subtle",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hkdf"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
+dependencies = [
+ "hmac",
+]
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "inout"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+dependencies = [
+ "block-padding",
+ "generic-array",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+dependencies = [
+ "spin",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "opaque-debug"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
+
+[[package]]
+name = "p256"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
+dependencies = [
+ "elliptic-curve",
+ "primeorder",
+]
+
+[[package]]
+name = "place_holder"
+version = "0.1.0"
+dependencies = [
+ "aead",
+ "aes",
+ "aes-gcm",
+ "aes-gcm-siv",
+ "cbc",
+ "cfg-if",
+ "ctr",
+ "ed25519-dalek",
+ "hkdf",
+ "hmac",
+ "lazy_static",
+ "nom",
+ "p256",
+ "rand",
+ "rand_chacha",
+ "rand_core",
+ "sec1",
+ "sha2",
+ "strum",
+ "strum_macros",
+ "subtle",
+ "thiserror",
+ "tinyvec",
+ "x25519-dalek",
+]
+
+[[package]]
+name = "platforms"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7"
+
+[[package]]
+name = "polyval"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "opaque-debug",
+ "universal-hash",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "primeorder"
+version = "0.13.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
+dependencies = [
+ "elliptic-curve",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
+
+[[package]]
+name = "sec1"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
+dependencies = [
+ "base16ct",
+ "der",
+ "generic-array",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "strum"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
+
+[[package]]
+name = "strum_macros"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn",
+]
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
+[[package]]
+name = "syn"
+version = "2.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "universal-hash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+dependencies = [
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "x25519-dalek"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
+dependencies = [
+ "curve25519-dalek",
+ "rand_core",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
diff --git a/bazel_placeholder/Cargo.toml b/bazel_placeholder/Cargo.toml
new file mode 100644
index 0000000..e7ee82a
--- /dev/null
+++ b/bazel_placeholder/Cargo.toml
@@ -0,0 +1,32 @@
+[package]
+name = "place_holder"
+version = "0.1.0"
+edition = "2021"
+publish = false
+rust-version = "1.77.1"
+
+[dependencies]
+lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
+strum = { version = "0.25.0", default-features = false }
+strum_macros = { version = "0.25.3", default-features = false }
+thiserror = "1.0.51"
+nom = { version = "7.1.3", default-features = false }
+tinyvec = { version = "1.6.0", features = ["rustc_1_55"] }
+cfg-if = "1.0.0"
+aead = { version = "0.5.1", features = ["alloc"] }
+aes = "0.8.3"
+aes-gcm-siv = { version = "0.11.1", features = ["aes"], default-features = false }
+aes-gcm = { version = "0.10.3", features = ["aes"], default-features = false }
+rand = { version = "0.8.5" }
+rand_core = { version = "0.6.4", features = ["getrandom"] }
+rand_chacha = { version = "0.3.1", default-features = false }
+cbc = { version = "0.1.2", features = ["block-padding", "alloc"], default-features = false }
+ctr = "0.9.2"
+ed25519-dalek = { version = "2.1.0", features = ["rand_core"], default-features = false }
+hkdf = "0.12.3"
+hmac = "0.12.1"
+p256 = { version = "0.13.2", features = ["ecdh"], default-features = false }
+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
diff --git a/bazel_placeholder/src/lib.rs b/bazel_placeholder/src/lib.rs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bazel_placeholder/src/lib.rs
diff --git a/cmd-runner/Cargo.lock b/cmd-runner/Cargo.lock
deleted file mode 100644
index 4dadf2e..0000000
--- a/cmd-runner/Cargo.lock
+++ /dev/null
@@ -1,30 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "anyhow"
-version = "1.0.75"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
-
-[[package]]
-name = "cmd-runner"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "owo-colors",
- "shell-escape",
-]
-
-[[package]]
-name = "owo-colors"
-version = "3.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
-
-[[package]]
-name = "shell-escape"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
diff --git a/cmd-runner/Cargo.toml b/cmd-runner/Cargo.toml
deleted file mode 100644
index e79317e..0000000
--- a/cmd-runner/Cargo.toml
+++ /dev/null
@@ -1,11 +0,0 @@
-[package]
-name = "cmd-runner"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-[dependencies]
-anyhow = "1.0.64"
-shell-escape = "0.1.5"
-owo-colors = "3.5.0"
-
diff --git a/common/Cargo.lock b/common/Cargo.lock
new file mode 100644
index 0000000..6a98fd6
--- /dev/null
+++ b/common/Cargo.lock
@@ -0,0 +1,1482 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anes"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
+
+[[package]]
+name = "anstream"
+version = "0.6.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
+
+[[package]]
+name = "arbitrary"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+
+[[package]]
+name = "autocfg"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bitflags"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+
+[[package]]
+name = "bstr"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "build_scripts"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "clap",
+ "cmd_runner",
+ "file-header",
+ "globset",
+ "log",
+ "xshell",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
+
+[[package]]
+name = "bytes"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+
+[[package]]
+name = "cast"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
+
+[[package]]
+name = "cc"
+version = "1.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
+dependencies = [
+ "jobserver",
+ "libc",
+]
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "ciborium"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
+dependencies = [
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
+dependencies = [
+ "ciborium-io",
+ "half",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+
+[[package]]
+name = "cmd_runner"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "clap",
+ "file-header",
+ "globset",
+ "log",
+ "owo-colors",
+ "shell-escape",
+ "xshell",
+]
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "combine"
+version = "4.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "criterion"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
+dependencies = [
+ "anes",
+ "cast",
+ "ciborium",
+ "clap",
+ "criterion-plot",
+ "is-terminal",
+ "itertools",
+ "num-traits",
+ "once_cell",
+ "oorandom",
+ "plotters",
+ "rayon",
+ "regex",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "tinytemplate",
+ "walkdir",
+]
+
+[[package]]
+name = "criterion-plot"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
+dependencies = [
+ "cast",
+ "itertools",
+]
+
+[[package]]
+name = "crossbeam"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-epoch",
+ "crossbeam-queue",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
+
+[[package]]
+name = "either"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
+
+[[package]]
+name = "env_logger"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
+
+[[package]]
+name = "file-header"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5568149106e77ae33bc3a2c3ef3839cbe63ffa4a8dd4a81612a6f9dfdbc2e9f"
+dependencies = [
+ "crossbeam",
+ "lazy_static",
+ "license",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "getrandom"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "globset"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "log",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "half"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+]
+
+[[package]]
+name = "handle_map"
+version = "0.1.0"
+dependencies = [
+ "criterion",
+ "lazy_static",
+ "lock_adapter",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "java-locator"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90003f2fd9c52f212c21d8520f1128da0080bad6fff16b68fe6e7f2f0c3780c2"
+dependencies = [
+ "glob",
+ "lazy_static",
+]
+
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "java-locator",
+ "jni-sys",
+ "libloading",
+ "log",
+ "thiserror",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+dependencies = [
+ "spin 0.5.2",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+
+[[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",
+]
+
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
+[[package]]
+name = "license"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "778718185117620a06e95d2b1e57d50166b1d6bfad93c8abfc1b3344c863ad8c"
+dependencies = [
+ "reword",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+
+[[package]]
+name = "lock_adapter"
+version = "0.1.0"
+dependencies = [
+ "spin 0.9.8",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "oorandom"
+version = "11.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
+
+[[package]]
+name = "owo-colors"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
+
+[[package]]
+name = "plotters"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
+dependencies = [
+ "num-traits",
+ "plotters-backend",
+ "plotters-svg",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "plotters-backend"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
+
+[[package]]
+name = "plotters-svg"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
+dependencies = [
+ "plotters-backend",
+]
+
+[[package]]
+name = "pourover"
+version = "0.1.0"
+dependencies = [
+ "jni",
+ "pourover_macro",
+]
+
+[[package]]
+name = "pourover_macro"
+version = "0.1.0"
+dependencies = [
+ "jni",
+ "nom",
+ "pourover",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+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.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
+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.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[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"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+
+[[package]]
+name = "reword"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe272098dce9ed76b479995953f748d1851261390b08f8a0ff619c885a1f0765"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rusty-fork"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
+dependencies = [
+ "fnv",
+ "quick-error",
+ "tempfile",
+ "wait-timeout",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[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.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.115"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
+dependencies = [
+ "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"
+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"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "2.0.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tinytemplate"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "web-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[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.4",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+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]]
+name = "windows-targets"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.4",
+ "windows_aarch64_msvc 0.52.4",
+ "windows_i686_gnu 0.52.4",
+ "windows_i686_msvc 0.52.4",
+ "windows_x86_64_gnu 0.52.4",
+ "windows_x86_64_gnullvm 0.52.4",
+ "windows_x86_64_msvc 0.52.4",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
+
+[[package]]
+name = "xshell"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437"
+dependencies = [
+ "xshell-macros",
+]
+
+[[package]]
+name = "xshell-macros"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852"
+
+[[package]]
+name = "yansi"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
diff --git a/common/Cargo.toml b/common/Cargo.toml
new file mode 100644
index 0000000..9e8972b
--- /dev/null
+++ b/common/Cargo.toml
@@ -0,0 +1,84 @@
+[workspace]
+members = [
+    "build_scripts",
+    "cmd_runner",
+    "derive_fuzztest",
+    "derive_fuzztest/fuzz",
+    "derive_fuzztest_macro",
+    "handle_map",
+    "lock_adapter",
+    "pourover",
+    "pourover_macro",
+]
+default-members = ["build_scripts"]
+resolver = "2"
+
+[workspace.lints.rust]
+missing_docs = "deny"
+trivial_casts = "deny"
+trivial_numeric_casts = "deny"
+unsafe_code = "deny"
+unsafe_op_in_unsafe_fn = "deny"
+unused_extern_crates = "deny"
+unused_import_braces = "deny"
+unused_results = "deny"
+
+[workspace.lints.clippy]
+expect_used = "deny"
+indexing_slicing = "deny"
+panic = "deny"
+unwrap_used = "deny"
+
+[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" }
+pourover_macro = { path = "pourover_macro" }
+
+# from crates.io
+anyhow = "1.0.75"
+arbitrary = "1.3.2"
+clap = { version = "4.4.11", features = ["derive"] }
+criterion = { version = "0.5.1", features = ["html_reports"] }
+jni = "0.21.1"
+lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
+libfuzzer-sys = "0.4.7"
+nom = { version = "7.1.3", default-features = false }
+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"
+
+[workspace.package]
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[profile.test]
+# speed up test execution
+opt-level = 3
+
+[profile.bench]
+# Since xts, ldt, etc are in separate crates, use LTO to allow cross-crate inlining.
+# fat vs thin: thin compiles a lot faster, and doesn't seem any slower.
+lto = "thin"
+
+# build profile optimized for size
+[profile.release-min-size]
+inherits = "release"
+panic = "abort"
+codegen-units = 1
+lto = true
+# z optimizes for size
+opt-level = "z"
+strip = true
diff --git a/common/build_scripts/Cargo.toml b/common/build_scripts/Cargo.toml
new file mode 100644
index 0000000..8f9d217
--- /dev/null
+++ b/common/build_scripts/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "build_scripts"
+version.workspace = true
+edition.workspace = true
+publish.workspace = true
+rust-version = "1.71.0"
+
+[dependencies]
+anyhow.workspace = true
+clap.workspace = true
+xshell.workspace = true
+cmd_runner.workspace = true
+log = "0.4.21"
+file-header = "0.1.2"
+chrono = "0.4.37"
+globset = "0.4.14"
diff --git a/common/build_scripts/src/license.rs b/common/build_scripts/src/license.rs
new file mode 100644
index 0000000..113b7a0
--- /dev/null
+++ b/common/build_scripts/src/license.rs
@@ -0,0 +1,65 @@
+// 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 cmd_runner::license_checker::LicenseChecker;
+
+pub const LICENSE_CHECKER: LicenseChecker = LicenseChecker {
+    ignore: &[
+        "**/android/build/**",
+        "**/target/**",
+        "**/.idea/**",
+        "**/cmake-build/**",
+        "**/java/build/**",
+        "**/java/*/build/**",
+        "**/*.toml",
+        "**/*.md",
+        "**/*.lock",
+        "**/*.json",
+        "**/*.rsp",
+        "**/*.patch",
+        "**/*.dockerignore",
+        "**/*.apk",
+        "**/gradle/*",
+        "**/.gradle/*",
+        "**/.git*",
+        "**/*test*vectors.txt",
+        "**/auth_token.txt",
+        "**/*.mdb",
+        "**/.DS_Store",
+        "**/fuzz/corpus/**",
+        "**/.*.swp",
+        "**/*.vim",
+        "**/*.properties",
+        "**/third_party/**",
+        "**/*.png",
+        "**/*.ico",
+        "**/node_modules/**",
+        "**/.angular/**",
+        "**/.editorconfig",
+        "**/*.class",
+        "**/fuzz/artifacts/**",
+        "**/cmake-build-debug/**",
+        "**/tags",
+    ],
+};
+
+#[cfg(test)]
+mod tests {
+    use super::LICENSE_CHECKER;
+
+    #[test]
+    fn new_ignore_is_likely_buggy() {
+        LICENSE_CHECKER.check_new_ignore_is_likely_buggy();
+    }
+}
diff --git a/common/build_scripts/src/main.rs b/common/build_scripts/src/main.rs
new file mode 100644
index 0000000..53fd178
--- /dev/null
+++ b/common/build_scripts/src/main.rs
@@ -0,0 +1,66 @@
+// 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 std::path;
+
+use clap::Parser;
+use cmd_runner::{
+    cargo_workspace::{CargoOptions, CargoWorkspaceSubcommand, FormatterOptions},
+    license_checker::LicenseSubcommand,
+};
+use license::LICENSE_CHECKER;
+use xshell::Shell;
+
+mod license;
+
+#[derive(clap::Parser)]
+struct Cli {
+    #[clap(subcommand)]
+    subcommand: Subcommand,
+}
+
+#[derive(clap::Subcommand, Debug, Clone)]
+enum Subcommand {
+    VerifyCi {
+        #[command(flatten)]
+        cargo_options: CargoOptions,
+    },
+    #[command(flatten)]
+    CargoWorkspace(CargoWorkspaceSubcommand),
+    #[command(flatten)]
+    License(LicenseSubcommand),
+}
+
+fn main() -> anyhow::Result<()> {
+    let args = Cli::parse();
+    let root_dir = path::Path::new(
+        &std::env::var_os("CARGO_MANIFEST_DIR")
+            .expect("Must be run via Cargo to establish root directory"),
+    )
+    .parent()
+    .expect("Workspace directory should exist")
+    .to_path_buf();
+    let sh = Shell::new()?;
+    sh.change_dir(&root_dir);
+    match args.subcommand {
+        Subcommand::VerifyCi { cargo_options } => {
+            cargo_options.check_workspace(&sh, "common")?;
+            FormatterOptions { reformat: false }.check_format(&sh)?;
+            LICENSE_CHECKER.check(&root_dir)?;
+        }
+        Subcommand::CargoWorkspace(workspace) => workspace.run("common", &sh)?,
+        Subcommand::License(license) => license.run(&LICENSE_CHECKER, &root_dir)?,
+    }
+    Ok(())
+}
diff --git a/common/cmd_runner/Cargo.lock b/common/cmd_runner/Cargo.lock
new file mode 100644
index 0000000..3f3f5e1
--- /dev/null
+++ b/common/cmd_runner/Cargo.lock
@@ -0,0 +1,714 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+
+[[package]]
+name = "autocfg"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+
+[[package]]
+name = "bstr"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
+
+[[package]]
+name = "cc"
+version = "1.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-targets",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+
+[[package]]
+name = "cmd-runner"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "clap",
+ "file-header",
+ "globset",
+ "log",
+ "owo-colors",
+ "shell-escape",
+ "xshell",
+]
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "crossbeam"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-epoch",
+ "crossbeam-queue",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+
+[[package]]
+name = "file-header"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5568149106e77ae33bc3a2c3ef3839cbe63ffa4a8dd4a81612a6f9dfdbc2e9f"
+dependencies = [
+ "crossbeam",
+ "lazy_static",
+ "license",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "globset"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "log",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+
+[[package]]
+name = "license"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "778718185117620a06e95d2b1e57d50166b1d6bfad93c8abfc1b3344c863ad8c"
+dependencies = [
+ "reword",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "owo-colors"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+
+[[package]]
+name = "reword"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe272098dce9ed76b479995953f748d1851261390b08f8a0ff619c885a1f0765"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.115"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
+dependencies = [
+ "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 = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "2.0.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
+
+[[package]]
+name = "xshell"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437"
+dependencies = [
+ "xshell-macros",
+]
+
+[[package]]
+name = "xshell-macros"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852"
diff --git a/common/cmd_runner/Cargo.toml b/common/cmd_runner/Cargo.toml
new file mode 100644
index 0000000..435b29f
--- /dev/null
+++ b/common/cmd_runner/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "cmd_runner"
+version.workspace = true
+edition.workspace = true
+publish.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"
+globset = "0.4.14"
+
diff --git a/common/cmd_runner/src/cargo_workspace.rs b/common/cmd_runner/src/cargo_workspace.rs
new file mode 100644
index 0000000..2b6840e
--- /dev/null
+++ b/common/cmd_runner/src/cargo_workspace.rs
@@ -0,0 +1,106 @@
+// 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 std::ffi::OsStr;
+
+use xshell::cmd;
+
+#[derive(clap::Subcommand, Debug, Clone)]
+pub enum CargoWorkspaceSubcommand {
+    /// Checks test, clippy, and cargo deny in the workspace
+    CheckWorkspace(CargoOptions),
+    /// Checks the formatting of the workspace
+    CheckFormat(FormatterOptions),
+}
+
+impl CargoWorkspaceSubcommand {
+    pub fn run(&self, tag: &str, sh: &xshell::Shell) -> anyhow::Result<()> {
+        match self {
+            CargoWorkspaceSubcommand::CheckWorkspace(cargo_options) => {
+                cargo_options.check_workspace(sh, tag)
+            }
+            CargoWorkspaceSubcommand::CheckFormat(formatter_options) => {
+                formatter_options.check_format(sh)
+            }
+        }
+    }
+}
+
+#[derive(clap::Args, Debug, Clone, Default)]
+pub struct CargoOptions {
+    #[arg(long, help = "whether to run cargo with --locked")]
+    locked: bool,
+    #[arg(long, help = "gather coverage metrics")]
+    coverage: bool,
+}
+
+impl CargoOptions {
+    /// Run `cargo test` or `cargo llvm-cov` depending on the configured options.
+    pub fn test<'sh, S: AsRef<OsStr>>(
+        &self,
+        sh: &'sh xshell::Shell,
+        tag: &str,
+        args: impl IntoIterator<Item = S>,
+    ) -> xshell::Cmd<'sh> {
+        let locked = if self.locked { "--locked" } else { "" };
+        if self.coverage {
+            cmd!(
+                sh,
+                "cargo llvm-cov {locked} {args...} --lcov --output-path target/{tag}.info -- --color=always"
+            )
+        } else {
+            cmd!(sh, "cargo test {locked} {args...} -- --color=always")
+        }
+    }
+
+    /// Run the default set of checks on a cargo workspace
+    pub fn check_workspace(&self, sh: &xshell::Shell, tag: &str) -> anyhow::Result<()> {
+        self.test(sh, tag, ["--workspace"]).run()?;
+        cmd!(
+            sh,
+            "cargo clippy --all-targets --workspace -- --deny warnings"
+        )
+        .run()?;
+        cmd!(sh, "cargo deny --workspace check").run()?;
+        // ensure the docs are valid (cross-references to other code, etc)
+        cmd!(
+            sh,
+            "cargo doc --quiet --workspace --no-deps --document-private-items
+                --target-dir target/dist_docs/{tag}"
+        )
+        .env("RUSTDOCFLAGS", "--deny warnings")
+        .run()?;
+        Ok(())
+    }
+}
+
+#[derive(clap::Args, Debug, Clone, Default)]
+pub struct FormatterOptions {
+    #[arg(
+        long,
+        help = "reformat files files in the workspace with the code formatter"
+    )]
+    pub reformat: bool,
+}
+
+impl FormatterOptions {
+    pub fn check_format(&self, sh: &xshell::Shell) -> anyhow::Result<()> {
+        if self.reformat {
+            cmd!(sh, "cargo fmt").run()?;
+        } else {
+            cmd!(sh, "cargo fmt --check").run()?;
+        }
+        Ok(())
+    }
+}
diff --git a/cmd-runner/src/lib.rs b/common/cmd_runner/src/lib.rs
similarity index 98%
rename from cmd-runner/src/lib.rs
rename to common/cmd_runner/src/lib.rs
index fa2f1f4..8c45df8 100644
--- a/cmd-runner/src/lib.rs
+++ b/common/cmd_runner/src/lib.rs
@@ -16,6 +16,9 @@
 use owo_colors::OwoColorize as _;
 use std::{collections, env, ffi, io, io::BufRead, path, process, thread};
 
+pub mod cargo_workspace;
+pub mod license_checker;
+
 pub fn run_cmd_shell(
     dir: &path::Path,
     cmd: impl AsRef<ffi::OsStr>,
diff --git a/common/cmd_runner/src/license_checker.rs b/common/cmd_runner/src/license_checker.rs
new file mode 100644
index 0000000..19286bb
--- /dev/null
+++ b/common/cmd_runner/src/license_checker.rs
@@ -0,0 +1,105 @@
+// 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 chrono::Datelike;
+use file_header::{check_headers_recursively, license::spdx::*};
+use std::path;
+
+#[derive(clap::Subcommand, Debug, Clone)]
+pub enum LicenseSubcommand {
+    /// Checks the workspace 3rd party crates and makes sure they have a valid license
+    CheckLicenseHeaders,
+    /// Generate new headers for any files that are missing them
+    AddLicenseHeaders,
+}
+
+impl LicenseSubcommand {
+    pub fn run(&self, checker: &LicenseChecker, root: &path::Path) -> anyhow::Result<()> {
+        match self {
+            LicenseSubcommand::CheckLicenseHeaders => checker.check(root)?,
+            LicenseSubcommand::AddLicenseHeaders => checker.add_missing(root)?,
+        }
+        Ok(())
+    }
+}
+
+pub struct LicenseChecker {
+    pub ignore: &'static [&'static str],
+}
+
+impl LicenseChecker {
+    pub fn check(&self, root: &path::Path) -> anyhow::Result<()> {
+        log::info!("Checking license headers");
+        let ignore = self.ignore_globset()?;
+        let results = check_headers_recursively(
+            root,
+            |p| !ignore.is_match(p),
+            APACHE_2_0.build_header(YearCopyrightOwnerValue::new(
+                u32::try_from(chrono::Utc::now().year())?,
+                "Google LLC".to_string(),
+            )),
+            4,
+        )?;
+
+        for path in results.no_header_files.iter() {
+            eprintln!("Header not present: {path:?}");
+        }
+
+        for path in results.binary_files.iter() {
+            eprintln!("Binary file: {path:?}");
+        }
+        if !results.binary_files.is_empty() {
+            eprintln!("Consider adding binary files to the ignore list in src/licence.rs.");
+        }
+
+        if results.has_failure() {
+            Err(anyhow::anyhow!("License header check failed"))
+        } else {
+            Ok(())
+        }
+    }
+
+    pub fn add_missing(&self, root: &path::Path) -> anyhow::Result<()> {
+        let ignore = self.ignore_globset()?;
+        for p in file_header::add_headers_recursively(
+            root,
+            |p| !ignore.is_match(p),
+            APACHE_2_0.build_header(YearCopyrightOwnerValue::new(
+                u32::try_from(chrono::Utc::now().year())?,
+                "Google LLC".to_string(),
+            )),
+        )? {
+            println!("Added header: {:?}", p);
+        }
+
+        Ok(())
+    }
+
+    fn ignore_globset(&self) -> Result<globset::GlobSet, globset::Error> {
+        let mut builder = globset::GlobSet::builder();
+        for lic in self.ignore {
+            builder.add(globset::Glob::new(lic)?);
+        }
+        builder.build()
+    }
+
+    pub fn check_new_ignore_is_likely_buggy(&self) {
+        for dir in self.ignore {
+            assert!(
+                dir.starts_with("**/"),
+                "Matching on the root filesystem is likely unintended"
+            );
+        }
+    }
+}
diff --git a/common/deny.toml b/common/deny.toml
new file mode 100644
index 0000000..bd3f18b
--- /dev/null
+++ b/common/deny.toml
@@ -0,0 +1,186 @@
+# This template contains all of the possible sections and their default values
+
+# Note that all fields that take a lint level have these possible values:
+# * deny - An error will be produced and the check will fail
+# * warn - A warning will be produced, but the check will not fail
+# * allow - No warning or error will be produced, though in some cases a note
+# will be
+
+# The values provided in this template are the default values that will be used
+# when any section or field is not specified in your own configuration
+
+# If 1 or more target triples (and optionally, target_features) are specified,
+# only the specified targets will be checked when running `cargo deny check`.
+# This means, if a particular package is only ever used as a target specific
+# dependency, such as, for example, the `nix` crate only being used via the
+# `target_family = "unix"` configuration, that only having windows targets in
+# this list would mean the nix crate, as well as any of its exclusive
+# dependencies not shared by any other crates, would be ignored, as the target
+# list here is effectively saying which targets you are building for.
+graph.targets = [
+    # The triple can be any string, but only the target triples built in to
+    # rustc (as of 1.40) can be checked against actual config expressions
+    #{ triple = "x86_64-unknown-linux-musl" },
+    # You can also specify which target_features you promise are enabled for a
+    # particular target. target_features are currently not validated against
+    # the actual valid features supported by the target architecture.
+    #{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
+]
+
+# This section is considered when running `cargo deny check advisories`
+# More documentation for the advisories section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
+[advisories]
+version = 2
+# The path where the advisory database is cloned/fetched into
+db-path = "~/.cargo/advisory-db"
+# The url(s) of the advisory databases to use
+db-urls = ["https://github.com/rustsec/advisory-db"]
+# The lint level for crates that have been yanked from their source registry
+yanked = "warn"
+# A list of advisory IDs to ignore. Note that ignored advisories will still
+# output a note when they are encountered.
+ignore = [
+    # comment explaining why we have to ignore it
+    # "RUSTSEC-FOO",
+]
+# Threshold for security vulnerabilities, any vulnerability with a CVSS score
+# lower than the range specified will be ignored. Note that ignored advisories
+# will still output a note when they are encountered.
+# * None - CVSS Score 0.0
+# * Low - CVSS Score 0.1 - 3.9
+# * Medium - CVSS Score 4.0 - 6.9
+# * High - CVSS Score 7.0 - 8.9
+# * Critical - CVSS Score 9.0 - 10.0
+#severity-threshold =
+
+# If this is true, then cargo deny will use the git executable to fetch advisory database.
+# If this is false, then it uses a built-in git library.
+# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
+# See Git Authentication for more information about setting up git authentication.
+#git-fetch-with-cli = true
+
+# This section is considered when running `cargo deny check licenses`
+# More documentation for the licenses section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
+[licenses]
+version = 2
+# List of explicitly allowed licenses
+# See https://spdx.org/licenses/ for list of possible licenses
+# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
+allow = [
+    "MIT",
+    "Apache-2.0",
+    "Unicode-DFS-2016",
+    "ISC",
+]
+# The confidence threshold for detecting a license from license text.
+# The higher the value, the more closely the license text must be to the
+# canonical license text of a valid SPDX license file.
+# [possible values: any between 0.0 and 1.0].
+confidence-threshold = 0.8
+# Allow 1 or more licenses on a per-crate basis, so that particular licenses
+# aren't accepted for every possible crate as with the normal allow list
+exceptions = [
+    # Each entry is the crate and version constraint, and its specific allow
+    # list
+
+]
+
+# Some crates don't have (easily) machine readable licensing information,
+# adding a clarification entry for it allows you to manually specify the
+# licensing information
+#[[licenses.clarify]]
+# The name of the crate the clarification applies to
+#name = "ring"
+# The optional version constraint for the crate
+#version = "*"
+# The SPDX expression for the license requirements of the crate
+#expression = "MIT AND ISC AND OpenSSL"
+# One or more files in the crate's source used as the "source of truth" for
+# the license expression. If the contents match, the clarification will be used
+# when running the license check, otherwise the clarification will be ignored
+# and the crate will be checked normally, which may produce warnings or errors
+# depending on the rest of your configuration
+#license-files = [
+    # Each entry is a crate relative path, and the (opaque) hash of its contents
+    #{ path = "LICENSE", hash = 0xbd0eed23 }
+#]
+
+[[licenses.clarify]]
+name = "ring"
+version = "*"
+expression = "MIT AND ISC AND OpenSSL"
+license-files = [
+    # Each entry is a crate relative path, and the (opaque) hash of its contents
+    { path = "LICENSE", hash = 0xbd0eed23 }
+]
+
+[licenses.private]
+# If true, ignores workspace crates that aren't published, or are only
+# published to private registries.
+# To see how to mark a crate as unpublished (to the official registry),
+# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
+ignore = true
+# One or more private registries that you might publish crates to, if a crate
+# is only published to private registries, and ignore is true, the crate will
+# not have its license(s) checked
+registries = [
+    #"https://sekretz.com/registry
+]
+
+# This section is considered when running `cargo deny check bans`.
+# More documentation about the 'bans' section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
+[bans]
+# Lint level for when multiple versions of the same crate are detected
+multiple-versions = "allow"
+# Lint level for when a crate version requirement is `*`
+wildcards = "allow"
+# The graph highlighting used when creating dotgraphs for crates
+# with multiple versions
+# * lowest-version - The path to the lowest versioned duplicate is highlighted
+# * simplest-path - The path to the version with the fewest edges is highlighted
+# * all - Both lowest-version and simplest-path are used
+highlight = "all"
+# List of crates that are allowed. Use with care!
+allow = [
+    #{ name = "ansi_term", version = "=0.11.0" },
+]
+# List of crates to deny
+deny = [
+    # Each entry the name of a crate and a version range. If version is
+    # not specified, all versions will be matched.
+    #{ name = "ansi_term", version = "=0.11.0" },
+    #
+    # Wrapper crates can optionally be specified to allow the crate when it
+    # is a direct dependency of the otherwise banned crate
+    #{ name = "ansi_term", version = "=0.11.0", wrappers = [] },
+]
+# Certain crates/versions that will be skipped when doing duplicate detection.
+skip = [
+    #{ name = "ansi_term", version = "=0.11.0" },
+]
+# Similarly to `skip` allows you to skip certain crates during duplicate
+# detection. Unlike skip, it also includes the entire tree of transitive
+# dependencies starting at the specified crate, up to a certain depth, which is
+# by default infinite
+skip-tree = [
+    #{ name = "ansi_term", version = "=0.11.0", depth = 20 },
+]
+
+# This section is considered when running `cargo deny check sources`.
+# More documentation about the 'sources' section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
+[sources]
+# Lint level for what to happen when a crate from a crate registry that is not
+# in the allow list is encountered
+unknown-registry = "warn"
+# Lint level for what to happen when a crate from a git repository that is not
+# in the allow list is encountered
+unknown-git = "warn"
+# List of URLs for allowed crate registries. Defaults to the crates.io index
+# if not specified. If it is specified but empty, no registries are allowed.
+allow-registry = ["https://github.com/rust-lang/crates.io-index"]
+# List of URLs for allowed Git repositories
+allow-git = []
\ No newline at end of file
diff --git a/common/derive_fuzztest/Cargo.toml b/common/derive_fuzztest/Cargo.toml
new file mode 100644
index 0000000..6a22ce5
--- /dev/null
+++ b/common/derive_fuzztest/Cargo.toml
@@ -0,0 +1,24 @@
+[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
new file mode 100644
index 0000000..b94a8f4
--- /dev/null
+++ b/common/derive_fuzztest/fuzz/.gitignore
@@ -0,0 +1 @@
+/corpus
\ No newline at end of file
diff --git a/common/derive_fuzztest/fuzz/Cargo.toml b/common/derive_fuzztest/fuzz/Cargo.toml
new file mode 100644
index 0000000..1f493b6
--- /dev/null
+++ b/common/derive_fuzztest/fuzz/Cargo.toml
@@ -0,0 +1,16 @@
+[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
new file mode 100644
index 0000000..8f8d77b
--- /dev/null
+++ b/common/derive_fuzztest/fuzz/src/bin/arbitrary.rs
@@ -0,0 +1,32 @@
+// 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/nearby/util/pourover_macro_core/src/lib.rs b/common/derive_fuzztest/fuzz/src/bin/integer_add.rs
similarity index 74%
copy from nearby/util/pourover_macro_core/src/lib.rs
copy to common/derive_fuzztest/fuzz/src/bin/integer_add.rs
index ae06345..38b8172 100644
--- a/nearby/util/pourover_macro_core/src/lib.rs
+++ b/common/derive_fuzztest/fuzz/src/bin/integer_add.rs
@@ -12,5 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-mod jni_method;
-pub use jni_method::jni_method;
+#![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/src/lib.rs b/common/derive_fuzztest/src/lib.rs
new file mode 100644
index 0000000..c0dd11c
--- /dev/null
+++ b/common/derive_fuzztest/src/lib.rs
@@ -0,0 +1,141 @@
+// 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
new file mode 100644
index 0000000..37b480a
--- /dev/null
+++ b/common/derive_fuzztest_macro/Cargo.toml
@@ -0,0 +1,23 @@
+[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
new file mode 100644
index 0000000..beb692a
--- /dev/null
+++ b/common/derive_fuzztest_macro/src/lib.rs
@@ -0,0 +1,463 @@
+// 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/nearby/util/handle_map/Cargo.toml b/common/handle_map/Cargo.toml
similarity index 100%
rename from nearby/util/handle_map/Cargo.toml
rename to common/handle_map/Cargo.toml
diff --git a/nearby/util/handle_map/benches/benches.rs b/common/handle_map/benches/benches.rs
similarity index 91%
rename from nearby/util/handle_map/benches/benches.rs
rename to common/handle_map/benches/benches.rs
index f1988ba..176a54e 100644
--- a/nearby/util/handle_map/benches/benches.rs
+++ b/common/handle_map/benches/benches.rs
@@ -36,7 +36,10 @@
 type BenchHandleMap = HandleMap<u8>;
 
 fn build_handle_map(num_shards: u8) -> BenchHandleMap {
-    let dimensions = HandleMapDimensions { num_shards, max_active_handles: MAX_ACTIVE_HANDLES };
+    let dimensions = HandleMapDimensions {
+        num_shards,
+        max_active_handles: MAX_ACTIVE_HANDLES,
+    };
     HandleMap::with_dimensions(dimensions)
 }
 
@@ -97,12 +100,15 @@
     let handle_map = build_handle_map(8);
     let handle_map_ref = &handle_map;
     // Perform repeated allocation/deallocation pairs
-    c.bench_function("single-threaded allocate/deallocate pairs (empty init state)", |b| {
-        b.iter(|| {
-            let handle = handle_map_ref.allocate(|| black_box(0xDD)).unwrap();
-            handle_map_ref.deallocate(handle);
-        })
-    });
+    c.bench_function(
+        "single-threaded allocate/deallocate pairs (empty init state)",
+        |b| {
+            b.iter(|| {
+                let handle = handle_map_ref.allocate(|| black_box(0xDD)).unwrap();
+                handle_map_ref.deallocate(handle);
+            })
+        },
+    );
 }
 
 /// Benchmark for repeated allocation->deallocation starting
@@ -192,18 +198,38 @@
     num_writes: usize,
 }
 
-const READS_ONLY: ReadWriteCount = ReadWriteCount { num_reads: 4, num_writes: 0 };
+const READS_ONLY: ReadWriteCount = ReadWriteCount {
+    num_reads: 4,
+    num_writes: 0,
+};
 
-const READ_LEANING: ReadWriteCount = ReadWriteCount { num_reads: 3, num_writes: 1 };
+const READ_LEANING: ReadWriteCount = ReadWriteCount {
+    num_reads: 3,
+    num_writes: 1,
+};
 
-const BALANCED: ReadWriteCount = ReadWriteCount { num_reads: 2, num_writes: 2 };
+const BALANCED: ReadWriteCount = ReadWriteCount {
+    num_reads: 2,
+    num_writes: 2,
+};
 
-const WRITE_LEANING: ReadWriteCount = ReadWriteCount { num_reads: 1, num_writes: 3 };
+const WRITE_LEANING: ReadWriteCount = ReadWriteCount {
+    num_reads: 1,
+    num_writes: 3,
+};
 
-const WRITES_ONLY: ReadWriteCount = ReadWriteCount { num_reads: 0, num_writes: 4 };
+const WRITES_ONLY: ReadWriteCount = ReadWriteCount {
+    num_reads: 0,
+    num_writes: 4,
+};
 
-const READ_WRITE_COUNTS: [ReadWriteCount; 5] =
-    [READS_ONLY, READ_LEANING, BALANCED, WRITE_LEANING, WRITES_ONLY];
+const READ_WRITE_COUNTS: [ReadWriteCount; 5] = [
+    READS_ONLY,
+    READ_LEANING,
+    BALANCED,
+    WRITE_LEANING,
+    WRITES_ONLY,
+];
 
 /// Benchmarks a repeated allocate/[X writes]/[Y reads]/deallocate workflow across
 /// the default number of threads and shards.
diff --git a/nearby/util/handle_map/src/declare_handle_map.rs b/common/handle_map/src/declare_handle_map.rs
similarity index 97%
rename from nearby/util/handle_map/src/declare_handle_map.rs
rename to common/handle_map/src/declare_handle_map.rs
index 688ec18..89078fa 100644
--- a/nearby/util/handle_map/src/declare_handle_map.rs
+++ b/common/handle_map/src/declare_handle_map.rs
@@ -110,6 +110,10 @@
                 $crate::HandleMap::with_dimensions($map_dimension_provider);
             }
 
+            pub (crate) fn get_current_allocation_count() -> u32 {
+                GLOBAL_HANDLE_MAP.get_current_allocation_count()
+            }
+
             #[doc = ::core::concat!(
                         "A `#[repr(C)]` handle to a value of type `",
                         ::core::stringify!($wrapped_type), "`."
diff --git a/nearby/util/handle_map/src/guard.rs b/common/handle_map/src/guard.rs
similarity index 92%
rename from nearby/util/handle_map/src/guard.rs
rename to common/handle_map/src/guard.rs
index 0c8c9d7..67d9787 100644
--- a/nearby/util/handle_map/src/guard.rs
+++ b/common/handle_map/src/guard.rs
@@ -14,7 +14,7 @@
 
 use crate::Handle;
 use core::ops::{Deref, DerefMut};
-use lock_adapter::std::RwMapping;
+use lock_adapter::stdlib::RwMapping;
 use std::collections::HashMap;
 use std::marker::PhantomData;
 
@@ -23,7 +23,7 @@
 /// dropped, the underlying read lock on the associated
 /// shard will be dropped.
 pub struct ObjectReadGuardImpl<'a, T: 'a> {
-    pub(crate) guard: lock_adapter::std::MappedRwLockReadGuard<
+    pub(crate) guard: lock_adapter::stdlib::MappedRwLockReadGuard<
         'a,
         <Self as ObjectReadGuard>::Arg,
         <Self as ObjectReadGuard>::Ret,
@@ -81,7 +81,7 @@
 /// dropped, the underlying read-write lock on the associated
 /// shard will be dropped.
 pub struct ObjectReadWriteGuardImpl<'a, T: 'a> {
-    pub(crate) guard: lock_adapter::std::MappedRwLockWriteGuard<
+    pub(crate) guard: lock_adapter::stdlib::MappedRwLockWriteGuard<
         'a,
         <Self as ObjectReadWriteGuard>::Arg,
         <Self as ObjectReadWriteGuard>::Ret,
@@ -131,11 +131,13 @@
 
     fn map<'b>(&self, arg: &'b Self::Arg) -> &'b Self::Ret {
         #[allow(clippy::expect_used)]
-        arg.get(&self.handle).expect("Caller must verify that provided hande exists")
+        arg.get(&self.handle)
+            .expect("Caller must verify that provided hande exists")
     }
 
     fn map_mut<'b>(&self, arg: &'b mut Self::Arg) -> &'b mut Self::Ret {
         #[allow(clippy::expect_used)]
-        arg.get_mut(&self.handle).expect("Caller must verify that provided hande exists")
+        arg.get_mut(&self.handle)
+            .expect("Caller must verify that provided hande exists")
     }
 }
diff --git a/nearby/util/handle_map/src/lib.rs b/common/handle_map/src/lib.rs
similarity index 94%
rename from nearby/util/handle_map/src/lib.rs
rename to common/handle_map/src/lib.rs
index 22009c1..72bb78a 100644
--- a/nearby/util/handle_map/src/lib.rs
+++ b/common/handle_map/src/lib.rs
@@ -16,9 +16,7 @@
 //! a safer alternative to raw pointers for FFI interop.
 
 use core::fmt::Debug;
-use std::boxed::Box;
 use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
-use std::vec::Vec;
 
 pub mod declare_handle_map;
 mod guard;
@@ -159,21 +157,23 @@
         initial_value_provider: impl FnOnce() -> T,
     ) -> Result<Handle, HandleMapFullError> {
         let wrapped_value_provider = move || Ok(initial_value_provider());
-        self.try_allocate::<core::convert::Infallible>(wrapped_value_provider).map_err(
-            |e| match e {
+        self.try_allocate::<core::convert::Infallible>(wrapped_value_provider)
+            .map_err(|e| match e {
                 HandleMapTryAllocateError::ValueProviderFailed(never) => match never {},
                 HandleMapTryAllocateError::HandleMapFull => HandleMapFullError,
-            },
-        )
+            })
     }
 
     /// Attempts to allocate a new object within the given handle-map, returning
-    /// a handle to the location it was stored at. This operation
-    /// may fail if attempting to allocate over the `dimensions.max_active_handles`
-    /// limit imposed on the handle-map, in which case this method
-    /// will return a `HandleMapTryAllocateError::HandleMapFull`,
-    /// or if the passed initial-value provider fails, in which case this
-    /// will return the error wrapped in `HandleMapTryAllocateError::ValueProviderFailed`.
+    /// a handle to the location it was stored at.
+    ///
+    /// This operation may fail if attempting to allocate over the `dimensions.max_active_handles`
+    /// limit imposed on the handle-map, in which case this method will return a
+    /// `HandleMapTryAllocateError::HandleMapFull` and the given `initial_value_provider` will not
+    /// be run.
+    ///
+    /// The passed initial-value provider may also fail, in which case this will return the error
+    /// wrapped in `HandleMapTryAllocateError::ValueProviderFailed`.
     ///
     /// If your initial-value provider is infallible, see [`Self::allocate`] instead.
     pub fn try_allocate<E: Debug>(
@@ -257,10 +257,8 @@
     }
 
     /// Gets the actual number of elements stored in the entire map.
-    /// Only suitable for single-threaded sections of tests.
-    #[cfg(test)]
-    pub(crate) fn len(&self) -> usize {
-        self.handle_map_shards.iter().map(|s| s.len()).sum()
+    pub fn get_current_allocation_count(&self) -> u32 {
+        self.outstanding_allocations_counter.load(Ordering::Relaxed)
     }
 
     /// Sets the new-handle-id counter to the given value.
diff --git a/nearby/util/handle_map/src/shard.rs b/common/handle_map/src/shard.rs
similarity index 91%
rename from nearby/util/handle_map/src/shard.rs
rename to common/handle_map/src/shard.rs
index e5aad2f..c7cf509 100644
--- a/nearby/util/handle_map/src/shard.rs
+++ b/common/handle_map/src/shard.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 use core::ops::{Deref, DerefMut};
-use lock_adapter::std::{RwLock, RwLockReadGuard, RwLockWriteGuard};
+use lock_adapter::stdlib::{RwLock, RwLockReadGuard, RwLockWriteGuard};
 use lock_adapter::RwLock as _;
 use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::collections::HashMap;
@@ -52,7 +52,9 @@
 
 impl<T: Send + Sync> Default for HandleMapShard<T> {
     fn default() -> Self {
-        Self { data: RwLock::new(HashMap::new()) }
+        Self {
+            data: RwLock::new(HashMap::new()),
+        }
     }
 }
 
@@ -63,9 +65,14 @@
         if read_only_map_ref.contains_key(&handle) {
             let object_read_guard = ShardReadGuard::<T>::map(
                 map_read_guard,
-                ObjectReadGuardMapping { handle, _marker: PhantomData },
+                ObjectReadGuardMapping {
+                    handle,
+                    _marker: PhantomData,
+                },
             );
-            Ok(ObjectReadGuardImpl { guard: object_read_guard })
+            Ok(ObjectReadGuardImpl {
+                guard: object_read_guard,
+            })
         } else {
             // Auto-drop the read guard, and return an error
             Err(HandleNotPresentError)
@@ -102,9 +109,14 @@
         // Expose only the pointed-to object with a mapped read-write guard
         let object_read_write_guard = ShardReadWriteGuard::<T>::map(
             map_read_write_guard,
-            ObjectReadWriteGuardMapping { handle, _marker: PhantomData },
+            ObjectReadWriteGuardMapping {
+                handle,
+                _marker: PhantomData,
+            },
         );
-        Ok(ObjectReadWriteGuardImpl { guard: object_read_write_guard })
+        Ok(ObjectReadWriteGuardImpl {
+            guard: object_read_write_guard,
+        })
     }
 
     pub fn deallocate(
@@ -188,11 +200,4 @@
             }
         }
     }
-    /// Gets the actual number of elements stored in this shard.
-    /// Only suitable for single-threaded sections of tests.
-    #[cfg(test)]
-    pub fn len(&self) -> usize {
-        let guard = ShardReadWriteLock::<T>::read(&self.data);
-        guard.deref().len()
-    }
 }
diff --git a/nearby/util/handle_map/src/tests.rs b/common/handle_map/src/tests.rs
similarity index 80%
rename from nearby/util/handle_map/src/tests.rs
rename to common/handle_map/src/tests.rs
index 90e773a..658b55d 100644
--- a/nearby/util/handle_map/src/tests.rs
+++ b/common/handle_map/src/tests.rs
@@ -34,8 +34,10 @@
 // Deliberately picking a higher number of threads.
 const NUM_ACTIVE_THREADS: u32 = 8;
 
-const DEFAULT_DIMENSIONS: HandleMapDimensions =
-    HandleMapDimensions { num_shards: NUM_SHARDS, max_active_handles: MAX_ACTIVE_HANDLES };
+const DEFAULT_DIMENSIONS: HandleMapDimensions = HandleMapDimensions {
+    num_shards: NUM_SHARDS,
+    max_active_handles: MAX_ACTIVE_HANDLES,
+};
 
 fn build_handle_map<T: Send + Sync>() -> HandleMap<T> {
     HandleMap::with_dimensions(DEFAULT_DIMENSIONS)
@@ -67,7 +69,9 @@
 fn test_read_consistency_same_address() {
     let num_repetitions_per_thread = 10000;
     let handle_map = build_handle_map::<String>();
-    let handle = handle_map.allocate(|| "hello".to_string()).expect("Allocation shouldn't fail");
+    let handle = handle_map
+        .allocate(|| "hello".to_string())
+        .expect("Allocation shouldn't fail");
     let test_fn = Arc::new(move || {
         let value_ref = handle_map.get(handle).expect("Getting shouldn't fail");
         assert_eq!("hello", value_ref.deref());
@@ -92,8 +96,8 @@
     });
     test_for_each_thread(test_fn, num_repetitions_per_thread);
 
-    let actual_num_active_handles = handle_map_post_function_ref.len();
-    assert_eq!(MAX_ACTIVE_HANDLES as usize, actual_num_active_handles);
+    let actual_num_active_handles = handle_map_post_function_ref.get_current_allocation_count();
+    assert_eq!(MAX_ACTIVE_HANDLES, actual_num_active_handles);
 }
 
 /// Tests deallocations and allocations near the allocation limit.
@@ -122,8 +126,8 @@
     // No matter what happened above, we should have the same number
     // of handles as when we started, because every successful allocation
     // should have been paired with a successful deallocation.
-    let actual_num_active_handles = handle_map_post_function_ref.len();
-    assert_eq!((MAX_ACTIVE_HANDLES - 1) as usize, actual_num_active_handles);
+    let actual_num_active_handles = handle_map_post_function_ref.get_current_allocation_count();
+    assert_eq!(MAX_ACTIVE_HANDLES - 1, actual_num_active_handles);
 
     //Verify that we still have space for one more entry after all that.
     let _ = handle_map_post_function_ref.allocate(|| 0xEE).unwrap();
@@ -136,15 +140,19 @@
     let num_repetitions_per_thread = 10000;
     let handle_map = build_handle_map::<String>();
     let test_fn = Arc::new(move || {
-        let handle =
-            handle_map.allocate(|| "Hello".to_string()).expect("Allocation shouldn't fail");
+        let handle = handle_map
+            .allocate(|| "Hello".to_string())
+            .expect("Allocation shouldn't fail");
         {
-            let value_ref = handle_map.get(handle).expect("Getting the value shouldn't fail");
+            let value_ref = handle_map
+                .get(handle)
+                .expect("Getting the value shouldn't fail");
             assert_eq!("Hello", &*value_ref);
         };
         {
-            let mut value_mut_ref =
-                handle_map.get_mut(handle).expect("Mutating the value shouldn't fail");
+            let mut value_mut_ref = handle_map
+                .get_mut(handle)
+                .expect("Mutating the value shouldn't fail");
             value_mut_ref.deref_mut().push_str(" World!");
         };
         {
@@ -153,7 +161,9 @@
                 .expect("Getting the value after modification shouldn't fail");
             assert_eq!("Hello World!", &*value_ref);
         };
-        let removed = handle_map.deallocate(handle).expect("Deallocation shouldn't fail");
+        let removed = handle_map
+            .deallocate(handle)
+            .expect("Deallocation shouldn't fail");
         assert_eq!("Hello World!", removed);
     });
     test_for_each_thread(test_fn, num_repetitions_per_thread);
@@ -175,8 +185,9 @@
     let join_handle_a = thread::spawn(move || {
         for i in 1..num_repetitions_per_thread {
             {
-                let value_ref =
-                    handle_map.get(handle).expect("Getting the value from thread A shouldn't fail");
+                let value_ref = handle_map
+                    .get(handle)
+                    .expect("Getting the value from thread A shouldn't fail");
                 let value = &value_ref.0;
                 assert_eq!(i, value.len());
             }
@@ -221,7 +232,9 @@
     let num_repetitions_per_thread = 100;
     let mut handle_map = build_handle_map::<u8>();
     for _ in 0..(num_repetitions_per_thread * NUM_ACTIVE_THREADS) {
-        let handle = handle_map.allocate(|| 0xFF).expect("Initial allocations shouldn't fail");
+        let handle = handle_map
+            .allocate(|| 0xFF)
+            .expect("Initial allocations shouldn't fail");
         let _ = all_handles.insert(handle);
     }
     // Reset the new-handle-id counter
@@ -245,7 +258,9 @@
         thread_handles.push(thread_handle);
     }
     for thread_handle in thread_handles {
-        let handles: Vec<Handle> = thread_handle.join().expect("Individual threads shouldn't fail");
+        let handles: Vec<Handle> = thread_handle
+            .join()
+            .expect("Individual threads shouldn't fail");
         for handle in handles {
             let was_distinct = all_handles.insert(handle);
             assert!(was_distinct);
@@ -257,16 +272,23 @@
 fn test_id_wraparound() {
     let mut handle_map = build_handle_map::<u8>();
     handle_map.set_new_handle_id_counter(u64::MAX);
-    let _ = handle_map.allocate(|| 0xAB).expect("Counter wrap-around allocation should not fail");
-    let _ =
-        handle_map.allocate(|| 0xCD).expect("Post-counter-wrap-around allocation should not fail");
+    let _ = handle_map
+        .allocate(|| 0xAB)
+        .expect("Counter wrap-around allocation should not fail");
+    let _ = handle_map
+        .allocate(|| 0xCD)
+        .expect("Post-counter-wrap-around allocation should not fail");
 }
 
 #[test]
 fn test_deallocate_unallocated_handle() {
     let handle_map = build_handle_map::<usize>();
-    let handle = handle_map.allocate(|| 2).expect("Allocation shouldn't fail");
-    let deallocated = handle_map.deallocate(handle).expect("Deallocation shouldn't fail");
+    let handle = handle_map
+        .allocate(|| 2)
+        .expect("Allocation shouldn't fail");
+    let deallocated = handle_map
+        .deallocate(handle)
+        .expect("Deallocation shouldn't fail");
     assert_eq!(2, deallocated);
     let double_deallocate_result = handle_map.deallocate(handle);
     assert!(double_deallocate_result.is_err());
@@ -275,8 +297,12 @@
 #[test]
 fn test_get_unallocated_handle() {
     let handle_map = build_handle_map::<u8>();
-    let handle = handle_map.allocate(|| 0xFE).expect("Allocation shouldn't fail");
-    let deallocated = handle_map.deallocate(handle).expect("Deallocation shouldn't fail");
+    let handle = handle_map
+        .allocate(|| 0xFE)
+        .expect("Allocation shouldn't fail");
+    let deallocated = handle_map
+        .deallocate(handle)
+        .expect("Deallocation shouldn't fail");
     assert_eq!(0xFE, deallocated);
     let read_result = handle_map.get(handle);
     assert!(read_result.is_err());
@@ -285,8 +311,12 @@
 #[test]
 fn test_get_mut_unallocated_handle() {
     let handle_map = build_handle_map::<(usize, usize, usize)>();
-    let handle = handle_map.allocate(|| (1, 2, 3)).expect("Allocation shouldn't fail");
-    let deallocated = handle_map.deallocate(handle).expect("Deallocation shouldn't fail");
+    let handle = handle_map
+        .allocate(|| (1, 2, 3))
+        .expect("Allocation shouldn't fail");
+    let deallocated = handle_map
+        .deallocate(handle)
+        .expect("Deallocation shouldn't fail");
     assert_eq!((1, 2, 3), deallocated);
     let get_mut_result = handle_map.get_mut(handle);
     assert!(get_mut_result.is_err());
diff --git a/nearby/util/lock_adapter/Cargo.toml b/common/lock_adapter/Cargo.toml
similarity index 100%
rename from nearby/util/lock_adapter/Cargo.toml
rename to common/lock_adapter/Cargo.toml
diff --git a/nearby/util/lock_adapter/src/lib.rs b/common/lock_adapter/src/lib.rs
similarity index 97%
rename from nearby/util/lock_adapter/src/lib.rs
rename to common/lock_adapter/src/lib.rs
index 6bf6f0f..1c77ee8 100644
--- a/nearby/util/lock_adapter/src/lib.rs
+++ b/common/lock_adapter/src/lib.rs
@@ -15,7 +15,10 @@
 //! An abstraction layer for Rust synchronization primitives which provides both no_std and std library
 //! based implementations
 
-#![cfg_attr(not(feature = "std"), no_std)]
+#![no_std]
+
+#[cfg(feature = "std")]
+extern crate std;
 
 /// A Spinlock-based implementation of Mutex using the `spin` crate that can be used in `no_std`
 /// environments.
@@ -28,7 +31,7 @@
 ///
 /// Available with the feature `std`.
 #[cfg(feature = "std")]
-pub mod std;
+pub mod stdlib;
 
 /// A trait for mutex implementations that doesn't support poisoning. If the thread panicked while
 /// holding the mutex, the data will be released normally (as spin::Mutex would).
diff --git a/nearby/util/lock_adapter/src/spin.rs b/common/lock_adapter/src/spin.rs
similarity index 100%
rename from nearby/util/lock_adapter/src/spin.rs
rename to common/lock_adapter/src/spin.rs
diff --git a/nearby/util/lock_adapter/src/std.rs b/common/lock_adapter/src/stdlib.rs
similarity index 100%
rename from nearby/util/lock_adapter/src/std.rs
rename to common/lock_adapter/src/stdlib.rs
diff --git a/nearby/util/pourover/Cargo.toml b/common/pourover/Cargo.toml
similarity index 64%
rename from nearby/util/pourover/Cargo.toml
rename to common/pourover/Cargo.toml
index ce6fefa..eb03cdb 100644
--- a/nearby/util/pourover/Cargo.toml
+++ b/common/pourover/Cargo.toml
@@ -4,6 +4,12 @@
 edition.workspace = true
 publish.workspace = true
 
+[lints]
+workspace = true
+# Needed for FFI, but cannot be overidden in this file. Usages will be marked
+# with `#[allow(unsafe_code)]`.
+# rust.unsafe_code = "allow"
+
 [dependencies]
 jni.workspace = true
 pourover_macro.workspace = true
diff --git a/nearby/util/pourover/src/conversions.rs b/common/pourover/src/conversions.rs
similarity index 97%
rename from nearby/util/pourover/src/conversions.rs
rename to common/pourover/src/conversions.rs
index 69a00a2..e311e6d 100644
--- a/nearby/util/pourover/src/conversions.rs
+++ b/common/pourover/src/conversions.rs
@@ -26,6 +26,7 @@
 impl<'a> ToSigned for &'a [u8] {
     type Signed = &'a [i8];
 
+    #[allow(unsafe_code)]
     fn to_signed(self) -> Self::Signed {
         let len = self.len();
         // Safety:
@@ -46,6 +47,7 @@
 impl<'a> ToUnsigned for &'a [i8] {
     type Unsigned = &'a [u8];
 
+    #[allow(unsafe_code)]
     fn to_unsigned(self) -> Self::Unsigned {
         let len = self.len();
         // Safety:
diff --git a/nearby/util/pourover/src/desc.rs b/common/pourover/src/desc.rs
similarity index 96%
rename from nearby/util/pourover/src/desc.rs
rename to common/pourover/src/desc.rs
index 507d3de..26bada6 100644
--- a/nearby/util/pourover/src/desc.rs
+++ b/common/pourover/src/desc.rs
@@ -35,7 +35,8 @@
 //! static MY_CLASS_GET_BAR_METHOD: MethodDesc = MY_CLASS_DESC.method("getBar", "I()");
 //! ```
 
-use core::convert::AsRef;
+#![allow(unsafe_code)]
+
 use jni::{
     descriptors::Desc,
     objects::{GlobalRef, JClass, JFieldID, JMethodID, JObject, JStaticFieldID, JStaticMethodID},
@@ -57,7 +58,10 @@
 impl ClassDesc {
     /// Create a new descriptor with the given JNI descriptor string.
     pub const fn new(descriptor: &'static str) -> Self {
-        Self { descriptor, cls: RwLock::new(None) }
+        Self {
+            descriptor,
+            cls: RwLock::new(None),
+        }
     }
 
     /// Create a new descriptor for a field member of this class.
@@ -139,7 +143,11 @@
 impl<'lock> AsRef<JClass<'static>> for CachedClass<'lock> {
     fn as_ref(&self) -> &JClass<'static> {
         // `unwrap` is valid since we checked for `Some` in the constructor.
-        let global = self.0.as_ref().unwrap();
+        #[allow(clippy::expect_used)]
+        let global = self
+            .0
+            .as_ref()
+            .expect("Created CachedClass in an invalid state");
         // No direct conversion to JClass, so let's go through JObject first.
         let obj: &JObject<'static> = global.as_ref();
         // This assumes our object is a class object.
@@ -181,6 +189,7 @@
 
         // Safe to unwrap since we just set `self.cls` to `Some`. `ClassDesc::free` can't be called
         // before this point because it takes a mutable reference to `*self`.
+        #[allow(clippy::unwrap_used)]
         Ok(self.get_cached().unwrap())
     }
 }
@@ -209,7 +218,12 @@
     ///
     /// Please use the helpers on [`ClassDesc`] instead of directly calling this method.
     pub const fn new(cls: &'cls ClassDesc, name: &'static str, sig: &'static str) -> Self {
-        Self { cls, name, sig, id: RwLock::new(None) }
+        Self {
+            cls,
+            name,
+            sig,
+            id: RwLock::new(None),
+        }
     }
 
     /// Get the class descriptor that this member is associated to.
@@ -248,6 +262,7 @@
                 Ok(id)
             } else {
                 // Can unwrap since we just checked for `None`.
+                #[allow(clippy::unwrap_used)]
                 Ok(*guard.as_ref().unwrap())
             }
         }
diff --git a/nearby/util/pourover/src/lib.rs b/common/pourover/src/lib.rs
similarity index 85%
rename from nearby/util/pourover/src/lib.rs
rename to common/pourover/src/lib.rs
index 51495de..ed861ea 100644
--- a/nearby/util/pourover/src/lib.rs
+++ b/common/pourover/src/lib.rs
@@ -14,11 +14,7 @@
 
 //! Utilties for JNI interactions.
 
-// Enforce documentation
-#![deny(missing_docs)]
-#![deny(unsafe_op_in_unsafe_fn)]
-
-pub use pourover_macro::jni_method;
+pub use pourover_macro::{call_constructor, call_method, call_static_method, jni_method};
 
 pub mod desc;
 
diff --git a/nearby/util/pourover/tests/Foo.java b/common/pourover/tests/Foo.java
similarity index 95%
rename from nearby/util/pourover/tests/Foo.java
rename to common/pourover/tests/Foo.java
index 0907964..eb76e9b 100644
--- a/nearby/util/pourover/tests/Foo.java
+++ b/common/pourover/tests/Foo.java
@@ -31,6 +31,10 @@
     this.foo = f;
   }
 
+  public int getFoo() {
+    return foo;
+  }
+
   public boolean mfoo() {
     return true;
   }
diff --git a/common/pourover/tests/call_method_integration.rs b/common/pourover/tests/call_method_integration.rs
new file mode 100644
index 0000000..5de2547
--- /dev/null
+++ b/common/pourover/tests/call_method_integration.rs
@@ -0,0 +1,48 @@
+// 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::JObject, JavaVM};
+use std::error::Error;
+
+mod common;
+use common::foo_class::*;
+
+#[test]
+fn jni_access() -> 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 foo_obj = pourover::call_constructor!(&mut env, &FOO, "(I)V", 123)?;
+    let inner_int = pourover::call_method!(&mut env, &FOO, "getFoo", "()I", &foo_obj)?;
+    assert_eq!(123, inner_int);
+    env.delete_local_ref(foo_obj)?;
+
+    let static_int = pourover::call_static_method!(&mut env, &FOO, "smfoo", "()I")?;
+    assert_eq!(3, static_int);
+
+    Ok(())
+}
diff --git a/nearby/util/pourover/tests/common/foo_class.rs b/common/pourover/tests/common/foo_class.rs
similarity index 96%
rename from nearby/util/pourover/tests/common/foo_class.rs
rename to common/pourover/tests/common/foo_class.rs
index 65971d7..3ded762 100644
--- a/nearby/util/pourover/tests/common/foo_class.rs
+++ b/common/pourover/tests/common/foo_class.rs
@@ -41,7 +41,7 @@
 /// stdout.
 #[test]
 fn has_java() -> Result<(), Box<dyn Error>> {
-    Command::new("java").arg("--version").status()?;
+    let _ = Command::new("java").arg("--version").status()?;
     Ok(())
 }
 
@@ -67,7 +67,7 @@
     }
 
     // Compile Foo.java into the temp dir
-    Command::new("javac")
+    let _ = Command::new("javac")
         .args(["--release", "8"])
         .arg("-d")
         .arg(&tmp)
diff --git a/nearby/util/pourover/tests/common/mod.rs b/common/pourover/tests/common/mod.rs
similarity index 100%
rename from nearby/util/pourover/tests/common/mod.rs
rename to common/pourover/tests/common/mod.rs
diff --git a/nearby/util/pourover/tests/desc_integration.rs b/common/pourover/tests/desc_integration.rs
similarity index 90%
rename from nearby/util/pourover/tests/desc_integration.rs
rename to common/pourover/tests/desc_integration.rs
index 7eec528..dd330c3 100644
--- a/nearby/util/pourover/tests/desc_integration.rs
+++ b/common/pourover/tests/desc_integration.rs
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#![allow(unsafe_code)]
+
 use jni::{
     descriptors::Desc,
     objects::JObject,
@@ -27,7 +29,10 @@
 fn jni_access() -> Result<(), Box<dyn Error>> {
     // Create the environment
     let vm = JavaVM::new(
-        jni::InitArgsBuilder::new().version(jni::JNIVersion::V8).option("-Xcheck:jni").build()?,
+        jni::InitArgsBuilder::new()
+            .version(jni::JNIVersion::V8)
+            .option("-Xcheck:jni")
+            .build()?,
     )?;
     let mut env = vm.attach_current_thread()?;
 
@@ -52,8 +57,10 @@
 
     // Verify we can access all of the members
 
-    let field_value =
-        { env.get_field_unchecked(&obj_foo, &FIELD, ReturnType::Primitive(Primitive::Int))?.i()? };
+    let field_value = {
+        env.get_field_unchecked(&obj_foo, &FIELD, ReturnType::Primitive(Primitive::Int))?
+            .i()?
+    };
     assert_eq!(123, field_value);
 
     let method_value = {
diff --git a/nearby/util/pourover/tests/jni_method_integration.rs b/common/pourover/tests/jni_method_integration.rs
similarity index 95%
rename from nearby/util/pourover/tests/jni_method_integration.rs
rename to common/pourover/tests/jni_method_integration.rs
index 73d89d1..4bd6547 100644
--- a/nearby/util/pourover/tests/jni_method_integration.rs
+++ b/common/pourover/tests/jni_method_integration.rs
@@ -12,6 +12,8 @@
 // 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},
@@ -69,10 +71,14 @@
 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()?,
+        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)?;
diff --git a/common/pourover_macro/Cargo.toml b/common/pourover_macro/Cargo.toml
new file mode 100644
index 0000000..0473dd4
--- /dev/null
+++ b/common/pourover_macro/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "pourover_macro"
+version.workspace = true
+edition.workspace = true
+publish.workspace = true
+
+[lints]
+workspace = true
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro2.workspace = true
+syn.workspace = true
+quote.workspace = true
+nom = { workspace = true, features = ["alloc"] }
+jni = { workspace = true, optional = true }
+
+[dev-dependencies]
+pourover.workspace = true # doc only
+jni.workspace = true # doc only
diff --git a/common/pourover_macro/src/call_method.rs b/common/pourover_macro/src/call_method.rs
new file mode 100644
index 0000000..5886a86
--- /dev/null
+++ b/common/pourover_macro/src/call_method.rs
@@ -0,0 +1,100 @@
+// 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.
+
+//! Implementation of the `call_method!` series of macros. These macros are meant to be used as
+//! function-like macros and implement a statically type-safe way to call Java methods while also
+//! caching the method id. The macro arguments are implemented in [`mod ast`](mod@ast) and the
+//! generated code is implemented in [`mod codegen`](mod@codegen).
+
+use proc_macro2::TokenStream;
+use syn::parse_quote;
+
+use ast::{ConstructorArgs, InstanceArgs, StaticArgs};
+use codegen::{MethodCall, MethodInfo, Receiver};
+
+mod ast;
+mod codegen;
+
+/// See [`crate::call_method!`] for usage.
+pub fn call_method(args: TokenStream) -> syn::Result<TokenStream> {
+    let args = syn::parse2::<InstanceArgs>(args)?;
+
+    let method_info = MethodInfo::new(args.cls, args.name, args.sig);
+    let receiver = Receiver::Instance(args.this);
+    let method_call = MethodCall::new(
+        args.env,
+        method_info,
+        receiver,
+        args.args.into_iter().collect(),
+    );
+
+    method_call.generate().map_err(syn::Error::from)
+}
+
+/// See [`crate::call_static_method!`] for usage.
+pub fn call_static_method(args: TokenStream) -> syn::Result<TokenStream> {
+    let args = syn::parse2::<StaticArgs>(args)?;
+
+    let method_info = MethodInfo::new(args.cls, args.name, args.sig);
+    let receiver = Receiver::Static;
+    let method_call = MethodCall::new(
+        args.env,
+        method_info,
+        receiver,
+        args.args.into_iter().collect(),
+    );
+
+    method_call.generate().map_err(syn::Error::from)
+}
+
+/// See [`crate::call_constructor!`] for usage.
+pub fn call_constructor(args: TokenStream) -> syn::Result<TokenStream> {
+    let args = syn::parse2::<ConstructorArgs>(args)?;
+
+    let name = parse_quote!["<init>"];
+    let method_info = MethodInfo::new(args.cls, name, args.sig);
+    let receiver = Receiver::Constructor;
+    let method_call = MethodCall::new(
+        args.env,
+        method_info,
+        receiver,
+        args.args.into_iter().collect(),
+    );
+
+    method_call.generate().map_err(syn::Error::from)
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use quote::quote;
+
+    #[test]
+    fn call_method_error() {
+        let out = call_method(quote![&mut env, &CLS, "method", "INVALID", &this_obj]);
+        assert!(out.is_err());
+    }
+
+    #[test]
+    fn call_static_method_error() {
+        let out = call_static_method(quote![&mut env, &CLS, "method", "INVALID"]);
+        assert!(out.is_err());
+    }
+
+    #[test]
+    fn call_constructor_error() {
+        let out = call_constructor(quote![&mut env, &CLS, "INVALID"]);
+        assert!(out.is_err());
+    }
+}
diff --git a/common/pourover_macro/src/call_method/ast.rs b/common/pourover_macro/src/call_method/ast.rs
new file mode 100644
index 0000000..c5e66f9
--- /dev/null
+++ b/common/pourover_macro/src/call_method/ast.rs
@@ -0,0 +1,290 @@
+// 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.
+
+//! Syntax trees for the arguments to the `call_method!` series of proc macros.
+
+use syn::{
+    parse::{Parse, ParseStream},
+    punctuated::Punctuated,
+    Expr, LitStr, Token,
+};
+
+/// The trailing method arguments
+type Args = Punctuated<Expr, Token![,]>;
+
+/// See [`crate::call_constructor!`].
+pub struct ConstructorArgs {
+    pub env: Expr,
+    pub cls: Expr,
+    pub sig: LitStr,
+    pub args: Args,
+}
+
+impl Parse for ConstructorArgs {
+    fn parse(stream: ParseStream<'_>) -> syn::Result<Self> {
+        let env = CommaSep::parse(stream)?.node;
+        let cls = CommaSep::parse(stream)?.node;
+        let sig = stream.parse()?;
+        let args = parse_args(stream)?;
+
+        Ok(Self {
+            env,
+            cls,
+            sig,
+            args,
+        })
+    }
+}
+
+/// See [`crate::call_static_method!`].
+pub struct StaticArgs {
+    pub env: Expr,
+    pub cls: Expr,
+    pub name: Expr,
+    pub sig: LitStr,
+    pub args: Args,
+}
+
+impl Parse for StaticArgs {
+    fn parse(stream: ParseStream<'_>) -> syn::Result<Self> {
+        let env = CommaSep::parse(stream)?.node;
+        let cls = CommaSep::parse(stream)?.node;
+        let name = CommaSep::parse(stream)?.node;
+        let sig = stream.parse()?;
+        let args = parse_args(stream)?;
+
+        Ok(Self {
+            env,
+            cls,
+            name,
+            sig,
+            args,
+        })
+    }
+}
+
+/// See [`crate::call_method!`].
+pub struct InstanceArgs {
+    pub env: Expr,
+    pub cls: Expr,
+    pub name: Expr,
+    pub sig: LitStr,
+    pub this: Expr,
+    pub args: Args,
+}
+
+impl Parse for InstanceArgs {
+    fn parse(stream: ParseStream<'_>) -> syn::Result<Self> {
+        let env = CommaSep::parse(stream)?.node;
+        let cls = CommaSep::parse(stream)?.node;
+        let name = CommaSep::parse(stream)?.node;
+        let sig = CommaSep::parse(stream)?.node;
+        let this = stream.parse()?;
+        let args = parse_args(stream)?;
+
+        Ok(Self {
+            env,
+            cls,
+            name,
+            sig,
+            this,
+            args,
+        })
+    }
+}
+
+/// Parses the variable number of arguments to the method. A leading comma is required when there
+/// are arguments being passed.
+fn parse_args(stream: ParseStream<'_>) -> syn::Result<Args> {
+    Ok(if stream.is_empty() {
+        Punctuated::new()
+    } else {
+        let _ = stream.parse::<Token![,]>()?;
+        Punctuated::parse_terminated(stream)?
+    })
+}
+
+/// A syntax node followed by a comma.
+#[allow(dead_code)]
+pub struct CommaSep<T> {
+    pub node: T,
+    pub comma: Token![,],
+}
+
+impl<T: Parse> Parse for CommaSep<T> {
+    fn parse(stream: ParseStream<'_>) -> syn::Result<Self> {
+        Ok(Self {
+            node: stream.parse()?,
+            comma: stream.parse()?,
+        })
+    }
+}
+
+#[cfg(test)]
+#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
+mod test {
+    use super::*;
+    use quote::quote;
+    use syn::parse::Parser;
+
+    #[test]
+    fn comma_sep_correct() {
+        let ret: CommaSep<syn::Ident> = syn::parse2(quote![abc,]).unwrap();
+
+        assert_eq!(ret.node, "abc");
+    }
+
+    #[test]
+    fn comma_sep_incorrect() {
+        assert!(syn::parse2::<CommaSep<syn::Ident>>(quote![abc]).is_err());
+        assert!(syn::parse2::<CommaSep<syn::Ident>>(quote![,abc]).is_err());
+        assert!(syn::parse2::<CommaSep<syn::Ident>>(quote![,]).is_err());
+    }
+
+    #[test]
+    fn parse_args_no_args() {
+        let args = parse_args.parse2(quote![]).unwrap();
+
+        assert_eq!(0, args.len());
+    }
+
+    #[test]
+    fn parse_args_no_args_extra_comma() {
+        let args = parse_args.parse2(quote![,]).unwrap();
+
+        assert_eq!(0, args.len());
+    }
+
+    #[test]
+    fn parse_args_single_no_trailing() {
+        let args = parse_args.parse2(quote![, foo]).unwrap();
+
+        assert_eq!(1, args.len());
+    }
+
+    #[test]
+    fn parse_args_single_trailing() {
+        let args = parse_args.parse2(quote![, foo,]).unwrap();
+
+        assert_eq!(1, args.len());
+    }
+
+    #[test]
+    fn parse_args_multi_no_trailing() {
+        let args = parse_args.parse2(quote![, one, two, three]).unwrap();
+
+        assert_eq!(3, args.len());
+    }
+
+    #[test]
+    fn parse_args_multi_trailing() {
+        let args = parse_args.parse2(quote![, one, two, three,]).unwrap();
+
+        assert_eq!(3, args.len());
+    }
+
+    #[test]
+    fn parse_args_error_two_commas() {
+        let res = parse_args.parse2(quote![, ,]);
+
+        assert!(res.is_err());
+    }
+
+    #[test]
+    fn parse_constructor_args() {
+        let tests = [
+            quote![&mut env, &CLS, "()V"],
+            quote![&mut env, &CLS, "()V",],
+            quote![&mut env, &CLS, "(I)V", 123],
+            quote![&mut env, &CLS, "(I)V", 123,],
+            quote![&mut env, &CLS, "(ILjava/lang/String;)V", 123, &some_str],
+            quote![&mut env, &CLS, "(ILjava/lang/String;)V", 123, &some_str,],
+        ];
+
+        for valid in tests {
+            assert!(
+                syn::parse2::<ConstructorArgs>(valid.clone()).is_ok(),
+                "test: {valid}"
+            );
+        }
+    }
+
+    #[test]
+    fn parse_static_method_args() {
+        let tests = [
+            quote![&mut env, &CLS, "methodName", "()V"],
+            quote![&mut env, &CLS, "methodName", "()V",],
+            quote![&mut env, &CLS, "methodName", "(I)V", 123],
+            quote![&mut env, &CLS, "methodName", "(I)V", 123,],
+            quote![
+                &mut env,
+                &CLS,
+                "methodName",
+                "(ILjava/lang/String;)V",
+                123,
+                &some_str
+            ],
+            quote![
+                &mut env,
+                &CLS,
+                "methodName",
+                "(ILjava/lang/String;)V",
+                123,
+                &some_str,
+            ],
+        ];
+
+        for valid in tests {
+            assert!(
+                syn::parse2::<StaticArgs>(valid.clone()).is_ok(),
+                "test: {valid}"
+            );
+        }
+    }
+
+    #[test]
+    fn parse_method_args() {
+        let tests = [
+            quote![&mut env, &CLS, "methodName", "()V", &this_obj],
+            quote![&mut env, &CLS, "methodName", "()V", &this_obj,],
+            quote![&mut env, &CLS, "methodName", "(I)V", &this_obj, 123],
+            quote![&mut env, &CLS, "methodName", "(I)V", &this_obj, 123,],
+            quote![
+                &mut env,
+                &CLS,
+                "methodName",
+                "(ILjava/lang/String;)V",
+                &this_obj,
+                123,
+                &some_str
+            ],
+            quote![
+                &mut env,
+                &CLS,
+                "methodName",
+                "(ILjava/lang/String;)V",
+                &this_obj,
+                123,
+                &some_str,
+            ],
+        ];
+
+        for valid in tests {
+            assert!(
+                syn::parse2::<InstanceArgs>(valid.clone()).is_ok(),
+                "test: {valid}"
+            );
+        }
+    }
+}
diff --git a/common/pourover_macro/src/call_method/codegen.rs b/common/pourover_macro/src/call_method/codegen.rs
new file mode 100644
index 0000000..bce3976
--- /dev/null
+++ b/common/pourover_macro/src/call_method/codegen.rs
@@ -0,0 +1,610 @@
+// 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.
+
+//! Code generation for the `call_method!` series of proc-macros. This module is used by creating a
+//! [`MethodCall`] instance and calling [`MethodCall::generate`]. The generated code will vary
+//! for each macro based on the contained [`Receiver`] value. If there is a detected issue with the
+//! [`MethodCall`] provided, then `generate` will return a [`CodegenError`] with information on
+//! what is wrong. This error can be converted into a [`syn::Error`] for reporting any failures to
+//! the user.
+
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, quote_spanned, TokenStreamExt};
+use syn::{parse_quote, spanned::Spanned, Expr, Ident, ItemStatic, LitStr, Type};
+
+use crate::type_parser::{JavaType, MethodSig, NonArray, Primitive, ReturnType};
+
+/// The errors that can be encountered during codegen. Used in [`CodegenError`].
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum ErrorKind {
+    InvalidArgsLength { expected: usize, found: usize },
+    ConstructorRetValShouldBeVoid,
+    InvalidTypeSignature,
+}
+
+/// An error encountered during codegen with span information. Can be converted to [`syn::Error`].
+/// using [`From`].
+#[derive(Clone, Debug)]
+pub struct CodegenError(pub Span, pub ErrorKind);
+
+impl From<CodegenError> for syn::Error {
+    fn from(CodegenError(span, kind): CodegenError) -> syn::Error {
+        use ErrorKind::*;
+        match kind {
+            InvalidArgsLength { expected, found } => {
+                syn::Error::new(span, format!("The number of args does not match the type signature provided: expected={expected}, found={found}"))
+            }
+            ConstructorRetValShouldBeVoid => {
+                syn::Error::new(span, "Return type should be `void` (`V`) for constructor methods")
+            }
+            InvalidTypeSignature => syn::Error::new(span, "Failed to parse type signature"),
+        }
+    }
+}
+
+/// Codegen can fail with [`CodegenError`].
+pub type CodegenResult<T> = Result<T, CodegenError>;
+
+/// Describes a method that will be generated. Create one with [`MethodCall::new`] and generate
+/// code with [`MethodCall::generate`]. This should be given AST nodes from the macro input so that
+/// errors are associated properly to the input span.
+pub struct MethodCall {
+    env: Expr,
+    method: MethodInfo,
+    receiver: Receiver,
+    arg_exprs: Vec<Expr>,
+}
+
+impl MethodCall {
+    /// Create a new MethodCall instance
+    pub fn new(env: Expr, method: MethodInfo, receiver: Receiver, arg_exprs: Vec<Expr>) -> Self {
+        Self {
+            env,
+            method,
+            receiver,
+            arg_exprs,
+        }
+    }
+
+    /// Generate code to call the described method.
+    pub fn generate(&self) -> CodegenResult<TokenStream> {
+        // Needs to be threaded manually to other methods since self-referential structs can't
+        // exist.
+        let sig = self.method.sig()?;
+
+        let args = self.generate_args(&sig)?;
+
+        let method_call = self
+            .receiver
+            .generate_call(&self.env, &self.method, &sig, &args)?;
+
+        // Wrap the generated code in a closure so that we can access the outer scope but any
+        // variables we define aren't accessible by the outer scope. There is small hygiene issue
+        // where the arg exprs have our `env` variable in scope. If this becomes an issue we can
+        // refactor these exprs to be passed as closure parameters instead.
+        Ok(quote! {
+            (|| {
+                #method_call
+            })()
+        })
+    }
+
+    /// Generate the `&[jni::jvalue]` arguments slice that will be passed to the `jni` method call.
+    /// This validates the argument count and types.
+    fn generate_args(&self, sig: &MethodSig<'_>) -> CodegenResult<Expr> {
+        // Safety: must check that arg count matches the signature
+        if self.arg_exprs.len() != sig.args.len() {
+            return Err(CodegenError(
+                self.method.sig.span(),
+                ErrorKind::InvalidArgsLength {
+                    expected: sig.args.len(),
+                    found: self.arg_exprs.len(),
+                },
+            ));
+        }
+
+        // Create each `jvalue` expression
+        let type_expr_pairs = core::iter::zip(sig.args.iter().copied(), self.arg_exprs.iter());
+        let jvalues = type_expr_pairs.map(|(ty, expr)| generate_jvalue(ty, expr));
+
+        // Put the `jvalue` expressions in a slice.
+        Ok(parse_quote! {
+            &[#(#jvalues),*]
+        })
+    }
+}
+
+/// The receiver of the method call and the type of the method.
+pub enum Receiver {
+    /// A constructor.
+    Constructor,
+    /// A static method.
+    Static,
+    /// An instance method. The `Expr` here is the `this` object.
+    Instance(Expr),
+}
+
+impl Receiver {
+    /// Generate the code that performs the JNI call.
+    fn generate_call(
+        &self,
+        env: &Expr,
+        method_info: &MethodInfo,
+        sig: &MethodSig<'_>,
+        args: &Expr,
+    ) -> CodegenResult<TokenStream> {
+        // Constructors are void methods. Validate this fact.
+        if matches!(*self, Receiver::Constructor) && !sig.ret.is_void() {
+            return Err(CodegenError(
+                method_info.sig.span(),
+                ErrorKind::ConstructorRetValShouldBeVoid,
+            ));
+        }
+
+        // The static item containing the `pourover::[Static]MethodDesc`.
+        let method_desc = self.generate_method_desc(method_info);
+
+        // The `jni::signature::ReturnType` that the `jni` crate uses to perform the correct native
+        // call.
+        let return_type = return_type_from_sig(sig.ret);
+
+        // A conversion expression to convert from `jni::object::JValueOwned` to the actual return
+        // type. We have this information from the parsed method signature whereas the `jni` crate
+        // only knows this at runtime.
+        let conversion = return_value_conversion_from_sig(sig.ret);
+
+        // This preamble is used to evaluate all the client-provided expressions outside of the
+        // `unsafe` block. This is the same for all receiver kinds.
+        let mut method_call = quote! {
+            #method_desc
+
+            let env: &mut ::jni::JNIEnv = #env;
+            let method_id = ::jni::descriptors::Desc::lookup(&METHOD_DESC, env)?;
+            let args: &[::jni::sys::jvalue] = #args;
+        };
+
+        // Generate the unsafe JNI call.
+        //
+        // Safety: `args` contains the arguments to this method. The type signature of this
+        // method is `#sig`.
+        //
+        // `args` must adhere to the following:
+        //  - `args.len()` must match the number of arguments given in the type signature.
+        //  - The union value of each arg in `args` must match the type specified in the type
+        //    signature.
+        //
+        // These conditions are upheld by this proc macro and a compile error will be caused if
+        // they are broken. No user-provided code is executed within the `unsafe` block.
+        method_call.append_all(match self {
+            Self::Constructor => quote! {
+                unsafe {
+                    env.new_object_unchecked(
+                        METHOD_DESC.cls(),
+                        method_id,
+                        args,
+                    )
+                }
+            },
+            Self::Static => quote! {
+                unsafe {
+                    env.call_static_method_unchecked(
+                        METHOD_DESC.cls(),
+                        method_id,
+                        #return_type,
+                        args,
+                    )
+                }#conversion
+            },
+            Self::Instance(this) => quote! {
+                let this_obj: &JObject = #this;
+                unsafe {
+                    env.call_method_unchecked(
+                        this_obj,
+                        method_id,
+                        #return_type,
+                        args,
+                    )
+                }#conversion
+            },
+        });
+
+        Ok(method_call)
+    }
+
+    fn generate_method_desc(&self, MethodInfo { cls, name, sig, .. }: &MethodInfo) -> ItemStatic {
+        match self {
+            Self::Constructor => parse_quote! {
+                static METHOD_DESC: ::pourover::desc::MethodDesc = (#cls).constructor(#sig);
+            },
+            Self::Static => parse_quote! {
+                static METHOD_DESC: ::pourover::desc::StaticMethodDesc = (#cls).static_method(#name, #sig);
+            },
+            Self::Instance(_) => parse_quote! {
+                static METHOD_DESC: ::pourover::desc::MethodDesc = (#cls).method(#name, #sig);
+            },
+        }
+    }
+}
+
+/// Information about the method being called
+pub struct MethodInfo {
+    cls: Expr,
+    name: Expr,
+    sig: LitStr,
+    /// Derived from `sig.value()`. This string must be stored in the struct so that we can return
+    /// a `MethodSig` instance that references it from `MethodInfo::sig()`.
+    sig_str: String,
+}
+
+impl MethodInfo {
+    pub fn new(cls: Expr, name: Expr, sig: LitStr) -> Self {
+        let sig_str = sig.value();
+        Self {
+            cls,
+            name,
+            sig,
+            sig_str,
+        }
+    }
+
+    /// Parse the type signature from `sig`. Will return a [`CodegenError`] if the signature cannot
+    /// be parsed.
+    fn sig(&self) -> CodegenResult<MethodSig<'_>> {
+        MethodSig::try_from_str(&self.sig_str)
+            .ok_or_else(|| CodegenError(self.sig.span(), ErrorKind::InvalidTypeSignature))
+    }
+}
+
+/// Generate a `jni::sys::jvalue` instance given a Java type and a Rust value.
+///
+/// Safety: The generated `jvalue` must match the given type `ty`.
+fn generate_jvalue(ty: JavaType<'_>, expr: &Expr) -> TokenStream {
+    // The `jvalue` field to inhabit
+    let union_field: Ident;
+    // The expected input type
+    let type_name: Type;
+    // Whether we need to call `JObject::as_raw()` on the input type
+    let needs_as_raw: bool;
+
+    // Fill the above values based the type signature.
+    match ty {
+        JavaType::Array { depth, ty } => {
+            union_field = parse_quote![l];
+            if let NonArray::Primitive(p) = ty {
+                if depth.get() == 1 {
+                    let prim_type = prim_to_sys_type(p);
+                    type_name = parse_quote![::jni::objects::JPrimitiveArray<'_, #prim_type>]
+                } else {
+                    type_name = parse_quote![&::jni::objects::JObjectArray<'_>];
+                }
+            } else {
+                type_name = parse_quote![&::jni::objects::JObjectArray<'_>];
+            }
+            needs_as_raw = true;
+        }
+        JavaType::NonArray(NonArray::Object { cls }) => {
+            union_field = parse_quote![l];
+            type_name = match cls {
+                "java/lang/String" => parse_quote![&::jni::objects::JString<'_>],
+                "java/util/List" => parse_quote![&::jni::objects::JList<'_>],
+                "java/util/Map" => parse_quote![&::jni::objects::JMap<'_>],
+                _ => parse_quote![&::jni::objects::JObject<'_>],
+            };
+            needs_as_raw = true;
+        }
+        JavaType::NonArray(NonArray::Primitive(p)) => {
+            union_field = prim_to_union_field(p);
+            type_name = prim_to_sys_type(p);
+            needs_as_raw = false;
+        }
+    }
+
+    // The as_raw() tokens if required.
+    let as_raw = if needs_as_raw {
+        quote! { .as_raw() }
+    } else {
+        quote![]
+    };
+
+    // Create the `jvalue` expression. This uses `identity` to produce nice type error messages.
+    quote_spanned! { expr.span() =>
+        ::jni::sys::jvalue {
+            #union_field: ::core::convert::identity::<#type_name>(#expr) #as_raw
+        }
+    }
+}
+
+/// Get a `::jni::signature::ReturnType` expression from a [`crate::type_parser::ReturnType`]. This
+/// value is passed to the `jni` crate so that it knows which JNI method to call.
+fn return_type_from_sig(ret: ReturnType<'_>) -> Expr {
+    let prim_type = |prim| parse_quote![::jni::signature::ReturnType::Primitive(::jni::signature::Primitive::#prim)];
+
+    use crate::type_parser::{JavaType::*, NonArray::*, Primitive::*};
+
+    match ret {
+        ReturnType::Void => prim_type(quote![Void]),
+        ReturnType::Returns(NonArray(Primitive(Boolean))) => prim_type(quote![Boolean]),
+        ReturnType::Returns(NonArray(Primitive(Byte))) => prim_type(quote![Byte]),
+        ReturnType::Returns(NonArray(Primitive(Char))) => prim_type(quote![Char]),
+        ReturnType::Returns(NonArray(Primitive(Double))) => prim_type(quote![Double]),
+        ReturnType::Returns(NonArray(Primitive(Float))) => prim_type(quote![Float]),
+        ReturnType::Returns(NonArray(Primitive(Int))) => prim_type(quote![Int]),
+        ReturnType::Returns(NonArray(Primitive(Long))) => prim_type(quote![Long]),
+        ReturnType::Returns(NonArray(Primitive(Short))) => prim_type(quote![Short]),
+        ReturnType::Returns(NonArray(Object { .. })) => {
+            parse_quote![::jni::signature::ReturnType::Object]
+        }
+        ReturnType::Returns(Array { .. }) => parse_quote![::jni::signature::ReturnType::Array],
+    }
+}
+
+/// A postfix call on a `jni::objects::JValueOwned` instance to convert it to the type specified by
+/// `ret`. Since we have this information from the type signature we can  perform this conversion
+/// in the macro.
+fn return_value_conversion_from_sig(ret: ReturnType<'_>) -> TokenStream {
+    use crate::type_parser::{JavaType::*, NonArray::*};
+
+    match ret {
+        ReturnType::Void => quote! { .and_then(::jni::objects::JValueOwned::v) },
+        ReturnType::Returns(NonArray(Primitive(p))) => {
+            let prim = prim_to_union_field(p);
+            quote! { .and_then(::jni::objects::JValueOwned::#prim) }
+        }
+        ReturnType::Returns(NonArray(Object { cls })) => {
+            let mut conversion = quote! { .and_then(::jni::objects::JValueOwned::l) };
+            match cls {
+                "java/lang/String" => {
+                    conversion.append_all(quote! { .map(::jni::objects::JString::from) });
+                }
+                "java/util/List" => {
+                    conversion.append_all(quote! { .map(::jni::objects::JList::from) });
+                }
+                "java/util/Map" => {
+                    conversion.append_all(quote! { .map(::jni::objects::JMap::from) });
+                }
+                _ => {
+                    // Already a JObject, so we are good here
+                }
+            }
+            conversion
+        }
+        ReturnType::Returns(Array {
+            depth,
+            ty: Primitive(p),
+        }) if depth.get() == 1 => {
+            let sys_type = prim_to_sys_type(p);
+            quote! {
+                .and_then(::jni::objects::JValueOwned::l)
+                .map(::jni::objects::JPrimitiveArray::<#sys_type>::from)
+            }
+        }
+        ReturnType::Returns(Array { .. }) => quote! {
+            .and_then(::jni::objects::JValueOwned::l)
+            .map(::jni::objects::JObjectArray::from)
+        },
+    }
+}
+
+/// From a [`Primitive`], this gets the `jni::sys::jvalue` union field name for that type. This is
+/// also the `jni::objects::JValueGen` getter name.
+fn prim_to_union_field(p: Primitive) -> Ident {
+    quote::format_ident!("{}", p.as_char().to_ascii_lowercase())
+}
+
+/// From a [`Primitive`], this gets the matching `jvalue::sys` type.
+fn prim_to_sys_type(p: Primitive) -> Type {
+    match p {
+        Primitive::Boolean => parse_quote![::jni::sys::jboolean],
+        Primitive::Byte => parse_quote![::jni::sys::jbyte],
+        Primitive::Char => parse_quote![::jni::sys::jchar],
+        Primitive::Double => parse_quote![::jni::sys::jdouble],
+        Primitive::Float => parse_quote![::jni::sys::jfloat],
+        Primitive::Int => parse_quote![::jni::sys::jint],
+        Primitive::Long => parse_quote![::jni::sys::jlong],
+        Primitive::Short => parse_quote![::jni::sys::jshort],
+    }
+}
+
+#[cfg(test)]
+#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
+mod tests {
+    use super::*;
+    use crate::test_util::contains_ident;
+    use quote::ToTokens;
+    use syn::parse_quote;
+
+    fn example_method_call() -> MethodCall {
+        MethodCall::new(
+            parse_quote![&mut env],
+            MethodInfo::new(
+                parse_quote![&FOO_CLS],
+                parse_quote!["example"],
+                parse_quote!["(II)I"],
+            ),
+            Receiver::Instance(parse_quote![&foo]),
+            vec![parse_quote![123], parse_quote![2 + 3]],
+        )
+    }
+
+    #[test]
+    fn args_are_counted() {
+        let mut call = example_method_call();
+        call.arg_exprs.push(parse_quote![too_many]);
+
+        let CodegenError(_span, kind) = call.generate().unwrap_err();
+
+        assert_eq!(
+            ErrorKind::InvalidArgsLength {
+                expected: 2,
+                found: 3
+            },
+            kind
+        );
+    }
+
+    #[test]
+    fn constructor_return_type_is_void() {
+        let mut call = example_method_call();
+        call.receiver = Receiver::Constructor;
+
+        let CodegenError(_span, kind) = call.generate().unwrap_err();
+
+        assert_eq!(ErrorKind::ConstructorRetValShouldBeVoid, kind);
+    }
+
+    #[test]
+    fn invalid_type_sig_is_error() {
+        let mut call = example_method_call();
+        call.method.sig = parse_quote!["L"];
+        call.method.sig_str = call.method.sig.value();
+
+        let CodegenError(_span, kind) = call.generate().unwrap_err();
+
+        assert_eq!(ErrorKind::InvalidTypeSignature, kind);
+    }
+
+    #[test]
+    fn jni_types_are_used_for_stdlib_classes_input() {
+        let types = [
+            ("Ljava/lang/String;", "JString"),
+            ("Ljava/util/Map;", "JMap"),
+            ("Ljava/util/List;", "JList"),
+            ("[Ljava/lang/String;", "JObjectArray"),
+            ("[[I", "JObjectArray"),
+            ("[I", "JPrimitiveArray"),
+            ("Lcom/example/MyObject;", "JObject"),
+            ("Z", "jboolean"),
+            ("C", "jchar"),
+            ("B", "jbyte"),
+            ("S", "jshort"),
+            ("I", "jint"),
+            ("J", "jlong"),
+            ("F", "jfloat"),
+            ("D", "jdouble"),
+        ];
+
+        for (desc, jni_type) in types {
+            let jt = JavaType::try_from_str(desc).unwrap();
+            let expr = parse_quote![some_value];
+
+            let jvalue = generate_jvalue(jt, &expr);
+
+            assert!(
+                contains_ident(jvalue, jni_type),
+                "desc: {desc}, jni_type: {jni_type}"
+            );
+        }
+    }
+
+    #[test]
+    fn jni_types_are_used_for_stdlib_classes_output() {
+        let types = [
+            ("Ljava/lang/String;", "JString"),
+            ("Ljava/util/Map;", "JMap"),
+            ("Ljava/util/List;", "JList"),
+            ("[Ljava/lang/String;", "JObjectArray"),
+            ("[[I", "JObjectArray"),
+            ("[I", "JPrimitiveArray"),
+        ];
+
+        for (desc, jni_type) in types {
+            let rt = ReturnType::try_from_str(desc).unwrap();
+
+            let conversion = return_value_conversion_from_sig(rt);
+
+            assert!(
+                contains_ident(conversion, jni_type),
+                "desc: {desc}, jni_type: {jni_type}"
+            );
+        }
+    }
+
+    #[test]
+    fn return_type_passed_to_jni_is_correct() {
+        let types = [
+            ("Ljava/lang/String;", "Object"),
+            ("Ljava/util/Map;", "Object"),
+            ("Ljava/util/List;", "Object"),
+            ("[Ljava/lang/String;", "Array"),
+            ("[[I", "Array"),
+            ("[I", "Array"),
+            ("V", "Void"),
+            ("Z", "Boolean"),
+            ("C", "Char"),
+            ("B", "Byte"),
+            ("S", "Short"),
+            ("I", "Int"),
+            ("J", "Long"),
+            ("F", "Float"),
+            ("D", "Double"),
+        ];
+
+        for (desc, return_type) in types {
+            let rt = ReturnType::try_from_str(desc).unwrap();
+
+            let expr = return_type_from_sig(rt).into_token_stream();
+
+            assert!(
+                contains_ident(expr, return_type),
+                "desc: {desc}, return_type: {return_type}"
+            );
+        }
+    }
+
+    #[test]
+    fn method_desc_is_correct() {
+        let mut call = example_method_call();
+        call.method.sig = parse_quote!["(II)V"];
+        call.method.sig_str = call.method.sig.value();
+
+        let tests = [
+            (Receiver::Constructor, "constructor"),
+            (Receiver::Static, "static_method"),
+            (Receiver::Instance(parse_quote![this_value]), "method"),
+        ];
+
+        for (receiver, method_ident) in tests {
+            let desc = receiver.generate_method_desc(&call.method);
+            let rhs = desc.expr.into_token_stream();
+
+            assert!(contains_ident(rhs, method_ident), "method: {method_ident}");
+        }
+    }
+
+    #[test]
+    fn jni_call_is_correct() {
+        let mut call = example_method_call();
+        call.method.sig = parse_quote!["(II)V"];
+        call.method.sig_str = call.method.sig.value();
+        let sig = call.method.sig().unwrap();
+        let args = parse_quote![test_stub];
+
+        let tests = [
+            (Receiver::Constructor, "new_object_unchecked"),
+            (Receiver::Static, "call_static_method_unchecked"),
+            (
+                Receiver::Instance(parse_quote![this_value]),
+                "call_method_unchecked",
+            ),
+        ];
+
+        for (receiver, method_ident) in tests {
+            let call = receiver
+                .generate_call(&call.env, &call.method, &sig, &args)
+                .unwrap();
+
+            assert!(contains_ident(call, method_ident), "method: {method_ident}");
+        }
+    }
+}
diff --git a/nearby/util/pourover_macro_core/src/jni_method.rs b/common/pourover_macro/src/jni_method.rs
similarity index 64%
rename from nearby/util/pourover_macro_core/src/jni_method.rs
rename to common/pourover_macro/src/jni_method.rs
index 73ca155..760aa64 100644
--- a/nearby/util/pourover_macro_core/src/jni_method.rs
+++ b/common/pourover_macro/src/jni_method.rs
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 use proc_macro2::TokenStream;
-use quote::ToTokens;
 use syn::{punctuated::Punctuated, spanned::Spanned, ItemFn, LitStr, Token};
 
 mod meta;
@@ -23,20 +22,18 @@
 use meta::JniMethodMeta;
 use substitutions::substitute_method_chars;
 
-/// See `pourover_macro::jni_method` for usage.
-pub fn jni_method(meta: TokenStream, item: TokenStream) -> TokenStream {
-    match jni_method_inner(meta, item) {
-        Ok(out) => out.to_token_stream(),
-        Err(err) => err.to_compile_error(),
-    }
-}
-
-fn jni_method_inner(meta: TokenStream, item: TokenStream) -> syn::Result<impl ToTokens> {
+pub fn jni_method(meta: TokenStream, item: TokenStream) -> syn::Result<ItemFn> {
     let meta = syn::parse2::<JniMethodMeta>(meta)?;
     let mut func = syn::parse2::<ItemFn>(item)?;
 
     // Check that ABI is set to `extern "system"`
-    if let Some(ref abi @ syn::Abi { name: Some(ref abi_name), .. }) = func.sig.abi {
+    if let Some(
+        ref abi @ syn::Abi {
+            name: Some(ref abi_name),
+            ..
+        },
+    ) = func.sig.abi
+    {
         if abi_name.value() != "system" {
             return Err(syn::Error::new(
                 abi.span(),
@@ -52,10 +49,14 @@
 
     let export_attr = {
         // Format the name of the function as expected by the JNI layer
-        let method_name = func.sig.ident.to_string();
+        let (method_name, method_name_span) = if let Some(meta_name) = &meta.method_name {
+            (meta_name.value(), meta_name.span())
+        } else {
+            (func.sig.ident.to_string(), func.sig.ident.span())
+        };
         if method_name.starts_with("Java_") {
             return Err(syn::Error::new(
-                func.sig.ident.span(),
+                method_name_span,
                 "The `jni_method` attribute will perform the JNI name formatting",
             ));
         }
@@ -64,13 +65,18 @@
         // NOTE: doesn't handle overload suffix
         let link_name = LitStr::new(
             &format!("Java_{class}_{method_name}", class = &meta.class_desc),
-            func.sig.ident.span(),
+            method_name_span,
         );
 
         syn::parse_quote! { #[export_name = #link_name] }
     };
-    let allow_attr = syn::parse_quote! { #[allow(non_snake_case)] };
-    func.attrs.extend([export_attr, allow_attr]);
+    func.attrs.push(export_attr);
+
+    // Allow function name to be non_snake_case if we are using it as the Java method name
+    if meta.method_name.is_none() {
+        let allow_attr = syn::parse_quote! { #[allow(non_snake_case)] };
+        func.attrs.push(allow_attr);
+    }
 
     // Add a panic handler if requested
     if let Some(panic_returns) = meta.panic_returns {
@@ -108,50 +114,11 @@
 }
 
 #[cfg(test)]
+#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
 mod tests {
     use super::*;
-    use proc_macro2::{TokenStream, TokenTree};
-    use quote::quote;
-
-    fn contains_ident(stream: TokenStream, ident: &str) -> bool {
-        /// Iterator that traverses TokenTree:Group structures in preorder.
-        struct FlatStream {
-            streams: Vec<<TokenStream as IntoIterator>::IntoIter>,
-        }
-
-        impl FlatStream {
-            fn new(stream: TokenStream) -> Self {
-                Self { streams: vec![stream.into_iter()] }
-            }
-        }
-
-        impl Iterator for FlatStream {
-            type Item = TokenTree;
-
-            fn next(&mut self) -> Option<TokenTree> {
-                let next = loop {
-                    let stream = self.streams.last_mut()?;
-                    if let Some(next) = stream.next() {
-                        break next;
-                    }
-                    self.streams.pop();
-                };
-
-                if let TokenTree::Group(group) = &next {
-                    self.streams.push(group.stream().into_iter());
-                }
-
-                Some(next)
-            }
-        }
-
-        FlatStream::new(stream)
-            .filter_map(|tree| {
-                let TokenTree::Ident(ident) = tree else { return None };
-                Some(ident.to_string())
-            })
-            .any(|ident_str| ident_str == ident)
-    }
+    use crate::test_util::contains_ident;
+    use quote::{quote, ToTokens};
 
     #[test]
     fn can_parse() {
@@ -170,9 +137,9 @@
             }
         };
 
-        let out = jni_method(meta, func);
+        let out = jni_method(meta, func).unwrap();
 
-        assert!(contains_ident(out, "catch_unwind"));
+        assert!(contains_ident(out.into_token_stream(), "catch_unwind"));
     }
 
     fn parse_example_output() -> syn::ItemFn {
@@ -191,7 +158,27 @@
             }
         };
 
-        syn::parse2(jni_method(meta, func)).expect("Proc macro output should be a function item")
+        jni_method(meta, func).expect("failed to generate example")
+    }
+
+    fn parse_example_output_method_name() -> syn::ItemFn {
+        let meta = quote! {
+            package = "com.example",
+            class = "Foo.Inner",
+            method_name = "nativeBar",
+            panic_returns = false,
+        };
+
+        let func = quote! {
+            extern "system" fn native_bar<'local>(
+                mut env: JNIEnv<'local>,
+                this: JObject<'local>
+            ) -> jint {
+                123
+            }
+        };
+
+        jni_method(meta, func).expect("failed to generate example")
     }
 
     #[test]
@@ -213,7 +200,10 @@
                 if !nv.path.is_ident("export_name") {
                     return None;
                 }
-                let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit_str), .. }) = &nv.value
+                let syn::Expr::Lit(syn::ExprLit {
+                    lit: syn::Lit::Str(lit_str),
+                    ..
+                }) = &nv.value
                 else {
                     return None;
                 };
@@ -224,6 +214,33 @@
     }
 
     #[test]
+    fn check_output_export_name_with_method_name() {
+        let out = parse_example_output_method_name();
+
+        let export_name = out
+            .attrs
+            .iter()
+            .find_map(|attr| {
+                let syn::Meta::NameValue(nv) = &attr.meta else {
+                    return None;
+                };
+                if !nv.path.is_ident("export_name") {
+                    return None;
+                }
+                let syn::Expr::Lit(syn::ExprLit {
+                    lit: syn::Lit::Str(lit_str),
+                    ..
+                }) = &nv.value
+                else {
+                    return None;
+                };
+                Some(lit_str.value())
+            })
+            .expect("Failed to find `export_name` attribute");
+        assert_eq!("Java_com_example_Foo_00024Inner_nativeBar", export_name);
+    }
+
+    #[test]
     fn check_output_allow_non_snake_case() {
         let out = parse_example_output();
 
@@ -246,6 +263,25 @@
     }
 
     #[test]
+    fn check_output_allow_non_snake_case_not_present_with_method_name() {
+        let out = parse_example_output_method_name();
+
+        let allow_attr = out.attrs.iter().find(|attr| {
+            let syn::Meta::List(ml) = &attr.meta else {
+                return false;
+            };
+            if !ml.path.is_ident("allow") {
+                return false;
+            }
+            let Ok(value) = syn::parse2::<syn::Path>(ml.tokens.clone()) else {
+                return false;
+            };
+            value.is_ident("non_snake_case")
+        });
+        assert!(allow_attr.is_none());
+    }
+
+    #[test]
     fn no_panic_returns() {
         let meta = quote! {
             package = "com.example",
@@ -261,7 +297,10 @@
             }
         };
 
-        let out = jni_method(meta, func);
+        let out = match jni_method(meta, func) {
+            Ok(item_fn) => item_fn.into_token_stream(),
+            Err(err) => err.into_compile_error(),
+        };
         assert!(!contains_ident(out.clone(), "compile_error"));
         assert!(!contains_ident(out, "catch_unwind"));
     }
@@ -283,7 +322,7 @@
             }
         };
 
-        let Err(err) = jni_method_inner(meta, func) else {
+        let Err(err) = jni_method(meta, func) else {
             panic!("Should fail to generate code");
         };
 
@@ -309,7 +348,7 @@
             }
         };
 
-        let Err(err) = jni_method_inner(meta, func) else {
+        let Err(err) = jni_method(meta, func) else {
             panic!("Should fail to generate code");
         };
 
@@ -335,7 +374,34 @@
             }
         };
 
-        let Err(err) = jni_method_inner(meta, func) else {
+        let Err(err) = jni_method(meta, func) else {
+            panic!("Should fail to generate code");
+        };
+
+        assert!(err
+            .to_string()
+            .contains("The `jni_method` attribute will perform the JNI name formatting"));
+    }
+
+    #[test]
+    fn already_mangled_method_name() {
+        let meta = quote! {
+            package = "com.example",
+            class = "Foo.Inner",
+            method_name = "Java_com_example_Foo_00024Inner_nativeFoo",
+            panic_returns = false,
+        };
+
+        let func = quote! {
+            extern "system" fn native_foo<'local>(
+                mut env: JNIEnv<'local>,
+                this: JObject<'local>
+            ) -> jint {
+                123
+            }
+        };
+
+        let Err(err) = jni_method(meta, func) else {
             panic!("Should fail to generate code");
         };
 
diff --git a/nearby/util/pourover_macro_core/src/jni_method/meta.rs b/common/pourover_macro/src/jni_method/meta.rs
similarity index 70%
rename from nearby/util/pourover_macro_core/src/jni_method/meta.rs
rename to common/pourover_macro/src/jni_method/meta.rs
index 1e07edd..85e8c1a 100644
--- a/nearby/util/pourover_macro_core/src/jni_method/meta.rs
+++ b/common/pourover_macro/src/jni_method/meta.rs
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use proc_macro2::Span;
 use syn::{
     parse::{Parse, ParseStream},
-    Expr, Token,
+    Expr, LitStr, Token,
 };
 
 use super::meta_arg::MetaArg;
@@ -23,6 +24,8 @@
 pub struct JniMethodMeta {
     /// The class descriptor in [export_name format](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names)
     pub class_desc: String,
+    /// The method name in Java. Use the Rust method name if not specified
+    pub method_name: Option<LitStr>,
     /// The expression to run when a panic is encountered. The value of this expression will be
     /// returned by the annotated function.
     pub panic_returns: Option<Expr>,
@@ -32,35 +35,58 @@
     fn parse(stream: ParseStream<'_>) -> syn::Result<Self> {
         let mut package = None;
         let mut class = None;
+        let mut method_name = None;
         let mut panic_returns = None;
 
+        fn set_once<T>(opt: &mut Option<T>, value: T, span: Span, field: &str) -> syn::Result<()> {
+            if let Some(_old) = opt.replace(value) {
+                return Err(syn::Error::new(
+                    span,
+                    format!("`{field}` should not be specified twice"),
+                ));
+            }
+            Ok(())
+        }
+
         type Structure = syn::punctuated::Punctuated<MetaArg, Token![,]>;
 
         for arg in Structure::parse_terminated(stream)? {
             match arg {
-                MetaArg::Package { package_token, value, .. } => {
-                    if let Some(_old) = package.replace(value) {
-                        return Err(syn::Error::new(
-                            package_token.span,
-                            "`package` should not be specified twice",
-                        ));
-                    }
+                MetaArg::Package {
+                    package_token,
+                    value,
+                    ..
+                } => {
+                    set_once(&mut package, value, package_token.span, "package")?;
                 }
-                MetaArg::Class { class_token, value, .. } => {
-                    if let Some(_old) = class.replace(value) {
-                        return Err(syn::Error::new(
-                            class_token.span,
-                            "`class` should not be specified twice",
-                        ));
-                    }
+                MetaArg::Class {
+                    class_token, value, ..
+                } => {
+                    set_once(&mut class, value, class_token.span, "class")?;
                 }
-                MetaArg::PanicReturns { panic_returns_token, value, .. } => {
-                    if let Some(_old) = panic_returns.replace(value) {
-                        return Err(syn::Error::new(
-                            panic_returns_token.span,
-                            "`panic_returns` should not be specified twice",
-                        ));
-                    }
+                MetaArg::MethodName {
+                    method_name_token,
+                    value,
+                    ..
+                } => {
+                    set_once(
+                        &mut method_name,
+                        value,
+                        method_name_token.span,
+                        "method_name",
+                    )?;
+                }
+                MetaArg::PanicReturns {
+                    panic_returns_token,
+                    value,
+                    ..
+                } => {
+                    set_once(
+                        &mut panic_returns,
+                        value,
+                        panic_returns_token.span,
+                        "panic_returns",
+                    )?;
                 }
             }
         }
@@ -86,7 +112,11 @@
         let class = substitute_class_chars(&class.value());
         let class_desc = format!("{package}_{class}");
 
-        Ok(Self { class_desc, panic_returns })
+        Ok(Self {
+            class_desc,
+            method_name,
+            panic_returns,
+        })
     }
 }
 
diff --git a/nearby/util/pourover_macro_core/src/jni_method/meta_arg.rs b/common/pourover_macro/src/jni_method/meta_arg.rs
similarity index 73%
rename from nearby/util/pourover_macro_core/src/jni_method/meta_arg.rs
rename to common/pourover_macro/src/jni_method/meta_arg.rs
index aa28aa1..02b1900 100644
--- a/nearby/util/pourover_macro_core/src/jni_method/meta_arg.rs
+++ b/common/pourover_macro/src/jni_method/meta_arg.rs
@@ -21,14 +21,32 @@
 pub mod kw {
     syn::custom_keyword!(package);
     syn::custom_keyword!(class);
+    syn::custom_keyword!(method_name);
     syn::custom_keyword!(panic_returns);
 }
 
 /// Arguments to the attribute
 pub enum MetaArg {
-    Package { package_token: kw::package, _eq_token: Token![=], value: LitStr },
-    Class { class_token: kw::class, _eq_token: Token![=], value: LitStr },
-    PanicReturns { panic_returns_token: kw::panic_returns, _eq_token: Token![=], value: Expr },
+    Package {
+        package_token: kw::package,
+        _eq_token: Token![=],
+        value: LitStr,
+    },
+    Class {
+        class_token: kw::class,
+        _eq_token: Token![=],
+        value: LitStr,
+    },
+    MethodName {
+        method_name_token: kw::method_name,
+        _eq_token: Token![=],
+        value: LitStr,
+    },
+    PanicReturns {
+        panic_returns_token: kw::panic_returns,
+        _eq_token: Token![=],
+        value: Expr,
+    },
 }
 
 impl Parse for MetaArg {
@@ -46,6 +64,12 @@
                 _eq_token: stream.parse()?,
                 value: stream.parse()?,
             })
+        } else if lookahead.peek(kw::method_name) {
+            Ok(MetaArg::MethodName {
+                method_name_token: stream.parse::<kw::method_name>()?,
+                _eq_token: stream.parse()?,
+                value: stream.parse()?,
+            })
         } else if lookahead.peek(kw::panic_returns) {
             Ok(MetaArg::PanicReturns {
                 panic_returns_token: stream.parse::<kw::panic_returns>()?,
@@ -59,6 +83,7 @@
 }
 
 #[cfg(test)]
+#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
 mod tests {
     use super::*;
     use syn::parse_quote;
@@ -87,6 +112,8 @@
             panic!("failed to parse")
         };
 
-        let syn::Expr::Block(_) = value else { panic!("not a block expression") };
+        let syn::Expr::Block(_) = value else {
+            panic!("not a block expression")
+        };
     }
 }
diff --git a/nearby/util/pourover_macro_core/src/jni_method/substitutions.rs b/common/pourover_macro/src/jni_method/substitutions.rs
similarity index 100%
rename from nearby/util/pourover_macro_core/src/jni_method/substitutions.rs
rename to common/pourover_macro/src/jni_method/substitutions.rs
diff --git a/common/pourover_macro/src/lib.rs b/common/pourover_macro/src/lib.rs
new file mode 100644
index 0000000..f46b7b1
--- /dev/null
+++ b/common/pourover_macro/src/lib.rs
@@ -0,0 +1,286 @@
+// 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.
+
+//! Proc macros for `pourover`. These macros are reexported by the `pourover` crate, so this crate
+//! is an implementation detail.
+
+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].
+///
+/// [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.
+/// - `method_name` (*optional* LitStr): the method's name in Java. The Rust function name will be
+/// 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.
+///
+/// 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
+/// arguments and return values are passed by pointer or value and not by Rust reference.
+///
+/// # Example
+/// ```
+/// # use pourover_macro::jni_method;
+/// # use jni::{sys::jint, objects::{JObject, JString}, JNIEnv};
+///
+/// #[jni_method(package = "my.package", class = "Foo", panic_returns = -1)]
+/// extern "system" fn getFoo<'local>(
+///     mut env: JNIEnv<'local>,
+///     this: JObject<'local>,
+/// ) -> jint {
+///     // ...
+///     0
+/// }
+/// ```
+///
+/// This function will be exported with `#[export_name = "Java_my_package_Foo_getFoo"]`.
+#[proc_macro_attribute]
+pub fn jni_method(meta: TokenStream, item: TokenStream) -> TokenStream {
+    use quote::ToTokens;
+    match jni_method::jni_method(meta.into(), item.into()) {
+        Ok(item_fn) => item_fn.into_token_stream(),
+        Err(err) => err.into_compile_error(),
+    }
+    .into()
+}
+
+/// Call a Java method.
+///
+/// # Parameters
+/// `call_method!($env, $cls, $name, $sig, $this, $($args),*)`
+/// - `env` (Expr: `&mut jni::JNIEnv`): The JNI environment.
+/// - `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.
+/// - `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.
+///
+/// # Type-Safety
+/// The given type signature will be parsed and arguments will be type checked against it. The
+/// expected types are from the `jni` crate:
+/// - Primitives: `jni::sys::{jboolean, jbyte, jchar, jshort, jint, jlong, jfloat, jdouble}`
+/// - Arrays: `jni::objects::{JPrimitiveArray, JObjectArray}`
+/// - Objects: `jni::objects::{JObject, JString, JMap, JList}`
+///
+/// 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.
+///
+/// # Example
+/// Let's call `sayHello` from the following class.
+/// ```java
+/// package com.example;
+/// class Foo {
+///     int sayHello(String name) { /* ... */ }
+/// }
+/// ```
+/// We can use `call_method!` to implement the function call.
+/// ```rust
+/// # use jni::{sys::jint, objects::{JObject, JString}, JNIEnv};
+/// # use pourover_macro::call_method;
+/// # use pourover::desc::*;
+/// static MY_CLASS: ClassDesc = ClassDesc::new("com/example/Foo");
+/// fn say_hello<'l>(
+///     env: &mut JNIEnv<'l>,
+///     my_obj: &JObject<'_>,
+///     name: &JString<'_>
+/// ) -> jni::errors::Result<jint> {
+///     call_method!(env, &MY_CLASS, "sayHello", "(Ljava/lang/String;)I", my_obj, name)
+/// }
+/// ```
+#[proc_macro]
+pub fn call_method(args: TokenStream) -> TokenStream {
+    call_method::call_method(args.into())
+        .unwrap_or_else(syn::Error::into_compile_error)
+        .into()
+}
+
+/// Call a static Java method.
+///
+/// # Parameters
+/// `call_static_method!($env, $cls, $name, $sig, $($args),*)`
+/// - `env` (Expr: `&mut jni::JNIEnv`): The JNI environment.
+/// - `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.
+/// - `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.
+///
+/// # Type-Safety
+/// The given type signature will be parsed and arguments will be type checked against it. The
+/// expected types are from the `jni` crate:
+/// - Primitives: `jni::sys::{jboolean, jbyte, jchar, jshort, jint, jlong, jfloat, jdouble}`
+/// - Arrays: `jni::objects::{JPrimitiveArray, JObjectArray}`
+/// - Objects: `jni::objects::{JObject, JString, JMap, JList}`
+///
+/// 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.
+///
+/// # Example
+/// Let's call `sayHello` from the following class.
+/// ```java
+/// package com.example;
+/// class Foo {
+///     static int sayHello(String name) { /* ... */ }
+/// }
+/// ```
+/// We can use `call_static_method!` to implement the function call.
+/// ```rust
+/// # use jni::{sys::jint, objects::{JObject, JString}, JNIEnv};
+/// # use pourover_macro::call_static_method;
+/// # use pourover::desc::*;
+/// static MY_CLASS: ClassDesc = ClassDesc::new("com/example/Foo");
+/// fn say_hello<'l>(
+///     env: &mut JNIEnv<'l>,
+///     name: &JString<'_>
+/// ) -> jni::errors::Result<jint> {
+///     call_static_method!(env, &MY_CLASS, "sayHello", "(Ljava/lang/String;)I", name)
+/// }
+/// ```
+#[proc_macro]
+pub fn call_static_method(args: TokenStream) -> TokenStream {
+    call_method::call_static_method(args.into())
+        .unwrap_or_else(syn::Error::into_compile_error)
+        .into()
+}
+
+/// Call a Java constructor.
+///
+/// # Parameters
+/// `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.
+/// - `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.
+///
+/// # Type-Safety
+/// The given type signature will be parsed and arguments will be type checked against it. The
+/// expected types are from the `jni` crate:
+/// - Primitives: `jni::sys::{jboolean, jbyte, jchar, jshort, jint, jlong, jfloat, jdouble}`
+/// - Arrays: `jni::objects::{JPrimitiveArray, JObjectArray}`
+/// - Objects: `jni::objects::{JObject, JString, JMap, JList}`
+///
+/// # Returns
+/// The macro will evaluate to `jni::errors::Result<jni::objects::JObject>`.
+///
+/// # Example
+/// Let's call the constructor from the following class.
+/// ```java
+/// package com.example;
+/// class Foo {
+///     Foo(String name) { /* ... */ }
+/// }
+/// ```
+/// We can use `call_constructor!` to implement the function call.
+/// ```rust
+/// # use jni::{objects::{JObject, JString}, JNIEnv};
+/// # use pourover_macro::call_constructor;
+/// # use pourover::desc::*;
+/// static MY_CLASS: ClassDesc = ClassDesc::new("com/example/Foo");
+/// fn construct_foo<'l>(
+///     env: &mut JNIEnv<'l>,
+///     name: &JString<'_>
+/// ) -> jni::errors::Result<JObject<'l>> {
+///     call_constructor!(env, &MY_CLASS, "(Ljava/lang/String;)V", name)
+/// }
+/// ```
+#[proc_macro]
+pub fn call_constructor(args: TokenStream) -> TokenStream {
+    call_method::call_constructor(args.into())
+        .unwrap_or_else(syn::Error::into_compile_error)
+        .into()
+}
+
+#[cfg(test)]
+pub(crate) mod test_util {
+    use proc_macro2::{TokenStream, TokenTree};
+
+    /// Iterator that traverses TokenTree:Group structures in preorder.
+    struct FlatStream {
+        streams: Vec<<TokenStream as IntoIterator>::IntoIter>,
+    }
+
+    impl FlatStream {
+        fn new(stream: TokenStream) -> Self {
+            Self {
+                streams: vec![stream.into_iter()],
+            }
+        }
+    }
+
+    impl Iterator for FlatStream {
+        type Item = TokenTree;
+
+        fn next(&mut self) -> Option<TokenTree> {
+            let next = loop {
+                let stream = self.streams.last_mut()?;
+                if let Some(next) = stream.next() {
+                    break next;
+                }
+                let _ = self.streams.pop();
+            };
+
+            if let TokenTree::Group(group) = &next {
+                self.streams.push(group.stream().into_iter());
+            }
+
+            Some(next)
+        }
+    }
+
+    pub fn contains_ident(stream: TokenStream, ident: &str) -> bool {
+        FlatStream::new(stream)
+            .filter_map(|tree| {
+                let TokenTree::Ident(ident) = tree else {
+                    return None;
+                };
+                Some(ident.to_string())
+            })
+            .any(|ident_str| ident_str == ident)
+    }
+}
diff --git a/common/pourover_macro/src/type_parser.rs b/common/pourover_macro/src/type_parser.rs
new file mode 100644
index 0000000..e6834e2
--- /dev/null
+++ b/common/pourover_macro/src/type_parser.rs
@@ -0,0 +1,503 @@
+// 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.
+
+//! Parsers for JNI type descriptors.
+
+// There is code that is used in tests but not in the crate.
+#![allow(dead_code)]
+
+use core::fmt;
+use core::num::NonZeroU8;
+use nom::{IResult, Parser};
+
+/// Describes a type in Java
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum JavaType<'a> {
+    /// An array type.
+    Array {
+        /// The number of dimensions. The JVM spec limits this to 255.
+        depth: NonZeroU8,
+        /// The type of objects in the array.
+        ty: NonArray<'a>,
+    },
+    /// A non-array type. See [`NonArray`].
+    NonArray(NonArray<'a>),
+}
+
+impl<'a> JavaType<'a> {
+    /// Check if this type is a primitive type.
+    pub fn is_primitive(&self) -> bool {
+        matches!(*self, Self::NonArray(NonArray::Primitive(_)))
+    }
+
+    fn parse(s: &'a str) -> IResult<&'a str, JavaType<'a>> {
+        use nom::bytes::complete::take_while1;
+        use nom::combinator::{map, map_opt, opt};
+
+        let parse_array = opt(map_opt(take_while1(|c| c == '['), |brackets: &str| {
+            u8::try_from(brackets.len()).ok().and_then(NonZeroU8::new)
+        }));
+
+        map(
+            parse_array.and(NonArray::parse),
+            |(depth, ty)| match depth {
+                Some(depth) => JavaType::Array { depth, ty },
+                None => JavaType::NonArray(ty),
+            },
+        )(s)
+    }
+
+    /// Try to parse a type from the given string in JNI descriptor format.
+    pub fn try_from_str(s: &'a str) -> Option<Self> {
+        run_parser(Self::parse, s)
+    }
+}
+
+impl<'a> fmt::Display for JavaType<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            JavaType::Array { depth, ty } => {
+                for _ in 0..depth.get() {
+                    write!(f, "[")?;
+                }
+                write!(f, "{ty}")
+            }
+            JavaType::NonArray(ty) => write!(f, "{ty}"),
+        }
+    }
+}
+
+impl<'a> From<NonArray<'a>> for JavaType<'a> {
+    fn from(ty: NonArray<'a>) -> Self {
+        JavaType::NonArray(ty)
+    }
+}
+
+impl<'a> From<Primitive> for JavaType<'a> {
+    fn from(prim: Primitive) -> Self {
+        JavaType::NonArray(NonArray::Primitive(prim))
+    }
+}
+
+#[cfg(jni)]
+impl<'a> From<JavaType<'a>> for jni::signature::ReturnType {
+    fn from(ty: JavaType<'a>) -> Self {
+        match ty {
+            JavaType::Array { .. } => Self::Array,
+            JavaType::NonArray(NonArray::Object { .. }) => Self::Object,
+            JavaType::NonArray(NonArray::Primitive(p)) => Self::Primitive(p.into()),
+        }
+    }
+}
+
+/// Describes a non-array type in Java
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum NonArray<'a> {
+    /// A primitive type. See [`Primitive`].
+    Primitive(Primitive),
+    /// An object type.
+    Object {
+        /// The class name in JNI form.
+        cls: &'a str,
+    },
+}
+
+impl<'a> NonArray<'a> {
+    fn parse(s: &'a str) -> IResult<&'a str, NonArray<'a>> {
+        use nom::branch::alt;
+        use nom::bytes::complete::take_while1;
+        use nom::character::complete::char;
+        use nom::combinator::map;
+        use nom::sequence::delimited;
+
+        let parse_prim = map(Primitive::parse, NonArray::Primitive);
+
+        let parse_object = map(
+            delimited(char('L'), take_while1(|c| c != ';'), char(';')),
+            |cls| NonArray::Object { cls },
+        );
+
+        alt((parse_prim, parse_object))(s)
+    }
+}
+
+impl<'a> fmt::Display for NonArray<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            NonArray::Primitive(p) => write!(f, "{p}"),
+            NonArray::Object { cls } => write!(f, "L{cls};"),
+        }
+    }
+}
+
+impl<'a> From<Primitive> for NonArray<'a> {
+    fn from(p: Primitive) -> Self {
+        Self::Primitive(p)
+    }
+}
+
+/// Describes a primitive type in Java
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum Primitive {
+    /// Java `boolean`
+    Boolean,
+    /// Java `byte`
+    Byte,
+    /// Java `char`
+    Char,
+    /// Java `double`
+    Double,
+    /// Java `float`
+    Float,
+    /// Java `int`
+    Int,
+    /// Java `long`
+    Long,
+    /// Java `short`
+    Short,
+}
+
+impl Primitive {
+    fn parse(s: &str) -> IResult<&str, Primitive> {
+        use nom::branch::alt;
+        use nom::character::complete::char as match_char;
+        use nom::combinator::value;
+
+        let parse_prim = |c: char, p: Primitive| value(p, match_char(c));
+
+        alt((
+            parse_prim('Z', Primitive::Boolean),
+            parse_prim('B', Primitive::Byte),
+            parse_prim('C', Primitive::Char),
+            parse_prim('D', Primitive::Double),
+            parse_prim('F', Primitive::Float),
+            parse_prim('I', Primitive::Int),
+            parse_prim('J', Primitive::Long),
+            parse_prim('S', Primitive::Short),
+        ))(s)
+    }
+
+    pub fn as_char(&self) -> char {
+        match self {
+            Primitive::Boolean => 'Z',
+            Primitive::Byte => 'B',
+            Primitive::Char => 'C',
+            Primitive::Double => 'D',
+            Primitive::Float => 'F',
+            Primitive::Int => 'I',
+            Primitive::Long => 'J',
+            Primitive::Short => 'S',
+        }
+    }
+}
+
+impl fmt::Display for Primitive {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "{}",
+            match *self {
+                Primitive::Boolean => 'Z',
+                Primitive::Byte => 'B',
+                Primitive::Char => 'C',
+                Primitive::Double => 'D',
+                Primitive::Float => 'F',
+                Primitive::Int => 'I',
+                Primitive::Long => 'J',
+                Primitive::Short => 'S',
+            }
+        )
+    }
+}
+
+#[cfg(jni)]
+impl From<Primitive> for jni::signature::Primitive {
+    fn from(p: Primitive) -> Self {
+        match p {
+            Primitive::Boolean => Self::Boolean,
+            Primitive::Byte => Self::Byte,
+            Primitive::Char => Self::Char,
+            Primitive::Double => Self::Double,
+            Primitive::Float => Self::Float,
+            Primitive::Int => Self::Int,
+            Primitive::Long => Self::Long,
+            Primitive::Short => Self::Short,
+        }
+    }
+}
+
+/// A Java return type. This may be `void`.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum ReturnType<'a> {
+    /// Java `void`. Only valid in return position.
+    Void,
+    /// A non-void return type.
+    Returns(JavaType<'a>),
+}
+
+impl<'a> ReturnType<'a> {
+    fn parse(s: &'a str) -> IResult<&'a str, ReturnType<'a>> {
+        use nom::branch::alt;
+        use nom::character::complete::char;
+        use nom::combinator::{map, value};
+
+        alt((
+            value(ReturnType::Void, char('V')),
+            map(JavaType::parse, ReturnType::Returns),
+        ))(s)
+    }
+
+    /// Try to parse a return type from the given string in JNI descriptor format.
+    pub fn try_from_str(s: &'a str) -> Option<Self> {
+        run_parser(Self::parse, s)
+    }
+
+    /// Check if the return type is `ReturnType::Void`
+    pub fn is_void(&self) -> bool {
+        matches!(*self, Self::Void)
+    }
+}
+
+impl<'a> From<JavaType<'a>> for ReturnType<'a> {
+    fn from(ty: JavaType<'a>) -> Self {
+        Self::Returns(ty)
+    }
+}
+
+#[cfg(jni)]
+impl<'a> From<ReturnType<'a>> for jni::signature::ReturnType {
+    fn from(ty: ReturnType<'a>) -> Self {
+        match ty {
+            ReturnType::Void => Self::Primitive(jni::signature::Primitive::Void),
+            ReturnType::Returns(ty) => ty.into(),
+        }
+    }
+}
+
+/// A type signature of a Java method.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct MethodSig<'a> {
+    /// The types of each argument.
+    pub args: Vec<JavaType<'a>>,
+    /// The return type.
+    pub ret: ReturnType<'a>,
+}
+
+impl<'a> MethodSig<'a> {
+    fn parse(s: &'a str) -> IResult<&'a str, MethodSig<'a>> {
+        use nom::character::complete::char;
+        use nom::combinator::map;
+        use nom::multi::many0;
+        use nom::sequence::delimited;
+
+        let parse_args = delimited(char('('), many0(JavaType::parse), char(')'));
+        let mut parser = map(parse_args.and(ReturnType::parse), |(args, ret)| MethodSig {
+            args,
+            ret,
+        });
+
+        parser(s)
+    }
+
+    /// Try to parse a method signature from the given string in JNI descriptor format.
+    pub fn try_from_str(s: &'a str) -> Option<Self> {
+        run_parser(Self::parse, s)
+    }
+}
+
+/// Helper to run a parser and return its result if it parsed the entire string.
+fn run_parser<'a, O, E>(mut parser: impl Parser<&'a str, O, E>, s: &'a str) -> Option<O> {
+    parser.parse(s).ok().and_then(|(rest, out)| {
+        if !rest.is_empty() {
+            return None;
+        }
+        Some(out)
+    })
+}
+
+#[cfg(test)]
+#[allow(clippy::unwrap_used, clippy::indexing_slicing)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn parse_primitive() {
+        let (rest, parsed) = JavaType::parse("II").unwrap();
+        assert_eq!("I", rest);
+        assert_eq!(
+            JavaType::NonArray(NonArray::Primitive(Primitive::Int)),
+            parsed
+        );
+    }
+
+    #[test]
+    fn parse_object() {
+        let (rest, parsed) = JavaType::parse("Ljava/lang/String;I").unwrap();
+        assert_eq!("I", rest);
+        assert_eq!(
+            JavaType::NonArray(NonArray::Object {
+                cls: "java/lang/String"
+            }),
+            parsed
+        );
+    }
+
+    #[test]
+    fn parse_primitive_array() {
+        let (rest, parsed) = JavaType::parse("[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[II").unwrap();
+        assert_eq!("I", rest);
+        assert_eq!(
+            JavaType::Array {
+                depth: NonZeroU8::new(255).unwrap(),
+                ty: NonArray::Primitive(Primitive::Int)
+            },
+            parsed
+        );
+    }
+
+    #[test]
+    fn parse_object_array() {
+        let (rest, parsed) = JavaType::parse("[[[Ljava/lang/String;I").unwrap();
+        assert_eq!("I", rest);
+        assert_eq!(
+            JavaType::Array {
+                depth: NonZeroU8::new(3).unwrap(),
+                ty: NonArray::Object {
+                    cls: "java/lang/String"
+                }
+            },
+            parsed
+        );
+    }
+
+    #[test]
+    fn parse_invalid_array() {
+        let opt = JavaType::try_from_str("[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[I");
+
+        assert_eq!(None, opt);
+    }
+
+    #[test]
+    fn parse_invalid_method() {
+        let parsed = MethodSig::try_from_str("I(I)I");
+        assert_eq!(None, parsed);
+    }
+
+    #[test]
+    fn parse_argless_method() {
+        let parsed = MethodSig::try_from_str("()V").unwrap();
+        assert_eq!(ReturnType::Void, parsed.ret);
+        assert_eq!(0, parsed.args.len());
+    }
+
+    #[test]
+    fn parse_void_method() {
+        let parsed = MethodSig::try_from_str("([ILjava/lang/String;Z)V").unwrap();
+        assert_eq!(ReturnType::Void, parsed.ret);
+        assert_eq!(3, parsed.args.len());
+        assert_eq!(
+            JavaType::Array {
+                depth: NonZeroU8::new(1).unwrap(),
+                ty: NonArray::Primitive(Primitive::Int)
+            },
+            parsed.args[0]
+        );
+        assert_eq!(
+            JavaType::NonArray(NonArray::Object {
+                cls: "java/lang/String"
+            }),
+            parsed.args[1]
+        );
+        assert_eq!(
+            JavaType::NonArray(NonArray::Primitive(Primitive::Boolean)),
+            parsed.args[2]
+        );
+    }
+
+    #[test]
+    fn parse_nonvoid_method() {
+        let parsed = MethodSig::try_from_str("([ILjava/lang/String;Z)D").unwrap();
+        assert_eq!(
+            ReturnType::Returns(JavaType::NonArray(NonArray::Primitive(Primitive::Double))),
+            parsed.ret
+        );
+        assert_eq!(3, parsed.args.len());
+        assert_eq!(
+            JavaType::Array {
+                depth: NonZeroU8::new(1).unwrap(),
+                ty: NonArray::Primitive(Primitive::Int)
+            },
+            parsed.args[0]
+        );
+        assert_eq!(
+            JavaType::NonArray(NonArray::Object {
+                cls: "java/lang/String"
+            }),
+            parsed.args[1]
+        );
+        assert_eq!(
+            JavaType::NonArray(NonArray::Primitive(Primitive::Boolean)),
+            parsed.args[2]
+        );
+    }
+
+    #[test]
+    fn parse_trailing_data_will_error() {
+        assert!(JavaType::try_from_str("Itrailing").is_none());
+        assert!(JavaType::try_from_str("Lcom/example/Foo;trailing").is_none());
+        assert!(JavaType::try_from_str("[Ztrailing").is_none());
+        assert!(MethodSig::try_from_str("()Itrailing").is_none());
+        assert!(MethodSig::try_from_str("()Lcom/example/Foo;trailing").is_none());
+        assert!(MethodSig::try_from_str("()[Ztrailing").is_none());
+    }
+
+    #[test]
+    fn java_type_roundtrip_through_display() {
+        let tests = [
+            "Z",
+            "C",
+            "B",
+            "S",
+            "I",
+            "J",
+            "F",
+            "D",
+            "Ljava/lang/String;",
+            "[Z",
+            "[[B",
+            "[[[Ljava/lang/String;",
+        ];
+
+        for test in tests {
+            let parsed = JavaType::try_from_str(test).unwrap();
+
+            let display = format!("{parsed}");
+
+            assert_eq!(test, display);
+        }
+    }
+
+    #[test]
+    fn test_is_prim() {
+        assert!(JavaType::try_from_str("I").unwrap().is_primitive());
+        assert!(JavaType::try_from_str("Z").unwrap().is_primitive());
+        assert!(!JavaType::try_from_str("[I").unwrap().is_primitive());
+        assert!(!JavaType::try_from_str("[Ljava/lang/String;")
+            .unwrap()
+            .is_primitive());
+        assert!(!JavaType::try_from_str("Ljava/lang/String;")
+            .unwrap()
+            .is_primitive());
+    }
+}
diff --git a/nearby/Cargo.lock b/nearby/Cargo.lock
index e50e7b7..da61780 100644
--- a/nearby/Cargo.lock
+++ b/nearby/Cargo.lock
@@ -61,9 +61,9 @@
 
 [[package]]
 name = "aho-corasick"
-version = "1.0.2"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
 dependencies = [
  "memchr",
 ]
@@ -144,6 +144,15 @@
 checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
 
 [[package]]
+name = "arbitrary"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+dependencies = [
+ "derive_arbitrary",
+]
+
+[[package]]
 name = "array_ref"
 version = "0.1.0"
 
@@ -238,9 +247,9 @@
 
 [[package]]
 name = "bstr"
-version = "1.6.0"
+version = "1.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05"
+checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
 dependencies = [
  "memchr",
  "serde",
@@ -252,10 +261,10 @@
 dependencies = [
  "anyhow",
  "chrono",
- "clap 4.5.1",
- "cmd-runner",
+ "clap 4.5.4",
+ "cmd_runner",
  "crossbeam",
- "env_logger",
+ "env_logger 0.10.2",
  "file-header",
  "glob",
  "globset",
@@ -268,7 +277,7 @@
  "tempfile",
  "thiserror",
  "walkdir",
- "which",
+ "xshell",
 ]
 
 [[package]]
@@ -311,7 +320,7 @@
 checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49"
 dependencies = [
  "clap 3.2.25",
- "heck",
+ "heck 0.4.1",
  "indexmap",
  "log",
  "proc-macro2",
@@ -328,6 +337,9 @@
 version = "1.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+dependencies = [
+ "jobserver",
+]
 
 [[package]]
 name = "cesu8"
@@ -343,13 +355,15 @@
 
 [[package]]
 name = "chrono"
-version = "0.4.34"
+version = "0.4.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
+ "js-sys",
  "num-traits",
+ "wasm-bindgen",
  "windows-targets 0.52.3",
 ]
 
@@ -407,9 +421,9 @@
 
 [[package]]
 name = "clap"
-version = "4.5.1"
+version = "4.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
+checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -417,9 +431,9 @@
 
 [[package]]
 name = "clap_builder"
-version = "4.5.1"
+version = "4.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
+checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
 dependencies = [
  "anstream",
  "anstyle",
@@ -429,11 +443,11 @@
 
 [[package]]
 name = "clap_derive"
-version = "4.5.0"
+version = "4.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
+checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
 dependencies = [
- "heck",
+ "heck 0.5.0",
  "proc-macro2",
  "quote",
  "syn 2.0.50",
@@ -455,12 +469,18 @@
 checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
 
 [[package]]
-name = "cmd-runner"
+name = "cmd_runner"
 version = "0.1.0"
 dependencies = [
  "anyhow",
+ "chrono",
+ "clap 4.5.4",
+ "file-header",
+ "globset",
+ "log",
  "owo-colors",
  "shell-escape",
+ "xshell",
 ]
 
 [[package]]
@@ -518,10 +538,10 @@
  "anes",
  "cast",
  "ciborium",
- "clap 4.5.1",
+ "clap 4.5.4",
  "criterion-plot",
  "is-terminal",
- "itertools",
+ "itertools 0.10.5",
  "num-traits",
  "once_cell",
  "oorandom",
@@ -542,7 +562,7 @@
 checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
 dependencies = [
  "cast",
- "itertools",
+ "itertools 0.10.5",
 ]
 
 [[package]]
@@ -619,7 +639,7 @@
 checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15"
 dependencies = [
  "generic-array",
- "rand_core 0.6.4",
+ "rand_core",
  "subtle",
  "zeroize",
 ]
@@ -631,7 +651,7 @@
 checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
 dependencies = [
  "generic-array",
- "rand_core 0.6.4",
+ "rand_core",
  "typenum",
 ]
 
@@ -686,7 +706,7 @@
  "p256",
  "rand",
  "rand_chacha",
- "rand_core 0.6.4",
+ "rand_core",
  "sec1",
  "sha2",
  "subtle",
@@ -716,6 +736,16 @@
 ]
 
 [[package]]
+name = "crypto_provider_test-fuzz"
+version = "0.0.0"
+dependencies = [
+ "crypto_provider",
+ "crypto_provider_default",
+ "derive_fuzztest",
+ "libfuzzer-sys",
+]
+
+[[package]]
 name = "ctr"
 version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -763,6 +793,35 @@
 ]
 
 [[package]]
+name = "derive_arbitrary"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.50",
+]
+
+[[package]]
+name = "derive_fuzztest"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "derive_fuzztest_macro",
+ "quickcheck",
+]
+
+[[package]]
+name = "derive_fuzztest_macro"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.50",
+]
+
+[[package]]
 name = "diff"
 version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -797,7 +856,7 @@
 dependencies = [
  "curve25519-dalek",
  "ed25519",
- "rand_core 0.6.4",
+ "rand_core",
  "serde",
  "sha2",
  "subtle",
@@ -823,7 +882,7 @@
  "generic-array",
  "group",
  "hkdf",
- "rand_core 0.6.4",
+ "rand_core",
  "sec1",
  "subtle",
  "zeroize",
@@ -831,6 +890,16 @@
 
 [[package]]
 name = "env_logger"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "env_logger"
 version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
@@ -864,7 +933,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
 dependencies = [
- "rand_core 0.6.4",
+ "rand_core",
  "subtle",
 ]
 
@@ -898,12 +967,6 @@
 ]
 
 [[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
 name = "generic-array"
 version = "0.14.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -943,15 +1006,15 @@
 
 [[package]]
 name = "globset"
-version = "0.4.12"
+version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aca8bbd8e0707c1887a8bbb7e6b40e228f251ff5d62c8220a4a7a53c73aff006"
+checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
 dependencies = [
  "aho-corasick",
  "bstr",
- "fnv",
  "log",
- "regex",
+ "regex-automata",
+ "regex-syntax",
 ]
 
 [[package]]
@@ -961,7 +1024,7 @@
 checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
 dependencies = [
  "ff",
- "rand_core 0.6.4",
+ "rand_core",
  "subtle",
 ]
 
@@ -975,8 +1038,6 @@
 name = "handle_map"
 version = "0.1.0"
 dependencies = [
- "criterion",
- "lazy_static",
  "lock_adapter",
 ]
 
@@ -1007,6 +1068,12 @@
 checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 
 [[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
 name = "hermit-abi"
 version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1121,22 +1188,21 @@
 ]
 
 [[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
 name = "itoa"
 version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
 
 [[package]]
-name = "java-locator"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90003f2fd9c52f212c21d8520f1128da0080bad6fff16b68fe6e7f2f0c3780c2"
-dependencies = [
- "glob",
- "lazy_static",
-]
-
-[[package]]
 name = "jni"
 version = "0.21.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1145,9 +1211,7 @@
  "cesu8",
  "cfg-if",
  "combine",
- "java-locator",
  "jni-sys",
- "libloading",
  "log",
  "thiserror",
  "walkdir",
@@ -1161,6 +1225,15 @@
 checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
 
 [[package]]
+name = "jobserver"
+version = "0.1.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2"
+dependencies = [
+ "libc",
+]
+
+[[package]]
 name = "js-sys"
 version = "0.3.64"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1186,7 +1259,7 @@
  "anyhow",
  "base64",
  "blake2",
- "clap 4.5.1",
+ "clap 4.5.4",
  "criterion",
  "crypto_provider",
  "crypto_provider_default",
@@ -1206,6 +1279,32 @@
 ]
 
 [[package]]
+name = "ldt-fuzz"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "crypto_provider_rustcrypto",
+ "derive_fuzztest",
+ "ldt",
+ "libfuzzer-sys",
+ "xts_aes",
+]
+
+[[package]]
+name = "ldt-np-adv-fuzz"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "crypto_provider_rustcrypto",
+ "derive_fuzztest",
+ "ldt",
+ "ldt_np_adv",
+ "libfuzzer-sys",
+ "np_hkdf",
+ "xts_aes",
+]
+
+[[package]]
 name = "ldt_np_adv"
 version = "0.1.0"
 dependencies = [
@@ -1225,6 +1324,7 @@
  "rand_pcg",
  "serde_json",
  "test_helper",
+ "test_vector_hkdf",
  "xts_aes",
 ]
 
@@ -1270,13 +1370,14 @@
 checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
 
 [[package]]
-name = "libloading"
-version = "0.7.4"
+name = "libfuzzer-sys"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
 dependencies = [
- "cfg-if",
- "winapi",
+ "arbitrary",
+ "cc",
+ "once_cell",
 ]
 
 [[package]]
@@ -1315,15 +1416,15 @@
 
 [[package]]
 name = "log"
-version = "0.4.20"
+version = "0.4.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
 
 [[package]]
 name = "memchr"
-version = "2.6.4"
+version = "2.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
 
 [[package]]
 name = "memoffset"
@@ -1383,6 +1484,7 @@
  "strum",
  "strum_macros",
  "test_helper",
+ "test_vector_hkdf",
  "tinyvec",
  "xts_aes",
 ]
@@ -1394,6 +1496,7 @@
  "array_view",
  "crypto_provider",
  "np_adv",
+ "np_hkdf",
  "sink",
 ]
 
@@ -1430,6 +1533,8 @@
  "np_adv",
  "np_adv_dynamic",
  "np_hkdf",
+ "strum",
+ "strum_macros",
 ]
 
 [[package]]
@@ -1446,10 +1551,24 @@
  "rand_ext",
  "serde_json",
  "test_helper",
+ "test_vector_hkdf",
  "xts_aes",
 ]
 
 [[package]]
+name = "np_java_ffi"
+version = "0.1.0"
+dependencies = [
+ "crypto_provider_default",
+ "handle_map",
+ "jni",
+ "np_adv",
+ "np_ffi_core",
+ "pourover",
+ "pourover_macro",
+]
+
+[[package]]
 name = "num-bigint"
 version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1597,13 +1716,7 @@
 name = "pourover_macro"
 version = "0.1.0"
 dependencies = [
- "pourover_macro_core",
-]
-
-[[package]]
-name = "pourover_macro_core"
-version = "0.1.0"
-dependencies = [
+ "nom",
  "proc-macro2",
  "quote",
  "syn 2.0.50",
@@ -1685,6 +1798,17 @@
 ]
 
 [[package]]
+name = "quickcheck"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
+dependencies = [
+ "env_logger 0.8.4",
+ "log",
+ "rand",
+]
+
+[[package]]
 name = "quote"
 version = "1.0.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1701,7 +1825,7 @@
 dependencies = [
  "libc",
  "rand_chacha",
- "rand_core 0.6.4",
+ "rand_core",
 ]
 
 [[package]]
@@ -1711,17 +1835,11 @@
 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
 dependencies = [
  "ppv-lite86",
- "rand_core 0.6.4",
+ "rand_core",
 ]
 
 [[package]]
 name = "rand_core"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
-
-[[package]]
-name = "rand_core"
 version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
@@ -1730,14 +1848,6 @@
 ]
 
 [[package]]
-name = "rand_core_05_adapter"
-version = "0.1.0"
-dependencies = [
- "rand",
- "rand_core 0.5.1",
-]
-
-[[package]]
 name = "rand_ext"
 version = "0.1.0"
 dependencies = [
@@ -1753,7 +1863,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e"
 dependencies = [
- "rand_core 0.6.4",
+ "rand_core",
 ]
 
 [[package]]
@@ -2039,7 +2149,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
 dependencies = [
- "heck",
+ "heck 0.4.1",
  "proc-macro2",
  "quote",
  "rustversion",
@@ -2100,10 +2210,19 @@
 version = "0.1.0"
 dependencies = [
  "hex",
+ "itertools 0.12.1",
  "serde_json",
 ]
 
 [[package]]
+name = "test_vector_hkdf"
+version = "0.1.0"
+dependencies = [
+ "crypto_provider",
+ "crypto_provider_default",
+]
+
+[[package]]
 name = "textwrap"
 version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2172,7 +2291,6 @@
  "rand",
  "rand_chacha",
  "ukey2_connections",
- "ukey2_rs",
 ]
 
 [[package]]
@@ -2191,6 +2309,19 @@
 ]
 
 [[package]]
+name = "ukey2_connections-fuzz"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "crypto_provider_rustcrypto",
+ "derive_fuzztest",
+ "libfuzzer-sys",
+ "rand_chacha",
+ "ukey2_connections",
+ "ukey2_rs",
+]
+
+[[package]]
 name = "ukey2_jni"
 version = "0.1.0"
 dependencies = [
@@ -2203,7 +2334,6 @@
  "rand",
  "rand_chacha",
  "ukey2_connections",
- "ukey2_rs",
 ]
 
 [[package]]
@@ -2232,10 +2362,9 @@
 name = "ukey2_shell"
 version = "0.1.0"
 dependencies = [
- "clap 4.5.1",
+ "clap 4.5.4",
  "crypto_provider_rustcrypto",
  "ukey2_connections",
- "ukey2_rs",
 ]
 
 [[package]]
@@ -2620,7 +2749,35 @@
 checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
 dependencies = [
  "curve25519-dalek",
- "rand_core 0.6.4",
+ "rand_core",
+]
+
+[[package]]
+name = "xshell"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437"
+dependencies = [
+ "xshell-macros",
+]
+
+[[package]]
+name = "xshell-macros"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852"
+
+[[package]]
+name = "xts-aes-fuzz"
+version = "0.0.0"
+dependencies = [
+ "arbitrary",
+ "crypto_provider",
+ "crypto_provider_rustcrypto",
+ "derive_fuzztest",
+ "ldt_tbc",
+ "libfuzzer-sys",
+ "xts_aes",
 ]
 
 [[package]]
diff --git a/nearby/Cargo.toml b/nearby/Cargo.toml
index 4897eaa..f69bcf7 100644
--- a/nearby/Cargo.toml
+++ b/nearby/Cargo.toml
@@ -2,6 +2,7 @@
 members = [
     "connections/ukey2/ukey2",
     "connections/ukey2/ukey2_connections",
+    "connections/ukey2/ukey2_connections/fuzz",
     "connections/ukey2/ukey2_c_ffi",
     "connections/ukey2/ukey2_jni",
     "connections/ukey2/ukey2_proto",
@@ -10,11 +11,13 @@
     "crypto/crypto_provider_rustcrypto",
     "crypto/crypto_provider_stubs",
     "crypto/crypto_provider_test",
+    "crypto/crypto_provider_test/fuzz",
     "crypto/crypto_provider_default",
-    "crypto/rand_core_05_adapter",
     "presence/array_view",
     "presence/ldt",
+    "presence/ldt/fuzz",
     "presence/ldt_np_adv",
+    "presence/ldt_np_adv/fuzz",
     "presence/ldt_np_adv_ffi",
     "presence/ldt_np_jni",
     "presence/ldt_tbc",
@@ -24,15 +27,13 @@
     "presence/np_ed25519",
     "presence/np_ffi_core",
     "presence/np_hkdf",
+    "presence/np_java_ffi",
     "presence/rand_ext",
     "presence/sink",
     "presence/test_helper",
+    "presence/test_vector_hkdf",
     "presence/xts_aes",
-    "util/lock_adapter",
-    "util/handle_map",
-    "util/pourover",
-    "util/pourover_macro",
-    "util/pourover_macro_core",
+    "presence/xts_aes/fuzz",
 ]
 
 # TODO: remove boringssl once we figure out a better plan for integrating the build system
@@ -41,35 +42,35 @@
 ]
 
 [workspace.lints.rust]
-unsafe_code = "deny"
 missing_docs = "deny"
 trivial_casts = "deny"
 trivial_numeric_casts = "deny"
+unsafe_code = "deny"
+unsafe_op_in_unsafe_fn = "deny"
 unused_extern_crates = "deny"
 unused_import_braces = "deny"
 unused_results = "deny"
 
 [workspace.lints.clippy]
-indexing_slicing = "deny"
-unwrap_used = "deny"
-panic = "deny"
 expect_used = "deny"
+indexing_slicing = "deny"
+panic = "deny"
+unwrap_used = "deny"
 
 [workspace.dependencies]
 # local crates
 array_ref = { path = "presence/array_ref" }
 array_view = { path = "presence/array_view" }
 crypto_provider = { path = "crypto/crypto_provider", default-features = false }
-crypto_provider_default = { path = "crypto/crypto_provider_default", default-features=false}
+crypto_provider_default = { path = "crypto/crypto_provider_default", default-features = false }
 crypto_provider_boringssl = { path = "crypto/crypto_provider_boringssl" }
 crypto_provider_rustcrypto = { path = "crypto/crypto_provider_rustcrypto" }
 crypto_provider_stubs = { path = "crypto/crypto_provider_stubs" }
 crypto_provider_test = { path = "crypto/crypto_provider_test" }
-lock_adapter = { path = "util/lock_adapter" }
-handle_map = { path = "util/handle_map" }
-rand_core_05_adapter = { path = "crypto/rand_core_05_adapter" }
 rand_ext = { path = "presence/rand_ext" }
 test_helper = { path = "presence/test_helper" }
+ukey2_connections = { path = "connections/ukey2/ukey2_connections" }
+ukey2_rs = { path = "connections/ukey2/ukey2" }
 ukey2_proto = { path = "connections/ukey2/ukey2_proto" }
 np_hkdf = { path = "presence/np_hkdf" }
 xts_aes = { path = "presence/xts_aes" }
@@ -80,10 +81,17 @@
 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 }
-pourover = { path = "util/pourover" }
-pourover_macro = { path = "util/pourover_macro" }
-pourover_macro_core = { path = "util/pourover_macro_core" }
+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" }
+pourover_macro = { path = "../common/pourover_macro" }
 
 # from crates.io
 rand = { version = "0.8.5", default-features = false }
@@ -91,10 +99,12 @@
 rand_pcg = "0.3.1"
 sha2 = { version = "0.10.8", default-features = false }
 aes = "0.8.3"
+arbitrary = "1.3.2"
 cbc = { version = "0.1.2", features = ["block-padding"] }
 ctr = "0.9.2"
 hkdf = "0.12.3"
 hmac = "0.12.1"
+nom = { version = "7.1.3", default-features = false }
 ed25519-dalek = { version = "2.1.0", default-features = false }
 ed25519 = "2.2.3"
 aes-gcm = "0.10.3"
@@ -144,6 +154,11 @@
 syn = { version = "2.0", features = ["full"] }
 proc-macro2 = "1.0"
 quote = "1.0"
+itertools = "0.12.1"
+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"
 
 [workspace.package]
 version = "0.1.0"
@@ -178,7 +193,7 @@
 
 [dependencies]
 clap.workspace = true
-cmd-runner = { path = "../cmd-runner" }
+cmd_runner = { path = "../common/cmd_runner" }
 anyhow.workspace = true
 shell-escape = "0.1.5"
 owo-colors.workspace = true
@@ -191,10 +206,10 @@
 thiserror.workspace = true
 log.workspace = true
 env_logger.workspace = true
-which = "4.4.0"
 file-header = "0.1.2"
-serde_json.workspace = true
+serde_json = { workspace = true, features = ["std"] }
 regex = "1.10.2"
+xshell = "0.2.6"
 
 [dev-dependencies]
 tempfile.workspace = true
diff --git a/nearby/README.md b/nearby/README.md
index 86db67d..f951a8b 100644
--- a/nearby/README.md
+++ b/nearby/README.md
@@ -32,10 +32,10 @@
 protobuf in order to build correctly. To make the setup of this easier you can
 use Docker to handle setting up the environment in a container.
 
-First install Docker then build and run the image:
+First install Docker then build and run the image from repo root:
 
 ```
-sudo docker build -t nearby_rust:v1.0 ..
+sudo docker build -t nearby_rust:v1.0 .
 sudo docker run --rm -it nearby_rust:v1.0
 ```
 
diff --git a/nearby/connections/ukey2/ukey2/src/lib.rs b/nearby/connections/ukey2/ukey2/src/lib.rs
index dc17b83..4d64bc8 100644
--- a/nearby/connections/ukey2/ukey2/src/lib.rs
+++ b/nearby/connections/ukey2/ukey2/src/lib.rs
@@ -25,6 +25,7 @@
 mod tests;
 mod ukey2_handshake;
 
+pub use proto_adapter::NextProtocol;
 pub use state_machine::{SendAlert, StateMachine};
 pub use ukey2_handshake::{
     CompletedHandshake, HandshakeImplementation, Ukey2Client, Ukey2ClientStage1, Ukey2Server,
diff --git a/nearby/connections/ukey2/ukey2/src/proto_adapter.rs b/nearby/connections/ukey2/ukey2/src/proto_adapter.rs
index 2986276..0ecbe8a 100644
--- a/nearby/connections/ukey2/ukey2/src/proto_adapter.rs
+++ b/nearby/connections/ukey2/ukey2/src/proto_adapter.rs
@@ -17,6 +17,8 @@
 use crypto_provider::elliptic_curve::EcdhProvider;
 use crypto_provider::p256::{P256EcdhProvider, P256PublicKey, P256};
 use crypto_provider::CryptoProvider;
+use std::collections::HashSet;
+use std::fmt::{Display, Formatter};
 use ukey2_proto::ukey2_all_proto::{securemessage, ukey};
 
 /// For generated proto types for UKEY2 messages
@@ -71,6 +73,41 @@
     fn into_adapter(self) -> Result<A, ukey::ukey2alert::AlertType>;
 }
 
+/// Enum representing the different supported next_protocol strings, ordered by desirability.
+#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
+#[repr(i32)]
+pub enum NextProtocol {
+    /// AES-256-GCM-SIV, for use with newer clients.
+    Aes256GcmSiv,
+    /// AES_256_CBC-HMAC_SHA256, already in use and supported by all clients.
+    Aes256CbcHmacSha256,
+}
+
+impl TryFrom<&String> for NextProtocol {
+    type Error = ukey::ukey2alert::AlertType;
+
+    fn try_from(value: &String) -> Result<Self, Self::Error> {
+        match value.as_str() {
+            "AES_256_GCM_SIV" => Ok(NextProtocol::Aes256GcmSiv),
+            "AES_256_CBC-HMAC_SHA256" => Ok(NextProtocol::Aes256CbcHmacSha256),
+            _ => Err(ukey::ukey2alert::AlertType::BAD_NEXT_PROTOCOL),
+        }
+    }
+}
+
+impl Display for NextProtocol {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "{}",
+            match self {
+                NextProtocol::Aes256CbcHmacSha256 => "AES_256_CBC-HMAC_SHA256",
+                NextProtocol::Aes256GcmSiv => "AES_256_GCM_SIV",
+            },
+        )
+    }
+}
+
 #[derive(Debug, PartialEq, Eq)]
 pub(crate) enum MessageType {
     ClientInit,
@@ -81,7 +118,7 @@
 pub(crate) struct ClientInit {
     version: i32,
     commitments: Vec<CipherCommitment>,
-    next_protocol: String,
+    next_protocols: HashSet<NextProtocol>,
 }
 
 impl ClientInit {
@@ -93,8 +130,8 @@
         &self.commitments
     }
 
-    pub fn next_protocol(&self) -> &str {
-        &self.next_protocol
+    pub fn next_protocols(&self) -> &HashSet<NextProtocol> {
+        &self.next_protocols
     }
 }
 
@@ -104,6 +141,7 @@
     random: [u8; 32],
     handshake_cipher: HandshakeCipher,
     pub(crate) public_key: Vec<u8>,
+    selected_next_protocol: NextProtocol,
 }
 
 impl ServerInit {
@@ -114,6 +152,10 @@
     pub fn handshake_cipher(&self) -> HandshakeCipher {
         self.handshake_cipher
     }
+
+    pub fn selected_next_protocol(&self) -> NextProtocol {
+        self.selected_next_protocol
+    }
 }
 
 pub(crate) struct ClientFinished {
@@ -208,8 +250,13 @@
             .next_protocol
             .filter(|n| !n.is_empty())
             .ok_or(ukey::ukey2alert::AlertType::BAD_NEXT_PROTOCOL)?;
+        let mut next_protocols: HashSet<NextProtocol> =
+            HashSet::from([(&next_protocol).try_into()?]);
+        let other_next_protocols: Vec<NextProtocol> =
+            self.next_protocols.iter().filter_map(|p| p.try_into().ok()).collect();
+        next_protocols.extend(&other_next_protocols);
         Ok(ClientInit {
-            next_protocol,
+            next_protocols,
             version,
             commitments: self
                 .cipher_commitments
@@ -234,7 +281,11 @@
         // We will be handling bad pubkeys in the layers above
         let public_key: Vec<u8> =
             self.public_key.ok_or(ukey::ukey2alert::AlertType::BAD_PUBLIC_KEY)?;
-        Ok(ServerInit { handshake_cipher, version, public_key, random })
+        let selected_next_protocol = self
+            .selected_next_protocol
+            .and_then(|p| (&p).try_into().ok())
+            .unwrap_or(NextProtocol::Aes256CbcHmacSha256);
+        Ok(ServerInit { handshake_cipher, version, public_key, random, selected_next_protocol })
     }
 }
 
diff --git a/nearby/connections/ukey2/ukey2/src/state_machine.rs b/nearby/connections/ukey2/ukey2/src/state_machine.rs
index d2021e6..b4ad360 100644
--- a/nearby/connections/ukey2/ukey2/src/state_machine.rs
+++ b/nearby/connections/ukey2/ukey2/src/state_machine.rs
@@ -12,17 +12,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use std::fmt::Debug;
+
+use log::error;
+
+use crypto_provider::CryptoProvider;
+use ukey2_proto::protobuf::Message;
+use ukey2_proto::ukey2_all_proto::ukey;
+
 use crate::proto_adapter::{IntoAdapter, MessageType, ToWrappedMessage as _};
 use crate::ukey2_handshake::ClientFinishedError;
 use crate::ukey2_handshake::{
     ClientInit, ClientInitError, Ukey2Client, Ukey2ClientStage1, Ukey2Server, Ukey2ServerStage1,
     Ukey2ServerStage2,
 };
-use crypto_provider::CryptoProvider;
-use log::error;
-use std::fmt::Debug;
-use ukey2_proto::protobuf::Message;
-use ukey2_proto::ukey2_all_proto::ukey;
 
 /// An alert type and message to be sent to the other party.
 #[derive(Debug, PartialEq, Eq)]
diff --git a/nearby/connections/ukey2/ukey2/src/tests.rs b/nearby/connections/ukey2/ukey2/src/tests.rs
index 92c0358..1b2424a 100644
--- a/nearby/connections/ukey2/ukey2/src/tests.rs
+++ b/nearby/connections/ukey2/ukey2/src/tests.rs
@@ -12,22 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#![allow(clippy::unwrap_used)]
+use std::collections::hash_set;
+
+use rand::rngs::StdRng;
+use rand::{Rng, SeedableRng};
+use sha2::Digest;
 
 use crate::{
     proto_adapter::{IntoAdapter as _, MessageType, ToWrappedMessage as _},
     ukey2_handshake::HandshakeCipher,
-    HandshakeImplementation, StateMachine, Ukey2ClientStage1, Ukey2ServerStage1,
+    HandshakeImplementation, NextProtocol, StateMachine, Ukey2ClientStage1, Ukey2ServerStage1,
 };
 use crypto_provider::elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey};
 use crypto_provider::p256::P256;
 use crypto_provider::x25519::X25519;
 use crypto_provider::{CryptoProvider, CryptoRng};
 use crypto_provider_default::CryptoProviderImpl;
-use rand::rngs::StdRng;
-use rand::{Rng, SeedableRng};
-use sha2::Digest;
-use std::collections::hash_set;
 use ukey2_proto::protobuf::Message;
 use ukey2_proto::ukey2_all_proto::ukey;
 
@@ -39,11 +39,12 @@
     <<CryptoProviderImpl as CryptoProvider>::P256 as EcdhProvider<P256>>::EphemeralSecret;
 
 #[test]
+#[allow(clippy::unwrap_used)]
 fn advance_from_init_to_finish_client_test() {
     let mut rng = StdRng::from_entropy();
     let client1 = Ukey2ClientStage1::<CryptoProviderImpl>::from(
         &mut rng,
-        "next protocol".to_string(),
+        vec![NextProtocol::Aes256CbcHmacSha256],
         HandshakeImplementation::Spec,
     );
 
@@ -68,6 +69,7 @@
 }
 
 #[test]
+#[allow(clippy::unwrap_used)]
 fn advance_from_init_to_complete_server_x25519_test() {
     let mut rng = StdRng::from_entropy();
     let mut next_protocols = hash_set::HashSet::new();
@@ -121,6 +123,7 @@
 }
 
 #[test]
+#[allow(clippy::unwrap_used)]
 fn advance_from_init_to_complete_server_p256_test() {
     let mut rng = StdRng::from_entropy();
     let mut next_protocols = hash_set::HashSet::new();
@@ -182,6 +185,7 @@
 }
 
 #[test]
+#[allow(clippy::unwrap_used)]
 fn convert_to_message_type() {
     assert_eq!(
         MessageType::ClientInit,
@@ -198,7 +202,28 @@
 }
 
 #[test]
+#[allow(clippy::unwrap_used)]
 fn convert_to_cipher_type() {
     assert_eq!(HandshakeCipher::P256Sha512, 100.into_adapter().unwrap());
     assert_eq!(HandshakeCipher::Curve25519Sha512, 200.into_adapter().unwrap());
 }
+
+#[test]
+fn convert_next_protocols() {
+    assert_eq!(
+        (&"AES_256_CBC-HMAC_SHA256".to_string()).try_into(),
+        Ok(NextProtocol::Aes256CbcHmacSha256)
+    );
+    assert_eq!((&"AES_256_GCM_SIV".to_string()).try_into(), Ok(NextProtocol::Aes256GcmSiv));
+    assert_eq!(
+        TryInto::<NextProtocol>::try_into(&"Random protocol".to_string()),
+        Err(ukey::ukey2alert::AlertType::BAD_NEXT_PROTOCOL)
+    );
+}
+
+#[test]
+fn sort_next_protocols() {
+    let mut next_protocols = [NextProtocol::Aes256CbcHmacSha256, NextProtocol::Aes256GcmSiv];
+    next_protocols.sort();
+    assert_eq!(next_protocols, [NextProtocol::Aes256GcmSiv, NextProtocol::Aes256CbcHmacSha256])
+}
diff --git a/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs b/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs
index 0b2be0e..95d3b92 100644
--- a/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs
+++ b/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs
@@ -16,10 +16,12 @@
 // TODO: remove this and convert all unwraps to expects
 #![allow(clippy::unwrap_used)]
 
-pub(crate) use crate::proto_adapter::{
-    CipherCommitment, ClientFinished, ClientInit, GenericPublicKey, HandshakeCipher,
-    IntoAdapter as _, ServerInit, ToWrappedMessage as _,
+use std::{
+    collections::HashSet,
+    fmt::{self, Formatter},
+    marker::PhantomData,
 };
+
 use crypto_provider::elliptic_curve::EphemeralSecret;
 use crypto_provider::p256::{P256EcdhProvider, P256PublicKey, P256};
 use crypto_provider::x25519::X25519;
@@ -30,14 +32,15 @@
     sha2::{Sha256, Sha512},
     CryptoRng,
 };
-use std::{
-    collections::hash_set,
-    fmt::{self, Formatter},
-    marker::PhantomData,
-};
 use ukey2_proto::protobuf::Message;
 use ukey2_proto::ukey2_all_proto::{securemessage, ukey};
 
+use crate::proto_adapter::NextProtocol;
+pub(crate) use crate::proto_adapter::{
+    CipherCommitment, ClientFinished, ClientInit, GenericPublicKey, HandshakeCipher,
+    IntoAdapter as _, ServerInit, ToWrappedMessage as _,
+};
+
 pub trait WireCompatibilityLayer {
     fn encode_public_key<C: CryptoProvider>(
         &self,
@@ -145,7 +148,7 @@
 }
 
 pub struct Ukey2ServerStage1<C: CryptoProvider> {
-    pub(crate) next_protocols: hash_set::HashSet<String>,
+    pub(crate) next_protocols: HashSet<NextProtocol>,
     pub(crate) handshake_impl: HandshakeImplementation,
     _marker: PhantomData<C>,
 }
@@ -157,11 +160,12 @@
 }
 
 impl<C: CryptoProvider> Ukey2ServerStage1<C> {
-    pub fn from(
-        next_protocols: hash_set::HashSet<String>,
-        handshake_impl: HandshakeImplementation,
-    ) -> Self {
-        Self { next_protocols, handshake_impl, _marker: PhantomData }
+    pub fn from(next_protocols: HashSet<String>, handshake_impl: HandshakeImplementation) -> Self {
+        Self {
+            next_protocols: next_protocols.iter().filter_map(|p| p.try_into().ok()).collect(),
+            handshake_impl,
+            _marker: PhantomData,
+        }
     }
 
     pub(crate) fn handle_client_init<R: rand::Rng + rand::CryptoRng>(
@@ -174,10 +178,11 @@
             return Err(ClientInitError::BadVersion);
         }
 
-        let next_protocol = client_init.next_protocol();
-        if !self.next_protocols.contains(next_protocol) {
+        let next_protocols = client_init.next_protocols();
+        let Some(selected_protocol) = next_protocols.intersection(&self.next_protocols).min()
+        else {
             return Err(ClientInitError::BadNextProtocol);
-        }
+        };
 
         // nothing to check here about client_init.random -- already been validated as 32 bytes
 
@@ -207,7 +212,7 @@
                     commitment.clone(),
                     secret,
                     self.handshake_impl,
-                    next_protocol.to_string(),
+                    *selected_protocol,
                 ))
             }
             HandshakeCipher::P256Sha512 => {
@@ -224,7 +229,7 @@
                     commitment.clone(),
                     secret,
                     self.handshake_impl,
-                    next_protocol.to_string(),
+                    *selected_protocol,
                 ))
             }
         }
@@ -242,7 +247,7 @@
     commitment: CipherCommitment,
     key_pair: ServerKeyPair<C>,
     pub(crate) handshake_impl: HandshakeImplementation,
-    next_protocol: String,
+    next_protocol: NextProtocol,
     _marker: PhantomData<C>,
 }
 
@@ -262,7 +267,7 @@
         commitment: CipherCommitment,
         key_pair: ServerKeyPair<C>,
         handshake_impl: HandshakeImplementation,
-        next_protocol: String,
+        next_protocol: NextProtocol,
     ) -> Self {
         let random: [u8; 32] = rng.gen();
         let mut server_init = ukey::Ukey2ServerInit::default();
@@ -278,6 +283,7 @@
                 )
                 .unwrap(),
         });
+        server_init.set_selected_next_protocol(next_protocol.to_string());
 
         Self {
             client_init_msg,
@@ -372,7 +378,7 @@
     client_init_bytes: Vec<u8>,
     commitment_ciphers: Vec<HandshakeCipher>,
     handshake_impl: HandshakeImplementation,
-    next_protocol: String,
+    next_protocols: Vec<NextProtocol>,
     _marker: PhantomData<C>,
 }
 
@@ -383,11 +389,15 @@
 }
 
 impl<C: CryptoProvider> Ukey2ClientStage1<C> {
+    // Clippy: we assert that there must be at least one element in `next_protocols`, so indexing
+    // [0] is safe.
+    #[allow(clippy::indexing_slicing)]
     pub fn from<R: rand::Rng + rand::SeedableRng + rand::CryptoRng>(
         rng: &mut R,
-        next_protocol: String,
+        next_protocols: Vec<NextProtocol>,
         handshake_impl: HandshakeImplementation,
     ) -> Self {
+        assert!(!next_protocols.is_empty());
         let random = rng.gen::<[u8; 32]>().to_vec();
         // Curve25519 ClientFinished Message
         let curve25519_secret =
@@ -408,10 +418,10 @@
 
         // P256 ClientFinished Message
         let p256_secret = <C::P256 as EcdhProvider<P256>>::EphemeralSecret::generate_random(
-                        &mut<<<C::P256 as EcdhProvider<P256>>::EphemeralSecret as EphemeralSecret<
-                            P256,
-                        >>::Rng as CryptoRng>::new(),
-                    );
+            &mut <<<C::P256 as EcdhProvider<P256>>::EphemeralSecret as EphemeralSecret<
+                P256,
+            >>::Rng as CryptoRng>::new(),
+        );
         let p256_client_finished_bytes = {
             let client_finished = ukey::Ukey2ClientFinished {
                 public_key: Some(
@@ -446,7 +456,8 @@
                 version: Some(1),
                 random: Some(random),
                 cipher_commitments: vec![curve25519_commitment, p256_commitment],
-                next_protocol: Some(next_protocol.to_string()),
+                next_protocol: Some(next_protocols[0].to_string()),
+                next_protocols: next_protocols.iter().map(|x| x.to_string()).collect(),
                 ..Default::default()
             };
             client_init.to_wrapped_msg().write_to_bytes().unwrap()
@@ -463,7 +474,7 @@
                 HandshakeCipher::P256Sha512,
             ],
             handshake_impl,
-            next_protocol,
+            next_protocols,
             _marker: PhantomData,
         }
     }
@@ -481,6 +492,11 @@
             return Err(ServerInitError::BadVersion);
         }
 
+        if !self.next_protocols.contains(&server_init.selected_next_protocol()) {
+            return Err(ServerInitError::BadNextProtocol);
+        }
+        let next_protocol = server_init.selected_next_protocol();
+
         // loop over all commitments every time for a semblance of constant time-ness
         let server_cipher = self
             .commitment_ciphers
@@ -537,7 +553,7 @@
                 self.client_init_bytes,
                 server_init_bytes.to_vec(),
                 shared_secret_bytes,
-                self.next_protocol,
+                next_protocol,
             ),
         })
     }
@@ -551,6 +567,8 @@
     BadPublicKey,
     /// The diffie-hellman key exchange failed to generate a shared secret
     BadKeyExchange,
+    /// The server sent an invalid next protocol that is not available to the client.
+    BadNextProtocol,
 }
 
 #[derive(Clone)]
@@ -596,7 +614,7 @@
     client_init_bytes: Vec<u8>,
     server_init_bytes: Vec<u8>,
     shared_secret: Vec<u8>,
-    pub next_protocol: String,
+    pub next_protocol: NextProtocol,
 }
 
 impl CompletedHandshake {
@@ -604,7 +622,7 @@
         client_init_bytes: Vec<u8>,
         server_init_bytes: Vec<u8>,
         shared_secret: Vec<u8>,
-        next_protocol: String,
+        next_protocol: NextProtocol,
     ) -> Self {
         Self { client_init_bytes, server_init_bytes, shared_secret, next_protocol }
     }
diff --git a/nearby/connections/ukey2/ukey2/tests/tests.rs b/nearby/connections/ukey2/ukey2/tests/tests.rs
index 85b972c..d0acacf 100644
--- a/nearby/connections/ukey2/ukey2/tests/tests.rs
+++ b/nearby/connections/ukey2/ukey2/tests/tests.rs
@@ -21,9 +21,8 @@
 
 #[test]
 fn full_integration_state_machine() {
-    let mut next_protocols = hash_set::HashSet::new();
-    let next_protocol = "AES_256_CBC-HMAC_SHA256".to_string();
-    let _ = next_protocols.insert(next_protocol.clone());
+    let next_protocol = NextProtocol::Aes256CbcHmacSha256;
+    let next_protocols = hash_set::HashSet::from([next_protocol.to_string()]);
     let server1 = Ukey2ServerStage1::<CryptoProviderImpl>::from(
         next_protocols,
         HandshakeImplementation::Spec,
@@ -31,7 +30,7 @@
     let mut rng = StdRng::from_entropy();
     let client1 = Ukey2ClientStage1::<CryptoProviderImpl>::from(
         &mut rng,
-        next_protocol,
+        vec![next_protocol],
         HandshakeImplementation::Spec,
     );
     let server2 = server1.advance_state(&mut rng, client1.client_init_msg()).unwrap();
@@ -58,9 +57,8 @@
 
 #[test]
 fn full_integration_state_machine_public_key_in_protobuf() {
-    let mut next_protocols = hash_set::HashSet::new();
-    let next_protocol = "AES_256_CBC-HMAC_SHA256".to_string();
-    let _ = next_protocols.insert(next_protocol.clone());
+    let next_protocol = NextProtocol::Aes256CbcHmacSha256;
+    let next_protocols = hash_set::HashSet::from([next_protocol.to_string()]);
     let server1 = Ukey2ServerStage1::<CryptoProviderImpl>::from(
         next_protocols,
         HandshakeImplementation::PublicKeyInProtobuf,
@@ -68,7 +66,7 @@
     let mut rng = StdRng::from_entropy();
     let client1 = Ukey2ClientStage1::<CryptoProviderImpl>::from(
         &mut rng,
-        next_protocol,
+        vec![next_protocol],
         HandshakeImplementation::PublicKeyInProtobuf,
     );
     let server2 = server1.advance_state(&mut rng, client1.client_init_msg()).unwrap();
@@ -92,3 +90,79 @@
             .derive_array::<32>()
     );
 }
+
+#[test]
+fn full_integration_state_machine_multiple_next_protocols_sort() {
+    let raw_next_protocols = [NextProtocol::Aes256CbcHmacSha256, NextProtocol::Aes256GcmSiv];
+    let next_protocols = hash_set::HashSet::from(raw_next_protocols.map(|p| p.to_string()));
+    let server1 = Ukey2ServerStage1::<CryptoProviderImpl>::from(
+        next_protocols,
+        HandshakeImplementation::Spec,
+    );
+    let mut rng = StdRng::from_entropy();
+    let client1 = Ukey2ClientStage1::<CryptoProviderImpl>::from(
+        &mut rng,
+        raw_next_protocols.to_vec(),
+        HandshakeImplementation::Spec,
+    );
+    let server2 = server1.advance_state(&mut rng, client1.client_init_msg()).unwrap();
+
+    let client2 = client1.advance_state(&mut rng, server2.server_init_msg()).unwrap();
+
+    let server3 = server2.advance_state(&mut rng, client2.client_finished_msg()).unwrap();
+
+    assert_eq!(
+        server3.completed_handshake().auth_string::<CryptoProviderImpl>().derive_array::<32>(),
+        client2.completed_handshake().auth_string::<CryptoProviderImpl>().derive_array::<32>()
+    );
+    assert_eq!(
+        server3
+            .completed_handshake()
+            .next_protocol_secret::<CryptoProviderImpl>()
+            .derive_array::<32>(),
+        client2
+            .completed_handshake()
+            .next_protocol_secret::<CryptoProviderImpl>()
+            .derive_array::<32>()
+    );
+    assert_eq!(server3.completed_handshake().next_protocol, NextProtocol::Aes256GcmSiv);
+    assert_eq!(client2.completed_handshake().next_protocol, NextProtocol::Aes256GcmSiv);
+}
+
+#[test]
+fn full_integration_state_machine_multiple_next_protocols_client() {
+    let raw_next_protocols = [NextProtocol::Aes256CbcHmacSha256, NextProtocol::Aes256GcmSiv];
+    let next_protocols = hash_set::HashSet::from(raw_next_protocols.map(|p| p.to_string()));
+    let server1 = Ukey2ServerStage1::<CryptoProviderImpl>::from(
+        next_protocols,
+        HandshakeImplementation::Spec,
+    );
+    let mut rng = StdRng::from_entropy();
+    let client1 = Ukey2ClientStage1::<CryptoProviderImpl>::from(
+        &mut rng,
+        [NextProtocol::Aes256CbcHmacSha256].to_vec(),
+        HandshakeImplementation::Spec,
+    );
+    let server2 = server1.advance_state(&mut rng, client1.client_init_msg()).unwrap();
+
+    let client2 = client1.advance_state(&mut rng, server2.server_init_msg()).unwrap();
+
+    let server3 = server2.advance_state(&mut rng, client2.client_finished_msg()).unwrap();
+
+    assert_eq!(
+        server3.completed_handshake().auth_string::<CryptoProviderImpl>().derive_array::<32>(),
+        client2.completed_handshake().auth_string::<CryptoProviderImpl>().derive_array::<32>()
+    );
+    assert_eq!(
+        server3
+            .completed_handshake()
+            .next_protocol_secret::<CryptoProviderImpl>()
+            .derive_array::<32>(),
+        client2
+            .completed_handshake()
+            .next_protocol_secret::<CryptoProviderImpl>()
+            .derive_array::<32>()
+    );
+    assert_eq!(server3.completed_handshake().next_protocol, NextProtocol::Aes256CbcHmacSha256);
+    assert_eq!(client2.completed_handshake().next_protocol, NextProtocol::Aes256CbcHmacSha256);
+}
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml b/nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml
index f302119..1034359 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml
@@ -6,7 +6,6 @@
 
 [dependencies]
 ukey2_connections = { path = "../ukey2_connections" }
-ukey2_rs = { path = "../ukey2" }
 cfg-if.workspace = true
 crypto_provider_default.workspace = true
 lock_adapter = {workspace = true, features = ["spin"]}
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_bindings.h b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_bindings.h
index e1f7e1e..e19aa68 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_bindings.h
+++ b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_bindings.h
@@ -64,13 +64,20 @@
 // Common handshake methods
 bool is_handshake_complete(Ukey2HandshakeContextHandle handle);
 RustFFIByteArray get_next_handshake_message(Ukey2HandshakeContextHandle handle);
-CMessageParseResult parse_handshake_message(Ukey2HandshakeContextHandle handle, CFFIByteArray message);
-Ukey2ConnectionContextHandle to_connection_context(Ukey2HandshakeContextHandle handle);
-RustFFIByteArray get_verification_string(Ukey2HandshakeContextHandle handle, size_t output_length);
+CMessageParseResult parse_handshake_message(Ukey2HandshakeContextHandle handle,
+                                            CFFIByteArray message);
+Ukey2ConnectionContextHandle to_connection_context(
+    Ukey2HandshakeContextHandle handle);
+RustFFIByteArray get_verification_string(Ukey2HandshakeContextHandle handle,
+                                         size_t output_length);
 
 // D2DConnectionContextV1 methods
-RustFFIByteArray encode_message_to_peer(Ukey2ConnectionContextHandle handle, CFFIByteArray message, CFFIByteArray associated_data);
-RustFFIByteArray decode_message_from_peer(Ukey2ConnectionContextHandle handle, CFFIByteArray message, CFFIByteArray associated_data);
+RustFFIByteArray encode_message_to_peer(Ukey2ConnectionContextHandle handle,
+                                        CFFIByteArray message,
+                                        CFFIByteArray associated_data);
+RustFFIByteArray decode_message_from_peer(Ukey2ConnectionContextHandle handle,
+                                          CFFIByteArray message,
+                                          CFFIByteArray associated_data);
 RustFFIByteArray get_session_unique(Ukey2ConnectionContextHandle handle);
 int get_sequence_number_for_encoding(Ukey2ConnectionContextHandle handle);
 int get_sequence_number_for_decoding(Ukey2ConnectionContextHandle handle);
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h
index 654e293..457286d 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h
+++ b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h
@@ -11,80 +11,88 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
-#include "ukey2_bindings.h"
+#pragma once
 
 #include <string>
 
+#include "ukey2_bindings.h"
+
+namespace rust {
 struct D2DRestoreConnectionContextV1Result;
 
-// The Connection object that can handle encryption/decryption of messages over the wire.
-// This object should only be constructed via FromSavedSession() or Ukey2Handshake::ToConnectionContext().
+// The Connection object that can handle encryption/decryption of messages over
+// the wire. This object should only be constructed via FromSavedSession() or
+// Ukey2Handshake::ToConnectionContext().
 class D2DConnectionContextV1 {
-    public:
-        // Encodes a message to the connection peer using the derived key from the handshake
-        // If associated_data is not empty, it will be used to compute the signature and the same
-        // associated_data string must be passed into DecodeMessageFromPeer() in order for the
-        // message to be validated.
-        std::string EncodeMessageToPeer(std::string message, std::string associated_data);
-        // Decodes a message from the connection peer. If associated_data was passed into
-        // EncodeMessageToPeer(), that same associated_data must be passed here in order for
-        // this function to succeed.
-        std::string DecodeMessageFromPeer(std::string message, std::string associated_data);
-        // Gets a session-specific unique identifier.
-        std::string GetSessionUnique();
-        // Gets the encoding sequence number.
-        int GetSequenceNumberForEncoding();
-        // Gets the decoding sequence number.
-        int GetSequenceNumberForDecoding();
-        // Returns byte data suitable for use with FromSavedSession().
-        std::string SaveSession();
-        // Recreates the state of a previous D2DConnectionContextV1 using the data from SaveSession().
-        // This function will return an error if the byte pattern is not as expected.
-        // Expected format:
-        // -------------------------------------------------------------------------------------------
-        // |     1 byte      |       4 bytes       |         4 bytes        |  32 bytes |  32 bytes  |
-        // -------------------------------------------------------------------------------------------
-        //  Protocol version | Encode sequence number | Decode sequence number | Encode key | Decode key
-        //    (always 1)
-        static D2DRestoreConnectionContextV1Result FromSavedSession(std::string data);
+ public:
+  // Encodes a message to the connection peer using the derived key from the
+  // handshake If associated_data is not empty, it will be used to compute the
+  // signature and the same associated_data string must be passed into
+  // DecodeMessageFromPeer() in order for the message to be validated.
+  std::string EncodeMessageToPeer(std::string message,
+                                  std::string associated_data);
+  // Decodes a message from the connection peer. If associated_data was passed
+  // into EncodeMessageToPeer(), that same associated_data must be passed here
+  // in order for this function to succeed.
+  std::string DecodeMessageFromPeer(std::string message,
+                                    std::string associated_data);
+  // Gets a session-specific unique identifier.
+  std::string GetSessionUnique();
+  // Gets the encoding sequence number.
+  int GetSequenceNumberForEncoding();
+  // Gets the decoding sequence number.
+  int GetSequenceNumberForDecoding();
+  // Returns byte data suitable for use with FromSavedSession().
+  std::string SaveSession();
+  // Recreates the state of a previous D2DConnectionContextV1 using the data
+  // from SaveSession(). This function will return an error if the byte pattern
+  // is not as expected. Expected format:
+  // ---------------------------------------------------------------------------
+  // | 1 byte |       4 bytes     |      4 bytes      |  32 bytes |  32 bytes  |
+  // ---------------------------------------------------------------------------
+  //  Version | Encode sequence # | Decode sequence # | Encode key | Decode key
+  static D2DRestoreConnectionContextV1Result FromSavedSession(std::string data);
 
-    private:
-        friend class Ukey2Handshake;
-        D2DConnectionContextV1(Ukey2ConnectionContextHandle handle) : handle_(handle) {}
-        const Ukey2ConnectionContextHandle handle_;
+ private:
+  friend class Ukey2Handshake;
+  D2DConnectionContextV1(Ukey2ConnectionContextHandle handle)
+      : handle_(handle) {}
+  const Ukey2ConnectionContextHandle handle_;
 };
 
 struct D2DRestoreConnectionContextV1Result {
-    D2DConnectionContextV1 handle;
-    CD2DRestoreConnectionContextV1Status status;
+  D2DConnectionContextV1 handle;
+  CD2DRestoreConnectionContextV1Status status;
 };
 
 struct ParseResult {
-    bool success;
-    std::string alert_to_send;
+  bool success;
+  std::string alert_to_send;
 };
 
-// Base handshake. This should be used to start a secure channel represented by a D2DConnectionContextV1.
+// Base handshake. This should be used to start a secure channel represented by
+// a D2DConnectionContextV1.
 class Ukey2Handshake {
-    public:
-        // Creates a Ukey2Handshake instance for the responder.
-        static Ukey2Handshake ForResponder();
-        // Creates a Ukey2Handshake instance for the initiator.
-        static Ukey2Handshake ForInitiator();
-        // Returns true if the handshake is complete, false otherwise.
-        bool IsHandshakeComplete();
-        // Returns raw byte data with the message to send over the wire.
-        std::string GetNextHandshakeMessage();
-        // Parses the raw handshake message received over the wire.
-        ParseResult ParseHandshakeMessage(std::string message);
-        // Returns the authentication string of length output_length to be confirmed on both devices.
-        std::string GetVerificationString(size_t output_length);
-        // Turns this Ukey2Handshake instance into a D2DConnectionContextV1. This method once called,
-        // renders the Ukey2Handshake object unusable.
-        D2DConnectionContextV1 ToConnectionContext();
+ public:
+  // Creates a Ukey2Handshake instance for the responder.
+  static Ukey2Handshake ForResponder();
+  // Creates a Ukey2Handshake instance for the initiator.
+  static Ukey2Handshake ForInitiator();
+  // Returns true if the handshake is complete, false otherwise.
+  bool IsHandshakeComplete();
+  // Returns raw byte data with the message to send over the wire.
+  std::string GetNextHandshakeMessage();
+  // Parses the raw handshake message received over the wire.
+  ParseResult ParseHandshakeMessage(std::string message);
+  // Returns the authentication string of length output_length to be confirmed
+  // on both devices.
+  std::string GetVerificationString(size_t output_length);
+  // Turns this Ukey2Handshake instance into a D2DConnectionContextV1. This
+  // method once called, renders the Ukey2Handshake object unusable.
+  D2DConnectionContextV1 ToConnectionContext();
 
-    private:
-        Ukey2Handshake(Ukey2HandshakeContextHandle handle) : handle_(handle) {}
-        const Ukey2HandshakeContextHandle handle_;
+ private:
+  Ukey2Handshake(Ukey2HandshakeContextHandle handle) : handle_(handle) {}
+  const Ukey2HandshakeContextHandle handle_;
 };
+}  // namespace rust
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_glue.cc b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_glue.cc
index 6b03f1c..354112c 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_glue.cc
+++ b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_glue.cc
@@ -12,139 +12,143 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "ukey2_bindings.h"
-#include "ukey2_ffi.h"
-
 #include <cassert>
 #include <cstring>
 #include <iostream>
 #include <string>
 
+#include "ukey2_bindings.h"
+#include "ukey2_ffi.h"
+
 CFFIByteArray nullByteArray() {
-    return {
-        .handle = nullptr,
-        .len = 0,
-    };
+  return {
+      .handle = nullptr,
+      .len = 0,
+  };
 }
 
 // Implementation of functions
-Ukey2Handshake Ukey2Handshake::ForInitiator() {
-    return Ukey2Handshake(initiator_new());
+rust::Ukey2Handshake rust::Ukey2Handshake::ForInitiator() {
+  return Ukey2Handshake(initiator_new());
 }
 
-Ukey2Handshake Ukey2Handshake::ForResponder() {
-    return Ukey2Handshake(responder_new());
+rust::Ukey2Handshake rust::Ukey2Handshake::ForResponder() {
+  return Ukey2Handshake(responder_new());
 }
 
-bool Ukey2Handshake::IsHandshakeComplete() {
-    return is_handshake_complete(handle_);
+bool rust::Ukey2Handshake::IsHandshakeComplete() {
+  return is_handshake_complete(handle_);
 }
 
-std::string Ukey2Handshake::GetNextHandshakeMessage() {
-    RustFFIByteArray array = get_next_handshake_message(handle_);
-    std::string ret = std::string((const char*) array.handle, array.len);
-    rust_dealloc_ffi_byte_array(array);
-    return ret;
+std::string rust::Ukey2Handshake::GetNextHandshakeMessage() {
+  RustFFIByteArray array = get_next_handshake_message(handle_);
+  std::string ret = std::string((const char*)array.handle, array.len);
+  rust_dealloc_ffi_byte_array(array);
+  return ret;
 }
 
-ParseResult Ukey2Handshake::ParseHandshakeMessage(std::string message) {
-    CFFIByteArray messageRaw{
-        .handle = (uint8_t*)message.c_str(),
-        .len = message.length(),
-    };
-    CMessageParseResult result = parse_handshake_message(handle_, messageRaw);
-    std::string alert;
-    if (!result.success) {
-        std::cout << "parse failed" << std::endl;
-        RustFFIByteArray array = result.alert_to_send;
-        if (array.handle != nullptr) {
-            alert = std::string((const char*) array.handle, array.len);
-            rust_dealloc_ffi_byte_array(array);
-        }
+rust::ParseResult rust::Ukey2Handshake::ParseHandshakeMessage(
+    std::string message) {
+  CFFIByteArray messageRaw{
+      .handle = (uint8_t*)message.c_str(),
+      .len = message.length(),
+  };
+  CMessageParseResult result = parse_handshake_message(handle_, messageRaw);
+  std::string alert;
+  if (!result.success) {
+    std::cout << "parse failed" << std::endl;
+    RustFFIByteArray array = result.alert_to_send;
+    if (array.handle != nullptr) {
+      alert = std::string((const char*)array.handle, array.len);
+      rust_dealloc_ffi_byte_array(array);
     }
-    return ParseResult {
-        .success = result.success,
-        .alert_to_send = alert,
-    };
+  }
+  return ParseResult{
+      .success = result.success,
+      .alert_to_send = alert,
+  };
 }
 
-std::string Ukey2Handshake::GetVerificationString(size_t output_length) {
-    RustFFIByteArray array = get_verification_string(handle_, output_length);
-    std::string ret = std::string((const char*) array.handle, array.len);
-    rust_dealloc_ffi_byte_array(array);
-    return ret;
+std::string rust::Ukey2Handshake::GetVerificationString(size_t output_length) {
+  RustFFIByteArray array = get_verification_string(handle_, output_length);
+  std::string ret = std::string((const char*)array.handle, array.len);
+  rust_dealloc_ffi_byte_array(array);
+  return ret;
 }
 
-D2DConnectionContextV1 Ukey2Handshake::ToConnectionContext() {
-    assert(IsHandshakeComplete());
-    return D2DConnectionContextV1(to_connection_context(handle_));
+rust::D2DConnectionContextV1 rust::Ukey2Handshake::ToConnectionContext() {
+  assert(IsHandshakeComplete());
+  return D2DConnectionContextV1(to_connection_context(handle_));
 }
 
-std::string D2DConnectionContextV1::DecodeMessageFromPeer(std::string message, std::string associated_data) {
-    CFFIByteArray messageRaw{
-        .handle = (uint8_t*)message.c_str(),
-        .len = message.length(),
-    };
-    CFFIByteArray associatedDataRaw{
-        .handle = (uint8_t*)associated_data.c_str(),
-        .len = associated_data.length(),
-    };
-    RustFFIByteArray array =
-        decode_message_from_peer(handle_, messageRaw, associatedDataRaw);
-    if (array.handle == nullptr) {
-        return "";
-    }
-    std::string ret = std::string((const char*) array.handle, array.len);
-    rust_dealloc_ffi_byte_array(array);
-    return ret;
+std::string rust::D2DConnectionContextV1::DecodeMessageFromPeer(
+    std::string message, std::string associated_data) {
+  CFFIByteArray messageRaw{
+      .handle = (uint8_t*)message.c_str(),
+      .len = message.length(),
+  };
+  CFFIByteArray associatedDataRaw{
+      .handle = (uint8_t*)associated_data.c_str(),
+      .len = associated_data.length(),
+  };
+  RustFFIByteArray array =
+      decode_message_from_peer(handle_, messageRaw, associatedDataRaw);
+  if (array.handle == nullptr) {
+    return "";
+  }
+  std::string ret = std::string((const char*)array.handle, array.len);
+  rust_dealloc_ffi_byte_array(array);
+  return ret;
 }
 
-std::string D2DConnectionContextV1::EncodeMessageToPeer(std::string message, std::string associated_data) {
-    CFFIByteArray messageRaw{
-        .handle = (uint8_t*)message.c_str(),
-        .len = message.length(),
-    };
-    CFFIByteArray associatedDataRaw{
-        .handle = (uint8_t*)associated_data.c_str(),
-        .len = associated_data.length(),
-    };
-    RustFFIByteArray array =
-        encode_message_to_peer(handle_, messageRaw, associatedDataRaw);
-    std::string ret = std::string((const char*) array.handle, array.len);
-    rust_dealloc_ffi_byte_array(array);
-    return ret;
+std::string rust::D2DConnectionContextV1::EncodeMessageToPeer(
+    std::string message, std::string associated_data) {
+  CFFIByteArray messageRaw{
+      .handle = (uint8_t*)message.c_str(),
+      .len = message.length(),
+  };
+  CFFIByteArray associatedDataRaw{
+      .handle = (uint8_t*)associated_data.c_str(),
+      .len = associated_data.length(),
+  };
+  RustFFIByteArray array =
+      encode_message_to_peer(handle_, messageRaw, associatedDataRaw);
+  std::string ret = std::string((const char*)array.handle, array.len);
+  rust_dealloc_ffi_byte_array(array);
+  return ret;
 }
 
-std::string D2DConnectionContextV1::GetSessionUnique() {
-    RustFFIByteArray array = get_session_unique(handle_);
-    std::string ret = std::string((const char*) array.handle, array.len);
-    rust_dealloc_ffi_byte_array(array);
-    return ret;
+std::string rust::D2DConnectionContextV1::GetSessionUnique() {
+  RustFFIByteArray array = get_session_unique(handle_);
+  std::string ret = std::string((const char*)array.handle, array.len);
+  rust_dealloc_ffi_byte_array(array);
+  return ret;
 }
 
-int D2DConnectionContextV1::GetSequenceNumberForEncoding() {
-    return get_sequence_number_for_encoding(handle_);
+int rust::D2DConnectionContextV1::GetSequenceNumberForEncoding() {
+  return get_sequence_number_for_encoding(handle_);
 }
 
-int D2DConnectionContextV1::GetSequenceNumberForDecoding() {
-    return get_sequence_number_for_decoding(handle_);
+int rust::D2DConnectionContextV1::GetSequenceNumberForDecoding() {
+  return get_sequence_number_for_decoding(handle_);
 }
 
-std::string D2DConnectionContextV1::SaveSession() {
-    RustFFIByteArray array = save_session(handle_);
-    std::string ret = std::string((const char*) array.handle, array.len);
-    rust_dealloc_ffi_byte_array(array);
-    return ret;
+std::string rust::D2DConnectionContextV1::SaveSession() {
+  RustFFIByteArray array = save_session(handle_);
+  std::string ret = std::string((const char*)array.handle, array.len);
+  rust_dealloc_ffi_byte_array(array);
+  return ret;
 }
 
-D2DRestoreConnectionContextV1Result D2DConnectionContextV1::FromSavedSession(std::string data) {
-    CFFIByteArray arr{
-        .handle = (uint8_t*)data.c_str(),
-        .len = data.length(),
-    };
-    auto result = from_saved_session(arr);
-    return {
-        D2DConnectionContextV1(result.handle),
-        result.status,
-    };
+rust::D2DRestoreConnectionContextV1Result
+rust::D2DConnectionContextV1::FromSavedSession(std::string data) {
+  CFFIByteArray arr{
+      .handle = (uint8_t*)data.c_str(),
+      .len = data.length(),
+  };
+  auto result = from_saved_session(arr);
+  return {
+      D2DConnectionContextV1(result.handle),
+      result.status,
+  };
 }
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc
index 962ada8..1a23278 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc
+++ b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc
@@ -12,15 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "ukey2_ffi.h"
-
 #include <string>
 
 #include "gtest/gtest.h"
+#include "ukey2_ffi.h"
 
+namespace rust {
 namespace {
 
-void RunHandshake(Ukey2Handshake initiator_handle, Ukey2Handshake responder_handle) {
+void RunHandshake(Ukey2Handshake initiator_handle,
+                  Ukey2Handshake responder_handle) {
   ParseResult parse_result = responder_handle.ParseHandshakeMessage(
       initiator_handle.GetNextHandshakeMessage());
   ASSERT_TRUE(parse_result.success);
@@ -87,3 +88,4 @@
 }
 
 }  // namespace
+}  // namespace rust
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs b/nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs
index e5cc548..77d9b59 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs
+++ b/nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs
@@ -25,11 +25,11 @@
 #[cfg(not(feature = "std"))]
 use lock_adapter::spin::Mutex;
 #[cfg(feature = "std")]
-use lock_adapter::std::Mutex;
+use lock_adapter::stdlib::Mutex;
 
 use ukey2_connections::{
     D2DConnectionContextV1, D2DHandshakeContext, HandleMessageError, HandshakeImplementation,
-    InitiatorD2DHandshakeContext, ServerD2DHandshakeContext,
+    InitiatorD2DHandshakeContext, NextProtocol, ServerD2DHandshakeContext,
 };
 
 #[repr(C)]
@@ -188,6 +188,7 @@
 pub extern "C" fn responder_new() -> u64 {
     let ctx = Box::new(ServerD2DHandshakeContext::<CryptoProvider>::new(
         HandshakeImplementation::PublicKeyInProtobuf,
+        &[NextProtocol::Aes256CbcHmacSha256],
     ));
     insert_gen_handle(ctx)
 }
@@ -200,6 +201,7 @@
 pub extern "C" fn initiator_new() -> u64 {
     let ctx = Box::new(InitiatorD2DHandshakeContext::<CryptoProvider>::new(
         HandshakeImplementation::PublicKeyInProtobuf,
+        vec![NextProtocol::Aes256CbcHmacSha256],
     ));
     insert_gen_handle(ctx)
 }
diff --git a/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs b/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs
index f816165..8c55d23 100644
--- a/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs
+++ b/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs
@@ -23,10 +23,11 @@
     D2DConnectionContextV1, D2DHandshakeContext, InitiatorD2DHandshakeContext,
     ServerD2DHandshakeContext,
 };
-use ukey2_rs::HandshakeImplementation;
+use ukey2_rs::{HandshakeImplementation, NextProtocol};
 
 fn run_handshake_with_rng<C, R>(
     mut rng: R,
+    next_protocols: Vec<NextProtocol>,
 ) -> (D2DConnectionContextV1<R>, D2DConnectionContextV1<R>)
 where
     C: CryptoProvider,
@@ -35,10 +36,12 @@
     let mut initiator_ctx = InitiatorD2DHandshakeContext::<C, R>::new_impl(
         HandshakeImplementation::Spec,
         R::from_rng(&mut rng).unwrap(),
+        next_protocols.clone(),
     );
     let mut server_ctx = ServerD2DHandshakeContext::<C, R>::new_impl(
         HandshakeImplementation::Spec,
         R::from_rng(&mut rng).unwrap(),
+        &next_protocols,
     );
     server_ctx
         .handle_handshake_message(
@@ -60,27 +63,61 @@
     (initiator_ctx.to_connection_context().unwrap(), server_ctx.to_connection_context().unwrap())
 }
 
-fn criterion_benchmark(c: &mut Criterion) {
+fn cbc_criterion_benchmark(c: &mut Criterion) {
     let kib = 1024;
     let mut group = c.benchmark_group("throughput");
     let mut plaintext = Vec::new();
-    let (mut initiator_ctx, mut server_ctx) =
-        run_handshake_with_rng::<CryptoProviderImpl, _>(rand::rngs::StdRng::from_entropy());
+    let (mut initiator_ctx, mut server_ctx) = run_handshake_with_rng::<CryptoProviderImpl, _>(
+        rand::rngs::StdRng::from_entropy(),
+        vec![NextProtocol::Aes256CbcHmacSha256],
+    );
     for len in [10 * kib, 1024 * kib] {
         let _ = group.throughput(Throughput::Bytes(len as u64));
         plaintext.resize(len, 0);
         rand::thread_rng().fill(&mut plaintext[..]);
-        let _ = group.bench_function(format!("UKEY2 encrypt/decrypt {}KiB", len / kib), |b| {
-            b.iter(|| {
-                let msg = initiator_ctx
-                    .encode_message_to_peer::<CryptoProviderImpl, &[u8]>(&plaintext, None);
-                black_box(
-                    server_ctx.decode_message_from_peer::<CryptoProviderImpl, &[u8]>(&msg, None),
-                )
-            })
-        });
+        let _ = group.bench_function(
+            format!("AES-CBC-256_HMAC-SHA256 UKEY2 encrypt/decrypt {}KiB", len / kib),
+            |b| {
+                b.iter(|| {
+                    let msg = initiator_ctx
+                        .encode_message_to_peer::<CryptoProviderImpl, &[u8]>(&plaintext, None);
+                    black_box(
+                        server_ctx
+                            .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(&msg, None),
+                    )
+                })
+            },
+        );
     }
 }
 
-criterion_group!(benches, criterion_benchmark);
+fn gcm_criterion_benchmark(c: &mut Criterion) {
+    let kib = 1024;
+    let mut group = c.benchmark_group("throughput");
+    let mut plaintext = Vec::new();
+    let (mut initiator_ctx, mut server_ctx) = run_handshake_with_rng::<CryptoProviderImpl, _>(
+        rand::rngs::StdRng::from_entropy(),
+        vec![NextProtocol::Aes256GcmSiv],
+    );
+    for len in [10 * kib, 1024 * kib] {
+        let _ = group.throughput(Throughput::Bytes(len as u64));
+        plaintext.resize(len, 0);
+        rand::thread_rng().fill(&mut plaintext[..]);
+        let _ = group.bench_function(
+            format!("AES-GCM-SIV UKEY2 encrypt/decrypt {}KiB", len / kib),
+            |b| {
+                b.iter(|| {
+                    let msg = initiator_ctx
+                        .encode_message_to_peer::<CryptoProviderImpl, &[u8]>(&plaintext, None);
+                    black_box(
+                        server_ctx
+                            .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(&msg, None),
+                    )
+                })
+            },
+        );
+    }
+}
+
+criterion_group!(benches, cbc_criterion_benchmark, gcm_criterion_benchmark);
 criterion_main!(benches);
diff --git a/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.toml b/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.toml
index b8a1295..2c94ebb 100644
--- a/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.toml
@@ -1,43 +1,34 @@
 [package]
 name = "ukey2_connections-fuzz"
-version = "0.0.0"
-publish = false
-edition = "2021"
+version.workspace = true
+publish.workspace = true
+edition.workspace = true
 
 [package.metadata]
 cargo-fuzz = true
 
 [dependencies]
-libfuzzer-sys = "0.4"
-crypto_provider_rustcrypto = { path = "../../../../crypto/crypto_provider_rustcrypto" }
-ukey2_rs = { path = "../../ukey2" }
-rand_chacha = "0.3.1"
-arbitrary = { version = "1.2.3", features = ["derive"] }
+arbitrary = { workspace = true, features = ["derive"] }
+crypto_provider_rustcrypto.workspace = true
+derive_fuzztest.workspace = true
+rand_chacha.workspace = true
+ukey2_connections.workspace = true
+ukey2_rs.workspace = true
 
-[dependencies.ukey2_connections]
-path = ".."
-
-# Prevent this from interfering with workspaces
-[workspace]
-members = ["."]
-
-[profile.release]
-debug = 1
+[target.'cfg(fuzzing)'.dependencies]
+libfuzzer-sys.workspace = true
 
 [[bin]]
 name = "fuzz_connection"
 path = "fuzz_targets/fuzz_connection.rs"
-test = false
 doc = false
 
 [[bin]]
 name = "fuzz_handshake"
 path = "fuzz_targets/fuzz_handshake.rs"
-test = false
 doc = false
 
 [[bin]]
 name = "fuzz_from_saved_session"
 path = "fuzz_targets/fuzz_from_saved_session.rs"
-test = false
 doc = false
diff --git a/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_connection.rs b/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_connection.rs
index 1db7e63..8a02bc0 100644
--- a/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_connection.rs
+++ b/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_connection.rs
@@ -1,4 +1,4 @@
-#![no_main]
+#![cfg_attr(fuzzing, no_main)]
 // Copyright 2023 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,14 +15,15 @@
 
 use arbitrary::Arbitrary;
 use crypto_provider_rustcrypto::RustCrypto;
-use libfuzzer_sys::fuzz_target;
+use derive_fuzztest::fuzztest;
 use rand_chacha::rand_core::SeedableRng;
 use ukey2_connections::HandshakeImplementation;
 use ukey2_connections::{
     D2DHandshakeContext, InitiatorD2DHandshakeContext, ServerD2DHandshakeContext,
 };
+use ukey2_rs::NextProtocol;
 
-#[derive(Debug, Arbitrary)]
+#[derive(Clone, Debug, Arbitrary)]
 enum Type {
     SentByInitiator,
     SentByServer,
@@ -30,28 +31,24 @@
     ReceivedByServer,
 }
 
-#[derive(Debug, Arbitrary)]
-struct Message<'a> {
+#[derive(Clone, Debug, Arbitrary)]
+struct Message {
     sender: Type,
-    payload: &'a [u8],
-    associated_data: Option<&'a [u8]>,
+    payload: Vec<u8>,
+    associated_data: Option<Vec<u8>>,
 }
 
-#[derive(Debug, Arbitrary)]
-struct FuzzInput<'a> {
-    client_rng_seed: [u8; 32],
-    server_rng_seed: [u8; 32],
-    messages: Vec<Message<'a>>,
-}
-
-fuzz_target!(|input: FuzzInput| {
+#[fuzztest]
+fn test(client_rng_seed: [u8; 32], server_rng_seed: [u8; 32], messages: Vec<Message>) {
     let mut initiator_ctx = InitiatorD2DHandshakeContext::<RustCrypto, _>::new_impl(
         HandshakeImplementation::Spec,
-        rand_chacha::ChaChaRng::from_seed(input.client_rng_seed),
+        rand_chacha::ChaChaRng::from_seed(client_rng_seed),
+        vec![NextProtocol::Aes256CbcHmacSha256],
     );
     let mut server_ctx = ServerD2DHandshakeContext::<RustCrypto, _>::new_impl(
         HandshakeImplementation::Spec,
-        rand_chacha::ChaChaRng::from_seed(input.server_rng_seed),
+        rand_chacha::ChaChaRng::from_seed(server_rng_seed),
+        &[NextProtocol::Aes256CbcHmacSha256],
     );
     let client_init = initiator_ctx
         .get_next_handshake_message()
@@ -81,39 +78,40 @@
         "Initator handshake context should be converted to connection context successfully",
     );
 
-    for Message {
-        sender,
-        payload,
-        associated_data,
-    } in input.messages
-    {
+    for Message { sender, payload, associated_data } in messages {
         match sender {
             Type::SentByInitiator => {
                 let ciphertext = initiator_connection
-                    .encode_message_to_peer::<RustCrypto, _>(payload, associated_data);
+                    .encode_message_to_peer::<RustCrypto, _>(&payload, associated_data.as_ref());
                 let decoded = server_connection
-                    .decode_message_from_peer::<RustCrypto, _>(&ciphertext, associated_data)
+                    .decode_message_from_peer::<RustCrypto, _>(
+                        &ciphertext,
+                        associated_data.as_ref(),
+                    )
                     .unwrap();
                 assert_eq!(decoded, payload);
             }
             Type::SentByServer => {
                 let ciphertext = server_connection
-                    .encode_message_to_peer::<RustCrypto, _>(payload, associated_data);
+                    .encode_message_to_peer::<RustCrypto, _>(&payload, associated_data.as_ref());
                 let decoded = initiator_connection
-                    .decode_message_from_peer::<RustCrypto, _>(&ciphertext, associated_data)
+                    .decode_message_from_peer::<RustCrypto, _>(
+                        &ciphertext,
+                        associated_data.as_ref(),
+                    )
                     .unwrap();
                 assert_eq!(decoded, payload);
             }
             Type::ReceivedByInitiator => {
                 // Both Ok and Err results are possible here since the input is Arbitrary payload
                 let _unused_result = initiator_connection
-                    .decode_message_from_peer::<RustCrypto, _>(&payload, associated_data);
+                    .decode_message_from_peer::<RustCrypto, _>(&payload, associated_data.as_ref());
             }
             Type::ReceivedByServer => {
                 // Both Ok and Err results are possible here since the input is Arbitrary payload
                 let _unused_result = server_connection
-                    .decode_message_from_peer::<RustCrypto, _>(&payload, associated_data);
+                    .decode_message_from_peer::<RustCrypto, _>(&payload, associated_data.as_ref());
             }
         }
     }
-});
+}
diff --git a/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_from_saved_session.rs b/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_from_saved_session.rs
index c2cd385..0d35f7e 100644
--- a/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_from_saved_session.rs
+++ b/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_from_saved_session.rs
@@ -1,4 +1,4 @@
-#![no_main]
+#![cfg_attr(fuzzing, no_main)]
 // Copyright 2023 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,17 +13,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use libfuzzer_sys::fuzz_target;
-use ukey2_connections::{D2DConnectionContextV1, DeserializeError};
 use crypto_provider_rustcrypto::RustCrypto;
+use derive_fuzztest::fuzztest;
+use ukey2_connections::{D2DConnectionContextV1, DeserializeError};
 
 const PROTOCOL_VERSION: u8 = 1;
 
-fuzz_target!(|input: [u8; 73]| {
+#[fuzztest]
+fn test(input: [u8; 73]) {
     let result = D2DConnectionContextV1::from_saved_session::<RustCrypto>(&input);
     if input[0] != PROTOCOL_VERSION {
         assert_eq!(result.unwrap_err(), DeserializeError::BadProtocolVersion);
     } else {
         assert!(result.is_ok());
     }
-});
+}
diff --git a/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_handshake.rs b/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_handshake.rs
index 3007c9a..24cde94 100644
--- a/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_handshake.rs
+++ b/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_handshake.rs
@@ -1,4 +1,4 @@
-#![no_main]
+#![cfg_attr(fuzzing, no_main)]
 // Copyright 2023 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,55 +13,54 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use arbitrary::Arbitrary;
 use crypto_provider_rustcrypto::RustCrypto;
-use libfuzzer_sys::fuzz_target;
+use derive_fuzztest::fuzztest;
 use rand_chacha::rand_core::SeedableRng;
 use ukey2_connections::HandshakeImplementation;
 use ukey2_connections::{
     D2DHandshakeContext, InitiatorD2DHandshakeContext, ServerD2DHandshakeContext,
 };
+use ukey2_rs::NextProtocol;
 
-#[derive(Debug, Arbitrary)]
-struct FuzzInput<'a> {
+#[fuzztest]
+fn test(
     client_rng_seed: [u8; 32],
     server_rng_seed: [u8; 32],
-    override_client_init: Option<&'a [u8]>,
-    override_server_init: Option<&'a [u8]>,
-    override_client_finish: Option<&'a [u8]>,
-}
-
-fuzz_target!(|input: FuzzInput| {
+    override_client_init: Option<Vec<u8>>,
+    override_server_init: Option<Vec<u8>>,
+    override_client_finish: Option<Vec<u8>>,
+) {
     let mut initiator_ctx = InitiatorD2DHandshakeContext::<RustCrypto, _>::new_impl(
         HandshakeImplementation::Spec,
-        rand_chacha::ChaChaRng::from_seed(input.client_rng_seed),
+        rand_chacha::ChaChaRng::from_seed(client_rng_seed),
+        vec![NextProtocol::Aes256CbcHmacSha256],
     );
     let mut server_ctx = ServerD2DHandshakeContext::<RustCrypto, _>::new_impl(
         HandshakeImplementation::Spec,
-        rand_chacha::ChaChaRng::from_seed(input.server_rng_seed),
+        rand_chacha::ChaChaRng::from_seed(server_rng_seed),
+        &[NextProtocol::Aes256CbcHmacSha256],
     );
     let client_init = initiator_ctx
         .get_next_handshake_message()
         .expect("Initial get_next_handshake_message should succeed");
-    let client_init_with_override = input.override_client_init.unwrap_or(&client_init);
+    let client_init_with_override = override_client_init.unwrap_or(client_init);
     let _result = server_ctx
-        .handle_handshake_message(client_init_with_override)
+        .handle_handshake_message(&client_init_with_override)
         .and_then(|_| {
             let server_init = server_ctx.get_next_handshake_message().expect(concat!(
                 "get_next_handshake_message should succeed when previous ",
                 "handle_handshake_message succeeded"
             ));
-            let server_init_with_override = input.override_server_init.unwrap_or(&server_init);
-            initiator_ctx.handle_handshake_message(server_init_with_override)
+            let server_init_with_override = override_server_init.unwrap_or(server_init);
+            initiator_ctx.handle_handshake_message(&server_init_with_override)
         })
         .and_then(|_| {
             let client_finish = initiator_ctx.get_next_handshake_message().expect(concat!(
                 "get_next_handshake_message should succeed when previous ",
                 "handle_handshake_message succeeded"
             ));
-            let client_finish_with_override =
-                input.override_client_finish.unwrap_or(&client_finish);
-            server_ctx.handle_handshake_message(client_finish_with_override)
+            let client_finish_with_override = override_client_finish.unwrap_or(client_finish);
+            server_ctx.handle_handshake_message(&client_finish_with_override)
         })
         .map(|_| {
             assert!(server_ctx.is_handshake_complete());
@@ -71,4 +70,4 @@
             }
             // Note: initiator keeps returning client_finish at the Complete state
         });
-});
+}
diff --git a/nearby/connections/ukey2/ukey2_connections/src/crypto_utils.rs b/nearby/connections/ukey2/ukey2_connections/src/crypto_utils.rs
index f4bd671..815d3f0 100644
--- a/nearby/connections/ukey2/ukey2_connections/src/crypto_utils.rs
+++ b/nearby/connections/ukey2/ukey2_connections/src/crypto_utils.rs
@@ -13,10 +13,11 @@
 // limitations under the License.
 
 use crate::d2d_connection_context_v1::{Aes256Key as RawAes256Key, AesCbcIv};
+use crypto_provider::aead::AeadError;
 use crypto_provider::aes::cbc::DecryptionError;
 
-/// Encrypt message of length N
-pub(crate) fn encrypt<
+/// Encrypt message of length N with AES-CBC-256
+pub(crate) fn encrypt_cbc<
     R: rand::Rng + rand::CryptoRng,
     A: crypto_provider::aes::cbc::AesCbcPkcs7Padded,
 >(
@@ -29,10 +30,40 @@
     (ciphertext, iv)
 }
 
-pub(crate) fn decrypt<A: crypto_provider::aes::cbc::AesCbcPkcs7Padded>(
+/// Decrypt message of length N with AES-CBC-256
+pub(crate) fn decrypt_cbc<A: crypto_provider::aes::cbc::AesCbcPkcs7Padded>(
     key: &RawAes256Key,
     ciphertext: &[u8],
     iv: &AesCbcIv,
 ) -> Result<Vec<u8>, DecryptionError> {
     A::decrypt(&key[..].try_into().unwrap(), iv, ciphertext)
 }
+
+// TODO: Implement caching of these ciphers per connection so we don't recreate on each computation.
+pub(crate) fn encrypt_gcm_siv<
+    A: crypto_provider::aead::AesGcmSiv
+        + crypto_provider::aead::AeadInit<crypto_provider::aes::Aes256Key>,
+>(
+    key: &RawAes256Key,
+    plaintext: &[u8],
+    aad: &[u8],
+    nonce: &A::Nonce,
+) -> Result<Vec<u8>, AeadError> {
+    let converted_key = key.as_slice().try_into().unwrap();
+    let encrypter = A::new(&converted_key);
+    encrypter.encrypt(plaintext, aad, nonce)
+}
+
+pub(crate) fn decrypt_gcm_siv<
+    A: crypto_provider::aead::AesGcmSiv
+        + crypto_provider::aead::AeadInit<crypto_provider::aes::Aes256Key>,
+>(
+    key: &RawAes256Key,
+    ciphertext: &[u8],
+    aad: &[u8],
+    nonce: &A::Nonce,
+) -> Result<Vec<u8>, AeadError> {
+    let converted_key = key.as_slice().try_into().unwrap();
+    let decrypter = A::new(&converted_key);
+    decrypter.decrypt(ciphertext, aad, nonce)
+}
diff --git a/nearby/connections/ukey2/ukey2_connections/src/d2d_connection_context_v1.rs b/nearby/connections/ukey2/ukey2_connections/src/d2d_connection_context_v1.rs
index 70a1556..22def10 100644
--- a/nearby/connections/ukey2/ukey2_connections/src/d2d_connection_context_v1.rs
+++ b/nearby/connections/ukey2/ukey2_connections/src/d2d_connection_context_v1.rs
@@ -18,14 +18,15 @@
 use bytes::BufMut;
 use rand::SeedableRng as _;
 
+use crypto_provider::aead::Aead;
 use crypto_provider::{hkdf::Hkdf, hmac::Hmac, sha2::Sha256, CryptoProvider};
-use ukey2_proto::protobuf::Message as _;
+use ukey2_proto::protobuf::{Enum, Message as _};
 use ukey2_proto::ukey2_all_proto::{
     device_to_device_messages::DeviceToDeviceMessage,
     securegcm::{GcmMetadata, Type},
     securemessage::{EncScheme, Header, HeaderAndBody, SecureMessage, SigScheme},
 };
-use ukey2_rs::CompletedHandshake;
+use ukey2_rs::{CompletedHandshake, NextProtocol};
 
 use crate::{crypto_utils, java_utils};
 
@@ -52,7 +53,7 @@
 ];
 
 pub(crate) type AesCbcIv = [u8; 16];
-pub type Aes256Key = [u8; 32];
+pub type Aes256Key = [u8; AES_256_KEY_SIZE];
 
 const HKDF_INFO_KEY_INITIATOR: &[u8; 6] = b"client";
 const HKDF_INFO_KEY_RESPONDER: &[u8; 6] = b"server";
@@ -118,6 +119,7 @@
     signing_key: Aes256Key,
     verify_key: Aes256Key,
     rng: R,
+    protocol: NextProtocol,
 }
 
 /// Error type for [`decode_message_from_peer`][D2DConnectionContextV1::decode_message_from_peer].
@@ -160,14 +162,13 @@
 where
     R: rand::Rng + rand::SeedableRng + rand::CryptoRng,
 {
-    pub(crate) const NEXT_PROTOCOL_IDENTIFIER: &'static str = "AES_256_CBC-HMAC_SHA256";
-
     pub fn new<C: CryptoProvider>(
         decode_sequence_num: i32,
         encode_sequence_num: i32,
         encode_key: Aes256Key,
         decode_key: Aes256Key,
         rng: R,
+        protocol: NextProtocol,
     ) -> Self {
         let encryption_key = derive_aes256_key::<C>(&encode_key, b"ENC:2");
         let decryption_key = derive_aes256_key::<C>(&decode_key, b"ENC:2");
@@ -183,6 +184,7 @@
             signing_key,
             verify_key,
             rng,
+            protocol,
         }
     }
 
@@ -198,6 +200,7 @@
             encryption_key::<32, C>(&next_protocol_secret, HKDF_INFO_KEY_INITIATOR).unwrap(),
             encryption_key::<32, C>(&next_protocol_secret, HKDF_INFO_KEY_RESPONDER).unwrap(),
             rng,
+            handshake.next_protocol,
         )
     }
 
@@ -213,6 +216,7 @@
             encryption_key::<32, C>(&next_protocol_secret, HKDF_INFO_KEY_RESPONDER).unwrap(),
             encryption_key::<32, C>(&next_protocol_secret, HKDF_INFO_KEY_INITIATOR).unwrap(),
             rng,
+            handshake.next_protocol,
         )
     }
 
@@ -240,6 +244,9 @@
         ret.put_i32(self.decode_sequence_num);
         ret.extend_from_slice(self.encode_key.as_slice());
         ret.extend_from_slice(self.decode_key.as_slice());
+        if self.protocol == NextProtocol::Aes256GcmSiv {
+            ret.extend_from_slice(&EncScheme::AES_256_GCM_SIV.value().to_be_bytes())
+        }
         ret
     }
 
@@ -247,31 +254,52 @@
         session: &[u8],
         rng: R,
     ) -> Result<Self, DeserializeError> {
-        if session.len() != 73 {
+        if session.len() != 73 && session.len() != 77 {
             return Err(DeserializeError::BadDataLength);
         }
         let (rem, _) = nom::bytes::complete::tag(PROTOCOL_VERSION.to_be_bytes())(session)
             .map_err(|_: nom::Err<nom::error::Error<_>>| DeserializeError::BadProtocolVersion)?;
-
-        let (_, (encode_sequence_num, decode_sequence_num, encode_key, decode_key)) =
-            nom::combinator::all_consuming(nom::sequence::tuple::<_, _, nom::error::Error<_>, _>(
-                (
-                    nom::number::complete::be_i32,
-                    nom::number::complete::be_i32,
-                    nom::combinator::map_res(
-                        nom::bytes::complete::take(32_usize),
-                        TryInto::<Aes256Key>::try_into,
-                    ),
-                    nom::combinator::map_res(
-                        nom::bytes::complete::take(32_usize),
-                        TryInto::<Aes256Key>::try_into,
-                    ),
+        let (
+            _,
+            (encode_sequence_num, decode_sequence_num, encode_key, decode_key, next_protocol_int),
+        ) = nom::combinator::all_consuming(nom::sequence::tuple::<_, _, nom::error::Error<_>, _>(
+            (
+                nom::number::complete::be_i32,
+                nom::number::complete::be_i32,
+                nom::combinator::map_res(
+                    nom::bytes::complete::take(32_usize),
+                    TryInto::<Aes256Key>::try_into,
                 ),
-            ))(rem)
-            // This should always succeed since all of the parsers above are valid over the entire
-            // [u8] space, and we already checked the length at the start.
-            .expect("Saved session parsing should succeed");
-        Ok(Self::new::<C>(encode_sequence_num, decode_sequence_num, encode_key, decode_key, rng))
+                nom::combinator::map_res(
+                    nom::bytes::complete::take(32_usize),
+                    TryInto::<Aes256Key>::try_into,
+                ),
+                nom::combinator::opt(nom::number::complete::be_i32),
+            ),
+        ))(rem)
+        // This should always succeed since all of the parsers above are valid over the entire
+        // [u8] space, and we already checked the length at the start.
+        .expect("Saved session parsing should succeed");
+
+        let next_protocol = if let Some(next_protocol_raw) = next_protocol_int {
+            let enc_scheme =
+                EncScheme::from_i32(next_protocol_raw).ok_or(DeserializeError::BadData)?;
+            match enc_scheme {
+                EncScheme::NONE => Err(DeserializeError::BadData),
+                EncScheme::AES_256_CBC => Ok(NextProtocol::Aes256CbcHmacSha256),
+                EncScheme::AES_256_GCM_SIV => Ok(NextProtocol::Aes256GcmSiv),
+            }?
+        } else {
+            NextProtocol::Aes256CbcHmacSha256
+        };
+        Ok(Self::new::<C>(
+            encode_sequence_num,
+            decode_sequence_num,
+            encode_key,
+            decode_key,
+            rng,
+            next_protocol,
+        ))
     }
 
     /// Once initiator and responder have exchanged public keys, use this method to encrypt and
@@ -292,11 +320,6 @@
             message: payload.to_vec(),
             sequence_num: self.get_sequence_number_for_encoding(),
         });
-        let (ciphertext, iv) = crypto_utils::encrypt::<_, C::AesCbcPkcs7Padded>(
-            &self.encryption_key,
-            message.as_slice(),
-            &mut self.rng,
-        );
         let metadata = GcmMetadata {
             type_: Some(Type::DEVICE_TO_DEVICE_MESSAGE.into()),
             // As specified in
@@ -304,32 +327,74 @@
             version: Some(1),
             ..Default::default()
         };
-        let header = Header {
-            signature_scheme: Some(SigScheme::HMAC_SHA256.into()),
-            encryption_scheme: Some(EncScheme::AES_256_CBC.into()),
-            iv: Some(iv.to_vec()),
-            public_metadata: Some(metadata.write_to_bytes().unwrap()),
-            associated_data_length: associated_data.as_ref().map(|d| d.as_ref().len() as u32),
-            ..Default::default()
+        let (ciphertext, header) = match self.protocol {
+            NextProtocol::Aes256GcmSiv => {
+                let nonce: [u8; 12] = self.rng.gen();
+                let ciphertext = crypto_utils::encrypt_gcm_siv::<C::Aes256GcmSiv>(
+                    &self.encryption_key,
+                    &message,
+                    associated_data.as_ref().map_or(&[], AsRef::as_ref),
+                    &nonce,
+                )
+                .unwrap();
+                (
+                    ciphertext,
+                    Header {
+                        signature_scheme: Some(SigScheme::AEAD.into()),
+                        encryption_scheme: Some(EncScheme::AES_256_GCM_SIV.into()),
+                        nonce: Some(nonce.to_vec()),
+                        public_metadata: Some(metadata.write_to_bytes().unwrap()),
+                        associated_data_length: associated_data
+                            .as_ref()
+                            .map(|d| d.as_ref().len() as u32),
+                        ..Default::default()
+                    },
+                )
+            }
+            NextProtocol::Aes256CbcHmacSha256 => {
+                let (ciphertext, iv) = crypto_utils::encrypt_cbc::<_, C::AesCbcPkcs7Padded>(
+                    &self.encryption_key,
+                    message.as_slice(),
+                    &mut self.rng,
+                );
+                (
+                    ciphertext,
+                    Header {
+                        signature_scheme: Some(SigScheme::HMAC_SHA256.into()),
+                        encryption_scheme: Some(EncScheme::AES_256_CBC.into()),
+                        iv: Some(iv.to_vec()),
+                        public_metadata: Some(metadata.write_to_bytes().unwrap()),
+                        associated_data_length: associated_data
+                            .as_ref()
+                            .map(|d| d.as_ref().len() as u32),
+                        ..Default::default()
+                    },
+                )
+            }
         };
+
         let header_and_body = HeaderAndBody {
             header: Some(header).into(),
             body: Some(ciphertext),
             ..Default::default()
         };
         let header_and_body_bytes = header_and_body.write_to_bytes().unwrap();
-
-        // add sha256 MAC
-        let mut hmac = C::HmacSha256::new_from_slice(&self.signing_key).unwrap();
-        hmac.update(header_and_body_bytes.as_slice());
-        if let Some(associated_data_vec) = associated_data.as_ref() {
-            hmac.update(associated_data_vec.as_ref())
-        }
-        let result_mac = hmac.finalize().to_vec();
+        let signature = match self.protocol {
+            NextProtocol::Aes256CbcHmacSha256 => {
+                // add sha256 MAC
+                let mut hmac = C::HmacSha256::new_from_slice(&self.signing_key).unwrap();
+                hmac.update(header_and_body_bytes.as_slice());
+                if let Some(associated_data_vec) = associated_data.as_ref() {
+                    hmac.update(associated_data_vec.as_ref())
+                }
+                Some(hmac.finalize().to_vec())
+            }
+            NextProtocol::Aes256GcmSiv => Some(vec![]),
+        };
 
         let secure_message = SecureMessage {
             header_and_body: Some(header_and_body_bytes),
-            signature: Some(result_mac),
+            signature,
             ..Default::default()
         };
         secure_message.write_to_bytes().unwrap()
@@ -349,36 +414,60 @@
     ) -> Result<Vec<u8>, DecodeError> {
         // first confirm that the payload MAC matches the header_and_body
         let message = SecureMessage::parse_from_bytes(payload).map_err(|_| DecodeError::BadData)?;
-        let payload_mac: [u8; 32] = message
-            .signature
-            .and_then(|signature| signature.try_into().ok())
-            .ok_or(DecodeError::BadData)?;
-        let payload = message.header_and_body.ok_or(DecodeError::BadData)?;
-        let mut hmac = C::HmacSha256::new_from_slice(&self.verify_key).unwrap();
-        hmac.update(&payload);
-        if let Some(associated_data) = associated_data.as_ref() {
-            hmac.update(associated_data.as_ref())
+        let header_and_body = message.header_and_body.ok_or(DecodeError::BadData)?;
+        match self.protocol {
+            NextProtocol::Aes256CbcHmacSha256 => {
+                let payload_mac: [u8; 32] = message
+                    .signature
+                    .and_then(|signature| signature.try_into().ok())
+                    .ok_or(DecodeError::BadData)?;
+                let mut hmac = C::HmacSha256::new_from_slice(&self.verify_key).unwrap();
+                hmac.update(&header_and_body);
+                if let Some(associated_data) = associated_data.as_ref() {
+                    hmac.update(associated_data.as_ref())
+                }
+                hmac.verify(payload_mac).map_err(|_| DecodeError::BadData)?;
+            }
+            NextProtocol::Aes256GcmSiv => {} // No need to check signature on an AEAD cipher.
         }
-        hmac.verify(payload_mac).map_err(|_| DecodeError::BadData)?;
+
         let payload =
-            HeaderAndBody::parse_from_bytes(&payload).map_err(|_| DecodeError::BadData)?;
-        let associated_data_len =
-            payload.header.as_ref().and_then(|header| header.associated_data_length);
-        if associated_data_len != associated_data.map(|ad| ad.as_ref().len() as u32) {
-            return Err(DecodeError::BadData);
-        }
-        let iv: AesCbcIv = payload
-            .header
-            .as_ref()
-            .and_then(|header| header.iv().try_into().ok())
-            .ok_or(DecodeError::BadData)?;
-        let decrypted = crypto_utils::decrypt::<C::AesCbcPkcs7Padded>(
-            &self.decryption_key,
-            &payload.body.unwrap_or_default(),
-            &iv,
-        )
-        .map_err(|_| DecodeError::BadData)?;
-        let d2d_message = unwrap_device_to_device_message(decrypted.as_slice())?;
+            HeaderAndBody::parse_from_bytes(&header_and_body).map_err(|_| DecodeError::BadData)?;
+        let decrypted = match self.protocol {
+            NextProtocol::Aes256GcmSiv => {
+                let nonce: <<C as CryptoProvider>::Aes256GcmSiv as Aead>::Nonce = payload
+                    .header
+                    .as_ref()
+                    .and_then(|header| header.nonce().try_into().ok())
+                    .ok_or(DecodeError::BadData)?;
+                crypto_utils::decrypt_gcm_siv::<C::Aes256GcmSiv>(
+                    &self.decryption_key,
+                    &payload.body.unwrap_or_default(),
+                    associated_data.as_ref().map_or(&[], AsRef::as_ref),
+                    &nonce,
+                )
+                .map_err(|_| DecodeError::BadData)?
+            }
+            NextProtocol::Aes256CbcHmacSha256 => {
+                let associated_data_len =
+                    payload.header.as_ref().and_then(|header| header.associated_data_length);
+                if associated_data_len != associated_data.map(|ad| ad.as_ref().len() as u32) {
+                    return Err(DecodeError::BadData);
+                }
+                let iv: AesCbcIv = payload
+                    .header
+                    .as_ref()
+                    .and_then(|header| header.iv().try_into().ok())
+                    .ok_or(DecodeError::BadData)?;
+                crypto_utils::decrypt_cbc::<C::AesCbcPkcs7Padded>(
+                    &self.decryption_key,
+                    &payload.body.unwrap_or_default(),
+                    &iv,
+                )
+                .map_err(|_| DecodeError::BadData)?
+            }
+        };
+        let d2d_message = unwrap_device_to_device_message(&decrypted)?;
         if d2d_message.sequence_num != self.get_sequence_number_for_decoding() + 1 {
             return Err(DecodeError::BadSequenceNumber);
         }
diff --git a/nearby/connections/ukey2/ukey2_connections/src/d2d_handshake_context.rs b/nearby/connections/ukey2/ukey2_connections/src/d2d_handshake_context.rs
index d9e1524..5e7262b 100644
--- a/nearby/connections/ukey2/ukey2_connections/src/d2d_handshake_context.rs
+++ b/nearby/connections/ukey2/ukey2_connections/src/d2d_handshake_context.rs
@@ -15,11 +15,11 @@
 
 use crate::d2d_connection_context_v1::D2DConnectionContextV1;
 use crypto_provider::CryptoProvider;
-use rand::{rngs::StdRng, SeedableRng as _};
+use rand::SeedableRng as _;
 use std::{collections::HashSet, mem};
 use ukey2_rs::{
-    CompletedHandshake, HandshakeImplementation, StateMachine, Ukey2Client, Ukey2ClientStage1,
-    Ukey2Server, Ukey2ServerStage1, Ukey2ServerStage2,
+    CompletedHandshake, HandshakeImplementation, NextProtocol, StateMachine, Ukey2Client,
+    Ukey2ClientStage1, Ukey2Server, Ukey2ServerStage1, Ukey2ServerStage2,
 };
 
 #[derive(Debug)]
@@ -99,8 +99,8 @@
 }
 
 impl<C: CryptoProvider> InitiatorD2DHandshakeContext<C, rand::rngs::StdRng> {
-    pub fn new(handshake_impl: HandshakeImplementation) -> Self {
-        Self::new_impl(handshake_impl, rand::rngs::StdRng::from_entropy())
+    pub fn new(handshake_impl: HandshakeImplementation, next_protocols: Vec<NextProtocol>) -> Self {
+        Self::new_impl(handshake_impl, rand::rngs::StdRng::from_entropy(), next_protocols)
     }
 }
 
@@ -110,12 +110,12 @@
 {
     // Used for testing / fuzzing only.
     #[doc(hidden)]
-    pub fn new_impl(handshake_impl: HandshakeImplementation, mut rng: R) -> Self {
-        let client = Ukey2ClientStage1::from(
-            &mut rng,
-            D2DConnectionContextV1::<StdRng>::NEXT_PROTOCOL_IDENTIFIER.to_owned(),
-            handshake_impl,
-        );
+    pub fn new_impl(
+        handshake_impl: HandshakeImplementation,
+        mut rng: R,
+        next_protocols: Vec<NextProtocol>,
+    ) -> Self {
+        let client = Ukey2ClientStage1::from(&mut rng, next_protocols, handshake_impl);
         Self { state: InitiatorState::Stage1(client), rng }
     }
 }
@@ -157,6 +157,14 @@
         }
     }
 
+    fn to_connection_context(&mut self) -> Result<D2DConnectionContextV1<R>, HandshakeError> {
+        // Since self.rng is expected to be a seeded PRNG, not an OsRng directly, from_rng
+        // should never fail. https://rust-random.github.io/book/guide-err.html
+        let rng = R::from_rng(&mut self.rng).unwrap();
+        self.to_completed_handshake()
+            .map(|h| D2DConnectionContextV1::from_initiator_handshake::<C>(h, rng))
+    }
+
     fn to_completed_handshake(&self) -> Result<&CompletedHandshake, HandshakeError> {
         match &self.state {
             InitiatorState::Stage1(_) | InitiatorState::Invalid => {
@@ -165,18 +173,6 @@
             InitiatorState::Complete(c) => Ok(c.completed_handshake()),
         }
     }
-
-    fn to_connection_context(&mut self) -> Result<D2DConnectionContextV1<R>, HandshakeError> {
-        // Since self.rng is expected to be a seeded PRNG, not an OsRng directly, from_rng
-        // should never fail. https://rust-random.github.io/book/guide-err.html
-        let rng = R::from_rng(&mut self.rng).unwrap();
-        self.to_completed_handshake().and_then(|h| match h.next_protocol.as_ref() {
-            D2DConnectionContextV1::<R>::NEXT_PROTOCOL_IDENTIFIER => {
-                Ok(D2DConnectionContextV1::from_initiator_handshake::<C>(h, rng))
-            }
-            _ => Err(HandshakeError::HandshakeNotComplete),
-        })
-    }
 }
 
 enum ServerState<C: CryptoProvider> {
@@ -198,8 +194,8 @@
 }
 
 impl<C: CryptoProvider> ServerD2DHandshakeContext<C, rand::rngs::StdRng> {
-    pub fn new(handshake_impl: HandshakeImplementation) -> Self {
-        Self::new_impl(handshake_impl, rand::rngs::StdRng::from_entropy())
+    pub fn new(handshake_impl: HandshakeImplementation, next_protocols: &[NextProtocol]) -> Self {
+        Self::new_impl(handshake_impl, rand::rngs::StdRng::from_entropy(), next_protocols)
     }
 }
 
@@ -209,13 +205,14 @@
 {
     // Used for testing / fuzzing only.
     #[doc(hidden)]
-    pub fn new_impl(handshake_impl: HandshakeImplementation, rng: R) -> Self {
+    pub fn new_impl(
+        handshake_impl: HandshakeImplementation,
+        rng: R,
+        next_protocols: &[NextProtocol],
+    ) -> Self {
         Self {
             state: ServerState::Stage1(Ukey2ServerStage1::from(
-                HashSet::from([
-                    D2DConnectionContextV1::<rand::rngs::StdRng>::NEXT_PROTOCOL_IDENTIFIER
-                        .to_owned(),
-                ]),
+                HashSet::from_iter(next_protocols.iter().map(|np| np.to_string())),
                 handshake_impl,
             )),
             rng,
@@ -280,16 +277,7 @@
         // Since self.rng is expected to be a seeded PRNG, not an OsRng directly, from_rng
         // should never fail. https://rust-random.github.io/book/guide-err.html
         let rng = R::from_rng(&mut self.rng).unwrap();
-        self.to_completed_handshake().map(|h| match h.next_protocol.as_ref() {
-            D2DConnectionContextV1::<R>::NEXT_PROTOCOL_IDENTIFIER => {
-                D2DConnectionContextV1::from_responder_handshake::<C>(h, rng)
-            }
-            _ => {
-                // This should never happen because ukey2_handshake should set next_protocol to
-                // one of the values we passed in Ukey2ServerStage1::from, which doesn't contain
-                // any other value.
-                panic!("Unknown next protocol: {}", h.next_protocol);
-            }
-        })
+        self.to_completed_handshake()
+            .map(|h| D2DConnectionContextV1::from_responder_handshake::<C>(h, rng))
     }
 }
diff --git a/nearby/connections/ukey2/ukey2_connections/src/lib.rs b/nearby/connections/ukey2/ukey2_connections/src/lib.rs
index b3ed937..eb8a55b 100644
--- a/nearby/connections/ukey2/ukey2_connections/src/lib.rs
+++ b/nearby/connections/ukey2/ukey2_connections/src/lib.rs
@@ -42,4 +42,4 @@
     D2DHandshakeContext, HandleMessageError, HandshakeError, InitiatorD2DHandshakeContext,
     ServerD2DHandshakeContext,
 };
-pub use ukey2_rs::HandshakeImplementation;
+pub use ukey2_rs::{HandshakeImplementation, NextProtocol};
diff --git a/nearby/connections/ukey2/ukey2_connections/src/tests.rs b/nearby/connections/ukey2/ukey2_connections/src/tests.rs
index d6acecc..9f0b13e 100644
--- a/nearby/connections/ukey2/ukey2_connections/src/tests.rs
+++ b/nearby/connections/ukey2/ukey2_connections/src/tests.rs
@@ -18,10 +18,10 @@
 use crypto_provider_default::CryptoProviderImpl;
 use rand::SeedableRng;
 use rand::{rngs::StdRng, CryptoRng, RngCore};
-use ukey2_rs::HandshakeImplementation;
+use ukey2_rs::{HandshakeImplementation, NextProtocol};
 
 use crate::{
-    crypto_utils::{decrypt, encrypt},
+    crypto_utils::{decrypt_cbc, encrypt_cbc},
     java_utils, Aes256Key, D2DConnectionContextV1, D2DHandshakeContext, DeserializeError,
     InitiatorD2DHandshakeContext, ServerD2DHandshakeContext,
 };
@@ -33,8 +33,8 @@
     let message = b"Hello World!";
     let key = b"42424242424242424242424242424242";
     let (ciphertext, iv) =
-        encrypt::<_, AesCbcPkcs7Padded>(key, message, &mut rand::rngs::StdRng::from_entropy());
-    let decrypt_result = decrypt::<AesCbcPkcs7Padded>(key, ciphertext.as_slice(), &iv);
+        encrypt_cbc::<_, AesCbcPkcs7Padded>(key, message, &mut rand::rngs::StdRng::from_entropy());
+    let decrypt_result = decrypt_cbc::<AesCbcPkcs7Padded>(key, ciphertext.as_slice(), &iv);
     let ptext = decrypt_result.expect("Decrypt should be successful");
     assert_eq!(ptext, message.to_vec());
 }
@@ -44,7 +44,7 @@
     let message = b"Hello World!";
     let key = b"42424242424242424242424242424242";
     let mut rng = MockRng;
-    let (ciphertext, iv) = encrypt::<_, AesCbcPkcs7Padded>(key, message, &mut rng);
+    let (ciphertext, iv) = encrypt_cbc::<_, AesCbcPkcs7Padded>(key, message, &mut rng);
     // Expected values extracted from the results of the current implementation.
     // This test makes sure that we don't accidentally change the encryption logic that
     // causes incompatibility between versions.
@@ -60,7 +60,7 @@
     let iv = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
     let ciphertext = [20, 59, 195, 101, 11, 208, 245, 128, 247, 196, 81, 80, 158, 77, 174, 61];
     let key = b"42424242424242424242424242424242";
-    let plaintext = decrypt::<AesCbcPkcs7Padded>(key, &ciphertext, &iv).unwrap();
+    let plaintext = decrypt_cbc::<AesCbcPkcs7Padded>(key, &ciphertext, &iv).unwrap();
     assert_eq!(plaintext, b"Hello World!");
 }
 
@@ -68,27 +68,41 @@
 fn decrypt_test_wrong_key() {
     let message = b"Hello World!";
     let good_key = b"42424242424242424242424242424242";
-    let (ciphertext, iv) =
-        encrypt::<_, AesCbcPkcs7Padded>(good_key, message, &mut rand::rngs::StdRng::from_entropy());
+    let (ciphertext, iv) = encrypt_cbc::<_, AesCbcPkcs7Padded>(
+        good_key,
+        message,
+        &mut rand::rngs::StdRng::from_entropy(),
+    );
     let bad_key = b"43434343434343434343434343434343";
-    let decrypt_result = decrypt::<AesCbcPkcs7Padded>(bad_key, ciphertext.as_slice(), &iv);
+    let decrypt_result = decrypt_cbc::<AesCbcPkcs7Padded>(bad_key, ciphertext.as_slice(), &iv);
     match decrypt_result {
         // The padding is valid, but the decrypted value should be bad since the keys don't match
         Ok(decrypted_bad) => assert_ne!(decrypted_bad, message),
         // The padding is bad, so it returns an error and is unable to decrypt
         Err(crypto_provider::aes::cbc::DecryptionError::BadPadding) => (),
     }
-    let decrypt_result = decrypt::<AesCbcPkcs7Padded>(good_key, ciphertext.as_slice(), &iv);
+    let decrypt_result = decrypt_cbc::<AesCbcPkcs7Padded>(good_key, ciphertext.as_slice(), &iv);
     let ptext = decrypt_result.unwrap();
     assert_eq!(ptext, message.to_vec());
 }
 
-fn run_handshake() -> (D2DConnectionContextV1, D2DConnectionContextV1) {
-    run_handshake_with_rng::<CryptoProviderImpl, _>(rand::rngs::StdRng::from_entropy())
+fn run_cbc_handshake() -> (D2DConnectionContextV1, D2DConnectionContextV1) {
+    run_handshake_with_rng::<CryptoProviderImpl, _>(
+        rand::rngs::StdRng::from_entropy(),
+        vec![NextProtocol::Aes256CbcHmacSha256],
+    )
+}
+
+fn run_gcm_handshake() -> (D2DConnectionContextV1, D2DConnectionContextV1) {
+    run_handshake_with_rng::<CryptoProviderImpl, _>(
+        rand::rngs::StdRng::from_entropy(),
+        vec![NextProtocol::Aes256GcmSiv],
+    )
 }
 
 fn run_handshake_with_rng<C, R>(
     mut rng: R,
+    next_protocols: Vec<NextProtocol>,
 ) -> (D2DConnectionContextV1<R>, D2DConnectionContextV1<R>)
 where
     C: CryptoProvider,
@@ -97,10 +111,12 @@
     let mut initiator_ctx = InitiatorD2DHandshakeContext::<C, R>::new_impl(
         HandshakeImplementation::Spec,
         R::from_rng(&mut rng).unwrap(),
+        next_protocols.clone(),
     );
     let mut server_ctx = ServerD2DHandshakeContext::<C, R>::new_impl(
         HandshakeImplementation::Spec,
         R::from_rng(&mut rng).unwrap(),
+        &next_protocols,
     );
     server_ctx
         .handle_handshake_message(
@@ -130,7 +146,10 @@
     let rng = MockRng;
     let message = b"Hello World!";
     let (mut init_conn_ctx, mut server_conn_ctx) =
-        run_handshake_with_rng::<RustCryptoImpl<MockRng>, _>(rng);
+        run_handshake_with_rng::<RustCryptoImpl<MockRng>, _>(
+            rng,
+            vec![NextProtocol::Aes256CbcHmacSha256],
+        );
     let encoded =
         init_conn_ctx.encode_message_to_peer::<RustCryptoImpl<MockRng>, &[u8]>(message, None);
     // Expected values extracted from the results of the current implementation.
@@ -140,10 +159,10 @@
         encoded,
         &[
             10, 64, 10, 28, 8, 1, 16, 2, 42, 16, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-            50, 4, 8, 13, 16, 1, 18, 32, 58, 224, 12, 10, 216, 38, 219, 232, 231, 222, 226, 63, 37,
-            20, 92, 208, 40, 8, 29, 98, 226, 132, 30, 61, 229, 78, 20, 182, 217, 26, 176, 77, 18,
-            32, 212, 221, 67, 39, 137, 138, 163, 222, 119, 216, 28, 176, 130, 152, 211, 63, 182,
-            45, 239, 234, 248, 148, 9, 150, 204, 117, 32, 216, 5, 126, 224, 39
+            50, 4, 8, 13, 16, 1, 18, 32, 23, 58, 102, 24, 40, 222, 59, 212, 182, 181, 96, 44, 57,
+            21, 93, 253, 71, 54, 67, 37, 226, 43, 104, 224, 178, 221, 219, 189, 106, 135, 175, 150,
+            18, 32, 134, 9, 237, 41, 112, 183, 129, 198, 240, 13, 139, 66, 21, 56, 28, 100, 142,
+            240, 155, 52, 242, 11, 211, 132, 175, 230, 15, 241, 208, 185, 15, 105
         ]
     );
     let decoded = server_conn_ctx
@@ -155,7 +174,17 @@
 #[test]
 fn send_receive_message() {
     let message = b"Hello World!";
-    let (mut init_conn_ctx, mut server_conn_ctx) = run_handshake();
+    let (mut init_conn_ctx, mut server_conn_ctx) = run_cbc_handshake();
+    let encoded = init_conn_ctx.encode_message_to_peer::<CryptoProviderImpl, &[u8]>(message, None);
+    let decoded = server_conn_ctx
+        .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(encoded.as_slice(), None);
+    assert_eq!(message.to_vec(), decoded.expect("Decode should be successful"));
+}
+
+#[test]
+fn send_receive_message_gcm() {
+    let message = b"Hello World!";
+    let (mut init_conn_ctx, mut server_conn_ctx) = run_gcm_handshake();
     let encoded = init_conn_ctx.encode_message_to_peer::<CryptoProviderImpl, &[u8]>(message, None);
     let decoded = server_conn_ctx
         .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(encoded.as_slice(), None);
@@ -165,7 +194,7 @@
 #[test]
 fn send_receive_message_associated_data() {
     let message = b"Hello World!";
-    let (mut init_conn_ctx, mut server_conn_ctx) = run_handshake();
+    let (mut init_conn_ctx, mut server_conn_ctx) = run_cbc_handshake();
     let encoded = init_conn_ctx
         .encode_message_to_peer::<CryptoProviderImpl, _>(message, Some(b"associated data"));
     let decoded = server_conn_ctx.decode_message_from_peer::<CryptoProviderImpl, _>(
@@ -187,7 +216,7 @@
 
 #[test]
 fn test_save_restore_session() {
-    let (init_conn_ctx, server_conn_ctx) = run_handshake();
+    let (init_conn_ctx, server_conn_ctx) = run_cbc_handshake();
     let init_session = init_conn_ctx.save_session();
     let server_session = server_conn_ctx.save_session();
     let mut init_restored_ctx =
@@ -206,7 +235,7 @@
 
 #[test]
 fn test_save_restore_bad_session() {
-    let (init_conn_ctx, server_conn_ctx) = run_handshake();
+    let (init_conn_ctx, server_conn_ctx) = run_cbc_handshake();
     let init_session = init_conn_ctx.save_session();
     let server_session = server_conn_ctx.save_session();
     let _ =
@@ -219,7 +248,7 @@
 
 #[test]
 fn test_save_restore_bad_protocol_version() {
-    let (init_conn_ctx, server_conn_ctx) = run_handshake();
+    let (init_conn_ctx, server_conn_ctx) = run_cbc_handshake();
     let init_session = init_conn_ctx.save_session();
     let mut server_session = server_conn_ctx.save_session();
     let _ =
@@ -233,7 +262,7 @@
 
 #[test]
 fn test_unique_session() {
-    let (mut init_conn_ctx, mut server_conn_ctx) = run_handshake();
+    let (mut init_conn_ctx, mut server_conn_ctx) = run_cbc_handshake();
     let init_session = init_conn_ctx.get_session_unique::<CryptoProviderImpl>();
     let server_session = server_conn_ctx.get_session_unique::<CryptoProviderImpl>();
     let message = b"Hello World!";
@@ -249,6 +278,7 @@
         Aes256Key::default(),
         Aes256Key::default(),
         StdRng::from_entropy(),
+        NextProtocol::Aes256CbcHmacSha256,
     );
     assert_eq!(init_session, init_session_after);
     assert_eq!(server_session, server_session_after);
diff --git a/nearby/connections/ukey2/ukey2_jni/Cargo.toml b/nearby/connections/ukey2/ukey2_jni/Cargo.toml
index 8a5b0fe..bd615f5 100644
--- a/nearby/connections/ukey2/ukey2_jni/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_jni/Cargo.toml
@@ -11,7 +11,6 @@
 
 [dependencies]
 ukey2_connections = { path = "../ukey2_connections" }
-ukey2_rs = { path = "../ukey2" }
 lock_adapter = {workspace = true, features = ["spin"]}
 
 cfg-if.workspace = true
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/ukey2/Ukey2Benchmark.java b/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/ukey2/Ukey2Benchmark.java
index fcc07b8..537280a 100644
--- a/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/ukey2/Ukey2Benchmark.java
+++ b/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/ukey2/Ukey2Benchmark.java
@@ -16,6 +16,7 @@
 
 package com.google.security.cryptauth.lib.securegcm.ukey2;
 
+import com.google.security.cryptauth.lib.securegcm.ukey2.D2DHandshakeContext.NextProtocol;
 import com.google.security.cryptauth.lib.securegcm.ukey2.D2DHandshakeContext.Role;
 import java.util.Random;
 import java.util.concurrent.TimeUnit;
@@ -43,15 +44,19 @@
     D2DConnectionContextV1 connContext;
     D2DConnectionContextV1 serverConnContext;
 
-    @Param({"10", "1024"})
+    @Param({"10", "512", "1024"})
     int sizeKibs;
 
+    @Param NextProtocol nextProtocol;
+
     byte[] inputBytes;
 
     @Setup
     public void setup() throws Exception {
-      D2DHandshakeContext initiatorContext = new D2DHandshakeContext(Role.INITIATOR);
-      D2DHandshakeContext serverContext = new D2DHandshakeContext(Role.RESPONDER);
+      D2DHandshakeContext initiatorContext =
+          new D2DHandshakeContext(Role.INITIATOR, new NextProtocol[] {nextProtocol});
+      D2DHandshakeContext serverContext =
+          new D2DHandshakeContext(Role.RESPONDER, new NextProtocol[] {nextProtocol});
       serverContext.parseHandshakeMessage(initiatorContext.getNextHandshakeMessage());
       initiatorContext.parseHandshakeMessage(serverContext.getNextHandshakeMessage());
       serverContext.parseHandshakeMessage(initiatorContext.getNextHandshakeMessage());
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DHandshakeContext.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DHandshakeContext.java
index 0e1ad62..a2db5ee 100644
--- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DHandshakeContext.java
+++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DHandshakeContext.java
@@ -28,11 +28,16 @@
     RESPONDER,
   }
 
+  public enum NextProtocol {
+    AES_256_GCM_SIV,
+    AES_256_CBC_HMAC_SHA256,
+  }
+
   private final long contextPtr;
 
   private static native boolean is_handshake_complete(long contextPtr) throws BadHandleException;
 
-  private static native long create_context(boolean isClient);
+  private static native long create_context(boolean isClient, int[] supported_next_protocols);
 
   private static native byte[] get_next_handshake_message(long contextPtr)
       throws BadHandleException;
@@ -45,8 +50,21 @@
 
   private static native long to_connection_context(long contextPtr) throws HandshakeException;
 
-  public D2DHandshakeContext(@Nonnull Role role) {
-    this.contextPtr = create_context(role == Role.INITIATOR);
+  public D2DHandshakeContext(@Nonnull Role role) throws HandshakeException {
+    this(role, new NextProtocol[] {NextProtocol.AES_256_CBC_HMAC_SHA256});
+  }
+
+  public D2DHandshakeContext(@Nonnull Role role, @Nonnull NextProtocol[] nextProtocols)
+      throws HandshakeException {
+    if (nextProtocols.length < 1) {
+      throw new HandshakeException("Need more than one supported next protocol!");
+    }
+    int[] nextProtocolCodes = new int[nextProtocols.length];
+    for (int i = 0; i < nextProtocols.length; i++) {
+      nextProtocolCodes[i] = nextProtocols[i].ordinal();
+    }
+
+    this.contextPtr = create_context(role == Role.INITIATOR, nextProtocolCodes);
   }
 
   /**
@@ -54,20 +72,42 @@
    *
    * @return a D2DHandshakeContext for the role of initiator in the handshake.
    */
-  public static D2DHandshakeContext forInitiator() {
+  public static D2DHandshakeContext forInitiator() throws HandshakeException {
     return new D2DHandshakeContext(Role.INITIATOR);
   }
 
   /**
    * Convenience constructor that creates a UKEY2 D2DHandshakeContext for the initiator role.
    *
+   * @param nextProtocols Specification for the supported next protocols for this initiator.
+   * @return a D2DHandshakeContext for the role of initiator in the handshake.
+   */
+  public static D2DHandshakeContext forInitiator(NextProtocol[] nextProtocols)
+      throws HandshakeException {
+    return new D2DHandshakeContext(Role.INITIATOR, nextProtocols);
+  }
+
+  /**
+   * Convenience constructor that creates a UKEY2 D2DHandshakeContext for the initiator role.
+   *
    * @return a D2DHandshakeContext for the role of responder/server in the handshake.
    */
-  public static D2DHandshakeContext forResponder() {
+  public static D2DHandshakeContext forResponder() throws HandshakeException {
     return new D2DHandshakeContext(Role.RESPONDER);
   }
 
   /**
+   * Convenience constructor that creates a UKEY2 D2DHandshakeContext for the initiator role.
+   *
+   * @param nextProtocols Specification for the supported next protocols for this responder.
+   * @return a D2DHandshakeContext for the role of responder/server in the handshake.
+   */
+  public static D2DHandshakeContext forResponder(NextProtocol[] nextProtocols)
+      throws HandshakeException {
+    return new D2DHandshakeContext(Role.RESPONDER, nextProtocols);
+  }
+
+  /**
    * Function that checks if the handshake is completed.
    *
    * @return true/false depending on if the handshake is complete.
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/ukey2/TestUkey2Protocol.kt b/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/ukey2/TestUkey2Protocol.kt
index 2940652..ba3811c 100644
--- a/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/ukey2/TestUkey2Protocol.kt
+++ b/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/ukey2/TestUkey2Protocol.kt
@@ -167,9 +167,71 @@
   @Test
   fun throwsAlertExceptionWhenBadMessage() {
     val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
-    val exception = assertThrows<AlertException> {
-      serverContext.parseHandshakeMessage("Hello UKEY2".toByteArray())
-    }
+    val exception =
+        assertThrows<AlertException> {
+          serverContext.parseHandshakeMessage("Hello UKEY2".toByteArray())
+        }
     assert(exception.alertMessageToSend.isNotEmpty())
   }
+
+  @Test
+  fun testGcm() {
+    val initiatorContext =
+        D2DHandshakeContext(
+            D2DHandshakeContext.Role.INITIATOR,
+            arrayOf(D2DHandshakeContext.NextProtocol.AES_256_GCM_SIV))
+    val serverContext =
+        D2DHandshakeContext(
+            D2DHandshakeContext.Role.RESPONDER,
+            arrayOf(D2DHandshakeContext.NextProtocol.AES_256_GCM_SIV))
+    assertDoesNotThrow {
+      serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+      initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
+      serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+    }
+    assert(serverContext.isHandshakeComplete)
+    assert(initiatorContext.isHandshakeComplete)
+  }
+
+  @Test
+  fun testGcmServer_cbcClient() {
+    val initiatorContext =
+        D2DHandshakeContext(
+            D2DHandshakeContext.Role.INITIATOR,
+            arrayOf(D2DHandshakeContext.NextProtocol.AES_256_CBC_HMAC_SHA256))
+    val serverContext =
+        D2DHandshakeContext(
+            D2DHandshakeContext.Role.RESPONDER,
+            arrayOf(
+                D2DHandshakeContext.NextProtocol.AES_256_CBC_HMAC_SHA256,
+                D2DHandshakeContext.NextProtocol.AES_256_GCM_SIV))
+    assertDoesNotThrow {
+      serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+      initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
+      serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+    }
+    assert(serverContext.isHandshakeComplete)
+    assert(initiatorContext.isHandshakeComplete)
+  }
+
+  @Test
+  fun testGcmClient_cbcServer() {
+    val initiatorContext =
+        D2DHandshakeContext(
+            D2DHandshakeContext.Role.INITIATOR,
+            arrayOf(
+                D2DHandshakeContext.NextProtocol.AES_256_CBC_HMAC_SHA256,
+                D2DHandshakeContext.NextProtocol.AES_256_GCM_SIV))
+    val serverContext =
+        D2DHandshakeContext(
+            D2DHandshakeContext.Role.RESPONDER,
+            arrayOf(D2DHandshakeContext.NextProtocol.AES_256_CBC_HMAC_SHA256))
+    assertDoesNotThrow {
+      serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+      initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
+      serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+    }
+    assert(serverContext.isHandshakeComplete)
+    assert(initiatorContext.isHandshakeComplete)
+  }
 }
diff --git a/nearby/connections/ukey2/ukey2_jni/src/lib.rs b/nearby/connections/ukey2/ukey2_jni/src/lib.rs
index 2b277aa..e10f723 100644
--- a/nearby/connections/ukey2/ukey2_jni/src/lib.rs
+++ b/nearby/connections/ukey2/ukey2_jni/src/lib.rs
@@ -18,25 +18,24 @@
 //TODO: remove this and fix instances of unwrap/panic
 #![allow(clippy::unwrap_used, clippy::panic)]
 
-use std::collections::HashMap;
-
-use jni::objects::{JByteArray, JClass, JThrowable};
-use jni::sys::{jboolean, jbyteArray, jint, jlong, JNI_TRUE};
+use jni::objects::{JByteArray, JClass, JIntArray, JThrowable};
+use jni::sys::{jboolean, jbyteArray, jint, jintArray, jlong, JNI_TRUE};
 use jni::JNIEnv;
 use lazy_static::lazy_static;
 use lock_adapter::NoPoisonMutex;
 use rand::Rng;
 use rand_chacha::rand_core::SeedableRng;
 use rand_chacha::ChaCha20Rng;
+use std::collections::HashMap;
 
 #[cfg(not(feature = "std"))]
 use lock_adapter::spin::Mutex;
 #[cfg(feature = "std")]
-use lock_adapter::std::Mutex;
+use lock_adapter::stdlib::Mutex;
 
 use ukey2_connections::{
     D2DConnectionContextV1, D2DHandshakeContext, DecodeError, DeserializeError, HandleMessageError,
-    HandshakeError, HandshakeImplementation, InitiatorD2DHandshakeContext,
+    HandshakeError, HandshakeImplementation, InitiatorD2DHandshakeContext, NextProtocol,
     ServerD2DHandshakeContext,
 };
 
@@ -110,20 +109,57 @@
 }
 
 /// Creates a new handshake context
+// Safety:
+// - Valid pointer: We know the message pointer is safe as it is coming directly from the JVM.
+// - This pointer is nullable, but we null-check and default to AES-CBC-256_HMAC-SHA256 otherwise.
+// - Lifetime - the jintArray passed in here is consumed immediately and is copied into a Rust array,
+//   so this data does not outlive this frame.
+// - Aliasing - there is no other JObject representing this as it is only used in one place.
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
 #[no_mangle]
 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_create_1context(
-    _: JNIEnv,
+    mut env: JNIEnv,
     _: JClass,
     is_client: jboolean,
+    next_protocols: jintArray,
 ) -> jlong {
+    let next_protocols = if next_protocols.is_null() {
+        vec![NextProtocol::Aes256CbcHmacSha256]
+    } else {
+        let next_protocols_raw = unsafe { JIntArray::from_raw(next_protocols) };
+        let next_protocols_len =
+            env.get_array_length(&next_protocols_raw).expect("Array should be valid!");
+        let mut next_protocol_buf =
+            vec![0; usize::try_from(next_protocols_len).expect("len should be valid usize!")];
+        env.get_int_array_region(&next_protocols_raw, 0, &mut next_protocol_buf)
+            .expect("Should've extracted next protocols!");
+        next_protocol_buf
+            .iter()
+            .map(|p| match *p {
+                0 => NextProtocol::Aes256CbcHmacSha256,
+                1 => NextProtocol::Aes256GcmSiv,
+                _ => {
+                    env.throw_new(
+                        "com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException",
+                        "Unsupported next protocols selected! Supported: [0, 1]",
+                    )
+                    .expect("failed to find error class");
+                    unreachable!()
+                }
+            })
+            .collect()
+    };
+
     if is_client == JNI_TRUE {
         let client_obj = Box::new(InitiatorD2DHandshakeContext::<CryptoProvider>::new(
             HandshakeImplementation::PublicKeyInProtobuf,
+            next_protocols,
         ));
         insert_handshake_handle(client_obj) as jlong
     } else {
         let server_obj = Box::new(ServerD2DHandshakeContext::<CryptoProvider>::new(
             HandshakeImplementation::PublicKeyInProtobuf,
+            &next_protocols,
         ));
         insert_handshake_handle(server_obj) as jlong
     }
diff --git a/nearby/connections/ukey2/ukey2_proto/proto/securegcm.proto b/nearby/connections/ukey2/ukey2_proto/proto/securegcm.proto
index 40ac604..d5f2b14 100644
--- a/nearby/connections/ukey2/ukey2_proto/proto/securegcm.proto
+++ b/nearby/connections/ukey2/ukey2_proto/proto/securegcm.proto
@@ -21,216 +21,6 @@
 option java_outer_classname = "SecureGcmProto";
 option objc_class_prefix = "SGCM";
 
-// Message used only during enrollment
-// Field numbers should be kept in sync with DeviceInfo in:
-//   java/com/google/security/cryptauth/backend/services/common/common.proto
-message GcmDeviceInfo {
-  // This field's name does not match the one in DeviceInfo for legacy reasons.
-  // Consider using long_device_id and device_type instead when enrolling
-  // non-android devices.
-  optional fixed64 android_device_id = 1;
-
-  // Used for device_address of DeviceInfo field 2, but for GCM capable devices.
-  optional bytes gcm_registration_id = 102;
-
-  // Used for device_address of DeviceInfo field 2, but for iOS devices.
-  optional bytes apn_registration_id = 202;
-
-  // Does the user have notifications enabled for the given device address.
-  optional bool notification_enabled = 203 [default = true];
-
-  // Used for device_address of DeviceInfo field 2, a Bluetooth Mac address for
-  // the device (e.g., to be used with EasyUnlock)
-  optional string bluetooth_mac_address = 302;
-
-  // SHA-256 hash of the device master key (from the key exchange).
-  // Differs from DeviceInfo field 3, which contains the actual master key.
-  optional bytes device_master_key_hash = 103;
-
-  // A SecureMessage.EcP256PublicKey
-  required bytes user_public_key = 4;
-
-  // device's model name
-  // (e.g., an android.os.Build.MODEL or UIDevice.model)
-  optional string device_model = 7;
-
-  // device's locale
-  optional string locale = 8;
-
-  // The handle for user_public_key (and implicitly, a master key)
-  optional bytes key_handle = 9;
-
-  // The initial counter value for the device, sent by the device
-  optional int64 counter = 12 [default = 0];
-
-  // The Operating System version on the device
-  // (e.g., an android.os.Build.DISPLAY or UIDevice.systemVersion)
-  optional string device_os_version = 13;
-
-  // The Operating System version number on the device
-  // (e.g., an android.os.Build.VERSION.SDK_INT)
-  optional int64 device_os_version_code = 14;
-
-  // The Operating System release on the device
-  // (e.g., an android.os.Build.VERSION.RELEASE)
-  optional string device_os_release = 15;
-
-  // The Operating System codename on the device
-  // (e.g., an android.os.Build.VERSION.CODENAME or UIDevice.systemName)
-  optional string device_os_codename = 16;
-
-  // The software version running on the device
-  // (e.g., Authenticator app version string)
-  optional string device_software_version = 17;
-
-  // The software version number running on the device
-  // (e.g., Authenticator app version code)
-  optional int64 device_software_version_code = 18;
-
-  // Software package information if applicable
-  // (e.g., com.google.android.apps.authenticator2)
-  optional string device_software_package = 19;
-
-  // Size of the display in thousandths of an inch (e.g., 7000 mils = 7 in)
-  optional int32 device_display_diagonal_mils = 22;
-
-  // For Authzen capable devices, their Authzen protocol version
-  optional int32 device_authzen_version = 24;
-
-  // Not all devices have device identifiers that fit in 64 bits.
-  optional bytes long_device_id = 29;
-
-  // The device manufacturer name
-  // (e.g., android.os.Build.MANUFACTURER)
-  optional string device_manufacturer = 31;
-
-  // Used to indicate which type of device this is.
-  optional DeviceType device_type = 32 [default = ANDROID];
-
-  // Fields corresponding to screenlock type/features and hardware features
-  // should be numbered in the 400 range.
-
-  // Is this device using  a secure screenlock (e.g., pattern or pin unlock)
-  optional bool using_secure_screenlock = 400 [default = false];
-
-  // Is auto-unlocking the screenlock (e.g., when at "home") supported?
-  optional bool auto_unlock_screenlock_supported = 401 [default = false];
-
-  // Is auto-unlocking the screenlock (e.g., when at "home") enabled?
-  optional bool auto_unlock_screenlock_enabled = 402 [default = false];
-
-  // Does the device have a Bluetooth (classic) radio?
-  optional bool bluetooth_radio_supported = 403 [default = false];
-
-  // Is the Bluetooth (classic) radio on?
-  optional bool bluetooth_radio_enabled = 404 [default = false];
-
-  // Does the device hardware support a mobile data connection?
-  optional bool mobile_data_supported = 405 [default = false];
-
-  // Does the device support tethering?
-  optional bool tethering_supported = 406 [default = false];
-
-  // Does the device have a BLE radio?
-  optional bool ble_radio_supported = 407 [default = false];
-
-  // Is the device a "Pixel Experience" Android device?
-  optional bool pixel_experience = 408 [default = false];
-
-  // Is the device running in the ARC++ container on a chromebook?
-  optional bool arc_plus_plus = 409 [default = false];
-
-  // Is the value set in |using_secure_screenlock| reliable? On some Android
-  // devices, the platform API to get the screenlock state is not trustworthy.
-  // See b/32212161.
-  optional bool is_screenlock_state_flaky = 410 [default = false];
-
-  // A list of multi-device software features supported by the device.
-  repeated SoftwareFeature supported_software_features = 411;
-
-  // A list of multi-device software features currently enabled (active) on the
-  // device.
-  repeated SoftwareFeature enabled_software_features = 412;
-
-  // The enrollment session id this is sent with
-  optional bytes enrollment_session_id = 1000;
-
-  // A copy of the user's OAuth token
-  optional string oauth_token = 1001;
-}
-
-// This enum is used by iOS devices as values for device_display_diagonal_mils
-// in GcmDeviceInfo. There is no good way to calculate it on those devices.
-enum AppleDeviceDiagonalMils {
-  // This is the mils diagonal on an iPhone 5.
-  APPLE_PHONE = 4000;
-  // This is the mils diagonal on an iPad mini.
-  APPLE_PAD = 7900;
-}
-
-// This should be kept in sync with DeviceType in:
-// java/com/google/security/cryptauth/backend/services/common/common_enums.proto
-enum DeviceType {
-  UNKNOWN = 0;
-  ANDROID = 1;
-  CHROME = 2;
-  IOS = 3;
-  BROWSER = 4;
-  OSX = 5;
-}
-
-// MultiDevice features which may be supported and enabled on a device. See
-enum SoftwareFeature {
-  UNKNOWN_FEATURE = 0;
-  BETTER_TOGETHER_HOST = 1;
-  BETTER_TOGETHER_CLIENT = 2;
-  EASY_UNLOCK_HOST = 3;
-  EASY_UNLOCK_CLIENT = 4;
-  MAGIC_TETHER_HOST = 5;
-  MAGIC_TETHER_CLIENT = 6;
-  SMS_CONNECT_HOST = 7;
-  SMS_CONNECT_CLIENT = 8;
-}
-
-// A list of "reasons" that can be provided for calling server-side APIs.
-// This is particularly important for calls that can be triggered by different
-// kinds of events. Please try to keep reasons as generic as possible, so that
-// codes can be re-used by various callers in a sensible fashion.
-enum InvocationReason {
-  REASON_UNKNOWN = 0;
-  // First run of the software package invoking this call
-  REASON_INITIALIZATION = 1;
-  // Ordinary periodic actions (e.g. monthly master key rotation)
-  REASON_PERIODIC = 2;
-  // Slow-cycle periodic action (e.g. yearly keypair rotation???)
-  REASON_SLOW_PERIODIC = 3;
-  // Fast-cycle periodic action (e.g. daily sync for Smart Lock users)
-  REASON_FAST_PERIODIC = 4;
-  // Expired state (e.g. expired credentials, or cached entries) was detected
-  REASON_EXPIRATION = 5;
-  // An unexpected protocol failure occurred (so attempting to repair state)
-  REASON_FAILURE_RECOVERY = 6;
-  // A new account has been added to the device
-  REASON_NEW_ACCOUNT = 7;
-  // An existing account on the device has been changed
-  REASON_CHANGED_ACCOUNT = 8;
-  // The user toggled the state of a feature (e.g. Smart Lock enabled via BT)
-  REASON_FEATURE_TOGGLED = 9;
-  // A "push" from the server caused this action (e.g. a sync tickle)
-  REASON_SERVER_INITIATED = 10;
-  // A local address change triggered this (e.g. GCM registration id changed)
-  REASON_ADDRESS_CHANGE = 11;
-  // A software update has triggered this
-  REASON_SOFTWARE_UPDATE = 12;
-  // A manual action by the user triggered this (e.g. commands sent via adb)
-  REASON_MANUAL = 13;
-  // A custom key has been invalidated on the device (e.g. screen lock is
-  // disabled).
-  REASON_CUSTOM_KEY_INVALIDATION = 14;
-  // Periodic action triggered by auth_proximity
-  REASON_PROXIMITY_PERIODIC = 15;
-}
-
 enum Type {
   ENROLLMENT = 0;
   TICKLE = 1;
@@ -284,25 +74,3 @@
   required Type type = 1;
   optional int32 version = 2 [default = 0];
 }
-
-message Tickle {
-  // Time after which this tickle should expire
-  optional fixed64 expiry_time = 1;
-}
-
-message LoginNotificationInfo {
-  // Time at which the server received the login notification request.
-  optional fixed64 creation_time = 2;
-
-  // Must correspond to user_id in LoginNotificationRequest, if set.
-  optional string email = 3;
-
-  // Host where the user's credentials were used to login, if meaningful.
-  optional string host = 4;
-
-  // Location from where the user's credentials were used, if meaningful.
-  optional string source = 5;
-
-  // Type of login, e.g. ssh, gnome-screensaver, or web.
-  optional string event_type = 6;
-}
diff --git a/nearby/connections/ukey2/ukey2_proto/proto/securemessage.proto b/nearby/connections/ukey2/ukey2_proto/proto/securemessage.proto
index 7a19739..bbaa5d5 100644
--- a/nearby/connections/ukey2/ukey2_proto/proto/securemessage.proto
+++ b/nearby/connections/ukey2/ukey2_proto/proto/securemessage.proto
@@ -72,18 +72,6 @@
   required bytes body = 2;
 }
 
-// Must be kept wire-format compatible with HeaderAndBody. Provides the
-// SecureMessage code with a consistent wire-format representation that
-// remains stable irrespective of protobuf implementation choices. This
-// low-level representation of a HeaderAndBody should not be used by
-// any code outside of the SecureMessage library implementation/tests.
-message HeaderAndBodyInternal {
-  // A raw (wire-format) byte encoding of a Header, suitable for hashing
-  required bytes header = 1;
-  // Payload data
-  required bytes body = 2;
-}
-
 // -------
 // The remainder of the messages defined here are provided only for
 // convenience. They are not needed for SecureMessage proper, but are
diff --git a/nearby/connections/ukey2/ukey2_proto/proto/ukey.proto b/nearby/connections/ukey2/ukey2_proto/proto/ukey.proto
index 1edec1f..450541c 100644
--- a/nearby/connections/ukey2/ukey2_proto/proto/ukey.proto
+++ b/nearby/connections/ukey2/ukey2_proto/proto/ukey.proto
@@ -87,8 +87,8 @@
 
   // Next protocol that the client wants to speak.
   optional string next_protocol = 4;
-  // Other next protocols the client can speak.
-  repeated string other_next_protocols = 5;
+  // Next protocols the client can speak.
+  repeated string next_protocols = 5;
 }
 
 message Ukey2ServerInit {
diff --git a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs
index fb44c51..ee43758 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.21.12
+// .proto file is parsed by protoc 3.19.1
 // @generated
 
 // https://github.com/rust-lang/rust-clippy/issues/702
diff --git a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs
index 3ba20e0..e394495 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.21.12
+// .proto file is parsed by protoc 3.19.1
 // @generated
 
 // https://github.com/rust-lang/rust-clippy/issues/702
@@ -41,1562 +41,6 @@
 const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0;
 
 #[derive(PartialEq,Clone,Default,Debug)]
-// @@protoc_insertion_point(message:securegcm.GcmDeviceInfo)
-pub struct GcmDeviceInfo {
-    // message fields
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.android_device_id)
-    pub android_device_id: ::std::option::Option<u64>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.gcm_registration_id)
-    pub gcm_registration_id: ::std::option::Option<::std::vec::Vec<u8>>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.apn_registration_id)
-    pub apn_registration_id: ::std::option::Option<::std::vec::Vec<u8>>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.notification_enabled)
-    pub notification_enabled: ::std::option::Option<bool>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.bluetooth_mac_address)
-    pub bluetooth_mac_address: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_master_key_hash)
-    pub device_master_key_hash: ::std::option::Option<::std::vec::Vec<u8>>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.user_public_key)
-    pub user_public_key: ::std::option::Option<::std::vec::Vec<u8>>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_model)
-    pub device_model: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.locale)
-    pub locale: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.key_handle)
-    pub key_handle: ::std::option::Option<::std::vec::Vec<u8>>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.counter)
-    pub counter: ::std::option::Option<i64>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_os_version)
-    pub device_os_version: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_os_version_code)
-    pub device_os_version_code: ::std::option::Option<i64>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_os_release)
-    pub device_os_release: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_os_codename)
-    pub device_os_codename: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_software_version)
-    pub device_software_version: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_software_version_code)
-    pub device_software_version_code: ::std::option::Option<i64>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_software_package)
-    pub device_software_package: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_display_diagonal_mils)
-    pub device_display_diagonal_mils: ::std::option::Option<i32>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_authzen_version)
-    pub device_authzen_version: ::std::option::Option<i32>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.long_device_id)
-    pub long_device_id: ::std::option::Option<::std::vec::Vec<u8>>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_manufacturer)
-    pub device_manufacturer: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.device_type)
-    pub device_type: ::std::option::Option<::protobuf::EnumOrUnknown<DeviceType>>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.using_secure_screenlock)
-    pub using_secure_screenlock: ::std::option::Option<bool>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.auto_unlock_screenlock_supported)
-    pub auto_unlock_screenlock_supported: ::std::option::Option<bool>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.auto_unlock_screenlock_enabled)
-    pub auto_unlock_screenlock_enabled: ::std::option::Option<bool>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.bluetooth_radio_supported)
-    pub bluetooth_radio_supported: ::std::option::Option<bool>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.bluetooth_radio_enabled)
-    pub bluetooth_radio_enabled: ::std::option::Option<bool>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.mobile_data_supported)
-    pub mobile_data_supported: ::std::option::Option<bool>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.tethering_supported)
-    pub tethering_supported: ::std::option::Option<bool>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.ble_radio_supported)
-    pub ble_radio_supported: ::std::option::Option<bool>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.pixel_experience)
-    pub pixel_experience: ::std::option::Option<bool>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.arc_plus_plus)
-    pub arc_plus_plus: ::std::option::Option<bool>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.is_screenlock_state_flaky)
-    pub is_screenlock_state_flaky: ::std::option::Option<bool>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.supported_software_features)
-    pub supported_software_features: ::std::vec::Vec<::protobuf::EnumOrUnknown<SoftwareFeature>>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.enabled_software_features)
-    pub enabled_software_features: ::std::vec::Vec<::protobuf::EnumOrUnknown<SoftwareFeature>>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.enrollment_session_id)
-    pub enrollment_session_id: ::std::option::Option<::std::vec::Vec<u8>>,
-    // @@protoc_insertion_point(field:securegcm.GcmDeviceInfo.oauth_token)
-    pub oauth_token: ::std::option::Option<::std::string::String>,
-    // special fields
-    // @@protoc_insertion_point(special_field:securegcm.GcmDeviceInfo.special_fields)
-    pub special_fields: ::protobuf::SpecialFields,
-}
-
-impl<'a> ::std::default::Default for &'a GcmDeviceInfo {
-    fn default() -> &'a GcmDeviceInfo {
-        <GcmDeviceInfo as ::protobuf::Message>::default_instance()
-    }
-}
-
-impl GcmDeviceInfo {
-    pub fn new() -> GcmDeviceInfo {
-        ::std::default::Default::default()
-    }
-
-    // optional fixed64 android_device_id = 1;
-
-    pub fn android_device_id(&self) -> u64 {
-        self.android_device_id.unwrap_or(0)
-    }
-
-    pub fn clear_android_device_id(&mut self) {
-        self.android_device_id = ::std::option::Option::None;
-    }
-
-    pub fn has_android_device_id(&self) -> bool {
-        self.android_device_id.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_android_device_id(&mut self, v: u64) {
-        self.android_device_id = ::std::option::Option::Some(v);
-    }
-
-    // optional bytes gcm_registration_id = 102;
-
-    pub fn gcm_registration_id(&self) -> &[u8] {
-        match self.gcm_registration_id.as_ref() {
-            Some(v) => v,
-            None => &[],
-        }
-    }
-
-    pub fn clear_gcm_registration_id(&mut self) {
-        self.gcm_registration_id = ::std::option::Option::None;
-    }
-
-    pub fn has_gcm_registration_id(&self) -> bool {
-        self.gcm_registration_id.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_gcm_registration_id(&mut self, v: ::std::vec::Vec<u8>) {
-        self.gcm_registration_id = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_gcm_registration_id(&mut self) -> &mut ::std::vec::Vec<u8> {
-        if self.gcm_registration_id.is_none() {
-            self.gcm_registration_id = ::std::option::Option::Some(::std::vec::Vec::new());
-        }
-        self.gcm_registration_id.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_gcm_registration_id(&mut self) -> ::std::vec::Vec<u8> {
-        self.gcm_registration_id.take().unwrap_or_else(|| ::std::vec::Vec::new())
-    }
-
-    // optional bytes apn_registration_id = 202;
-
-    pub fn apn_registration_id(&self) -> &[u8] {
-        match self.apn_registration_id.as_ref() {
-            Some(v) => v,
-            None => &[],
-        }
-    }
-
-    pub fn clear_apn_registration_id(&mut self) {
-        self.apn_registration_id = ::std::option::Option::None;
-    }
-
-    pub fn has_apn_registration_id(&self) -> bool {
-        self.apn_registration_id.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_apn_registration_id(&mut self, v: ::std::vec::Vec<u8>) {
-        self.apn_registration_id = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_apn_registration_id(&mut self) -> &mut ::std::vec::Vec<u8> {
-        if self.apn_registration_id.is_none() {
-            self.apn_registration_id = ::std::option::Option::Some(::std::vec::Vec::new());
-        }
-        self.apn_registration_id.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_apn_registration_id(&mut self) -> ::std::vec::Vec<u8> {
-        self.apn_registration_id.take().unwrap_or_else(|| ::std::vec::Vec::new())
-    }
-
-    // optional bool notification_enabled = 203;
-
-    pub fn notification_enabled(&self) -> bool {
-        self.notification_enabled.unwrap_or(true)
-    }
-
-    pub fn clear_notification_enabled(&mut self) {
-        self.notification_enabled = ::std::option::Option::None;
-    }
-
-    pub fn has_notification_enabled(&self) -> bool {
-        self.notification_enabled.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_notification_enabled(&mut self, v: bool) {
-        self.notification_enabled = ::std::option::Option::Some(v);
-    }
-
-    // optional string bluetooth_mac_address = 302;
-
-    pub fn bluetooth_mac_address(&self) -> &str {
-        match self.bluetooth_mac_address.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_bluetooth_mac_address(&mut self) {
-        self.bluetooth_mac_address = ::std::option::Option::None;
-    }
-
-    pub fn has_bluetooth_mac_address(&self) -> bool {
-        self.bluetooth_mac_address.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_bluetooth_mac_address(&mut self, v: ::std::string::String) {
-        self.bluetooth_mac_address = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_bluetooth_mac_address(&mut self) -> &mut ::std::string::String {
-        if self.bluetooth_mac_address.is_none() {
-            self.bluetooth_mac_address = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.bluetooth_mac_address.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_bluetooth_mac_address(&mut self) -> ::std::string::String {
-        self.bluetooth_mac_address.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-
-    // optional bytes device_master_key_hash = 103;
-
-    pub fn device_master_key_hash(&self) -> &[u8] {
-        match self.device_master_key_hash.as_ref() {
-            Some(v) => v,
-            None => &[],
-        }
-    }
-
-    pub fn clear_device_master_key_hash(&mut self) {
-        self.device_master_key_hash = ::std::option::Option::None;
-    }
-
-    pub fn has_device_master_key_hash(&self) -> bool {
-        self.device_master_key_hash.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_master_key_hash(&mut self, v: ::std::vec::Vec<u8>) {
-        self.device_master_key_hash = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_device_master_key_hash(&mut self) -> &mut ::std::vec::Vec<u8> {
-        if self.device_master_key_hash.is_none() {
-            self.device_master_key_hash = ::std::option::Option::Some(::std::vec::Vec::new());
-        }
-        self.device_master_key_hash.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_device_master_key_hash(&mut self) -> ::std::vec::Vec<u8> {
-        self.device_master_key_hash.take().unwrap_or_else(|| ::std::vec::Vec::new())
-    }
-
-    // required bytes user_public_key = 4;
-
-    pub fn user_public_key(&self) -> &[u8] {
-        match self.user_public_key.as_ref() {
-            Some(v) => v,
-            None => &[],
-        }
-    }
-
-    pub fn clear_user_public_key(&mut self) {
-        self.user_public_key = ::std::option::Option::None;
-    }
-
-    pub fn has_user_public_key(&self) -> bool {
-        self.user_public_key.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_user_public_key(&mut self, v: ::std::vec::Vec<u8>) {
-        self.user_public_key = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_user_public_key(&mut self) -> &mut ::std::vec::Vec<u8> {
-        if self.user_public_key.is_none() {
-            self.user_public_key = ::std::option::Option::Some(::std::vec::Vec::new());
-        }
-        self.user_public_key.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_user_public_key(&mut self) -> ::std::vec::Vec<u8> {
-        self.user_public_key.take().unwrap_or_else(|| ::std::vec::Vec::new())
-    }
-
-    // optional string device_model = 7;
-
-    pub fn device_model(&self) -> &str {
-        match self.device_model.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_device_model(&mut self) {
-        self.device_model = ::std::option::Option::None;
-    }
-
-    pub fn has_device_model(&self) -> bool {
-        self.device_model.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_model(&mut self, v: ::std::string::String) {
-        self.device_model = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_device_model(&mut self) -> &mut ::std::string::String {
-        if self.device_model.is_none() {
-            self.device_model = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.device_model.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_device_model(&mut self) -> ::std::string::String {
-        self.device_model.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-
-    // optional string locale = 8;
-
-    pub fn locale(&self) -> &str {
-        match self.locale.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_locale(&mut self) {
-        self.locale = ::std::option::Option::None;
-    }
-
-    pub fn has_locale(&self) -> bool {
-        self.locale.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_locale(&mut self, v: ::std::string::String) {
-        self.locale = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_locale(&mut self) -> &mut ::std::string::String {
-        if self.locale.is_none() {
-            self.locale = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.locale.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_locale(&mut self) -> ::std::string::String {
-        self.locale.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-
-    // optional bytes key_handle = 9;
-
-    pub fn key_handle(&self) -> &[u8] {
-        match self.key_handle.as_ref() {
-            Some(v) => v,
-            None => &[],
-        }
-    }
-
-    pub fn clear_key_handle(&mut self) {
-        self.key_handle = ::std::option::Option::None;
-    }
-
-    pub fn has_key_handle(&self) -> bool {
-        self.key_handle.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_key_handle(&mut self, v: ::std::vec::Vec<u8>) {
-        self.key_handle = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_key_handle(&mut self) -> &mut ::std::vec::Vec<u8> {
-        if self.key_handle.is_none() {
-            self.key_handle = ::std::option::Option::Some(::std::vec::Vec::new());
-        }
-        self.key_handle.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_key_handle(&mut self) -> ::std::vec::Vec<u8> {
-        self.key_handle.take().unwrap_or_else(|| ::std::vec::Vec::new())
-    }
-
-    // optional int64 counter = 12;
-
-    pub fn counter(&self) -> i64 {
-        self.counter.unwrap_or(0i64)
-    }
-
-    pub fn clear_counter(&mut self) {
-        self.counter = ::std::option::Option::None;
-    }
-
-    pub fn has_counter(&self) -> bool {
-        self.counter.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_counter(&mut self, v: i64) {
-        self.counter = ::std::option::Option::Some(v);
-    }
-
-    // optional string device_os_version = 13;
-
-    pub fn device_os_version(&self) -> &str {
-        match self.device_os_version.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_device_os_version(&mut self) {
-        self.device_os_version = ::std::option::Option::None;
-    }
-
-    pub fn has_device_os_version(&self) -> bool {
-        self.device_os_version.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_os_version(&mut self, v: ::std::string::String) {
-        self.device_os_version = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_device_os_version(&mut self) -> &mut ::std::string::String {
-        if self.device_os_version.is_none() {
-            self.device_os_version = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.device_os_version.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_device_os_version(&mut self) -> ::std::string::String {
-        self.device_os_version.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-
-    // optional int64 device_os_version_code = 14;
-
-    pub fn device_os_version_code(&self) -> i64 {
-        self.device_os_version_code.unwrap_or(0)
-    }
-
-    pub fn clear_device_os_version_code(&mut self) {
-        self.device_os_version_code = ::std::option::Option::None;
-    }
-
-    pub fn has_device_os_version_code(&self) -> bool {
-        self.device_os_version_code.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_os_version_code(&mut self, v: i64) {
-        self.device_os_version_code = ::std::option::Option::Some(v);
-    }
-
-    // optional string device_os_release = 15;
-
-    pub fn device_os_release(&self) -> &str {
-        match self.device_os_release.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_device_os_release(&mut self) {
-        self.device_os_release = ::std::option::Option::None;
-    }
-
-    pub fn has_device_os_release(&self) -> bool {
-        self.device_os_release.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_os_release(&mut self, v: ::std::string::String) {
-        self.device_os_release = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_device_os_release(&mut self) -> &mut ::std::string::String {
-        if self.device_os_release.is_none() {
-            self.device_os_release = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.device_os_release.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_device_os_release(&mut self) -> ::std::string::String {
-        self.device_os_release.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-
-    // optional string device_os_codename = 16;
-
-    pub fn device_os_codename(&self) -> &str {
-        match self.device_os_codename.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_device_os_codename(&mut self) {
-        self.device_os_codename = ::std::option::Option::None;
-    }
-
-    pub fn has_device_os_codename(&self) -> bool {
-        self.device_os_codename.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_os_codename(&mut self, v: ::std::string::String) {
-        self.device_os_codename = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_device_os_codename(&mut self) -> &mut ::std::string::String {
-        if self.device_os_codename.is_none() {
-            self.device_os_codename = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.device_os_codename.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_device_os_codename(&mut self) -> ::std::string::String {
-        self.device_os_codename.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-
-    // optional string device_software_version = 17;
-
-    pub fn device_software_version(&self) -> &str {
-        match self.device_software_version.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_device_software_version(&mut self) {
-        self.device_software_version = ::std::option::Option::None;
-    }
-
-    pub fn has_device_software_version(&self) -> bool {
-        self.device_software_version.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_software_version(&mut self, v: ::std::string::String) {
-        self.device_software_version = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_device_software_version(&mut self) -> &mut ::std::string::String {
-        if self.device_software_version.is_none() {
-            self.device_software_version = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.device_software_version.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_device_software_version(&mut self) -> ::std::string::String {
-        self.device_software_version.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-
-    // optional int64 device_software_version_code = 18;
-
-    pub fn device_software_version_code(&self) -> i64 {
-        self.device_software_version_code.unwrap_or(0)
-    }
-
-    pub fn clear_device_software_version_code(&mut self) {
-        self.device_software_version_code = ::std::option::Option::None;
-    }
-
-    pub fn has_device_software_version_code(&self) -> bool {
-        self.device_software_version_code.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_software_version_code(&mut self, v: i64) {
-        self.device_software_version_code = ::std::option::Option::Some(v);
-    }
-
-    // optional string device_software_package = 19;
-
-    pub fn device_software_package(&self) -> &str {
-        match self.device_software_package.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_device_software_package(&mut self) {
-        self.device_software_package = ::std::option::Option::None;
-    }
-
-    pub fn has_device_software_package(&self) -> bool {
-        self.device_software_package.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_software_package(&mut self, v: ::std::string::String) {
-        self.device_software_package = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_device_software_package(&mut self) -> &mut ::std::string::String {
-        if self.device_software_package.is_none() {
-            self.device_software_package = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.device_software_package.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_device_software_package(&mut self) -> ::std::string::String {
-        self.device_software_package.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-
-    // optional int32 device_display_diagonal_mils = 22;
-
-    pub fn device_display_diagonal_mils(&self) -> i32 {
-        self.device_display_diagonal_mils.unwrap_or(0)
-    }
-
-    pub fn clear_device_display_diagonal_mils(&mut self) {
-        self.device_display_diagonal_mils = ::std::option::Option::None;
-    }
-
-    pub fn has_device_display_diagonal_mils(&self) -> bool {
-        self.device_display_diagonal_mils.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_display_diagonal_mils(&mut self, v: i32) {
-        self.device_display_diagonal_mils = ::std::option::Option::Some(v);
-    }
-
-    // optional int32 device_authzen_version = 24;
-
-    pub fn device_authzen_version(&self) -> i32 {
-        self.device_authzen_version.unwrap_or(0)
-    }
-
-    pub fn clear_device_authzen_version(&mut self) {
-        self.device_authzen_version = ::std::option::Option::None;
-    }
-
-    pub fn has_device_authzen_version(&self) -> bool {
-        self.device_authzen_version.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_authzen_version(&mut self, v: i32) {
-        self.device_authzen_version = ::std::option::Option::Some(v);
-    }
-
-    // optional bytes long_device_id = 29;
-
-    pub fn long_device_id(&self) -> &[u8] {
-        match self.long_device_id.as_ref() {
-            Some(v) => v,
-            None => &[],
-        }
-    }
-
-    pub fn clear_long_device_id(&mut self) {
-        self.long_device_id = ::std::option::Option::None;
-    }
-
-    pub fn has_long_device_id(&self) -> bool {
-        self.long_device_id.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_long_device_id(&mut self, v: ::std::vec::Vec<u8>) {
-        self.long_device_id = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_long_device_id(&mut self) -> &mut ::std::vec::Vec<u8> {
-        if self.long_device_id.is_none() {
-            self.long_device_id = ::std::option::Option::Some(::std::vec::Vec::new());
-        }
-        self.long_device_id.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_long_device_id(&mut self) -> ::std::vec::Vec<u8> {
-        self.long_device_id.take().unwrap_or_else(|| ::std::vec::Vec::new())
-    }
-
-    // optional string device_manufacturer = 31;
-
-    pub fn device_manufacturer(&self) -> &str {
-        match self.device_manufacturer.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_device_manufacturer(&mut self) {
-        self.device_manufacturer = ::std::option::Option::None;
-    }
-
-    pub fn has_device_manufacturer(&self) -> bool {
-        self.device_manufacturer.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_manufacturer(&mut self, v: ::std::string::String) {
-        self.device_manufacturer = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_device_manufacturer(&mut self) -> &mut ::std::string::String {
-        if self.device_manufacturer.is_none() {
-            self.device_manufacturer = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.device_manufacturer.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_device_manufacturer(&mut self) -> ::std::string::String {
-        self.device_manufacturer.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-
-    // optional .securegcm.DeviceType device_type = 32;
-
-    pub fn device_type(&self) -> DeviceType {
-        match self.device_type {
-            Some(e) => e.enum_value_or(DeviceType::ANDROID),
-            None => DeviceType::ANDROID,
-        }
-    }
-
-    pub fn clear_device_type(&mut self) {
-        self.device_type = ::std::option::Option::None;
-    }
-
-    pub fn has_device_type(&self) -> bool {
-        self.device_type.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_device_type(&mut self, v: DeviceType) {
-        self.device_type = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v));
-    }
-
-    // optional bool using_secure_screenlock = 400;
-
-    pub fn using_secure_screenlock(&self) -> bool {
-        self.using_secure_screenlock.unwrap_or(false)
-    }
-
-    pub fn clear_using_secure_screenlock(&mut self) {
-        self.using_secure_screenlock = ::std::option::Option::None;
-    }
-
-    pub fn has_using_secure_screenlock(&self) -> bool {
-        self.using_secure_screenlock.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_using_secure_screenlock(&mut self, v: bool) {
-        self.using_secure_screenlock = ::std::option::Option::Some(v);
-    }
-
-    // optional bool auto_unlock_screenlock_supported = 401;
-
-    pub fn auto_unlock_screenlock_supported(&self) -> bool {
-        self.auto_unlock_screenlock_supported.unwrap_or(false)
-    }
-
-    pub fn clear_auto_unlock_screenlock_supported(&mut self) {
-        self.auto_unlock_screenlock_supported = ::std::option::Option::None;
-    }
-
-    pub fn has_auto_unlock_screenlock_supported(&self) -> bool {
-        self.auto_unlock_screenlock_supported.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_auto_unlock_screenlock_supported(&mut self, v: bool) {
-        self.auto_unlock_screenlock_supported = ::std::option::Option::Some(v);
-    }
-
-    // optional bool auto_unlock_screenlock_enabled = 402;
-
-    pub fn auto_unlock_screenlock_enabled(&self) -> bool {
-        self.auto_unlock_screenlock_enabled.unwrap_or(false)
-    }
-
-    pub fn clear_auto_unlock_screenlock_enabled(&mut self) {
-        self.auto_unlock_screenlock_enabled = ::std::option::Option::None;
-    }
-
-    pub fn has_auto_unlock_screenlock_enabled(&self) -> bool {
-        self.auto_unlock_screenlock_enabled.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_auto_unlock_screenlock_enabled(&mut self, v: bool) {
-        self.auto_unlock_screenlock_enabled = ::std::option::Option::Some(v);
-    }
-
-    // optional bool bluetooth_radio_supported = 403;
-
-    pub fn bluetooth_radio_supported(&self) -> bool {
-        self.bluetooth_radio_supported.unwrap_or(false)
-    }
-
-    pub fn clear_bluetooth_radio_supported(&mut self) {
-        self.bluetooth_radio_supported = ::std::option::Option::None;
-    }
-
-    pub fn has_bluetooth_radio_supported(&self) -> bool {
-        self.bluetooth_radio_supported.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_bluetooth_radio_supported(&mut self, v: bool) {
-        self.bluetooth_radio_supported = ::std::option::Option::Some(v);
-    }
-
-    // optional bool bluetooth_radio_enabled = 404;
-
-    pub fn bluetooth_radio_enabled(&self) -> bool {
-        self.bluetooth_radio_enabled.unwrap_or(false)
-    }
-
-    pub fn clear_bluetooth_radio_enabled(&mut self) {
-        self.bluetooth_radio_enabled = ::std::option::Option::None;
-    }
-
-    pub fn has_bluetooth_radio_enabled(&self) -> bool {
-        self.bluetooth_radio_enabled.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_bluetooth_radio_enabled(&mut self, v: bool) {
-        self.bluetooth_radio_enabled = ::std::option::Option::Some(v);
-    }
-
-    // optional bool mobile_data_supported = 405;
-
-    pub fn mobile_data_supported(&self) -> bool {
-        self.mobile_data_supported.unwrap_or(false)
-    }
-
-    pub fn clear_mobile_data_supported(&mut self) {
-        self.mobile_data_supported = ::std::option::Option::None;
-    }
-
-    pub fn has_mobile_data_supported(&self) -> bool {
-        self.mobile_data_supported.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_mobile_data_supported(&mut self, v: bool) {
-        self.mobile_data_supported = ::std::option::Option::Some(v);
-    }
-
-    // optional bool tethering_supported = 406;
-
-    pub fn tethering_supported(&self) -> bool {
-        self.tethering_supported.unwrap_or(false)
-    }
-
-    pub fn clear_tethering_supported(&mut self) {
-        self.tethering_supported = ::std::option::Option::None;
-    }
-
-    pub fn has_tethering_supported(&self) -> bool {
-        self.tethering_supported.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_tethering_supported(&mut self, v: bool) {
-        self.tethering_supported = ::std::option::Option::Some(v);
-    }
-
-    // optional bool ble_radio_supported = 407;
-
-    pub fn ble_radio_supported(&self) -> bool {
-        self.ble_radio_supported.unwrap_or(false)
-    }
-
-    pub fn clear_ble_radio_supported(&mut self) {
-        self.ble_radio_supported = ::std::option::Option::None;
-    }
-
-    pub fn has_ble_radio_supported(&self) -> bool {
-        self.ble_radio_supported.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_ble_radio_supported(&mut self, v: bool) {
-        self.ble_radio_supported = ::std::option::Option::Some(v);
-    }
-
-    // optional bool pixel_experience = 408;
-
-    pub fn pixel_experience(&self) -> bool {
-        self.pixel_experience.unwrap_or(false)
-    }
-
-    pub fn clear_pixel_experience(&mut self) {
-        self.pixel_experience = ::std::option::Option::None;
-    }
-
-    pub fn has_pixel_experience(&self) -> bool {
-        self.pixel_experience.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_pixel_experience(&mut self, v: bool) {
-        self.pixel_experience = ::std::option::Option::Some(v);
-    }
-
-    // optional bool arc_plus_plus = 409;
-
-    pub fn arc_plus_plus(&self) -> bool {
-        self.arc_plus_plus.unwrap_or(false)
-    }
-
-    pub fn clear_arc_plus_plus(&mut self) {
-        self.arc_plus_plus = ::std::option::Option::None;
-    }
-
-    pub fn has_arc_plus_plus(&self) -> bool {
-        self.arc_plus_plus.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_arc_plus_plus(&mut self, v: bool) {
-        self.arc_plus_plus = ::std::option::Option::Some(v);
-    }
-
-    // optional bool is_screenlock_state_flaky = 410;
-
-    pub fn is_screenlock_state_flaky(&self) -> bool {
-        self.is_screenlock_state_flaky.unwrap_or(false)
-    }
-
-    pub fn clear_is_screenlock_state_flaky(&mut self) {
-        self.is_screenlock_state_flaky = ::std::option::Option::None;
-    }
-
-    pub fn has_is_screenlock_state_flaky(&self) -> bool {
-        self.is_screenlock_state_flaky.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_is_screenlock_state_flaky(&mut self, v: bool) {
-        self.is_screenlock_state_flaky = ::std::option::Option::Some(v);
-    }
-
-    // optional bytes enrollment_session_id = 1000;
-
-    pub fn enrollment_session_id(&self) -> &[u8] {
-        match self.enrollment_session_id.as_ref() {
-            Some(v) => v,
-            None => &[],
-        }
-    }
-
-    pub fn clear_enrollment_session_id(&mut self) {
-        self.enrollment_session_id = ::std::option::Option::None;
-    }
-
-    pub fn has_enrollment_session_id(&self) -> bool {
-        self.enrollment_session_id.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_enrollment_session_id(&mut self, v: ::std::vec::Vec<u8>) {
-        self.enrollment_session_id = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_enrollment_session_id(&mut self) -> &mut ::std::vec::Vec<u8> {
-        if self.enrollment_session_id.is_none() {
-            self.enrollment_session_id = ::std::option::Option::Some(::std::vec::Vec::new());
-        }
-        self.enrollment_session_id.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_enrollment_session_id(&mut self) -> ::std::vec::Vec<u8> {
-        self.enrollment_session_id.take().unwrap_or_else(|| ::std::vec::Vec::new())
-    }
-
-    // optional string oauth_token = 1001;
-
-    pub fn oauth_token(&self) -> &str {
-        match self.oauth_token.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_oauth_token(&mut self) {
-        self.oauth_token = ::std::option::Option::None;
-    }
-
-    pub fn has_oauth_token(&self) -> bool {
-        self.oauth_token.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_oauth_token(&mut self, v: ::std::string::String) {
-        self.oauth_token = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_oauth_token(&mut self) -> &mut ::std::string::String {
-        if self.oauth_token.is_none() {
-            self.oauth_token = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.oauth_token.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_oauth_token(&mut self) -> ::std::string::String {
-        self.oauth_token.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-}
-
-impl ::protobuf::Message for GcmDeviceInfo {
-    const NAME: &'static str = "GcmDeviceInfo";
-
-    fn is_initialized(&self) -> bool {
-        if self.user_public_key.is_none() {
-            return false;
-        }
-        true
-    }
-
-    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
-        while let Some(tag) = is.read_raw_tag_or_eof()? {
-            match tag {
-                9 => {
-                    self.android_device_id = ::std::option::Option::Some(is.read_fixed64()?);
-                },
-                818 => {
-                    self.gcm_registration_id = ::std::option::Option::Some(is.read_bytes()?);
-                },
-                1618 => {
-                    self.apn_registration_id = ::std::option::Option::Some(is.read_bytes()?);
-                },
-                1624 => {
-                    self.notification_enabled = ::std::option::Option::Some(is.read_bool()?);
-                },
-                2418 => {
-                    self.bluetooth_mac_address = ::std::option::Option::Some(is.read_string()?);
-                },
-                826 => {
-                    self.device_master_key_hash = ::std::option::Option::Some(is.read_bytes()?);
-                },
-                34 => {
-                    self.user_public_key = ::std::option::Option::Some(is.read_bytes()?);
-                },
-                58 => {
-                    self.device_model = ::std::option::Option::Some(is.read_string()?);
-                },
-                66 => {
-                    self.locale = ::std::option::Option::Some(is.read_string()?);
-                },
-                74 => {
-                    self.key_handle = ::std::option::Option::Some(is.read_bytes()?);
-                },
-                96 => {
-                    self.counter = ::std::option::Option::Some(is.read_int64()?);
-                },
-                106 => {
-                    self.device_os_version = ::std::option::Option::Some(is.read_string()?);
-                },
-                112 => {
-                    self.device_os_version_code = ::std::option::Option::Some(is.read_int64()?);
-                },
-                122 => {
-                    self.device_os_release = ::std::option::Option::Some(is.read_string()?);
-                },
-                130 => {
-                    self.device_os_codename = ::std::option::Option::Some(is.read_string()?);
-                },
-                138 => {
-                    self.device_software_version = ::std::option::Option::Some(is.read_string()?);
-                },
-                144 => {
-                    self.device_software_version_code = ::std::option::Option::Some(is.read_int64()?);
-                },
-                154 => {
-                    self.device_software_package = ::std::option::Option::Some(is.read_string()?);
-                },
-                176 => {
-                    self.device_display_diagonal_mils = ::std::option::Option::Some(is.read_int32()?);
-                },
-                192 => {
-                    self.device_authzen_version = ::std::option::Option::Some(is.read_int32()?);
-                },
-                234 => {
-                    self.long_device_id = ::std::option::Option::Some(is.read_bytes()?);
-                },
-                250 => {
-                    self.device_manufacturer = ::std::option::Option::Some(is.read_string()?);
-                },
-                256 => {
-                    self.device_type = ::std::option::Option::Some(is.read_enum_or_unknown()?);
-                },
-                3200 => {
-                    self.using_secure_screenlock = ::std::option::Option::Some(is.read_bool()?);
-                },
-                3208 => {
-                    self.auto_unlock_screenlock_supported = ::std::option::Option::Some(is.read_bool()?);
-                },
-                3216 => {
-                    self.auto_unlock_screenlock_enabled = ::std::option::Option::Some(is.read_bool()?);
-                },
-                3224 => {
-                    self.bluetooth_radio_supported = ::std::option::Option::Some(is.read_bool()?);
-                },
-                3232 => {
-                    self.bluetooth_radio_enabled = ::std::option::Option::Some(is.read_bool()?);
-                },
-                3240 => {
-                    self.mobile_data_supported = ::std::option::Option::Some(is.read_bool()?);
-                },
-                3248 => {
-                    self.tethering_supported = ::std::option::Option::Some(is.read_bool()?);
-                },
-                3256 => {
-                    self.ble_radio_supported = ::std::option::Option::Some(is.read_bool()?);
-                },
-                3264 => {
-                    self.pixel_experience = ::std::option::Option::Some(is.read_bool()?);
-                },
-                3272 => {
-                    self.arc_plus_plus = ::std::option::Option::Some(is.read_bool()?);
-                },
-                3280 => {
-                    self.is_screenlock_state_flaky = ::std::option::Option::Some(is.read_bool()?);
-                },
-                3288 => {
-                    self.supported_software_features.push(is.read_enum_or_unknown()?);
-                },
-                3290 => {
-                    ::protobuf::rt::read_repeated_packed_enum_or_unknown_into(is, &mut self.supported_software_features)?
-                },
-                3296 => {
-                    self.enabled_software_features.push(is.read_enum_or_unknown()?);
-                },
-                3298 => {
-                    ::protobuf::rt::read_repeated_packed_enum_or_unknown_into(is, &mut self.enabled_software_features)?
-                },
-                8002 => {
-                    self.enrollment_session_id = ::std::option::Option::Some(is.read_bytes()?);
-                },
-                8010 => {
-                    self.oauth_token = ::std::option::Option::Some(is.read_string()?);
-                },
-                tag => {
-                    ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
-                },
-            };
-        }
-        ::std::result::Result::Ok(())
-    }
-
-    // Compute sizes of nested messages
-    #[allow(unused_variables)]
-    fn compute_size(&self) -> u64 {
-        let mut my_size = 0;
-        if let Some(v) = self.android_device_id {
-            my_size += 1 + 8;
-        }
-        if let Some(v) = self.gcm_registration_id.as_ref() {
-            my_size += ::protobuf::rt::bytes_size(102, &v);
-        }
-        if let Some(v) = self.apn_registration_id.as_ref() {
-            my_size += ::protobuf::rt::bytes_size(202, &v);
-        }
-        if let Some(v) = self.notification_enabled {
-            my_size += 2 + 1;
-        }
-        if let Some(v) = self.bluetooth_mac_address.as_ref() {
-            my_size += ::protobuf::rt::string_size(302, &v);
-        }
-        if let Some(v) = self.device_master_key_hash.as_ref() {
-            my_size += ::protobuf::rt::bytes_size(103, &v);
-        }
-        if let Some(v) = self.user_public_key.as_ref() {
-            my_size += ::protobuf::rt::bytes_size(4, &v);
-        }
-        if let Some(v) = self.device_model.as_ref() {
-            my_size += ::protobuf::rt::string_size(7, &v);
-        }
-        if let Some(v) = self.locale.as_ref() {
-            my_size += ::protobuf::rt::string_size(8, &v);
-        }
-        if let Some(v) = self.key_handle.as_ref() {
-            my_size += ::protobuf::rt::bytes_size(9, &v);
-        }
-        if let Some(v) = self.counter {
-            my_size += ::protobuf::rt::int64_size(12, v);
-        }
-        if let Some(v) = self.device_os_version.as_ref() {
-            my_size += ::protobuf::rt::string_size(13, &v);
-        }
-        if let Some(v) = self.device_os_version_code {
-            my_size += ::protobuf::rt::int64_size(14, v);
-        }
-        if let Some(v) = self.device_os_release.as_ref() {
-            my_size += ::protobuf::rt::string_size(15, &v);
-        }
-        if let Some(v) = self.device_os_codename.as_ref() {
-            my_size += ::protobuf::rt::string_size(16, &v);
-        }
-        if let Some(v) = self.device_software_version.as_ref() {
-            my_size += ::protobuf::rt::string_size(17, &v);
-        }
-        if let Some(v) = self.device_software_version_code {
-            my_size += ::protobuf::rt::int64_size(18, v);
-        }
-        if let Some(v) = self.device_software_package.as_ref() {
-            my_size += ::protobuf::rt::string_size(19, &v);
-        }
-        if let Some(v) = self.device_display_diagonal_mils {
-            my_size += ::protobuf::rt::int32_size(22, v);
-        }
-        if let Some(v) = self.device_authzen_version {
-            my_size += ::protobuf::rt::int32_size(24, v);
-        }
-        if let Some(v) = self.long_device_id.as_ref() {
-            my_size += ::protobuf::rt::bytes_size(29, &v);
-        }
-        if let Some(v) = self.device_manufacturer.as_ref() {
-            my_size += ::protobuf::rt::string_size(31, &v);
-        }
-        if let Some(v) = self.device_type {
-            my_size += ::protobuf::rt::int32_size(32, v.value());
-        }
-        if let Some(v) = self.using_secure_screenlock {
-            my_size += 2 + 1;
-        }
-        if let Some(v) = self.auto_unlock_screenlock_supported {
-            my_size += 2 + 1;
-        }
-        if let Some(v) = self.auto_unlock_screenlock_enabled {
-            my_size += 2 + 1;
-        }
-        if let Some(v) = self.bluetooth_radio_supported {
-            my_size += 2 + 1;
-        }
-        if let Some(v) = self.bluetooth_radio_enabled {
-            my_size += 2 + 1;
-        }
-        if let Some(v) = self.mobile_data_supported {
-            my_size += 2 + 1;
-        }
-        if let Some(v) = self.tethering_supported {
-            my_size += 2 + 1;
-        }
-        if let Some(v) = self.ble_radio_supported {
-            my_size += 2 + 1;
-        }
-        if let Some(v) = self.pixel_experience {
-            my_size += 2 + 1;
-        }
-        if let Some(v) = self.arc_plus_plus {
-            my_size += 2 + 1;
-        }
-        if let Some(v) = self.is_screenlock_state_flaky {
-            my_size += 2 + 1;
-        }
-        for value in &self.supported_software_features {
-            my_size += ::protobuf::rt::int32_size(411, value.value());
-        };
-        for value in &self.enabled_software_features {
-            my_size += ::protobuf::rt::int32_size(412, value.value());
-        };
-        if let Some(v) = self.enrollment_session_id.as_ref() {
-            my_size += ::protobuf::rt::bytes_size(1000, &v);
-        }
-        if let Some(v) = self.oauth_token.as_ref() {
-            my_size += ::protobuf::rt::string_size(1001, &v);
-        }
-        my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
-        self.special_fields.cached_size().set(my_size as u32);
-        my_size
-    }
-
-    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
-        if let Some(v) = self.android_device_id {
-            os.write_fixed64(1, v)?;
-        }
-        if let Some(v) = self.gcm_registration_id.as_ref() {
-            os.write_bytes(102, v)?;
-        }
-        if let Some(v) = self.apn_registration_id.as_ref() {
-            os.write_bytes(202, v)?;
-        }
-        if let Some(v) = self.notification_enabled {
-            os.write_bool(203, v)?;
-        }
-        if let Some(v) = self.bluetooth_mac_address.as_ref() {
-            os.write_string(302, v)?;
-        }
-        if let Some(v) = self.device_master_key_hash.as_ref() {
-            os.write_bytes(103, v)?;
-        }
-        if let Some(v) = self.user_public_key.as_ref() {
-            os.write_bytes(4, v)?;
-        }
-        if let Some(v) = self.device_model.as_ref() {
-            os.write_string(7, v)?;
-        }
-        if let Some(v) = self.locale.as_ref() {
-            os.write_string(8, v)?;
-        }
-        if let Some(v) = self.key_handle.as_ref() {
-            os.write_bytes(9, v)?;
-        }
-        if let Some(v) = self.counter {
-            os.write_int64(12, v)?;
-        }
-        if let Some(v) = self.device_os_version.as_ref() {
-            os.write_string(13, v)?;
-        }
-        if let Some(v) = self.device_os_version_code {
-            os.write_int64(14, v)?;
-        }
-        if let Some(v) = self.device_os_release.as_ref() {
-            os.write_string(15, v)?;
-        }
-        if let Some(v) = self.device_os_codename.as_ref() {
-            os.write_string(16, v)?;
-        }
-        if let Some(v) = self.device_software_version.as_ref() {
-            os.write_string(17, v)?;
-        }
-        if let Some(v) = self.device_software_version_code {
-            os.write_int64(18, v)?;
-        }
-        if let Some(v) = self.device_software_package.as_ref() {
-            os.write_string(19, v)?;
-        }
-        if let Some(v) = self.device_display_diagonal_mils {
-            os.write_int32(22, v)?;
-        }
-        if let Some(v) = self.device_authzen_version {
-            os.write_int32(24, v)?;
-        }
-        if let Some(v) = self.long_device_id.as_ref() {
-            os.write_bytes(29, v)?;
-        }
-        if let Some(v) = self.device_manufacturer.as_ref() {
-            os.write_string(31, v)?;
-        }
-        if let Some(v) = self.device_type {
-            os.write_enum(32, ::protobuf::EnumOrUnknown::value(&v))?;
-        }
-        if let Some(v) = self.using_secure_screenlock {
-            os.write_bool(400, v)?;
-        }
-        if let Some(v) = self.auto_unlock_screenlock_supported {
-            os.write_bool(401, v)?;
-        }
-        if let Some(v) = self.auto_unlock_screenlock_enabled {
-            os.write_bool(402, v)?;
-        }
-        if let Some(v) = self.bluetooth_radio_supported {
-            os.write_bool(403, v)?;
-        }
-        if let Some(v) = self.bluetooth_radio_enabled {
-            os.write_bool(404, v)?;
-        }
-        if let Some(v) = self.mobile_data_supported {
-            os.write_bool(405, v)?;
-        }
-        if let Some(v) = self.tethering_supported {
-            os.write_bool(406, v)?;
-        }
-        if let Some(v) = self.ble_radio_supported {
-            os.write_bool(407, v)?;
-        }
-        if let Some(v) = self.pixel_experience {
-            os.write_bool(408, v)?;
-        }
-        if let Some(v) = self.arc_plus_plus {
-            os.write_bool(409, v)?;
-        }
-        if let Some(v) = self.is_screenlock_state_flaky {
-            os.write_bool(410, v)?;
-        }
-        for v in &self.supported_software_features {
-            os.write_enum(411, ::protobuf::EnumOrUnknown::value(v))?;
-        };
-        for v in &self.enabled_software_features {
-            os.write_enum(412, ::protobuf::EnumOrUnknown::value(v))?;
-        };
-        if let Some(v) = self.enrollment_session_id.as_ref() {
-            os.write_bytes(1000, v)?;
-        }
-        if let Some(v) = self.oauth_token.as_ref() {
-            os.write_string(1001, v)?;
-        }
-        os.write_unknown_fields(self.special_fields.unknown_fields())?;
-        ::std::result::Result::Ok(())
-    }
-
-    fn special_fields(&self) -> &::protobuf::SpecialFields {
-        &self.special_fields
-    }
-
-    fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
-        &mut self.special_fields
-    }
-
-    fn new() -> GcmDeviceInfo {
-        GcmDeviceInfo::new()
-    }
-
-    fn clear(&mut self) {
-        self.android_device_id = ::std::option::Option::None;
-        self.gcm_registration_id = ::std::option::Option::None;
-        self.apn_registration_id = ::std::option::Option::None;
-        self.notification_enabled = ::std::option::Option::None;
-        self.bluetooth_mac_address = ::std::option::Option::None;
-        self.device_master_key_hash = ::std::option::Option::None;
-        self.user_public_key = ::std::option::Option::None;
-        self.device_model = ::std::option::Option::None;
-        self.locale = ::std::option::Option::None;
-        self.key_handle = ::std::option::Option::None;
-        self.counter = ::std::option::Option::None;
-        self.device_os_version = ::std::option::Option::None;
-        self.device_os_version_code = ::std::option::Option::None;
-        self.device_os_release = ::std::option::Option::None;
-        self.device_os_codename = ::std::option::Option::None;
-        self.device_software_version = ::std::option::Option::None;
-        self.device_software_version_code = ::std::option::Option::None;
-        self.device_software_package = ::std::option::Option::None;
-        self.device_display_diagonal_mils = ::std::option::Option::None;
-        self.device_authzen_version = ::std::option::Option::None;
-        self.long_device_id = ::std::option::Option::None;
-        self.device_manufacturer = ::std::option::Option::None;
-        self.device_type = ::std::option::Option::None;
-        self.using_secure_screenlock = ::std::option::Option::None;
-        self.auto_unlock_screenlock_supported = ::std::option::Option::None;
-        self.auto_unlock_screenlock_enabled = ::std::option::Option::None;
-        self.bluetooth_radio_supported = ::std::option::Option::None;
-        self.bluetooth_radio_enabled = ::std::option::Option::None;
-        self.mobile_data_supported = ::std::option::Option::None;
-        self.tethering_supported = ::std::option::Option::None;
-        self.ble_radio_supported = ::std::option::Option::None;
-        self.pixel_experience = ::std::option::Option::None;
-        self.arc_plus_plus = ::std::option::Option::None;
-        self.is_screenlock_state_flaky = ::std::option::Option::None;
-        self.supported_software_features.clear();
-        self.enabled_software_features.clear();
-        self.enrollment_session_id = ::std::option::Option::None;
-        self.oauth_token = ::std::option::Option::None;
-        self.special_fields.clear();
-    }
-
-    fn default_instance() -> &'static GcmDeviceInfo {
-        static instance: GcmDeviceInfo = GcmDeviceInfo {
-            android_device_id: ::std::option::Option::None,
-            gcm_registration_id: ::std::option::Option::None,
-            apn_registration_id: ::std::option::Option::None,
-            notification_enabled: ::std::option::Option::None,
-            bluetooth_mac_address: ::std::option::Option::None,
-            device_master_key_hash: ::std::option::Option::None,
-            user_public_key: ::std::option::Option::None,
-            device_model: ::std::option::Option::None,
-            locale: ::std::option::Option::None,
-            key_handle: ::std::option::Option::None,
-            counter: ::std::option::Option::None,
-            device_os_version: ::std::option::Option::None,
-            device_os_version_code: ::std::option::Option::None,
-            device_os_release: ::std::option::Option::None,
-            device_os_codename: ::std::option::Option::None,
-            device_software_version: ::std::option::Option::None,
-            device_software_version_code: ::std::option::Option::None,
-            device_software_package: ::std::option::Option::None,
-            device_display_diagonal_mils: ::std::option::Option::None,
-            device_authzen_version: ::std::option::Option::None,
-            long_device_id: ::std::option::Option::None,
-            device_manufacturer: ::std::option::Option::None,
-            device_type: ::std::option::Option::None,
-            using_secure_screenlock: ::std::option::Option::None,
-            auto_unlock_screenlock_supported: ::std::option::Option::None,
-            auto_unlock_screenlock_enabled: ::std::option::Option::None,
-            bluetooth_radio_supported: ::std::option::Option::None,
-            bluetooth_radio_enabled: ::std::option::Option::None,
-            mobile_data_supported: ::std::option::Option::None,
-            tethering_supported: ::std::option::Option::None,
-            ble_radio_supported: ::std::option::Option::None,
-            pixel_experience: ::std::option::Option::None,
-            arc_plus_plus: ::std::option::Option::None,
-            is_screenlock_state_flaky: ::std::option::Option::None,
-            supported_software_features: ::std::vec::Vec::new(),
-            enabled_software_features: ::std::vec::Vec::new(),
-            enrollment_session_id: ::std::option::Option::None,
-            oauth_token: ::std::option::Option::None,
-            special_fields: ::protobuf::SpecialFields::new(),
-        };
-        &instance
-    }
-}
-
-#[derive(PartialEq,Clone,Default,Debug)]
 // @@protoc_insertion_point(message:securegcm.GcmMetadata)
 pub struct GcmMetadata {
     // message fields
@@ -1743,669 +187,6 @@
     }
 }
 
-#[derive(PartialEq,Clone,Default,Debug)]
-// @@protoc_insertion_point(message:securegcm.Tickle)
-pub struct Tickle {
-    // message fields
-    // @@protoc_insertion_point(field:securegcm.Tickle.expiry_time)
-    pub expiry_time: ::std::option::Option<u64>,
-    // special fields
-    // @@protoc_insertion_point(special_field:securegcm.Tickle.special_fields)
-    pub special_fields: ::protobuf::SpecialFields,
-}
-
-impl<'a> ::std::default::Default for &'a Tickle {
-    fn default() -> &'a Tickle {
-        <Tickle as ::protobuf::Message>::default_instance()
-    }
-}
-
-impl Tickle {
-    pub fn new() -> Tickle {
-        ::std::default::Default::default()
-    }
-
-    // optional fixed64 expiry_time = 1;
-
-    pub fn expiry_time(&self) -> u64 {
-        self.expiry_time.unwrap_or(0)
-    }
-
-    pub fn clear_expiry_time(&mut self) {
-        self.expiry_time = ::std::option::Option::None;
-    }
-
-    pub fn has_expiry_time(&self) -> bool {
-        self.expiry_time.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_expiry_time(&mut self, v: u64) {
-        self.expiry_time = ::std::option::Option::Some(v);
-    }
-}
-
-impl ::protobuf::Message for Tickle {
-    const NAME: &'static str = "Tickle";
-
-    fn is_initialized(&self) -> bool {
-        true
-    }
-
-    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
-        while let Some(tag) = is.read_raw_tag_or_eof()? {
-            match tag {
-                9 => {
-                    self.expiry_time = ::std::option::Option::Some(is.read_fixed64()?);
-                },
-                tag => {
-                    ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
-                },
-            };
-        }
-        ::std::result::Result::Ok(())
-    }
-
-    // Compute sizes of nested messages
-    #[allow(unused_variables)]
-    fn compute_size(&self) -> u64 {
-        let mut my_size = 0;
-        if let Some(v) = self.expiry_time {
-            my_size += 1 + 8;
-        }
-        my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
-        self.special_fields.cached_size().set(my_size as u32);
-        my_size
-    }
-
-    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
-        if let Some(v) = self.expiry_time {
-            os.write_fixed64(1, v)?;
-        }
-        os.write_unknown_fields(self.special_fields.unknown_fields())?;
-        ::std::result::Result::Ok(())
-    }
-
-    fn special_fields(&self) -> &::protobuf::SpecialFields {
-        &self.special_fields
-    }
-
-    fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
-        &mut self.special_fields
-    }
-
-    fn new() -> Tickle {
-        Tickle::new()
-    }
-
-    fn clear(&mut self) {
-        self.expiry_time = ::std::option::Option::None;
-        self.special_fields.clear();
-    }
-
-    fn default_instance() -> &'static Tickle {
-        static instance: Tickle = Tickle {
-            expiry_time: ::std::option::Option::None,
-            special_fields: ::protobuf::SpecialFields::new(),
-        };
-        &instance
-    }
-}
-
-#[derive(PartialEq,Clone,Default,Debug)]
-// @@protoc_insertion_point(message:securegcm.LoginNotificationInfo)
-pub struct LoginNotificationInfo {
-    // message fields
-    // @@protoc_insertion_point(field:securegcm.LoginNotificationInfo.creation_time)
-    pub creation_time: ::std::option::Option<u64>,
-    // @@protoc_insertion_point(field:securegcm.LoginNotificationInfo.email)
-    pub email: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.LoginNotificationInfo.host)
-    pub host: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.LoginNotificationInfo.source)
-    pub source: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.LoginNotificationInfo.event_type)
-    pub event_type: ::std::option::Option<::std::string::String>,
-    // special fields
-    // @@protoc_insertion_point(special_field:securegcm.LoginNotificationInfo.special_fields)
-    pub special_fields: ::protobuf::SpecialFields,
-}
-
-impl<'a> ::std::default::Default for &'a LoginNotificationInfo {
-    fn default() -> &'a LoginNotificationInfo {
-        <LoginNotificationInfo as ::protobuf::Message>::default_instance()
-    }
-}
-
-impl LoginNotificationInfo {
-    pub fn new() -> LoginNotificationInfo {
-        ::std::default::Default::default()
-    }
-
-    // optional fixed64 creation_time = 2;
-
-    pub fn creation_time(&self) -> u64 {
-        self.creation_time.unwrap_or(0)
-    }
-
-    pub fn clear_creation_time(&mut self) {
-        self.creation_time = ::std::option::Option::None;
-    }
-
-    pub fn has_creation_time(&self) -> bool {
-        self.creation_time.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_creation_time(&mut self, v: u64) {
-        self.creation_time = ::std::option::Option::Some(v);
-    }
-
-    // optional string email = 3;
-
-    pub fn email(&self) -> &str {
-        match self.email.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_email(&mut self) {
-        self.email = ::std::option::Option::None;
-    }
-
-    pub fn has_email(&self) -> bool {
-        self.email.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_email(&mut self, v: ::std::string::String) {
-        self.email = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_email(&mut self) -> &mut ::std::string::String {
-        if self.email.is_none() {
-            self.email = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.email.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_email(&mut self) -> ::std::string::String {
-        self.email.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-
-    // optional string host = 4;
-
-    pub fn host(&self) -> &str {
-        match self.host.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_host(&mut self) {
-        self.host = ::std::option::Option::None;
-    }
-
-    pub fn has_host(&self) -> bool {
-        self.host.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_host(&mut self, v: ::std::string::String) {
-        self.host = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_host(&mut self) -> &mut ::std::string::String {
-        if self.host.is_none() {
-            self.host = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.host.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_host(&mut self) -> ::std::string::String {
-        self.host.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-
-    // optional string source = 5;
-
-    pub fn source(&self) -> &str {
-        match self.source.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_source(&mut self) {
-        self.source = ::std::option::Option::None;
-    }
-
-    pub fn has_source(&self) -> bool {
-        self.source.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_source(&mut self, v: ::std::string::String) {
-        self.source = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_source(&mut self) -> &mut ::std::string::String {
-        if self.source.is_none() {
-            self.source = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.source.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_source(&mut self) -> ::std::string::String {
-        self.source.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-
-    // optional string event_type = 6;
-
-    pub fn event_type(&self) -> &str {
-        match self.event_type.as_ref() {
-            Some(v) => v,
-            None => "",
-        }
-    }
-
-    pub fn clear_event_type(&mut self) {
-        self.event_type = ::std::option::Option::None;
-    }
-
-    pub fn has_event_type(&self) -> bool {
-        self.event_type.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_event_type(&mut self, v: ::std::string::String) {
-        self.event_type = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_event_type(&mut self) -> &mut ::std::string::String {
-        if self.event_type.is_none() {
-            self.event_type = ::std::option::Option::Some(::std::string::String::new());
-        }
-        self.event_type.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_event_type(&mut self) -> ::std::string::String {
-        self.event_type.take().unwrap_or_else(|| ::std::string::String::new())
-    }
-}
-
-impl ::protobuf::Message for LoginNotificationInfo {
-    const NAME: &'static str = "LoginNotificationInfo";
-
-    fn is_initialized(&self) -> bool {
-        true
-    }
-
-    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
-        while let Some(tag) = is.read_raw_tag_or_eof()? {
-            match tag {
-                17 => {
-                    self.creation_time = ::std::option::Option::Some(is.read_fixed64()?);
-                },
-                26 => {
-                    self.email = ::std::option::Option::Some(is.read_string()?);
-                },
-                34 => {
-                    self.host = ::std::option::Option::Some(is.read_string()?);
-                },
-                42 => {
-                    self.source = ::std::option::Option::Some(is.read_string()?);
-                },
-                50 => {
-                    self.event_type = ::std::option::Option::Some(is.read_string()?);
-                },
-                tag => {
-                    ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
-                },
-            };
-        }
-        ::std::result::Result::Ok(())
-    }
-
-    // Compute sizes of nested messages
-    #[allow(unused_variables)]
-    fn compute_size(&self) -> u64 {
-        let mut my_size = 0;
-        if let Some(v) = self.creation_time {
-            my_size += 1 + 8;
-        }
-        if let Some(v) = self.email.as_ref() {
-            my_size += ::protobuf::rt::string_size(3, &v);
-        }
-        if let Some(v) = self.host.as_ref() {
-            my_size += ::protobuf::rt::string_size(4, &v);
-        }
-        if let Some(v) = self.source.as_ref() {
-            my_size += ::protobuf::rt::string_size(5, &v);
-        }
-        if let Some(v) = self.event_type.as_ref() {
-            my_size += ::protobuf::rt::string_size(6, &v);
-        }
-        my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
-        self.special_fields.cached_size().set(my_size as u32);
-        my_size
-    }
-
-    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
-        if let Some(v) = self.creation_time {
-            os.write_fixed64(2, v)?;
-        }
-        if let Some(v) = self.email.as_ref() {
-            os.write_string(3, v)?;
-        }
-        if let Some(v) = self.host.as_ref() {
-            os.write_string(4, v)?;
-        }
-        if let Some(v) = self.source.as_ref() {
-            os.write_string(5, v)?;
-        }
-        if let Some(v) = self.event_type.as_ref() {
-            os.write_string(6, v)?;
-        }
-        os.write_unknown_fields(self.special_fields.unknown_fields())?;
-        ::std::result::Result::Ok(())
-    }
-
-    fn special_fields(&self) -> &::protobuf::SpecialFields {
-        &self.special_fields
-    }
-
-    fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
-        &mut self.special_fields
-    }
-
-    fn new() -> LoginNotificationInfo {
-        LoginNotificationInfo::new()
-    }
-
-    fn clear(&mut self) {
-        self.creation_time = ::std::option::Option::None;
-        self.email = ::std::option::Option::None;
-        self.host = ::std::option::Option::None;
-        self.source = ::std::option::Option::None;
-        self.event_type = ::std::option::Option::None;
-        self.special_fields.clear();
-    }
-
-    fn default_instance() -> &'static LoginNotificationInfo {
-        static instance: LoginNotificationInfo = LoginNotificationInfo {
-            creation_time: ::std::option::Option::None,
-            email: ::std::option::Option::None,
-            host: ::std::option::Option::None,
-            source: ::std::option::Option::None,
-            event_type: ::std::option::Option::None,
-            special_fields: ::protobuf::SpecialFields::new(),
-        };
-        &instance
-    }
-}
-
-#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
-// @@protoc_insertion_point(enum:securegcm.AppleDeviceDiagonalMils)
-pub enum AppleDeviceDiagonalMils {
-    // @@protoc_insertion_point(enum_value:securegcm.AppleDeviceDiagonalMils.APPLE_PHONE)
-    APPLE_PHONE = 4000,
-    // @@protoc_insertion_point(enum_value:securegcm.AppleDeviceDiagonalMils.APPLE_PAD)
-    APPLE_PAD = 7900,
-}
-
-impl ::protobuf::Enum for AppleDeviceDiagonalMils {
-    const NAME: &'static str = "AppleDeviceDiagonalMils";
-
-    fn value(&self) -> i32 {
-        *self as i32
-    }
-
-    fn from_i32(value: i32) -> ::std::option::Option<AppleDeviceDiagonalMils> {
-        match value {
-            4000 => ::std::option::Option::Some(AppleDeviceDiagonalMils::APPLE_PHONE),
-            7900 => ::std::option::Option::Some(AppleDeviceDiagonalMils::APPLE_PAD),
-            _ => ::std::option::Option::None
-        }
-    }
-
-    const VALUES: &'static [AppleDeviceDiagonalMils] = &[
-        AppleDeviceDiagonalMils::APPLE_PHONE,
-        AppleDeviceDiagonalMils::APPLE_PAD,
-    ];
-}
-
-// Note, `Default` is implemented although default value is not 0
-impl ::std::default::Default for AppleDeviceDiagonalMils {
-    fn default() -> Self {
-        AppleDeviceDiagonalMils::APPLE_PHONE
-    }
-}
-
-
-#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
-// @@protoc_insertion_point(enum:securegcm.DeviceType)
-pub enum DeviceType {
-    // @@protoc_insertion_point(enum_value:securegcm.DeviceType.UNKNOWN)
-    UNKNOWN = 0,
-    // @@protoc_insertion_point(enum_value:securegcm.DeviceType.ANDROID)
-    ANDROID = 1,
-    // @@protoc_insertion_point(enum_value:securegcm.DeviceType.CHROME)
-    CHROME = 2,
-    // @@protoc_insertion_point(enum_value:securegcm.DeviceType.IOS)
-    IOS = 3,
-    // @@protoc_insertion_point(enum_value:securegcm.DeviceType.BROWSER)
-    BROWSER = 4,
-    // @@protoc_insertion_point(enum_value:securegcm.DeviceType.OSX)
-    OSX = 5,
-}
-
-impl ::protobuf::Enum for DeviceType {
-    const NAME: &'static str = "DeviceType";
-
-    fn value(&self) -> i32 {
-        *self as i32
-    }
-
-    fn from_i32(value: i32) -> ::std::option::Option<DeviceType> {
-        match value {
-            0 => ::std::option::Option::Some(DeviceType::UNKNOWN),
-            1 => ::std::option::Option::Some(DeviceType::ANDROID),
-            2 => ::std::option::Option::Some(DeviceType::CHROME),
-            3 => ::std::option::Option::Some(DeviceType::IOS),
-            4 => ::std::option::Option::Some(DeviceType::BROWSER),
-            5 => ::std::option::Option::Some(DeviceType::OSX),
-            _ => ::std::option::Option::None
-        }
-    }
-
-    const VALUES: &'static [DeviceType] = &[
-        DeviceType::UNKNOWN,
-        DeviceType::ANDROID,
-        DeviceType::CHROME,
-        DeviceType::IOS,
-        DeviceType::BROWSER,
-        DeviceType::OSX,
-    ];
-}
-
-impl ::std::default::Default for DeviceType {
-    fn default() -> Self {
-        DeviceType::UNKNOWN
-    }
-}
-
-
-#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
-// @@protoc_insertion_point(enum:securegcm.SoftwareFeature)
-pub enum SoftwareFeature {
-    // @@protoc_insertion_point(enum_value:securegcm.SoftwareFeature.UNKNOWN_FEATURE)
-    UNKNOWN_FEATURE = 0,
-    // @@protoc_insertion_point(enum_value:securegcm.SoftwareFeature.BETTER_TOGETHER_HOST)
-    BETTER_TOGETHER_HOST = 1,
-    // @@protoc_insertion_point(enum_value:securegcm.SoftwareFeature.BETTER_TOGETHER_CLIENT)
-    BETTER_TOGETHER_CLIENT = 2,
-    // @@protoc_insertion_point(enum_value:securegcm.SoftwareFeature.EASY_UNLOCK_HOST)
-    EASY_UNLOCK_HOST = 3,
-    // @@protoc_insertion_point(enum_value:securegcm.SoftwareFeature.EASY_UNLOCK_CLIENT)
-    EASY_UNLOCK_CLIENT = 4,
-    // @@protoc_insertion_point(enum_value:securegcm.SoftwareFeature.MAGIC_TETHER_HOST)
-    MAGIC_TETHER_HOST = 5,
-    // @@protoc_insertion_point(enum_value:securegcm.SoftwareFeature.MAGIC_TETHER_CLIENT)
-    MAGIC_TETHER_CLIENT = 6,
-    // @@protoc_insertion_point(enum_value:securegcm.SoftwareFeature.SMS_CONNECT_HOST)
-    SMS_CONNECT_HOST = 7,
-    // @@protoc_insertion_point(enum_value:securegcm.SoftwareFeature.SMS_CONNECT_CLIENT)
-    SMS_CONNECT_CLIENT = 8,
-}
-
-impl ::protobuf::Enum for SoftwareFeature {
-    const NAME: &'static str = "SoftwareFeature";
-
-    fn value(&self) -> i32 {
-        *self as i32
-    }
-
-    fn from_i32(value: i32) -> ::std::option::Option<SoftwareFeature> {
-        match value {
-            0 => ::std::option::Option::Some(SoftwareFeature::UNKNOWN_FEATURE),
-            1 => ::std::option::Option::Some(SoftwareFeature::BETTER_TOGETHER_HOST),
-            2 => ::std::option::Option::Some(SoftwareFeature::BETTER_TOGETHER_CLIENT),
-            3 => ::std::option::Option::Some(SoftwareFeature::EASY_UNLOCK_HOST),
-            4 => ::std::option::Option::Some(SoftwareFeature::EASY_UNLOCK_CLIENT),
-            5 => ::std::option::Option::Some(SoftwareFeature::MAGIC_TETHER_HOST),
-            6 => ::std::option::Option::Some(SoftwareFeature::MAGIC_TETHER_CLIENT),
-            7 => ::std::option::Option::Some(SoftwareFeature::SMS_CONNECT_HOST),
-            8 => ::std::option::Option::Some(SoftwareFeature::SMS_CONNECT_CLIENT),
-            _ => ::std::option::Option::None
-        }
-    }
-
-    const VALUES: &'static [SoftwareFeature] = &[
-        SoftwareFeature::UNKNOWN_FEATURE,
-        SoftwareFeature::BETTER_TOGETHER_HOST,
-        SoftwareFeature::BETTER_TOGETHER_CLIENT,
-        SoftwareFeature::EASY_UNLOCK_HOST,
-        SoftwareFeature::EASY_UNLOCK_CLIENT,
-        SoftwareFeature::MAGIC_TETHER_HOST,
-        SoftwareFeature::MAGIC_TETHER_CLIENT,
-        SoftwareFeature::SMS_CONNECT_HOST,
-        SoftwareFeature::SMS_CONNECT_CLIENT,
-    ];
-}
-
-impl ::std::default::Default for SoftwareFeature {
-    fn default() -> Self {
-        SoftwareFeature::UNKNOWN_FEATURE
-    }
-}
-
-
-#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
-// @@protoc_insertion_point(enum:securegcm.InvocationReason)
-pub enum InvocationReason {
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_UNKNOWN)
-    REASON_UNKNOWN = 0,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_INITIALIZATION)
-    REASON_INITIALIZATION = 1,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_PERIODIC)
-    REASON_PERIODIC = 2,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_SLOW_PERIODIC)
-    REASON_SLOW_PERIODIC = 3,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_FAST_PERIODIC)
-    REASON_FAST_PERIODIC = 4,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_EXPIRATION)
-    REASON_EXPIRATION = 5,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_FAILURE_RECOVERY)
-    REASON_FAILURE_RECOVERY = 6,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_NEW_ACCOUNT)
-    REASON_NEW_ACCOUNT = 7,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_CHANGED_ACCOUNT)
-    REASON_CHANGED_ACCOUNT = 8,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_FEATURE_TOGGLED)
-    REASON_FEATURE_TOGGLED = 9,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_SERVER_INITIATED)
-    REASON_SERVER_INITIATED = 10,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_ADDRESS_CHANGE)
-    REASON_ADDRESS_CHANGE = 11,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_SOFTWARE_UPDATE)
-    REASON_SOFTWARE_UPDATE = 12,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_MANUAL)
-    REASON_MANUAL = 13,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_CUSTOM_KEY_INVALIDATION)
-    REASON_CUSTOM_KEY_INVALIDATION = 14,
-    // @@protoc_insertion_point(enum_value:securegcm.InvocationReason.REASON_PROXIMITY_PERIODIC)
-    REASON_PROXIMITY_PERIODIC = 15,
-}
-
-impl ::protobuf::Enum for InvocationReason {
-    const NAME: &'static str = "InvocationReason";
-
-    fn value(&self) -> i32 {
-        *self as i32
-    }
-
-    fn from_i32(value: i32) -> ::std::option::Option<InvocationReason> {
-        match value {
-            0 => ::std::option::Option::Some(InvocationReason::REASON_UNKNOWN),
-            1 => ::std::option::Option::Some(InvocationReason::REASON_INITIALIZATION),
-            2 => ::std::option::Option::Some(InvocationReason::REASON_PERIODIC),
-            3 => ::std::option::Option::Some(InvocationReason::REASON_SLOW_PERIODIC),
-            4 => ::std::option::Option::Some(InvocationReason::REASON_FAST_PERIODIC),
-            5 => ::std::option::Option::Some(InvocationReason::REASON_EXPIRATION),
-            6 => ::std::option::Option::Some(InvocationReason::REASON_FAILURE_RECOVERY),
-            7 => ::std::option::Option::Some(InvocationReason::REASON_NEW_ACCOUNT),
-            8 => ::std::option::Option::Some(InvocationReason::REASON_CHANGED_ACCOUNT),
-            9 => ::std::option::Option::Some(InvocationReason::REASON_FEATURE_TOGGLED),
-            10 => ::std::option::Option::Some(InvocationReason::REASON_SERVER_INITIATED),
-            11 => ::std::option::Option::Some(InvocationReason::REASON_ADDRESS_CHANGE),
-            12 => ::std::option::Option::Some(InvocationReason::REASON_SOFTWARE_UPDATE),
-            13 => ::std::option::Option::Some(InvocationReason::REASON_MANUAL),
-            14 => ::std::option::Option::Some(InvocationReason::REASON_CUSTOM_KEY_INVALIDATION),
-            15 => ::std::option::Option::Some(InvocationReason::REASON_PROXIMITY_PERIODIC),
-            _ => ::std::option::Option::None
-        }
-    }
-
-    const VALUES: &'static [InvocationReason] = &[
-        InvocationReason::REASON_UNKNOWN,
-        InvocationReason::REASON_INITIALIZATION,
-        InvocationReason::REASON_PERIODIC,
-        InvocationReason::REASON_SLOW_PERIODIC,
-        InvocationReason::REASON_FAST_PERIODIC,
-        InvocationReason::REASON_EXPIRATION,
-        InvocationReason::REASON_FAILURE_RECOVERY,
-        InvocationReason::REASON_NEW_ACCOUNT,
-        InvocationReason::REASON_CHANGED_ACCOUNT,
-        InvocationReason::REASON_FEATURE_TOGGLED,
-        InvocationReason::REASON_SERVER_INITIATED,
-        InvocationReason::REASON_ADDRESS_CHANGE,
-        InvocationReason::REASON_SOFTWARE_UPDATE,
-        InvocationReason::REASON_MANUAL,
-        InvocationReason::REASON_CUSTOM_KEY_INVALIDATION,
-        InvocationReason::REASON_PROXIMITY_PERIODIC,
-    ];
-}
-
-impl ::std::default::Default for InvocationReason {
-    fn default() -> Self {
-        InvocationReason::REASON_UNKNOWN
-    }
-}
-
-
 #[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
 // @@protoc_insertion_point(enum:securegcm.Type)
 pub enum Type {
diff --git a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs
index d11c330..cafd900 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.21.12
+// .proto file is parsed by protoc 3.19.1
 // @generated
 
 // https://github.com/rust-lang/rust-clippy/issues/702
@@ -803,187 +803,6 @@
 }
 
 #[derive(PartialEq,Clone,Default,Debug)]
-// @@protoc_insertion_point(message:securemessage.HeaderAndBodyInternal)
-pub struct HeaderAndBodyInternal {
-    // message fields
-    // @@protoc_insertion_point(field:securemessage.HeaderAndBodyInternal.header)
-    pub header: ::std::option::Option<::std::vec::Vec<u8>>,
-    // @@protoc_insertion_point(field:securemessage.HeaderAndBodyInternal.body)
-    pub body: ::std::option::Option<::std::vec::Vec<u8>>,
-    // special fields
-    // @@protoc_insertion_point(special_field:securemessage.HeaderAndBodyInternal.special_fields)
-    pub special_fields: ::protobuf::SpecialFields,
-}
-
-impl<'a> ::std::default::Default for &'a HeaderAndBodyInternal {
-    fn default() -> &'a HeaderAndBodyInternal {
-        <HeaderAndBodyInternal as ::protobuf::Message>::default_instance()
-    }
-}
-
-impl HeaderAndBodyInternal {
-    pub fn new() -> HeaderAndBodyInternal {
-        ::std::default::Default::default()
-    }
-
-    // required bytes header = 1;
-
-    pub fn header(&self) -> &[u8] {
-        match self.header.as_ref() {
-            Some(v) => v,
-            None => &[],
-        }
-    }
-
-    pub fn clear_header(&mut self) {
-        self.header = ::std::option::Option::None;
-    }
-
-    pub fn has_header(&self) -> bool {
-        self.header.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_header(&mut self, v: ::std::vec::Vec<u8>) {
-        self.header = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_header(&mut self) -> &mut ::std::vec::Vec<u8> {
-        if self.header.is_none() {
-            self.header = ::std::option::Option::Some(::std::vec::Vec::new());
-        }
-        self.header.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_header(&mut self) -> ::std::vec::Vec<u8> {
-        self.header.take().unwrap_or_else(|| ::std::vec::Vec::new())
-    }
-
-    // required bytes body = 2;
-
-    pub fn body(&self) -> &[u8] {
-        match self.body.as_ref() {
-            Some(v) => v,
-            None => &[],
-        }
-    }
-
-    pub fn clear_body(&mut self) {
-        self.body = ::std::option::Option::None;
-    }
-
-    pub fn has_body(&self) -> bool {
-        self.body.is_some()
-    }
-
-    // Param is passed by value, moved
-    pub fn set_body(&mut self, v: ::std::vec::Vec<u8>) {
-        self.body = ::std::option::Option::Some(v);
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_body(&mut self) -> &mut ::std::vec::Vec<u8> {
-        if self.body.is_none() {
-            self.body = ::std::option::Option::Some(::std::vec::Vec::new());
-        }
-        self.body.as_mut().unwrap()
-    }
-
-    // Take field
-    pub fn take_body(&mut self) -> ::std::vec::Vec<u8> {
-        self.body.take().unwrap_or_else(|| ::std::vec::Vec::new())
-    }
-}
-
-impl ::protobuf::Message for HeaderAndBodyInternal {
-    const NAME: &'static str = "HeaderAndBodyInternal";
-
-    fn is_initialized(&self) -> bool {
-        if self.header.is_none() {
-            return false;
-        }
-        if self.body.is_none() {
-            return false;
-        }
-        true
-    }
-
-    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
-        while let Some(tag) = is.read_raw_tag_or_eof()? {
-            match tag {
-                10 => {
-                    self.header = ::std::option::Option::Some(is.read_bytes()?);
-                },
-                18 => {
-                    self.body = ::std::option::Option::Some(is.read_bytes()?);
-                },
-                tag => {
-                    ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
-                },
-            };
-        }
-        ::std::result::Result::Ok(())
-    }
-
-    // Compute sizes of nested messages
-    #[allow(unused_variables)]
-    fn compute_size(&self) -> u64 {
-        let mut my_size = 0;
-        if let Some(v) = self.header.as_ref() {
-            my_size += ::protobuf::rt::bytes_size(1, &v);
-        }
-        if let Some(v) = self.body.as_ref() {
-            my_size += ::protobuf::rt::bytes_size(2, &v);
-        }
-        my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
-        self.special_fields.cached_size().set(my_size as u32);
-        my_size
-    }
-
-    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
-        if let Some(v) = self.header.as_ref() {
-            os.write_bytes(1, v)?;
-        }
-        if let Some(v) = self.body.as_ref() {
-            os.write_bytes(2, v)?;
-        }
-        os.write_unknown_fields(self.special_fields.unknown_fields())?;
-        ::std::result::Result::Ok(())
-    }
-
-    fn special_fields(&self) -> &::protobuf::SpecialFields {
-        &self.special_fields
-    }
-
-    fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
-        &mut self.special_fields
-    }
-
-    fn new() -> HeaderAndBodyInternal {
-        HeaderAndBodyInternal::new()
-    }
-
-    fn clear(&mut self) {
-        self.header = ::std::option::Option::None;
-        self.body = ::std::option::Option::None;
-        self.special_fields.clear();
-    }
-
-    fn default_instance() -> &'static HeaderAndBodyInternal {
-        static instance: HeaderAndBodyInternal = HeaderAndBodyInternal {
-            header: ::std::option::Option::None,
-            body: ::std::option::Option::None,
-            special_fields: ::protobuf::SpecialFields::new(),
-        };
-        &instance
-    }
-}
-
-#[derive(PartialEq,Clone,Default,Debug)]
 // @@protoc_insertion_point(message:securemessage.EcP256PublicKey)
 pub struct EcP256PublicKey {
     // message fields
diff --git a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs
index 4535937..648f2ac 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.21.12
+// .proto file is parsed by protoc 3.19.1
 // @generated
 
 // https://github.com/rust-lang/rust-clippy/issues/702
@@ -499,8 +499,8 @@
     pub cipher_commitments: ::std::vec::Vec<ukey2client_init::CipherCommitment>,
     // @@protoc_insertion_point(field:securegcm.Ukey2ClientInit.next_protocol)
     pub next_protocol: ::std::option::Option<::std::string::String>,
-    // @@protoc_insertion_point(field:securegcm.Ukey2ClientInit.other_next_protocols)
-    pub other_next_protocols: ::std::vec::Vec<::std::string::String>,
+    // @@protoc_insertion_point(field:securegcm.Ukey2ClientInit.next_protocols)
+    pub next_protocols: ::std::vec::Vec<::std::string::String>,
     // special fields
     // @@protoc_insertion_point(special_field:securegcm.Ukey2ClientInit.special_fields)
     pub special_fields: ::protobuf::SpecialFields,
@@ -632,7 +632,7 @@
                     self.next_protocol = ::std::option::Option::Some(is.read_string()?);
                 },
                 42 => {
-                    self.other_next_protocols.push(is.read_string()?);
+                    self.next_protocols.push(is.read_string()?);
                 },
                 tag => {
                     ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
@@ -659,7 +659,7 @@
         if let Some(v) = self.next_protocol.as_ref() {
             my_size += ::protobuf::rt::string_size(4, &v);
         }
-        for value in &self.other_next_protocols {
+        for value in &self.next_protocols {
             my_size += ::protobuf::rt::string_size(5, &value);
         };
         my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
@@ -680,7 +680,7 @@
         if let Some(v) = self.next_protocol.as_ref() {
             os.write_string(4, v)?;
         }
-        for v in &self.other_next_protocols {
+        for v in &self.next_protocols {
             os.write_string(5, &v)?;
         };
         os.write_unknown_fields(self.special_fields.unknown_fields())?;
@@ -704,7 +704,7 @@
         self.random = ::std::option::Option::None;
         self.cipher_commitments.clear();
         self.next_protocol = ::std::option::Option::None;
-        self.other_next_protocols.clear();
+        self.next_protocols.clear();
         self.special_fields.clear();
     }
 
@@ -714,7 +714,7 @@
             random: ::std::option::Option::None,
             cipher_commitments: ::std::vec::Vec::new(),
             next_protocol: ::std::option::Option::None,
-            other_next_protocols: ::std::vec::Vec::new(),
+            next_protocols: ::std::vec::Vec::new(),
             special_fields: ::protobuf::SpecialFields::new(),
         };
         &instance
diff --git a/nearby/connections/ukey2/ukey2_shell/Cargo.toml b/nearby/connections/ukey2/ukey2_shell/Cargo.toml
index 2ac12fa..c5c84ea 100644
--- a/nearby/connections/ukey2/ukey2_shell/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_shell/Cargo.toml
@@ -9,6 +9,5 @@
 
 [dependencies]
 crypto_provider_rustcrypto = { workspace = true, features = [ "alloc" ] }
-ukey2_rs = { version = "0.1.0", path = "../ukey2" }
 ukey2_connections = { version = "0.1.0", path = "../ukey2_connections" }
 clap = { workspace = true, features = ["std", "derive"] }
diff --git a/nearby/connections/ukey2/ukey2_shell/src/main.rs b/nearby/connections/ukey2/ukey2_shell/src/main.rs
index 3eca7b0..c50ee93 100644
--- a/nearby/connections/ukey2/ukey2_shell/src/main.rs
+++ b/nearby/connections/ukey2/ukey2_shell/src/main.rs
@@ -25,10 +25,9 @@
 
 use crypto_provider_rustcrypto::RustCrypto;
 use ukey2_connections::{
-    D2DConnectionContextV1, D2DHandshakeContext, InitiatorD2DHandshakeContext,
-    ServerD2DHandshakeContext,
+    D2DConnectionContextV1, D2DHandshakeContext, HandshakeImplementation,
+    InitiatorD2DHandshakeContext, NextProtocol, ServerD2DHandshakeContext,
 };
-use ukey2_rs::HandshakeImplementation;
 
 const MODE_INITIATOR: &str = "initiator";
 const MODE_RESPONDER: &str = "responder";
@@ -139,6 +138,7 @@
     fn run_as_initiator(&self) -> bool {
         let mut initiator_ctx = InitiatorD2DHandshakeContext::<RustCrypto, _>::new(
             HandshakeImplementation::PublicKeyInProtobuf,
+            vec![NextProtocol::Aes256CbcHmacSha256, NextProtocol::Aes256GcmSiv],
         );
         write_frame(initiator_ctx.get_next_handshake_message().unwrap());
         let server_init_msg = read_frame();
@@ -166,6 +166,7 @@
     fn run_as_responder(&self) -> bool {
         let mut server_ctx = ServerD2DHandshakeContext::<RustCrypto, _>::new(
             HandshakeImplementation::PublicKeyInProtobuf,
+            &[NextProtocol::Aes256GcmSiv, NextProtocol::Aes256CbcHmacSha256],
         );
         let initiator_init_msg = read_frame();
         server_ctx.handle_handshake_message(initiator_init_msg.as_slice()).unwrap();
diff --git a/nearby/crypto/crypto_provider/Cargo.toml b/nearby/crypto/crypto_provider/Cargo.toml
index a31f5fa..bafdfcc 100644
--- a/nearby/crypto/crypto_provider/Cargo.toml
+++ b/nearby/crypto/crypto_provider/Cargo.toml
@@ -22,7 +22,6 @@
 std = []
 alloc = []
 test_vectors = []
-test_boringssl = ["crypto_provider_default/boringssl"]
 raw_private_key_permit = []
 
 [[bench]]
diff --git a/nearby/crypto/crypto_provider/src/ed25519.rs b/nearby/crypto/crypto_provider/src/ed25519.rs
index 7002335..94e98d0 100644
--- a/nearby/crypto/crypto_provider/src/ed25519.rs
+++ b/nearby/crypto/crypto_provider/src/ed25519.rs
@@ -14,16 +14,7 @@
 
 use core::fmt::Debug;
 
-/// Collection of types used to provide an implementation of ed25519, the Edwards-curve Digital
-/// Signature Algorithm scheme using sha-512 (sha2) and Curve25519
-pub trait Ed25519Provider {
-    /// The keypair which includes both public and secret halves of an asymmetric key.
-    type KeyPair: KeyPair<PublicKey = Self::PublicKey, Signature = Self::Signature>;
-    /// The ed25519 public key, used when verifying a message
-    type PublicKey: PublicKey<Signature = Self::Signature>;
-    /// The ed25519 signature which is the result of signing a message
-    type Signature: Signature;
-}
+// User-facing, crypto-provider independent structs
 
 /// The length of a ed25519 `Signature`, in bytes.
 pub const SIGNATURE_LENGTH: usize = 64;
@@ -74,35 +65,139 @@
 /// Useful for when you want a data-structure to be
 /// crypto-provider independent and contain a private key.
 #[derive(Clone)]
-pub struct PrivateKey {
-    wrapped: RawPrivateKey,
-}
+pub struct PrivateKey(RawPrivateKey);
 
 impl PrivateKey {
     /// Derives the public key corresponding to this private key.
-    pub fn derive_public_key<E: Ed25519Provider>(&self) -> E::PublicKey {
+    pub fn derive_public_key<E: Ed25519Provider>(&self) -> PublicKey {
         let key_pair = E::KeyPair::from_private_key(self);
-        key_pair.public()
+        key_pair.public_key().to_external()
     }
+    /// Sign the given message and return a digital signature
+    pub fn sign<E: Ed25519Provider>(&self, msg: &[u8]) -> Signature {
+        let key_pair = E::KeyPair::from_private_key(self);
+        key_pair.sign(msg).to_external()
+    }
+    /// Generate an ed25519 private key from a CSPRNG
+    /// generate is not available in `no-std`.
+    #[cfg(feature = "std")]
+    pub fn generate<E: Ed25519Provider>() -> Self {
+        let key_pair = E::KeyPair::generate();
+        key_pair.private_key()
+    }
+
     /// Returns the raw bytes of this private key.
     /// This operation is only possible while holding a [`RawPrivateKeyPermit`].
     pub fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawPrivateKey {
-        self.wrapped
+        self.0
     }
     /// Constructs a private key from the raw bytes of the key.
     /// This operation is only possible while holding a [`RawPrivateKeyPermit`].
     pub fn from_raw_private_key(wrapped: RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self {
-        Self { wrapped }
+        PrivateKey(wrapped)
     }
 }
 
+/// error returned when bad bytes are provided to generate keypair
+#[derive(Debug)]
+pub struct InvalidPublicKeyBytes;
+
+/// Error returned if the verification on the signature + message fails
+#[derive(Debug)]
+pub struct SignatureError;
+
+/// A crypto-provider-independent representation of a valid
+/// public key for an ed25519 key-pair in the Edwards Y-format.
+///
+/// Useful for when you want a data-structure to be crypto-provider
+/// independent and contain a public key.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct PublicKey(RawPublicKey);
+
+impl PublicKey {
+    /// Attempts to parse a public key from an array of bytes.
+    /// If the input is not in the Edwards Y-format, this method
+    /// will yield an `InvalidPublicKeyBytes` error.
+    pub fn from_bytes<E: Ed25519Provider>(
+        wrapped: RawPublicKey,
+    ) -> Result<Self, InvalidPublicKeyBytes> {
+        // Simply verify that we can construct the crypto-provider-dependent variant.
+        let _ = <E::PublicKey as PublicKeyImpl>::from_bytes(&wrapped)?;
+        Ok(PublicKey(wrapped))
+    }
+    /// Converts this public-key into the raw bytes of this public-key.
+    pub fn into_bytes(self) -> RawPublicKey {
+        self.0
+    }
+
+    /// Converts this crypto-provider-independent public key to a crypto-provider's internal rep.
+    #[allow(clippy::expect_used)]
+    fn as_internal<E: Ed25519Provider>(&self) -> E::PublicKey {
+        <E::PublicKey as PublicKeyImpl>::from_bytes(&self.0)
+            .expect("Public key bytes validated upon construction.")
+    }
+
+    /// Succeeds if the signature on the given message is verified
+    /// by this public key.
+    pub fn verify_strict<E: Ed25519Provider>(
+        &self,
+        message: &[u8],
+        signature: Signature,
+    ) -> Result<(), SignatureError> {
+        let public_key = self.as_internal::<E>();
+        let signature = signature.as_internal::<E>();
+        public_key.verify_strict(message, &signature)
+    }
+}
+
+/// A crypto-provider-independent representation of an Ed25519 signature.
+/// The underlying representation here can be any arbitrary bytes - verifying
+/// code handles determining whether/not the signature actually corresponds
+/// to a real Ed25519 signature and matches the given public key.
+#[derive(Clone)]
+pub struct Signature(RawSignature);
+
+impl From<RawSignature> for Signature {
+    fn from(wrapped: RawSignature) -> Self {
+        Signature(wrapped)
+    }
+}
+
+impl Signature {
+    /// Constructs a signature from raw bytes.
+    pub fn new(wrapped: RawSignature) -> Self {
+        Signature(wrapped)
+    }
+    /// Transforms this signature back into raw bytes.
+    pub fn to_bytes(self) -> RawSignature {
+        self.0
+    }
+    /// Converts this crypto-provider-independent signature to a crypto-provider's internal rep.
+    fn as_internal<E: Ed25519Provider>(&self) -> E::Signature {
+        <E::Signature as SignatureImpl>::from_bytes(&self.0)
+    }
+}
+
+// Implementor-facing crypto-provider-internal traits.
+
+/// Collection of types used to provide an implementation of ed25519, the Edwards-curve Digital
+/// Signature Algorithm scheme using sha-512 (sha2) and Curve25519
+pub trait Ed25519Provider {
+    /// The internal representation of a keypair which includes both public and secret halves of an asymmetric key.
+    type KeyPair: KeyPairImpl<PublicKey = Self::PublicKey, Signature = Self::Signature>;
+    /// The internal representation of an ed25519 public key, used when verifying a message
+    type PublicKey: PublicKeyImpl<Signature = Self::Signature>;
+    /// The internal representation of an ed25519 signature which is the result of signing a message
+    type Signature: SignatureImpl;
+}
+
 /// The keypair which includes both public and secret halves of an asymmetric key.
-pub trait KeyPair: Sized {
+pub trait KeyPairImpl: Sized {
     /// The ed25519 public key, used when verifying a message
-    type PublicKey: PublicKey;
+    type PublicKey: PublicKeyImpl;
 
     /// The ed25519 signature returned when signing a message
-    type Signature: Signature;
+    type Signature: SignatureImpl;
 
     /// Returns the private key bytes of the `KeyPair`.
     /// This operation is only possible while holding a [`RawPrivateKeyPermit`].
@@ -120,7 +215,7 @@
         // since the way that we're exposing it would require a valid
         // [`RawPrivateKeyPermit`] to extract them again.
         let wrapped = self.raw_private_key(&RawPrivateKeyPermit::new());
-        PrivateKey { wrapped }
+        PrivateKey(wrapped)
     }
 
     /// Builds a key-pair from a [`PrivateKey`], given in an opaque form.
@@ -132,7 +227,7 @@
         // the bytes of the private key, since the way that they
         // were originally expressed would still require a valid
         // [`RawPrivateKeyPermit`] to access them.
-        let raw_private_key = &private_key.wrapped;
+        let raw_private_key = &private_key.0;
         Self::from_raw_private_key(raw_private_key, &RawPrivateKeyPermit::new())
     }
 
@@ -145,24 +240,33 @@
     fn generate() -> Self;
 
     /// getter function for the Public Key of the key pair
-    fn public(&self) -> Self::PublicKey;
+    fn public_key(&self) -> Self::PublicKey;
 }
 
 /// An ed25519 signature
-pub trait Signature: Sized {
-    /// Create a new signature from a byte slice, and return an error on an invalid signature
-    /// An `Ok` result does not guarantee that the Signature is valid, however it will catch a
-    /// number of invalid signatures relatively inexpensively.
+pub trait SignatureImpl: Sized {
+    /// Create a new signature from a fixed size byte array. This represents a container for the
+    /// byte serialization of an Ed25519 signature, and does not necessarily represent well-formed
+    /// field or curve elements.
+    ///
+    /// Signature verification libraries are expected to reject invalid field
+    /// elements at the time a signature is verified (not constructed).
     fn from_bytes(bytes: &RawSignature) -> Self;
 
     /// Returns a slice of the signature bytes
     fn to_bytes(&self) -> RawSignature;
+
+    /// Returns a crypto-provider-independent `Signature` from this implementation-specific struct.
+    fn to_external(&self) -> Signature {
+        let wrapped = self.to_bytes();
+        Signature(wrapped)
+    }
 }
 
 /// An ed25519 public key
-pub trait PublicKey {
+pub trait PublicKeyImpl {
     /// the signature type being used by verify
-    type Signature: Signature;
+    type Signature: SignatureImpl;
 
     /// Builds this public key from an array of bytes in
     /// the format yielded by `to_bytes`.
@@ -173,6 +277,12 @@
     /// Yields the bytes of the public key
     fn to_bytes(&self) -> RawPublicKey;
 
+    /// Returns a crypto-provider-independent `PublicKey` from this implementation-specific struct.
+    fn to_external(&self) -> PublicKey {
+        let wrapped = self.to_bytes();
+        PublicKey(wrapped)
+    }
+
     /// Succeeds if the signature was a valid signature created by this Keypair on the prehashed_message.
     fn verify_strict(
         &self,
@@ -180,11 +290,3 @@
         signature: &Self::Signature,
     ) -> Result<(), SignatureError>;
 }
-
-/// error returned when bad bytes are provided to generate keypair
-#[derive(Debug)]
-pub struct InvalidPublicKeyBytes;
-
-/// Error returned if the verification on the signature + message fails
-#[derive(Debug)]
-pub struct SignatureError;
diff --git a/nearby/crypto/crypto_provider_boringssl/Cargo.lock b/nearby/crypto/crypto_provider_boringssl/Cargo.lock
index 432c546..356a65d 100644
--- a/nearby/crypto/crypto_provider_boringssl/Cargo.lock
+++ b/nearby/crypto/crypto_provider_boringssl/Cargo.lock
@@ -66,6 +66,12 @@
 ]
 
 [[package]]
+name = "either"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
+
+[[package]]
 name = "getrandom"
 version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -95,6 +101,15 @@
 checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
 
 [[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
 name = "itoa"
 version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -333,6 +348,7 @@
 version = "0.1.0"
 dependencies = [
  "hex",
+ "itertools",
  "serde_json",
 ]
 
diff --git a/nearby/crypto/crypto_provider_boringssl/src/ed25519.rs b/nearby/crypto/crypto_provider_boringssl/src/ed25519.rs
index e411df1..0841e2b 100644
--- a/nearby/crypto/crypto_provider_boringssl/src/ed25519.rs
+++ b/nearby/crypto/crypto_provider_boringssl/src/ed25519.rs
@@ -14,7 +14,7 @@
 
 use crypto_provider::ed25519::{
     InvalidPublicKeyBytes, RawPrivateKey, RawPrivateKeyPermit, RawPublicKey, RawSignature,
-    Signature as _, SignatureError,
+    SignatureError, SignatureImpl,
 };
 
 pub struct Ed25519;
@@ -27,7 +27,7 @@
 
 pub struct KeyPair(bssl_crypto::ed25519::PrivateKey);
 
-impl crypto_provider::ed25519::KeyPair for KeyPair {
+impl crypto_provider::ed25519::KeyPairImpl for KeyPair {
     type PublicKey = PublicKey;
     type Signature = Signature;
 
@@ -51,14 +51,14 @@
         Self(bssl_crypto::ed25519::PrivateKey::generate())
     }
 
-    fn public(&self) -> Self::PublicKey {
+    fn public_key(&self) -> Self::PublicKey {
         PublicKey(self.0.to_public())
     }
 }
 
 pub struct Signature(bssl_crypto::ed25519::Signature);
 
-impl crypto_provider::ed25519::Signature for Signature {
+impl crypto_provider::ed25519::SignatureImpl for Signature {
     fn from_bytes(bytes: &RawSignature) -> Self {
         Self(bssl_crypto::ed25519::Signature::from(*bytes))
     }
@@ -70,7 +70,7 @@
 
 pub struct PublicKey(bssl_crypto::ed25519::PublicKey);
 
-impl crypto_provider::ed25519::PublicKey for PublicKey {
+impl crypto_provider::ed25519::PublicKeyImpl for PublicKey {
     type Signature = Signature;
 
     fn from_bytes(bytes: &RawPublicKey) -> Result<Self, InvalidPublicKeyBytes>
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs b/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs
index 46abfc3..ee01101 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs
@@ -16,7 +16,7 @@
 
 use crypto_provider::ed25519::{
     InvalidPublicKeyBytes, RawPrivateKey, RawPrivateKeyPermit, RawPublicKey, RawSignature,
-    Signature as _, SignatureError, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH,
+    SignatureError, SignatureImpl, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH,
 };
 
 pub struct Ed25519;
@@ -29,7 +29,7 @@
 
 pub struct KeyPair(ed25519_dalek::SigningKey);
 
-impl crypto_provider::ed25519::KeyPair for KeyPair {
+impl crypto_provider::ed25519::KeyPairImpl for KeyPair {
     type PublicKey = PublicKey;
     type Signature = Signature;
 
@@ -54,14 +54,14 @@
         Self(ed25519_dalek::SigningKey::generate(&mut csprng))
     }
 
-    fn public(&self) -> Self::PublicKey {
+    fn public_key(&self) -> Self::PublicKey {
         PublicKey(self.0.verifying_key())
     }
 }
 
 pub struct Signature(ed25519_dalek::Signature);
 
-impl crypto_provider::ed25519::Signature for Signature {
+impl crypto_provider::ed25519::SignatureImpl for Signature {
     fn from_bytes(bytes: &RawSignature) -> Self {
         Self(ed25519_dalek::Signature::from_bytes(bytes))
     }
@@ -73,7 +73,7 @@
 
 pub struct PublicKey(ed25519_dalek::VerifyingKey);
 
-impl crypto_provider::ed25519::PublicKey for PublicKey {
+impl crypto_provider::ed25519::PublicKeyImpl for PublicKey {
     type Signature = Signature;
 
     fn from_bytes(bytes: &RawPublicKey) -> Result<Self, InvalidPublicKeyBytes>
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs b/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
index 2985dec..97cf6a7 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
@@ -56,7 +56,7 @@
     }
 }
 
-/// The the RustCrypto backed struct which implements CryptoProvider
+/// The RustCrypto backed struct which implements CryptoProvider
 #[derive(Default, Clone, Debug, PartialEq, Eq)]
 pub struct RustCryptoImpl<R: CryptoRng + SeedableRng + RngCore> {
     _marker: PhantomData<R>,
diff --git a/nearby/crypto/crypto_provider_stubs/src/lib.rs b/nearby/crypto/crypto_provider_stubs/src/lib.rs
index 83d54ff..35bc274 100644
--- a/nearby/crypto/crypto_provider_stubs/src/lib.rs
+++ b/nearby/crypto/crypto_provider_stubs/src/lib.rs
@@ -29,8 +29,8 @@
         Aes, Aes128Key, Aes256Key, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher,
     },
     ed25519::{
-        self, Ed25519Provider, InvalidPublicKeyBytes, KeyPair, RawPrivateKey, RawPrivateKeyPermit,
-        RawPublicKey, RawSignature, Signature, SignatureError,
+        self, Ed25519Provider, InvalidPublicKeyBytes, KeyPairImpl, RawPrivateKey,
+        RawPrivateKeyPermit, RawPublicKey, RawSignature, SignatureError, SignatureImpl,
     },
     elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey},
     hkdf::{Hkdf, InvalidLength},
@@ -477,7 +477,7 @@
     type Signature = SignatureStubs;
 }
 
-impl ed25519::PublicKey for PublicKeyStubs {
+impl ed25519::PublicKeyImpl for PublicKeyStubs {
     type Signature = SignatureStubs;
 
     fn from_bytes(bytes: &RawPublicKey) -> Result<Self, InvalidPublicKeyBytes>
@@ -502,7 +502,7 @@
 
 pub struct SignatureStubs;
 
-impl Signature for SignatureStubs {
+impl SignatureImpl for SignatureStubs {
     fn from_bytes(_bytes: &RawSignature) -> Self {
         unimplemented!()
     }
@@ -514,7 +514,7 @@
 
 pub struct KeyPairStubs;
 
-impl KeyPair for KeyPairStubs {
+impl KeyPairImpl for KeyPairStubs {
     type PublicKey = PublicKeyStubs;
     type Signature = SignatureStubs;
 
@@ -537,7 +537,7 @@
         unimplemented!()
     }
 
-    fn public(&self) -> Self::PublicKey {
+    fn public_key(&self) -> Self::PublicKey {
         unimplemented!()
     }
 }
diff --git a/nearby/crypto/crypto_provider_test/fuzz/Cargo.lock b/nearby/crypto/crypto_provider_test/fuzz/Cargo.lock
index 72c827b..844b537 100644
--- a/nearby/crypto/crypto_provider_test/fuzz/Cargo.lock
+++ b/nearby/crypto/crypto_provider_test/fuzz/Cargo.lock
@@ -434,9 +434,9 @@
 
 [[package]]
 name = "jobserver"
-version = "0.1.27"
+version = "0.1.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
+checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6"
 dependencies = [
  "libc",
 ]
diff --git a/nearby/crypto/crypto_provider_test/fuzz/Cargo.toml b/nearby/crypto/crypto_provider_test/fuzz/Cargo.toml
index 5bfc9ab..10c3e5c 100644
--- a/nearby/crypto/crypto_provider_test/fuzz/Cargo.toml
+++ b/nearby/crypto/crypto_provider_test/fuzz/Cargo.toml
@@ -8,24 +8,18 @@
 cargo-fuzz = true
 
 [dependencies]
-libfuzzer-sys = "0.4"
-crypto_provider = { path = "../../crypto_provider" }
-crypto_provider_default = { path = "../../crypto_provider_default", default-features = false }
-arbitrary = { version = "1.2.3", features = ["derive"] }
+crypto_provider.workspace = true
+crypto_provider_default = { workspace = true, default-features = false }
+derive_fuzztest.workspace = true
+
+[target.'cfg(fuzzing)'.dependencies]
+libfuzzer-sys.workspace = true
 
 [features]
 default = ["crypto_provider_default/default"]
 boringssl = ["crypto_provider_default/boringssl"]
 
-# Prevent this from interfering with workspaces
-[workspace]
-members = ["."]
-
-[profile.release]
-debug = 1
-
 [[bin]]
 name = "fuzz_p256"
-path = "fuzz_targets/fuzz_p256.rs"
-test = false
+path = "src/bin/fuzz_p256.rs"
 doc = false
diff --git a/nearby/crypto/crypto_provider_test/fuzz/fuzz_targets/fuzz_p256.rs b/nearby/crypto/crypto_provider_test/fuzz/src/bin/fuzz_p256.rs
similarity index 86%
rename from nearby/crypto/crypto_provider_test/fuzz/fuzz_targets/fuzz_p256.rs
rename to nearby/crypto/crypto_provider_test/fuzz/src/bin/fuzz_p256.rs
index 00a0624..f5d3111 100644
--- a/nearby/crypto/crypto_provider_test/fuzz/fuzz_targets/fuzz_p256.rs
+++ b/nearby/crypto/crypto_provider_test/fuzz/src/bin/fuzz_p256.rs
@@ -1,4 +1,4 @@
-#![no_main]
+#![cfg_attr(fuzzing, no_main)]
 // Copyright 2023 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,24 +13,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use arbitrary::Arbitrary;
 use crypto_provider::{
     elliptic_curve::EcdhProvider,
     p256::{P256PublicKey, P256},
     CryptoProvider,
 };
 use crypto_provider_default::CryptoProviderImpl;
-use libfuzzer_sys::fuzz_target;
-
-#[derive(Debug, Arbitrary)]
-struct FuzzInput {
-    bytes: Vec<u8>,
-}
+use derive_fuzztest::fuzztest;
 
 type P256PublicKeyAlias<P> = <<P as CryptoProvider>::P256 as EcdhProvider<P256>>::PublicKey;
 
-fuzz_target!(|input: FuzzInput| {
-    let pubkey = P256PublicKeyAlias::<CryptoProviderImpl>::from_sec1_bytes(&input.bytes);
+#[fuzztest]
+fn test(input: Vec<u8>) {
+    let pubkey = P256PublicKeyAlias::<CryptoProviderImpl>::from_sec1_bytes(&input);
     if let Ok(key) = pubkey {
         let (x, y) = key
             .to_affine_coordinates()
@@ -40,4 +35,4 @@
                 .expect("Creating public key from affine coordinates should succeed");
         assert_eq!(key, recreated_pubkey);
     }
-});
+}
diff --git a/nearby/crypto/crypto_provider_test/src/aead/aes_gcm.rs b/nearby/crypto/crypto_provider_test/src/aead/aes_gcm.rs
index 88eaf32..4a84a67 100644
--- a/nearby/crypto/crypto_provider_test/src/aead/aes_gcm.rs
+++ b/nearby/crypto/crypto_provider_test/src/aead/aes_gcm.rs
@@ -11,7 +11,6 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-use alloc::vec::Vec;
 use core::marker;
 
 use hex_literal::hex;
diff --git a/nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs b/nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs
index 56d2215..672b9ce 100644
--- a/nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs
+++ b/nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs
@@ -11,7 +11,6 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-use alloc::vec::Vec;
 use core::marker;
 
 use hex_literal::hex;
diff --git a/nearby/crypto/crypto_provider_test/src/ed25519.rs b/nearby/crypto/crypto_provider_test/src/ed25519.rs
index 0fdb484..f2bc7ef 100644
--- a/nearby/crypto/crypto_provider_test/src/ed25519.rs
+++ b/nearby/crypto/crypto_provider_test/src/ed25519.rs
@@ -15,11 +15,8 @@
 extern crate alloc;
 extern crate std;
 
-use alloc::borrow::ToOwned;
-use alloc::string::String;
-use alloc::vec::Vec;
 use crypto_provider::ed25519::{
-    Ed25519Provider, KeyPair, PublicKey, RawPrivateKeyPermit, RawSignature, Signature,
+    Ed25519Provider, KeyPairImpl, PublicKeyImpl, RawPrivateKeyPermit, RawSignature, SignatureImpl,
 };
 use wycheproof::TestResult;
 
@@ -149,7 +146,7 @@
         sig.as_slice().try_into().expect("Test signature should be the correct length"),
     );
 
-    let pub_key = kp.public();
+    let pub_key = kp.public_key();
     assert_eq!(pub_key.to_bytes().as_slice(), expected_pub_key.as_slice());
     pub_key.verify_strict(msg.as_slice(), &signature).map_err(|_| "verify failed")?;
 
diff --git a/nearby/crypto/crypto_provider_test/src/hkdf.rs b/nearby/crypto/crypto_provider_test/src/hkdf.rs
index d664230..d8982fd 100644
--- a/nearby/crypto/crypto_provider_test/src/hkdf.rs
+++ b/nearby/crypto/crypto_provider_test/src/hkdf.rs
@@ -14,8 +14,6 @@
 extern crate alloc;
 pub use crate::prelude::*;
 use crate::CryptoProvider;
-use alloc::vec;
-use alloc::vec::Vec;
 use core::iter;
 use core::marker::PhantomData;
 use crypto_provider::hkdf::Hkdf;
@@ -51,7 +49,7 @@
 
 const MAX_SHA256_LENGTH: usize = 255 * (256 / 8); // =8160
 
-///
+/// Content of a HKDF test-case.
 pub struct Test<'a> {
     ikm: &'a [u8],
     salt: &'a [u8],
@@ -79,9 +77,8 @@
     assert_eq!(okm, expected);
 }
 
-// Test Vectors from https://tools.ietf.org/html/rfc5869.
 #[rustfmt::skip]
-    ///
+    /// Test Vectors from <https://tools.ietf.org/html/rfc5869>.
     pub fn test_rfc5869_sha256<C: CryptoProvider>(_: PhantomData<C>) {
         let tests = [
             Test {
@@ -152,7 +149,7 @@
         }
     }
 
-///
+/// Tests a bunch of HKDFs of differing lengths.
 pub fn test_lengths<C: CryptoProvider>(_: PhantomData<C>) {
     let hkdf = C::HkdfSha256::new(None, &[]);
     let mut longest = vec![0u8; MAX_SHA256_LENGTH];
@@ -173,28 +170,28 @@
     }
 }
 
-///
+/// Tests an HKDF with the maximum length for Sha256.
 pub fn test_max_length<C: CryptoProvider>(_: PhantomData<C>) {
     let hkdf = C::HkdfSha256::new(Some(&[]), &[]);
     let mut okm = vec![0u8; MAX_SHA256_LENGTH];
     assert!(hkdf.expand(&[], &mut okm).is_ok());
 }
 
-///
+/// Tests an HKDF above the maximum length for Sha256.
 pub fn test_max_length_exceeded<C: CryptoProvider>(_: PhantomData<C>) {
     let hkdf = C::HkdfSha256::new(Some(&[]), &[]);
     let mut okm = vec![0u8; MAX_SHA256_LENGTH + 1];
     assert!(hkdf.expand(&[], &mut okm).is_err());
 }
 
-///
+/// Tests an HKDF with an unsupported length.
 pub fn test_unsupported_length<C: CryptoProvider>(_: PhantomData<C>) {
     let hkdf = C::HkdfSha256::new(Some(&[]), &[]);
     let mut okm = vec![0u8; 90000];
     assert!(hkdf.expand(&[], &mut okm).is_err());
 }
 
-///
+/// Tests HKDF-Expand on the concatenation of info components.
 pub fn test_expand_multi_info<C: CryptoProvider>(_: PhantomData<C>) {
     let info_components = &[
         &b"09090909090909090909090909090909090909090909"[..],
@@ -231,12 +228,12 @@
     }
 }
 
-///
+/// Runs hkdf test vectors using Sha256.
 pub fn run_hkdf_sha256_vectors<C: CryptoProvider>(_: PhantomData<C>) {
     run_hkdf_test_vectors::<C::HkdfSha256>(HashAlg::Sha256)
 }
 
-///
+/// Runs hkdf test vectors using Sha512.
 pub fn run_hkdf_sha512_vectors<C: CryptoProvider>(_: PhantomData<C>) {
     run_hkdf_test_vectors::<C::HkdfSha512>(HashAlg::Sha512)
 }
@@ -246,7 +243,7 @@
     Sha512,
 }
 
-///
+/// Runs the wycheproof test vectors for the given hashing algorithm.
 fn run_hkdf_test_vectors<K: Hkdf>(hash: HashAlg) {
     let test_name = match hash {
         HashAlg::Sha256 => wycheproof::hkdf::TestName::HkdfSha256,
diff --git a/nearby/crypto/crypto_provider_test/src/lib.rs b/nearby/crypto/crypto_provider_test/src/lib.rs
index 2209a3e..6ba6413 100644
--- a/nearby/crypto/crypto_provider_test/src/lib.rs
+++ b/nearby/crypto/crypto_provider_test/src/lib.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 extern crate alloc;
 
-use alloc::{format, string::String};
+use alloc::format;
 use core::marker::PhantomData;
 
 use crypto_provider::CryptoProvider;
@@ -47,7 +47,7 @@
 pub type CryptoProviderTestCase<T> = fn(PhantomData<T>);
 
 #[derive(Debug)]
-pub(crate) struct TestError(String);
+pub(crate) struct TestError(#[allow(unused)] String);
 
 impl TestError {
     pub(crate) fn new<D: core::fmt::Debug>(value: D) -> Self {
diff --git a/nearby/crypto/crypto_provider_test/src/sha2.rs b/nearby/crypto/crypto_provider_test/src/sha2.rs
index 66a3025..b10ac9a 100644
--- a/nearby/crypto/crypto_provider_test/src/sha2.rs
+++ b/nearby/crypto/crypto_provider_test/src/sha2.rs
@@ -16,7 +16,6 @@
 extern crate std;
 use crate::prelude::*;
 use crate::CryptoProvider;
-use alloc::vec::Vec;
 use core::{marker::PhantomData, str::FromStr};
 use crypto_provider::sha2::{Sha256, Sha512};
 use hex::FromHex;
diff --git a/nearby/crypto/rand_core_05_adapter/Cargo.toml b/nearby/crypto/rand_core_05_adapter/Cargo.toml
deleted file mode 100644
index 1a02526..0000000
--- a/nearby/crypto/rand_core_05_adapter/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name = "rand_core_05_adapter"
-version.workspace = true
-edition.workspace = true
-publish.workspace = true
-
-[lints]
-workspace = true
-
-[dependencies]
-rand.workspace = true
-#  an older rand_core used by ed25519-dalek so we can adapt newer rand to it
-rand_core05 = { package = "rand_core", version = "0.5.1" }
diff --git a/nearby/crypto/rand_core_05_adapter/src/lib.rs b/nearby/crypto/rand_core_05_adapter/src/lib.rs
deleted file mode 100644
index 9de0020..0000000
--- a/nearby/crypto/rand_core_05_adapter/src/lib.rs
+++ /dev/null
@@ -1,61 +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.
-
-//! Adapter for using rand_core 0.5 RNGs with code that expects rand_core 0.5 RNGs.
-
-#![no_std]
-
-/// A trivial adapter to expose rand 1.0/rand_core 0.6 rngs to ed25519-dalek's rand_core 0.5 types,
-/// which we import under a separate name so there's no clash.
-pub struct RandWrapper<'r, R: rand::RngCore + rand::CryptoRng> {
-    rng: &'r mut R,
-}
-
-impl<'r, R: rand::RngCore + rand::CryptoRng> RandWrapper<'r, R> {
-    /// Build a rand_core 0.5 compatible wrapper around the provided rng.
-    pub fn from(rng: &'r mut R) -> RandWrapper<'r, R> {
-        RandWrapper { rng }
-    }
-}
-
-impl<'r, R: rand::RngCore + rand::CryptoRng> rand_core05::RngCore for RandWrapper<'r, R> {
-    fn next_u32(&mut self) -> u32 {
-        self.rng.next_u32()
-    }
-
-    fn next_u64(&mut self) -> u64 {
-        self.rng.next_u64()
-    }
-
-    fn fill_bytes(&mut self, dest: &mut [u8]) {
-        self.rng.fill_bytes(dest)
-    }
-
-    #[cfg(feature = "std")]
-    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core05::Error> {
-        self.rng.try_fill_bytes(dest).map_err(|e| rand_core05::Error::new(e.take_inner()))
-    }
-
-    #[cfg(not(feature = "std"))]
-    #[allow(clippy::expect_used)]
-    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core05::Error> {
-        self.rng
-            .try_fill_bytes(dest)
-            .map_err(|e| rand_core05::Error::from(e.code().expect("for no_std this is never none")))
-    }
-}
-
-impl<'r, R: rand::RngCore + rand::CryptoRng> rand_core05::CryptoRng for RandWrapper<'r, R> {
-    // marker trait
-}
diff --git a/nearby/deny.toml b/nearby/deny.toml
index d9f6dc8..30dd444 100644
--- a/nearby/deny.toml
+++ b/nearby/deny.toml
@@ -48,8 +48,8 @@
 # A list of advisory IDs to ignore. Note that ignored advisories will still
 # output a note when they are encountered.
 ignore = [
-    # criterion 0.4.0 depends on a version of atty w/unaligned reads
-    "RUSTSEC-2021-0145",
+    # comment explaining why we have to ignore it
+    # "RUSTSEC-FOO",
 ]
 # Threshold for security vulnerabilities, any vulnerability with a CVSS score
 # lower than the range specified will be ignored. Note that ignored advisories
@@ -85,7 +85,8 @@
     "ISC",
     "Unicode-DFS-2016",
     "OpenSSL",
-    "Unlicense"
+    "Unlicense",
+    "CC0-1.0"
 ]
 # List of explicitly disallowed licenses
 # See https://spdx.org/licenses/ for list of possible licenses
@@ -94,7 +95,7 @@
     #"Nokia",
 ]
 # Lint level for licenses considered copyleft
-copyleft = "warn"
+copyleft = "deny"
 # 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
@@ -117,7 +118,23 @@
 exceptions = [
     # Each entry is the crate and version constraint, and its specific allow
     # list
-    #{ allow = ["Zlib"], name = "adler32", version = "*" },
+
+
+    # "Reciprocal" licensed crate pulled directly from crates.io without modifications
+    # Important: Update https://third-party-mirror.googlesource.com/webpki-roots/ if you update this version
+    { allow = ["MPL-2.0"], name = "webpki-roots", version = "0.25.2" },
+
+    # "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" },
+
+    # "Reciprocal" licensed crate pulled directly from crates.io without modifications
+    # Important: Update https://third-party-mirror.googlesource.com/smartstring/ if you update this version
+    { allow = ["MPL-2.0"], name = "smartstring", version = "1.0.1" },
+
+    # "Reciprocal" licensed crate pulled directly from crates.io without modifications
+    # Important: Update https://third-party-mirror.googlesource.com/cbindgen/ if you update this version
+    { allow = ["MPL-2.0"], name = "cbindgen", version = "0.26.0" },
 ]
 
 # Some crates don't have (easily) machine readable licensing information,
diff --git a/nearby/presence/CMakeLists.txt b/nearby/presence/CMakeLists.txt
index 630d172..bb099de 100644
--- a/nearby/presence/CMakeLists.txt
+++ b/nearby/presence/CMakeLists.txt
@@ -67,7 +67,7 @@
 if (ENABLE_FUZZ)
     if (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
         MESSAGE(FATAL_ERROR "fuzzing is only enabed when building with Clang, please set CC and CXX to use clang instead of ${CMAKE_CXX_COMPILER_ID}")
-    endif()
+    endif ()
 
     add_subdirectory(${THIRD_PARTY_DIR}/boringssl boringssl-build)
     add_subdirectory(${THIRD_PARTY_DIR}/fuzztest fuzztest-build)
@@ -83,9 +83,13 @@
     set_directory_properties(
             PROPERTIES
             COMPILE_OPTIONS
-            -Wall -Wextra -Wimplicit-fallthrough -Wextra-semi
-            -Wno-missing-field-initializers -Wno-unused-parameter -Wno-psabi
-            -Wshadow -Wsign-compare
+            -Werror
+            -Wall
+            -Wextra
+            -Wimplicit-fallthrough
+            -Wextra-semi
+            -Wshadow
+            -Wsign-compare
     )
 endif ()
 
diff --git a/nearby/presence/array_view/src/lib.rs b/nearby/presence/array_view/src/lib.rs
index e2ea8d4..d4d7c84 100644
--- a/nearby/presence/array_view/src/lib.rs
+++ b/nearby/presence/array_view/src/lib.rs
@@ -62,7 +62,7 @@
         }
     }
 
-    /// Returns the prefix of the array as a slice.
+    /// Returns the occupied portion of the array as a slice.
     pub fn as_slice(&self) -> &[T] {
         &self.array[..self.len]
     }
@@ -105,9 +105,8 @@
 }
 
 #[cfg(test)]
+#[allow(clippy::unwrap_used)]
 mod tests {
-    #![allow(clippy::unwrap_used)]
-
     extern crate std;
     use crate::ArrayView;
     use std::format;
diff --git a/nearby/presence/ldt/benches/ldt_scan.rs b/nearby/presence/ldt/benches/ldt_scan.rs
index b1711ad..f5fa359 100644
--- a/nearby/presence/ldt/benches/ldt_scan.rs
+++ b/nearby/presence/ldt/benches/ldt_scan.rs
@@ -19,7 +19,8 @@
 use crypto_provider_rustcrypto::RustCrypto;
 use ctr::cipher::{KeyIvInit as _, StreamCipher as _, StreamCipherSeek as _};
 use ldt::{
-    DefaultPadder, LdtDecryptCipher, LdtEncryptCipher, LdtKey, Mix, Padder, Swap, XorPadder,
+    DefaultPadder, LdtCipher, LdtDecryptCipher, LdtEncryptCipher, LdtKey, Mix, Padder, Swap,
+    XorPadder,
 };
 use ldt_tbc::TweakableBlockCipher;
 use sha2::Digest as _;
@@ -159,7 +160,9 @@
 }
 
 trait ScanCipher {
+    #[allow(dead_code)]
     fn encrypt(&mut self, buf: &mut [u8]);
+
     fn decrypt(&mut self, buf: &mut [u8]);
 }
 
diff --git a/nearby/presence/ldt/examples/gen_ldt_xor_pad_test_vectors.rs b/nearby/presence/ldt/examples/gen_ldt_xor_pad_test_vectors.rs
index 7e7c943..43ba617 100644
--- a/nearby/presence/ldt/examples/gen_ldt_xor_pad_test_vectors.rs
+++ b/nearby/presence/ldt/examples/gen_ldt_xor_pad_test_vectors.rs
@@ -19,7 +19,7 @@
 use crypto_provider::aes::BLOCK_SIZE;
 use crypto_provider::{aes, CryptoProvider, CryptoRng};
 use crypto_provider_rustcrypto::RustCrypto;
-use ldt::{LdtEncryptCipher, LdtKey, Swap, XorPadder};
+use ldt::{LdtCipher, LdtEncryptCipher, LdtKey, Swap, XorPadder};
 use rand::{Rng as _, SeedableRng as _};
 use rand_ext::*;
 use serde_json::json;
diff --git a/nearby/presence/ldt/examples/ldt_benchmark.rs b/nearby/presence/ldt/examples/ldt_benchmark.rs
index 101e17b..a710d56 100644
--- a/nearby/presence/ldt/examples/ldt_benchmark.rs
+++ b/nearby/presence/ldt/examples/ldt_benchmark.rs
@@ -18,7 +18,7 @@
 
 use clap::Parser as _;
 use crypto_provider_rustcrypto::RustCrypto;
-use ldt::{LdtDecryptCipher, LdtEncryptCipher, LdtKey, Mix, Swap, XorPadder};
+use ldt::{LdtCipher, LdtDecryptCipher, LdtEncryptCipher, LdtKey, Mix, Swap, XorPadder};
 
 use crypto_provider::{CryptoProvider, CryptoRng};
 use ldt_tbc::TweakableBlockCipher;
diff --git a/nearby/presence/ldt/examples/ldt_prp.rs b/nearby/presence/ldt/examples/ldt_prp.rs
index 7734952..fea1904 100644
--- a/nearby/presence/ldt/examples/ldt_prp.rs
+++ b/nearby/presence/ldt/examples/ldt_prp.rs
@@ -23,7 +23,7 @@
 
 #![allow(clippy::unwrap_used, clippy::indexing_slicing)]
 
-use clap::{self, Parser as _};
+use clap::Parser as _;
 use crypto_provider::aes::BLOCK_SIZE;
 use crypto_provider::{CryptoProvider, CryptoRng};
 use crypto_provider_rustcrypto::RustCrypto;
diff --git a/nearby/presence/ldt/fuzz/Cargo.toml b/nearby/presence/ldt/fuzz/Cargo.toml
index 3f37e98..c192792 100644
--- a/nearby/presence/ldt/fuzz/Cargo.toml
+++ b/nearby/presence/ldt/fuzz/Cargo.toml
@@ -1,32 +1,23 @@
 [package]
 name = "ldt-fuzz"
-version = "0.0.0"
-authors = ["Automatically generated"]
-publish = false
-edition = "2018"
+version.workspace = true
+publish.workspace = true
+edition.workspace = true
 
 [package.metadata]
 cargo-fuzz = true
 
 [dependencies]
-libfuzzer-sys = "0.4"
-arbitrary = { version = "1.1.7", features = ["derive"] }
+arbitrary = { workspace = true, features = ["derive"] }
+crypto_provider_rustcrypto.workspace = true
+derive_fuzztest.workspace = true
+ldt.workspace = true
+xts_aes.workspace = true
 
-[dependencies.ldt]
-path = ".."
-
-[dependencies.xts_aes]
-path = "../../xts_aes"
-
-[dependencies.crypto_provider_rustcrypto]
-path = "../../../crypto/crypto_provider_rustcrypto"
-
-# Prevent this from interfering with workspaces
-[workspace]
-members = ["."]
+[target.'cfg(fuzzing)'.dependencies]
+libfuzzer-sys.workspace = true
 
 [[bin]]
-name = "ldt-roundtrip"
-path = "fuzz_targets/ldt_roundtrip.rs"
-test = false
+name = "ldt_roundtrip"
+path = "src/bin/ldt_roundtrip.rs"
 doc = false
diff --git a/nearby/presence/ldt/fuzz/fuzz_targets/ldt_roundtrip.rs b/nearby/presence/ldt/fuzz/src/bin/ldt_roundtrip.rs
similarity index 69%
rename from nearby/presence/ldt/fuzz/fuzz_targets/ldt_roundtrip.rs
rename to nearby/presence/ldt/fuzz/src/bin/ldt_roundtrip.rs
index 13e2a46..097900f 100644
--- a/nearby/presence/ldt/fuzz/fuzz_targets/ldt_roundtrip.rs
+++ b/nearby/presence/ldt/fuzz/src/bin/ldt_roundtrip.rs
@@ -1,4 +1,4 @@
-#![no_main]
+#![cfg_attr(fuzzing, no_main)]
 // Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,25 +14,28 @@
 // limitations under the License.
 
 use crypto_provider_rustcrypto::RustCrypto;
+use derive_fuzztest::fuzztest;
 use ldt::*;
-use libfuzzer_sys::fuzz_target;
 use xts_aes::XtsAes128;
 
-fuzz_target!(|data: LdtFuzzInput| {
-    let ldt_enc =
-        LdtEncryptCipher::<16, XtsAes128<RustCrypto>, Swap>::new(&LdtKey::from_concatenated(&data.ldt_key));
-    let ldt_dec =
-        LdtDecryptCipher::<16, XtsAes128<RustCrypto>, Swap>::new(&LdtKey::from_concatenated(&data.ldt_key));
+#[fuzztest]
+fn test(data: LdtFuzzInput) {
+    let ldt_enc = LdtEncryptCipher::<16, XtsAes128<RustCrypto>, Swap>::new(
+        &LdtKey::from_concatenated(&data.ldt_key),
+    );
+    let ldt_dec = LdtDecryptCipher::<16, XtsAes128<RustCrypto>, Swap>::new(
+        &LdtKey::from_concatenated(&data.ldt_key),
+    );
     let len = 16 + (data.len as usize % 16);
-    let padder: XorPadder<16> = data.xor_padder.clone().into();
+    let padder: XorPadder<16> = data.xor_padder.into();
 
-    let mut buffer = data.plaintext.clone();
+    let mut buffer = data.plaintext;
     ldt_enc.encrypt(&mut buffer[..len], &padder).unwrap();
     ldt_dec.decrypt(&mut buffer[..len], &padder).unwrap();
     assert_eq!(data.plaintext, buffer);
-});
+}
 
-#[derive(Debug, arbitrary::Arbitrary)]
+#[derive(Clone, Debug, arbitrary::Arbitrary)]
 struct LdtFuzzInput {
     // XTS-AES128 keys
     ldt_key: [u8; 64],
diff --git a/nearby/presence/ldt/src/lib.rs b/nearby/presence/ldt/src/lib.rs
index 8596ad2..2b4a962 100644
--- a/nearby/presence/ldt/src/lib.rs
+++ b/nearby/presence/ldt/src/lib.rs
@@ -19,11 +19,20 @@
 #[cfg(feature = "std")]
 extern crate std;
 
-use core::{fmt, marker::PhantomData};
+use core::{fmt, marker::PhantomData, ops};
 use crypto_provider::CryptoProvider;
 use ldt_tbc::{ConcatenatedKeyArray, TweakableBlockCipher, TweakableBlockCipherKey};
 use ldt_tbc::{TweakableBlockCipherDecrypter, TweakableBlockCipherEncrypter};
 
+/// Common functionality for [LdtEncryptCipher] and [LdtDecryptCipher]
+pub trait LdtCipher<const B: usize, T: TweakableBlockCipher<B>> {
+    /// The range of input lengths the cipher can operate on
+    const VALID_INPUT_LEN: ops::Range<usize>;
+
+    /// Create a new cipher with the provided [TweakableBlockCipher] and [Mix] function
+    fn new(key: &LdtKey<T::Key>) -> Self;
+}
+
 /// Implementation of the [LDT](https://eprint.iacr.org/2017/841.pdf) length doubler encryption cipher.
 ///
 /// `B` is the block size.
@@ -36,16 +45,21 @@
     mix_phantom: PhantomData<M>,
 }
 
-impl<const B: usize, T: TweakableBlockCipher<B>, M: Mix> LdtEncryptCipher<B, T, M> {
-    /// Create an [LdtEncryptCipher] with the provided Tweakable block cipher and Mix function
-    pub fn new(key: &LdtKey<T::Key>) -> Self {
+impl<const B: usize, T: TweakableBlockCipher<B>, M: Mix> LdtCipher<B, T>
+    for LdtEncryptCipher<B, T, M>
+{
+    const VALID_INPUT_LEN: ops::Range<usize> = input_len_range::<B>();
+
+    fn new(key: &LdtKey<T::Key>) -> Self {
         LdtEncryptCipher {
             cipher_1: T::EncryptionCipher::new(&key.key_1),
             cipher_2: T::EncryptionCipher::new(&key.key_2),
             mix_phantom: PhantomData,
         }
     }
+}
 
+impl<const B: usize, T: TweakableBlockCipher<B>, M: Mix> LdtEncryptCipher<B, T, M> {
     /// Encrypt `data` in place, performing the pad operation with `padder`.
     ///
     /// Unless you have particular padding needs, use [DefaultPadder].
@@ -79,13 +93,6 @@
 
 impl<const B: usize, T: TweakableBlockCipher<B>, M: Mix> LdtDecryptCipher<B, T, M> {
     /// Create an [LdtDecryptCipher] with the provided Tweakable block cipher and Mix function
-    pub fn new(key: &LdtKey<T::Key>) -> Self {
-        LdtDecryptCipher {
-            cipher_1: T::DecryptionCipher::new(&key.key_1),
-            cipher_2: T::DecryptionCipher::new(&key.key_2),
-            mix_phantom: PhantomData,
-        }
-    }
 
     /// Decrypt `data` in place, performing the pad operation with `padder`.
     ///
@@ -106,6 +113,26 @@
     }
 }
 
+impl<const B: usize, T: TweakableBlockCipher<B>, M: Mix> LdtCipher<B, T>
+    for LdtDecryptCipher<B, T, M>
+{
+    const VALID_INPUT_LEN: ops::Range<usize> = input_len_range::<B>();
+
+    fn new(key: &LdtKey<T::Key>) -> Self {
+        LdtDecryptCipher {
+            cipher_1: T::DecryptionCipher::new(&key.key_1),
+            cipher_2: T::DecryptionCipher::new(&key.key_2),
+            mix_phantom: PhantomData,
+        }
+    }
+}
+
+/// Returns the range of valid input lengths to encrypt or decrypt with LDT for a given tweakable
+/// block cipher block size `B`, namely `[B, B * 2)`.
+const fn input_len_range<const B: usize>() -> ops::Range<usize> {
+    B..B * 2
+}
+
 // internal implementation of ldt cipher operations, re-used by encryption and decryption, by providing
 // the corresponding cipher_op and mix operation
 fn do_ldt<const B: usize, T, O, C, X, P>(
@@ -124,7 +151,7 @@
     X: for<'a, 'b> Fn(&'a [u8], &'b [u8]) -> (&'b [u8], &'a [u8]),
     P: Padder<B, T>,
 {
-    if data.len() < B || data.len() >= B * 2 {
+    if !input_len_range::<B>().contains(&data.len()) {
         return Err(LdtError::InvalidLength(data.len()));
     }
     let s = data.len() - B;
@@ -229,6 +256,7 @@
 
 /// Per section 2.4, swapping `a` and `b` is a valid mix function
 pub struct Swap {}
+
 impl Mix for Swap {
     fn forwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8]) {
         debug_assert_eq!(a.len(), b.len());
diff --git a/nearby/presence/ldt/tests/ldt_roundtrip.rs b/nearby/presence/ldt/tests/ldt_roundtrip.rs
index 251cefe..fabddf0 100644
--- a/nearby/presence/ldt/tests/ldt_roundtrip.rs
+++ b/nearby/presence/ldt/tests/ldt_roundtrip.rs
@@ -20,7 +20,7 @@
 use ldt::*;
 use ldt_tbc::TweakableBlockCipher;
 use rand::rngs::StdRng;
-use rand::{self, distributions, Rng as _, SeedableRng as _};
+use rand::{distributions, Rng as _, SeedableRng as _};
 use rand_ext::{random_bytes, random_vec};
 use xts_aes::{XtsAes128, XtsAes256};
 
@@ -57,9 +57,7 @@
 fn roundtrip_xor_padder() {
     let mut rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
     let mut rc_rng = rand::rngs::StdRng::from_entropy();
-    // 2 bytes smaller because we're using a 2 byte salt
-    let plaintext_len_range =
-        distributions::Uniform::new_inclusive(BLOCK_SIZE, BLOCK_SIZE * 2 - 1 - 2);
+    let plaintext_len_range = distributions::Uniform::new_inclusive(BLOCK_SIZE, BLOCK_SIZE * 2 - 1);
 
     for _ in 0..100_000 {
         let padder: XorPadder<BLOCK_SIZE> =
diff --git a/nearby/presence/ldt/tests/ldt_test_vectors.rs b/nearby/presence/ldt/tests/ldt_test_vectors.rs
index 03b5d3f..1e8a5e1 100644
--- a/nearby/presence/ldt/tests/ldt_test_vectors.rs
+++ b/nearby/presence/ldt/tests/ldt_test_vectors.rs
@@ -16,7 +16,7 @@
 
 use anyhow::anyhow;
 use crypto_provider_default::CryptoProviderImpl;
-use ldt::{DefaultPadder, LdtDecryptCipher, LdtEncryptCipher, LdtKey, Swap, XorPadder};
+use ldt::{DefaultPadder, LdtCipher, LdtDecryptCipher, LdtEncryptCipher, LdtKey, Swap, XorPadder};
 use std::{fs, io::Read as _};
 use test_helper::{extract_key_array, extract_key_vec};
 use xts_aes::XtsAes128;
diff --git a/nearby/presence/ldt/tests/tests.rs b/nearby/presence/ldt/tests/tests.rs
index 25938fb..e68273f 100644
--- a/nearby/presence/ldt/tests/tests.rs
+++ b/nearby/presence/ldt/tests/tests.rs
@@ -18,7 +18,8 @@
 use crypto_provider::aes::BLOCK_SIZE;
 use crypto_provider_default::CryptoProviderImpl;
 use ldt::{
-    DefaultPadder, LdtDecryptCipher, LdtEncryptCipher, LdtError, LdtKey, Padder, Swap, XorPadder,
+    DefaultPadder, LdtCipher, LdtDecryptCipher, LdtEncryptCipher, LdtError, LdtKey, Padder, Swap,
+    XorPadder,
 };
 use xts_aes::{XtsAes128, XtsAes128Key};
 
@@ -137,20 +138,22 @@
 
 #[test]
 fn encrypt_too_short_err() {
-    do_length_check_enc(7)
+    do_length_check_enc(15)
 }
 
 #[test]
 fn encrypt_too_long_err() {
-    do_length_check_enc(40)
+    do_length_check_enc(32)
 }
+
 #[test]
 fn decrypt_too_short_err() {
-    do_length_check_dec(7)
+    do_length_check_dec(15)
 }
+
 #[test]
 fn decrypt_too_long_err() {
-    do_length_check_dec(40)
+    do_length_check_dec(32)
 }
 
 fn do_length_check_dec(len: usize) {
diff --git a/nearby/presence/ldt_np_adv/Cargo.toml b/nearby/presence/ldt_np_adv/Cargo.toml
index ce84505..5d18a28 100644
--- a/nearby/presence/ldt_np_adv/Cargo.toml
+++ b/nearby/presence/ldt_np_adv/Cargo.toml
@@ -23,6 +23,7 @@
 crypto_provider_default = { workspace = true, features = ["rustcrypto", "std"] }
 rand_ext.workspace = true
 test_helper.workspace = true
+test_vector_hkdf.workspace = true
 
 rand.workspace = true
 base64.workspace = true
diff --git a/nearby/presence/ldt_np_adv/benches/ldt_adv_scan.rs b/nearby/presence/ldt_np_adv/benches/ldt_adv_scan.rs
index b8de6fd..f9b1880 100644
--- a/nearby/presence/ldt_np_adv/benches/ldt_adv_scan.rs
+++ b/nearby/presence/ldt_np_adv/benches/ldt_adv_scan.rs
@@ -36,7 +36,7 @@
             let payload_len = rng.gen_range(crypto_provider::aes::BLOCK_SIZE..=LDT_XTS_AES_MAX_LEN);
             let payload = random_vec(&mut rng, payload_len);
 
-            let salt = LegacySalt::from(rng.gen::<[u8; 2]>());
+            let salt = V0Salt::from(rng.gen::<[u8; 2]>());
             #[allow(clippy::unit_arg)]
             b.iter(|| {
                 let ciphers = build_ciphers(&configs);
@@ -48,7 +48,7 @@
             let payload_len = rng.gen_range(crypto_provider::aes::BLOCK_SIZE..=LDT_XTS_AES_MAX_LEN);
             let payload = random_vec(&mut rng, payload_len);
 
-            let salt = LegacySalt::from(rng.gen::<[u8; 2]>());
+            let salt = V0Salt::from(rng.gen::<[u8; 2]>());
             let ciphers = build_ciphers(&configs);
             #[allow(clippy::unit_arg)]
             b.iter(|| black_box(find_matching_item::<C>(&ciphers, salt, &payload)));
@@ -60,11 +60,11 @@
 criterion_main!(benches);
 
 fn find_matching_item<C: CryptoProvider>(
-    ciphers: &[LdtNpAdvDecrypterXtsAes128<C>],
-    salt: LegacySalt,
+    ciphers: &[AuthenticatedNpLdtDecryptCipher<C>],
+    salt: V0Salt,
     payload: &[u8],
 ) {
-    let padder = salt_padder::<16, C>(salt);
+    let padder = salt_padder::<C>(salt);
     let _ = ciphers
         .iter()
         .enumerate()
@@ -75,12 +75,12 @@
                 .ok()
         })
         .next()
-        .map(|(index, buffer)| MatchResult { matching_index: index, buffer });
+        .map(|(index, (token, plaintext))| MatchResult { matching_index: index, token, plaintext });
 }
 
 fn build_ciphers<C: CryptoProvider>(
     configs: &[CipherConfig<C>],
-) -> Vec<LdtNpAdvDecrypterXtsAes128<C>> {
+) -> Vec<AuthenticatedNpLdtDecryptCipher<C>> {
     configs
         .iter()
         .map(|config| {
@@ -99,8 +99,10 @@
 pub struct MatchResult<const O: usize> {
     /// The index of the batch item that matched
     matching_index: usize,
-    /// The buffer holding the plaintext
-    buffer: ArrayView<u8, O>,
+    /// The matched identity token
+    token: V0IdentityToken,
+    /// The buffer holding the remaining plaintext
+    plaintext: ArrayView<u8, O>,
 }
 
 fn random_configs<C: CryptoProvider, R: rand::Rng>(
diff --git a/nearby/presence/ldt_np_adv/fuzz/Cargo.toml b/nearby/presence/ldt_np_adv/fuzz/Cargo.toml
index a346c6b..e4f497c 100644
--- a/nearby/presence/ldt_np_adv/fuzz/Cargo.toml
+++ b/nearby/presence/ldt_np_adv/fuzz/Cargo.toml
@@ -1,40 +1,30 @@
 [package]
 name = "ldt-np-adv-fuzz"
-version = "0.0.0"
-authors = ["Automatically generated"]
-publish = false
-edition = "2018"
+version.workspace = true
+publish.workspace = true
+edition.workspace = true
 
 [package.metadata]
 cargo-fuzz = true
 
 [dependencies]
-libfuzzer-sys = "0.4"
-arbitrary = { version = "1.1.7", features = ["derive"] }
-crypto_provider_rustcrypto = { path = "../../../crypto/crypto_provider_rustcrypto", features = ["alloc"] }
+arbitrary = { workspace = true, features = ["derive"] }
+crypto_provider_rustcrypto = { workspace = true, features = ["alloc"] }
+derive_fuzztest.workspace = true
+ldt.workspace = true
+ldt_np_adv.workspace = true
+np_hkdf.workspace = true
+xts_aes.workspace = true
 
-[dependencies.ldt_np_adv]
-path = ".."
-
-[dependencies.np_hkdf]
-path = "../../np_hkdf"
-[dependencies.xts_aes]
-path = "../../xts_aes"
-[dependencies.ldt]
-path = "../../ldt"
-
-# Prevent this from interfering with workspaces
-[workspace]
-members = ["."]
+[target.'cfg(fuzzing)'.dependencies]
+libfuzzer-sys.workspace = true
 
 [[bin]]
-name = "ldt-np-roundtrip"
-path = "fuzz_targets/ldt_np_roundtrip.rs"
-test = false
+name = "ldt_np_roundtrip"
+path = "src/bin/ldt_np_roundtrip.rs"
 doc = false
 
 [[bin]]
-name = "ldt-np-decrypt"
-path = "fuzz_targets/ldt_np_decrypt.rs"
-test = false
+name = "ldt_np_decrypt"
+path = "src/bin/ldt_np_decrypt.rs"
 doc = false
diff --git a/nearby/presence/ldt_np_adv/fuzz/fuzz_targets/ldt_np_decrypt.rs b/nearby/presence/ldt_np_adv/fuzz/src/bin/ldt_np_decrypt.rs
similarity index 82%
rename from nearby/presence/ldt_np_adv/fuzz/fuzz_targets/ldt_np_decrypt.rs
rename to nearby/presence/ldt_np_adv/fuzz/src/bin/ldt_np_decrypt.rs
index 2039219..995ac15 100644
--- a/nearby/presence/ldt_np_adv/fuzz/fuzz_targets/ldt_np_decrypt.rs
+++ b/nearby/presence/ldt_np_adv/fuzz/src/bin/ldt_np_decrypt.rs
@@ -1,4 +1,4 @@
-#![no_main]
+#![cfg_attr(fuzzing, no_main)]
 // Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,26 +14,25 @@
 // limitations under the License.
 
 use crypto_provider_rustcrypto::RustCrypto;
+use derive_fuzztest::fuzztest;
 use ldt_np_adv::*;
-use libfuzzer_sys::fuzz_target;
 
-fuzz_target!(|data: LdtNpDecryptFuzzInput| {
+#[fuzztest]
+fn test(data: LdtNpDecryptFuzzInput) {
     // try to decrypt data that won't pass validation
     let salt = data.salt.into();
-    let padder = salt_padder::<16, RustCrypto>(salt);
+    let padder = salt_padder::<RustCrypto>(salt);
     let hkdf = np_hkdf::NpKeySeedHkdf::<RustCrypto>::new(&data.key_seed);
     let cipher = build_np_adv_decrypter_from_key_seed::<RustCrypto>(&hkdf, data.metadata_key_hmac);
 
     let len = 16 + (data.len as usize % 16);
     let ciphertext = data.ciphertext;
-    let err = cipher
-        .decrypt_and_verify(&ciphertext[..len], &padder)
-        .unwrap_err();
+    let err = cipher.decrypt_and_verify(&ciphertext[..len], &padder).unwrap_err();
 
     assert_eq!(LdtAdvDecryptError::MacMismatch, err);
-});
+}
 
-#[derive(Debug, arbitrary::Arbitrary)]
+#[derive(Clone, Debug, arbitrary::Arbitrary)]
 struct LdtNpDecryptFuzzInput {
     key_seed: [u8; 32],
     salt: [u8; 2],
diff --git a/nearby/presence/ldt_np_adv/fuzz/fuzz_targets/ldt_np_roundtrip.rs b/nearby/presence/ldt_np_adv/fuzz/src/bin/ldt_np_roundtrip.rs
similarity index 68%
rename from nearby/presence/ldt_np_adv/fuzz/fuzz_targets/ldt_np_roundtrip.rs
rename to nearby/presence/ldt_np_adv/fuzz/src/bin/ldt_np_roundtrip.rs
index 69f82c3..27ee8aa 100644
--- a/nearby/presence/ldt_np_adv/fuzz/fuzz_targets/ldt_np_roundtrip.rs
+++ b/nearby/presence/ldt_np_adv/fuzz/src/bin/ldt_np_roundtrip.rs
@@ -1,4 +1,4 @@
-#![no_main]
+#![cfg_attr(fuzzing, no_main)]
 // Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,35 +14,34 @@
 // limitations under the License.
 
 use crypto_provider_rustcrypto::RustCrypto;
+use derive_fuzztest::fuzztest;
 use ldt::*;
 use ldt_np_adv::*;
-use libfuzzer_sys::fuzz_target;
 use xts_aes::XtsAes128;
 
-fuzz_target!(|data: LdtNpRoundtripFuzzInput| {
+#[fuzztest]
+fn test(data: LdtNpRoundtripFuzzInput) {
     let salt = data.salt.into();
-    let padder = salt_padder::<16, RustCrypto>(salt);
+    let padder = salt_padder::<RustCrypto>(salt);
 
     let hkdf = np_hkdf::NpKeySeedHkdf::<RustCrypto>::new(&data.key_seed);
-    let ldt_enc = LdtEncryptCipher::<16, XtsAes128<RustCrypto>, Swap>::new(&hkdf.legacy_ldt_key());
-    let metadata_key_hmac: [u8; 32] = hkdf
-        .legacy_metadata_key_hmac_key()
-        .calculate_hmac(&data.plaintext[..14]);
+    let ldt_enc = LdtEncryptCipher::<16, XtsAes128<RustCrypto>, Swap>::new(&hkdf.v0_ldt_key());
+    let identity_token_hmac: [u8; 32] =
+        hkdf.v0_identity_token_hmac_key().calculate_hmac::<RustCrypto>(&data.plaintext[..14]);
 
-    let cipher = build_np_adv_decrypter_from_key_seed::<RustCrypto>(&hkdf, metadata_key_hmac);
+    let cipher = build_np_adv_decrypter_from_key_seed::<RustCrypto>(&hkdf, identity_token_hmac);
 
     let len = 16 + (data.len as usize % 16);
     let mut ciphertext = data.plaintext;
 
     ldt_enc.encrypt(&mut ciphertext[..len], &padder).unwrap();
-    let plaintext = cipher
-        .decrypt_and_verify(&ciphertext[..len], &padder)
-        .unwrap();
+    let (token, plaintext) = cipher.decrypt_and_verify(&ciphertext[..len], &padder).unwrap();
 
-    assert_eq!(&data.plaintext[..len], plaintext.as_slice());
-});
+    assert_eq!(&data.plaintext[..14], token.as_slice());
+    assert_eq!(&data.plaintext[14..len], plaintext.as_slice());
+}
 
-#[derive(Debug, arbitrary::Arbitrary)]
+#[derive(Clone, Debug, arbitrary::Arbitrary)]
 struct LdtNpRoundtripFuzzInput {
     key_seed: [u8; 32],
     salt: [u8; 2],
diff --git a/nearby/presence/ldt_np_adv/resources/test/np_adv_test_vectors.json b/nearby/presence/ldt_np_adv/resources/test/np_adv_test_vectors.json
index f93ed7d..9c038f6 100644
--- a/nearby/presence/ldt_np_adv/resources/test/np_adv_test_vectors.json
+++ b/nearby/presence/ldt_np_adv/resources/test/np_adv_test_vectors.json
@@ -1,9002 +1,902 @@
 [
   {
-    "key_seed": "CCDB2489E9FCAC42B39348B8941ED19A1D360E75E098C8C15E6B1CC2B620CD39",
-    "ldt_key": "2E5F784296A2ECBB93D7D6E691541F610D6559D5A71903DD80E9FC84659DCAD60687E41330962A9499268ACE1F57857E35220D25B6AD795F9AEDC1A98D948937",
-    "hmac_key": "CAEF6DF5857EB67D8D3904D28AF752DCD3115C3CD7C37ECC5076ACE2FAEA5931",
-    "adv_salt": "32EE",
-    "plaintext": "CD683FE1A1D1F846543D0A13D4AEA40040C8D67B",
-    "ciphertext": "04344411F1E57C841FE0F7150636BC782455059A",
-    "metadata_key_hmac": "DFB90A1F9B1FE28D18BBCCA52240B5CC2CCB5F8D5289A3CB64EB3541CA614BB4"
+    "adv_salt": "837A",
+    "ciphertext": "A3D54A03A1464A5D99ECE37D911D52B4E947D4089A",
+    "hmac_key": "BE092602F20D6B4EC33E2B9AEF678C2B023EA9FAB898301BEC13C38FDBFBDFF9",
+    "identity_token_hmac": "D93A08B8D832EA27B73E7E968A91B6C81B0DBBD4FC78296A69F47D022B64B08E",
+    "key_seed": "B79F74DE4ED9D43E3FF020A41E977AD5A323E42CA0BE566CB760E020B375840A",
+    "ldt_key": "5EEA9AC9AD55DDA8AF8E9C58A2A0755C3F14FC1A64B1EEAF7685F414841570A3218AD128B5BA7854ABA0F57F0D0ABE8A49A735EB6FC71761D8230C4B05FFFB39",
+    "plaintext": "240F027310713775CC527734711817A03BEB8E56A2"
   },
   {
-    "key_seed": "7673D9F05DB9AEA7C7C794A24AEF8275384CECB1149B24B9150F56FAF35E3738",
-    "ldt_key": "BC059EC24647A31ABA5FA99A6254868E422D26478F5D5D3BE65802D04353ECD1FF8FE74F36F7CB58FFC2DE87CEF1739E6EF6D64B3BB592F49BEEE2EBDE83F673",
-    "hmac_key": "B78D559004B09975C2F52F5CACD5D4531A24AADE730E1B37E01BF8DD7D5AFA92",
-    "adv_salt": "E578",
-    "plaintext": "4306ECBADD6023CAA503600AC716EBDFC97EC8333D1CDC1FF91A76",
-    "ciphertext": "877672F5A7AE17B57DADC197A8EC2D20F7BF14472AE73E8842A0C1",
-    "metadata_key_hmac": "62DE26DA7D947434D0C371205398FB2A1B960B34AE226ED73AA118AD7016AF73"
+    "adv_salt": "96A6",
+    "ciphertext": "44C8C117FE2D5586022C937EF257D5C4044BFACD7BA9E87B40",
+    "hmac_key": "139AF9A6201E31A30F7FEE1F30611D38E972E9D184A1FCDD44BDCE63CD9AB49C",
+    "identity_token_hmac": "A36DCAE0D2A6653B48D3F5011575E9068125B1DF6C881E3BC7DB6B159EA72CB5",
+    "key_seed": "B500DA3B332D2479A6D8DB6E7D15ABF6B4FF4B904BE5F80AFAE122195A799FAB",
+    "ldt_key": "45C63D99AC38E54FB4FDB53DC596B454D76CEF966B599C27419F4819FC9810CD87312C979C48ED6B83C53125C9EF55192458BF679541E9861E8E49A5FA6BAF6E",
+    "plaintext": "5403F6847243EFA04FC14888589736F0A5679472ACB8B13E9D"
   },
   {
-    "key_seed": "A07E28586F397D9C8D2EBF5E8236647FDB887D15772BC7E3C0C85FC7754F5C4D",
-    "ldt_key": "EBB039B779FF16AC0AF67D799B882107A694F689F19CB85E1C847CA34A58344A3581D059BDB0F0A7A0C34D19539E45215F4CBAE183B9047B34A347CBD3017F1F",
-    "hmac_key": "AE83401E6174D5F087EFDB1E0FBB7D77808BE24CD708F3180036BBA298C76268",
-    "adv_salt": "4D27",
-    "plaintext": "EF34E725EDA3C6069C9B63C5E8000C3ACA9A027E",
-    "ciphertext": "33466B24B8137296608257C97EC181F1EB574F04",
-    "metadata_key_hmac": "5EA6CB73502B1419F1BC85D04E73EC72889635F972C8C221FDB0F0EC1CAF09CF"
+    "adv_salt": "07B6",
+    "ciphertext": "E319FCF0BB99BBFC0777A05AB272EADC7F0E821AF8C338",
+    "hmac_key": "38E785461FF16DF3B7863F0F96B9D74B83F2A8525FA6A4A2F1DB3221EDD9F3BE",
+    "identity_token_hmac": "B1725BEEE1B8C61FD200FB13BE3585F39F9EECF6DD31E35C0133619B56ED048B",
+    "key_seed": "A077F988C1B6EA01E5D963A4E2A20D80FD488004E38883EADE3718227002FE71",
+    "ldt_key": "E2CC5F34FC7CF7B12D2A79ACFC59EA4BB129BDA2D56F538C4932A890A814567991FC5187ACFCC520744AB24DBCB30076D71E5B2A9F412F040D6BBCD20B0C758E",
+    "plaintext": "9745E73B23263894A11B0C1AB144CDDE85EF145BA18CAF"
   },
   {
-    "key_seed": "71F35AB829BB1E2F7BDA59A65BE55741F7FC87BBDE81D6B10D85CB6557DC47AC",
-    "ldt_key": "5960D8FED51947B2ED92B3F268DB6641A508629E747923B91F67C3F8426895ACAD3800AAA2F72D4EA3D26D1C620D2D8EFDAFAD7CAB0516F0DE82D1C8E5C0826A",
-    "hmac_key": "3E14DEF966F921D933E3625A8C11AC4544D5A8A7882F53C67BAF9243DD1F7B2F",
-    "adv_salt": "4330",
-    "plaintext": "F46197691BD5D241DEC41D8CE7B4B58A932446DCD3",
-    "ciphertext": "57642D5ACAFCA0DAD6B1DF1BEDBF11D3282CD1B822",
-    "metadata_key_hmac": "ACB7C8AE37F526CD581783E63043A48FF823B094D0AA5E6311FEBFC4D8F57CC2"
+    "adv_salt": "B673",
+    "ciphertext": "D788023D9DA9237C2DDED0AAE3BFAD1DF9D1EB2641B3426776C5",
+    "hmac_key": "69C7E2FBEEF2D39029CD4C12B38F8BE0531766EFF1C9D8D341E0177F7708B7B5",
+    "identity_token_hmac": "D63CA07B22D9FE3D0F3FCF5A0924D73F21646215F366F6555BCD230B5990E2FB",
+    "key_seed": "1255F9C1EF194FF42AA4D890AE745CE4944D18F4E6F52672E808FA77CDB5149F",
+    "ldt_key": "A21D10CEF0130B8F6CAEF9A3BA2D6549440D008D9112A4289AAA128CD9E0536DE643AA593A485A898EC7113853ECDFC283DEA363C8AEE35DAC38DC77233FAF02",
+    "plaintext": "5C0EF76123617A69862568D1B3822767AF785A37D4834D7CA4F6"
   },
   {
-    "key_seed": "8A5DD2068ADD99ABF9FF0020A1728AAA51DE020711BC6825D034764CD622E0DA",
-    "ldt_key": "2698F62A3F4AC3CC2A47E779BB9C11C1C56ED1C47B2BDC3DC4A1607B91C02472D2BDEE84359CB7E8DF1539CBF87772374E4DA14B2D860FCD581780CB5E86E410",
-    "hmac_key": "350C4F03B87A73C299D4875776CDAAFA4BD62BF0FF671A8DEB624844B413B1EA",
-    "adv_salt": "87C9",
-    "plaintext": "2FBFE59B984F78E52858AA5F954302FA039A17DC9B",
-    "ciphertext": "045CB170393A527E4C27BCF5F0B5CC1C8ACA416A62",
-    "metadata_key_hmac": "549162023841BF6155AFB443FB966C4173982D736ACC036E4EAFF0934806853B"
+    "adv_salt": "1C75",
+    "ciphertext": "2AE1434E1D7E5B3F8F472E2CF8B2534CFF2A1B9A12",
+    "hmac_key": "10F332744FCFC9703953BA2F85C33CE1EA943E45FF43A48C78BE7FECBD34E338",
+    "identity_token_hmac": "D8BA9FF93E085E6929DE053FF5E297DD75B152ACB8AA9B44097331FE43BFB383",
+    "key_seed": "6249B433AD883CC6749400BAC62FD94AF309D2FAA3F0906E7AF2743B09E842F7",
+    "ldt_key": "DD7AFE6BB2C1216B81257E09E1E7C06E65A63B9C24A3AC1A5472B79CF264DB986381B58BE21BE91FCB55A40F74A563E50D65768603574E7778663528109ACEDE",
+    "plaintext": "9C0407828591BC9853079B1054D2A10C606572E71B"
   },
   {
-    "key_seed": "7E100C7A639AEBCA822893923FFE2D8F27A7F2488FE0A5BCC7275B3A02EA4F03",
-    "ldt_key": "09119B7782E29A680A068AC19D469315E4381A063FBE8FE921BB20EC6C845E883CCA820D159C824194E53281E5228B1C3438A64B8DBADFD1E514397E4AC877C8",
-    "hmac_key": "69A75B56EEAFEB03334758B127DD700E4A40517C4C6F9D2C43E35B9948615807",
-    "adv_salt": "A2D2",
-    "plaintext": "AC3A74E6D924FF68ED4EFB0CC6C5E78F",
-    "ciphertext": "15707F19DDB011093048FA78B5411CAD",
-    "metadata_key_hmac": "25100413FD7F97F99F5E266A7B240AC5F77C9009D52E488D07B08665BE97C4F9"
+    "adv_salt": "51B7",
+    "ciphertext": "F66B47B187370914A59EE09EAEE4316E932B",
+    "hmac_key": "1B6460F0A60C325D5DE22E82EF4D002C6A323B6181CC3837B7A934CCCEA0EA53",
+    "identity_token_hmac": "F611A97D2914DAF39110CF9456CB281A384FF5C62DEA8416F45F49A464F726DB",
+    "key_seed": "777D1B46773DD5BA813A18AA35EDC68DD3D745C09FC265EB9C576B66C0C45168",
+    "ldt_key": "EFA36B82D474732A8CDA1A1388B8AF2C49CD1B1A5EBA2966BE98BCAFBF86F265CEA44C1F7AD32DB2C9DFFD58D2E7E2B377644B3DF806E180ADAAA1B98AC24247",
+    "plaintext": "B4F9CC4D7AFA0805B9AF2D196F690C1622B3"
   },
   {
-    "key_seed": "B109A2FD9FCC6ED5F0E4AC288C13C7D2A019CF1C3E74015B2B72BFE412FF2205",
-    "ldt_key": "313B0D71CFC77E9B6F5D504473C0C2449B8AC600D0B0A8E138B24C4AC25293BDA7C061B39529DF9FFC8DB8B0924D6426E4ABFBC198E0D7B0B95F6B6EC1EED9DD",
-    "hmac_key": "E06EFB927FA2EAEABF0ADD366231BE98A798232E117AAB23A43B7093C4F0B758",
-    "adv_salt": "A39F",
-    "plaintext": "34FCEE9C0B0D3DFB623FD68221220EAB",
-    "ciphertext": "AF802195A59AF7DC3675757026789B67",
-    "metadata_key_hmac": "D16684237542761E91538996F8C9F732D9E4CC2BF2E0641514C004048E2246FE"
+    "adv_salt": "75FB",
+    "ciphertext": "7778EE076A15789D61B2BBA7B70A7AD47AE2",
+    "hmac_key": "C7D207E1C26E848C1E06AA795504A9CFB7B19E1F6941924405A110AD9F600487",
+    "identity_token_hmac": "DBDF585FB25FBBF5577B43718F750D2DE0624835FB0C04D1E994DB0AE168EFE1",
+    "key_seed": "2E4D649C4150E4BBBE5257EDF4EB7D822F294B1545E636174D5FA9130166D9C9",
+    "ldt_key": "E83DFA126AA19B06E93F9DE23BF9B5E900E8179318E6D82F2C3F9A29C306DAAD280C8780DDFC28F0AD2C21D95342488771778C31FFB27D86A73CEC71928B5B66",
+    "plaintext": "CD6CC0B8FBB7A945D6CC1251C41BBF78B09A"
   },
   {
-    "key_seed": "7650C00799B0B33334FD2328E66632159F0510C206D9476CD7457B88449AD908",
-    "ldt_key": "AEDA9657903EC6D5258CF7EE66853B9AC85962B7D2F9429A2B09013C6FF5C9B1931F73875822E027913F0BD0D00E73086193040A108958A66136528945DB05F1",
-    "hmac_key": "8A4660095102A7727A50E6809C2FBE0AF359003F68E47D9CFA3AA7D4B8C1CD5B",
-    "adv_salt": "7127",
-    "plaintext": "E8E00B596CA5207090D29EABA3336F77EDC9916AB5D325A2BB798CFB79",
-    "ciphertext": "94F71B56EBD8CF9829285FA6282C2A4FA781D1F060B7E46064EDB9F00E",
-    "metadata_key_hmac": "545549A7AD83A0D60D5031E371BA1F90235606CE3C8C21469FAF2FA06147D112"
+    "adv_salt": "6B06",
+    "ciphertext": "5D19BEE1FEFC66260CB85107F192824DEB52BD887A5B",
+    "hmac_key": "8B4CB5A132FE3FA569ADCF4E6D3544FF87D9A3685672822A4155E4C5541C477F",
+    "identity_token_hmac": "87F4AF538B304AB7423E7C6DE66986CB0E3B0627288B12B5B6DCFE158A77102C",
+    "key_seed": "D6FA314EEA8A2BD61C0026516EE4BBE03620F11013CD17BF19BDECED48DAD396",
+    "ldt_key": "6E37C54D3A143CBE7B690F3659BEF8CAA0625960D88AF699DB29DF90C5736DA73B66D21C27862AEF984590069B4AE542156C381B864A53EC0ABAC796027568EC",
+    "plaintext": "2645CE96C6195ABECA54CBB8FE9F5EDE303E3C5CDF49"
   },
   {
-    "key_seed": "4BC857AB21F85D7019DF55B6DC04F222274369A6825692AC9D63BAC25EBEF47B",
-    "ldt_key": "339C8EBBED3C88C1DF8C225F355E7FCA833874458F3CEB85BFBCFADD4F8C7AA04F860152E98A9E89142EFBE04A361397802464894E4F2F5B927A9750C8FC78CE",
-    "hmac_key": "978FDC344AEC925814FC8E3F1663024915C9F2A95710F44A26BD4B2D4A7521F3",
-    "adv_salt": "5669",
-    "plaintext": "037BE6930684D4C235D234CA7DA8B515",
-    "ciphertext": "BBB7E9148422FFBF79C506B00EA1F86F",
-    "metadata_key_hmac": "E82B43F4CACF8E3D7047B484EAED8BD8FD772ECA7C7BA18FAFD4B6C991FB0723"
+    "adv_salt": "858E",
+    "ciphertext": "43DFAFB88F737CD8C82C984CB63BC53B98",
+    "hmac_key": "CD7244FA9000586CA5C83A99ED4DD525A73847DA15403371C6A78467A30862E2",
+    "identity_token_hmac": "DD77C9770E7388701DEC7C9DC97B7225F19F06784F61E4E263B746EAAE9667BE",
+    "key_seed": "21AA1006D01D948D7F3878D0CF6BDA2C9FBC0CEA090147BE500062A8D1929620",
+    "ldt_key": "C39D2E1F194BA9BF4055F389AD3AF0F55366F803477CEE608DE018F0166793400EEF3F04DF9CD38AB01F39095E7F6CDE09BCF245EF38B91B29CEC130EA6277BD",
+    "plaintext": "E6978A5DF9C6EA4F57C8C0FBFC5310B6B6"
   },
   {
-    "key_seed": "B0312EBB99279933724BCAA461A59F19AA0550107076D40297E3D91A64D38864",
-    "ldt_key": "356D1D2EDF489DAEB0263916DAE62352FBD32BBE14DF306846784670B02EBE9B51368877E277A914CEFAC15CF3C763AF5EF0FC5E72CA2E83D994BD8F6FD93E14",
-    "hmac_key": "48FE4A7DE06D21ABBBA9CA919C7F339C0D6495D38F8137FFC57FE85ECFA396C9",
-    "adv_salt": "FEE3",
-    "plaintext": "007455C803830AA1122787EEF4FCD71F57C29C06",
-    "ciphertext": "E5739DD2B3172E84F5FCF17EC6C92B3BBD3F86F9",
-    "metadata_key_hmac": "575F6793DC05B2184C21426FF985064CB2E6EEDFDB051C3E7A61E737E93BDFBC"
+    "adv_salt": "9D72",
+    "ciphertext": "3D546469EAF0B3CD40680CD1882A0BCA2525061690554DD3B0EE22A2",
+    "hmac_key": "93FE3B606C24EF0977399A2143DEA58BCC2804ADB51BCCE9033C3BD3B49BEF56",
+    "identity_token_hmac": "EB660374A3AC93ABB905EF49D6E0DF3AB2BDE95662A71D415B18D6D337DE76D0",
+    "key_seed": "DAF10E64CA62A68825D4FDD1610D1EA3F73DE73565F5F6A5EEF34F079FAFFF31",
+    "ldt_key": "1828E0C0EC85EAD87246E8ACB196C2D4E27F00B9B05D96942C95E3F90E423FD120BDCCA4C851BAA87D6F70B1F09C90E7C01C56499B8F7EA3EB066C8993317056",
+    "plaintext": "DC107CD0B5CA7D542327B0BFC785C41AD1F0E2EB6A4110361D276674"
   },
   {
-    "key_seed": "A6C270332D6B8DA4419490908DC76D6AF162E819BCB44C69BCB6B093C86025DB",
-    "ldt_key": "C30EC3DD334F09F0618822C3E1D93D2C59EDE05F15712A173D7CDC9738132A441FBA14AE65D6D772332B1CC80C3DC880C232A39258A21A07D3D613D0DB1958EA",
-    "hmac_key": "07F6BD1301DC800AE625475240241CEF294E0ADFE29CE4645295DE28644C7468",
-    "adv_salt": "E79A",
-    "plaintext": "69AED23A21571027A99FDBA8937B3728C7C8FE22",
-    "ciphertext": "655793F9E26F92328504E2B1746C35D060D57F47",
-    "metadata_key_hmac": "72F10799AAB2A3D1CFD021582F92EEF45245914E540073B0BC950D12DED27C5D"
+    "adv_salt": "0B4F",
+    "ciphertext": "745859D632D7B1A0BFD118D13B85B1BD",
+    "hmac_key": "6CA7661C2D4B90D3FB24F85C546A2DB1BBF013C3549B522CF6043DEBE145C771",
+    "identity_token_hmac": "0DEB10E261D6DEE81268BA4AD15E41D1FC6C4F04E73F12742400D3D2B5359C5D",
+    "key_seed": "D049B2FCD0CDD4596F93D9D7B868E88B3EEBAEF43E61A9FAF9158A6150A1EF5F",
+    "ldt_key": "B78A86927FD21E35A74BBF2CB83826D0F3C3ECC36930EC8C995597CD2EC6F1330B8D82E2F042C28B2493AB37783596F98AB85BAFCF4764201EDD9922B1E773F4",
+    "plaintext": "6FCE05ACE2E2B53392362B97920C3E12"
   },
   {
-    "key_seed": "1B3DB09F98B49E09FC7A7712DA6975A435C9228A8A10A4B7100015B1BF2A9170",
-    "ldt_key": "D7E460C842C14011B55A3E5D1DFAD8EC4D1751A06687A6F3038FE52F9F900C9D01CD3907531209C5699E7611A1C690854D1A83B4B4CD468EBA6C5C7A1BD9E433",
-    "hmac_key": "D62A374514FF4D010C6C706F71DD837CBB77204E8238133CE41DCF6C3DEEAD30",
-    "adv_salt": "2EC3",
-    "plaintext": "91C3CC39C1DFA0A98DDB8669F8BF4100C85EB809CE6E96A23A8F76D1ACA2",
-    "ciphertext": "3575C1D1B970E2BAA292F39FFB0886B7FDA0C8F7A6ABFE2E4C23186F9796",
-    "metadata_key_hmac": "442509E2FFDD39BDB85EA97040BD519CBACE8B49971B68D7E06F058A87EEDB89"
+    "adv_salt": "DB70",
+    "ciphertext": "9F6ABD20EBD91A3E4927662D302423DAFE74ABC3",
+    "hmac_key": "330915EAB5F5E75DC6747A893474BACAFC7F116FA2E693DEC2C7DCD6155A2FEF",
+    "identity_token_hmac": "D674C7AC837A05191E705C9ABF59C9456F8BE9B1A6B67910D5469ABE61403905",
+    "key_seed": "0961CBDD12A7B644E7B9E702271E4BA6017F429A6DCA110FF326CAC98B37AD5D",
+    "ldt_key": "7CFD66C5CA07AC6A4D530CA801DCA0338448322A3BC5A2B56BC028C0F31E5BFA5D8098DF92F6062214CC8A4E421F873B1388F26F4A7F8D97123FA9530A8F9B32",
+    "plaintext": "479F535BD5B82ABB82E5F014CF3865BE9453DF17"
   },
   {
-    "key_seed": "98C8FFD6BF66DDE36B52F95264970F3ABFA03546BB9E42EEAC73B861C4668EC8",
-    "ldt_key": "7D0C4847D29D4110156447E82106A97715A6DD664BDF3B4262FBDCAC71C6D9ECC87D7824B1E65E741DDEFCF9BDD149C9B127C7AFA9E1FD73CEE66C441CE257EA",
-    "hmac_key": "17971EB7FCD0281E0C5CE05969BDB40EBF768CB8B73E816DAE761DEFDE5C569B",
-    "adv_salt": "D5D8",
-    "plaintext": "2BC85FB9B4B8597E0B7E02AB5BB183F4C0",
-    "ciphertext": "C50688FC3AD46A9C15A5C9818864856D7A",
-    "metadata_key_hmac": "44DEE13232B3537982D17A0B6EAD9BC8357C00A7C590D7AB37B6F9C7BDD259F3"
+    "adv_salt": "F62F",
+    "ciphertext": "2E39B8DE8CEA7523627609C0A76C1CCD3E2122F82ACB2E7852",
+    "hmac_key": "04322B90B4ED3D3621EBEC8E27E4683A67BA2C82041403D26035E310A32F9278",
+    "identity_token_hmac": "AAB5BAD093219054629BB37AA41D83E34E4445CD4057D056858DBF8EB4397034",
+    "key_seed": "9B3A74F8DB6BCA0BD09AFFFE06DD78211AE9DF8F11A9F3A3417B39C2D01E4AE6",
+    "ldt_key": "1CAF350FD51CBE30E00A92B2198A17906F0AF4D45236595A5C54903C666744DC4DCF78FF098677898659E5987B35E6C573C3DE83750C29EC69853C3E4C353177",
+    "plaintext": "9D51CE24875ABC6F43E164ECE4679180826009C2D183A261AC"
   },
   {
-    "key_seed": "1906EAF9DA10460DBC7CAF2DA723C0BD4A087462C162BA91FC9BC9C4B0D91264",
-    "ldt_key": "06E58CF7DB917DAD1A8FC436B25FA4860AF9B0D8E80362499F2F80D294AA0BD88F070E0DCB8A9D801B35EE4FB587955B751658B9717596272CDCD7BE1DEB7D33",
-    "hmac_key": "891198DB242A226E7D1BE5B08E5F786D38AE8C8FBA009EBBD8ED26DDBCD7053D",
-    "adv_salt": "A352",
-    "plaintext": "DB729216056241B8A2BF9A9A362DF2DB5F",
-    "ciphertext": "B75F7873302FC7FFA4AD4FB9A320C96F43",
-    "metadata_key_hmac": "B26F69630F29582F0516D5CFC12BEA2380CFCAE49FA987A491633AA9E60CA89E"
+    "adv_salt": "1DF7",
+    "ciphertext": "A5B4B307838B1B761B52ACDD98AF4D8867DDEB43BC3A569D",
+    "hmac_key": "6EAD3FD46FF8E8FD4ADFA97D709120A07496FD0D84142C956EC9ACA2E35D7F0B",
+    "identity_token_hmac": "C5945044702D6DCE67A45EA35FA28C217C80A952C8A5F080F945F9B5E98C6394",
+    "key_seed": "CD657B540A64750A595AA0F644B5876CB07BC81137CD11CA44484CE5183F1993",
+    "ldt_key": "779E316C081F91AAD914EBB6562A7DAFC2A9B4193D92B4888BB72925FBC5D31DC279AC22E56575AF1F77DC55513EA0A0A02F248258A0799A1C7E9FBFF4A8F9E3",
+    "plaintext": "067C8183FB8641294C5BD71D2CC9384B27448DAA7ABDCA51"
   },
   {
-    "key_seed": "C903E4AD3E9CF458BFCCE2E1F5AD924523C20A7F1C73FB6A02F04C49D95A6066",
-    "ldt_key": "8319E13B431FAE7314B09BD90C628C05F469877FE0F245D84A6C3670AD60C65568B6A2CE61DA831A47C53AF8BDFABE1A6461899CA0ED14CDEA3946FD0DAF302C",
-    "hmac_key": "DF550EE4AA78C5DA0061C698AACA07A6F2D00B041554DA56CAC1BF42520EB77E",
-    "adv_salt": "F5DE",
-    "plaintext": "6757E541F331AE07400C295A4712FA634A712EA86517B559862FD6731E44",
-    "ciphertext": "DA74D9D92E47043556FD82FA4D22E46D18A8A9A2313E2BEC9FB9E8D78267",
-    "metadata_key_hmac": "0AD05442F7AC6ECF628844E9C3C7046FB926709CBC6519EE5775DE03A42CD811"
+    "adv_salt": "C04A",
+    "ciphertext": "FAE99D33CA11035E83B25BD44716745DC09E9B93",
+    "hmac_key": "152204070190AC7162FE20CE7F87B93854B7B3619E424A3FBA00362444CBCE09",
+    "identity_token_hmac": "941D8591674C49951D466350D2693FC2B3676EBB7D7B571592C4A0348084F6A4",
+    "key_seed": "CC962B750300AF9F8E3A0815A2CC2D9382B91C4DACE92F84E0E90B5910A205DB",
+    "ldt_key": "AC57494B5908F709C98B61947D619007CC5A27E8F195897BBB6A9B3BF446FB36732FDD80C6D3C7C7462AED8D6E8C2E0D3B24A0F3C8A3D4317FC26E56FC446E6F",
+    "plaintext": "CC2CB221F08C8264834D8828F2C38F8CD138742E"
   },
   {
-    "key_seed": "BEADE49E784716A7BEC51E332BD5540A25C1CE71A278A54CC07B649D048A5561",
-    "ldt_key": "BD54BC42F56935AA1FC5D2A8C76294DAD6E40F00FC306BF33F29C0866B1B5F5F1261F78B08B6D61736DA480626DB27E07C9FBAD41582C25B6EE92EB2394163AB",
-    "hmac_key": "FB9031E5428C831BD0969F4EDE7CBCA5244142D9EFDDA4A03FEA452D25A0254F",
-    "adv_salt": "AABC",
-    "plaintext": "63EB8971511A43049DFD7EEBB8075D82521F3654C458C3B5149DB1",
-    "ciphertext": "C3A855294A2AEB154347758C6FB052BEAFC2789E60573AE8CA80B0",
-    "metadata_key_hmac": "E5FA9F69D44C7E68DDC4F687C6BCAC9EA78690233AA3C545CCED835E7CCB2021"
+    "adv_salt": "DDC9",
+    "ciphertext": "768AAEEB82FADA42AAAC879A4A3F64DA",
+    "hmac_key": "E5B3B593A79805E7D13F67FD2DFE1C3F370C4DF341522BC15122C66A457EF9A8",
+    "identity_token_hmac": "0EFDE1DC85702380F1E80C31C0CB20CC1A84ABDA047984E0BCFB85457A913540",
+    "key_seed": "E5C94807F0EE9585721AB18CF7202726A9988C27B18B4BC3B8965B65425D1EC1",
+    "ldt_key": "860D05C7AE3D48EE932CC244F644579259E3B8A5876919A62AE0AC64BAEC10A1F3FF69031077DE256D611D210F39DF21D5354A92020C67ECA7AA107157582897",
+    "plaintext": "84912B83445F4F172AB424DC4B5866CA"
   },
   {
-    "key_seed": "F3916DA3C3328FC1271463E1C44D533C27C59D7C5CF9C7433E986605EB958E70",
-    "ldt_key": "02EE10F2D441AE3C5C31E1872BB8E7C91D86279E9F8CF51915FFAF05F58A4F1C1CCBE91AF3BC8D4F61F4ED33F3D7C24B91D7DA9D8999F6576BC001307B260F75",
-    "hmac_key": "8D50A24DDD4DF78A0D360B9A16356C73ED7C61DC8371B8477B9C34B09FDDE9A9",
-    "adv_salt": "CAE1",
-    "plaintext": "99EFD86FEDD4AB164A959BC0416802258BA2B968E3A52D",
-    "ciphertext": "BA17B32F85E71560DF2F894B76661A03E51802D4674EC8",
-    "metadata_key_hmac": "E7F669E5B1A44A2439ED07F2743E15AE10B44C51F19E16C43FE4E2EB4346A784"
+    "adv_salt": "F1E9",
+    "ciphertext": "2EA0800CCAB683820FA2B6374C9A85F2B4F796F5C05E4E880F6A",
+    "hmac_key": "801BB36813FB520F44EAA47BB377F90F923E32C5FFCB86F0C05D377CB8BABBFF",
+    "identity_token_hmac": "FF06F63BA8B586C7CBA78803F3EDFC37961E6340D1CE30833593F7BB4993FCE5",
+    "key_seed": "C89DF8AE8A40E7D121F11725EDED654E9F148002061645DCC338E2A096F77309",
+    "ldt_key": "690F37B0BF9C0EDF8152BF5F604EF5E4B8A2158C78E8AE1A514C217D09334BF2566E50A786C8DBFB5CE89C6A831BF2BF3590EB227742D92DA64CD7E048A4ADC6",
+    "plaintext": "D501664DB8049B24C9361ED89794C035478F38D16F80E8A2DA98"
   },
   {
-    "key_seed": "C4317D4E906F9BA4539B813384B14E530AA69BFD2DEA76107FAD0847AFA8453E",
-    "ldt_key": "988195FD6BCFACD2A4EDA1ADB4CE9F562D2053D41E94763B816C113A15A4A05C3F9D952831023940BC66BAF00A8C347FE77E0438990A01D42951B23B9920F01F",
-    "hmac_key": "6DFBAD52E858DA32E6659D7173348B34F7E015E93F1F34DB6AAAC8DA11AFA82F",
-    "adv_salt": "2B7C",
-    "plaintext": "DD70CC7F846A0FC9EBB24F46E4D61C58",
-    "ciphertext": "6218162C76FA90C27845628EAE8E2F5A",
-    "metadata_key_hmac": "0F0C6BDCED527251DD706A3D2723FDF8E70A666E190755D5AB515AFCD3CE13B2"
+    "adv_salt": "9E18",
+    "ciphertext": "46161EADA1410BA034B67EB8706239ABB492802FDF8577CE9C2750A1DFB126",
+    "hmac_key": "EC60B45E22554C987304C8B8BAF5840B29031D9E3DA18A8930162EA90B5B09DC",
+    "identity_token_hmac": "57CB29DC2EF23C376C0C0AAD8BDC33502D595BFB4185DBC714ACCF86837B306A",
+    "key_seed": "ADEFF11EB08514AF099F3C8D3D87C19FDD13B0FA6056BE585555BEC6A15E3AF2",
+    "ldt_key": "5E4D969FE686B8E1BD8DCA17542F636E8A990165943637AE48C50C65B7164DC66A0CDFEF83B14D63410D46D6F4F05BBB87C9225A5B90123A4C7BB0BCB309ED2B",
+    "plaintext": "F203ADF87EBE1B2ADD0A2F234F016BBE73B6E52D93D7E2DBEFF4F7A2F3854A"
   },
   {
-    "key_seed": "3E702C13AEB7F8F75B8142978D771C9A6695F65E9D83724B600C7C504C06AD11",
-    "ldt_key": "D97054FE9C78D83EA387377C8A9DF47959780CF1A3AE864B618ADE48F6F4C74F8C5F1CCD8BB22F12B4BE04B1728C0859ED849B92D6AE9A455421AC22EC75005F",
-    "hmac_key": "A170DB24F77CEF208F83ABF01FA07B236D38FE276BFFADD60DFF5150AF1C5076",
-    "adv_salt": "E442",
-    "plaintext": "6566009789ADD17B430B8E31E70951A8917F2598FBDB63366EEDBBC0FA176F",
-    "ciphertext": "041BF4C98B853251F8FBAF522F25F3439E57A1E6F817F5D08AC0EECB10EC91",
-    "metadata_key_hmac": "2364F70676B650040030D6CF42B0E69D50FA56EA87C0BBA8B9127157531BA035"
+    "adv_salt": "D1BE",
+    "ciphertext": "F589135CBA5632A5FC722D7867B5830DC84E089085",
+    "hmac_key": "F618404EC6BF2914E16F892EBF50FECD660AB380AE7BCD791BE086C0DE8C1F73",
+    "identity_token_hmac": "B096CA6E7A55E87F1843B730E8BA39D849E8398D4D91F21BB61D349EA0B80CDF",
+    "key_seed": "2CA0C6DEA290320E9A19899BC25E7E04E7FEBACB862D092D703246DF905DE6AC",
+    "ldt_key": "5CB0D8CD3D5519B9633E59DAE23B9FCF9D118A771051D5A0DC7048D7F178011050606687E211DEA1DFF2E204FD79E4A177963CCD8304EFE8F2CDA6C59E9B5E7D",
+    "plaintext": "B6BFEDE720032795891C4487A1FAECDEAB3456A650"
   },
   {
-    "key_seed": "CDC2C95F7099A6BCA83A96FFAA9848A06ECDE7A297EED14A9C88ACEDD485C55A",
-    "ldt_key": "2E02B7A12A3F1280EBBC8B9B9E73CAD6D3A708F71335C21C9E690B7C6EDB221DFF27802E34A9FECE2886BEF0328AC0B685DF28CD591BC25DF5FC6F05AE0037D4",
-    "hmac_key": "E037D28C89419F303C3B22704B03B9565CA4AEB4A820E2A7DAE163AF3093F315",
-    "adv_salt": "313D",
-    "plaintext": "A9334CAABC7A3EEEC389B8366574AC31FF921F4D025E74326871218B36",
-    "ciphertext": "71F285C42EFBC9F80DBB857BC58B4B82CF22AA3E68A6B19845CA6EE672",
-    "metadata_key_hmac": "8EEA0E056F28756417FAFBB00619EF84C7AF7953423F697243A35B53A98C2B3B"
+    "adv_salt": "3FD9",
+    "ciphertext": "A0379898ADC0643A4803D19A8494F80452",
+    "hmac_key": "4E94A209650C7C46D8305F07B0BF0678D742B091AB2CC47B241483D2E161CD32",
+    "identity_token_hmac": "EA7E5643E1CEC8DCAB7CFAF7ECE09AB1790A3BED6B872CB7C7BADA3E181D60BC",
+    "key_seed": "4CEAB4B692FB0C1BB435FBA247C7171C1D18E60D5C8FCFD364A8016741DA64AD",
+    "ldt_key": "B4B8D87D8B72502EFDB340C8E090883751F48D855D0D41C74175C4B4F460ADE3F5A8DFD73431C78EB58B6E20203911B37450CC8CF8D759F3838D2676C7283835",
+    "plaintext": "5A02766D11BCE1529A159CD54BBED5EFF1"
   },
   {
-    "key_seed": "9E3A32C3CA43D7DA82838C21DC8D7A92B34E3029F2150EB6AE08E21E54DA60B5",
-    "ldt_key": "550C54BCE30C335EFC1B2CF3C69B6F36B199C11C48D8B82DE689245CB647C21FDD5697F8004D5D4409B9D7A5B5E4BCCED86624FBEB89C4E24D69FE614BB6F4F0",
-    "hmac_key": "84DCADE652FFB2082BE54A83C761B8D3BCB3D004847DBC22B7B4F56E1DAD1AA8",
-    "adv_salt": "22A7",
-    "plaintext": "E800C9832BDCAC3DDB8115515F54B42DF4F46A0642",
-    "ciphertext": "18C49172DE8492EC00FD766E94C32E65F9D94253E8",
-    "metadata_key_hmac": "C7F591B8ECAEE9E130E70B87D4F4CC6F0CF79781C11BFA3205DBB647ED8A6EB9"
+    "adv_salt": "D284",
+    "ciphertext": "59F8F5A7062564B2D2A76EF50896F901F64490FB4344",
+    "hmac_key": "8E821028E468700B4EAE999D7587758390881E984733B63F4266B09A79789981",
+    "identity_token_hmac": "77B6A66877A1F5F0B8D67CBF5149768D7D436215ED29C995927A0C72F7E59E00",
+    "key_seed": "CC127D438633974D6089E0070302248F4771920B69284BDDC10EB5EFC869AB65",
+    "ldt_key": "EA0932C453E066100CB5870FA19549ACC4470B59493DA3260AD7EA9992130B690FE7D178742D4D526EDC9126DB59E20905241DC065390E73FBF73FB35627FA3C",
+    "plaintext": "78AD3E63BF9931E2F0F0024D8E2581EC42E31D02E81C"
   },
   {
-    "key_seed": "208584B114FB579D651DB0AE915AE22A74161DA1612FD66934AAB98970B4F3CF",
-    "ldt_key": "18F509AF830D036179EE8F5579620233859AD012DCE5CC9964BF83AD1C8BEAC752A3F856066991F3F899D7CEE08F76943D0E898C73622FB6BEFE1635A752A258",
-    "hmac_key": "D8A0B7CEEB921A4EAF78C315DCEF3AA1A219D8207C2EB750CF5C9A5C057F5AC0",
-    "adv_salt": "9B40",
-    "plaintext": "643A11C2ED48C3FBFB1568646836D664D2",
-    "ciphertext": "ED655DE4A3CCA56EF5E45CD58BC6EC981B",
-    "metadata_key_hmac": "BA95BFE69F798B5F499736260A9E16D1AEFDEA921BAE567166C7155553DD2A72"
+    "adv_salt": "CCE2",
+    "ciphertext": "5721107BF13FCD455C7B1709A01F9117D1866BDC71AC996BF5F4E4",
+    "hmac_key": "217E8FB03E291C886BA12807F04BF334FCE42A0CE97BE91EE2E804B8154F179F",
+    "identity_token_hmac": "7FF0C58C0820FD3E27D06407B5EA6A2295FE6E6F3ABDF2216B46B12527EEE853",
+    "key_seed": "3E3E9F0D4675C1486C207E8F887189244DB317A7A5CB5200038BCA439DBEB43E",
+    "ldt_key": "EF24EA00E39981DE418DF10A9A51D01C24764ADC50DDA5DC90839B0553AF291C3FECFA46B2E9E65E0CF10754C59D10C230C2432F96572CDCBE8D061BBCD33F23",
+    "plaintext": "193A16F2383DE9F25BB8A9CB1113F5E52CA38DB8F6C3CD45025D05"
   },
   {
-    "key_seed": "AA182BA6F55C8B6E248AC754E735F24C73089ED81455F7327B4FD32CB10986F8",
-    "ldt_key": "C6AF6AB4ECA397CA4C72F2231C91A31EF7BAE7402F214BEE91A26000A41C83AB49DE9F83A6DB14C1B7089B98CBD5EF56C821152A777A62A69224CD24C87214E3",
-    "hmac_key": "B3B94FD6E36ED7EB23C5FFB2B9FE39AA8E958C7968B54CC1374C3DE2CACCF83C",
-    "adv_salt": "BDB3",
-    "plaintext": "6F1112EC7F239D89826A71A35CFBA83A",
-    "ciphertext": "1D2EB0A2102A78B09B6FE01461420937",
-    "metadata_key_hmac": "901621B523F0C0C37593BD1C9A8992A11D6E00B308956B0A1CA1B20342AAF924"
+    "adv_salt": "1BCB",
+    "ciphertext": "37C9179727A7C1E836A404C48A747DBD75B58A1D23EFCE204D5F50",
+    "hmac_key": "8F2F4BE5B3169271086E83C71E9B9348ACD2912565174BBDB1262A7840F3E32A",
+    "identity_token_hmac": "2C386D3797A9EF6E15F6D90A7A5182C45ED100A6E8920AF8823B25C022FD6C0F",
+    "key_seed": "C81AF9A3D6CB7150C1E8008737E5ED33F6F088BDCA4F6A6AE7E6B563CA23725D",
+    "ldt_key": "CF078C229178D3DD9BF48F51DE27B2D1432250CB1B0F67F5D2ADBECB2BA5743B017705ADA1B6CDE56CDEEE13A7461FB9A47955444F1992FA2E0D4886F21927D7",
+    "plaintext": "1D8CC80CC19E18DDB69FFD214F36869A5B78EB94B027ABCC5104B2"
   },
   {
-    "key_seed": "6396EF1AB8B1AB63FB63917F29FED78EFF57ADD13B849349B6537878F1580A9D",
-    "ldt_key": "2D5ED0EF006E0D023A2D76051D936964DF8DCF63CA1E0B90A9AC4024E3544DEC4C1EA88718DE3B1B60845BC8915EDE331909415B024BC421BD5A4734ED06CD71",
-    "hmac_key": "BCCB2F43EEABA659F0962C885B69037243F54692244700EF83FC4CEB4CF47AFE",
-    "adv_salt": "4B17",
-    "plaintext": "CF7231679510559FB935294595824823",
-    "ciphertext": "6CFB394FA9522089E320871C70D84A65",
-    "metadata_key_hmac": "B1DD5AF143DB53CE62FE9488C54E581B16C31EA6FC2B68D6F37F6497D6A8FD10"
+    "adv_salt": "C63C",
+    "ciphertext": "0F6D4AA3A9D4749A9BCF40B1505EB05776",
+    "hmac_key": "10582CC54F02A033EAA5FC4C1A6247EE563A74D80EC6C476E026CB4F2D17956F",
+    "identity_token_hmac": "B5C7278B84491072B0A1BB498A2E0D16F9236693B481A71DB14D7E79C2B3ED65",
+    "key_seed": "3ECF1FC2E62DB5EDE18A4AE95C634824A9E6E81E750AB74696D102546E9BBBF6",
+    "ldt_key": "B33996937E2E37A41407C8ECFF867D3CE49057412D968E513BEE72602616D70D04B9A10760CA5B104D2887FD24E92C925160529C985D2DE000B9A437EB2C6322",
+    "plaintext": "D11094AD21CF9E7D6B40737BFE75D3FB57"
   },
   {
-    "key_seed": "CE60CB333D364A1D6915B37FBF1C92091829DE2B1B6B7B8FC4D1349038A44D6B",
-    "ldt_key": "63307367255949F902F770F3F3AA6AA0DB1034A93C9B6D177182C097AE3834F55BB44259D53296B3AA80A2A3A950DA6C705A39248AA4A53EA2EE5029A612C339",
-    "hmac_key": "B140C0E4E7FF5F891F795DC224BE23C376A6C2CA6E7828DB2A608B37F719C050",
-    "adv_salt": "49C5",
-    "plaintext": "16E6B82B12476FF394AD541550943BCD0687EA15B290F238",
-    "ciphertext": "2BFDC35E78862910FBB626C9A124242A0E3101B4D1B506A0",
-    "metadata_key_hmac": "6894A92C027E0D13A0A17F0A036D49621D8C092BF64B37A6D5D7F88719C34B1C"
+    "adv_salt": "93B5",
+    "ciphertext": "34B5B9320E5AB45ED9D317B281D5C506DC3377BD4F5E6579B71A4F",
+    "hmac_key": "B78802ADE1C1E235840299682F4A887AABE8C0C7CE302E97C09E43D3A4E63C42",
+    "identity_token_hmac": "FE0762AFB535721F478633F45AA82B45C8548F57EC506D3E7FC99B3987310ABE",
+    "key_seed": "4E4307B12B7D4AE374B141DA13BC0244F405590441087FBC4E4CB601BC8782C2",
+    "ldt_key": "9385F28378E997B357479AB99B537A917F4D7C8C035093E217498284F69BB904ABC851B91EE639134BED28EAA649C42234101B86380F195457AF14F758CCA0EA",
+    "plaintext": "C762C9CCA95E9058C382EAC3F85AEFCAF9577C2DB3B179B203499E"
   },
   {
-    "key_seed": "E9CD02A26F9C4AA593C8FA3161F71A36A5B01B5997CD37E03FBAFE255F0A9904",
-    "ldt_key": "82A0D8C3F7436C38DC56950A6BAB1295ED7AE5B20FD624B500E7FA2CD76949B47C6E7E92194A97D02D034F3FD963F3AD58F798D73B3CBE4E7FE133C033C7D2AA",
-    "hmac_key": "269411A0CA960F8EFFD49327974D759EC1C658FCDDA6EE313917010EEA06CCE1",
-    "adv_salt": "B23B",
-    "plaintext": "EA133D6192C98F803D46FA4D09EE84EDBB432A315378A6ED5BF6691B",
-    "ciphertext": "D85B0CA6B5E9A77FD2700CB0AC3D0C193A465F0E7CC52729ACC1A61A",
-    "metadata_key_hmac": "58C3CAF1EEEBF55F0D7D27BEF516986EF9253BE453A8487EA39F56CCB6D53DD9"
+    "adv_salt": "6D7D",
+    "ciphertext": "A811F10BA24142D7655D84A4CCDFEECF3B0773EF9D68156BC4BD4D77A30B",
+    "hmac_key": "11651BC94CBD0EB53B3B6A1797BECFBDE748A53174416F85E68C5501E4D7EE40",
+    "identity_token_hmac": "8E35E56A14AE67F069C07ACF655233CCE20EF2E475A9ED06EADA6FD2283BD2FE",
+    "key_seed": "48DA18F6C4A2E8FE8FD65595EE6EE30150F88DF47E6E5533D15235B529A02B40",
+    "ldt_key": "7D030CF23298B907E6C330364F7B758C7A875B400FE712F01AF511483AFB07DB068BFE744E17914360041F451AD9416B046D784A79BC4427C1CBBD7416F8F6CC",
+    "plaintext": "464436B39F78BE4FC6F322354DCBC1899F26292243F624814D9F3DE898F1"
   },
   {
-    "key_seed": "D3BC36A108E864BF1013B0DEBD7C9F340442341A5C70EF7A9E8C437D33433EDA",
-    "ldt_key": "99DD0595D3FCECAF7D98E0C26266CD1B04CA62DD2558B5021061A7F7E1D419A804547B513DACC7529952FF238B9B4720D62D4744B023925A88E5D3E24C3B26BB",
-    "hmac_key": "B3681FA6E9112A91DB33D045EE54F3C7B914FE9FFE835C03A38D1D039FB56AA1",
-    "adv_salt": "DBF4",
-    "plaintext": "3557B3C4BCEC8E7B0ACD0A0134C47494A460F799E821C675F3AD12",
-    "ciphertext": "449CCB6BB67FE23807FAE60469DB06C7D8E72C9364FF1310A27D37",
-    "metadata_key_hmac": "4BB084A1FA068300E490F67B29CDC91AE45DA0C70709BAEBD33BA231AA2B4CE0"
+    "adv_salt": "8E8D",
+    "ciphertext": "04EEE502D4F5AA60B94E0EBCDA1290264CEF1649BF30DF53A8",
+    "hmac_key": "318D9B3647011FCB71D9CF4BC713637895E073207CFFF196A3E6D770C9B6219B",
+    "identity_token_hmac": "B173B54D70C518BBB2A4C5A2A7334F2982577822AB1E2D487C365822BCD2A83A",
+    "key_seed": "A921F57C7C59EE2F89BF1F0EE7EEC865590A36BAB2DD2EFE3B3AC65A4354145A",
+    "ldt_key": "7B3BA48E4648C073143D02617CAD45493A55B77573662AEFFDAF23420180BBDFE152E8F954A9D43D85D3B3F1D0F35908AEF63832F1B1AFFEE991E4E4940761B1",
+    "plaintext": "D8E9CD99AA75B35F474AC5BE6D6ED95795D12C5AE8518D62F1"
   },
   {
-    "key_seed": "6DD1A5FDC69B668839D44722823CB1CBEE2827A78943761C2DC92349283BD34B",
-    "ldt_key": "1D3B333FB54E240DFCAA8892332A7E7323F93A4ADA114E9664BD7564969182EFFA6C193C0ECD2A67B18113E4CE254AE6C8350ED57126FC9A4D909860BA509DEB",
-    "hmac_key": "D73A2C7983617D6F346895DA10012E42A8DD8FDDA9D6B21F3B8A91546E638848",
-    "adv_salt": "A1F7",
-    "plaintext": "B4F3EBA80957B3EFFFED35FA1487CC8FDCB0",
-    "ciphertext": "4FA99A5587D446031AE0DD322E28DEA1CD3D",
-    "metadata_key_hmac": "FDD24B63C520BB33D124E74DDAC334175C00BA0CF350F0CC6D3CFD51A57A5B12"
+    "adv_salt": "C3E0",
+    "ciphertext": "24D350D1D9D2BC6AD3DDA958B3E7FD2F5821C858C5EFB70592",
+    "hmac_key": "E243DE29568404760BE59D1836D791ED8A0C78C7CAF4ECD49EE9AFF65BBE5076",
+    "identity_token_hmac": "E050BDFF46A32F0299A083563206F04F41ED6D9E4A8489F18A73E4209B41ECDD",
+    "key_seed": "9EBCBF72866BA9B742577C57F73FFFDC3CDEF13FB54257B36D95CCA1684FB626",
+    "ldt_key": "2CD45198DC0E1036046E67B0EE1F3899BDC2309ABF334F77564C7590066FF1D6833D9ADEF5037ED5ED0B6B595720159A81E33494846B2CC23E2E5F1BAFC7D87D",
+    "plaintext": "3E082216CB59F558991B00747C27BCAE6139C37BE41AD6A8E9"
   },
   {
-    "key_seed": "1521D786973327F460210B6E4A98241DFD125976BB346CC05582B705CF2C81E3",
-    "ldt_key": "4273A7E59F40DB5DE89D3EAD6732B12FC8A7671B4CFB6DF7BDA02167B0F3414CA1EAF32BB53E1D892B9932DE2163ECA9CF4C822179C89BE705EDAB667C25B58D",
-    "hmac_key": "90F0E29BD73A103AC87C3F60753DAA6641DEAA3E9034C25BD878ACD3EF14B7B9",
-    "adv_salt": "650A",
-    "plaintext": "98FD73B1CBF4A46CF1B855EC6D4DBB755716B2E1D2EC3C",
-    "ciphertext": "56A77F96DDCB43A70530B88669BADD12F7F16E4DAC74D8",
-    "metadata_key_hmac": "1E2DDAD56AFA04DD197D0E75BC60A3A303AE2FED2250D62BFD5613505DD3CB2C"
+    "adv_salt": "CEEF",
+    "ciphertext": "59B25F6C47627DA4090B53063D5575442F06A08DE58EAB64D68BF8FAECA7",
+    "hmac_key": "C4DE203603F16FB3FEDD229456CB7A60E417D00A169DF9285A508B548BACA491",
+    "identity_token_hmac": "A23559801DF147E623EC67E779ECB673E84892D6EEB2678438BCF34A0457F675",
+    "key_seed": "2591BF35AD9148C46AE34848FD205F6EC1BBDF2B75BCD9F6E2604EE945CB8D23",
+    "ldt_key": "154130784C121CE60A793A0EFA716E469D58A70B5D8656054DEC2D55A145FFE990D454DBBC0FA2BEAAB2F340F2C7F7BA2877F8E742FDA6AA2E8F3D8FA5A4FFC9",
+    "plaintext": "7F9C2C7BC22D8B9E0C60B2368F36BA901CAE0DF7BAE687C5E0F92AE9865C"
   },
   {
-    "key_seed": "76C21E2D70694F810B5F730962CC6EC1E5B4F9542990E15DDBD2ABEA92B4AE69",
-    "ldt_key": "CA8485DC112182FEBFFD7DF96365F8E7C97BAA0701F516C191DE98E772FBCAA94E87C062A2A69D94E66942D262D4C5DFCA62F2B293BB485B5AD399A1192FB5B2",
-    "hmac_key": "F18E562B465B4D05D17C30AF43DC2A2C674DE6BC09D50A31A11E4246AFFF22F3",
-    "adv_salt": "26D8",
-    "plaintext": "8CFBAB03370DBAF4228B2BA60A4009A8",
-    "ciphertext": "B5F69C72D7F281D68236E70FEFF3AE85",
-    "metadata_key_hmac": "0F9AA07E623C81AA9F497CC6DDA13E0C6752170393EB205DFEDD3EFFBDA5E959"
+    "adv_salt": "7E2B",
+    "ciphertext": "1688E19A95742C16497585EB5E1BF8E729",
+    "hmac_key": "E7EF33D0CC8CEFD9926308D187A6613F8A9F1CE207CAFEC51F327407D5398438",
+    "identity_token_hmac": "59C9BBFDE59DB9CB354A905DB22215E25B747EC69CE22F9B76CA8F27C2297E8D",
+    "key_seed": "92343E22B489FA1C9ABC0BA72ECDC7D9A0398C02D4FD57734BE451C0A2F124E0",
+    "ldt_key": "0DE6ED726ACE044F30F18455137E7FB059C9EB0C573376FB0A3191D1699C747414EDE680A061AB213C3877EA9CABE725D6FEDB3DC7B56BAAA386471467E7CD6B",
+    "plaintext": "CCC3A15107F749514AA4EE7F2DC19AD77C"
   },
   {
-    "key_seed": "9560513480DDAD0E1413E38F9D6E897ACE1A7632D596D23769498B8BE29715B1",
-    "ldt_key": "10F5156C4B8B7829C1C4D6F145DEF13B9A324C35D5A3E4820F892584844CD853D94FA8CE5A1AD2823F59D5A96A4E9EC4411BFD13781392ADAAFE6EE12C85ADBB",
-    "hmac_key": "25FF587E6307020556B1F164B8DE84074F17E3035DF540FA6F9DDA4D2C890ED3",
-    "adv_salt": "6656",
-    "plaintext": "2E131D72176A4200B363D13D80DD10A12A698AC0A1E099A4523F13F6A9094D",
-    "ciphertext": "8E6C2B66D73724060636779E93F2EB02534AD93613BDD424AD387554C3257D",
-    "metadata_key_hmac": "A3B660C747BDDBFB92E8B48B6DFBF3B6A927EBCD836B98968E4DBE1A225C0CCF"
+    "adv_salt": "1FC5",
+    "ciphertext": "CD7E0E0FF578E0E80BDB0FEED0C5491A12C346A27380D642BBC13CC6",
+    "hmac_key": "1288291EC7FBE104F8EC09DC9A78E1A47A8B362AC9A5377FE85B3D9EC50F68EC",
+    "identity_token_hmac": "6BB5AEA5ED0C1F4B56470F9D6629BBD29A23B6A601765CC004639A6A5E8BA626",
+    "key_seed": "14E59B92D58461D1093A92B936326986D141F71DAE9DBEC2B3BC4508345D2976",
+    "ldt_key": "6B76182617C20677B7A2F132F6F2FE3FA0166D7BD46D2DCC75645F95014EE9E1E0A553BE54D654E40A95FD2C56503487E7E163A2F58E996EC585CEEDCC3C47D0",
+    "plaintext": "9B1143A836ED3758A3D16D7B11621BBBE28A42ECF87F325550DE8192"
   },
   {
-    "key_seed": "8A7FA1444A5AB5FD9C754FB5C17B24521A4BBCA87304668ED171E63ADE00B53A",
-    "ldt_key": "62E5CE70A1E00AB0344AFFC21660B81B9AF7DBC98E2B3BBFB0580F95CDF6607EE86E3E027910C133A445A54EEF164717F60BBF581FD911D16C16797F093AA376",
-    "hmac_key": "A8796FDDAFDA42BEA46CFAFC15B3054AA95C7C6D9842C056C5D5CA3290870A2A",
-    "adv_salt": "3ABB",
-    "plaintext": "96C67D0472764E1FBA67D78C9E23130EA569D80850D67860705DB18A84D9",
-    "ciphertext": "E253ABEFA95A3005FF8A637C64C4979D527823DA079C56ABD7F8A226254E",
-    "metadata_key_hmac": "29630F405187DF2E46FD1568B885C8F342B6A0C736D05EF2708A15E89EB4F77E"
+    "adv_salt": "2D53",
+    "ciphertext": "EF83ABA138A689555458B97DB59EA003BB",
+    "hmac_key": "16248A307CDC2A250B424BF36988B0B6366EB803ACCE340AE88B07903183B401",
+    "identity_token_hmac": "64C0A74AE467B4603FD291FE6B32B49D8DC36457668015700EB89BC987095BC3",
+    "key_seed": "335FB592EFB2916D06EEB949514C40E4407490506AC269ECC30F66F70DEFA836",
+    "ldt_key": "A282B486080966A74C664BF30CE063B511A8F4FCA7DFC39D81C5BD314141705FBCC539418CA0CD6B5C5781D52CBFD4C1A722D1D00CA06F55EB85C16B7F631CAF",
+    "plaintext": "DEB6E4513B1B0CF9BA4A98D379A8E13403"
   },
   {
-    "key_seed": "C9CEF79735A49737B2054FD28AFD49A32BC27413A645B81EA41ED0976EB2C1BA",
-    "ldt_key": "CCD43C05EBB5729D56B9ABCD430ADD48AA324F888273F11990F29B1764976AC52F579204BBA9153C1C87E895FCB10EC57E5C27F67166794A4D056114ECE090FD",
-    "hmac_key": "84E721FF3054B223F07971A69C4EF2065FFA8BEBBCA6E21F86D3195404D10CBD",
-    "adv_salt": "DBF9",
-    "plaintext": "3EB9E1F6C5C925B9DB9B9358054A7110AF19FFA771",
-    "ciphertext": "05A4431BF908CAEC6B3FB847C2F45CE4CA666F2669",
-    "metadata_key_hmac": "256FF13A29DD13554EA3B53476950960BA8FF3404DA2EA647D657DF077504116"
+    "adv_salt": "8ECE",
+    "ciphertext": "443127411C98DF4DA8A06D675255D709508C854ED3AC3C6CA0847DD054",
+    "hmac_key": "6E8C7E7E1CBE3CB0917F0DD7041B80C1ECAE2DB5A1EFDAC720830B4765F8F719",
+    "identity_token_hmac": "61644E0BFF22A073B2B04529CA04C34535B85E1D426FBBFF574FE28628D9EA31",
+    "key_seed": "40FC5113E1557A72F698E3AC3EF051FC3AEC386233954D61C936FD3B66882422",
+    "ldt_key": "775686C9C7FC526CED20B718E8675697776199E2371A558DE33E3E78C53CB734A2E933C02C41F8826DEEED9769EE7E558A780CDACE276D40BF067BF1000AFE80",
+    "plaintext": "6CDF43EAAAB305E79BBE1AD6FD7BD70A630E9CE50591D86A22456FB556"
   },
   {
-    "key_seed": "E48BFCC68FCA39CFE1CA4EDFC684E37D20BD27D56257CB0EF4F109AAD61D32A8",
-    "ldt_key": "DB6EFAD0649A4D700171A22AE74D0C87CD78A15BC2103B2CDD10676190D519227039177E4FDA4003C6C2770E577BDAEDC1124EC003996B1F63B0DDF882988BA4",
-    "hmac_key": "868E89F711B4890B4F3309E16C33CF8DDF9BFF32E94B3B9CBE091BFA3D9413DB",
-    "adv_salt": "DD6C",
-    "plaintext": "966A5DA11194D05AF55AE62928946B3066E956B3C268878B8B",
-    "ciphertext": "A4E406ED299EAB31C8EAA4179CC35F6F5ACD3BAA2037C68340",
-    "metadata_key_hmac": "9A521E13E251F0015C3740EA21D84E108263125989719A2E7E3C847DC4DC08DC"
+    "adv_salt": "9D91",
+    "ciphertext": "170FE1A9E44F0467B74F1A40C3B7DB88B0AC12490B5E1E",
+    "hmac_key": "F32663EB2FCE17B3C25297D9984AFCDD13316E7674E140818009F46F029C9513",
+    "identity_token_hmac": "6868CAE353ACE437F0217688CFFC414E64030D7C867D1D1E7A047D2B356A02A5",
+    "key_seed": "74958FA7D2A63FCB32DD38BD6CC4247DA571817FDC2D259DDF4044B7818E9B3E",
+    "ldt_key": "615CD06A1E653207B0132535C85839525E479E67E3438D8A3A8B8739F41D5FAE773429F1B70899136C3614F9E53E3ED0266B03A2F8C2E060B76B3AD8384E9B83",
+    "plaintext": "013B4F0CDCD662C220CF77C3EAED783048FFBA61D5E1B0"
   },
   {
-    "key_seed": "36EB0F6236D1072C71FEBE86953757768518126B710BF644C724B22C6E811361",
-    "ldt_key": "34D51D3D4D442AB9EDF9CA19D84D0A23BA2DEBA4F49F1322BC37A5CD72BF0EC5B7183DA85F90AE029F8DD752E177CCAE26D73DB63F27F99AB39B4C2027FA5731",
-    "hmac_key": "6CF977E944CD2F42EA05A0F833E39E855FA219D30EA6ED07A85300654B8AF653",
-    "adv_salt": "D695",
-    "plaintext": "11C0603EFF5B63E3700C4441ADDD8BDB8B8D5413B06D0B547B65",
-    "ciphertext": "1BD66DF65CA78541E3EA94ECC88C352450710DA6F22C5283EB15",
-    "metadata_key_hmac": "7D26D8BECD71E3C98BB5B60493D5FBC5594473DC1A333BA123C4007B10FAE74C"
+    "adv_salt": "73A6",
+    "ciphertext": "53911B8E6FA54286BA4DBEE7E103F4B73D10C873AF7EF3267F",
+    "hmac_key": "9245A0A189CFF97F3716521EC06C7B5EC28ACF1D175CF584577FC31677033C55",
+    "identity_token_hmac": "DF63054ABB719ADA13BF2FE69F0224962CAF30A444138646A14CC1B6274C038C",
+    "key_seed": "2CF63DF744353224F434313B0468607C5CCABA9C26C8258F0AF5D3FB4A3C4693",
+    "ldt_key": "2F762E3AEB80C1106D7774B3D0C211B302972CD0A4BF7D9C988D537436F2400C264702F0A630D1E69EF470A5B7094566F82D876412FA64139F0ABBF0CF9D1596",
+    "plaintext": "0D309F7E410220E23520E6BF1A33CD6C1AD4ED1CEF645339FD"
   },
   {
-    "key_seed": "9608BC776DBA4873F0BFC953265CB8E4C36966BFD6230C35B63E723722458E08",
-    "ldt_key": "22E962B7ABFCE353EEDA7F16CDAE977B7F496C938A663465C36BBD2B433A03F7A477190B8974A5ECF41C29ADB0E767A8BDDF4636F369C56E83EACF1B39FF3CE4",
-    "hmac_key": "E69E2A790681904BFD66D09CD60F291E84DCACD490208F5682DF994291F4B3F4",
-    "adv_salt": "4F53",
-    "plaintext": "E125883CB677E805178785ED8FDC525E0054E48192CDE9",
-    "ciphertext": "7DD36CB0146BDDD1FE95606A15CAD69164EDA3CE7C7B29",
-    "metadata_key_hmac": "0EEAE07CBAE833B659F0236D45B965F7ED423CD8CAFD921E8B52932AF0FC36C5"
+    "adv_salt": "E85E",
+    "ciphertext": "BC1027E1A064D40A93DDFD479B7660AEE059519A8A173319D356B8E6436EBE",
+    "hmac_key": "78AE4E730FD438D605FD3EC3404C4C80BE1D406A0E61CB50987D90CCE12473D3",
+    "identity_token_hmac": "0D6DB7E94AF7A07B4EA5ECEDE6CCA60D49BF612C3B109EA0AAF5E7CF888813B5",
+    "key_seed": "121F2970696FC34F1BF2518216A6440D18EB8E3BC8618350E112CA0B30859022",
+    "ldt_key": "888E59808FC618FD9A4F7E625953B25AE6372B526F65870AA04C0677BEB554EB32317057612DF6B344861C3006989237870C5778FBE834DB73E3B30AB8A0BFF5",
+    "plaintext": "0D41C7319A3282F415077C042913C0A790D7116E74A94B267BF37F2F7617F2"
   },
   {
-    "key_seed": "B2B6B53D51B08DF36375C4F761E4EF7696B8727CCC69C055F59E1DFAE7465B61",
-    "ldt_key": "FF16F56B0EF97ED2DE15F7210D39F17D7E2A0809AB33B6F03A48B0D67FE60258A576E642A001C160068D183C6B8D691C42E7B9B5833AE1D4EB6C174A40022167",
-    "hmac_key": "BACEE2A7FDB9343A443199D0AE356C3FA81F3B4E9A23AF015111FF65AA577396",
-    "adv_salt": "6F8F",
-    "plaintext": "D802DB2CEC8B2D5C60AD56EEDE2E9F703208D9074C87F301CDDE9ADE",
-    "ciphertext": "940E65E0B35E2C55C95DB6615BDB983157F4E487030E089FD7BB36EC",
-    "metadata_key_hmac": "7BFCA5F80A1C0151FCB6AF5B0254A5FA6D1C1D3838171C9BA7239D2691BB7DAC"
+    "adv_salt": "F380",
+    "ciphertext": "C01C4C4CC32F8211804EC1F30D8D4675CD39366E908A6DDF7C44FEBE",
+    "hmac_key": "ED79226508FC7349E3672C89302BACDF6523BB1EC1D91D978FFDF7218FBDBA6A",
+    "identity_token_hmac": "F1EC22047C3EC3185DA15CC5FA206103DC0E97F90D31D5F4D85DF66B20739402",
+    "key_seed": "A090D3D433D62BE1AB26AEB4613C96F44079A2BACE83E5E98028D7A3BC6F463A",
+    "ldt_key": "F882A283703F9B5F4A6F5CB7713B2E65220AF2A37711145546AFFBFB3054C31F520838544B4E5411DBDAE1BFE2C8452E73B954DA1EB1EA931EB0752155732AC7",
+    "plaintext": "A0D92E271704EBD8CE417F16ED748C52A32CF2F0D6BAC91710E3D32B"
   },
   {
-    "key_seed": "12DA1EBEAA3D43E08E600C6AA716B207D36C6758802374C0E4EA7C32BE675D34",
-    "ldt_key": "062A158A5B903F3101F185D3BD4D40AAACEB2790C556E5DDC9AB463A5DDCE0787CF1179F73956D6C6899607BE6F76FA9336D6FA0B4CF1A2282675175910CBC49",
-    "hmac_key": "3E8D020F7D8B0CE5D79C8F16F0343F04D7336C8D27041AFA158BBD005EEBBF6B",
-    "adv_salt": "2D65",
-    "plaintext": "402574521678897BBE04FC669DC869542E54E549331D42DA1A",
-    "ciphertext": "12F37BB97EA1AC0409881C93DE6CCC872B56D1213F19E6F7D3",
-    "metadata_key_hmac": "E0FB235D6DF5EE15F51D8BED378CD53C176A20FB9C609AD165229E0A7619EC29"
+    "adv_salt": "00D1",
+    "ciphertext": "52025755D177839E5AFD6875F692FBB4F228BE5BC3583249390050E95EEF3C",
+    "hmac_key": "148D8C9025A23C28642569A13F70B90C8B4619F1D37B35610A73D7DD2002AF51",
+    "identity_token_hmac": "9E1144CC5EC70D0B8243E106516B277E53B5B75D2F82B7CF573F79202FACE05B",
+    "key_seed": "B00E867E23E7B5A4CE44967F173961C584D481506AB467A3A47589280D06F719",
+    "ldt_key": "BE576CD183CD359A2AAB6A3FB1D000BD1002FA7EF62BA2C68EFE5DC97A37C2B646EC64B21B5D17AC9AF991C8F4846DEF855D6D8ADDF344D112FC0308E1088F22",
+    "plaintext": "852403CEC6446A618E60EC5E46A4CDFC960B8D8332C75A403F680584E7F19C"
   },
   {
-    "key_seed": "F7BA3402556935A790BECBF1BEB3152EE9416A022F90FC3D4446DF8D97600191",
-    "ldt_key": "919BBF75697F5344409D34BD5D708B2A60272FC3E0B75378331B62BD1B4E1A11E0091FA4900E9DDC91E73AEF4D2386D9736F6270041B7B7D2843F8FD6D96A76C",
-    "hmac_key": "50E71BD98F83211A1F4066ADD9F4854A157849C16C07A791D1BF42B9A9E4738C",
-    "adv_salt": "34C7",
-    "plaintext": "A84BC1B49EB1276B9B66B88EFF282C5FF055BA61F169FFCD860C1C",
-    "ciphertext": "DA0275FCD07244BE53E80E32A4049CE8239AC7C4FAC554250CFB0D",
-    "metadata_key_hmac": "7D9D7E557903E103A6F55D2AB5F8CDBE2BF501EA1EAD9D1DA31F8E536E4E9B8E"
+    "adv_salt": "54F8",
+    "ciphertext": "AC2CD5D1A00D5BE2276FCD1D8B3DB3BA82E444E3C34550978EA861631EC8",
+    "hmac_key": "77AE3C0342EDD44559E430C72482E130DD5D71E6ECC981BDF08DB87511FA3605",
+    "identity_token_hmac": "A107BAB5AECB87954FAD5577CF3E081F6D7F6AAB482D1AA6DFE094746C046255",
+    "key_seed": "9775916C644EA12143896868986CF1E4B8B7DD4F2C95B322DFAD7B216CB23669",
+    "ldt_key": "888A01B8B67964BF529D506D66284D5D919403F5CE7705DB946253AC7F18716C74B0E0F15ECD96D0D60A2A6072798D0B347A7849C1137C24207C2BF9CF7966FE",
+    "plaintext": "62668FC2E5A380858863046818C061D30A1DDC3B29E7F1FFEDEC12DBD344"
   },
   {
-    "key_seed": "B7743F2EB3B31527222B0938A519EC45B2F1EEBC58200CB62E353419232747F7",
-    "ldt_key": "2B9060A96D16FD2CC61D30BBC3690007AAE85FD2167855933FF3A35ED3957C8D17168B6B3219152E40CE2F0246A4038F9F06DEF59559D16B1296C3B38BE9B70C",
-    "hmac_key": "4623A09FE3176EC70944793D576E326FE057A72AB595FE23CFFF4845E99FC9AE",
-    "adv_salt": "C4CF",
-    "plaintext": "5D1CE03573BCA43C8766F10C03A2C810D54D8B70E2F4C5E5EEC5",
-    "ciphertext": "E339FDB9FE6EA518EB5FDDC3C4DCF951963F2D90FBE4090EDCFE",
-    "metadata_key_hmac": "92ED758AAEB7236E22ACCEBBC0B33AA23DC6727677D69594F09342984F5C863D"
+    "adv_salt": "AA38",
+    "ciphertext": "9E5962AFFA94C9143502358EA34B3E39FF5E4C9F7FBF57BB3206E2CFF635",
+    "hmac_key": "8E04FB092D8FA08D3E2BF791641C65E452A4B61C61ACEA5327E68F090229A015",
+    "identity_token_hmac": "224B6C4818E51AA638CCAC259B37F17957FA582E7992CAB661890E20C261B8B9",
+    "key_seed": "6CFF000FDC2F09BC79802265252E8800F974CC60421BBFF68F2089CAEBF01CD8",
+    "ldt_key": "3E3688B8589F59D7196CEA6EDC0193828B4E0B0D588675739A2775891BED869972F3596A72A97DF684B424A41FDE41C496D99EE49111F86A2847D5234441289C",
+    "plaintext": "51D1D11F873CDF7815FCE20010254FA8998ABE1525848EDD477CE2B5E2A8"
   },
   {
-    "key_seed": "7E6AD26DF188EAA50EA6BB1DC755E674332952F84A902CF17351BF83B3B3D40B",
-    "ldt_key": "4823F16CFACE349ACECA6A797F7F6949B5CF0E86B14F2FA86A82F9E5344DD5FBA46CF1DBE420200966B1B4E16134A1735BA922829F7B9F504DDB76E87C8620B6",
-    "hmac_key": "C867A614967B144D921C37E855F2EC8C017A0CA5150842136B67D247E4509D1C",
-    "adv_salt": "DC9D",
-    "plaintext": "AC66F58970822EBF3EC8FB8A6BC0B88ABE4AEE",
-    "ciphertext": "697EE0ACABBD0F2C888A27005A568659EBACE0",
-    "metadata_key_hmac": "5B9CBA94B48443F07B608B1DC93F5FBA745A593296EA9F670E6F148BE667D6B4"
+    "adv_salt": "B1F3",
+    "ciphertext": "FE4366AEE6DF5F7D28BF0667085557D7CC4EA6E307A6C3295DA1AA1A56",
+    "hmac_key": "D3BD9864B5D175ECE39D123D43F0FBB781F637891A048FE8F6D6F2D44BF2DCC3",
+    "identity_token_hmac": "25CA8316EBD6A264FA762F72AFFB221FFA7F65F8330C12A3787233907D956D18",
+    "key_seed": "5814F434D390EA83722FA8F0D7BD91F65090BF6DFCFD42C28E4DCAEE4F095B98",
+    "ldt_key": "04AAD7F99CB9CA6A1A8246EFB82C3183BBE4B225D7C95D52403C82F4A819805D41ED1DAF64927ED74F63AA3A4DA3E2BF6B4D0F04E64CA51CEE24415AA512399A",
+    "plaintext": "8F3A30C6EFDE14B9367EDFD4166756D08226C64A78F7495CF001392510"
   },
   {
-    "key_seed": "3AA30B6E523E212F9BC549B1468624C3C39D4446C1613F99EC525E90B563E114",
-    "ldt_key": "32EEA16100A29DEEDAF425D5EDCECBA51621B8F1A3FFA232D0804FED7B9F05B15A3D0638AB519640E927F61C2107E4A56556B054CB0323CAFF375A144FDDA9D9",
-    "hmac_key": "07A1CF987BEF7C4DD83B831FFC6A67405ADC35B465063AD3201A15570EE65B6D",
-    "adv_salt": "C521",
-    "plaintext": "43FD7D528C2B9B610C8AEEBD9B136D6DABEF53F920BE",
-    "ciphertext": "DEF7A393BE5094EFA7BFAC518C014979B32FABB4DB26",
-    "metadata_key_hmac": "9AD8D7883FF0D6C9F2408EA1D04F6036C587CB9275F9EB53CACAA220F6B8AF42"
+    "adv_salt": "5DBB",
+    "ciphertext": "D24534D81CF934D117CF8A227B974C4A8670CC0794D7786F77D4",
+    "hmac_key": "AF4DA47793A376C768CEB102145C644670AE13566778327953E8B7965B26DC43",
+    "identity_token_hmac": "E359B6A3EEF1E0C5A5EC1E7B6D3362FD31F12AC9C43E717198CEB14F092A0ADB",
+    "key_seed": "484F18C39DF6592153C8A653BD398D961108A717A0AB2CC282813C6FD258C0B8",
+    "ldt_key": "FA78F41F44E5AF7861C11D23E563B91FC32370A0E3D40BFDF07495E9519EDEFFCAAC939D1F5C3B554265553307666CE02FBC31D31F29FDD3632BB24F39545F54",
+    "plaintext": "5F0D1829F7DE91792483F92AA7ED87012A64FC38D41F4D4461F5"
   },
   {
-    "key_seed": "E9E8F9E8DAC560A5CD0136D59C94099D601EFDB849F599A3B58078BDD2DC3E96",
-    "ldt_key": "A437E4C55CCDD67EAF8DFAF9C65213E8A2232FE624662D4B682ABB8A5B9B9D21307BCDE683C42FC22F0595AE41D59BA016140A309B8825143E5BF5C12B3B7549",
-    "hmac_key": "6C4768193DDBB1477C893AC14A87563FF249654574814C4153DC1DE46CD9A2B6",
-    "adv_salt": "3813",
-    "plaintext": "856CFE7BF88E2E8695F34DE8694F3DFA61F2B8D93A148264E4354B",
-    "ciphertext": "86EDC0F3E4F9A9BED9ABB4DB56C2E2316C61097F2944F2306A785A",
-    "metadata_key_hmac": "ACC695BCF1063CB8ED7B2E456DE86FA0CC74C1FE44C4F637AF3495A409F6D0C9"
+    "adv_salt": "33A3",
+    "ciphertext": "F492673ECCC90ED523086900A06AB897FD03C0CB8BF74CFBF5FD07",
+    "hmac_key": "78E1D683B105EB15A33B7AD6C2DC0695C7848FE72A0BB1993988034823F63C7E",
+    "identity_token_hmac": "3F1FA207DEACA96FDB08E1619653117B42922CBAA1088CAF8225B248875774E2",
+    "key_seed": "632642AF207B26D998D5605BFD4FC57804270E76AFC0813B101AE5E25A8F06D5",
+    "ldt_key": "6C447AB95DDB5972EAFBC4E697BBA41D9B8F3393F4FCFD8AD83FCFAE4F719BEF694C1588300C2653C665B62BB6523CB333809643530A758A7731771A9E39D751",
+    "plaintext": "4FB623482FDF7DFADDA9F32F0ABB75DEDC8F516639934A414C23B1"
   },
   {
-    "key_seed": "53E237DE3511AD7221FDC3EAB249724CB9FBA19C4FD08E65915188264C639AB7",
-    "ldt_key": "16A2905D758DD04EFD7715AB796FE5E5CE922B3458733A8C369C4B5C1433A1597A50D58832593831F52B33FFF07EA67EBA625635F9949889CC57B92D99E39868",
-    "hmac_key": "E29C815883E98D5DA9C4CA403AEB9EBED8F7590A15500692563113D74CC57112",
-    "adv_salt": "4005",
-    "plaintext": "77358652AB18EE47794341B2438A77770A358CD1E9A00DC0BBF79E0D5E",
-    "ciphertext": "3606F86BF8017D9DCBC8442C1D9C50F4B853C6DC6F1E8121F59113B4E5",
-    "metadata_key_hmac": "DF01BCC3F032DDE4CB7FA01CC8EEEC96C5339F2DA060240CAB3F1C8F0DA58E5F"
+    "adv_salt": "4D1D",
+    "ciphertext": "DEF23BF8CE6D352C03F684873197F1E126EF",
+    "hmac_key": "25669AAEBADA492252B93E6D7E94D75312D3B78B28D3A42B5A170F1532F91498",
+    "identity_token_hmac": "C4B1D109778B6E1C0069B0268E640D44F190AFE69E6E1C38940F05B24B5A705E",
+    "key_seed": "35344CFB928DD670C8F9B554CAFA56B71E898EEE153B7960D62444999D4F8EC7",
+    "ldt_key": "4DB7CD0D9F6E7E9B0985432CF28B73C6C04993B55816F6631F5E6B1EE8E0E25B653BEFB1C72F19B409972D02076DBE50138616ABCF243B55503BAFD17B1354CB",
+    "plaintext": "56F164E945994FD2F9FF17FB9A37D2CF848D"
   },
   {
-    "key_seed": "7312EB9FEA5C7CA9F945DF91E6CC5512844A1F3694FF1F8334C14A8CC1C514DB",
-    "ldt_key": "9B14E89C2BFF72EEB7C9F484BF20DDADB3716055A55DA9CB9629F98B456E8F5FC16D03D399D8D2077668D58046029E1BFA35A75B22C0E2C66CD79F17214D3F40",
-    "hmac_key": "CBAB6250E2F312A36E475FB89B17661810DA0F6B3E0E54980CCB409F72B6FB11",
-    "adv_salt": "8A4B",
-    "plaintext": "8649F437928B3DEB3271CF32D20C82D92B0DEFF30FC189B17E",
-    "ciphertext": "3742B52ED6A5033C1E3B9C4AC90E025761AF6F021A36775D8F",
-    "metadata_key_hmac": "3725CA84C82101D045522383C65A2CF82DC7ABEE776A2598CE6DF5979738CE9C"
+    "adv_salt": "171A",
+    "ciphertext": "62E2114BC26661BA79826CF62A546D1510020961EFFB600FB183DA4464",
+    "hmac_key": "01288B1D7E7F8EB6FEAC69FD20BB11AB56D345BCF0AE49A674170946A708FFE4",
+    "identity_token_hmac": "69F504A58777284025B9E33C2CE8585958A1E21763624D3026D4C6EEBB6F2157",
+    "key_seed": "6C0304E547553359B89861721D451170273DB250EC89F0386750F8AB208C4F7A",
+    "ldt_key": "8960E2FC42E9F317C808DF38E0B971944F5F1E971A77999120FFB125E6F78A046018AD83467760FE0FA2D770259D54663845BDDFF24E846969BC0ACEC57C8552",
+    "plaintext": "3B0C65B9D02FF4DD90DB2099A0A0CF10DC35A2E7C1DE8A281D15A1AAD6"
   },
   {
-    "key_seed": "1773861943D0B75137AC484431C0E3A5A01AD365E210D49FCB600ACA63EED3FC",
-    "ldt_key": "0ADBB1905A684E8B43901DC20C5D5320A3F446E22F6897FD4000893FEDB66A21B2BFE78CE6E9614642444A885392BFECE5D01D9610971697BBB0243689AE20BD",
-    "hmac_key": "8DE824B2E5E8EE81E6853B2B6B5476F01D8C32956110BABA92A826E3110D9E73",
-    "adv_salt": "666B",
-    "plaintext": "8F451FB1E8EA039FD315E22C77752A81E0A5",
-    "ciphertext": "5FD110690047A765DBB47E7F2AF9AC7A2095",
-    "metadata_key_hmac": "7094CD4AE6B7C81475FAECD9C12542CA1B0BA018DB216BA6437512E09B5B7F6B"
+    "adv_salt": "D96E",
+    "ciphertext": "289873B493AE26B051E1BA5C9630C03B",
+    "hmac_key": "7F34069F1E02C5863E59B49AE7A09A740FD9EF2130FE241930853C6638F6EA5C",
+    "identity_token_hmac": "4EE58E92ACD3792F79BAED54D9DCB9AEC99C8155666FA18BF47104C5205E4FBD",
+    "key_seed": "68E0BA10E0C3A18E5D61EE0370F54593E0288CDDF65C6C8057300CAD29B090B7",
+    "ldt_key": "441C58203B54555CF21A8AC286512FE9FD850074DE73056687B0C84F54891F2F51BB71595DCEC31BB425C760D62114706423EB5522B6938D1C26CD078C9E4958",
+    "plaintext": "F023B0469092095BF11BE2C8A3D1F538"
   },
   {
-    "key_seed": "C81619628FCCA36C3333A9DF317A30A38E992B99B4C613D54EF5D8A84E041CB2",
-    "ldt_key": "DF08364FCF56F754A435A293DB8F061F63EFEA9B7E6677D85F3C7FB184C6CCF577A2BD0ACDA5DC1D2523EE1F17124C2165FE283314C5570A77DF48ECDC355AF6",
-    "hmac_key": "139F438F13C38116CEAF0008113831A0B2B4AB26C2C2C67E7E0789D457A6FB58",
-    "adv_salt": "B2D5",
-    "plaintext": "8EBB4881DD7A89B035180226619EA16AA87857F56336D921",
-    "ciphertext": "4D405B876C12A543559F879C464E87A318B0E3778F2C884C",
-    "metadata_key_hmac": "A51503260CDB7DA3B8A8A625C7D0FE672E9C4DFF1A61083507785911782EAB70"
+    "adv_salt": "387F",
+    "ciphertext": "121B881D86C73C55C54B58ADF7C74888AD718266BDA183",
+    "hmac_key": "F52485E359F8C8AE2A9A2367E83F4DF59B3F9671371995CADA1FB7D676ED9ED1",
+    "identity_token_hmac": "38622556DA5CDA145459709B8A2EA758E36C4E7259ED1CC23EA3F16BAC9B3942",
+    "key_seed": "AB297422AE12CB88D6B1626645362D6A648B602421B1F50E9C5726F3524E5443",
+    "ldt_key": "B41F670F935CB3779A0D6A10E1CBFDD2262402DDA4EB4AE875F49D685BA4F78B3558D08F2A918E93758F1452ACAE11B091C11B0E43541BC631E2B640F6145B7C",
+    "plaintext": "F11D62D12CBB4AE6584A56A3C1DE1ECD6618AF34C198F8"
   },
   {
-    "key_seed": "C40DDC0D09883D89AB43D4868A619200BCA6AD3077298150EE3B24A538259899",
-    "ldt_key": "9EAA4B0A788B9722526A0CDC24D12CAC087BCEBA93DEC1BF91A75B083E237CCC81EA08EA411E17E3D456BD84DD62D3767B816D71495013A76CE01381BF079DC1",
-    "hmac_key": "1C06DD06F878794583907EB532AA977111887BBC3C04D206E6AAD6C4B396EEBC",
-    "adv_salt": "6A17",
-    "plaintext": "7F48ACA677EA0623CB99F4489684B63644F4",
-    "ciphertext": "4643C1A64BE170CD2FAB442F1744774FD2F0",
-    "metadata_key_hmac": "50B67E416D25520E17E3AA41D9D91B40E161B46EB4A844194F830412B51DD82A"
+    "adv_salt": "1577",
+    "ciphertext": "BDC6A5298AAAB19D43FDDEA801F547CF2BD10F55FAD4E7A9627D43E9D2CE",
+    "hmac_key": "6422A087D0436BE49D14DBB2669135E591FD7ED3546E94B1656B72FE993EC6D0",
+    "identity_token_hmac": "79FB70FA422A1E985FEAA92800389DC7011B114BFD2CFBAE02AFCF0869EBCDAA",
+    "key_seed": "A9AD292C8B18B7C15EF3FF48C0B96C7DDD41035ECC8CA2FA6E693DD26B9FF0B9",
+    "ldt_key": "4EDC9FCFCA44CBE5FA9F4FD05076E268C2B6B995FDEA5614CDD788830276E8A44AA2FC4CD016500324CCDA2A360721C6228938461164A38E7F2B5D3BD93C3640",
+    "plaintext": "6ADEB26B7E31EBFF8AA3033E03F4D51F2DF26AB6DCE786F4F36B92466070"
   },
   {
-    "key_seed": "5303BA3383DADF741D20FA46C3EBE1B0857E0525C88E7D60491C7CC09BC83133",
-    "ldt_key": "E7BD40E7A7AACD7C889767679F2A6F8F1ECD3EE7BE80C954E28CF4B18060ED8112FFECDCAF162A21F8D1A310F941B3B144B6430B2B02F284E3C278C246517BD9",
-    "hmac_key": "2B9A31A18F3516FB7A03E7A0C018507499CF16075D6CDFE395F38F466133D106",
-    "adv_salt": "F11C",
-    "plaintext": "62C335B8AEBC64111E88458AC17DBCAAD9FFACE7F2F34A62AAEE1F03F409",
-    "ciphertext": "7A187B905C3A923F2882D66C11CA32ADCE01EC46EF2F606DCD92E6EA1777",
-    "metadata_key_hmac": "98B6F0CAB8B8DE46C4762284899C2E0AB18BF7FAB1016A312DF886AF3188056A"
+    "adv_salt": "651E",
+    "ciphertext": "C6E7BF11F4C60727F4ED2FBFBF6D0F2FD862",
+    "hmac_key": "21FF3858D29A9CF38E86C40AAF83B5BD0A60B317906B4E3F4FFC2EAA718890EB",
+    "identity_token_hmac": "AE6CC49ACDA2014A5134AF8D3E318243EF342317E7B341D00AEC6839F31742D4",
+    "key_seed": "B4A98978D3F552C9A81DB5BEAFF274F596ACAF4E4A6612E81A064DEE39CE4F16",
+    "ldt_key": "A82FD91507080BC8F2FC5B4C3C2088752FACA99047CDC4A1776ECD1E461E28012398058C220DFA667507060C5D71ECD840F9DFC44E5DAFDBB499571EAC48DA9D",
+    "plaintext": "0E1AE48BF75A15436F226724B006968A2B6C"
   },
   {
-    "key_seed": "753528F822537C4D415D303A60E262A1B2940080851F56EEB3A139ECFC542FBC",
-    "ldt_key": "D0DBC4CBA4FCCDCEA4488E96818529AF39194E4CC727D4E7462E08E6489FBC514FA2E49FABF7EF2D79D4C64AD5BE6411F22A95BF0D6395FDF6D1A6D9E571D4F2",
-    "hmac_key": "880716AF02E75D557E6F5FBE217E25EDAEAC5D4409B167D29C8F5D84B163D3FD",
-    "adv_salt": "126E",
-    "plaintext": "EDDB6DDE5172724C4AED0FC8DAAAB28D89C4278B1A40E3332944BC",
-    "ciphertext": "1C6207B72F07609CD5A780F30D8FCE434C4F9B27AC7168709B5D5F",
-    "metadata_key_hmac": "2AE916B61DA3329C97B25E50CC2E3F16BFE28E811CB4EB84D317B9FEA37F1448"
-  },
-  {
-    "key_seed": "E758DEEBC05D7496829489D628C73ACC9610CEA8156F7FDCEE71F5CDDFF19FF1",
-    "ldt_key": "798A19F3F00AB6043F151CF56B6E420F92579A1C7BA59EF6BED390547F269AE271CC0AFDFB7CFBCFA9D7556E7E4B65BA89C7DC769E7FCFF84274175A592A7124",
-    "hmac_key": "F3773E92263D77390B619936774B47E41B4EBF50ACE1446B76738E5E538C83A2",
-    "adv_salt": "DC11",
-    "plaintext": "31E392CC99247408D4560CAEDC95CF866AEDAE",
-    "ciphertext": "3B4814FF273551D37E33D7A957F066730E3B7B",
-    "metadata_key_hmac": "FB168B3C5C1D90160D5A4E9BA3E77B485FBEE19A96B0201F72694C7DFA6E1E5E"
-  },
-  {
-    "key_seed": "2B47278A6B48C731880FB87A2DCA8554639BA9D823B4747624B6E2859705848A",
-    "ldt_key": "BB04B6BB8AB23527697B9D9EBF422C242C98BE7E3A61C52F4AA88980F3E31BD92FD51C039F907CC7D98AD0D2ECAFB1D1CB872B07273DB62BD1DFCE1D148EBBCC",
-    "hmac_key": "45732D754EC4492F07C42B598CC4972345C04FD2A0B9AB260F24889181095855",
-    "adv_salt": "5FDD",
-    "plaintext": "51EF41FD9A1E6E3239B05D7B663E7628DACBC8EC579831740ACBA4BC",
-    "ciphertext": "677BA674167B0C7C62245041C90F894F93BCA3822DF31B097DB940FE",
-    "metadata_key_hmac": "665F0B4DDF0ECA663AEAB45D5C0F221EA736BFBF23C240D11FE78036D52D919A"
-  },
-  {
-    "key_seed": "885E516FA5AE3EE60BECBB3CCD69F3277FC70FB98E93EEC511764DDB6DE834D7",
-    "ldt_key": "C7764DBF1114AB161E4466C48DB5A441746E5634D68FC267C974CDAA38F867A7427F5844EEEE7EAB25E8CB811DEFD7A40AA1C38484D3949E3507FAC5067DCB31",
-    "hmac_key": "6A46572FE998C253F213534B0FE6F875C944DBA34174045C1C991E050EC07E0A",
-    "adv_salt": "2985",
-    "plaintext": "12501D4AAC3B854F1A56A0CE3B2216F5327D8296777C5763B0299780",
-    "ciphertext": "F18310F993F3BCFDCACFD8E3A61D97114FE73B2515B952DCC4505860",
-    "metadata_key_hmac": "9A52C36B094238C0C3FBC0774B8CE7E5A4624EC70EDE250F1580548EB30BCAC2"
-  },
-  {
-    "key_seed": "D56B601EE412A42981E5F9BF1AB7113ED37F06B99BAD84128C1B29FCD99AB55A",
-    "ldt_key": "6DE9F0640326E8EF67D01DC252C3888F5C4BA0E3B1C8504022E328A6E679CCD1EF5508681D1A17FB7E76EB96C41EDDFFFAE08EA207A99256BCBA1C008A4BFEF2",
-    "hmac_key": "B734884B932BAA0ACFE10228C97194F9CE395B3EDEA2A0433AEB92B21C136E1C",
-    "adv_salt": "1810",
-    "plaintext": "F9F1E08CA0D03CF62B915AE0FB3F306D288F3F3F9F78",
-    "ciphertext": "04EC3FE723D1946741103511D1E7ECC723BB88E33060",
-    "metadata_key_hmac": "A8383FA1B78EA4D04A5AE8E97BA985518DD9AC3FF6E897C71C3131C63BE1AB1E"
-  },
-  {
-    "key_seed": "45BB14939A3827965AE97E6C098D34E1299F61FC22663695D0548DEDE836D06F",
-    "ldt_key": "C2B7B55F49F3EBD92E0488A8F131E7E221F122EF963CF74EA0CC84ECA58AF09893D5BFDE1A2A5FEEA4560D0022786FDB112FC2070351420938DF112A850F2225",
-    "hmac_key": "D754E3E689B9D6345A6476D6BCCAECA2E9E0D3BDFD71D6EE2FB3375F3891DAE2",
-    "adv_salt": "8444",
-    "plaintext": "04763A7656C5DFAB6A837336917B595F28",
-    "ciphertext": "84564DF47E50AF227B6F8D5E63A1203F0D",
-    "metadata_key_hmac": "49DDEB980A70C46077645F40DF71ABE5B2AAC895BA8636A5E8505DC9B9C1F47D"
-  },
-  {
-    "key_seed": "5EA520B5A5F71495CFACACC3768168C9BD00E1923095EBB6C73081E1332A34D9",
-    "ldt_key": "C59E13A7E1E3C3660737B37B29786658536BC0D4A04AC75F1092E7EC9F7D1CA21DFBA04A66A1AEF372F509DFA6A23E1574E14C701EBEF74BEDF3AFD989EEB01F",
-    "hmac_key": "A3390A01124DE94322178DB2128C964DCDB914898F356FB7D92F7D65AB78F226",
-    "adv_salt": "6967",
-    "plaintext": "F4F059C16741C8AEA673ABCE63B6B6FC357BBBF1A362",
-    "ciphertext": "846E2631110F587BD6F2E019B8385A490F5B01E1389F",
-    "metadata_key_hmac": "501FDF96FE16C164ED5CC86C68CD077855490CFBF23D6E33E1207F98F2B6C6FF"
-  },
-  {
-    "key_seed": "6D086EA32423D3814829003D2A4B80A3CECD9806E554E770769CC2DF85F17B25",
-    "ldt_key": "D1ADCC0ABD0A7676AB6F624B91EADC14FF103711E95F61FF3EEBEDCCDF6F771472EF0B6DF8C1A4148D740EF7CD7C74D39E114DD6C8223F628A4AB1D7EBB1DC52",
-    "hmac_key": "14F5EAB189EDD46596F0C6476051F0FAAFA98C0FFBE8AEB97EAED13FCCE61721",
-    "adv_salt": "1B39",
-    "plaintext": "2B2414E9FC526303C7AFEEFB88BCAF9BC6352EBF1D47CF",
-    "ciphertext": "95D27ACEECC6CD1683CA568B16FB1B352D2A49E4ED261B",
-    "metadata_key_hmac": "9B37A7FDECF53FC87FE673C5DA643F7CC3750BAF69AF8A459E46377E3299554F"
-  },
-  {
-    "key_seed": "FBB48C887B96BBEDFB63CE3169BEB2C9F83C3EF8D691F4DB40C665581E609F66",
-    "ldt_key": "65F098FF1543E2057A402DE0DF3464EADA379EA9C1E332C97AB38E803DC90EE811FF5F143D180D13C1216421025E8A966B6BB5A427D0E38595CB894104F9656A",
-    "hmac_key": "43BFCB395E05EC1B3E0F17D91DD4142BB379FB902D623CC20F226FD79F7EAD04",
-    "adv_salt": "1B93",
-    "plaintext": "EE6A2392F7F711B335293E904630456BF5D8FFA11E70885B062F4DD7",
-    "ciphertext": "4C0338F081FABCE8971695F79D1A50D34258B7113952EBCA7CC56D2D",
-    "metadata_key_hmac": "7F8D64FF7F7CE6F8992179C7D46821B384102D113FA632713077D2EC2567AD46"
-  },
-  {
-    "key_seed": "4C3068791225D0D0CE172BCDA1C834CB3F6D5A3EB3A8715F091241506F9D4FFA",
-    "ldt_key": "3137D6D9EEE8A4534C7EEAE2AAB3546C0CF9CD596393C9F6868D05ACE1CFA0AD6903A5EC10536DFC9CDC875C777601647452E5F77CA0C4B61371F4D5346C408D",
-    "hmac_key": "2FE9676B4AA49986EA5DA2401C133269B786317D646252D76304C6F054BDEC2D",
-    "adv_salt": "A13E",
-    "plaintext": "B1327C0315DA8E72BBAABB0FE86A8742",
-    "ciphertext": "7E6507EA1FFD3BC7DB35FC4592BF1AC0",
-    "metadata_key_hmac": "9540042EFCA7FC661E337DE0077115880ABD2CC39A8B5BEE17C2A5E622EDC1AB"
-  },
-  {
-    "key_seed": "4DC6D9D1C0BD2CCFB35512AAE3C47A6EA53F01D531E19243E8E34DA7239E6910",
-    "ldt_key": "085F9E2103793B6A062D69718245D6123E14DDFD370EA9140BC977D4B3AADC7A17E0DA21D9D32DC4D0415ACB0EC38787F9A92E96827576366BCB453C47FD3B79",
-    "hmac_key": "C2B5F83A37BB2B09C48A4B09A030ED89DCC7352A81296D8D25CF3CF3D3DF1915",
-    "adv_salt": "9365",
-    "plaintext": "7498C00FB8330954C9AF29A70172F609D87ABBC6B4C07AF2542A3F",
-    "ciphertext": "51BB7A9BD05CB79A9F53C16AD3ADFBF4FC7ABC6520CC165F5CBCE2",
-    "metadata_key_hmac": "2170D9F7E820117052B1892384C5EA49850C672D0B325B02D2B920CEF51D50C5"
-  },
-  {
-    "key_seed": "2E93A996FD7BA54B6E223913A6424A9E78C2BF654660D7A101EB60106F6E2D34",
-    "ldt_key": "086D83C6B9EF677F2FF0751EBEABCCBDFD3CC5AFA4B19F100BA9C5BA1906858AC5B0ECA0CC49BEB013199F658366EBB40D58F42A0CABE9C90A9B6A41D2D4A8F1",
-    "hmac_key": "81E7CEDFB01B32C2F1B57082D75A594A791267558ABCE8A3652C3BED7D1FE201",
-    "adv_salt": "9B0C",
-    "plaintext": "0CFBD922038FBE12059BB9C89566EDE98C94662754D0B5",
-    "ciphertext": "95757F09585E76F32BB65EAD5ED22A83C1654EC08290D9",
-    "metadata_key_hmac": "323A8A88C47EA852B343839D8234230A847E954D4BAD825DE2E22B2039A1F566"
-  },
-  {
-    "key_seed": "A741ADDAC9A6DFDFC504013B902D3DF2940160E903B7D22C1EFC2B2386504A97",
-    "ldt_key": "37F1AAB33A89C8BB79C8009D6A29B565704FF38641CC79C0CA09D1CDA73CAD58EC9628289C083846D764724E64763741CDBD0A5F8E75C24E28D883BEF3B0D221",
-    "hmac_key": "A9358E0F639E9879D985DF06D547CFF8ADDEE0D1F1BB762942C1359706D94676",
-    "adv_salt": "FE8B",
-    "plaintext": "B855E6C34E803990374A952351683A3EE11A9DC9AF8B9B95BE4B77D161431C",
-    "ciphertext": "DD5416F0008C369D9A9EEF4391886B8AB46C7DCD105FD186AE3100E591538B",
-    "metadata_key_hmac": "0BB1A55A428C404E16494855A9171439B2DDE2BFB708884DE308F8AEE3DF9BAF"
-  },
-  {
-    "key_seed": "FC20FDBE17A2011B670A9A5F8191190AF117822ADD3C9030969753C3AA7DD0AB",
-    "ldt_key": "8A2725155600A03DAFDE9BDDBDF51D0350A6DE3BAA1210A54E09AE6D9657B7C975AEF4E2CB057DB80C24B5FBD3601501B80EDD38E4E0B6A4F941719BE8E4B21C",
-    "hmac_key": "687CD66D7CEB8D59724E68FF1EB7602C34F19FECDD9BB41966C944CC7A1B0769",
-    "adv_salt": "63E5",
-    "plaintext": "1FFF9123DCFDA90C84EE36692CC1454675E8621AE5DA6A65",
-    "ciphertext": "CA92DB91D551BEFC693C508A10B256435AFCEB49F0FE3096",
-    "metadata_key_hmac": "F544A2B9F0207AF8A88E47FE408E926331D448C94504F6100D59A1AE6CA95BB6"
-  },
-  {
-    "key_seed": "686BAE3B084557346B32DF983153F4F1C3233B975244D358FAE4C9651B66DFAF",
-    "ldt_key": "DE80CC5B3AF1318B189DBD54E27B0DD9477E0D6A034B057E69DD1CF029DB2C0750AE67B47333C142034CEE7B2A68885690B109C5E0114CD165D2C4BF57200056",
-    "hmac_key": "BC7AA3D96F3E8688DE2CF939F0AB6B9E5797F0717CAEB0343A35E2D0ABE577F4",
-    "adv_salt": "A0C6",
-    "plaintext": "1DB632F5463172CFE9BEFCB1D8053FA2CE49989688",
-    "ciphertext": "3D54D60D010B56FDC56DC5AC1221BEE7C98FA6AA66",
-    "metadata_key_hmac": "9B6D110128D2868EEE1E539F9867070BAD300157F0F9F7BE79E4A3377CEBF699"
-  },
-  {
-    "key_seed": "F48CB050D56C56F2BF68C668C8995E3E1C85842BC9DAD696A792691B39D9B37F",
-    "ldt_key": "2BDDAC5E9CC28E34656A72CEAD0ED9B742E0EB07ED1A62CF284667ADB0F621303A917C828AC1E48AF23B30C91508CD93D90CEC11F7765356855F9881A4CEA5CD",
-    "hmac_key": "1D70DD188BAA206D12D8748DF8BD59CB49E7157066A4930BE0008EA64B2BE2DC",
-    "adv_salt": "ED24",
-    "plaintext": "EB7EB22ACC5DE936DDD1EFC1191E3E03DDC084E266C5F9C9528647",
-    "ciphertext": "A936AD0E462B50C982065913CB0EAB81E8556871621796DDEE5984",
-    "metadata_key_hmac": "BAE81BAFADA8E2D62FFD7D8920F7C3444D68F993D33E1EF8E7FD1C0103BDADA5"
-  },
-  {
-    "key_seed": "47FE92ACDA70B97D1C8E764BB9D85DC262D3E89FF73B6D486C3B9C28210752D9",
-    "ldt_key": "C0170B48E37AA9E271FCA686E30EDAA61CD32A5AE177DAD131D3510F78B89D0D090A5AF2DF4E71F0EB10BB3C12CC7990E979C24696E27B5523B8AAE77B25D431",
-    "hmac_key": "B4EA8E59115CC92EB24803FDF2EBF6F9E00FC35C26350DCA68A59444F57A3121",
-    "adv_salt": "5BEE",
-    "plaintext": "5DE1B7A4A4CD2A2816326C21A86FAA7709CB38BE5020C3C3C4529520B2D4AF",
-    "ciphertext": "B32932CA4C55F76290B1352435CDE49750DACCA224BC9A12ACDEE6273E1CAE",
-    "metadata_key_hmac": "6AC40E38522B09F86B9164EC5DEDC982F5D382E11BEF43C38BF2C067AF0EF414"
-  },
-  {
-    "key_seed": "3391E3D0A6622A01F0F8AE2CCC8004516C08EB6481FBCC4704DFBD13BBB9DC73",
-    "ldt_key": "046C2F962E1CCD28BE2E42020E274382429948D78AC48C4EB89623F19F31168FA47B6B58DE6E720A0138797FBF4C57D453B63C397437ECE8666D346E43E8236A",
-    "hmac_key": "A1C0C56FA3D27F8C0CD451FBE60358D70B12FB5DD2CC6C66F32C174F5EC9BFFF",
-    "adv_salt": "C2F8",
-    "plaintext": "C37A278E7D53F3E73D5002CCE45E134BFEE6AB49850072",
-    "ciphertext": "46F94584B0D4B87311107AEB7E8B2CDC15F6E26BE24128",
-    "metadata_key_hmac": "48E79F8509803B096BEB4A75139582757A57080B7703BF4F95697A822C67726E"
-  },
-  {
-    "key_seed": "38B9DBF9ABDA33704A2168E9BD52F90E337302E3F7E07F4B0B464ABC614E7430",
-    "ldt_key": "7AD9F3D9AE327ADBA9CECBC5F94DEF3AC7CF40A4449D2DCB84CABA549B0D36E7254C5C6DCEA577B897095E7B4EEC9EA8A3DFD24EB0EA09C6C51725320DAF2E89",
-    "hmac_key": "B5F3C37B210937B2BF1E6A4294C86FDD6FDEB026936051AD98004FF6007FD76D",
-    "adv_salt": "DDAF",
-    "plaintext": "E03C3082240189D29C59621D198725F8DE880517518F382EB96D",
-    "ciphertext": "10CD66A54174A7E413301E4A4E92B4D94B1CD73C76835797E1EB",
-    "metadata_key_hmac": "880FFE60BF390EA2D88F0B22F71D9E8BE4B1C2F856809E6CC5F310DE53FEE644"
-  },
-  {
-    "key_seed": "D191625D4D507328FEB93F5C2903269007E585E68E48D7AA468BBB2C8E42B2CE",
-    "ldt_key": "0CAACAA6340DD428911C41F2450FC15545B2F08B3EC034F3E7E875146F2A07DD19D2C7773C26939A95757D65774142A1D3A0C634F23E4A420CF65C3ED854AC96",
-    "hmac_key": "F31616B1820AAB2E7B2FC8ABADBEBDED259807E17ED665F589669EA1321CBE59",
-    "adv_salt": "E47B",
-    "plaintext": "2F241ACC16ACB854D72C3D54B5D26D77ED3418E92B51502928AE77A39B",
-    "ciphertext": "25449D1203708D50D30923F1F8B59DD9E6FA2CE247D93770E876598985",
-    "metadata_key_hmac": "2D8256EBC845953D61FE235FCD7F11EFDAFCF41B293A74ED235219335B77D943"
-  },
-  {
-    "key_seed": "BF5A6418C6B69957F5DB9F398C443830E9EEB83FFC7BAC667B13FF7A26906ABB",
-    "ldt_key": "5FD0795789FFF63B4CC94254E05BE3313D60A255BA212AE730CA97708DA619325EE83EEC295B95FEC20A20F38BD9C24C0F6BBFB8CFBD6DF2F023F9EC0C83E6C6",
-    "hmac_key": "D097551A0CB465F2DAD0E07144CBC511BEFC216FC7CB2158CC6EB876029CE17F",
-    "adv_salt": "2030",
-    "plaintext": "9CEE002954D084AA2BE8313064E2E885E7072D24",
-    "ciphertext": "887BCE7C97F197BE375984B06DC80886986DB0FE",
-    "metadata_key_hmac": "E132320C8132C545DF1C811A26ED663A7DC9C4308741F6326D8137A1A496C768"
-  },
-  {
-    "key_seed": "C69450316ADB438F3E3770169803B49AD2F7DBECD7C80D57BBAA416B7B577C5E",
-    "ldt_key": "CFDBC2A26AE39B76DC6194DE00B651363EF93E28022647D193C90CBDF0EF85550BCDF0092BF4A3F1EA7DACE2852851AE2785A6F1D8C674D4039A866FE794962D",
-    "hmac_key": "9F3133A3629E3924A829B2AB91EDBA4DFC1624CDC4BD50F58AC6FE88293EE1B8",
-    "adv_salt": "AB4D",
-    "plaintext": "DD2639454D2EB8E10486C8EAF591A5ADDC1DC329BD493C8B451559B98CB7",
-    "ciphertext": "C3ED01D3C97D88E4D2EFE0AFFB70C59CDED3DDEE93588E7AB55EE5440439",
-    "metadata_key_hmac": "EC1C7765710667BB77C5C7841C49F71BC246F7D16F44ED95C2877871A3900807"
-  },
-  {
-    "key_seed": "52493BC618AF6AA2F1A380AC1F2DDBA2ED937B11DC57B34A59B35983A058538B",
-    "ldt_key": "D4932E7D729E466ED4C8722AF5C7C2C54C78F7F1BEBF518226CF5B7D0F499F05DB2815252C323C6DFA4386EB9A83C3CBE18125D720EFCEDCA6079E597427D5A8",
-    "hmac_key": "B95A0460351EDBC7B7DEFBF4D7A593EC0DAA34223DCD8DD27403F2E9F1E31433",
-    "adv_salt": "60D2",
-    "plaintext": "52D84A60CB537611D34467DE41AC5902DD670E91B882539988",
-    "ciphertext": "994C6A1F16A95B0F362936579B2CD020680F4083987F302D6B",
-    "metadata_key_hmac": "9DF639DDF92DAE260504072CB8A1D9342EE89C50A47FB84E7960A95904EC5A67"
-  },
-  {
-    "key_seed": "B8229EB01E75BA575AA1DCB4CD2BC8AC322400A7F4F07CB5264788148EB1D301",
-    "ldt_key": "749DB570D2483B1B6F8EB620120B14FF91A84C6074441A145C5FFB5524CBA9E4D95A776EBB9B2B6A876EBB3D6119E844763E5C2C01D3FA7E10DD820677E3FD77",
-    "hmac_key": "E353F7A4F7BD1A6EE7ED475C8A02584CD00173076A80BB96D40D6775421C5F9C",
-    "adv_salt": "FBA0",
-    "plaintext": "045992CC7CDBF4755C1A2477D51C3F23781ECD337548DB753AE124BA1E",
-    "ciphertext": "A0E1F33F23A5CA57D7DCBA88CC770C55D8EC1BBC7DF974EFA10167B798",
-    "metadata_key_hmac": "146376174676638B43F84DDB78052DB6AB3C0C0BBF9DC8B3594805183826D178"
-  },
-  {
-    "key_seed": "4321B4688EC7D9DDA0732CD27624B1A12CCE1E17A77C99C17914B0AA82419F7F",
-    "ldt_key": "2699EA5FF56FEC771CB80ABD68854D56FD38FE6AB6B4CC4C43DB01A63486280D91E7980882D41B3F3F0F225319F765E62C247416866EB695FDF34C756010B8F3",
-    "hmac_key": "0FD6C64B002DE65BAF239D8A14E1451D49BB86C5614EC7B89B3E19D6C4BDA787",
-    "adv_salt": "2573",
-    "plaintext": "E1F4E8DBF9C3A3C5417C3D2F7F12EDC36EE7EB8A1AA7D140052880F1B43F",
-    "ciphertext": "9ED5913A29C5DE4870226D199E60B17B3F4093D695811095896BC5D8E0DC",
-    "metadata_key_hmac": "AA72421717A2E5F2FFFB4C46DD76B3B1ACF7CC874039EE4CD6CB67B5255059C2"
-  },
-  {
-    "key_seed": "32A9AFB7C4797E2E058F7EF5B8865B78A9DB3BDCE5DAB7BB1C8D81276C2FB366",
-    "ldt_key": "76CD81D5D79AD3A9C195BF40ADDA5D8FB8914FA7FD9ADCF0803294CE111F3CD94A926F9E015DB3EFA0EE7B4A063E1B9A4070F391F5F04EA784856C79444437AA",
-    "hmac_key": "C00A3A044C520A6518F22D22BE38C72C6D1E5DCC4F02154EB6C8EC7A472D7879",
-    "adv_salt": "EBE0",
-    "plaintext": "A6100086D2E8E08ECFFB3CCF2872CA162D8917B98F8F3C2FACB5",
-    "ciphertext": "4BB5B9015F6263D206C503172D8F5764B00AE45EB1021069C36D",
-    "metadata_key_hmac": "162294272CB61330CFFBCA718BC1DB3E4014E35F9FFB02544E3888EDF51846FF"
-  },
-  {
-    "key_seed": "AC352972B4F2719F04353418A2150136FA39C884173E78C3EF5056EDAB755EED",
-    "ldt_key": "60ECF6CF50A4D4AB7647454B76787F4677BD9C2593CADF4E3DE02D219DBB8301F886404225B7187B8B238E9BFE991972B9DCCB925F8573688DB39B775666D99C",
-    "hmac_key": "3B2820C7CDC30E8DD382CDA3821F2BF138B1052F09AC12F261ECD5210D78D72D",
-    "adv_salt": "1C85",
-    "plaintext": "4E45393C4BADC74193B091E29A1A779854008EE09B",
-    "ciphertext": "C560A43D859C1DC898A65011021935909E5DF8F990",
-    "metadata_key_hmac": "C9849ECBD12A6786A35FA3338203A2742AE4653589D39BE1DB3C06C3E46362ED"
-  },
-  {
-    "key_seed": "B2C0F8149154C9CC46229E9C18369EBFF39F0F3EEF32B8F27E12F65B2903E7AA",
-    "ldt_key": "6F9071DFA96A1DA54B7B34A12574DBB1005DAD47DD0F7F7BD03C24632726ECA6114A1D3912D93771CFFBA6FD72654BA962E9E49C544A28F428FC854C65F05F97",
-    "hmac_key": "A90B7956F9F7F63A125D4602104BC6B00BE03B92165B6791D1D9C0F02A6F6B17",
-    "adv_salt": "1331",
-    "plaintext": "0E53F8702AFC1C5D707A55EF3127F17B4B37C7409EDCF491CA83",
-    "ciphertext": "484697262B3B548012649333839EC0871F1D80B14798F1C3009F",
-    "metadata_key_hmac": "3622734AE40DED11E7317E6203FEF81E2CD9EF335F1B5367EE41116F2EBFC4AA"
-  },
-  {
-    "key_seed": "F4C380BEF6D3F0790DB12C2749B409EFB86C08AA9D577B942C528E45E6E066FA",
-    "ldt_key": "4FAC464CC036715DAAE8CC20A1EB2C752F2C4F65AC8767D807D465A285AEC934FF3453FC17496AEBCB162A5445902E2C7A7BA5D04407236BE8CCFA157E298350",
-    "hmac_key": "3DE1EA0A96D39ED853A9A1236CEC7362E3262BB70228264CA28EBB45626573CF",
-    "adv_salt": "D1CC",
-    "plaintext": "36B65E91A8BFFC7B97A818B27DA1746DFFFD56830EE6E727CF1080B8FD",
-    "ciphertext": "DA3D0EC3D7EE345446C0D3A69F59392F726388CDAA0D7FA42A974CE868",
-    "metadata_key_hmac": "A248A9B938D6AB6D6EEEBB4E27DA6D85FEE7745DEC464EC46D0FE23B817570A7"
-  },
-  {
-    "key_seed": "B9C652DC08F8A504D7545F709EBC88B5CC9E0C2A86DD2051A7E9F03FD1CDEC70",
-    "ldt_key": "19B7D4078E491DF180524CE8AA2A9D8CF29F821428FB669CF8F68C4B63AE2A1B3405E1CDE97F35F10086FD1943A40DBD8528D8769D269687CD6E2D0716D8FF65",
-    "hmac_key": "B9300189841F2274CD6077DF4C7004AEF8968137E21935678CA34BBB7692B18C",
-    "adv_salt": "55A7",
-    "plaintext": "84EAE0A184F385D615CA217FE434FE4B4B1FF0",
-    "ciphertext": "F1C9E103D0E74A2EE6202667FFF755398CE4AF",
-    "metadata_key_hmac": "A7AF9B4A97EDEF7033EFDE386E2519656798230489248B139547FE50E5680ED6"
-  },
-  {
-    "key_seed": "7E3904C173082FFA3626FA79F4AB98D78079DB04A015F395C4334872429D9E02",
-    "ldt_key": "CCFCDC6CC91B9480177E994C8CB668685C383A2279C4365635561EC866DF07D21FF8B6F05794331FADF21CEBF86BCF2B68F6779E9F8DF5FCF45D5B983D877D4A",
-    "hmac_key": "7AFE8A851490B11ECD7E94FF528BDEC30EC65B3F8BF5D27A8D4ABBE98A1A8779",
-    "adv_salt": "7831",
-    "plaintext": "B91B4DABDB93A17AF70C75FE0AA56B2C9EA29E1B450D177419AFD7D3",
-    "ciphertext": "222B0702B74B1B19B866726FC51FD1BAA72837E3AD36A3E09590FFAD",
-    "metadata_key_hmac": "CC8C4A4A380FB928319DC3B7795BC02ECE8CEA1244723E7E65DA474E71922F30"
-  },
-  {
-    "key_seed": "2818D375E41F4BFF60BC5D400165FCD87311018B6F29B05890412393A0341D40",
-    "ldt_key": "475A78B0A6F340FAFB40B30D5E1C22CE7F0FE7FF9F193E95DB87A64E90233D707D2BD91B6A18C7FBFE1D63C00AAD1008043D61227E874220246839DECD31AD06",
-    "hmac_key": "05011F4F63F4DD955FF26582E8D6F40EEB1F72F6136BB741BFE66DAB3366EDDF",
-    "adv_salt": "4916",
-    "plaintext": "5B5CA3B4765B3EB9F28F9477F2BE9EB39ED6F71A",
-    "ciphertext": "384C0B47026B195EADF7A0EB288CAF28A8A8D7FF",
-    "metadata_key_hmac": "680E2C19C42A2C4BB032CC5855B21C19647F2196043BAC75060FD7BC852288A0"
-  },
-  {
-    "key_seed": "293B6D7B61EC04FF8B6324CDEE0B4215696B69EF91947293856DF8AEA7A33120",
-    "ldt_key": "5748E0A5E16A3506C5C6037262324C47DEEECF82D89014898F42168F26AF064FADB35BEF373E83E087F47D6AD2E3ACA442A6A2A90D04AC13A9D2791EC60DAD05",
-    "hmac_key": "942DFA6F88C0D4A8CC126C1F4E596DB791700F51238E73B6D00329B3100B72E8",
-    "adv_salt": "0B70",
-    "plaintext": "1F2FBA9E9CD01EE3570D9CFC677639A0A200083B47CBCCD9FC6367C6A31F53",
-    "ciphertext": "BC7EF3E920713AEA138D84039C99CC8FB42920E1158A1FCC287B19F1EA5927",
-    "metadata_key_hmac": "BE4555125280C327F2AB91B5086453A69D265BF882D38F241757828021703753"
-  },
-  {
-    "key_seed": "D0086215DC791940C22CA11AF2E211656608183B727E634CD45E2EFB02DB4BC3",
-    "ldt_key": "747DC06A5CF0E6B3544AD105C6E4646EEE1DA9C9A85A5ACF19FC4800F68D6D0E2B41D24D9F955626FECD92D44325703AEB5ADCC9EDC4FC4146F736ACA8AA1ADA",
-    "hmac_key": "1DF706DB07F3404922298D733E284C95A59D0F5DD078E2053D0FAF9A17AF2017",
-    "adv_salt": "5F49",
-    "plaintext": "724683E15CDFD3426D6AB9DDE6D0D125C8D5",
-    "ciphertext": "A7E55EB6D540B0D4572851CD336C4A66F7E2",
-    "metadata_key_hmac": "DAE761B6D5295D790AF8C5422DE5261E152041F5AD42A0B456D5AEE6E7D1F354"
-  },
-  {
-    "key_seed": "A71F8F6908A57048100CA20664D9910F1706B692337F605E381133A6DCE2CB0E",
-    "ldt_key": "8386F0BA6A123E4E3B0B20F9EA2986E63BC56CA98AA781FB7847533180F9BDDFD399598724282AB64A4040E44D149075984FC3239FC91980044E5F65B35C4E00",
-    "hmac_key": "430EBC4CC413E3350E61482401A1D06DE152759DC4A54162998B866F768CA6D2",
-    "adv_salt": "C5E1",
-    "plaintext": "3EECB162209101704CED7A0D843B8E9B7E932B0F82C4A9FF23BD164D0FFF",
-    "ciphertext": "1C4E76CF6ACF99EED45D731A7D864E78709F931147BCB5005E47382B4160",
-    "metadata_key_hmac": "4DD68F166C06B162AD8F84C1FF167C19AB8A596D50117487AF26E338A4D847A3"
-  },
-  {
-    "key_seed": "3B98434E78544A66FFCED9EE8D7B2C063B7ED7AD7AEED29B5559DC39AEA56DF1",
-    "ldt_key": "B953070D2CDA4353FE4E1B108004F1BF76D7CEA08019C64C888FBF702F67630B0500A7FC38231E59154786DD476C525E89F0A5BF95A1F226E20AFB5B7A856D10",
-    "hmac_key": "B2403EDD29D4C66C0E6333FF49EF200F68EB66E9C3C1221880849418ABD71DA7",
-    "adv_salt": "8507",
-    "plaintext": "1AE5221F2BF314C8E35555188C6D9C0E48A446B5",
-    "ciphertext": "7DED8D87E18E10496F0B58014B68CD2072C7890C",
-    "metadata_key_hmac": "E0FE0BAA12BBF15AB1B0D5AB48389D52F0846C354D34FFFDEC80E2B3059CB917"
-  },
-  {
-    "key_seed": "70DE3AB2D81F7ECC3C8BC8F4842C17E71C4B4A16486C9FFF8A2A08BC2BB45F61",
-    "ldt_key": "D4E93D2956B9A53A5D5504A84E0B34CE48A949DEE52C821392E63442DB5973610BFD38E50FAEFDC2FB2764C298630E32B7BC2687F67F5C43773BAE4C7691D087",
-    "hmac_key": "07F6A1092DCE7A0ECC5A25B91770F6611A1B259E1AEF307B7CADF418D0E7BEEB",
-    "adv_salt": "DF66",
-    "plaintext": "5034C0167A1F866508665B737DB926E5B7B2FC134B10460E9C59AA",
-    "ciphertext": "AB4E6DD75D1D2D70BDE86441C93C6419B0FE469AFB22174D8832B2",
-    "metadata_key_hmac": "F6EBDDDA8E24C585D980C9848AC5B575B21ECBFFA7E42C29A0A32667C776DB51"
-  },
-  {
-    "key_seed": "D6070E362975BE4208C68075DC58AEE151560F34467C339DE799AD1F4F89B43B",
-    "ldt_key": "F5B3013F1D2C809CD5972C4FD54DC52AE3D2CA049ACB9EFBBF415A6D70945B574550A5D69B47579426D3710F2C4C0C717EC23C7B59EF74CA3BB7FCEEFAFC8890",
-    "hmac_key": "FA77A236AA7006B1CF1161E8C9FD37E9501067EFDAC1F05FF7AE2C48FD5212AE",
-    "adv_salt": "38C9",
-    "plaintext": "4B7F78A6F253F71F7BC3BCA63706E5B3A87F",
-    "ciphertext": "D6743B0FC3C4849528CCD024E658C3001164",
-    "metadata_key_hmac": "A1FD7AFD923C5E086F5EC7252ADACDE857C43743CF3F6369B062191A779C7174"
-  },
-  {
-    "key_seed": "06D4CD5442C5FF74E3A72B644AE3117AA39C58AD5D6B2259A95279BD4E50ED3C",
-    "ldt_key": "5FEF4BAFF726D4C0649FC5EFCDC7D4C7021E29AE3EE9F300599D5507BBE822237C487414AA54418DE0DDC35011E8EC387B2FB11EA242E6E9136EA550DFDBD82C",
-    "hmac_key": "0C520AA5513E4B47457DC6A788890108714C05BC2B949CCD3437138C6F1C593A",
-    "adv_salt": "4244",
-    "plaintext": "56CF46926F1B3C2DDAA0DF35E126F2E60250",
-    "ciphertext": "C5C14C2EA129C85575F9437DBD36DD9640FD",
-    "metadata_key_hmac": "0BFD27EB29F997A790FDC9B951BDE7FB07F18144C0332F34257300621D6125A5"
-  },
-  {
-    "key_seed": "25C7513D18B3B943099EE74FA3964B6D3AF65A85BE21EC1A493463F641B0993F",
-    "ldt_key": "8892930D9E760904A6B57635D9191051C15D81432176129121DCE1D726FFE389FA1AE258E2782DC2536259783BE993C768F6CD19C5D42AC705973B9866F965A5",
-    "hmac_key": "6A22C2CEC91B54BB1DAF7025CDA3010B73085E5F0207918C10006CC02E1402A5",
-    "adv_salt": "269D",
-    "plaintext": "86E9F2435157AB4B5BE5DAAF51D5E59840D0994CE3CE0883D2611924",
-    "ciphertext": "52EBB65E85716A5FC5F364A92C753EFD030319E0FAAEC5F59970A5BF",
-    "metadata_key_hmac": "B5CFBF695F95A5692CE9E44F6153823507BA69CD40349D9F5E2EDAD8DD23F21F"
-  },
-  {
-    "key_seed": "6A3819092F4B9B1D287559B3333DEEB1A5C454C60AAAC5C338C859CE8A0EDF1D",
-    "ldt_key": "965901C57078EFEF917F2117AFA8D10965F1AB7688428AA0EBCC2CEC5F4005B8CC9F9B5F714B07BB4544F6A6EC49DBA71EB99E23ED9861E4F051B0093A649795",
-    "hmac_key": "A014E29D41094A6B2AF29F5803B09958F3B413724BD1DDFF9DE3A6922F5D33DE",
-    "adv_salt": "FE52",
-    "plaintext": "3E338836AAD143675865928DE46D0569B3293938",
-    "ciphertext": "0CF2BBFACE17016EB783DF024A6B93022F7F8F8B",
-    "metadata_key_hmac": "5C921A36EF9A5BC58E6A89FF629514312D2FABF72D0CB4EC838D0FABE0B163C9"
-  },
-  {
-    "key_seed": "A3908EEB18C7B14040AF677D936F841FE16B743A3B5D0C9B7B7437FC4A6BA882",
-    "ldt_key": "537B347160C78440DFB3BF95E7D9DE916E6B25B20A74895016AB2BCE4279597C46FE35F9308C9902A20E37CB2AD7CDD563CDCFBC31D69B7723A99F279EC4768A",
-    "hmac_key": "51F878A943B59A2E31B968AE9C07B76FE066AB3503583DEA9063F50A30F4B2CB",
-    "adv_salt": "16FF",
-    "plaintext": "720F44EFD5E3E49F33982B9DB21065E4D810DC5DB39A1ED0",
-    "ciphertext": "AB7006CA73C4740348DE94EC6B3FF8026532551D8D007EB6",
-    "metadata_key_hmac": "27B116068950B115B39B2A68FBA268F3BD2CD09D5863D68FEAB33BA039019644"
-  },
-  {
-    "key_seed": "BEB5B05E7982F57E486E70F4D06895FC35B75138987511BD89195A92F0D8DEA1",
-    "ldt_key": "923863B15F3141EB2524D2302D363AE74E36E6349D9F3A0D074D098D8992465151D26913D8202E7DC45F057F02727C3DF35861D861CC31F87EE212DFFEF9CA5A",
-    "hmac_key": "3B6868C1DF85C268AC1F34E6AFDF5F6872754B83BEEA87E5077D50D5403D9E6D",
-    "adv_salt": "305D",
-    "plaintext": "6BAC0E6CBE03B0F7C351156BBFEE2B9D5B",
-    "ciphertext": "9B360B3073625C1ADA54FFFBDCA6EE7B89",
-    "metadata_key_hmac": "14896DA42B1F8353A2ACF69032D651A852D40F5DE20B24EE990FE9D829687E91"
-  },
-  {
-    "key_seed": "6A0E2C7C7E761BB93AD9F18C39AFB835754CC36D9B6D0C37007EFCEDE3E194AF",
-    "ldt_key": "36F4D0B92C00D28AE0879F99AFC50B60F00CBEB9CDD6CFE59BC27E20312F4810482DA55332D168978D6BF6EE048CA21FAAFE115BC307BF91508F8FAF917F9110",
-    "hmac_key": "21C79D059AAB19B891D0D463CBECB7FA1C0B893C04B066E23CC1C7FFE16A5416",
-    "adv_salt": "88F9",
-    "plaintext": "108C588C57004BFB98000921739695DC68CE92D05F9252C423E929A9E2A66B",
-    "ciphertext": "F1EA3A73A26E0ABC68C096B43E5EC256CEB7398E705398999EA573EAC9E4A6",
-    "metadata_key_hmac": "B3D5C50176029E28512FAC5E685B8420890108608BF4F7D019C627ACF309C4E6"
-  },
-  {
-    "key_seed": "31C7CFADD40759B0E825EC5DBAF0931812F7E2B240F3FFACCCCB0F683D22ADAE",
-    "ldt_key": "6D174F3A9D35C18C5873FC6C0A78D804F98EF4F131930DBE7A62C599ECF5432F58A10C2300E5B6428E4C0DC7E0510880DB7AE4FFA2791F6276B4DEFD8D5141E9",
-    "hmac_key": "A0856C106CB1567EECA6F20E17038971EE7B56C67E0A91F3FA065AB51A2D1ED4",
-    "adv_salt": "1EC0",
-    "plaintext": "874FB6A79D5FFC5302E140950B2DC842FA9797D2FB6034DF74EA8A5373",
-    "ciphertext": "13ACC4B28FB9086DD45C48D52719129E973E4C4C443C08563A52DC814D",
-    "metadata_key_hmac": "273BCAAE09C489A4C8F388958D8B4A9876D7C6BE02D0DE29C75EEC96A608436A"
-  },
-  {
-    "key_seed": "5956A478D8411CA8A465158C2B3006149908F1EFCCBF8990317F20999AA4F70F",
-    "ldt_key": "4A4F052F402D0CB131C5B4FE1131AA7FD5C20ED9C2E748FD0EC1279F75858D47D648E5C0A457BCB65F222B3F17EAEF11940CD46A3A94805E51D11F16826ED5B7",
-    "hmac_key": "41F71CB578DBB33D3F003E8D78DEC4CCD6917236F0D9A0AC02CABAE06C2B5D65",
-    "adv_salt": "6BA1",
-    "plaintext": "FCBEC0CA4213B4B37E2DD6C8012AE01DF1E1242AA434A148D359277E62",
-    "ciphertext": "1E9AFC79DE49F4ECD3AEB564FFE40E4EA6A5194E61ACA01D8A9E9343BD",
-    "metadata_key_hmac": "BD3C3DBD20B210AFC2E33546377BBA80BC72A87EDFA13A34F8B5327C44834223"
-  },
-  {
-    "key_seed": "F148AE1510BFBF000897FF10BC5B28F67AEEA16C62A21EC917C25303DED409D6",
-    "ldt_key": "9CD5DB4CF2C2AE6147962770007D6C55BD3590B36F78DBCF116A8B57231DE71C26B023060860768F5F74E6067C05E3CBF3F06969F4763D3D4C40AF761BFD2292",
-    "hmac_key": "5C8B202DCAEFBE48669513A0A1AAC66FA6DA1E5C21442061B1A88E226C8112D8",
-    "adv_salt": "F331",
-    "plaintext": "89AA70C2B5389C5D1D1FA5B79BA158237CC15347EE30F0DDA3EEB2",
-    "ciphertext": "8DD3EAE0D868337481296D1A5E573B37FAB61C545D24D6BB623EC7",
-    "metadata_key_hmac": "221ED78ACCF95FDE481AF3326411BF7E818A218507C63C819866ED56385DDDAF"
-  },
-  {
-    "key_seed": "61600C7568EFE18CE65962F07F83A858A4CCC4F2F8770E6A86E0AE198FB1C771",
-    "ldt_key": "64803B5AEB725BE7E9541A7E7B2EAA9E43C80D6DE2A8700B89F48AC6AED908A894535C56B5AFEF66A3DAE9345ABA78B3E6363B81CA0BDC4F08809C32CA0165FD",
-    "hmac_key": "B4A02866B9F951B61A74FAFA06A865D5B1B6251D26A62394FC83AFC4F64706AB",
-    "adv_salt": "4624",
-    "plaintext": "312025180184B7C1B9AC2ACB4CF48AB26EA6924F5423AE7DED",
-    "ciphertext": "AF5C47FB013B6AAE01CD52207430D4506DF7E00927A943FCC2",
-    "metadata_key_hmac": "7980BB8D1F4751C04CBF6C5D4917B2CD6B6B9443777F7B5486284D0A47F305BC"
-  },
-  {
-    "key_seed": "F69E622B35584B35602B42740D4FFF8DA8871A984176CCDCA91353F9D22DB315",
-    "ldt_key": "05D4C4E9BE27F96890EBBD83FCD4E434484156EF823501EA28F92CADE68E564D10DAAB07D442AC8591349EB228E07AE51D9F2B302F6EE44892D65423F0D467DF",
-    "hmac_key": "1C78A8952309D55F09B6E806824F082EB42C1548EA9A860C5ADE79B058C223D1",
-    "adv_salt": "A33B",
-    "plaintext": "6A417988704D975DC2DDCCD8823986C1F4C74B288FBE72EB57",
-    "ciphertext": "C37A68F6D2B29B640870300FD08DD35A5F0BC438D6CAC897C4",
-    "metadata_key_hmac": "601E61D942E76B8997DD60775AAD1729D85AD7337F18174DEA7D67E3736E694C"
-  },
-  {
-    "key_seed": "83E3CB4033F12083313C134BB30C47F5EA0FF6A00727A3F64FC1AFA040B14E60",
-    "ldt_key": "8A4C959D115BE5FFD34936F1876DEEEB0836CC11B85FB29E4D914AC59D649B4C7B1BA55993C454572EA4F284718A56B2F66C5FB7BD7A0584E7206525DEB2249F",
-    "hmac_key": "2C4481D23836DAF9AEF1871899359EC2996F322425E4EB126CD8126A11568524",
-    "adv_salt": "66F1",
-    "plaintext": "8A7AAB07B16F67A50F62A57F62E7604D9EF3DD",
-    "ciphertext": "DCA7C698E366351965619E854DCE96280786A7",
-    "metadata_key_hmac": "1C17DBBA079B2949E4EA054E6875EDD41A8C6BFD7D0E731759549835EFD43ED8"
-  },
-  {
-    "key_seed": "9DEFCDC08EC6BD9D9D5EE8B4F1ECB0D080E80BA131D8E88C7108775B8FB1AFB5",
-    "ldt_key": "EF241E356B44D5FD66FE56F10AB825DFD6F80A850360A0A97427793B575E001070BED7C33D520DE89C249F896DC987FCC689293F469D0264CD8DFD92E3FAB2CB",
-    "hmac_key": "30FA2AEAA17C3A9A9E01139DE85BE7924C4212D382A2A924593B0772C84594D1",
-    "adv_salt": "0BE3",
-    "plaintext": "2B35B8AF25D51FD081579D2CF3FDB0B66DFF",
-    "ciphertext": "483147378F66A935037F06ACAAB160429969",
-    "metadata_key_hmac": "613573DC01B49019ED0F98559D39313F00E8BBD1B8AF91F23ECCE7DE5F535751"
-  },
-  {
-    "key_seed": "EB477CBD2CC5A7CD961412673019D66F91FA55E676892842B8363694A780700C",
-    "ldt_key": "066EA314177A5DBE76DD89A5E855E790B63CBF515E008D9D0A3CF24BF849735710E76B8453C0ED1F2D77C78245F022A6235E2A6760A53089110D60F3957B19CA",
-    "hmac_key": "60669EA78CCA2BC2828878FFDAE7DF7BC3F0F360A97A6806C1C76D19C90DF5B7",
-    "adv_salt": "2557",
-    "plaintext": "D35F9D849F3EBF4072F9E27CF2C8B9B5C97664A06C7E0360407A260941A00E",
-    "ciphertext": "FB6E965A8D084308748A5E70DD000544C2CC8BB2420426848DF29D09A57E4E",
-    "metadata_key_hmac": "7DFB8ACC1E2547D2D9DE93FA873D03EEB1911979388FE21B69C50739AB45043E"
-  },
-  {
-    "key_seed": "74AA682518382B6E3616DA0382721CA5E8DB50F030A50AC08276969E0562F4FA",
-    "ldt_key": "B1A8CF202F4B3922456528E5566AB7E71E5C2A6B7FBC65EF9CF030DFF5E258DB44E4BF2D3224F434B20A14FDC7384F5EA87969F403A1308719935BC0CDAD0FC1",
-    "hmac_key": "6B86101900B15E6E052900939C8FCD3B7594904560FB2C99699C5BC0AE78676C",
-    "adv_salt": "3895",
-    "plaintext": "20325082990043331BD2D3A80BB87E0B",
-    "ciphertext": "96DDD4BFDC23FD460754FF6468E7C332",
-    "metadata_key_hmac": "742E4068B5CA2992EFAE77AE5D6A85FC2EE4FC629048F57A7C113CF198266E8D"
-  },
-  {
-    "key_seed": "9A26B82724E2E75F688219290A5E71306259FFD45358CFCAB56820AF0AB84825",
-    "ldt_key": "716EF3C6A8E6D03AC8B48FB34352A3DF2BF8E585D3ABBC219CCAF7B724BC9F4A910FA552C9A7DA90943F22500AE0D795403FE3A39F5E06BF1CE082B82ACCA6D5",
-    "hmac_key": "8740E6B8E4AA733172A5C1D7BCCC2E38019460DF4A145A16999BF7FDDBBA6465",
-    "adv_salt": "E247",
-    "plaintext": "2C99B82779CBCF25E5023FA300044F7BD8E73711D183",
-    "ciphertext": "1C4E395A87CEC587FF9978A6C3839262C9CE2B1A4383",
-    "metadata_key_hmac": "36722D3E0A836A8E7A74D5193128D3BEA3503688DEEF566DDAFD1090D4A422E9"
-  },
-  {
-    "key_seed": "2372AF6AEE19D862907446970241429B0212F31C1531817FAE55B789DCE83419",
-    "ldt_key": "7CD870141005EB628EEC6185B4019F0F038CD76BAA1B559ABAD2FFA079209609537914652B3FF00ABDECBE8718D0890117A3B2472D3CC0EB72F04D0FA7083961",
-    "hmac_key": "567E5FE57EAF46A2E2C780EDEE97035315185FD8956BA5C4D76A78853663E746",
-    "adv_salt": "E9C2",
-    "plaintext": "2693825DC42C1DCF2A10AB81D9365BF891BCE6006C2DF0145F",
-    "ciphertext": "61CDC03B68C66D50E063FD4C8F647877E9D3252584AD713978",
-    "metadata_key_hmac": "42BBECE7CD1856970D01DA3E8734A65DFAEDF457DF1E72CA3599EABF0C22C41F"
-  },
-  {
-    "key_seed": "9D08404ECCCBCA0EE51E331B6D696ACD3C7176999823E2C7EEF4302FFA9BE0B9",
-    "ldt_key": "427CAD8F30A04435924421837BAB322F768179FBC4B784038984F8DAAD0BB82ADCE01C12C816A12BAE4623641BF1BEFBE1BDEFFFBD93D4BADDB001FE1C50337B",
-    "hmac_key": "EFE1F944399E5F414382AD4387C61CC384B61E99CC0435F3096E942219B91366",
-    "adv_salt": "B75D",
-    "plaintext": "350843181BA9E2B6ACBE63B7BA7D7D150E74",
-    "ciphertext": "5E9934E69293271897730ACBEBB03CA6FE15",
-    "metadata_key_hmac": "2C1CB1BD3BFE989FDACC33EE426239A738C73A16173CC80D7EE05EC3F8404374"
-  },
-  {
-    "key_seed": "74A2BB7471A0592D8DAC87B95547A1C9A3E8CB1AC308DF345A4DAB559034B5A9",
-    "ldt_key": "443C06E1C7F2B1752B274C23C289694FEE4FA80FEA17652F10E02AFA2E2C2F36735AE72D8D83EE6ED574F60DE7E0017E4847784772E8BB3E2FFBB191804DCCA2",
-    "hmac_key": "09566FCA537C7633AFAA67AAD2912B0D97C96CEF220994AAF0A33D2E02BAB4B1",
-    "adv_salt": "59FB",
-    "plaintext": "605007ADD735AFA8D07288AD1667877C7F33D8B574B3664540B3119927",
-    "ciphertext": "C398C214954F7586A9E3ED39A942D48D42EFCC15D259C91A0C17D390E0",
-    "metadata_key_hmac": "E7E896615023D1769C951BBBF61C102E5A99FC79CA90A9EAE14FB4D3ECEC750D"
-  },
-  {
-    "key_seed": "5CE66AB96688C6F777EF69861F715B7220C045147C30874900DFCBAD34918F25",
-    "ldt_key": "E7E24A6081A7D415F04EF19C540844B29837EB7AF9D97356BCC4AFB6CDAC80164A25448A12FECBDFA8E35B285A383B1BFE20E0CD0606E4E8078DD306BCB39405",
-    "hmac_key": "8DAD5A1D48DDC9C8496E5861514833D1B84B19787D4E1B40C78754B2EF047152",
-    "adv_salt": "3544",
-    "plaintext": "8862C2582FB4EA240D177A750CEF79C34B706E78AC",
-    "ciphertext": "DA89939D9BCDC1C14D85A5DC0A9CE0626522AE0E8E",
-    "metadata_key_hmac": "6581B9A1D1F0F4D165A0167E18C7295A731DC235926B8E410169F1C63A4DA4E8"
-  },
-  {
-    "key_seed": "EFC89CAC472E5F29C19909114C58ED36EE6CD3ACB421DF6E8196036B0BB2B26C",
-    "ldt_key": "27260C19A758519766A2524EF2930AF8312BE757975561E9C850CBBF9EAC6BEE0432523725E7DF2B0186F95BA26B59AF8F773FE2E412D28646ADBCAFBE48F2F0",
-    "hmac_key": "D0112796BB95B093CF956682B6EEC9929491F2CAAE8FAD2C957BB561F178DDDA",
-    "adv_salt": "4B32",
-    "plaintext": "FD682E2BE832CBBEEB345A4D38B78BA90AA637BE16BF51D4F9CB96",
-    "ciphertext": "CB4790894F1FBD9598C560CCA62F478DC17FFD4A1DF3AE520C6278",
-    "metadata_key_hmac": "A2BCD15C733F585645D887233A53E42DB824AD128F0BA0D979ABDBDC8EB376C1"
-  },
-  {
-    "key_seed": "1C9FB26139981EA626FDE6F56495710AB4F459F081BB68157D0E15818B85FA7D",
-    "ldt_key": "9E89B84362A0AD1CFFA71520222CDF3797CB0CD27963747DDDF30C64CC71471DAC44A480099B2CD48DFD7845A9B62451FD1FDACEE14E092A367B24EE1DEE3E06",
-    "hmac_key": "B83A1B3299AF327CCA3EB519EABC9024FADF40E90F73BF2F6A9B1282B31AE24A",
-    "adv_salt": "27B9",
-    "plaintext": "115B2F632D6CC15F641AB023B8A19E8E9EE3",
-    "ciphertext": "E19E55724D76166EAC48D7830F00605E1B52",
-    "metadata_key_hmac": "8C595243A2DEECF05FCB3779E5B65B89437FEE12917C70243244C07A73534FF9"
-  },
-  {
-    "key_seed": "12BE8D40B5A27A6FB578010630144DB83ACE0883724A4F521340E540E2E0C68C",
-    "ldt_key": "89B8F5A1823B9677AB985DD4DA5A16B24CA4DE066A0973DA3C37D0BE255AD0BDD05A747766F147C09ACFE92F89E388B5C27883434D9E4BD4C67A4E30955D4C8B",
-    "hmac_key": "7EEBC70EB7BBE030B3010F8C6F9BA6FD1808D3740F883E4739AD3D3BF91C0654",
-    "adv_salt": "1190",
-    "plaintext": "04B822A1371E8C9DC26A697DA51A9C77",
-    "ciphertext": "2DC428ADB7F503967850DB31047E887B",
-    "metadata_key_hmac": "B9F5FF9D4B7C4C7DA4F63598A9618417D1852E7CC85CC86CCBA8DD0497DD8D6E"
-  },
-  {
-    "key_seed": "F37D48DE8C8C1004B7F9551D590293B45DA71C45C4EAABAA1EFE2F28963D70D4",
-    "ldt_key": "11BC252F985F1F29B94903783DB6F8DFA80B41C13819507E5B4456294C04C5408AE4FD21EA7B5F2FF83CBCEB8D623BB5A6213CCD8DEEEAC85B97DB39031E6A04",
-    "hmac_key": "94939EC6BBF5833A6A2D54E4CC76A19CE4ECD27443B58465133DB9E8979AFDB7",
-    "adv_salt": "CD14",
-    "plaintext": "6C8A96613B5ED22D15BF5505318AC7B6B72F4E6CF78E9228",
-    "ciphertext": "9B0DC394A40DADABDE9454BF83DB14DF7683BF91FD6A6BE5",
-    "metadata_key_hmac": "28BBE4B7634E6DB87EA9EE4CB6D0D02824FC0D02FE169EB277050E877F5325CF"
-  },
-  {
-    "key_seed": "30AFA37FC0EA499F56BD5E3DBFAB8EC4F8831EF774EA8B470D06711E2526D17A",
-    "ldt_key": "E738F03C91E9651B4798C43013DE639FF8BEAE84904EFA3B3293F52B6DBF07335F32B00CFD3D0108EE0C05EE0EA18D12ACA8FEA0B04EB082D0421F1EC93CA891",
-    "hmac_key": "A6E71919A74F69E525B8F6E601294484F74399B8343F4A3EE4C39CA8E884C9BD",
-    "adv_salt": "9126",
-    "plaintext": "49B90ED5973BA948D4AD48CD58B18FF9BC0AE8F515",
-    "ciphertext": "47FA6B4679CE70CED6D4D459BDE5A9D70CB55F1D73",
-    "metadata_key_hmac": "A482A818936CBA441D8002C992A4E01FAAE6CD28BA441F635D5A2FDD942C90BF"
-  },
-  {
-    "key_seed": "8528A2F34296E3729E50E56A55855F59FBC902DED9D2377DCC39BF7231F11172",
-    "ldt_key": "0F0E76C4E3E7BF50E7D52E2AA6693419CB63824E94A0D4752D61E7FEBD0BAEC9939063AD802105C19BF9A00FDECB95CCE41E1DC198A8E496CC9AAAA8BBCDF7F7",
-    "hmac_key": "DF969A9D68140F537FF7FFF16472AEA113894FA265C84B75540C207CCF6BB85C",
-    "adv_salt": "105B",
-    "plaintext": "4D85C3056EDA3F063DDD092586D497446997CB274174",
-    "ciphertext": "4CF52656457A215F9D44683B783DA8D0D526EA42FBDC",
-    "metadata_key_hmac": "850891BEE1675AFC5D85978754AE1A328E8AB5BDA781B33C9614694FA65B168D"
-  },
-  {
-    "key_seed": "9D0D63B34C39D97396F1D4300ECD6F1DDF59EAD5CA6C91100BA47B0D53770A21",
-    "ldt_key": "29FCEA9F70AFCAD6BE477C8A7DB85A9A34CA55C8ED3F1AA0013E35C3A911B3E947537AFB66A2E244B252BA1551081FE8C559478B2379A677DE0E18A22475FAAB",
-    "hmac_key": "EDDA408416F36883183FD95538E71CFA009D9D97DFBBCB6017B14D746771A457",
-    "adv_salt": "7C76",
-    "plaintext": "24997451E6C5935B33124846BAF868A0F8B5910F1F1A9EAD",
-    "ciphertext": "C322A62CBA22ACA0EF4AB79E14A58CDBC293D50C354F0148",
-    "metadata_key_hmac": "6EBD3E2948C92FFF55571A81FD68E1DD9A41CF9D29AED0682D95BBEDEB9AFE07"
-  },
-  {
-    "key_seed": "CF3449A532950AA71097A57B4FCD9408C8F17CBB686FE08FEC1591FD5E6EA7F7",
-    "ldt_key": "5AFE3D6D4CDFD4D5065723402B7A2D9A7734301100ED0B8E47B2F85F7F20A56CC567F9C8456092DCB31D827354306DF52810B69F6EBF13734E9610893ED2128B",
-    "hmac_key": "E8ED4FC24E00C92C57D2F637E267BD016B2ABDD60B9983C554B1C183F09D84C7",
-    "adv_salt": "762F",
-    "plaintext": "7335332B8D79B72EDF461FDB8A6111C3",
-    "ciphertext": "3AC347CD0329B7ED01D92E95906BA819",
-    "metadata_key_hmac": "58ABC3309FAFAF2E0051D91743BD6B5CB364C0A2F278C818A084E3F58AD017DE"
-  },
-  {
-    "key_seed": "8502CC048239779635C0C16622C23F49B587E418289430A3122100F0CB72D7B1",
-    "ldt_key": "BA0D750543DCDD3A505BC7FF3DF2CCDF5F911588685CED3EABA6500E970CE49DA97D93512403B037CE708FBF5BB53C725DD19CCFEFB809C375BEA90C59A4F9C2",
-    "hmac_key": "F815117346818F376698B4A942459301FCBADA75C34833B2C20EC53EAB6B78F4",
-    "adv_salt": "8C0F",
-    "plaintext": "19D34DF12AF59C8ED87EEBED97FFFE2811C6CD64EE3139F427029E",
-    "ciphertext": "93E13EE67578A7D8CB748E55889F8838F5854F5B9300D93091D762",
-    "metadata_key_hmac": "DD2BDA4660BD3C149F56D4F0D8F39636531398BFB916E291A925594A1EE87CC4"
-  },
-  {
-    "key_seed": "DAA6AE9055EC48FCFCF0F5EEA10F9D54FC37203F21E0427F6C6F3EDBE3CF67CC",
-    "ldt_key": "BA1FF2973185299DC1E13C7FCD46A2CB8CF3E940F1F7B04DED6311DEFBCA0105F52BF9261A711EE436391471A7CF65A8D7182B4B1BCA9DD1321B37E93E8CC185",
-    "hmac_key": "007AE606E82C1E917203D48CD3B0CF7465C1E57CB55E2AEB41F7558FAF75F9BA",
-    "adv_salt": "C18A",
-    "plaintext": "34692A405DA47315781587F7FD8687F7FCA8131369096DAD96",
-    "ciphertext": "F17387CD4A36AD7B3FDD7291ADCEBEED9F408C1F9A1EFBC453",
-    "metadata_key_hmac": "BCA6BE8297D3BCFA926F80D1026F1C8DA7CCAA804CA805E5BE523B375B930023"
-  },
-  {
-    "key_seed": "F25660848C48B1809AA801DC67DED952DA73A200747DA59545DAD3F17273324D",
-    "ldt_key": "2F7ABFE27A5D7A0BA0063F061A69E070D2581FFC8D5257B61385645807E5F7F2EB966DC84F19BC8BB31C9EFA40EEE5D684F9FB928369C76AF27371810BA4DC6E",
-    "hmac_key": "64F9623263CF96FC66400186A2E6F7B4ADFE15F5E184E877F802FBABA4A6F3A8",
-    "adv_salt": "D14C",
-    "plaintext": "5773E1404053FD475905049812A1CC457BC51F1567C1456A",
-    "ciphertext": "985840834C0AEF4921523F842CCB203E0DB581007D513215",
-    "metadata_key_hmac": "D96254F0FCF232B538B7810568D884DC106988419FA5ED81C1EC852F3C317DBA"
-  },
-  {
-    "key_seed": "6090BF93D4371B05AF47E60940DF4DC98A56CC961B46363ECE74394BE523BF49",
-    "ldt_key": "5B61E2A831B733DE77A50F622F525ABC0CB299F1B96AC091F4D4122AF8048E54A9EDD97F6E53F05B382233C7D5C9BA4D1719474A1507C8F619E089EEA0C6C3BC",
-    "hmac_key": "7F19CEF943471DB2AAE52A95B3A90BE0983A37D02A851090B1078E983C8A86D3",
-    "adv_salt": "6394",
-    "plaintext": "FA349723D47F0E779BF19B8C517B7DCC9FE668B94D",
-    "ciphertext": "69C0C85FBCAAC15668AA6EB7472E613B1AA0C81E16",
-    "metadata_key_hmac": "8EE8E3927B0BAEBAD706C032EEEAD2E03E70211AA691F0FDF493408B17FD90F7"
-  },
-  {
-    "key_seed": "EC6E0C4933F7D73FB18A934619902BFC7C3DB0CD23754D57C3128E518F5A8173",
-    "ldt_key": "EB02D82B568542D1F74692F528BE19910D2BC2D491144E5AFB8DBB257729CF7F21A7CBC8BB350C1FEAFFB10F4A8D3BFF379191D0647A12023AB44968D53F2F25",
-    "hmac_key": "33C7B177902F2A5592A3B0166020215DA76B21CCF78959D7B21E7B0A304E0567",
-    "adv_salt": "7A79",
-    "plaintext": "39E5C5F7E08B25832D023E2FDE2B412E755814",
-    "ciphertext": "8AA7FD7E155EBADA20E1E65092E07310C16EBD",
-    "metadata_key_hmac": "A81E8596D25B7470C6A1B15B34D2C9D2593E25A8873322B296BF4AB480E1982F"
-  },
-  {
-    "key_seed": "2A685A402E6B4C3BA13818AC2495E6D7798163BC1AECF576DF183270DB2AC66C",
-    "ldt_key": "B9C393E46F60C9D1CA3FD883BE2828919E35291FCB7CF60F8E12FB544EE145330771D12DC6E1A02CF240D59B96B37093D993B2F1302222B04687D9472471472B",
-    "hmac_key": "62DDA07FCD2BDDC9076F474054E0458F6879516FC6E77155A12605CDD9ADFA5E",
-    "adv_salt": "03C6",
-    "plaintext": "558FCDBB132EA29FD9242E12681D3A0531DBD6353EA6011D29928D2E",
-    "ciphertext": "1E7FCD5BE6AA3ED1FE7C87AF62D81B0CAE76F8E62BF3DE639C1C6869",
-    "metadata_key_hmac": "F380B338EB5639A65FB7C2B1C8ED5E433DDB6271B5FE4B470F77784F4203B401"
-  },
-  {
-    "key_seed": "4D8A9B4D23A92C4C813D63980255838C29D17A06CD5CAB47737C926C0E5A5F2D",
-    "ldt_key": "4C1BD292C6F1D788264B74C6A7D50B56D6337FE643197C7B771FCA218A3FBE612B767B9A132726A45072AD08FE5994BEA3EDDFF503B56985CC72016E263B7EF8",
-    "hmac_key": "D60975FF61115BBB89BDBE3BF84B3A12019F754356FF66F2F1714A63201805FB",
-    "adv_salt": "B3F8",
-    "plaintext": "7197A958EA9D9D874353F3A5146BAA614A2C61A5815B5920419B5580",
-    "ciphertext": "D516F8023B411DD3A2E21553B7F0DE0855D8E81BFB0283890C902BFE",
-    "metadata_key_hmac": "EF3BA4E20A8112744B960DF77FF046FE9EA20C55115C088973624E1E3D8A6B5B"
-  },
-  {
-    "key_seed": "C9C566D3088648DCE86E7DA80EB9564616C6B173A01A00FF7C0CF37A01B21831",
-    "ldt_key": "CE038C13C852A8B6CDFCB62566A221B77031106FE9FE01A80A7EE530274839461BC90AE38440E97A02DBB6EAA332F069DDD3989ACBC3897C05B9C2D7F04FDEE7",
-    "hmac_key": "0DAA5072EF22712A2B4170BD88D19A8E4A80A96BD57D509FBC204E79274BCDEB",
-    "adv_salt": "153C",
-    "plaintext": "B54CCE60149E29D8BDD9C6641E1EC88863CDA651",
-    "ciphertext": "2CB51396470FDF4CACB8493B83B1E5EA0ADFC573",
-    "metadata_key_hmac": "806A5DB9A3ACF83345C3EA3D45DB135BA9007A923B4C7E394AECBEC2F7BF2190"
-  },
-  {
-    "key_seed": "4EC98D85F72FE8D7F6D04D4A202BA3300091FFC4B461EB93C286B0812AF56098",
-    "ldt_key": "D10CA2BEF1B09144569A8AD318882FEC986B13902CC6C8E6FF3C3EA9158EB81F1E5567E6C3B67054A80866A5E2A64EF6B20FA825C37C882A57C785B5F8FED8EE",
-    "hmac_key": "C50B0B4F9B844EAB7F75EA81790DA6B9202DBF09D588D3AD0E09F3C9C3153217",
-    "adv_salt": "8AAF",
-    "plaintext": "3E67656A66F8FA1CBF63B1CD162F39E4E99B0B837A",
-    "ciphertext": "340A53833B28EE6D68A63B742E153CC9C15E5EB061",
-    "metadata_key_hmac": "18094118719C7A553CCD7D75FA733B2EF100A0FF3E8CCAD62CDFFA7D839FB8F6"
-  },
-  {
-    "key_seed": "9A064BAA308923176299AC838F59C742FAFCF03396754384486A643B619CEF78",
-    "ldt_key": "799D31477FE6407751C233E8EA23E574B2C58B762195EB9BDDBCD2AEDF58F11C04768CB76CD9822E9DAFB0B74609AE4E91F67814085E19D21AF885AF079F98A6",
-    "hmac_key": "87FB2C5EC3426F8F8118CD237AF67A2D915F8E51B31AF731B2C53704E1850E8B",
-    "adv_salt": "5482",
-    "plaintext": "97A88CDAAA31ADF5F25DF156A65263EE2E6AEE7E19A0B07D509C7D",
-    "ciphertext": "D15B17B9D142E3B8D2478DEA14853ABD6D60CBF50E5C83A9558F90",
-    "metadata_key_hmac": "4A17C1A21242275D9FFCF6F2197B0F5A92A4F42D7DEC63EC91C48B33219F68B4"
-  },
-  {
-    "key_seed": "6E61EB596A2E5C30289BD0D62DFB6BC79EE546DD4DBFDF087426F31DA395D1AC",
-    "ldt_key": "8A55C3EF7748594CC2C7A624DF2843D603BD7D03383F72246BEF686FB052585E32E192F9FCFA847A2443C23395A6BC71930B1A5278CF5459DE5C61CC8A806274",
-    "hmac_key": "91741B464B1A30F832F2B1FFC94A0F4944A34A73160BB4A965466566C7FBA919",
-    "adv_salt": "66B5",
-    "plaintext": "9254F8FD7DD259B5F8D4ADB4168F6D9E9CC260",
-    "ciphertext": "7A19067485676DF76D868F5548E81DF3F7B218",
-    "metadata_key_hmac": "4889130DBBF07E75A1B864DE9D1FFF7C9D4645269633C4E484095D771C453A38"
-  },
-  {
-    "key_seed": "413C29D326C97973AA72C9445AF1490F38A1D91741E7A5915F6D2546FA73780E",
-    "ldt_key": "74B9A9EBBDEE549D75C619F19CF5797E58B71F29928E79B7700F7DDD02B2187166B23655B2432CFD057C3B4627F446CED8604D0306C61340C977139F6909C12D",
-    "hmac_key": "17592D7D59DB2D61CC93FA408AEBD68E9B6AFEA5E9DB8041B4F661059588CC05",
-    "adv_salt": "46D4",
-    "plaintext": "EC3CCD49517F7C4D28B7060925DE6EC61246CD179E515057BDD2",
-    "ciphertext": "D0C803AD7A7CFAE60D0AB2E902B4B0B264BAFDE23FF285822C34",
-    "metadata_key_hmac": "A6A8A38E5899DCBFE53F8C2F096D14875C28B9F17469432C2467918B5124E289"
-  },
-  {
-    "key_seed": "34D418B85A3F83C0C9161C9ECDE9F085E660C06B875FB09E7BD5DCFEABB5E716",
-    "ldt_key": "2EA9F9885172C679AD31E64BD2101593A448C95BAE3ABDCA3495D31E5FC75CE5F209B6F1F73F108A77004A51700B79CA2748DD29A805F057022057DED77E7607",
-    "hmac_key": "8DC19E5A9A7CB1008BDCFBE14906D56B66899D26E54925B372310BC02C1199ED",
-    "adv_salt": "6BA3",
-    "plaintext": "D08B3CDBBCC618CD992980B3D22B186DF94C0A646C762FFE544436B8",
-    "ciphertext": "632A1D66239EA12E38F2DDCB9BB0ABDB10189ED3F173B5F494E508F6",
-    "metadata_key_hmac": "C505EC0EA2BD905D96391B48B78AB0AE823AD75F952A29724984CF3C38EA2BBE"
-  },
-  {
-    "key_seed": "2F46796E27F2E5FCDF269E2B1BA585644C7714D4D3BAF6B5ECC61882569A4C21",
-    "ldt_key": "8425986BD785552A56CD689EA8AD2A05E3922710265F5A68BF83A2AB5655814A20E4850CCC76DBBDB38F1A61261142CDE744F4F9943A3D98E76DB2182A4A2628",
-    "hmac_key": "7D33A33F4386C4586C035A5E49BD1F663973A7297A7B8E6FC8A47C6D2C1819EC",
-    "adv_salt": "E2A4",
-    "plaintext": "F800EB3409FDDFE1E82DF9236A7925C5A5B4CC3CBFFE7A09AFF1",
-    "ciphertext": "EFE31E9A34ABF4880F19AB67C464448EF1FFCEA3A871A616C208",
-    "metadata_key_hmac": "C84CA1DF801588E0F05FED981BC716D718F712EB7B851157C5E9A671E8C1EDA0"
-  },
-  {
-    "key_seed": "9E289AC48C9757AD7085A31E4F274AF7343434BCAC94298F74D936F45FB13A5F",
-    "ldt_key": "B53BF793638F38A19222BEDE90EED4D5974D321DB66100E4E97F23C6E576C03F8D22AAB72CAE5BFD7174B757306C90481C29D843AE3D463271E575A8A28EECC3",
-    "hmac_key": "316CF1F279553D1F01E4021855F2456ABD87D17C4B0799AC59872B15F5DA2236",
-    "adv_salt": "28D1",
-    "plaintext": "6012835C1BB1059E313C7129CB01E73E4CE9BFBEC35AD1902591CA8E249D",
-    "ciphertext": "8D5A0749FBF107D3EBD02F7C6D6A3E1660A310CBC01982AAFC53B9A33518",
-    "metadata_key_hmac": "5B3C970E2CC40C1C010AFD7864B17662D968918B4560BE222A6625AF69663360"
-  },
-  {
-    "key_seed": "41A099C5D66CB945FD237961326E7E7D3025A2AD4AFF31BC560677315DE6984F",
-    "ldt_key": "66C76AAC504BA1EB783442279B852275DB61BF7411A16B54DE7BBFB36E046836C24D4F21C18AECC1B7C07CF1B8637F2C18D0BA897290F24CEA0ADAD7EBF3512A",
-    "hmac_key": "0EA893132D505F883067D2E126DAAE91CAAD6815FC157458D5060D9FD486A7B0",
-    "adv_salt": "4DE3",
-    "plaintext": "B1CF8B0EF26BC648B580B8319D0B7A1747BB7EB0AA5E3408FECF5052A6AB38",
-    "ciphertext": "0EE27C2179E912D1E3B755D7665A6CBF6CE5DCC2782342329B216B9710756F",
-    "metadata_key_hmac": "67CFC854F10DE020F9367C3E4A1707445D77D6BE0CE9608F0FC7512DA64FA646"
-  },
-  {
-    "key_seed": "22D103430AD13F0AAD09237D0956AC3CE7D346692ACBCC88C6B6169974A6C07F",
-    "ldt_key": "663EE65D559972B0E5FD2C5D06A705B6AD5707E650FA56DD6E5CED45AAC0FF6953B27FA7E54D721195B17A24235B08D276227A773249962C7410C467CE9B9308",
-    "hmac_key": "68B75BCD7A56B9FA87E376894B2B12206A4DF9036AAF59FEFF70A3E5BAA0D12D",
-    "adv_salt": "1A9A",
-    "plaintext": "569FFA5167D91F964AC3CC28E7E21B9C05B75CCC105A",
-    "ciphertext": "06F069614B544A4C62A1C89E84DA8035FF09C11B31D2",
-    "metadata_key_hmac": "53CBC54822859D915FD7611D6666C263A56662A6E4A0DA8C5D47955C8E6EC82A"
-  },
-  {
-    "key_seed": "9680FC88D8C9AA02F5F51BF197DFB26C081662A881F2E25F36C93C39ADF91EBF",
-    "ldt_key": "7F2A76B8E85178F52A34ED59FF49F22EDF11B692577F264280E48B47B5D0CA31241CC4745E0EB168D289C8C038331AE7763FEFD20BEDD5AE0D91182151630B71",
-    "hmac_key": "138182688470F6442DBC195B17130D7938518F6BC83FA37ADDB8D27A02811988",
-    "adv_salt": "B0DC",
-    "plaintext": "B2193E84A9AAE8D050EDE19F3FEF625C635617B44DDEDE561D086F73",
-    "ciphertext": "86F68E13C8DC80EA41E714D32F1F740605838006D7B0C03E71C2F825",
-    "metadata_key_hmac": "5A315A06B4675F86C422B31DC8929678BB2D30F88AC0E4718AC1DB0057BF2504"
-  },
-  {
-    "key_seed": "788B543F104FF097140DDAD8E3836D12A8BEE16E5A016A8DD663F46D5C8BD7D5",
-    "ldt_key": "3BB2663D34518529E4DBA6585CFE7389933B578BD555A1513AE34A22613198FFD83167F31CBB07B61E01D3B3187B7967AFAFA4BE079244EA840FD1907921C831",
-    "hmac_key": "7A013F5FCEDF4211D8AC39347A6EA17EA1FAE86C391A3D532C9CBAD3569868FC",
-    "adv_salt": "0A54",
-    "plaintext": "3868F0161F239403689EA195843E068F25BED2C4",
-    "ciphertext": "07C2B953927FDC86CEEC14E9785E8309ED048A4E",
-    "metadata_key_hmac": "21884989E2EEB3E60CFF4470A6DF3FB36E55B1377C28EBB03B84E1BAB7337F0D"
-  },
-  {
-    "key_seed": "3859912F98565FBD72AAFD02C7241AC31C65B0D8B3C3BE15A7C5ABF44DB123D2",
-    "ldt_key": "C943FE69AEA239EDDA3DBCA62B2D8C402D9CCB6A643F25CDF11C7BFD36CC8C612C950F7A81891F13495ACAE4CD08CD2930E115E0F9D0BD637FE8FC6785470D15",
-    "hmac_key": "5DC5C8397BF983D109283C156A954FA5DC1A50A10DC43C04EC4D0D57A905DE88",
-    "adv_salt": "9D1C",
-    "plaintext": "435233CA5EBC5FF190707E5FDA434A17EE19C50B2921F4",
-    "ciphertext": "DA361F78A91F12A55078769C4B1D11B6310C6E140C64CB",
-    "metadata_key_hmac": "E0FB801AA32CF13BF47E00050A088DE6C53A5E9E40B484377A9E62E4EBA6A265"
-  },
-  {
-    "key_seed": "16549C7D135CF031095B1C5E847BE71C69AC674597CD636A6E5903AD35B05128",
-    "ldt_key": "3242D94DFE7A36E6FECAD6F5CC58454565D8574DC843658713C6494EABC5C7CD7EFDD5420EADABC5A561CEBEEB379956CB1351EF83711B2B2A40D7FED6C9D6C2",
-    "hmac_key": "BAD2D225108964EBC04DEF61B9EBE88511C3BFB649D7F6B32EBAED20900E4DE3",
-    "adv_salt": "9C64",
-    "plaintext": "8B1B3CF0B247C961A552A4D9E154EBB59317A7A7B89E60C16842744453D1",
-    "ciphertext": "FA710865DAFB0B545E82A91F73A437AB9B8F7209292B9068F3FDC8B6D4C7",
-    "metadata_key_hmac": "5BE78832E7A926F05AD30277FF85BA56FA36DAAC13A97B05F8FF8A25B375188B"
-  },
-  {
-    "key_seed": "FC774651E46E37F2955FA37BC0ADDB4F71797DF194CEFD1082509BCD139C3EDE",
-    "ldt_key": "7E18FA91251EDCAE40CC43B2B3213E15B12A38A40CBB1F2A11D665F419A001D6A98055E49A52DD9A4E9AC913AC7C029C624B76FE869C242AA91269FA5A0E0E7B",
-    "hmac_key": "8E028AADA56B355B3D18FB1FF73A0FB97959865B3164025E54312F4B09D9501C",
-    "adv_salt": "B6C0",
-    "plaintext": "48D81A930FBA128969438F5B9181D829B3932E755DB87658C9EBC35A1C59",
-    "ciphertext": "7AC37A8A4379B10518D367C4615A099C600AF54E1370FB06C44FC88D26EA",
-    "metadata_key_hmac": "B2E406516B8892B19E61FF0C99A32C65F20FBD9D92F9833C8E7414A40E1997D1"
-  },
-  {
-    "key_seed": "9A68CE290FDE2590F423D2B173D6F700335D4935BA88EFBD84CCD9B98D62F148",
-    "ldt_key": "1B13E1A2580433EB1EF83845B35C499643F2928B55A381CE9ACD29A258AD325CF21858C9A354013DFBDF322F74B728538A4EAFD696531D7F80826FFACCD098A8",
-    "hmac_key": "31C2184AB947A9D39C101965EE706E2790FFA60DE7DC789597C52E3A3336ACDA",
-    "adv_salt": "F2D5",
-    "plaintext": "96A54987A54B00DC6E045D18FBD129D1DBDBB748BF44FF26BD867EC76203",
-    "ciphertext": "471A0CBDB571FDA0A7495FE7967545F2C661A4CC418DDCCFCD241CAD19D9",
-    "metadata_key_hmac": "E050224F87E969CD4F1B18AD5B19C2E8FCA68EF8965018F15BB1A7CC57EC9DCF"
-  },
-  {
-    "key_seed": "71284F8BF099909B5643B6295CDC676620A79F1FF4C37CCD044518E16E05EFAF",
-    "ldt_key": "1E6857D4B7B58BED2C299CFC0A88483BAD0C027F91C94975950A2BAFCE86FD399D614ADAEAD9D3DB80B6962520C05D5D68F99E6E9381DD65BB873DDC9E173061",
-    "hmac_key": "502BEB8C34A8A56C52EEB7B1F445BB935514D8D80E836A42E8AD762742CD2D0A",
-    "adv_salt": "C790",
-    "plaintext": "BC6AC409397647D14E11EAE3653123A1",
-    "ciphertext": "D3BAEB95A9B1DD399BC7572E1386AB62",
-    "metadata_key_hmac": "B33A36BE15F25EBA65835FEF8A548F283B2A57A681234CAF2AE57202D28976AB"
-  },
-  {
-    "key_seed": "DB32ADA65505A45781EF4E0D307B06911CE24C29B885FAD11EB653CEAA1801DA",
-    "ldt_key": "CD71C2CF5C35511A1CC36EB3B3A008D3D16F2AE176C273DDF9007064E71B7F16D64437BE221402994F1D11A36C9743D5BA8B4D2688052127A9A9C7AE34E2448F",
-    "hmac_key": "505F53627BB5E3E1443FB7A7504FDAB686EE6E8201F4DA5D59174BA77D10A3D4",
-    "adv_salt": "62FB",
-    "plaintext": "1D4D91AA7001F510B7708B11FAA34F6C2D3FAFC9A89666DF1CDB57366C19D4",
-    "ciphertext": "31A32EB26FC116911AEDBB2DECBA5FEDB1B8E8A3F1D6F226C2E0FF4480D2AF",
-    "metadata_key_hmac": "A17B8A4FE29187A40645F046971C4255F7287CF385504C556F044002CC46712E"
-  },
-  {
-    "key_seed": "4ABCC9B9953A6A9924115ED9E6F99D348B3C511A3623831B0471C33D7DDF2A4A",
-    "ldt_key": "B7F79966BFD45B6FF5B21826DD00CD71EF406E655F9212E2AA0F1CC651C623541FFD68C2345BD368E72869DED95F1CCD8F165581BB17752C62C3569170DF6C9D",
-    "hmac_key": "59888DEE165F45DD32A4096222BFE29A04BE79AF4702A99AB1AC12CB7B3E4789",
-    "adv_salt": "DE75",
-    "plaintext": "3DFBC7C04B895DF08DB17B04B187B53F34CE3F",
-    "ciphertext": "8732F4FCDCF9563AEB5258A3F6D9B12415C3A2",
-    "metadata_key_hmac": "439C89FDDE11E70F2CC3B886CB1094276C1EE5117BDDDEDDAA3EAFB3BAF1C1D8"
-  },
-  {
-    "key_seed": "2B1BEEBB2E17EB4B41F1D02DD6994A9E79115927C2FEC52843997C0E9B7A0857",
-    "ldt_key": "79901D166785D10371F5EB9631ADE4AED6F867AFB62BCDDD078A8C77ABB042A178FE9BDA4ED565D6E84CE1B55BA75421236FC8314ED62A3381DFD063EB85F438",
-    "hmac_key": "C7EC4159B8F69E7B89EC24F6757CA5B24D9BD7170A204E87C40ED79310545312",
-    "adv_salt": "2FA8",
-    "plaintext": "9D26AE2C24BAB406087FDF03F60436A9AFD891CA6081078AA6",
-    "ciphertext": "B6134005E78E33683CB7D972708848E3AA5D31637C90787593",
-    "metadata_key_hmac": "965CDB488C354A11737FE7F1E387BD788E943B26CA330634693F2827F14E150E"
-  },
-  {
-    "key_seed": "4406B55C675E896426783D5CA64576B18831889D891151F3C8AD4A309E73E2C4",
-    "ldt_key": "CA0F77A19D2AD1801BC76EA1FC100FEDBF17F82F478CCFEBB7FF5AC980567B4F9511E3E57C7FB8F097EDF5427000E5C895751D60692F7DC929174DD260948770",
-    "hmac_key": "EBCDA862E6B338B9802FD70A00BD7A171FCEF676BE3E613F778CC8375A64A702",
-    "adv_salt": "BF33",
-    "plaintext": "7E79CF2DA280607780D4518EE475E9AE2015E7050A1872964F06",
-    "ciphertext": "A229D7376013646117AA78B4B7436DDE99B80E64C1738F30E022",
-    "metadata_key_hmac": "EFCF0CDC83FCF0E287F11795E0D11CEF911A1D729199AAF27D9037262070FBC2"
-  },
-  {
-    "key_seed": "8190B60B4F263AD0661EECF3CF071A145AD22C1F04275CDEFD7073494F95F315",
-    "ldt_key": "31B1ECFF18C4976AB3E81CF8F04A2FB0DFAC8FCCBC9DED641520DA9CC82EA4A33B33BF8E5F45AEE2BE1B02AC46B8119E53D4AEA80AB9B5F37263905A64BE6141",
-    "hmac_key": "39B16AB474D1CEB8F37310CB3F72A6FF1E4B41E7FF89ED796B5B2C1AE38C948A",
-    "adv_salt": "C79A",
-    "plaintext": "3A8AF71CC6C5BB3C3D4B954C28F69DBBBE47",
-    "ciphertext": "2AD075D01B2F8C7653DC6446AF981BE95E97",
-    "metadata_key_hmac": "EA1BDF5B226105130FE5D85A030F7E045C11B238FC0CE0C3A3C45EF3274BB239"
-  },
-  {
-    "key_seed": "F58783AE9ED0E7AFD00F12BDAD1ECC6DE8BADDA679101B84C36F7B2417D3CCA0",
-    "ldt_key": "5595C8AB388892CE3EFA50C5F0BA40F1C646B775D7D1983AAD72687749A9933ADC9580566FF2A90415F49F76744CF1703C828AB6933867045EB54F6B76F5F15E",
-    "hmac_key": "71208853D3DCDB60D3985FA4AB2954EC18651BC144AFE10820274A542AAD2D44",
-    "adv_salt": "E686",
-    "plaintext": "9E9C0C848A6610881B438584C9CB12E61870",
-    "ciphertext": "7CAF839B2F82A56DB9880A371645734FE734",
-    "metadata_key_hmac": "7182053162E3D3E19349B309FFF978EEAFD47AFFE57D9D37AA6E6561EBD55BE5"
-  },
-  {
-    "key_seed": "B46AE555E1D0F018788D0B14C8A7C3B722980BDDCC417DC94B852493469A4A86",
-    "ldt_key": "1FD8F39D1665AA7F3C4C01F0920C99B8F754A49EC3698175D55E857C660BF188F738071E29C11C7DBE5EFC129BC5462FCA468A0396B2992C345FE9F660B0DED7",
-    "hmac_key": "DED8B1817D753047D90F39EF7977BE3F2D0840B29A3BB54C98978321F68F2C44",
-    "adv_salt": "AD78",
-    "plaintext": "0CB40F4D7AB70FD940CBD41D48E5510D71671772F4C7050D1A",
-    "ciphertext": "BD4D7F6E0249481917C2B209976E83580D4B4AD8FC4E60F515",
-    "metadata_key_hmac": "AD3121FDEEE62EBBCFBEE2EEC8ADED78E867F87BA5238AA6DBBB85852BECB0D7"
-  },
-  {
-    "key_seed": "DB9DD2F4ED734844D261A336076358202EB6D828B99F396B19EBC133781A60A4",
-    "ldt_key": "F091157C622B58661C92D079FD5FA8C7840ECA44CEADF634E2BBECC1CB2A42D3857BA8568AEE69CD92852384CEA086FFB45DC79C38813FE203832EC0216D03CA",
-    "hmac_key": "B856B2988B484356EE19E32460774A2A4220D4009FF272732C13B70A839E19B1",
-    "adv_salt": "8A36",
-    "plaintext": "C7C38D01C61FE412DCC84D910EC3EB7D4E44F4DA11",
-    "ciphertext": "D2CE35FBD1F5D2681B0EF65CB1006561480D39FD95",
-    "metadata_key_hmac": "B38B088F5600DD5EE317BB271117BC516E7BA5CF39AF1B6AA53600F77495AA84"
-  },
-  {
-    "key_seed": "8B51FCE2FF95A5F1E20138E5753B1F3C7E6F43E73C5CEB4495CE434CAE4EA356",
-    "ldt_key": "4323C8D74660ECCD2718FCF59FBF4A90643E9865C1637877BD3E40C470EBFC68B76834820A5B5D39BD1476D45482751EC918ACE5303627EF6DED59EA36890C0D",
-    "hmac_key": "25B8CFCA31C675CC7D8B74079C9CDC20644CCDD2E47D6A6273DD312EE4362A0D",
-    "adv_salt": "E808",
-    "plaintext": "F841AAB108AEDD72559A3E3015522D848C5751",
-    "ciphertext": "9A9653222FD23F8C5B1B9D600216899D1F8AD2",
-    "metadata_key_hmac": "31DEA95385C804AB3D94FC4FD6199A0947155D771DFF0FD1FCA26A9786F04ACC"
-  },
-  {
-    "key_seed": "B4BEBB32109FCDCF93551DFFF147D74C2277529C90C37A434A6CD01B2D8E7B51",
-    "ldt_key": "0CD0A7B2FF0588BE56F1292F2569AB952AB77EDA27786B25858A88A93E13DD45096ABE153F6B96E4D6382EB7A4D85F47BE6408041A86220CE687AA87F63BBE3D",
-    "hmac_key": "5C1E37F63AE477877C96D0AB37269EBCAB560D2542024A2AA14FFD4A3F23E8D1",
-    "adv_salt": "8CC6",
-    "plaintext": "118914D47B6F4A6285D194ABBC07E85AD3F8459FA8EC23",
-    "ciphertext": "B5BC04924C066803A5238772BF969141E84DFCF09A3402",
-    "metadata_key_hmac": "0A8030F9F7AE9F0016312C3F766A35A28A7DC4827F9FF54E927F66E1092601EA"
-  },
-  {
-    "key_seed": "8D36A709B972F31E6C65F898A6C1DDBA994764637C16DD7F3F0C01E25CDEF4BE",
-    "ldt_key": "EB64426C4879E69CE572204C3CDE9645BC12F97DFCC08A2F833FD4E897C8A949E138DFF41D9DF6A6F4909C96C6DCF18C6E142A983302B4183160130049EA67FA",
-    "hmac_key": "8B44C8C6CBC5308DA34EF69481C0E55CD8BCDA0DF97AB72D9FE32B439F32A3CF",
-    "adv_salt": "5650",
-    "plaintext": "55BB5DA88116677E60C209975D232FD18228",
-    "ciphertext": "505D8431CD5FA2A8610E4637ED1642209E18",
-    "metadata_key_hmac": "E6ABCCFD7722490F5737199427A5AC2D9590857E4E51A88F4D791B1E618D0989"
-  },
-  {
-    "key_seed": "B4887F6B359B821DB4D60546C6294EC221BDF666B4196BE5BA61721FE0B28BB8",
-    "ldt_key": "35CE72EF7251ABEA55353EB5BC0ECDA64A1168704A02640DCB5ED01A095152A0DC6F2224A8881E3553B80531B2CD69C18A4C341D968FE13FCA035C337C3CCCF1",
-    "hmac_key": "04FB7DD651E419A2910EB55CDDB1BEC0C1ACC55E399E32A187302354DD27FF30",
-    "adv_salt": "F882",
-    "plaintext": "0DCC4777A5D21C65AB07BD1CB0F012B2",
-    "ciphertext": "C6ECDB9157DD9B51D7E74F04DBBD7DBC",
-    "metadata_key_hmac": "AECAC660CA2ADD550FA35AD8167A559FDDE6EA2311D0F4EDAFD2D5795941B925"
-  },
-  {
-    "key_seed": "162CAD94DAD9F52B1249DEB412E9F5AFC7CBF5B49FAD17B01DC6A826C3C2CD43",
-    "ldt_key": "F4E47C120FBBE55A36E57FC19EEFC68BD6FDB905A48BD705FF183DCB8A67FF7A8B5C8DD239C3E9E0EDFDEAB0B5E6721329315E9C7592B9294E593C962130EDFB",
-    "hmac_key": "333DC195EC74020A025C45A88511E114B9483EBA1F1E654E10A2B44CBDD7CF26",
-    "adv_salt": "5392",
-    "plaintext": "978C189EFE7051EE0E5C66D3A14B95DA9C",
-    "ciphertext": "3E0821E933EEC846E07092486FCFB5B4AD",
-    "metadata_key_hmac": "DB7D14EF5BA498D4D1A98CFBA45BE5A666D707537417EF58C86323EE8871C584"
-  },
-  {
-    "key_seed": "65A386C3B6117EF7447B253D30A4C6C1BDEF6AB3C3900CEC59F1CB57BCD7224A",
-    "ldt_key": "AEC938648C3F12B794F472AFC642952AD3B54D3F7D2B87494D2789613D15F262C8288ED55607E1734FE46BC712CF233AC0AA42F4AE137534B3AD7E42E7313E7E",
-    "hmac_key": "3C68EB194AF430833AB155780E977E77666A159AF096E1807DF7E7AB90C48060",
-    "adv_salt": "0109",
-    "plaintext": "FAA440F3974DF45683059B00DA301B02C4",
-    "ciphertext": "62FEC26B06EA10192D9326C4B7F8DF9EDD",
-    "metadata_key_hmac": "08F4014B16123D25EBC9F7A37916130B60A1840BEA2B3B0BBD2D3BD0C55A84FD"
-  },
-  {
-    "key_seed": "F4F8F73D94974410827D1E45567FEED1B0DFE93CF5E6922FA271DC187DC55C6F",
-    "ldt_key": "B2D62C5A37C2EF10A13311EF791191560702D2D4048BBCB51EA05CF1C96436953D110C0FBE0BCBBDC2B10FA0B9A151A5496D601F0C8553FA09ACF96A85F036F4",
-    "hmac_key": "CB6D166C67FD8A2F15B6BF41A8687F297FFEE73742E2DC0188FE786F9E91FFCD",
-    "adv_salt": "B7D5",
-    "plaintext": "9841441046BD14AA74C6270768924FE80F1A9E87CF4FB58303C244D677857F",
-    "ciphertext": "97DAE3E3E2E62618463FB56A8992B6DCDF75C243683B889888241D9D41A9A6",
-    "metadata_key_hmac": "4C6B37E6E227CC21C346AB4949C97F46449D1B21DB5A10333DA96825979B285E"
-  },
-  {
-    "key_seed": "8DBD9E5EB9C9EE1C09BDFC5F27A81AB3D51EA20257FD550B4928745D4B9EA8A0",
-    "ldt_key": "00812DA977303D35CC113B82EF7145F5ED0B70DFDCDB22E34A930A960DA8AF57B09CD331FF8B8A523482992A5B1C8292AB7BB51C9CC990E768C76477ACA5C5FB",
-    "hmac_key": "309D2CE7A6C72A0EA9932CBBE1C40E150C5F60B29EBB0DAB37561B119BE7F6C0",
-    "adv_salt": "FBDE",
-    "plaintext": "085A47079930F5713309A6D762120C5701",
-    "ciphertext": "3888221A4488F4B6E106F38AA7B3A198C6",
-    "metadata_key_hmac": "A3ABD92B5808B5BD09EC1D2F383C82A77E5E36D20E7E2519D65CC38798960BBD"
-  },
-  {
-    "key_seed": "36B5FEAF45253E5FD7A3F2F2B75409EE518D5C231CBC6BC5642C94EF4BBA28A4",
-    "ldt_key": "0005617531C346C2C728766D9B02767D5198F7A93262C134079F3F26189F32BF2A6083706BB77BDAF78A9785F7CBE319A5E82B4BDCFED200ECB70E87B900A9EA",
-    "hmac_key": "DC4A9845768A5152B8B8795CFA8DFEA3B4B703303642E316D180AFF90A0DAD93",
-    "adv_salt": "74BA",
-    "plaintext": "9C499B714A71C5BCF065F99FFB91E98D7E3FF1F46964C1317D16C74304",
-    "ciphertext": "9560CB581FBC1DC76B3C9BC40F4C166CA771D3D5EF1BD35B3868B1C0F2",
-    "metadata_key_hmac": "E1F0037788E36AAE391E5644E9FAE133ADC982971DBC709959A05A8F4F84225F"
-  },
-  {
-    "key_seed": "156BABAEC767C47FFACBA5F54BAED7E8FFDF0FDFDF197A0A3A9E706D82E3B6F5",
-    "ldt_key": "40113FE72948F108C8B21B188B0412B4CF7B27472EF5B5D9CA7E637ECAA5EF3435E9B2B231B5617760CC926417CAE64C84E63C94648BFB076B9601E35A57BD2B",
-    "hmac_key": "3D06CD322ABCF9994C4349F30201E8B30762ABAD01821FAB80BC8CFA89A98917",
-    "adv_salt": "0272",
-    "plaintext": "85DFBD49AAF71A3EF9B243E1ABE18B5A949C7900F197DACE4F07B433E3C0",
-    "ciphertext": "EB60EB75B7DF32C0E527D498AB9FE613BCE84338DAB068CBC00880156F4F",
-    "metadata_key_hmac": "746279DB37F08D7827E9A164802466DC9F19AD5D23C071F5B1D84FC36C65EE4D"
-  },
-  {
-    "key_seed": "A7AA0C3F40BF3F5F82D329484E85E847AEE8F6C9B6D8F48E886EFB95ECA3A3CF",
-    "ldt_key": "D81154AF670C91CA943B511D8881825912BAE9E931DA78CD284D6B1B66D9F18CA3BCB59417BBA6DB89AD244DAD51BD74EDEE4FFF1647E822379CA2DDD01C85D0",
-    "hmac_key": "647B175FC721704E7F3914355F716BD4CCF89FDC6B40F0652D6991C78D7732CD",
-    "adv_salt": "094D",
-    "plaintext": "F1A7EF9EBE87F17ED1A9C5F20536F0B1",
-    "ciphertext": "B796373F6BB4CF61B756EA3573BF3C79",
-    "metadata_key_hmac": "EB93BFD52DB4FF7318E0347E2F2DDBC349D43B30505598BE5A2FA554A001251A"
-  },
-  {
-    "key_seed": "090646E72BEF838D0B591084CF7F200A2AE04CA7EAD150A07067DDE54B44FC8A",
-    "ldt_key": "44FC3318CFC2E466525C3D188C7C7C50623A9B12BFCFDA454BEF200B22998F0896FC1A03707908D689D16CF295BC617D50093ECF706FFAFC10B959011E21EF6C",
-    "hmac_key": "A62908D0222822364C78A9D3176E33C038722B18E2401ACDC2EDE95DF85B0964",
-    "adv_salt": "457A",
-    "plaintext": "E467FD1BD1874BE21617B0813C3B8284C26D",
-    "ciphertext": "A0FC977C5F13050F6007C6FBC51935CB5F2B",
-    "metadata_key_hmac": "65CCCB5065AF7CD4CF8ED8550899224BEAB8D36C9070EBB2F838FEF09A52708B"
-  },
-  {
-    "key_seed": "496478EB6984480B951E25F65E9FC85845B1418D32F55B4078B5419072EC726B",
-    "ldt_key": "A55A53F3BC4C077F42460C09F35CDC2777DDCC57BB39B2015D2A14EEBD1F34ADC7F50F4FEE2EE3C32EC76C6FC1A7AE7BE0840E925DE517DCB10BC6ABEC6DD57C",
-    "hmac_key": "CAEA21DE3A96C3F4C225249FE01A3F03AC650FD5603F444F38F3FF6FFDA15181",
-    "adv_salt": "C7DF",
-    "plaintext": "F0E1D9134AC84E5920EA442BFB97F4E7BD6430876E7756A282990768785222",
-    "ciphertext": "E8490FC5A4D063F15F1A5A533A90B49B81F053815C48692ABCCC953FA9D4AF",
-    "metadata_key_hmac": "BB2E44E96B5CD4C949B5FA3617BDB2636A5028F6E8B319B0A314E442EB6AB274"
-  },
-  {
-    "key_seed": "81769F513E7F04A5F46654FAA812CC261445F811EC5C51B02108EAC1A0113C4D",
-    "ldt_key": "CEB48FE083A2C24B0355E25C01EDDEBCE9A8CFE49FE1B1E8398399451457ED7888DA4FF7288717E348EE66035C118575CA2E370EE93A4B105DE78606B94B0A3D",
-    "hmac_key": "517E210C373009FE4B346AA7542BD6796662EED0F43D24A4E2BF805FF2B23767",
-    "adv_salt": "316E",
-    "plaintext": "7B920A59980B213E011A05BBB148B3D840679B1B49EE260C9A",
-    "ciphertext": "E4D3A9A7A8025C8AF0E94AE7683637BE2F15F4E6994F75F336",
-    "metadata_key_hmac": "EA0ACA6C565FB416FC16F0D4D0152B6EFE6271978DAE0EE97D3254A5F3092F2D"
-  },
-  {
-    "key_seed": "2CEDA9DEF003DFB2170584F5B4CC555B00BBDF31BCFA60365700772D40CD4DF4",
-    "ldt_key": "A63B02E2C5923639AD6E668FA63075665041D82BE75EEF2B0D005EC029923BB3DAA1394B776A71525B1605A056F8DCEB50DBD7ADF4DEAD271D5DA38C1D490DFF",
-    "hmac_key": "00C3B8DC4F7A9DDC6883A0AC253D4F62246C8DED8D5C7C5A816701A55392AC79",
-    "adv_salt": "64E1",
-    "plaintext": "568C456FF7E72EEF51E1DD3C89B84B2C60D10774339F7EBCE578",
-    "ciphertext": "DDC02E7EC002DB5EFC89E7BACFE18A4176FA37E7A8FCD066151C",
-    "metadata_key_hmac": "C2D87C8C84FEEA8B46DB522C4C5DD82DC974532DE5B0C47200A1CE4452018821"
-  },
-  {
-    "key_seed": "E586C7755F84369EE2A78A4CE017880ED73950F49BEE1B91FF1E284BF56C2AF6",
-    "ldt_key": "6BBB5418A787E5E06E2949E4D7B143931CB793E98FDC2E9D733E2C72C40C5AFDFEEE10FF5AD9A8D9BFFEAA49027397BE675D63250698206F4A908244D6402418",
-    "hmac_key": "A5F151C706A0ECC923F706DE2A1A458760C251581609478B9F70533C39EF3D55",
-    "adv_salt": "2872",
-    "plaintext": "96C459E676B460380DF3D503CC7E30BE31E05CC9F0C401",
-    "ciphertext": "16241F92F0A0AB166A9B4B64DA10A686B9C78B989A894F",
-    "metadata_key_hmac": "B16BEB1B3CBB77AEB8F3748286FCEBD3F6F2C21EE8C201C5E57B9BE24E907318"
-  },
-  {
-    "key_seed": "A845B8E174D742420B23349E36ECAF6E454D16F406F9BCA265071B3BD07662A5",
-    "ldt_key": "D8CC56D5C1C4CD27BAF1ADAF310A61E9E9934054D067E7D5EE8B4F2D91AD0540181981BCA0B356F49B38337D8CDA5A500007FED0AC21434A041F0E86BF36FB08",
-    "hmac_key": "8A78726DE42E0D88076AD5EA63DE08674CF8751BDFD0B9A74D233FD5511972F8",
-    "adv_salt": "5656",
-    "plaintext": "90D2B234C73C4DF113A4089EA4695FB3E1C5613A2C8F0DC7D0",
-    "ciphertext": "5053DBAB225D49C12C4FFBCD36711953A3E00B8ADBDCF0AF8F",
-    "metadata_key_hmac": "962782668CD846AC9D73E989A84F8FA095247B260ACB0E981854ACEBDAC13CF9"
-  },
-  {
-    "key_seed": "959D5D206A50FC83E74679834926F1B4447FC27A28162C95CD000C9612C4E088",
-    "ldt_key": "3B04A47EA64743AC0ED199A5BA0CCA7611F93E5600BB51D799CBF54E25DFD95F893FBB7877660A5351A859FC0F75C6EA33B470640554A1E2DE4D4452747EAD5D",
-    "hmac_key": "74BD0A56417D845A30F13B84150705277DBB6971AE8E24B523B99E573711554C",
-    "adv_salt": "BF9F",
-    "plaintext": "AB0C6FADB67BD79EB6B94A0AC20567E926086E",
-    "ciphertext": "FF008FB69B2440F889185B21DE5C3392516A9C",
-    "metadata_key_hmac": "B268E72C523B1B0A969F4BB7474AE203EBC0E861643F1EC6B6090F670763321F"
-  },
-  {
-    "key_seed": "51E94792E74002ACE7C3B512B18C37992BA25A9F74716BB981E322AD068348B7",
-    "ldt_key": "015E2B7B4F81E5EC77B382488E1B49E358562657A670CD3E532983F360583B2914E7B4E848051CA857657DD426AE9BD1C102AFAA5D4D08E7E48346E57E75E865",
-    "hmac_key": "C576F890E80E363AF243745856BC30CA6BB39FBE1A72749256C93AB7F87AB43E",
-    "adv_salt": "8885",
-    "plaintext": "331922A4837948608AEBEBCCDF8A44FAF93509B225CD",
-    "ciphertext": "C5B89D2504EE3160361D0AD031A1BE698DCC5C37BD04",
-    "metadata_key_hmac": "BA9060E2714880FB372713AFDA0DDC82C437C8B7E8BD37CD0504AF0B7D6B4AAB"
-  },
-  {
-    "key_seed": "4F2400095D77803F8C2044BE940CC3AB2444B314DB20EAF0C1F837FDCD1B5441",
-    "ldt_key": "17AB4714F2D72CA4C5371768D7C5569F6111E76305863F3702FFB514ACA56DCEC3DF724C1C6544E050E89B23F5741BC9297CA834E99A1A44E221587FF77B2CFC",
-    "hmac_key": "1992486F199F19765F4CF13F1CE7EFAA085C116988B925F9AFFB89DAC36757DF",
-    "adv_salt": "5818",
-    "plaintext": "3EE4FC2AC25DD812DADCECF73A638085DB9FD791C20A40FD",
-    "ciphertext": "7FCCEF8CCDB9771DBB025A611269B1276B9C0CDDADF63133",
-    "metadata_key_hmac": "03B15393F8AD1DA3F2E7F58AC261017CBD3BDBB603545EC39FC44C887A003BD0"
-  },
-  {
-    "key_seed": "E7137CC7DDA32EB0D1A6DCF6A8B48A14807D7F3265FECA890BE303333576FCCE",
-    "ldt_key": "5F090F80CC5AA508CF9A64EA25E2E84EFE8421B39D8946AEBC0D209BCECDAA3053A16E1EED1FF14213FC6BBF0187E75B0A1944DFAB7E793D4ACE94D418F42EE2",
-    "hmac_key": "45A6202083B74F0820356B21A83A4D61BF17E22EDC79274272332E6903E1A5B2",
-    "adv_salt": "1316",
-    "plaintext": "3187F589F42134ED6ADCA93D4412E1FC41E2FFF6",
-    "ciphertext": "97EEE19E9828D5FAAA213D0666B26873322A6F37",
-    "metadata_key_hmac": "36AFC08EF8950DACBC4049FBE005C001A773BA688CAB55AD71E3F05D4A8E1BB3"
-  },
-  {
-    "key_seed": "BD762827B67F301BC4F1205800A102707E28899C983B462C959B59FDFBA61DE6",
-    "ldt_key": "0DCA2FD12F7F8240F9F401DF62FAB2C9653DA29817D747E6BE7896E107C08A946FBC3E606B73B5383B7ACE9817D7E3867E076AF7FB88F1FFF15C39E0AEA236B4",
-    "hmac_key": "5D4DC5B69019BF4D5DB619169979683AACC8C7BAC8D90704187B1367EA0D3B96",
-    "adv_salt": "0C42",
-    "plaintext": "4982BDCAA4A8410E4EC8114A87250768EED2DAC6666AED7B050A2875",
-    "ciphertext": "8238354F1F9C09F1F2A998FA4CF8183E601E8D1201DF4B7406C895F2",
-    "metadata_key_hmac": "2B4A242393CDB8EBC0096D1A821E902CCED72A4F091741B252CF307DBD27FBC3"
-  },
-  {
-    "key_seed": "8A0747A091D9873604ED00DA5D28B4A671BC9F77A13275E0F57125EFE7F9EB0D",
-    "ldt_key": "F50A9C0F74B5E0D8A67CC9F401493DF30327AD605E0F978B822DEBDA52DF4FD2081641BE86B20D9EC0FC9A6551E1DFA8862EC04A685A86530346438F7D99CA22",
-    "hmac_key": "D12F236FA71A0E5454464BE860B6EF7FF5E73BBCDF4D7F3FF40EEB88635962F8",
-    "adv_salt": "3221",
-    "plaintext": "97CF5C94D322D5D5FFC04E1D598E163F60EB26C8",
-    "ciphertext": "40E8649F75C1FFA554406D5528E5935CA625CCC1",
-    "metadata_key_hmac": "B34E17DBB676F3700493F04649306EBAB8F3FBB4ED174B3D2208D59FEDE5C7F9"
-  },
-  {
-    "key_seed": "5E75B1A297B2B785F45C13973332ADF3E739299CD692D9A30EFF819D6FD92A5E",
-    "ldt_key": "E3CF63B24F1BC910162625022E9D8E9E6652D66F4D8B56CE68A355E752A99BFF573EB0A4BDE1BE9D8C6CF1BE0F643A57BCFE4D749A0FBA6ADE3EB9ED7E63C80B",
-    "hmac_key": "70E61B022F48A8CE14DC6019A5BC6BE00DA34DD33FD7A927A18C67DCFFF59372",
-    "adv_salt": "19F3",
-    "plaintext": "62B9B3AAA7AE06C1CF4FC10DAB9592DF32D664EB1529",
-    "ciphertext": "690158B2A92DB0D89AF96B2BAA2BDA0CB815A823BBAF",
-    "metadata_key_hmac": "EE9F79CC56697C64835A45BB52F787F0EBF2F47F7D1F39C65656A0EDD942417F"
-  },
-  {
-    "key_seed": "5F828B51F0DBBCF16F32B0D64EF0077E6E0ECF984FF6E5260ED08C2F199EEEC6",
-    "ldt_key": "774E5108F1F1E369B9205084718CB8482C1033D8C37CF01A0947C8894515498DBF9BB6931F1BCF3832BD3C89157B373E3D20ACA532907936A47E977A1506D695",
-    "hmac_key": "78A7578566907A579605F8C87EF2CD96B36EF30F29DFD1D19ADC6D080A7656C4",
-    "adv_salt": "BA3A",
-    "plaintext": "3DFA5DAE488331777806E37620BED8F6D7977D290D544A80A6",
-    "ciphertext": "EB54BE270C16BCE344F4007DCE950C7878EEBAFE9399FD2D41",
-    "metadata_key_hmac": "D70A299F865B49660C64E03E2B6A7D52FF6F5E0E334E2E5343150098136B59F4"
-  },
-  {
-    "key_seed": "C13E52792E005B76773A7CAA506BB7521C7BB609C227D20566A0D34B401F5010",
-    "ldt_key": "84098CB4CEB398349234613AB71C4FAD607EE8E9889FB33393B6D6036EC4947D26BF1AABBE6C3798FA01918A6EF490E8405F6D00DEAB21CB1A74040FB37D6B5D",
-    "hmac_key": "E8091487A98ABF54E8A894597102F2DA954D2D450714000337CDD52D811C18EB",
-    "adv_salt": "7D6D",
-    "plaintext": "3BEDD94396C68C204AE757B0C9EA8F99DEBA24A089C8A3CEAD48B24363A9",
-    "ciphertext": "ABAC2A91A0FF82E1CF952777BDCE67CF1583D4306E302C25ABE0FE0DCC7C",
-    "metadata_key_hmac": "8758858A8DD887BE0B06A4CE5085FB489D7F8E523AAA2090E823983C400180B2"
-  },
-  {
-    "key_seed": "99209F9ED8D3688E8A9BA71ACC974F6A33740EEE13D87E61117BBCD24ED46C9E",
-    "ldt_key": "2C8FA07E51065220E580BBECBAA8CED2ADF38AAD30483CB2AF6E93528AA00DA008B256EA8347C18974421E1D7817D8E1240E523221F223524ADEFC87F23BC245",
-    "hmac_key": "C2D44B7C9FBEE5AD19FAD4A2539B070CD52A5E4867843BFAF8AA20718F8C8532",
-    "adv_salt": "26F5",
-    "plaintext": "5F7788A29C92701BDD991C4F0BBBD89FE6D79830EBD58FB5DFA57F",
-    "ciphertext": "6966882738DDE48CC1A810C98F33177E41919B31D2E40780D94BB9",
-    "metadata_key_hmac": "B5231FEBA78D42FFF32386DFCBF05E4393B7883EC71D15774DEDE75D65063705"
-  },
-  {
-    "key_seed": "966DF7A7630E058186C2F28FEA9E6B9D581DF8CDE8157EE6442FDCD09FF52242",
-    "ldt_key": "0D99B0EDA92AD23AD08913C243DE0E033C3B6D60E9D9D8DD670AA41A14A4D5137FACE3AFDEF44EAD7E42DD20AE2A0F56B4441280627F5949315D1A5D83A0361F",
-    "hmac_key": "80C2A8E33E4A6BBB28B97B2153F3C5781FB735DD082220AF5F83E21D076A5BB7",
-    "adv_salt": "9F8A",
-    "plaintext": "9AC43899EF5C19527C17A5B42490FEB1932DBF3D",
-    "ciphertext": "052AB261787C5C32B97FAEEF61F2F7300579EA3D",
-    "metadata_key_hmac": "E9E5C776B7933516400C1CBE21C865B0EBBA231A48B094741D6C05056E81604A"
-  },
-  {
-    "key_seed": "3ACE45A27191E9008E66C3DB980CCB5044E1153E4DD3FECB051B26E568E895C3",
-    "ldt_key": "B3D4B3B28AB6A70AE36C589C5C16866FF9B414FEDF67669CFDD19FB085B64AEE16D6782B9E5DB2C40151BF21DB2DD322640200454EB7C2BF4A21FBE60A1C2C86",
-    "hmac_key": "146363C05656929CD63ACA8CE77879F6AF09CD6F820D5AC8D5E298283A3B3425",
-    "adv_salt": "FF1B",
-    "plaintext": "E44D6F311235103E94C01A3254A924794F80D0",
-    "ciphertext": "6561655D1D86932BCEF36373B893FF3BA770F8",
-    "metadata_key_hmac": "24DEE3BA49C15DD28B304F82F0A47DE1CFF96D303361E3C705AE931620ABCFF4"
-  },
-  {
-    "key_seed": "1EF1E6BD839009CD1815ADAFA4C842A633D992A4714EAC96134D1C28EA6C402C",
-    "ldt_key": "EAC3A6D962F5F1165527E85503E1C94DBF60050239E7833701295AB0AC6FA6F6AA6002D87E2FF49C620E55E7A3A466BD71CB5138AA781417E3194EB2DDC4E50C",
-    "hmac_key": "D32C2E77C735D326B3DA3E7E719DDAA155A1273E07DDA985AD15922D5BF65CE5",
-    "adv_salt": "A239",
-    "plaintext": "D0A2F69C1C0B59F54EA0B5F87079235890E62E93B72D71",
-    "ciphertext": "9216740B6A94FD6E2C2777F1E4F182491D6E662DCBF0D1",
-    "metadata_key_hmac": "9A63CB63EE1C702468D288341B77367DD59EC18F6711038A825C33553C4F7D8B"
-  },
-  {
-    "key_seed": "8B2535AB74D0E93B8E364F26280FEF24B6E78F93110F5BBF533874073138BC3A",
-    "ldt_key": "35BEEB94D853346D0044288FEDE4BB649BF03356997BBA1FB994C29311733BE112474086B34A429E6D56D5C39D429DD7A8829C3FB8E5DE99AA8805CABCE2F68A",
-    "hmac_key": "7F4E1608A595E7DCD915EE1339679AF08290A663ED76142B564CF33AA60E422B",
-    "adv_salt": "8D71",
-    "plaintext": "8B7C7D68516FECA71915B44644854032",
-    "ciphertext": "BCFD8E27F1A7CDEAB05B41FE8E94E813",
-    "metadata_key_hmac": "D1EC3ED33EF17B53D2E5C712C750AB9792608025A79166C87A631B5189CEFDE4"
-  },
-  {
-    "key_seed": "68B0C0AEBB1B4F1A8A398533657B26D224DF5633DACC640005FEFDDB30AA85FE",
-    "ldt_key": "E3CE400AC3AD9A993FC5C938CF1444CA907C0E62D3E399B40010E8FF70D812E2F5D0BF2F77796445C56CB25797B39359A991666217C8B3615A62A69EE56E57F2",
-    "hmac_key": "EE969BA6D7F13BB60136DA7450DFD7C03E664E39AD3F0FCA38E382D46C2BC5AC",
-    "adv_salt": "4BC7",
-    "plaintext": "607E9F8FD75E15BE4FCB847A15710379EF38E4B31BAF2C7C9A615A61B1",
-    "ciphertext": "CF992537BD9F2485F7A8AD17BC86EB95C0FAFED4E1F809C67E49BE2354",
-    "metadata_key_hmac": "25C04A466CE86994FAFBA8AABCF18C35E67C314396ABACEBF5EA046E551E814B"
-  },
-  {
-    "key_seed": "9A3A9BDD903C0173E410A0A3D14A8B91FD6FB25618D1D563CC029FD13ABA88BC",
-    "ldt_key": "F0235A77EF2E0931E460CEE605F87A75E9A94D62D0E7D37CAF0523CEC6D13C311B1F9CA8BA4DC2EEF40B57BDB36DB2EF718CF8F3B48186A01517D3CD766A7101",
-    "hmac_key": "6B112539523DD69EFEEA53DC54D0821B84A1BD2B37AF762728A82BFCFCA19949",
-    "adv_salt": "3EF7",
-    "plaintext": "C3D22D4D9B701A1459C3E76AFC5C8017",
-    "ciphertext": "904D9A291CC4CF4A4BCCC38D387B8205",
-    "metadata_key_hmac": "D8FFBFF6FF7D4E7D1C3CD8F77D4E2DD517BDCD57629394AAFBC19C7F0CA7D485"
-  },
-  {
-    "key_seed": "61E8511E69651AE7A73DBD04B941FDE17A1C52A3F2AB122DA320E974232E6E95",
-    "ldt_key": "6B7BE769068F80C0A373EB0D42CDC1954F74DE30A1CEF8E26D4F41C077AA068DE2F6AD43D0FDC3C7065D5C5FE0C88423DB13FD07D02FD2E5125EDDF85CAD30D6",
-    "hmac_key": "89C68164AF8C1951C587F92EC69D306A6FC98BCEAEE158F1744887309E2B225C",
-    "adv_salt": "D6C1",
-    "plaintext": "93F8A03B2E25AFA0845646761EFEF765589492",
-    "ciphertext": "39F3003C41BDE3078B7DC178756596F2FD9CE3",
-    "metadata_key_hmac": "91E93B8A40ED64BE00109BC8A7FA7BA556CED575F4FED68490703415B515C59C"
-  },
-  {
-    "key_seed": "F50FBA7C9FA978FDFC531AC8D80EAC2FCFE60597C3888E2D9DE78D743F44126D",
-    "ldt_key": "45ECFAD30CA47A46F9C0A943F90255B33F96ED1B1443B47E5CA93A7787FB7E547E435A1885AC56C6DD9E19F2496C6158AC8D3E85A5D66EF5E91DCF6EBBD05577",
-    "hmac_key": "D675D76E2070B2E5DF63878643CFDB9FEE5F1757CDEBB713B9A888AA808834C4",
-    "adv_salt": "BE73",
-    "plaintext": "9DD7CF9D31109D0319EFF2B0B77BA470A4",
-    "ciphertext": "AC2387B613EE0C5E2F1C726D1E2CE80165",
-    "metadata_key_hmac": "7E8E81B28F287F68DB90EC58BB234439EDE6EB16076BA17E6C9A9089148A7A12"
-  },
-  {
-    "key_seed": "0E3AA4661227ECF85B9803754BFC759904E1146FB14805B2C466924184299853",
-    "ldt_key": "B347FD5FDEB8592B629795B73376B4FDC5DA39330217B4D6763A700EFF120BA7B81CCC2B212C15F978BBCF3CD52CDAB68CE9334ED59F9FB976E5B549E38AE162",
-    "hmac_key": "BFDD346C88E410A7CE622A9BBD76EA1E17D8C0B2ABF99435AAB124CC52D5B456",
-    "adv_salt": "FB80",
-    "plaintext": "73FD04EE2340B9B90BB39A4533A8EAC1D6E79498E32FF240F657",
-    "ciphertext": "09FB6F7D88711FBAEEAA9BE40AB489976C0F8ABC0676F7F7CCE0",
-    "metadata_key_hmac": "4C8CB886B3A608257349BD50B51497ABB8FAF3F1343046991E283321939DCBF5"
-  },
-  {
-    "key_seed": "C099343FD4A232B819F963BF52B5F1C8EB6A31389EF5E3D9E9136E13E4C45C05",
-    "ldt_key": "88DB810F77BD9EDD223DFFFA1C030A222E4C876FCE7535E2F9F34087CA64BD0CD13A336FD968044C031234BC75837F5AE1CDCE777D1477DD063178911DCD30AF",
-    "hmac_key": "D95134F685C48D23509BD58D8117FAA6A91640F4E08DB3CC45DB252FADA86B43",
-    "adv_salt": "B312",
-    "plaintext": "DD081FCCB8A5D4CF75822E2C5DAA2DC18E1F03AC881097223C",
-    "ciphertext": "D7FDADA1C6F1308097BFC739AB1506EF5EB0BB5DFB7340EA1A",
-    "metadata_key_hmac": "112865C8F64E18D6EAE178D59A83FD2C932D8D0B6DBBD0442A19BA01E85E5286"
-  },
-  {
-    "key_seed": "8CE0F66327998E3F4C4DC7A9D2AFD4C96F8348716071E88C6CAE4E145FD9868F",
-    "ldt_key": "474AAC78E2EF5A8256B5AF15131DAFC3186C8BF80A26A092924569834745B35E50EDC31BDA3AEDBC73474312FF65E6846A02C848C01277BE6BED34FB8AF255AE",
-    "hmac_key": "C063797BA13AA07E77EDD2ED6AC7CC95B09CD318F2F57853DF9F621F5DA64D91",
-    "adv_salt": "3886",
-    "plaintext": "F438E75C0B6EC928A2D6446250613E3C",
-    "ciphertext": "45110B3BAD0E10347F5507AD69C4CB0D",
-    "metadata_key_hmac": "6919301B93BEA50AC96EBA3B14C93E995EB935B422AEF3572812D95139A20CB5"
-  },
-  {
-    "key_seed": "BE71C52BC69AC9B79811B15BC2415C637E6A071A4A0DD2B8C3B61450960E3AE9",
-    "ldt_key": "2EAFE088BD5C2CE73A7D441DF39CC7B75A8F8F9249368087647465DB4587F4A53841B7C22852723D8523AA61A890CC75C2E546BD579D4AE15C22061D35942A9D",
-    "hmac_key": "5D515675C2E137AFBEC0814EECBA2C96DB4233AA0590C77EED7016978A6BF4A6",
-    "adv_salt": "8B45",
-    "plaintext": "43F7EDE6E21FF7A7389F0071A5ADD10D18ED3AD5",
-    "ciphertext": "4AA9A385D2A9D46086B02411DB0B6CF0CF38F1E6",
-    "metadata_key_hmac": "DB6E1A1D2B326E3761639FFC2D7CFE7D14340155EB93FE921DAF1742CDC8DF42"
-  },
-  {
-    "key_seed": "F4FE69415D899F4C4BC785DA4EE1A7CA27A54623CF2F6E43D63354BE663CE05B",
-    "ldt_key": "2BE6FA71806DF6EA92CA2ACB1DFC561732F88F80C429132B9E803CDD8EC1B3677D3A86DF6B4637A56C1E340D6A40B8977ABB4072600D73B76F8795299F5A848E",
-    "hmac_key": "3AC4D4CC8A6D1E845E9566203E4448EFD72E4F5628CC7759E64071D47098295F",
-    "adv_salt": "7CCC",
-    "plaintext": "5348BB05E07CDFA4DE1A5032DC77AA18F8176B50D573D42A46E187349ED1",
-    "ciphertext": "9403D67D0A27A1409CE20F9F8B7D54EA92705E69379370C71A4CF8EF02E9",
-    "metadata_key_hmac": "0591E04F26AEF6FFC35FD1CBE405095B09D836A276A8F9E5DFD08820AAE41D4A"
-  },
-  {
-    "key_seed": "6A408471C5B4AA6A83C759E733EC913838A8AB51DF41F6E5BBC6C52103996EAB",
-    "ldt_key": "BE92E7B3230524E7D73E1641D7C17CDAB6183DF53BA9E23F2C06D27E0766D634C59D03E8352DDFFC3AC02FEFD1922525FC93A27005181F2B702275C6E5F58686",
-    "hmac_key": "B30A4502CEFAD6C91BDB8AF890188223FB9BF632239F4F8543EBAB2F5D44893B",
-    "adv_salt": "B569",
-    "plaintext": "D301E83E71779D5A5EE57D2DD388F8902E40B7D42B7D005A73",
-    "ciphertext": "73C7DA92FC745D6AF5366AF3C4629FE9CBB83A07148D049AB8",
-    "metadata_key_hmac": "D5729AAD46358847132C4CB9BD09086ED6660098FB562ED5096E148FB197CF0A"
-  },
-  {
-    "key_seed": "A7EB4F2F0675BB6B5EAECA9F62AF19858B8176B6C65DFF1AE6A079BA62D820F4",
-    "ldt_key": "37FC03570EE05E7382EAE553AA4DDC83A9CD52E9838788D3F1A7F0E31E0F48B0AE60B5267331C1AA70560F5235034939264F77F1C2DCCFD7E5D8B21437230582",
-    "hmac_key": "A0D12D31A7C92885F5EEEC124877D267488C54666A7483695C738FE15A0054E7",
-    "adv_salt": "7146",
-    "plaintext": "ED858374B2F63FE2295C03057FA1C608",
-    "ciphertext": "DF2BDD32B531487175FD70B52EE04359",
-    "metadata_key_hmac": "29E61C005E33B275034DF4D632CC745F589B41A57EA1DB3A932A3BA3763582E5"
-  },
-  {
-    "key_seed": "18EFCDFCA16E365030A5262183C26DA73D04E7AB89174A49FB4362C66890E118",
-    "ldt_key": "5148EC122339DED20615FB7DA30BF8BA2393CDDF53BFEB2BDFEDD013EC712644DC7C66A4EA0790088F589896900A3E4DF303E9F0BA07ABCC6005972A76EA7014",
-    "hmac_key": "61954A3DA2BDC3F3BE2575E19A57795F5815BE95F7EBE0CEC73E61090A9B1F1D",
-    "adv_salt": "5810",
-    "plaintext": "00EC894C423C147AC4D010DEC2E9736D44",
-    "ciphertext": "E9BE57C4FC0A029AB7B00D059E2B85F19E",
-    "metadata_key_hmac": "DB51ECB68F815EDAC9E9224B092829809BCA51682683A6CA4DDDCD8794018726"
-  },
-  {
-    "key_seed": "25AA14B16817BDBCED1E9694EBCA2F8BAE8FB017E55581DDBEEC6F0F68FF0F8E",
-    "ldt_key": "D326C60BC14193B314C09549AFBCE6C16783B28DC53561480EA5377DB3F1F4A71BCE8D000C9366E4646D1BC463EBC994753CAC00C4D99886DC4C3DA81B9D09BB",
-    "hmac_key": "4D3473A979B94BCE625FB3B6C0EADB1BAB813D7EDCE8CDF7B008CECFB5E26B25",
-    "adv_salt": "E239",
-    "plaintext": "97012D42AC4EC974B4B3BEFF19822A7CA44F26690F68409FEB0BA7",
-    "ciphertext": "2119590810AC61AA22DAC6A2472331DEF9910F169AE9F578449693",
-    "metadata_key_hmac": "CF8A02C467D95938D541EAA0FB11D88D8337B907DBDEC83C3E3B43252232F74B"
-  },
-  {
-    "key_seed": "82B6E5E0CE0A08A2E3AFFFE760C8A1D2EC885993248390D4F5E93B9A04F4D758",
-    "ldt_key": "A7E418C2C993648C90E6FACF91636DD34EA7D6BEEBCFA22C116B26AF2D5C2CD900B277B85A3E731096DF9A8A33CFA783FFA3A14EABAFAD6C1D117AB608A27DF0",
-    "hmac_key": "9D8F7FEA188248A5104B1DF4F79A9F6A5DD96ADE0DC893F28FD5570E4AE28438",
-    "adv_salt": "7CF9",
-    "plaintext": "9BBD2A7D75DFF4C34DC90AAED6814466D9BEA5",
-    "ciphertext": "5064BE5632989938326F75233EF85485C1AF71",
-    "metadata_key_hmac": "85FC88E620E660A1F0BC705A7A72F04F908494EDE585C7890BFCB6E0074D4655"
-  },
-  {
-    "key_seed": "4311E1E839B5C8ABB86378C3CD11861D230BBC9E6367184EE02A74B9BA73D0D6",
-    "ldt_key": "B5AD803ADAC55B93943AFA73E9500D6CEFD2F3FB75CF4B532452C68E2A9C9CE4497E260A0F5991A058D7154A954BECC4E1B227240EDD9AE661C40734AC5E5289",
-    "hmac_key": "444C68A622F716A29D1B042BDC2848449635C6557BB194CB04D4EBE9BFE3A538",
-    "adv_salt": "5182",
-    "plaintext": "74456B98E2FEAD8E389B4D4F032CB7A81664666C05C763864E523D",
-    "ciphertext": "A5B41A2E1668084498477567EC9BE1756B32AD19E2AAEE2D7B0CC9",
-    "metadata_key_hmac": "C5A709EE6A1294E887C68C4DBBB6E77BC7977C96667694BA404DA79B2B0D5BFB"
-  },
-  {
-    "key_seed": "2A13635460A9710B09F643F2CD2C220B5CDD9F98F4953CB600997A0058E8A556",
-    "ldt_key": "7E9D0633EA841E57B76DE4B61717FF2B31EF5980F1E02C52B0A41584AD47CAFE4F8470157DDF8314AC1471FEA14F4FB51A76B6D61A89AFA98C7BB8B8EEC0C92B",
-    "hmac_key": "3802765D17628C24C6223D0E8DF9AD55D3B277DE4F85708B6E96F29319B0D718",
-    "adv_salt": "6998",
-    "plaintext": "F7D8B1AF30844708D179106D5E329C02D72A7CD6",
-    "ciphertext": "5AF1C2E04FBAD114E66BDAD7530398F572559757",
-    "metadata_key_hmac": "430A3A52FAF79431C1A891AA9C3D705B17626B829EBCA6F7AC095CA938D08400"
-  },
-  {
-    "key_seed": "C2D25F59658A7206D3D749A794B681D12E6E0312DC1B45A8877A47BCD4790EE0",
-    "ldt_key": "E62E721FB8BE56F5ADB52434D778EBEF25CAFF0E184D448548CF3DD5372ECD4F7B8F8157CAFDC71AB78BAC3A236BD4A36F7D55ABA6E3091B3FD5CB56D4F9E5AC",
-    "hmac_key": "61B26D9D3FC8D6216ADECA830C1413590194FA25FEBBC84AF095BC314332E65F",
-    "adv_salt": "36D4",
-    "plaintext": "5D6BCADF581061C5CBBF0E6A40F17DBDADE5A7C7BBC1E8A1",
-    "ciphertext": "5ABFAB0B064982F46175541B4265D8993E1DCA74120B3634",
-    "metadata_key_hmac": "0EEAEBDF097DE3D854D3B0AAFD26DE8C2F0D96244C27559BBD6AD3E4F2706E7D"
-  },
-  {
-    "key_seed": "D846015F3A085AFCB52243AF98B86D5FBC9FE00B018F89094F2598436136DA10",
-    "ldt_key": "8C6B74CB4BF62487EA664B1CC00BE98A2077A0C6FD40A0042EA01695043C73CC3EE81D6E173977D2BE66AA9CC8236F299141B0329335ED5FA2DB77CD1FE44215",
-    "hmac_key": "B96764DD552A8A498A71ABA08B2016C6A476F76D5973BF8BD726BEB0BB8DD765",
-    "adv_salt": "C9A4",
-    "plaintext": "D4447137F58623746148C336D14E67BD3F29CD7C2D1274E0",
-    "ciphertext": "157292352629229CE134A70B8BD146C252EB926C4107AE2C",
-    "metadata_key_hmac": "BA859EC74F1438FB9FF40FE44DB76ADB052B81E65610E327570463AB751DB0B9"
-  },
-  {
-    "key_seed": "73FDD0C8E8FE44D996FA9180979591EA2448801AF4A355590F2BB563044E968D",
-    "ldt_key": "DFF7BD9BCE632499BECE737F13E127580E93233151360A07981C7E6226CDF2061A4CC2DE0FD7FFB770E3CDCEE6888C23523088DE0E257C37E01D24ADE12EC869",
-    "hmac_key": "6B997391B1DAFA341868A6F6F69F88A759839BD46074CC36D84A855E7EE761AA",
-    "adv_salt": "C576",
-    "plaintext": "6728C48ECC39EB80D2919A307C96C1ED4F1F760D20C75F690FA2C0687282",
-    "ciphertext": "9FCE1D5340D44AB1FDC5915B9DF3BF6362F24A6544B3F8A1A7D4250CE751",
-    "metadata_key_hmac": "0FFF129F5B044196E5188692B687CA74501D7D07FF649AD73B95DA3C22C36FD7"
-  },
-  {
-    "key_seed": "6DFD6D92352D2B434AE3AE589CC7AFEB88E35F76B95116805DE340CD6E456083",
-    "ldt_key": "A448803AFCBBC98631397AECCCF9F16723CE9EF37A9E94920260A15332B722329DEB61EBC8CB7ACF76D4AFF0A6DF60C159FC179E32B4FCA9A9237987699652C7",
-    "hmac_key": "BEACF74249CB4AAC9A74FC2E817D8FF046B9279CD7D51D0816EAA9A2680022D0",
-    "adv_salt": "A4A0",
-    "plaintext": "24C60DBD7A43C2B9C7A5A4F17991FAEE",
-    "ciphertext": "8EC524A39825365B8649D164FBDAFB9B",
-    "metadata_key_hmac": "C28849E4E7C18B1E42279AFEBC21866DC63B5C6EF1041568029AF4946950F208"
-  },
-  {
-    "key_seed": "5B556655C181D91F2571D072841B954622958A26E8B5FA22329BAF285EEC95AB",
-    "ldt_key": "88367E8701FB603E6ED5BE0D1F6DE4C1AEE0E79C9196B52F8E84B9DD8D60C16F7724952762C6DC2893F8287DFEF80A55C49FB417D2CF65B2CDE617ECE0062877",
-    "hmac_key": "0D686156F80AE8C1AA30706CD0ACA0CC860609ADD16948470CD4975774F55C96",
-    "adv_salt": "E675",
-    "plaintext": "712B448A4315C640379DE064133DE0E1",
-    "ciphertext": "9E6DBA6F506F10E84ABA9D777D005919",
-    "metadata_key_hmac": "E179FF8EC43888E59573387977A22EB28B5DB9BD1ABBE132836E63E66DD0EE7D"
-  },
-  {
-    "key_seed": "7569FCAD04512A9022654D0C42FC09E5EB23EE8CCB2BCF015752DC23258DF74C",
-    "ldt_key": "1EC058B5ADED8CAD6FE9E7C519CFAC976E464B6F1B2053457B60F0EDF67E9A69F0A3FFE26F9D9F1DB5F9E90CB5C44F765FD80E4B0D84EF0D20C3DC1CAA148B57",
-    "hmac_key": "4C0B183A19276F6CDD389EF363E7B73D26F52EB72E360817458F8A355B6A52B1",
-    "adv_salt": "ED54",
-    "plaintext": "3F66104FD3E653B05F7CB8C436FDDE2830E743422C9FE2",
-    "ciphertext": "882EBD21E8AE0B65901B274B564F6F7EB27530735CD991",
-    "metadata_key_hmac": "95E2D4BAA5D346CE447BF0CAE77B3136D6EDFF7B73720B8174CC7FA6AB25C60E"
-  },
-  {
-    "key_seed": "D734E43327907951231721D8EACF1CE1C67CF8344DCA240625044D41609B66CE",
-    "ldt_key": "A2500D3D317A4D1FB41D65DEC6D308925D093E0FD1AE16704F85B53467265681AC76581CA18EDA7083C79EE61ABCFEF9C1E0E40D8A676C6A122D91C9691A00D8",
-    "hmac_key": "10426F35CD6F93E45EDBF4B1AC284B0245F79126F7F713B82D8903C869989C26",
-    "adv_salt": "A66B",
-    "plaintext": "375C7E3373E88B579D4A4D87E5B8ADF9657180BBD647",
-    "ciphertext": "1F88538743A5DD988CEA35EB1B60DA18D9A0DA08EAE3",
-    "metadata_key_hmac": "C9B24D070C4E1D9F916E514D8E16CFBD749C2E788157E35B622D437B91423BEB"
-  },
-  {
-    "key_seed": "75BA47FB5A778384C5C92159BE3D12E13DAB1466FDD200A2FAD9EE3E624392D8",
-    "ldt_key": "12A62E7C37C689305D617E243172FC5F608B811B0F07C5616264BFF94715C193956D1D714D2E7D10A437B9F3B412F50E2C735B3481A4D857B39C7CD090277DFC",
-    "hmac_key": "F1ADEB123E02F76FB0AE9CE759D0FB6D23931A806B9FBA3159F1BF0B5AC41DFC",
-    "adv_salt": "3BD1",
-    "plaintext": "1C26BCC5456D118D9DE9C0FEB5EEDA73F3BA16CD0914F9",
-    "ciphertext": "B819FF5268145E99A8EECB759C776C426BCE55163047AD",
-    "metadata_key_hmac": "CB31D51393D442A6A13518E02CFCE0F0301E8AFA44FC60B853057907F116CE68"
-  },
-  {
-    "key_seed": "AABD54056C692FEA551A7199FC978C374D3CB32527CAA8920AA839B6F6670C34",
-    "ldt_key": "A84809E217F9E1AAB6A3C11B0DF47AC0F7A15E8D4218A842299180EDF56FFBD1E71846C83622F7014B6DD3EC1B1C3974D632F3069C88E477A124180BE903BF07",
-    "hmac_key": "F1A83D7FA59840297D52B05C679D66E40657402B0C501F0B8F139B4385F392D2",
-    "adv_salt": "8E00",
-    "plaintext": "06C795A1252A9BC6B8C71DAC3D856256F15BE1068AEA86E7BC68",
-    "ciphertext": "0634B91779A563C03D70EAA14E0B0C7E6A72A32D5AA3F9D27D85",
-    "metadata_key_hmac": "ED6417BF208F7810B9C4C7C504354EFD629A00E86B7CFA2EAD5B2103249C90D9"
-  },
-  {
-    "key_seed": "28778BF659017AC84B559C88882D9B309AD2C92C4CCBF2F8E512638D66C91733",
-    "ldt_key": "5CF94149484918D409F3E6E0235883118044976F2D9379C09AB55A875676EC6A9C80940DD890FF94407DE477EFF0F1F0A7D22BBC01592FA6AAA07188C02270A1",
-    "hmac_key": "4F207486F2A8504F64705AED1B1D197A26F26F284F0E3A8DE44326F16968562C",
-    "adv_salt": "E311",
-    "plaintext": "32B3AF5757B29A5DD9802E6C2CDBC5BCFAB6E083F0FC87A4AD62CE85BFF655",
-    "ciphertext": "7A4E0A1435A24A98005254F2C9AF92D3DEE98F8CE1B97C2508C1C0E3957E63",
-    "metadata_key_hmac": "2ADC12BBE2A3C72722721D8C5B43A351CF2CDD24BEBD3FEF1FA186A9A96BF11F"
-  },
-  {
-    "key_seed": "50A99CBB1FBB19263E42AC51D667C1C0B7ED680B43C147F0BAD56279722733BA",
-    "ldt_key": "2AFE26490D490840CFE7332BB070E05BEC9D93981469769C2FF03F695180D828A9B7273F4B541DD329E7E9907A91571EBBB832AEE61D2A7FCACDDA689C7D24D0",
-    "hmac_key": "6C2AA4D3940A672CE7ED32E0B0C67D7BCAADAFF90643618482270C86B2789ED8",
-    "adv_salt": "5FAC",
-    "plaintext": "DE38D1F851723F02BB74C7FF5FF813AC2DAF7880C08D0188F300",
-    "ciphertext": "1844A6EE5F4457EEC3906B9430752853EE032577C425119F611C",
-    "metadata_key_hmac": "522B09A18E7168063FCD85D0CBBB46E49B199D32FF1B853D9F5A2876D28EA24E"
-  },
-  {
-    "key_seed": "9FC111AB46387C47EC77ECAD602AE550A411D4C433763D2FFDE06765728C86A9",
-    "ldt_key": "3F05DC0D3297958CA2F3441821C02E48013C92059D85E43FD239B806086D73C81EAE6C02050169D47FDE796456A0F02EB595672C9A740091BC7705395D509B3F",
-    "hmac_key": "F631CDB66C21398092FCE374365D385509B6F89C7AC5754AD931E622F8DC6B0F",
-    "adv_salt": "1A27",
-    "plaintext": "839A7F8CE22B6796D4AB453254A7E06DE4004A9F7F41FE",
-    "ciphertext": "4EA1991ECCE09408C1F3E818B47965062AA73D2B105F86",
-    "metadata_key_hmac": "4804DF0393C3AF50066C999B7593E9BD09C55F5D5363DA3ECBDE091A9A5020C6"
-  },
-  {
-    "key_seed": "9085A08B9A529F4700953F9214136706C3AB56B09757DB19A733F646BFA689BF",
-    "ldt_key": "4261798242A6419B04C3C8580D45BCCC14E955085F1AE04A528FD085725C1EA92786A33C2FE0361665EAD307F606B1DF250C879171518EF33E3876A37F694A0F",
-    "hmac_key": "5C5D9187A1291CA90EB9EBA77F1AF3288FA1980332D874529AC94814B2E6095B",
-    "adv_salt": "BD05",
-    "plaintext": "5F1A621C4CFC22E0E4D622DAAA70E5178B",
-    "ciphertext": "24EEDF908E842903668F2501CCE5B1C588",
-    "metadata_key_hmac": "3769A0AC03460BA4A4D99F354ED894A292994B469BD225B91E852805227BB60B"
-  },
-  {
-    "key_seed": "BE2D76792684F3AE542005CC584DEF72794882A9F239E8907F9C4D077A75753C",
-    "ldt_key": "D2D7C518BC1295F1D0D11D19DAFD49220498F8942251CA0EC735710C3A4FB706B34EEDFFAA10E74686E1C2705BF5320645349AFCEC4391AC9F064A6ADDFC662F",
-    "hmac_key": "CE7988AFB852B691E917B6B40DFB760AFFCB5E27B6C547F3B124A0328BD2BB33",
-    "adv_salt": "F712",
-    "plaintext": "5CF5B0B548AF0BC3331C4425025ECEFB910858B3BDF0AE86618AD11B72B8",
-    "ciphertext": "CDB48FADA18E4F7E0025B62F3D052529EB4CEF8E5C0996A7C5CB9D3EBE9A",
-    "metadata_key_hmac": "6C675B3B1471235F10B93BF06919328EE45F91CEE0FCD5262B08DE13FC1E0C7A"
-  },
-  {
-    "key_seed": "A71EC15A86B5503D5B378EA02B12311B3E3666745687941782FF0E449DF555EA",
-    "ldt_key": "52C143EE553514DE2237F3D32B571AFF36558EE58640BE1DDDE6C94BDEA34C59A694D2EB0E0839852AC2BE1FBDF288603178E0A18FAD133115E2CA7E6608EF2C",
-    "hmac_key": "2E503289FEFD8B4E8B5E93ABBE92A031869D7D8914238AA7A31151486E5BC091",
-    "adv_salt": "5A5B",
-    "plaintext": "4F433B75A1FA22425FEFE0FD7AC8E402FA1431E7612EDC99347851",
-    "ciphertext": "11B6FACB1B39677CB8C372AB9357043773FD32795FF08B4B3E3323",
-    "metadata_key_hmac": "29D4D89A053A7857630AAF59E23121429E5F47F1269E6B83FC448AD00CBDCA1F"
-  },
-  {
-    "key_seed": "F05EC969A52DB799EBC2A857313FE6176B7F2D555594F2BB4BAFCC19040C7AD7",
-    "ldt_key": "FF591DC26B212260EBD43D17D08D6702732F5EC1E15A420EEA406EA2BF26D7BA1460F9836D0BA5043BA2B45AC62CDBF279B99BEF728FEA14F0407BA453EE12F1",
-    "hmac_key": "9894B05916662C8EB0DA8ED3CFBEF4C142302D38DBCFF6B0137714398682D635",
-    "adv_salt": "7DCB",
-    "plaintext": "F001995BD1175042055FB5E9F65CA877065D70538B3C1A1C0466",
-    "ciphertext": "C76A2812EDEB6FE15A71A87C1E774CD7D1DD8F0246ED5E95952A",
-    "metadata_key_hmac": "7165BE02DA03AF773DADEB1107070A1E66F96426AB17624A6E535EE991D6BA97"
-  },
-  {
-    "key_seed": "332FA48738B516DC5C7DE58C4922E7AB27912E44EAD434A3D758A57936606A9E",
-    "ldt_key": "1F79FE122B4E585A898B53172EF5D29DAD6EEBFF7B620A43B79108083CADBA5E1F347D044CB87420003E5CCA8E9B7F2C5BD8FC91AF63924DEC6445A135BA346C",
-    "hmac_key": "E4AC9347677DB2BBF6730D4FD7401A919E2356B5B130F0099648220F7D170280",
-    "adv_salt": "E54C",
-    "plaintext": "B66C2F1C04892188D338090EBB3B79B10184F8AF36785EF1BCE870E07A",
-    "ciphertext": "8A12EADE61D558CAFE09354AC08484C3BB2309294203F009AE552E0750",
-    "metadata_key_hmac": "F097551D91430B3FEDCE67D1B95A466D04AB1C361F8ACA47676387396C69EFAE"
-  },
-  {
-    "key_seed": "83E1C95BBB9D0F2F5FF04C04135C44C74F7FBBDF0A3E954B083D28895636F570",
-    "ldt_key": "406B4A3B05C30F607342077EDD7C0B1AB5A96861F38EFA221A804F691EDF82272AE1850C223890C1E2CA3624974ECA2EF0EEAFE92153D6B2A76FE8A50499F3B6",
-    "hmac_key": "9AF63F1E511117BCABC058DEB685486958AEF0DC19C8627F5CD004FFF3EC0297",
-    "adv_salt": "1B1F",
-    "plaintext": "BF1144EF14BF286F6EBD79A890CD19EA560D12E0BC3A33",
-    "ciphertext": "2BC58BAC575A72346D9198498EE020FAE15FF232AC5C8C",
-    "metadata_key_hmac": "E6D8AE6D060801CFA0A7DE1953C82415F712BC3623813FDD79EA8ECE27441967"
-  },
-  {
-    "key_seed": "B16EEE688EF2E975226581946F2474FE9471566E9D7BC198A4E1E59D9E924ADA",
-    "ldt_key": "AE056783CE871C8F87BE39C0BAB58DA475E64232336B36FB7EE0CC722A50B015D377F75A5EA28D05E8DBD6A30D6D159C8BAB40DB8BBA2C7B65AB3CE032083C2B",
-    "hmac_key": "F8CCA6345A63A86B3C98608B3F81CB87A0101C6D6472C8A233C33B14477E01FF",
-    "adv_salt": "2277",
-    "plaintext": "14D8647A5B948EF857869BBFC7C2A4386433",
-    "ciphertext": "BBF95D28B4F8E944E60618FCF36EEF7987F3",
-    "metadata_key_hmac": "D50F850029616818304DE732CF9C0F9E0C2E1B38052575B3CF3EDAB4E09F047F"
-  },
-  {
-    "key_seed": "889A1CD66AF641EF76A24C80DF360BCD030B39AC12E4795DEFF88D9310937232",
-    "ldt_key": "56C0941AF6C85BA3F6B4F2B66AEEC0E166B2EB1FB0DFD97F3032C9282145D311C44BFE289D114628E0AE0944A6FAD1DC6E6078B66659BF02E2053592731CF2AC",
-    "hmac_key": "A7CD55C44A009CE9083525BFE3A2D5A183A772004BDAC2B7D41466AF1B71C020",
-    "adv_salt": "35A5",
-    "plaintext": "1101FFFFD2839177C6D1A49B34128840E506",
-    "ciphertext": "B379D7F3AA98D8B4613E961EC0C6EC435316",
-    "metadata_key_hmac": "8E04E11B52FF3FF2273C615F4B1E4D8E195D19B35FD616AA38E009865021CB06"
-  },
-  {
-    "key_seed": "ECF470CD37ED8B48640531F3E7D4AEB728B6CBD8C9C0B7D2510C63D93FF0A091",
-    "ldt_key": "4263F97589C7A435494E2A9F40E6F2B47B9FEBFBAD5C2A807AF56D52F88AE2487BF354179FE0DB947359D3B94EB7C251EE678BCA19F8B489693732A9915D9AA6",
-    "hmac_key": "028C9C0A2EA89C0ECB9A9B96C4F1F9BA65EEA131054725E0CC5DDCAF203CA0D6",
-    "adv_salt": "61D8",
-    "plaintext": "DA4D5B503C04C69497E080CA76CBAD43A3",
-    "ciphertext": "DE3610A2D12F3C5C41A1525E09D74EC357",
-    "metadata_key_hmac": "A5BE078DA1268DA2192FA4463E3C954A97A85CE1537CB223C7FA594E74A4F0C9"
-  },
-  {
-    "key_seed": "86E881B42649FE24CE05235C224632BABE8E1405A24C0DC28D24FF0AF7B60B68",
-    "ldt_key": "43299E37E6E8BAAFF036AFD9D368E9B1F5CAA1424C22C7ACCC395C1F6645D31E5433E86EA21CA62CC115A2857AE12C01AA26F697845ED26FB484362E283E53E4",
-    "hmac_key": "74D53B450E5844276EB6B1B54E3F39BCDC025F37DE4CFEC297704CFC5163DA40",
-    "adv_salt": "16DB",
-    "plaintext": "757B10011CA10BA4A407A7FB9C1F5E7BBD51",
-    "ciphertext": "E2056AE97E26E7FFBC8F331EAA84B35F19FB",
-    "metadata_key_hmac": "B8EE570FFF1449B35F26B19A4F2AB43CC26AAE4CFF79BC3170CDCC45213FADB3"
-  },
-  {
-    "key_seed": "EE5337E8A310C7C94F216740C691A688FE8FF90CE183DAA33D7F042F4B50A1E9",
-    "ldt_key": "F9A70A0B325C72700845305DA134E227959BF8F0521AEE38DDED4EBB93CCBC09F6C2113A5EA658480FCB52742BA7FBF104D0A93A0548ADB4313A7EE2E6814F4F",
-    "hmac_key": "590CA973F27E2399B39D3E30B1ABCAE233C02B49D8E35EA708B322C34703C44A",
-    "adv_salt": "CC80",
-    "plaintext": "2C37AFABCD0FED7118EEC7DA89411F97261F4D2C84F8",
-    "ciphertext": "D7BF38B9D878A4FBE06EF3DBA6B9F2FE0CE830366ADF",
-    "metadata_key_hmac": "D9522B40F11053E5C57960A0D212C6A126B3AD67C5E12CB2DC85A5D120659E64"
-  },
-  {
-    "key_seed": "4526C12D8ABF2B204754265B932ED65C1C272217F72F832D7ABEA54C31A0925A",
-    "ldt_key": "76304EEE88BE11C0FCDC103F80E3079B25C7F0FDCB97520DACDE9E0E65C6F1A0C3A0A9DEBD4BD0EEBAC4D65F7E9EB1B5F13FBA910679FA424AFA95B5CF9739A0",
-    "hmac_key": "71E41C3296E3C397C3150CB29C36875D4714C3321FCDED6E001D9EC65ACC9C78",
-    "adv_salt": "D2C6",
-    "plaintext": "B655F13794C279D54FC64D87C9594D7811D2841DF9A62D5A",
-    "ciphertext": "57783E5836E0811E618A405063779BA35A909A1E50A0507C",
-    "metadata_key_hmac": "22BE5456B855489256715A0931A5D686F5B87C6C9A0992E6A694EE3BE8FEF746"
-  },
-  {
-    "key_seed": "461E80DCDA74BEDA35ACE9376E1EB05AA554771B9E5959EAB959E49A0E559300",
-    "ldt_key": "7574CA69BED05806C13F6901A5BBFA80AB3CC00DBA0B934DA7CE94064D7B55952941E3E81FC1EE7C61EAF1B30BE2141AAC6C7FFE8D0C33E1114A84FCF15EEB02",
-    "hmac_key": "152E43E82FC16A384EC3467EEE5B6CAB9A28CB31C7FD4E780989220D81BB68F9",
-    "adv_salt": "4FDF",
-    "plaintext": "773F77B30DD0BF7E16962FF93E85F3A0",
-    "ciphertext": "39A008F72CB3011DC67A472316B1AC89",
-    "metadata_key_hmac": "B41FC1515A21B25773032D7318A45DB772B5373B6E7BC7690E1AD7671EE98ACC"
-  },
-  {
-    "key_seed": "E5BE2F0117C32F3E6476FE2D60F84EB86EC676468FECFBF0E5FAB83DA50DE853",
-    "ldt_key": "1AF39EB2A3C9A7C385231A254EE36E9E2889ADF9908CEEAFF6E642F4E301AEE49109F3AFBD0E5A495F614D0DECBD3AC21C2246B553DA4320CB0C4F0C348D5FD7",
-    "hmac_key": "898F388F7584FDF404382D080CD3F0F8164835A4026C3E9BFEA691BD42D337C6",
-    "adv_salt": "65E5",
-    "plaintext": "1E71789A9256C512EDE163EE6615BED688D90ACC293C",
-    "ciphertext": "72814ED024666464596FD640C5CDFC070A122B0D5138",
-    "metadata_key_hmac": "35F34DCEE430E78956E84CE300F70D8D16784ED1CA1F4E51D20FD0AD96D2AD64"
-  },
-  {
-    "key_seed": "DEB8F3AB02D4CA40FA5DF4A8E0C755935C66113E49933EA39D5094AE9CE3C938",
-    "ldt_key": "5C51E916F1A2E55A7CC4167021A12241070FB268C481C5A442A341A339782AC383CEFE0ED38BA27C5E0950CFD98AB131C0EEAC2333B8A881F69F7F540D4174D9",
-    "hmac_key": "CD3C321F51017F76C486C86CE0909F6C1448CBD6356787EE2D8ED61E15993CB9",
-    "adv_salt": "B3E6",
-    "plaintext": "FDD913B0BA6F414E58EA34611CE0A4CF69B6",
-    "ciphertext": "C3534F67F05E0158BF61250C91DDEE5CC07A",
-    "metadata_key_hmac": "B68FCBA4A62F48821EE88DBA6C150ED4179FB22DBC355222DEF00BD9B7F7DD94"
-  },
-  {
-    "key_seed": "DDE4DEB7FB1509B4B94BD641F49BFFE25216C7800BA49DE9774E0B3E00F6B1F8",
-    "ldt_key": "F0022155C9FE86F00157652A1AF200182D2DF9BA33D569C7B047E664C8B675C6A3CA7BA70BFC9FB42F0EC7B4031860B33A6127F777F264E10745CE34162E38D0",
-    "hmac_key": "EA0780167FC4307FBA5AF6E1E3DCDAC804E04E640C3E901ED314EC6168477255",
-    "adv_salt": "9F99",
-    "plaintext": "1EECAD555BBF19AE0466800BF45934049BAFEFC17F4422914A5E18C3",
-    "ciphertext": "41E6559722EEF73DA8D074CC8F16928B2A9E7641C8DE05F20E8E272F",
-    "metadata_key_hmac": "492F0C9C8785C62C7DCEDD1FAC417F955B655E155082E537294AB766B6C6D0A8"
-  },
-  {
-    "key_seed": "44CB781146A3C78E6E8A208DBF6DEDC942DCDDB5DAE79B2AECA40A5D283E6958",
-    "ldt_key": "7D05A9CEB622104C1C613DC798EA67747C15045BFACC75FE83E64D1E559E9FBE5F8BC1A929938BA5163104CAB88BF8C351C9A80B40B8647C350D0C259E196BB4",
-    "hmac_key": "A876711C19F6F84BE72A67C71949E2E219334EA17808DE188C61CDF903E62888",
-    "adv_salt": "A42F",
-    "plaintext": "277FC7842DB8B13D285314F3970AC2F9C4566C",
-    "ciphertext": "791F4347A4B3480761E0EFC3CE606FA3A5DB3A",
-    "metadata_key_hmac": "019A49B77346288594C858C6321C99858927DC8244EC07980728F7898C53B9C5"
-  },
-  {
-    "key_seed": "CCC6BBD4A930ADAEF8381C0452577F7E86C85F0C37F0F3F51D83BE608DAE9729",
-    "ldt_key": "050C71B86F9E827BE2E90E0145917FB64185B4D8A999920B8FC795D0BBA3FD5A56D955E7125796A1D2F8E411FB3B98FDA9F23AD5BFBC0936C073105A9576043B",
-    "hmac_key": "80D960AF3A480DA154E0B219ED87B57D1A16A82CC1156C4281F5EC2BFED99228",
-    "adv_salt": "3A79",
-    "plaintext": "CFC608FED7EBC2B62C40878DC32459D6CCFB3202787666",
-    "ciphertext": "407709B5E4025C8F6850F820E542B2CA448A895A12C17C",
-    "metadata_key_hmac": "B58D1908D6AEB8F13B67BAB587FCCF97CE502315F08FDA824A85C8B27106FC24"
-  },
-  {
-    "key_seed": "8B179554B6C08F1CC2F308756ABE8DABEC0070D3B88E30A69A40CB7C0073D74F",
-    "ldt_key": "04CA9C627A91E340C3EE0035FA320161CFDA6E3A98ED48FA7025276EB65EBC61293246F65C1A856DB729CA1B8EB8429F976D016FF9E05619BBDBA6B7AF6FEF42",
-    "hmac_key": "F115187A05F7E99365B04CFC69025E64746C679CE382A5466030D2D36A84167D",
-    "adv_salt": "950B",
-    "plaintext": "263CBC2C39CE81978946AA45D2F80C4242383F",
-    "ciphertext": "A2AB036CFD8A49A0D96BF0AC8CB8FC30321FB1",
-    "metadata_key_hmac": "CCD14A8088B511E5D4391479030874AF78D51FDE0C7F3985DC91CCFFE7913A34"
-  },
-  {
-    "key_seed": "23F0800D7FE07E42EDA7970AF9A3946ED4B71D4317E6275955840AEDB49122E6",
-    "ldt_key": "D91C2E9862353F094F2AB6710A11BF6CE9E77D2EEFAE85FA0327678CBC6A50AB428B9A844911590B7836BBD61E473946FE412668DD720042C5ECC60910A013AC",
-    "hmac_key": "5D6D0D8E2E3059E60004DFF0502432B3DB25E9A669BE95483103CC57179DA59A",
-    "adv_salt": "09F9",
-    "plaintext": "0EC8CA3BCF7D13FA317406CA74DD9518FBB4397DFF598C31929E8EFE5F",
-    "ciphertext": "B7D68C3ADF3500F84379B71FDE36735042A5D1B82805947C61472FB609",
-    "metadata_key_hmac": "10A7EC01CCC08C077A801221A6C5AFCBE2C7CD8DDC82EE6E3C7F7F0FD9A9C5C1"
-  },
-  {
-    "key_seed": "E0C623A6D0CE4191239367A945BDB722BAA22D2E1A0D5247EF924D7C4CA68CFD",
-    "ldt_key": "F0803A39DDBF4CD032EB231AF6C0074B3D44940FD54CC95469D47BCF733AF58FD3BF516F018D93646030DC80B618E1101179AED6865F77159F36811FE1C2DF19",
-    "hmac_key": "E7D3F45040D509A1574A765B64FF3AB8C26A0FC6CF1D40F234FB09249CA40FC9",
-    "adv_salt": "2812",
-    "plaintext": "A72343C95BC54391FD228738D24F308CEE3D9A42",
-    "ciphertext": "B881F58C261E0D8C45775DB7C7888E4A7A41930E",
-    "metadata_key_hmac": "43AF65784B529CFF5BEC931118C36F7C70462D82B5F5888B0E78352BB2EECFF1"
-  },
-  {
-    "key_seed": "0CDF3A201E6C43C1892D459351D83A5ABB549755626837458658615CF184DD17",
-    "ldt_key": "5400C8B5A3561E13619F4C8392575A4E0150C46607BE1AE71C09A2ED031BFC2857679460EC9E4477F6D79C34AACBB8EB385E1875469010A14A3D34FE47210096",
-    "hmac_key": "5C9A65AD78DFBE78506F6E39EB32D95783A760DBF18C65DDEFC4533AC2BB703E",
-    "adv_salt": "7EDC",
-    "plaintext": "3070FAAA1F5E0F689157D8E5BB0A5653",
-    "ciphertext": "05837C74EAAC8D82208455C1F9DA79B7",
-    "metadata_key_hmac": "058486F7D7262DC6162CD2680AF73522ACC29860023A7153210B2F17705C4EA3"
-  },
-  {
-    "key_seed": "F4595E0315B783D09D0ACE96AF55E417E6B88EC9D6B019A55736C6BCBDEEEB40",
-    "ldt_key": "95E4AF265D858FEF7F15C542DBF1E231C0433634EC6B8D3BA12D5116897706CEEDBE8143D4A9F186C91E22F6E8A9CE05BC0C867323888364D0E86CB1FC9F3123",
-    "hmac_key": "F59932F5F489B3E304B4D055C368C851D0DAE57F82D338989BC2674B5966441A",
-    "adv_salt": "1125",
-    "plaintext": "BE0E27833763C0E7E4667BA54F0E3B2448",
-    "ciphertext": "9A4B53BF17C69A1B39E72C9AA89074894E",
-    "metadata_key_hmac": "A8F3AA49B8BA760F25991D609679553307DA29922A1AEA0B2CF57D1D7B28A6B9"
-  },
-  {
-    "key_seed": "F0ECE6EC0A3DC55DF7BAB18C41CA0C9F5A0408FFFCAEF32D3081C9DB335C7F2B",
-    "ldt_key": "CA3E68D26BE851EBA1782C75D10EAA039DC705B0A44091B93F1729E149D5D1783044D9EC2528C13F08AB5AF780160FB59978B98605C1B45F7482BD6CF4FE89B5",
-    "hmac_key": "152FD69D75C30961E52263BFCC0CD5196F186543289A96D38E2D22662070EBAF",
-    "adv_salt": "FD52",
-    "plaintext": "101076F25A10920A37E5AA9853701CD94EDE8C46377923A8E7AEECF73D",
-    "ciphertext": "E6B61ACE7739628E83EA5DEBACA6E815455A68F9ABF5F02BCC41AD7CE7",
-    "metadata_key_hmac": "9BF6AC832411926D45BDBBCCC16DB131270D751FD3D77ED9928733E460740478"
-  },
-  {
-    "key_seed": "6845EB5792CB894932F50C2CC19DDB0029A36F362318E85273BCD87DE2CEE71D",
-    "ldt_key": "E467836405D7A1899ACDC5BDAD71CDE12B2ACFD9A17CA9B643B3BC5D9DA84D4DEA9BDF84A5C511E0F0CF43FE51636A6A4D466C299254AF609E4B5C37DD7F65FA",
-    "hmac_key": "F47BF42FD2E4BFDB11832B25D360CE73F5DBAEE9EF9BAC9649B71B39A904D20A",
-    "adv_salt": "2B57",
-    "plaintext": "A411BA034E8A4BCCC3BB3EE9ECA169914AD28DFD2A621BC1D6AA7148",
-    "ciphertext": "0AE76BE8E290203BDAB2562944FCEC168ABEDA17360E8F6EA47E5031",
-    "metadata_key_hmac": "9F60DE167B3EF6EA0E0FB92FAEE7641D443BDB60AD83D823A485213183ECC301"
-  },
-  {
-    "key_seed": "7DD79D618E3D46D6F2F568D4937F7293F0A7C818133A2A2624A30678EF97F9A7",
-    "ldt_key": "3ABF1885AA6693AE77C62F9C6DCE003F923B371A17EA6994F35B68AE9E29B5652E829B340BC3CF455AF516E84FA29041CAD7F8574D842BEEA84B683F3D6AC682",
-    "hmac_key": "7B672E9CF695CF0E3F4BE0C75D58FF7AFBC07E09D82B02AC609DD009520D13CF",
-    "adv_salt": "D088",
-    "plaintext": "209E53F8D180BA340A62BD7B6C13390A89E4851B520847",
-    "ciphertext": "86595462A4F6018EF97376786754982C920474A985A733",
-    "metadata_key_hmac": "AC1FA00AAE3DB0F94FE860812724A7DC602B29E89179D43253EAB0B1B31CDDFD"
-  },
-  {
-    "key_seed": "886983BED2634BFDE5825B60E979F869091EFA35F4E4E48E1148F4CEA59E7EC6",
-    "ldt_key": "8D01837EBD1EB28BA3588908337F9B7DCDFBEC0ADAE46629B43B5B909C4A54FB63C67CF6E8641482A8C40440510BF6DEEE6A646E213A6858F61BC8BAB15D92E8",
-    "hmac_key": "410E201D89001A2308BA7395AF93031E09D275ECD2723BE229F0E241AAC79FD3",
-    "adv_salt": "752A",
-    "plaintext": "795B8C99EE3B8DA04522B32CCDF964C6",
-    "ciphertext": "BC830AA0C3C07AFB73EC43C341B472F6",
-    "metadata_key_hmac": "ECDA1D879E65F4C576C1B07C8AE57659F96863D1764A2D0F3D966757BDBEA59B"
-  },
-  {
-    "key_seed": "3B6AB10F9814492725455760BB62BB7AC2DA22DE202649B8931885859B2DD567",
-    "ldt_key": "C8960538AE52B2AD074BA5EC0249FD22F6E7AAA000348C59B522E3ECD6A35C9A324A59D1DC734CA0228A0D43AC9F8E809F65041F2F3BA7A8227335B0C5483CD3",
-    "hmac_key": "ED3470CBE882DD7A2420F9EDDE8190C182BDD6F41A05A93BC7F15F70324DB9B4",
-    "adv_salt": "3BB1",
-    "plaintext": "9D5F7599D4B4A44B7B8B75E64B3371882AFFAE8F41B87238E1",
-    "ciphertext": "1BB10248F26B45704AC0B1A02D0F700C6D4D10713442A15DAD",
-    "metadata_key_hmac": "FBF4F33EA32E3F888FBB55591CE60BB9844C92DBFC9A8BD54ABE6AABA881CCD0"
-  },
-  {
-    "key_seed": "1FCCFA2BFF7C4D711678145842EE6575F5AB575095C759F4D02AFE2EA8BF613A",
-    "ldt_key": "F5EAD66A08A058B727CFBA95FF6BEC745AFD0507C7E4567547CD8C44786222BAF3E77CF5C1CFBCA0DF6035113247F7B55F248D78B24BACE71AB3FF154760F1C1",
-    "hmac_key": "948A90DA3B1BCF09C1F363E92FBED428C25514E02B7D90DC1D58B116DC930AF8",
-    "adv_salt": "2A58",
-    "plaintext": "62C4B72073C76C3DAE8343EDF66F5C492E4978C67C199149",
-    "ciphertext": "510260D7D04687D5E1E92A964360FC145FA20C5D360EE8E1",
-    "metadata_key_hmac": "E030F6966A67F333E6FBCFF6439EBFECF6BE0D3BB7E9124B3237F1B4176F8BB9"
-  },
-  {
-    "key_seed": "A00B6671AAE6906C1478CF7BF2F6CD018E0C93CC645A0F3A14059E820FCC6AA1",
-    "ldt_key": "5A9C17410FF455E4C62E97605CB9FD196D5CCBE42EB9AF3AFD9B5302DFEC3CDD400EC95F5110CB95300E58CEEB85AB81988BEB9DA6E533AFDCC27C973F8C8592",
-    "hmac_key": "8FC128773B31D7C6B0CEF342DB3B958504E53445CF14651B8780D90ED83B8EC7",
-    "adv_salt": "F08B",
-    "plaintext": "06645B1F8B0099E32FF1F197744E4498577A",
-    "ciphertext": "685DFBBE76C2486733AD7CD5F11F1D1D0431",
-    "metadata_key_hmac": "609702A78C0F810F05CD25E1B42D992F5162CB3348281E554E1CB2754D71C7A9"
-  },
-  {
-    "key_seed": "9A1EBDDEDF77B01E0F9389D406C640B64F86EE57F142A8DBBC336B28CA9ED9CB",
-    "ldt_key": "84A871D9EF570E2D4022DF76A346526FF77A203CA24F159E84DE68253BF42E52F73522ABD09ADC290690D3CC97824494FA5B7C1D57F1C9660529918A08F8663A",
-    "hmac_key": "7451E1A8C1E351514AA3D427030B55F54A117D9D2E06CB53277306D8EE1E81AA",
-    "adv_salt": "3AEC",
-    "plaintext": "3E8EF057611BB2A24FA9A1FB6277BB5E4834",
-    "ciphertext": "6747ACD188E8E81E68AC103B54B1BEB0CB11",
-    "metadata_key_hmac": "21F87E9AABB821F059BE8428083A3E2D5C921D6E3C334EFD996AF80537EE2347"
-  },
-  {
-    "key_seed": "02BBE706B5607F60E480E1B313751D9963D8D62C85B04701248DBEDBF2E95E6F",
-    "ldt_key": "C674CB7FEA75105EE6CB2287E260C7D4F1DAB9637E4ABEEB9DE0201EFED078103408986A25BE5136349D9E9DE49651D73EA07A1834D5E9F9347177051E153D22",
-    "hmac_key": "8AE91FD62D9BDB9D876EA454B0514C1280983A0D4FEBB5EA2D1E8D859D9A872E",
-    "adv_salt": "00B8",
-    "plaintext": "85ABA9ACA14B5EFBCC63FFA1E303F13DB2CF78D4D6124395FAB3F1E28984",
-    "ciphertext": "876DE4842160DBCFC8E2FD5F18CE573060B850FF2FAF0E5CE2D65534B7E8",
-    "metadata_key_hmac": "9560019ED9FC93D3D744EF250DA756AD5A394360E55C95E1AA786D484EBF8FE9"
+    "adv_salt": "63B3",
+    "ciphertext": "E274427BBA4C12E4FF1E03193096450E8F8849A6CBDCE3",
+    "hmac_key": "72E8C574A8402765B5220C5FD95292DD3C7230DCD6E78EB5171FDF68FB6B2410",
+    "identity_token_hmac": "735F48DAAD03872E8E2C9873F29DEABF03BB0E174F62F679CD10F5BD021E816C",
+    "key_seed": "93291E25813E14BC2A72616946D7A5EF03BA8292016307ADA7D5533672D25C42",
+    "ldt_key": "D5FC26F6C1B3363578EB84386B06169CFCD2800582F8E3089DEFF90F04C39C92F2FC98DCBFE9E696AFFD308FB0D7D60B0A5CF2A4CCEE7748F9B4BD3D5288A1AC",
+    "plaintext": "9D799C298ECC149A0FBD23D0B5D7532872748782B8417E"
   },
   {
-    "key_seed": "4EA4AF5C49173C1B8AF0F28E15A1C539398591A2DD0D0179438A4A46FA1E397F",
-    "ldt_key": "F452E293CD698F02560E44CBA21D124AFADB3E75290F915F41142A6EC7E0F729202F11629A2C7F54420B8A5D5E488C88C9303A908F7E398CA9D85C22CC124DBE",
-    "hmac_key": "3BFE522958CA6DA64492B62B079736B32733108A743A3F308B3B1A2EAB6F526C",
     "adv_salt": "189B",
-    "plaintext": "8D054FBE40F231670A5E5001BC68B1852E",
-    "ciphertext": "64398AD9AA2327DD94DD55D3B662261808",
-    "metadata_key_hmac": "E4063B38746759C4C29D9D2DC0033DA320B107C07A778AAE5BB7E4F2AE096394"
+    "ciphertext": "2A178D14AC6AB43A4D4CE5518DFB18812AB5A775A92C53DF",
+    "hmac_key": "A4EFFFED83FB0970C2AC5D129E96559D2CD556DB229CF5B6577847EC214370C3",
+    "identity_token_hmac": "95E77505F78C78695FAAFB6073ECB7A19CDBC343EB3111927613E21A12430A08",
+    "key_seed": "3DF510C1EC101D1F4A41D20932E9503571EF92898C34D6A291D6AFBCA96D5982",
+    "ldt_key": "CC2E135B687B2D841810F96B9DAB9A6A8F32335A5A9A224FBE6ADB6FE742F157E91F2B397D4028F9F0CE20FB760F8870AD295E29A1F9DB4BB85CAC876E220560",
+    "plaintext": "DD2832FC6623AA56C4BD9293B036B5194D676566DE94C3A1"
   },
   {
-    "key_seed": "F42A1BE211F5C2E9E2037B43CD68DBEEAADFE69EAA443F9C8888370176C8C543",
-    "ldt_key": "002B3F92CE5D2020CB4F0E184A4AC41F5E948C61DE5BBA9AF06CC0714F9D9422B0C3E222A2DB03945BDD9451BCAC38E49A2B9C817F1D79F84683992DAF03BF86",
-    "hmac_key": "E7C31AD4D5C82B23BFEB4B6E41123AAD54AA49894C0D7F2D6717538C75EC5894",
-    "adv_salt": "B827",
-    "plaintext": "6D2217B067C2CAA899D22621D6D4FF1040B20A59572E7C90CF5FA1",
-    "ciphertext": "CEFBDF5AE9BF0AFC44758F0D16BF786DF0CB4A8162A2E4D37C7B95",
-    "metadata_key_hmac": "859138DDCBA951BF2A1A55980EA925FCDA9D2C4781BE9F0EFC3AC5762145FE9B"
+    "adv_salt": "4568",
+    "ciphertext": "471CD1F0BBEF830CAF51C91D58FF285107B27FA6E3A7BFF2C747D2",
+    "hmac_key": "EF8EFD105D39D95ED341D23C9CFBF1F946C894D9E56FC1E2D42F0B6BD91B37C3",
+    "identity_token_hmac": "16EA251947E144EE1FB14C3233A90E5165CFF4A35A2BBBC778FD1927C05ECE2B",
+    "key_seed": "FD10C04D5266C0629A5591F158B94F4F27F362F7CAA0FC65FA9C45B15A368B11",
+    "ldt_key": "BD876915705EAF5D40B71C13DD21DA541443165C3590502E8E833831C6DC987C86E6052FA0F46133AA9446807CE761C337711F6572D80F02F3F0F7BD7F399351",
+    "plaintext": "7C5456D3085F9292D5B3A4C74FB3A94477E91DC4ED6E34EA36AABD"
   },
   {
-    "key_seed": "AE01D3D7BAB394780FFB0C8DFD142473558F87796539BA4FA6B02E12A886A790",
-    "ldt_key": "7BC9A4C2BEC95850FE6DFD7BD9CEEEEBF0AE4FE3AC123C499E0B49D1CDFEB8F1FC72A70C8D7E764E2EF451DA3B5EF65D1B9467CFA8B5B4A958E882A8CC6A19AE",
-    "hmac_key": "D25F40424B68CB9D6D6FBD421C90A1B7011281B90BDC624B506A797548DBEA5D",
-    "adv_salt": "C420",
-    "plaintext": "A1E9E22F5D43C023AFC9668C85062E89D811E169",
-    "ciphertext": "DF37010A01172778F61742924B5B512BA0A60B61",
-    "metadata_key_hmac": "F2A089D75A15F0D3739A51A2941C37A661546F63F92B9DC218C754AC4329660B"
+    "adv_salt": "D733",
+    "ciphertext": "D34FCAF70EE1A3B16F39DF081F2B1E18BB6BAE100BF9",
+    "hmac_key": "E2CD0A84E585324F74B14760CED8FA3E749230B8EB52D9482626A2BC79C419B7",
+    "identity_token_hmac": "392EAB5592B90BED1C6D8148324B6C6A54A2970A7E6E8AD84F9A1E35B00C7FBE",
+    "key_seed": "3D70F1284B2007EAAAAC0107E63A5E29DFB1B951BF67F74A9F454C1540397466",
+    "ldt_key": "E2C0B7987BC05CC4FF3AD406421FE70F65682191708A7C8BE2B0CFD35AC7EB6A014CA36540240A83C202E1F8C8F64DFB201A85A38FE0DCDEF5C7004DBB2BC6CC",
+    "plaintext": "DBC428E33858278C6190505B43357EAF0AF3569BCEC8"
   },
   {
-    "key_seed": "70BEDA5EECC8D3DE9279AEBF08821AE91597C809492A5DBCC207D05CA24EDCD4",
-    "ldt_key": "87E82E7F869CD2D4BF48A8E6002FA871A6400C14F4603C94B7A6908669DAB1A4491DFB74DBB84D807907A22CF88A334B5335B4011D864467CB555B1564903A31",
-    "hmac_key": "1BBB2D77F26FE5F04DE6DE0CD30840FD5D5027B7851F9CCA2C7D63A0F360016C",
-    "adv_salt": "770E",
-    "plaintext": "47D9F1849D6694DF22FCDF35B98EA7A539345EA911",
-    "ciphertext": "346E66B1735597E9F8D23195D009972A120D63BFA0",
-    "metadata_key_hmac": "269F952F828590B58EC692CFB33476C07400989E1C1BE1534FC8FB905C47ACD4"
+    "adv_salt": "A601",
+    "ciphertext": "BDA9A063CFFA5DC94A6F079309148F736EB30730EC334954ADFB96",
+    "hmac_key": "073737D4AD528AF8DF09A400A22F9354732340D628A910A7ED2DFE439780D37C",
+    "identity_token_hmac": "752A241BDBD62ADA62B55DB137A812A81530F6945B347A72E10C9DDD3961076B",
+    "key_seed": "23D63C05009E3370B196DA6FE4A96E3E38594F3BA0383FE19B727C682839ECF8",
+    "ldt_key": "CA7CF088A8165728DEB146ACD30061FB7B5C5F17ED53445FFD3562F4D520AE4FF933F76774DFC0D3CFD4E4385FE57092B92D0AC4CBAAAF3C16483D12DCB12BF2",
+    "plaintext": "95A5877E5F58B6D995B2084E4F38EA8ABAE4BC83AAADF04DCFDB57"
   },
   {
-    "key_seed": "D507C742BA65C365B140AB279E7D5BAD07828ECA90E1045D546D4C4CE09ED6D4",
-    "ldt_key": "3C68C30F21525DB68A56DB84A99A2B0050B64F680C7B642EA0D85DFCA3BF6B18D84BFE8661033FC77A3707F733CCC7FCE6D782BD139A2B722030F48BA0BE3D61",
-    "hmac_key": "5FB552390E176EB1197E73C1474E889A2F85239B953FA0954DA8CECDDA64C020",
-    "adv_salt": "5267",
-    "plaintext": "9FCA0749B49D04C8E5B32A6DA1A9B52B9B1954FDB59CAD",
-    "ciphertext": "AE17138F7C26808A840A009F38B2BE17BEEF6A5DB9F655",
-    "metadata_key_hmac": "AA31DCB8A2C507B89209B1B37B19C136811C5467C61DC4D0D04EE503A4D390AD"
+    "adv_salt": "AAFD",
+    "ciphertext": "8803E1FD58511E33AED43339A9E0077C7012",
+    "hmac_key": "3AD326F0FD9F9E3C6216293F046E8E3367A218A0875555B50792368E9A7591D2",
+    "identity_token_hmac": "EFCE15BDB07640F9F2B81A42DAAA8F5C5580FC94295E154B8A41713CB89F27BA",
+    "key_seed": "DFC2389FDC8647D1C23DD7121A2DFB7A8BCE9CC9F69F50629D5338528BCD79FC",
+    "ldt_key": "4A7D987807CAD0F173ABD53F0A90D9DE1F365EE14CAEE6611CFF3FF4430C07EA7607BB3B7A56AA3FD4466541ADCE1BA06B7B55D4570E7AA0E329B20A307A5F3D",
+    "plaintext": "3905F15E8C081BCC6C9E5467578785CF8DC9"
   },
   {
-    "key_seed": "082C07502435C163C687BDF0D9C6F8C058125E6A957966A860C4C8FDE3BB76DB",
-    "ldt_key": "0337020D34C0EAF110148BB6CF71ACF77DEEF96C9097AA0F90606C849113FC8A3E02E840FF6DC12DC4C9E8C27E2CCA6D6A5429403C1CF04198670279F1A27518",
-    "hmac_key": "9FC12C38D43FA05ACBAB2A411E332B76274B0180BB670098FFB07327E96BB0FD",
-    "adv_salt": "91E9",
-    "plaintext": "2C40CBC0327A6AC756BDC545FC7656811607F325A6322D6CB0FC",
-    "ciphertext": "4905857773E7C38F6094B77491AF04E07135A3DBBFD61ED63D22",
-    "metadata_key_hmac": "745F00AA5C29AE6315E07C0AE2F7FB10C07C6D9F24E81E3F22921EE37159BDD1"
+    "adv_salt": "6063",
+    "ciphertext": "41A468606A9285661FA1A0E0641A1235",
+    "hmac_key": "AD9E86FD3ECFF585047BD57B84EB6B42B2FB6E6F6851F2B23C4D6F45C5F93989",
+    "identity_token_hmac": "BFC8B274D5CF97006B0C393C250CF9BD22FA71C26D8ED1301BB4E7F8BB4AD034",
+    "key_seed": "6C45D420A250B3F9EF075B78CF04C274C0C45CC0C8ABEE8D54740A4471628048",
+    "ldt_key": "13F9A8A679153671E1EEE4AD451DB5A32B2FA72FB57855FF99CAA454CA68910D27C9B7F7C99E1D1D16C4D3D07E7756BB822948DE982B322E6D3F536BDCB9C364",
+    "plaintext": "E8B1174511617F08A79EBBB2D4E324F0"
   },
   {
-    "key_seed": "9AC7DC14A8E9F6E17E98CAFE93607A4A018752248988A774F3D41DE4CB7C254F",
-    "ldt_key": "29DDAB2F42CA51EB35DF34053AFA8AD4304A5264C6E662D1882D91835AB747AE8BFA778125F3BC64C227FF8AFD8670AA64E546C12C6B094BB7080E0E6C3DD029",
-    "hmac_key": "055323B77EAE387521938AA096C573535B24FE81EB659C9607509674228E5398",
-    "adv_salt": "BB35",
-    "plaintext": "47887D96343D6575BD51502D25E4D6F549B9",
-    "ciphertext": "2051416E4B88A1406854EB29067BBF4692F7",
-    "metadata_key_hmac": "6FFE9D0276F20DF381DF22F7FD691C94DD22B5CE0D83C2C7C10E92E55AF666A0"
+    "adv_salt": "B09E",
+    "ciphertext": "66BAE514FE9FD3D4FD03943B731DFAD4186410673A19",
+    "hmac_key": "2B631AD159E8B1D7C4712A1C1D2E453BCDF2A9944181251928B2F88C069EED97",
+    "identity_token_hmac": "6661D3B31A9CA7A32994E35FC3DA8247B0BB26CB70FA76A91E69D271D365FA6B",
+    "key_seed": "A958898664C9EC450FAAF215FED0AE4C08E7A9AE8AEB547200687BBDC4F2DD6F",
+    "ldt_key": "34F34990D04CE8CB5B2CAE7B4CC5C772F7F3074B8348133BE384E5A40C179E91AF3C46231D464071757ED5343B27797D82E15D8D4651CF65ACD2D371F3F9D06E",
+    "plaintext": "3A87E4A4C1F450753A330CA5A2258810222C38337143"
   },
   {
-    "key_seed": "A7F821CEAB3B6D1D7FC036EC3EE7D3F4417A5C404E16E36D98101AD14481E509",
-    "ldt_key": "911E5548B9158AC32DBD213D4F13F38478D1D53B18E97E349675C82ACC9BAAC7332064BA5C46DAEDEC560D4CC61C23A82EE1D6EBF2E5034244A1CCDF32608B1E",
-    "hmac_key": "ACFC4DF4EABE77E22C04E1D350CC8E68436A422B7868C0370CC3B2B308B8273E",
-    "adv_salt": "E921",
-    "plaintext": "F8AA31BC1DA3C4ED9A8D67D1231695174916CDA64C2EF1",
-    "ciphertext": "ED4715C57B04B724A3931C0917325EBA202DEE87087658",
-    "metadata_key_hmac": "1276F63BA4A898834F61208E74169B5CFD856DDF96483012D63B8DF05693E8ED"
+    "adv_salt": "E6BE",
+    "ciphertext": "94DACA62F2E46079502E34FE601A765FD9",
+    "hmac_key": "0028DA5E27469A75C6CC83B6ECAA730F4D0C967107B3B1FF7934D8AEAFF59229",
+    "identity_token_hmac": "D13D8500C5A0B2E2B102B66D0F9DA33A69515F8FBDBD7972B456E101D921C5AF",
+    "key_seed": "D201C792BDDA73C0ED08AFB789EE1BCDCA1EB65784326A053AA860913FB01109",
+    "ldt_key": "7679881D455CD5811BFF61F1641D7A98E9C1EF6D29C27FAF6FAA39A0B5F5C473A5A2928EE721EB74491E0265BECDD88B67EF14E180A7F2387067113D32C0B607",
+    "plaintext": "3A10B1E09860673681B97E613F83A31EDA"
   },
   {
-    "key_seed": "05146E31D1D116189449455E6501366FB98EDA901511C8313F87B87851F50CAE",
-    "ldt_key": "A336203EAE0CF9F55BE6C666C794B600E167F53F38F8386BFA167F26ACD924CC8D5C86CE9F5C87EFBE3EDD8951116AF5197809D0AF26FBB1BA6AE73CC3E87E70",
-    "hmac_key": "666994FCE84DB1119ED4B98A3C7B8E90F3254B55762A384488B1102F090A2447",
-    "adv_salt": "C346",
-    "plaintext": "6A6FF037B5E075C69901ADF42CE1B49277DB",
-    "ciphertext": "34893FB6D8DF991304558F1CB8D595B125E6",
-    "metadata_key_hmac": "1FFDBD41802C5F9A5976342F548FC8B49015DFAAC72858B227B55BDC3CFEFFE5"
+    "adv_salt": "137A",
+    "ciphertext": "6661D85579EA7F0BB4236F4970226B920BFE9972",
+    "hmac_key": "18F7FD0D50302C2270E2B064E774C3F29C7F8D70D97B508E4B8779615C38A408",
+    "identity_token_hmac": "9DE247481C8E8522A87AAAD086780BA8F41C3A77FC9D8DA0BB640BA33BCB84C9",
+    "key_seed": "5DDA71768B7558FA3C98B3A29B47FC356B6B6DA5754D75689AF29617CE80AD1D",
+    "ldt_key": "1401881423FB090F773116675B1D5D3DB5380A30CA6342210E4BF415EEC78CBE3503049F2B6F6C2224324246044358467BC653FC10816153B30637879910EBF5",
+    "plaintext": "0DFABA7AE42D7114DF5FB4D42050BA1CA477E806"
   },
   {
-    "key_seed": "CB8368E78CF6BFF4E3F38E01C35D5EF9DEB72CAABD28173DC4DE59C8CB4D2ADD",
-    "ldt_key": "A006ACEC50B59BA7B05C36121C3029E0D0C15AD9A644B189CF2B193B707856347D692798023A4E30B7AFAA3B94F83F1DE1C806CD7FFE9F94A95BEC6620136D3D",
-    "hmac_key": "AF80B241E2AE4058794F8116C61E874AF7EB72E44521C1B88BF5D01B8EF89DAE",
-    "adv_salt": "2DFE",
-    "plaintext": "3ED1C991A0908D7D6A9F600038ED4020EFCCC3D9BF",
-    "ciphertext": "C17E5EC275B2C3815E76C6993D2ABD932E3F931CEF",
-    "metadata_key_hmac": "27347C08628562703834074132602FABBC18CF047E80B97F45D50B85348BE112"
+    "adv_salt": "7A27",
+    "ciphertext": "FE25721548C2E4C274F522F8F074743F9C58D90272DD81B5C08BBDDE7392",
+    "hmac_key": "A697350A4735005470C8D00E436DCE30D93D0EF7E1320EDE756AC076EF155BF4",
+    "identity_token_hmac": "CB23C030952822E5164C08ABC71C02E79C84EB317A952F42D8CC806C3677257D",
+    "key_seed": "B53B8AC84CA27A918A2C79425215E934D0E1AFB20FF246B5A7B5F2212CB75D03",
+    "ldt_key": "BB917A8EAAF7C391CAF0FF971AB3C8D2506C499CAF697A4B1F4C9761756998A3C9C752B535B957AA535B9849024162B2816E833F71E7ADABC568E384A7D166A3",
+    "plaintext": "77FEC4D2BBCEDBEF542E144D26FEEA7418969B2013A34F48733FEB89DBC1"
   },
   {
-    "key_seed": "47B05B1B7078EF2CD984B694BCE4FE8ECE2763F4BC9E5773DBCAE1E6CF907351",
-    "ldt_key": "5B018A74552C4161682CA23AC77A96209F9770294B125754CA8C31BEA3E2587EBF599ACC841494CBCE2E87EEC76D7BCD678F637988A28AD2523E5BAF838D3D28",
-    "hmac_key": "2AC31415141949B576D62AC0F09966823D4513E4EC90AD3BC1ED496B5B256158",
-    "adv_salt": "286B",
-    "plaintext": "2D12BD3EC6C4EF5F72116F3EF10602D1CD03463E3F8B",
-    "ciphertext": "E18B3A18B6A506A10742ECAED5C74B5FA9A9008D0CA4",
-    "metadata_key_hmac": "3A8207005DB61BEB73F1614EB70BC5D1BDB3C231FBEBEFDBA8D736C01BE563D9"
+    "adv_salt": "631F",
+    "ciphertext": "28399DB082C882B5B71AE5F57220B2FB10BEB0E317F0BEB0F4FF7B",
+    "hmac_key": "FE628D81C280912E866EF534EB73D3D26A2791C4E0348A3B735D4F6A58A276A8",
+    "identity_token_hmac": "87DA1BAE46083903B09A26D07D33D4E8A05FA6F766DFC16F5EBD1952D10CBAFB",
+    "key_seed": "9478B0EF762A5F474D2898CCDC23B2D35ECAC488AF848F204F18D65082EEBB49",
+    "ldt_key": "5E52F197C3EE72434BC12849DE8F4460E690F1BECDD676D7F5066EFCB5F2FED6D11CE8DFBCB0D5A44F5A10388756D0CF468CEDFF2E57EF37ECFAAF790C0A70B4",
+    "plaintext": "CDF16A05E3D0F01419E9A9E197CCEB77A94B8F0875A18EA07CC471"
   },
   {
-    "key_seed": "A731704EDABFFD225672895853D4CA8FF477C9D65A37552649DE0AFD5CAF9BFE",
-    "ldt_key": "471AEE153C461CEEF36FF4F7ED324EBD360C4330EF9041DD15D87F36FD49BC142366F26C20A388E8EF31B19E3E503721B3039EB39EFDDE3EE0B1AAC921480AB0",
-    "hmac_key": "BECD2B6C5469448FF69DEEEF6C603F1DE94DC9597A5F064BBE1F4C23366A6740",
-    "adv_salt": "AE19",
-    "plaintext": "577BCC8E5D86ECA8F738DD611E297E41ADF149F88CF396",
-    "ciphertext": "4204C7C25C7F5F823B92B687FF977E1561EBD28227FE0C",
-    "metadata_key_hmac": "00D88C16275FD4D4DFB56BD64A3344FBD50662A5AD408702944FF77B4D76EDF8"
+    "adv_salt": "3C06",
+    "ciphertext": "F3CB6C6E54F5404F710E395DFB745EBAD74532E3DB38CA69A77DE120EA2BDE",
+    "hmac_key": "97DDC5B9315B1DCE1F1B60E4243088953F35C485D20666ECD42E1ECF60346B69",
+    "identity_token_hmac": "3DCA4C5B3B8FDB8FAD3FE7F32C307D64555BE8ACD2447ED2FE95CC21990E3E15",
+    "key_seed": "D5C2342B100FB4C81AE3820F3C5B5503B089DC3FB065B4321D6CFC3D1EB82D1D",
+    "ldt_key": "983EFB98F5FA2DB96CA7D2A173B2CC03C926E09BE575F3514601E997FAFE4742B4294FD020914170E190EB3399470B90FC790ED7168A41DEE95738EFA119937F",
+    "plaintext": "4E1C99C2D02E2F9D6453224B8142FCD71515FE2838B6E7DDFAA7035D65B433"
   },
   {
-    "key_seed": "755BC50C45FA7D7F512DB0251C28C61093DDA9EE5AC3444E5265AB773271D211",
-    "ldt_key": "FEFDA5252816E78E13CD349E56C6D5C392606AA02E9BDFB50C6CF72FC4C728D894D4C6BB5E4709AAB0726B39AF1F3DF0C6D8896F16078E4ECB0B695F8E23BD33",
-    "hmac_key": "7A732507A869D82418C6657721DCCBFFC49EF3AFB44441C93F097EE1DB47FA37",
-    "adv_salt": "21BD",
-    "plaintext": "851D152EFB5AA2A792221DD11836E2BB1553ECFA1AAB8B83C48770BD91",
-    "ciphertext": "671C99E29BBD8AAD0DFAA6504229D28639C1D5224831FC056F72648D65",
-    "metadata_key_hmac": "4FC52636E718CF7D85B429CB734EDEEC7559C5B7A136578ED05E4ACC9B6A4CC6"
+    "adv_salt": "0A54",
+    "ciphertext": "216826E89337FF9C8C86A88A7F9610700886A36AD7CF767DA31A15",
+    "hmac_key": "C2B74FB5BAD82E56CCE52FF1E11F513F32528CCB480927EE74E5BD7CAB3FAC62",
+    "identity_token_hmac": "AA72967F9912971CFABB6C9B17C9A7F6C87A37EE2C239EF17874D00B1B5DE61A",
+    "key_seed": "0DB6C6843FF10D5F38F7809CB20D4D9E46497EB1F1D2F0C675021EA734EEDE03",
+    "ldt_key": "62513FCD2578DAB8D8C234E893EA8A1B6079D8034607F2B0C1DA3C062D5A1D8E40FCE047E785E3EA31287CDB59B27E4F86A35C9231EF03611BE8CEC4EB3EA511",
+    "plaintext": "0A17149A84904755A19D9648902C10CC407A7B75E20AD94B9524BD"
   },
   {
-    "key_seed": "2C9FEB9FDFABA657077CC7877C65FE30A257A58704E73F88A6CAFF9153790BE5",
-    "ldt_key": "F85C764E4D4464D0DB36D0F0F463048EFE2089111630D8E9B7758657189730B9630B9DFA4374166AE2F79BFFC5892AFD560BA2196E219B5A2368A21DAB1C123E",
-    "hmac_key": "F5289C28A60DCB53CC4278D34E4547514C61F504FE0F64F63F46340E3A759034",
-    "adv_salt": "3FEB",
-    "plaintext": "AAE2E09A0CB648FD9486FD845608F02766A9AD46F1CBD1287DC7BD2B",
-    "ciphertext": "C060C326622F2E899B721E08B4B2E7713FD738911443CE134C882F91",
-    "metadata_key_hmac": "DD496B4252BA8EE1A78DD00A3CB620CC29DF6D479B7F0B1E085EE1EC48AA4BBA"
+    "adv_salt": "8BE3",
+    "ciphertext": "388AA1AF4F579F96D9EE856BB4F6B3E17A242BB8A3E65688B1DB069C65",
+    "hmac_key": "4FCB9A35A2D7B4DAF8306AA49EBA5381B4B4B06BB304CAD1D2A038A7458C6A2D",
+    "identity_token_hmac": "3C6FD647D8723484F82C9A5030B020208FBD0719BC7D3D442B0D3144F0F91471",
+    "key_seed": "0DB07D210A15768D1570BE426E0404A907C6DE2346541763011C190939FFB3AD",
+    "ldt_key": "A402A13DBDF763065057DA78E634F46E7AA40B5957E4BC1C50A22CEDA9962C6A7EB4EB1566DD4A0394178F2B295E77AA044434AE2484512AB3D1EB57CD4554BF",
+    "plaintext": "D806CDF0B1B96817469D15A96448262DEC4A6BC167CBDA5AF46DA97DDF"
   },
   {
-    "key_seed": "DC0777F5390257D9662BEA2774088154796EEE18D2C6B6F294DA87A692EE590A",
-    "ldt_key": "544BBFF963DA99EF4C467967375B940BA005DAF5C67407ADE630A000B7D49D315504457B5373A7987C6CEA690A96D5F7BD6E313FFE159300ACC428F3AB9662DA",
-    "hmac_key": "3D37F8B0A5CE29C00D5295B2BB40670E9F08D51C020C722CEEA7CA0158512DF7",
-    "adv_salt": "6643",
-    "plaintext": "721FD2BCF08DD943D40E9ECA4B25FC810E86919F31",
-    "ciphertext": "611522B0F0950EF619157C921ADC2D134F18B93C10",
-    "metadata_key_hmac": "6A18A836A710EA43DC92FD5321ED90893EC229FD1E035AB776E7E93252C5EA7C"
+    "adv_salt": "7F11",
+    "ciphertext": "ADA4507A0043839EAEFB25E918F35BC2EF9C802AB9",
+    "hmac_key": "E45BE5793166A4AECAF49E88557A396772FBBDA6991CB7DF747C66D35C630071",
+    "identity_token_hmac": "664EE5E73ACE23F1C2ED4109B1A790B5D79CA3D1199F72D17325B4773CBCFAB2",
+    "key_seed": "189EB21B47BD26468B2688EF969AE3CAB335F851751754499624BF2ED101DD0C",
+    "ldt_key": "62BE54619E90A085C9033E75D0A0E4F80803CF8B21FF4E0BDDA00C91F9B65D1E8F689299EFBF3EE03758B182D0D4C81B3A37B181AB3B342686F3202552E25D36",
+    "plaintext": "1ED2E4DCB9EBCE51D81DE2440A88F8C4218EBA7880"
   },
   {
-    "key_seed": "6FF7DF107A419A8FB65097B6B2CCFDB1B92757E72A1538B9C582D5C05C839E91",
-    "ldt_key": "AB2A2E9622BF63CFFA5592E1EC7CA491ADD64E594C7441D45CCA381B879CDD73F79DAC43824EBCE0831F4B4A2D9D8A0699FB11FFF3D650166B8353D0D86EC30E",
-    "hmac_key": "C2730C47227BBB11ACE9FD69F0F716754CB0ECE8E7AAACEDD1C3EDF0A83E0A24",
-    "adv_salt": "E652",
-    "plaintext": "7878A0505FD09850B82EC2404E2847984026EC962F29",
-    "ciphertext": "68C40846FC1269959427C29BAF7275FAB70FAA0EADED",
-    "metadata_key_hmac": "AC7EA05D26A200231873CC00D02CAB9AB7FE74F736FB3136BC4E8770164BD89F"
+    "adv_salt": "E78B",
+    "ciphertext": "2750A1DD26974816A86F25D21A77BCCE7D0F162BB02A9E",
+    "hmac_key": "A6ECD71B681206038FEFE976CA6B480B3EF9C96F97614BEA5F97EB02F6F9FE0C",
+    "identity_token_hmac": "E233B0BB2E2042D6F097325B9D56EA1BD328861D76F670107FA18B46E509BCAD",
+    "key_seed": "060B02CC1ECFA8236FCC5573CB4900AD652913947000F1062571AECB42DCEE3A",
+    "ldt_key": "D2849561C2C8FA1A50CAFDD0CA727EE277732E942148B7BBA670F92C8AA53C42B71E332E41182EF28CA75285AF0692992E49A5BF049BF775181A15E2A458169D",
+    "plaintext": "C4D8C9B754A01732C3CC63D10E27CE3461F5551E7E43AE"
   },
   {
-    "key_seed": "6E12D74E582342F3308E467E5D702F3E5DF4240AAC170FE6DE888F3E0FE11EBC",
-    "ldt_key": "2FD4BB618D3BDD54E8D6E6E2B15FB085C8C38A8ED13DB81C8268BC76D76D5B98406213F8BF6435F9EDCE9AF02CC9E62C8A1536A3557AB946899C334558D8C86B",
-    "hmac_key": "72F271C80646ACDFB7D085B044140EC046910B5B707F0E29C0A3F9A5969A37D6",
-    "adv_salt": "59CA",
-    "plaintext": "7AD1A3957A05FD0FAFC434FED374579451F308F7FA33AEA7",
-    "ciphertext": "A50CC7B88D02C6787B2ECD0EDE9EDA564AA9CA9A2319819C",
-    "metadata_key_hmac": "8AF34F258AB3404A36828FF1CFB017D2044737F4A8D9DED39FCE9DFC83103ADD"
+    "adv_salt": "EA65",
+    "ciphertext": "57856CB60C92E5705AFA37A9831C7E2C0A",
+    "hmac_key": "38E27F67F0D0906DBDED17915739EDB44269B0CE9B20A0F9581F1CA16611D766",
+    "identity_token_hmac": "5F03FC11FD378E7BA7F0F076EBDD920A9227A7A011CD4B41DD6DBA50442D1A9E",
+    "key_seed": "BEEC1F791F0F46D638D543B13377B0BD8440EEEB8FB0B0609EC973EF694E5AF9",
+    "ldt_key": "9696153B6F0C612EC4C651751B4A236ACD4301DE680A021BA123286344D26543ABAC4905F32272333D1EB6C34E336180CF1EB4BF3BFD49DF1E856BC1B6835FC5",
+    "plaintext": "52F5AD3522DA1CB6CFCAB587B86E9A3E30"
   },
   {
-    "key_seed": "C8A294318414CAFF7BE91B7E8E671D1518B6C3F33ED3AFCCD0F48BE1E2C45D65",
-    "ldt_key": "0C03E2C14C0E304D6BB4FF746FD6F9C1094A6F46F141BF85FD78963C536306DA08C3F8C69BBE4A6EB9039F6DD7F34E11D0727DCB5E4D66C59678C368BB9512D1",
-    "hmac_key": "3495F1515C76C2B46FFD402C31D06C3BB13A4364268394830AC39F2003925A10",
-    "adv_salt": "7503",
-    "plaintext": "FA03E156319548EA54E252604D607B9EA4955D7497D0",
-    "ciphertext": "7D94D5C7B8BBB8C9C180E6C61AAE454FFD8556E2B006",
-    "metadata_key_hmac": "137DC239F65339791D617F036502BED994D27C084EF0A180FECDD9F8ECFB04FA"
+    "adv_salt": "AD59",
+    "ciphertext": "1F00BB7B0B050E10774747616B67004D03396ABFA0D8ADD0",
+    "hmac_key": "552017B1A0E047B5882918EB3A438A5C9A4EAD2A38353A9C64F71527DA419BA0",
+    "identity_token_hmac": "DED83CB27AFA8D5CE08D8D1C05C6385C9F6B96972351EBB8C9694894DD310537",
+    "key_seed": "11A1E82D6BD216E080F4DA502D4F62B406A99DE2296F83657C2731AD0F7A9DD7",
+    "ldt_key": "EAF9C2AFBAE16C0D32FBF91FF969D1EE7E009CDEF0084DC884354AA623A32DA581776A4D5E58FF70B17DD532482C1CCD63EB9EE362869E39EB79D7B305F3DF82",
+    "plaintext": "1BE6306F08E53128A20DCD8949CABBA76FB5B70937677545"
   },
   {
-    "key_seed": "B400D12BBA0D31B81B68983E87D7D63F768456601EDA03886E67C5C82B2C167A",
-    "ldt_key": "CCE2C3E3855D1E0B5196B009D8AA7E9A33D9BA36AC8A0F0BC4F9D4417FEA6F4782EE02DB8856276A3D26B17DE25A56DB0F39B34A62789801BD720A64A97E731D",
-    "hmac_key": "BAF408EA5111A8BF7022076F0C6A3FEEB11B88F7E106AD332F32A0395AA543F2",
-    "adv_salt": "2287",
-    "plaintext": "D0DFC032AE3A31B1658EB392B58A721CCC574A0DDE",
-    "ciphertext": "D30E3212FD8BB0092EC05F5401685650A874AFB2EB",
-    "metadata_key_hmac": "692398735A6A56DB92FE8A2DE308AA467B96B37F88E746C3DAB2E2ADD06EEFD1"
+    "adv_salt": "5F05",
+    "ciphertext": "D1144B782E41E9310836AD8FF4ADCDBAF4D50AFB20398880BFE01F40E6435D",
+    "hmac_key": "C3FDE0D1EE5DB2A4C4097C61212C85582F00A30066E8E8B2C189BC12AC313EB4",
+    "identity_token_hmac": "57A56DD881C931DFEF3A0CEFBD018DAE449BEBF6102F49CBFEDFEA4F349C89BD",
+    "key_seed": "6AE0CCD8C88341021FCA2AFAB8CF9A26F8213761C33AC1AA792C37DFE8DE23D1",
+    "ldt_key": "5ACC33C3EE28218743E13C94FF17580DF5373C66B31F058AA1891D0279B29248963CBB61908DD3837DA8A969354AF6FAAB90198CA8E9C25F99AFCC3F4E09F7B9",
+    "plaintext": "EF1667FA173964C2409BBEF9575B14BE42E55EE97E918B8C727296C3549C60"
   },
   {
-    "key_seed": "F9487077F24434B11148BB8DA3B2658108D2BF222ED246D527E40FB79B59FA65",
-    "ldt_key": "282CDF83E5EE41D0949197EBBAAD19B28F02FE6F44DA2FB9FEC35EA40F2348C7E95FBE9380BD8F77695BBD471A97FDE3450372654EA78542A3663380E8298EF8",
-    "hmac_key": "8EEC4840DE8DE0B81EDD07F9EE511117CD9A43CFE257FC01040D6DAF011F719E",
-    "adv_salt": "A067",
-    "plaintext": "7B9C8E0DAC001EB757759681787F4C0A1CB2B7030D",
-    "ciphertext": "50F99FBA92FA54662392A5AE8D2F1358F1190C4EA1",
-    "metadata_key_hmac": "35B5BBE5DA396CA036E210BEB2E03C9E7264EE17CDCB67160C90437055C61E8B"
+    "adv_salt": "E50C",
+    "ciphertext": "04DF1A3FFA9E13D0AE1247212515D2C874A3BB1B8FE95C9F",
+    "hmac_key": "35BFB86404B08157E7F7EA6AE76554F3B0D3E3729E76D239EF1D07C05B1C7751",
+    "identity_token_hmac": "42DF5349261E6B238DB891D50ACC180CD9B29D925644CC4DE2A6C04192A2475E",
+    "key_seed": "A457F41ED883C88BA95D1E5E7DD69BC4D7D809793F9662944F7CE53993BFC62D",
+    "ldt_key": "0C3FC621712AAE7F53A36882BBA9BBE89B593751F357441E1EF2A41ADED4D3C86CB4CD9A495EAA6889C6EEBE7D8301C04A1B079FC6C511DA8A4690638B8F2694",
+    "plaintext": "2C4AFBCAA759A02727DC48C8073F99E3B0F8E19675938653"
   },
   {
-    "key_seed": "94279528D378F37D783AA85DB718DA2941239400BE724FCC51DA86EFD6521446",
-    "ldt_key": "2CE2FA8D246C4B62057946F530FE68849CC4FBE0CCE24735AB40A48745B6C70525B09EED434E4D474DB9017C9CA2D96212D1E7D2944B2EA268AFBD3C1DBFF81C",
-    "hmac_key": "AB0C8B4EE4FC540883CD901E962208C3A679A4CDA121E3C3E47AC16A62C26552",
-    "adv_salt": "FA74",
-    "plaintext": "9D179C9EB9231E83D4902F87AFEAA97FFC82A8557D",
-    "ciphertext": "50AED9A942CE93D8A1EAFF67F166E0587D64F030CD",
-    "metadata_key_hmac": "777C2293B15142B27C7BB06C22B04CA63E98C4B7DB346D47A09D3EFDE8D57514"
+    "adv_salt": "369F",
+    "ciphertext": "D57B58FDE1F1A9FAA283F0F667EE82EC38C7FA83D2B5D58758570B85C03C25",
+    "hmac_key": "C07B61EB61B1E39F831A27481E05A45F646910DD10BFA3B9D4A9F2246C87C7CA",
+    "identity_token_hmac": "DC969C3953FBAE39F574ACE5D32636648454411B013A4D3F15F6CC03D6503116",
+    "key_seed": "0B70FAFE46D165C61580477BF0F8E95942E0D1313EF6DD38531256D331BA8EFC",
+    "ldt_key": "FAE613854ED28C718764B2DECA45FFB038D12ED0B8F4CF8E410CF664135CB9AB3F928CEC5ED9AF6EF05F4BC8A5B802112898DF299C8EE6D7B53364207FDCF802",
+    "plaintext": "BCAC01D17493B2EA537BD4D0F9E6A81A3C5FF21E2A9CEEA2B5A39066396600"
   },
   {
-    "key_seed": "C0A10BC69806D2FF28B5159E2F3F969A2B36F82E3D55DD99228517346A2C1984",
-    "ldt_key": "CA9710D1304CBD14C7D22F410443386DDF2583A20797D8BDC5A5713A47F6061C674F3E13CAB597A682DA56469EDDECE75DCFF1526F63235620F93A876F5977F4",
-    "hmac_key": "892C75A44BE2A7C54B36691D5ADBD115F0E1A6B020873896040A8B37E59C7EF0",
-    "adv_salt": "E15A",
-    "plaintext": "7B896CC8905C681DC6B1D687739184EAAD5C66C32BF17DE37E968DF5430B",
-    "ciphertext": "B10E441FD7D946E45E10E96F418B38D5EAD75409720F61E74B33BD7FBEB1",
-    "metadata_key_hmac": "9D1A04D804D3DFFE0F18C5AFE025097B72F24175EECF1FF4D32A9A0332A33CE1"
+    "adv_salt": "4270",
+    "ciphertext": "77E4E229422367E0E3C29826F7E586DE3813C9F22D41",
+    "hmac_key": "CBC601994BBFC3A47C23F03A6F55B265C76BF5B3D4DED01C7AB3417BEB970321",
+    "identity_token_hmac": "E1B64D12ED7836E3303F99A545F33DBDA58E3357AC39C5AE0B5CB728D5585510",
+    "key_seed": "4C0F2CB9CEDB8BB5C6B91C0DD7F769D2C6474EF2788F45DA8EE73CE1536E5BAF",
+    "ldt_key": "14D27C8012A68D40CBE9C725B1B20573F8BE0CEF409826236150505884B27D0DF25356EB0CA8E9E6A6247F563B4950BF76F59A491A56F76784C041C49D338C95",
+    "plaintext": "88189E5EEF3ED4711CAE61B36CD3C544997F28FA46AA"
   },
   {
-    "key_seed": "DC3CA750BA956949AC3389B896359142B0FC41CA5259676A4D974B627F355674",
-    "ldt_key": "884D16B4BFBDDD846857C57B5CD1912207788A1B3F95D5AA7218CB4AA3E21AB4D0C9518E3DBF09CD2C1A31DAE2DBF04E96840E81D8FD9166061B1F761834E270",
-    "hmac_key": "6437801B615156A92363D1D70CC37D8DBD1D5122F09AA4775C99402B29D97794",
-    "adv_salt": "2A38",
-    "plaintext": "565374DC1B36E89EDD90AE5CD77F9109819053615F590990D203",
-    "ciphertext": "EFA8C9D21EF4E65EB7D398EAF277EDE24F9D3D4D4CCC3DEADD4C",
-    "metadata_key_hmac": "CEC61A875AEDA4C3E1F4BEA7F5D5F48842C9677BFDFD3777ECB6998F412623E1"
+    "adv_salt": "0784",
+    "ciphertext": "8D5E6C77684770924C1A4A491ED92E63EA149226865EF70260",
+    "hmac_key": "209530DF75729473CD56AE03E754CA630B979A9232BADFE403B87D4271BBB80E",
+    "identity_token_hmac": "9BBE2CDD6C7318E218D6E9C7BCC7DB547EFCC275440A2BB7CCE82BACD6D777EE",
+    "key_seed": "C5E3CF6049EBD3BDFA10743AEE32537C75BC8D973C808FF955AD0EE4066E36C7",
+    "ldt_key": "AC229422EFF9F36205FB996C070D3115319F2A94A4C01A0DEB1094D3C56AF7092EA77EA7F8FDF879B22A42A9862610724AA8CF1FFFDDD7809513559534EB70D5",
+    "plaintext": "DD3E6E54270CD303B0DF06A5777D3AA7A82A855112A2925001"
   },
   {
-    "key_seed": "1C3B8C1D9EADA715EA9E9D0309204228015824BAA74D90880AB61C110D17B275",
-    "ldt_key": "6744922661B4330F6E0FFDE562D5CF4FB2D9A838E6FE17226FB9AD6DCDBD78BDACC587389421144AD105DAD1C5D5B2C0637A380C81A6AECCD5521D29A0E103F9",
-    "hmac_key": "7D4C0C0252D8010FDD7F1D4B77DA012C9F3A8CAB84D2736618F7F0991316BBA4",
-    "adv_salt": "8E40",
-    "plaintext": "9989667868EEF966DE52DAA83A79F442257CBF18D27769F2FAF5BDF6B785",
-    "ciphertext": "21A369488B4FAE255E9EC83844DAB5FCF3657BA593715570C010E1B3977B",
-    "metadata_key_hmac": "53E639D5AB5948942DD6D9946F95A1A6EBB9B93A186E50E918E0609B8E3DA45F"
+    "adv_salt": "FE5C",
+    "ciphertext": "A1C75E7100B6E524141E97A24DBB7071",
+    "hmac_key": "FD0776C73BC6A5B79D3E3FBFD04C7C9E3E137D82E9F5F426B2CDE832BC7DD6FD",
+    "identity_token_hmac": "A1C8176B483F1D6DBECBA76AF08D7E62972F98B78D0712B9AB1298BB08B890D5",
+    "key_seed": "1FA8E31F66CEFA5302493A7413EAF5A813ABD73BAB089BCC2E2B25DD5320F8B1",
+    "ldt_key": "862229764161E53FA6D8CF8CC6A3D56DCEF546483118DDDDB10A6C22287645B0375799197C9DC42DB8D7A96FBBA551B3F4BD28F908C8EAA7F177EFFA28D4BE59",
+    "plaintext": "AEDE409688E9631E14C183F57789AADE"
   },
   {
-    "key_seed": "9829D6447C561A4BA0A5EC0E0C9CAC8E5DA6430FE27DB0D0E1D751FFA5D31F48",
-    "ldt_key": "428CCDABE1FEC7368F57CD23F949E8146871678DC0A18F7566E29A07014D90524BB819332141592B70EE68833175CE000C9D63EFAC241B15720B4D19E0607C5D",
-    "hmac_key": "F47C1E3EEB9A14EF81797EC8FC3A5BE1D886A5F25386AD8B7413205273CCBBEC",
-    "adv_salt": "440F",
-    "plaintext": "920B77B8367C89A08EC9BD91DAE8F57E808A8D8570EFB5DE56F7BA33A75A6B",
-    "ciphertext": "6DE0BADF95B7EE68AADE0ABDAD0EF726C1584A80FC2A7965BEA2CA481D6A4E",
-    "metadata_key_hmac": "EFB87B5515B40D8717C7617C2033F9150B41C29BAFE50A69BF0A2C95DE5B19C0"
+    "adv_salt": "1E23",
+    "ciphertext": "D3015A7E2D2486B1D7F07E17F68C5E43A377",
+    "hmac_key": "248F166010A6A85ECCEDCCEC056D32104A7882D0BB0036ED5D5011D2A1BC9C6A",
+    "identity_token_hmac": "0D3B2CD8E7706A893755D6749924AD5CA2F755B427CA8D51786D578B0BF11F54",
+    "key_seed": "DB335588CBA2795FCB41A6568A4D38945E07892BBFA462FE7016D3CA1DFA8E78",
+    "ldt_key": "9EC0F76BD542B03159B10EB6F375D8F02C93D1243F434E4356A4D52714D42A6E7AFCF2C63D462FB2C45256E696F5DADD5AF5C8CFFFD6B2BB7D758EFFD8B82247",
+    "plaintext": "9FEF46046655876C4A397550BA23C069C869"
   },
   {
-    "key_seed": "E87CE30B3E8CA4FCCBD01458EB57F0A54E5F0A1F013F2377461AAACA9113A715",
-    "ldt_key": "207F249256B15A443DC418BE6CFD316529266F030513B25DC88D7E86141543E0639A9F1480C320E565DC0C04DF83CBF630A418A79847B7FC56D2E68E0545A557",
-    "hmac_key": "7BA64235DFADA23F35D3BE3CBF100A6B4BCDFEEE28BA79CF914400E1FBF5CECB",
-    "adv_salt": "BD1D",
-    "plaintext": "3E72346BB0A79E75F6DCB30AD5CDB27758BE405D615F7CD931CF468F01A6AC",
-    "ciphertext": "D9DE8FB0D2DAE8863511B3179E5CC7E11E77603AF625DE1157A492C6ED6EB9",
-    "metadata_key_hmac": "47E0AC2BCC093E9DD4D617D6D12697A7A8E6EF7EA3153C85D6E6F8B1F5CDB73A"
+    "adv_salt": "F60F",
+    "ciphertext": "72D6B0C1EF949E0C6AA41084D952AE49ECF7625E8ABA6C",
+    "hmac_key": "3F666E45E16F4208066365B7BE13BF730B5432297816FACBF4F0F42D68B7B330",
+    "identity_token_hmac": "44FCB520188601A392A46E78B799EED60232D4C34A60FEC15A4631FD19B819B0",
+    "key_seed": "34BC6654E79D5DFB031101C6B9A0530A66522BB053318F1A890E7659AF457FF6",
+    "ldt_key": "BA0872DBF9B506C80911EF96AC938DD5FC2556048DD8A0A6E09D1B16B1FE89948D892DAEF7ECF438F24020AD19043330CF88E9F951260D2764875C0D8CFA30FD",
+    "plaintext": "2671280D380269EFA4AFAB7F8AEFBEA3BAA546A693226F"
   },
   {
-    "key_seed": "0263E7AF4055525AE31D64FADFF6CC1295A8034432020528BE6E56D69CC2B681",
-    "ldt_key": "A8BB317A8086B5DEC813237497EC12C897D1621E8D60C22D015F2E2AABDFB504122D22843E26F4B2E9FB3E4D0A491473D13D7CFD604B9D51A6E06452FADD3E3E",
-    "hmac_key": "A65B8453EAF6B2783E3AF93D3102F4FA8CB2BA378482E83F38182FD064D007C3",
-    "adv_salt": "A10F",
-    "plaintext": "0EBC9C6404682C8BD3E9AD99A1A3D7618CAB6114FFF6C875958049C1C5",
-    "ciphertext": "8E75136143E94C631C2944DD32EBB9F0696F346EBB8D7444686BAF02C8",
-    "metadata_key_hmac": "AA9F58AC9FFD706330B4EC958F101DB57D46BD88AB2DD265F70F5250D9915A55"
+    "adv_salt": "6245",
+    "ciphertext": "1FB17D4DB41ED8A69423D1D00E70F1468A6282C4E46EFBED76",
+    "hmac_key": "842A046AA27B15941FBFC1ABA675D054ED43AE4A774A621C0D79E669BCD759E7",
+    "identity_token_hmac": "301488269C15C06DD58C63D9F32F3EF5F1F25FD5824F5BBE3A075A0294E2DE8B",
+    "key_seed": "7784295CB941C0EA8CE99F532BC4356BBB70D6ECBF7AC8AEA9092111D04D769D",
+    "ldt_key": "86D6FB6D2D96A3B0EC141A3D6A97B7F40DB56E2C068B735ECBD106E34335DDDB350CE5EFBBD76DDE01BD4C5BC8CF5AABC1C2144793C9F30417D59D3CDE0C7B77",
+    "plaintext": "8F1813881C5F60FD25124FE0EE83C7585D64B028989FEB6C71"
   },
   {
-    "key_seed": "7876C9FB9FB4F58A761A7C6CC0B04368C93DD0E955AFF705C013D59AC5051896",
-    "ldt_key": "A300A6502E3001A500382DB2A10FA6B8C8C8CE2CC52CC75573E140B45FF7070C17CCFDD48BFEE0E72E18E41A46C242514620462E9A9860ACF1B22C16A4C2A125",
-    "hmac_key": "58559479522C1832647EA8F24714DF9D91FBBC27706E50BCD2888D8A36F46325",
-    "adv_salt": "DD6D",
-    "plaintext": "A4719D72E2EDF46B58D89ED9E8083306F5411AD487AFF4ABFCED8947",
-    "ciphertext": "8C61A76BBB3C1632037E5F20924BFD1801801C917741AC2A59DC16FD",
-    "metadata_key_hmac": "0F395A1B09F0A4F5060FB521B9EF59796339A9C8B70429960676748E07A777EC"
+    "adv_salt": "0647",
+    "ciphertext": "D1DC1D59D804C6F1066A153C50E6422A1F2A7FBD178DF7FC68DD2A80660905",
+    "hmac_key": "07BA5F868765444FF645C2975B1FB48C940C7372BDFBAC405E567C90165E8210",
+    "identity_token_hmac": "CEEB6515F58C53E6DF672636B05AB55429B8AE86224F66C8A812CADBB3B98277",
+    "key_seed": "D52DA32B6F65746B70EF2E6F99935D94DD3D60C34F1A7B5876BF829E5C67982A",
+    "ldt_key": "5BCD26A319297E8C1456DA63E5B88650905E2829238078D6D7350B5A6F4F955389C62DA84D47152845FE19BF4FF79EE81326833C8AFBED1CF2ABDADB5493804D",
+    "plaintext": "B4AB4964CC3C08873D0EBC40C929A6E4D0D52A0B3FDC70D4F93D1432DF2D46"
   },
   {
-    "key_seed": "6FA2BC03F573158EABAB35B48390E46B1D4070C09E0B3EB762878E8DEBB5FC07",
-    "ldt_key": "9BD667DAB846D7A2DBFCECA1ADAD48A933A7C75256B71221925C7B14EC127BBF82F4EED25B83589447F3685A6A2D862504AEEFF120D4B20D2538E5A03909103A",
-    "hmac_key": "ABA1A56B7051C0344F2BC497B5AB2A1D7037D7040C821C13428569A3D1BD6FFB",
-    "adv_salt": "B9D6",
-    "plaintext": "9FB0C213C041D1D1E134EC482C0CCC80FC",
-    "ciphertext": "C8D19DFE4F8C99D1026AA6E3BECE8A82E0",
-    "metadata_key_hmac": "E8593E0D4C29738FF3752BC5E905AC21DA793328DB599552BD86B71334E92C58"
+    "adv_salt": "7560",
+    "ciphertext": "896F81B391D7D6722D9C3F13BA3C087543BDDF119DC6",
+    "hmac_key": "E517606145DDE8EB4DEEC1F3565AB4E85C7E1D843FE1ED24F54D4C582E85545E",
+    "identity_token_hmac": "89A947F96286E8EDE8FFBCAA51CE708C06F712E09A8EBEF024E1905F07F42058",
+    "key_seed": "5EFF0828AC1743AB1F473CA562FC647B9A95558E737E8E2FDBC3E05C15BE441E",
+    "ldt_key": "BE3BE0C9A041845831A8AE86DC23F8F8EFC935FE231D7D812AEAA1EF47D59AED776C8DD7080F9FBD00DF74D5D9A4C1FDA8ED1F141DFA1A2CEC01CD09FBF50805",
+    "plaintext": "A73CDA171E2ECCDFBD71C5B2BBBFA33380A79F91F5BC"
   },
   {
-    "key_seed": "A55A9CEC40E1E37FE8BFF5ADFF4C68034F4589064D530388005F97715011AE02",
-    "ldt_key": "75797008482530202CF7BD71CDB51A9CFE2D505A393FF9A0849178239EA6E739FFE58B8153EA5749B6B53F37C1E6BFEE930E5BCD97C9E470DCC6EAE6D3417A07",
-    "hmac_key": "3C6162C6EFCA96689B94E84F714C8A00D99789DF7D3FB8E06774C39C73BFEB7B",
-    "adv_salt": "5EFB",
-    "plaintext": "9A05585013112CBCB6716344AF4CF92C3764CEE150430C4A8B72B1B0303A0E",
-    "ciphertext": "D9814AB92D016981018F136C02107D819A27F3A2C0A2768D4AC36EADD7D672",
-    "metadata_key_hmac": "AC1982323640547F148E7ADFF917DD087FB98AFD3C3139645A2A9620907D00F1"
+    "adv_salt": "7CC3",
+    "ciphertext": "047C631815B607C024C28D89330ED7CA7C34A1F84D89A1F0",
+    "hmac_key": "54016DC977C59CDA4DB4386813221AB2A2461E27F762C9EBB414C5ED54699820",
+    "identity_token_hmac": "2E5F828069877C3C7D35754A773EF067AD4ACF6DC0C6F1EA38C981459172993F",
+    "key_seed": "09078590C19AE6B515DAD5F2739D35BBCE6BDFDAF3BC348C9E54D8DAC8131B28",
+    "ldt_key": "34D97B145B530111A67723BA9CD3ADF0A588B83952186868EE050758D29F8D486A2364FDF5939D0C9B1418507A68AA52FAEFA20C7EF288435429C638F477BAF0",
+    "plaintext": "020F95368483CBF3BC0868E3CC6E0439B117323016414726"
   },
   {
-    "key_seed": "5BB19F6715A9BF3C1572107C27104BBC0E6D6AC4F984BD4B96931F79E6C585CE",
-    "ldt_key": "626DD569F79E1B91B42ABBB45C4686895563370718BAE54A6955FFB4FF9A93058183CDDA102A870C2FD53B0CB0172D46BC037B3579FBFBA603639B705383BE0F",
-    "hmac_key": "5F4B431121110ECB86B89EE8FEF343E07E4BAE63452320375C13109CEDD98EB2",
-    "adv_salt": "6DDF",
-    "plaintext": "7FEAD68893990AF7CCA7E26E94D0B1FBFBE58BF130",
-    "ciphertext": "EF57EDBE41E2AEDA8F8CB43DCEFD5B6367BA5E8B92",
-    "metadata_key_hmac": "BEB64F4417ED86EE5C150BD546970894AE6C7F48F931CB980D454E91F5ACBC38"
+    "adv_salt": "2A62",
+    "ciphertext": "6260B62E7005E2BA06B5FB3FAF71920EB5A3A579E3D1",
+    "hmac_key": "2CC37F55F21309A6CF34822F88A121989182274CA8F5CE4DCB462DA1B40433DB",
+    "identity_token_hmac": "7E095BB480D774682CB8E4729D69713685FB39C907A0806D448C61D1F779A9FC",
+    "key_seed": "8786BE0DA0D4484ACA7EF0E0045B376C411AEC27B813EEA3A2B7596F46179433",
+    "ldt_key": "E7965F807E1D7E93863736102985B943A5C88621B560550C5288B28BCAAE6BC223B69EF2CAD44BDC5C2677F901F2169CD9E62850EC690DC86040925F4E2F9420",
+    "plaintext": "6F4AFB850CABEBA63777AE6424EB517E04D494CF3ADA"
   },
   {
-    "key_seed": "A6663A8EEE1EF41B70E814267C9F2603C8B557AC51DA181A69C9EEDFC5F19528",
-    "ldt_key": "DD58948746753E17912CFAD8BD69DC9ACAF20119AE60BC41AE2A6A1C4CA53B33F65922EB92C116E25D27B6B377BF4635C907106418291023444B0582B622C5A2",
-    "hmac_key": "1625F8FF5C659C118993078C731330521D63785B51E5C0A2A0BFF8DA6F3E69B1",
-    "adv_salt": "810F",
-    "plaintext": "1F326F6587D80675DBCE9E0C56F2E95663518BEE5E",
-    "ciphertext": "251F2687ADBFDE3F4163ECCDE0380D7746538155F7",
-    "metadata_key_hmac": "E6AE6DBEA57A83A5AF7A2E7AFE745D477CC39C10264B0B30F29CA846E6128E4A"
+    "adv_salt": "C856",
+    "ciphertext": "CF420AF892CC3B42ADD3577D350209CD538F2224302AD46ED89272",
+    "hmac_key": "C00EA1BFEAD49726A4B4C56CFBF219BD0E37295F4C1C838EC473DECC732F753C",
+    "identity_token_hmac": "F1A9C559BD880189641845E9A651884C590AC1CC4165DECA33AB64C51F2689E3",
+    "key_seed": "5D4FB15064519023C6218E35AE9B2D52D342E1E4310BC876A282E4078DE3B3D1",
+    "ldt_key": "BEDCA450ABCB8B5103A7DB0F0528A6730562B0934F62DFDFEB8949354D0CAFD4E101AF5F1ABFC20E64B979C3CB4D73E69190A41E6FB135DA8FD05D0631003882",
+    "plaintext": "249630967EA149B9B2F2498B9824A52D317F1CFDBD16B2EE0C830C"
   },
   {
-    "key_seed": "00828953A1AE0076B75DA512511FB1A429C25A0F1ABA6153368267A44FE960B2",
-    "ldt_key": "71F2A112C0956E80955B3577E51D44C41B1AEDFA74745AE617326FD6FD826985CC466AA4249AAA64AA122C4D5AF7129411358876971D31F875F969925529167E",
-    "hmac_key": "5BD823CFAD624CC841C0C714E08F1A919F846E0FCB8B125FD08761825D9087F8",
-    "adv_salt": "181A",
-    "plaintext": "9C048A6138808DDA7B467085DA77FA1ACFD07A3544DE",
-    "ciphertext": "AA663B104A0B7159D99C7C1576499B2A23FDA7A567E6",
-    "metadata_key_hmac": "37EC37D5308D056454955C51B93E4150F2C56416EA7C6C948B9076F3C573A187"
+    "adv_salt": "9020",
+    "ciphertext": "DB1FA74871692FB6FC716584821A0CAC608E",
+    "hmac_key": "F5282F1B47045F1503FA6F0BB6826C9CB4A5C3C2D96F4CAD328D91A0FA60DF5E",
+    "identity_token_hmac": "E011BCAECDBBCD331816032F019F76078C1E47806A38DD6A2928231C9C22960E",
+    "key_seed": "16042559E2AB7392A3C297B88FD0A3694D4FAC223086B03B60473DE84B3A4B98",
+    "ldt_key": "134CEFEE472C4CD6066D064A6DD705D21C1FAB43174B096063C8698D4878F9F356B15BB6B695AED38CCF7A54B132B47ECB9B429F6CC12376B16CD285F25883E4",
+    "plaintext": "3801AFDDFD22B16A9FC330A4D14C0C216D9A"
   },
   {
-    "key_seed": "D660CEB399C3E0E51F3C491444FF6516D5BE55D6EDF620998B7D30ED0E80A7BA",
-    "ldt_key": "2FDB0A3BAF8990143F8D70F648C4727675C5BF066DF7757FD6AC0F2D4A7BF3A3A10ECF1B66264CAA29F0FFAE99048FF0FA7A646E78B1EB78C5C1F2D4DE6AF2CB",
-    "hmac_key": "A5FD3131713A68D6BB887EBCF083ED873373959807431425A388F3AB8239B141",
-    "adv_salt": "FEB7",
-    "plaintext": "EA7CB59D1E9F4203E2539E950D60EE1DB77861FB",
-    "ciphertext": "486BB7E2D495B8A5980D0A4E1C9CC9D9B3F23F55",
-    "metadata_key_hmac": "0789ED2ABA65D4552A7CB7B9A9959E825F82FE5D9DE7AB9514655DF3AC873F11"
+    "adv_salt": "B416",
+    "ciphertext": "3FD2CA364E9ABBA987AAE45D5C022B4AF774F2",
+    "hmac_key": "0C4037B37F9C4D607D91304CFD787CCB548720965CD5FCCC9CF7D8F1B4EB9EDD",
+    "identity_token_hmac": "1CDEC3C91FE18DE6E6B6022DF8C095F0AC05DC94168D22E86B83DDDB59EEB5AA",
+    "key_seed": "DA40AC0B2424FA5F53FC032ECDFA9ABD8435B908DFE451A3943718A68FB1B900",
+    "ldt_key": "7706D3DB253174C1CD4AF31690B3F5BBB4723CE48DB1685812481FF083FB7522C8049855F9278E73BA19A670C1E6834341250D45B10AE3C0995AFF261EB7011D",
+    "plaintext": "7709999026D4CFCB96C8B51A18285F00AB0609"
   },
   {
-    "key_seed": "4312F7F5892996C4CA4342A3309FCD0EA601AE94845FE91DFF56A95385F3C60F",
-    "ldt_key": "B8BA8FC9BB5F78EA07F0FB9FDF9FB8842FF334AD67AB8EACAFD415A4F6E085D5D7518A7AFEBB6E5423D62986FB039AE8FD792A8E285F952E4A7FFEAF7D134EC9",
-    "hmac_key": "18D8409206BC997C599263D62CD44380B44BBE22004E510E2B29B06C6AA9A506",
-    "adv_salt": "0FAE",
-    "plaintext": "74148EAF740DD87A25DFF9285D1FBFDB19F3F950547A1E453949F6A2D8",
-    "ciphertext": "AC7873262675D66AED37E19730DDC2B36DF22A68CD8B1E1F08E6B3BD30",
-    "metadata_key_hmac": "3BC5EA33917B1A9B4FEACF43AED93705A47F96016DDF7DC4AC8707ED10B8433C"
+    "adv_salt": "513F",
+    "ciphertext": "4C356FB91804FDFE40D608D2FA95204F",
+    "hmac_key": "14E2A8EE9F3439A5AD0D91AD6659F861763A5EAEEA9BC9FF950DD550EA5E2144",
+    "identity_token_hmac": "9ACC36A7C4AFA0D810E51768DB0D1280D3E7CE394C7B3CACA544D86D7ADBE685",
+    "key_seed": "AD90C78112CC93EF1D588CEC6114ECA8F949106D7498B6FF2738F60D4C39E3B0",
+    "ldt_key": "4082257057BF11A9A8EF7EF91862E420957C86ABC1DBAEE5FBABC3833001CE0B90D2096AD4E44B16C3F76DDEBC0FCF2C9E320039BB528ADA89AB806C5EA47FF5",
+    "plaintext": "486BBB7782B4C679B59D8791D1982985"
   },
   {
-    "key_seed": "60936E83FEB011BBDDE2C861564F8B349DEFEAF325FFD852DA601D5521830C1C",
-    "ldt_key": "B6C7D5FBF879C182B491B13BEA3760A246A7D49052EDB4AA0B22E099706FC11F5A5D70679617F1B56647B1B5EFA7544973CD60A5007CE966BCDFF8873EE10C65",
-    "hmac_key": "3708BF9E9C2EA13AE58F00102B2744794614687B50D08560E22A09427E26EBAA",
-    "adv_salt": "0749",
-    "plaintext": "D647802D5DC37E30A795DF344746F51B5EF7E94549",
-    "ciphertext": "9C8E99384E6926877C9F02B22A2BCD219EBC68BA6A",
-    "metadata_key_hmac": "1E510537E57C913E1D20C57C8D21AFB54D5ACC9839D66B398C9ED2DAC72B6BA8"
+    "adv_salt": "BF51",
+    "ciphertext": "FA4F4B54B39030D78185D71ADF35B70CDA4A67FC61FA30118DE4",
+    "hmac_key": "5FE98DB4539BA363111EC9E63F36ECF1E712B5EEC6C1555E75F47F34A66D385F",
+    "identity_token_hmac": "93202A0D36100FF814B3B120FC185C84EAC4AA81CE5EA1A17D2D73650B6B502E",
+    "key_seed": "2E0266AA823838A0C03D3FB4AD0A51F425379379067D7A0CD1650A1BFB2296DE",
+    "ldt_key": "58618E080BCAA76BB5A446A81F26C902F9BA6246C1C9D01F5FF563386BB143CE1896D9CB3C37A369896C12D8088488A3EC82369EF659294E62F69046CC82C8E6",
+    "plaintext": "74F37042ADA22F88D0D338DC74409A3E80E676BF0043C850C404"
   },
   {
-    "key_seed": "AC3CD644EA7AFE34435A1F11546FEF56365C4BCC580F95447E03D5FE12AE230F",
-    "ldt_key": "6EC72CB753BCA51C05D3A6B4EDF1B34E531E0932565AA7B771C79240759F4BD0EA290CD8DD1C6EFC07A3F8113F4F58EB6A00979F8D3B7B855D9B665338F3ECE0",
-    "hmac_key": "5971FC82754E0EE3CF5A79D67535F9B6CA5DB532471F30D01CAE7484DB8EB33B",
-    "adv_salt": "4558",
-    "plaintext": "5C0343A00C399C9862B55BABF4C5D94CDF243989433B0E",
-    "ciphertext": "CA47DC24D0D1E4F976C5A58074DD1D9FA218E487113DE0",
-    "metadata_key_hmac": "A1A0FB564882EDA231E4CBB9AF4F12E674CA51ED56CE5C251933AD3BE39BEA2B"
+    "adv_salt": "D0FA",
+    "ciphertext": "89E8307036BE9F3443FD98A08B163015577770",
+    "hmac_key": "58432E0BDDB8747F3EDC68085087DA99E4F14798FC9F10B9ECDA092C8B00C7F2",
+    "identity_token_hmac": "726FFA6370BCC23E1DD2B80749CF9B5AC77668D189C46BC09086DDBE16F6B5BE",
+    "key_seed": "FA95613BC2BDC64E9C2D82C588162E1820BDB21BAF633D6FDC910FD281A53433",
+    "ldt_key": "FDCD5116C85434334874C24E5583304A5DD0ED401E9ACD37C2F67E61CA108D85389D37A59164AD103698855FBE92FE2C1BA44D9C7927B6463A4119C4113C24FB",
+    "plaintext": "F5B63533E7C12F59B7C7328B91771F20603B17"
   },
   {
-    "key_seed": "901A6F048DF80F4D09E6D15E793846E86A62529A17990A9A76AC394C2949FCC5",
-    "ldt_key": "5CC9EC33625EE5283826BE48DD42DA644F4224411E31E71A834CB241C8D56C442D119310956B7C096E6BAA30E1891E199C06D8767AC2D4C5B5D6B7B4BA096975",
-    "hmac_key": "0337A6DCB3FC021EFA02EDA3ED5399B3627771194F77D6F21E5C78D234E54221",
-    "adv_salt": "A8B2",
-    "plaintext": "1646727763D58BE646D4070BC80C4E5FBF1B067691A5D21229",
-    "ciphertext": "59E2B0C2BB88D5EEAD5A8F733F2DFF166556C36A115254841F",
-    "metadata_key_hmac": "301C5DEEBA0A29EE04DB005A0831D0CFE83B102DBCDB7323615E0CD986B4F735"
+    "adv_salt": "CF97",
+    "ciphertext": "8E61F3D054491EEDC16B5D1D41E6AA288DDE01EA85949CAF",
+    "hmac_key": "F4F2F4D0CD50C49DA9C53544C4A55E28076F32042F47EE683E819D88D485B2A8",
+    "identity_token_hmac": "C45FDE6D91388B9C50CC240AF29105B8FCEE1AE8DA78279A44CCA55D942267E1",
+    "key_seed": "8C055A0D0A65E9E916540B1A644402C49BB52A8640AA754289DE2DE277D0A8F8",
+    "ldt_key": "6A9BDFCDA9110ABC983DF3439B4D1D01FC85065BBBF29B0D7B8AFDC533D38A2CFE97C2A9E68ECCD5CF4A69ED2CDF4B8B6D832E382AB9BF6DCEA17CF16717162F",
+    "plaintext": "96AFDA3D88FE3DB2E3DE4DF332146F867BE90D9C43D2C47B"
   },
   {
-    "key_seed": "7FD07DD39B68FA5AC3B28290D300A2796061CEC41885AEF1605D3C420F4BE7DD",
-    "ldt_key": "CFF848B80B0E57D8B2A692AF7E8876CDA6F720E4E52566A4422C3F002835F0DA2B76813CCC068504CC78FB07BFBA602454146F2D76BD3EA439305FF2B7E5249C",
-    "hmac_key": "EC4070A926535FF84E397CD857DDA2AB8F5413DBF9F78078CCAA44EFDFA8449B",
-    "adv_salt": "EBCC",
-    "plaintext": "6C1E0BC720D2B60D5950D4F83FCD0AB3AA52CB",
-    "ciphertext": "2BD9294E740B0E86C0BB41A806A04A549903B2",
-    "metadata_key_hmac": "5EAC027DA7F61F2BC016C0F4B186DF3F036063DCF44EAD4527A05EB58D7309C8"
+    "adv_salt": "AC35",
+    "ciphertext": "3E928D6767AA90A759784D4D13FA6EE2EEAE2A35C4DCF0072E3A09D4F8",
+    "hmac_key": "DCD67593752AB14E87BB808300C6C66C91CB2FA25D62A6514F556564B331BED5",
+    "identity_token_hmac": "171F0532504142E41460159A7D1837DF2F850858DDCBC951EEDA65AC75C32A7F",
+    "key_seed": "882F96B6ADDB9D6FAB79F4E60C643AF05AD71F3B24E4ADB3CCB7161479BB0EE1",
+    "ldt_key": "31EA954EC47F5FDEE4D3938671AF77521788E73E090A6F2A66B67141055CD75DD33F4B8E42163B2F21C413ACAA498F03B74B936195D13042B76557C232960F22",
+    "plaintext": "CF7080DE671FF22EBF40D640BB936F353E82690498EA296F41F94D5C43"
   },
   {
-    "key_seed": "C68092CCC57F93CE005FFF8EF45F5B880F58836E6CE859FDE3E21811587541F4",
-    "ldt_key": "710B9E649C1594026187E19EADB0AA6879F7F8C0CD2C447C95143EF0F215B74F39DD092D87FAA0956A38028A6B1EB487209947F6ED26581B8770BD61FF41F313",
-    "hmac_key": "9DE1EA3B995E7372264FC4403F500FB1109F1E5E04611C22A2AB66012D04938D",
-    "adv_salt": "A212",
-    "plaintext": "C4FFCE44E03DFAB9C6E872306336F45D392B56890404FB",
-    "ciphertext": "FB89DCC92612421F8E738F6842757207D0D9B4F4A6C945",
-    "metadata_key_hmac": "16E4235205524E457427C43F40D1BB19DFD5AC2A5F8F0832E9F299AA1167430C"
+    "adv_salt": "B613",
+    "ciphertext": "CB3041FD5DA18A96FC80C8B1CEF3550B978C70DE98828C2B8386DB",
+    "hmac_key": "C80FB73025F878C94D9B1D0FBFBB0BCC2EC58E6B70CEDB0B6F5C8CAFC8B602A3",
+    "identity_token_hmac": "5401E6D4577B19A6EE3BE303522F6BCC479C3EF6ACC56D38A92BEDDB52939B6F",
+    "key_seed": "7DC4F44DE1798BE251D7E7A04C1DF966F9AB95F9FC5A49082748BA447DD15FBC",
+    "ldt_key": "440AB453063146EEC285BA78D07FDAFB4A16BB8B7B53BC280BC4E68CD941393049BD2A3CEE1E5BE355F45C0C6409EB1521D28C8E349D30F5C1B3288109BE10FE",
+    "plaintext": "AE6591893D5C744DA7994DF7739188EBEEFD817D1952236FE69AFE"
   },
   {
-    "key_seed": "15DDA0D93BF9B9FB9792A3916A0639E52AA8F9880CFEEE1C98F9D0C940104F61",
-    "ldt_key": "92F261A46A17E4A7CE4BEF7A817D79828DAB297CC5FACA83790EB864ADD70B935FFA1D2464469FD655988B0118EE931AA0FAE77F9521CC8CF35F87D87D4CCF4F",
-    "hmac_key": "FEE8B413DF5ABF9D109AC9CF93F842EC0739603CD1184F8805C5B488B52FCABE",
-    "adv_salt": "61BF",
-    "plaintext": "EBB8E7B325AA42A0450ED5F99A4164DEEC5A056183CD101F6A1971",
-    "ciphertext": "9A388163E462B8513A6CF4B333CC8F6E809B69E69C24E1075AA990",
-    "metadata_key_hmac": "66F0DAE501CF33F770BF920299390628952BE096C9387558A50CC0A754DE83FA"
+    "adv_salt": "0379",
+    "ciphertext": "DB996FAC1234138C23FB41992A7EF0780E206F2F92ABC2525A",
+    "hmac_key": "C1A05129B34CA6A1AF456D8131EAADA8C726F094900B80BF6803459BBB124695",
+    "identity_token_hmac": "9E03313DF9976F77780B8FA9EDA1CFE507E7D2C0CF319B4B9742A2B1AE0924A1",
+    "key_seed": "EB0E8902818056408E2596C714D968A023C67BD616739F144DF890D14EA219D7",
+    "ldt_key": "546A7DD47F7AB24A55A980576921D52FE5E70CA73C562CB8748570D572F26DD288055E938AEC8D20D1CFD9BB0BF62315996F8F8B9E85DC15A9F28DCCA26FCA1A",
+    "plaintext": "71AD500B84B6FF0779906ED33191567FBCCE0D96EA06861654"
   },
   {
-    "key_seed": "3B085F2421533E56367A9BE398D88BE12345D3DF2BBC7A6EE9803B8D734F24C8",
-    "ldt_key": "8C20759E8CE3D70DC11C3B80D6CD8317ECE48F49ADF67D3D65D8413CE3A5930AEDA60A8D91757F521714D7E245406EE7D2DFB309CD20FC9ECB339B6AAF30945D",
-    "hmac_key": "4350D0803416BFB74F93F742066EB1EEF1ECF72D9B7FAB2C1BE8811F80843D31",
-    "adv_salt": "42B1",
-    "plaintext": "1530D76F0258302D0B6A844DCCEE6598E31801D8EF2821BA1C",
-    "ciphertext": "8FA43962DC2A1EF3D107134EF59373CEA2BF3CB005ABFB0E5E",
-    "metadata_key_hmac": "F5575D9501C401AD0A3585553BA70EBF575306BB4BE08CC12136190FC1B0AD4A"
+    "adv_salt": "C35E",
+    "ciphertext": "EE37C54F29697110BA36D71664924192A6A325538B02404D456A8D887DA2",
+    "hmac_key": "6133AFD1126BE85311C0727EAA7F4D9BF1876A2A10E1AFA68788887471AF1A84",
+    "identity_token_hmac": "32DEEDCCB7BC8E996B9EAAEAE13E0E39E6780E45AE6FCAFF86EC06A7B7C69D5A",
+    "key_seed": "B5669B2D04A1DF129C5F9DB6615F7C441379A0390811D18BB37BD30BB68B4EFD",
+    "ldt_key": "5BA46FDC79A07DAE2FC23FDE2AFE925B452BAB69D2E590D47A59C653626AFCFEF901AD0930F8C3387948323C95CB81B978F16C2F3F60A728D9900CF70039934F",
+    "plaintext": "559CBD79CD4C31B6A5CC4D3D717D7DB5CCBE81A64ED6EBE45B51F56DA625"
   },
   {
-    "key_seed": "257947BBFC05D08F0F0FFEE03A2C2273A011A5F7EF9B9746158797420754095C",
-    "ldt_key": "17BD3B4365293E6061F40761C60443B80D6846CFD48820442E3A3B1ED58A9491397FE74C3379DFBB9A06BDF0BA9D95E1CF031E40F70B3EC96CFD344D7A945E29",
-    "hmac_key": "BA79FC2C050E725C90CAE0DBE419913AC645B87D79D9B4C3648EE306F3B2FC22",
-    "adv_salt": "98D8",
-    "plaintext": "7BD9593ADD66E8B0EB23C00ED62EB2EAC32C0D7A8803DE1D7CF9A864BD",
-    "ciphertext": "35D739708CAB44C53CDE37BB97F95396C5C73D7378E315FB2CB7E4D777",
-    "metadata_key_hmac": "3FC6C015D7154B5096FE63226A1144C929809D4F5A0D02D5017A35B8C441529C"
+    "adv_salt": "7748",
+    "ciphertext": "0C5878CFC2569F410D9778D63267A2E7BAA9D500CFDB22A88C5CA0",
+    "hmac_key": "3EF5244431BAB0D7C71A946E911D744A73F03A99C738F026D332D0F0F5AFC078",
+    "identity_token_hmac": "388B67797237FE594F5C2EFCBF455495EB12079DBFBCC5BE6433B5F7CFA352CC",
+    "key_seed": "DCD1C168499570924F48A270C2A124C0D6CC98C25BE10BFF9B1501AE975AABD5",
+    "ldt_key": "9BAFF61B1CD7F9CAD69CD0BF62F990D994B73322D6850A71976377AF8FB4AB909369FAC038B9F4EA187B71F68F5E213C0A79E43D98F0F3F896F5BF98BD1CC147",
+    "plaintext": "C4C5FF757AF9F00815632AAABFB61B7E040D44651CAECA132DD898"
   },
   {
-    "key_seed": "5EEF0225548AB29601E1EC1C274F5E6B5B6119FDF0DC5CEC69D0DEDC02DACD24",
-    "ldt_key": "00243E2ED2C291E58F0242AEB46C6FE0A54B0F71444855ECA6E20F977071896501078D86A130D90D593A3F977C99F8EACFE3B4E7D4CD8E8EC410C23833078CDF",
-    "hmac_key": "37929743724C60521295D912605CD5C215294F4B55F9A667AED0FE22A94B44F4",
-    "adv_salt": "BCD3",
-    "plaintext": "1371B4C2F32F11B5DC9F9AC4BC41D9EB6CB821",
-    "ciphertext": "5C3E8770D2AB1EDE58BF72C45A63F7AE6E3006",
-    "metadata_key_hmac": "8D025FC2390405E609B15665E54AE5F912E699C6C14C16B49ADFA489FF78C3A7"
+    "adv_salt": "0072",
+    "ciphertext": "DCC53FF627D8610BB11C91E0B4B55EA42F3DD3A15CDC7A39D1",
+    "hmac_key": "76ACCBBA980D236DCC82B2722EFCCEAC1D100623F43ED538294EDC50A251DE20",
+    "identity_token_hmac": "C7521A6CD92D5245556D6FB7940D6EA4C55AFA6BF485E39C13CBE285B13A68F0",
+    "key_seed": "4BCA4BE4CD51155A91A05378B1F4A260DF52F4C97695BB9583F0A3DCDF79C5D0",
+    "ldt_key": "55292E8A6D7DB367CCD8F1C70CB5E45DAD137B88B885D3E16DA8C1267ED79AE5AFABDF8A95C6B919BCFF06A1D53D7E8BABD2C5190DF0379775D503FBFE65504D",
+    "plaintext": "0F3CC471872DD0DB7ECCB49FC121F6B82F580CF8A056B38B91"
   },
   {
-    "key_seed": "4DBE9FAB35FB53FCF1B799B62FEA1FEB808A88C40ECFF20065A90F0F603236E7",
-    "ldt_key": "C2E3CF1D90B60532AEE73CE8AB1B37DA4D290E74A2AB309D3D93394945EAA28924D2CC1786D75A9AE0FA260D23EFB66E776BE36ABCA8B878F47072DF26EB2F52",
-    "hmac_key": "C863DD8C616E84252ADB5C42A45954744EB762A21D7FAC9A7994B45C21E4C677",
-    "adv_salt": "56E2",
-    "plaintext": "A55F99D538CD53D1E18270420BF27FDB20CC7C949ADA1332A547",
-    "ciphertext": "71AC20DCE1E5E1A81866DE2A2128C4BBFF2984D94387604C366C",
-    "metadata_key_hmac": "11D7DF11FE83F6F1A64A45EB4D517C750BEA1B1433C1649DD4A2A7021F09B0C4"
+    "adv_salt": "92E8",
+    "ciphertext": "9D7A16FF9BA61DF8E9C334DFA7DC4CACDB8E",
+    "hmac_key": "5DBB733D2B294A0588CA8269C78B0DF193248F1C57162DBC2964E4555C7D7A43",
+    "identity_token_hmac": "07CDE87E1ABEF617ABC4656486ACACF06F374CB1398321616535931073C0A679",
+    "key_seed": "9838CB399FE0A326FADCBD72741BFC44D7E3931B8DD1548106FD6B9EB2E957AD",
+    "ldt_key": "6E24140E0ED82DD3E8F438E00F9FABF0980A8B19119F077A294AC077F4662FDEDB96ABB3E60C700E926595A96172C657DB31D98899F4DECADC200360BF658F0F",
+    "plaintext": "E82C53A487B1F338B2BC803C600BD9A02096"
   },
   {
-    "key_seed": "D6D25ECF88E2502B7671472B6A552B36D67B9CB9321AABD1F806ECD314FB9FEF",
-    "ldt_key": "DB7A65F320FB5013BCA14C176A00F8C1DC73F49ED05BE2C1E378DAAA33FEB5F8153C201C4E82A6EA6D5224D10781EBC271547640C85E55FBA24A3620A79D341E",
-    "hmac_key": "6C6D5EF7242CE23D098FF85950E838E497E18450067AFE46DC58E937D909C484",
-    "adv_salt": "0E30",
-    "plaintext": "4F4AE5C4AB5DC7FD4D3E8929140FE83D32C59D9EFD84",
-    "ciphertext": "DA443DF8CCA50DEB682E1714095280857CA6F4AE6166",
-    "metadata_key_hmac": "3E602FF8473A4AF98A4AFA779C69B468099DFE47A6CF5DEA74E24974EDAD64E6"
+    "adv_salt": "9383",
+    "ciphertext": "4885E9C9E997A4B971DD11E3E77290A98D6D478124",
+    "hmac_key": "425B973660A913AD6CB0207E02AB0F345A89B508778E5416A01FC632D73ACB5D",
+    "identity_token_hmac": "1B0B1D1F74FAC903899A010DA97DCDA03EC4374730445056BD13AEB3A7CDC0CE",
+    "key_seed": "CDC7DA21484EA21B0BF595BBD199EC129A80575FB00AAA54AFD79B0EE7823439",
+    "ldt_key": "1D9FC4B353E8F42FFF9216EB74A63288266299972A2CF73AFC649A07967FA1096093CD34FE0A14B257AE04F616BE0729C3A782CFB1381A74EABAE8AE26A86644",
+    "plaintext": "3A4D5D67AC8DE93A7D44473D846090A8EE4EA0E875"
   },
   {
-    "key_seed": "4280AB46666D4D72EF2BA5E4F09F4A9B515A6211A5594F5B432E16704C06B65C",
-    "ldt_key": "5A3ECF4BCFD13F57049CB026312CBD9A082A84BEB0D5B8BED4A0958D890282E1C42198FD2DDA2C567D9040F6B3514D1A6DE327D961AB398D2C5E5B381F2AE297",
-    "hmac_key": "3CECCCCF1E22B4792EBC15075ED2119869A066427A857B97B2B3C048642902CD",
-    "adv_salt": "EF02",
-    "plaintext": "998488E3D3E1D033E6E11CE1BD83DA51CA2AA4D22798126218",
-    "ciphertext": "19ABE26D88D0358B9E2A4F74FD05849025CC0D8BA531F00DFC",
-    "metadata_key_hmac": "B2E23362172A282DE1058FB0349F897B0C346917F45FD8D9478A28E0230454D0"
+    "adv_salt": "C210",
+    "ciphertext": "34DBDFB607BE2B7D28585DA18509B3E335D7979AD84C5F",
+    "hmac_key": "8B2575D2DBEE5987CADC9362900CE93ACDC0EC0BB7BC500BBE64EBAF50691E4A",
+    "identity_token_hmac": "761325227F7FE98C167C9E02CA7820151C70D304E6399356050D4355CCE99D62",
+    "key_seed": "758AB716E1EE6062613F2AF07F136225BA1965BA8899AC8F5CBF947C048B3624",
+    "ldt_key": "F72B9009F078D118108CF081507821DA62C4522FF2595679B4E73E003E0BDAAB8263EE64BC4C62FD0AEAE0F635C1B3757280618C337E1267726183C18AA2F86F",
+    "plaintext": "8034DA557861DEB71ACBFAF06B2363E2217A842A0282DC"
   },
   {
-    "key_seed": "29688FFC6D8199FB3150EB61CA566A52F4DBDF34C91D4D8F30722665C7C2C8C4",
-    "ldt_key": "163A0881FAF42FA1B43EAD48D7192487C6DCE42F819A8C9DFA44FF71E0EF830793D9155856491DD6B6CBF44ADDB002465D7D6C06D9F43ED0DFBE5979EA3EE6D3",
-    "hmac_key": "C55C11DB9F8DB61E96D276E0F27A6451173095D3CEAFDF78E4AA824ACC39E42C",
-    "adv_salt": "F215",
-    "plaintext": "76DC9C1687E162B8A6F5305921E25D4F2309404CE35F570603",
-    "ciphertext": "A9E1B8BC917BAB6914A69E94564B04073988A489ECF2A8A545",
-    "metadata_key_hmac": "C3F4F141C6B6B251DA07F07B1BAB1572E6A41C08052C4FE50C0DADCCB5898F37"
+    "adv_salt": "4932",
+    "ciphertext": "18DFA83D270515BA234A700C014DA5D6EB53A3F74F5AEEDDF9792B0904A3F5",
+    "hmac_key": "F9DA93ED8A58CEE5875A0A57BA00457441F1DD66114DA3835CE947F6683327C6",
+    "identity_token_hmac": "B61DB6EE722BDD44FFB11AC3187BA9F59D25B726D3C91963A1B92ED2E68CDE63",
+    "key_seed": "170AE50C4C3D672E225AA92AC5414FD3B7B5073073A61D50DF3E36044EF582AE",
+    "ldt_key": "766F6E50B231EC7692A9D5B1CC14A009B7AC6431A642E2CE219EEA3045E7EC45EA0943C823E372057962167DF8F57C916B3D0BB9F7D7B0C82EC80D4B947B54DF",
+    "plaintext": "116E13593BE0E8FEE54932013E43E984AA7FF6C8C0A825AED747D175BCF2B6"
   },
   {
-    "key_seed": "DCCB95471CD9428172C964CBCBAE520CDB3914FCB751EF98EBF864796729D8F1",
-    "ldt_key": "CF452D0A9AACA5C57E3D84937AEF3730F5EF28C4C2209D836629E9DFB32F40DF7F0C8524335C3FF39EC6091CE07BD720FC232C3910C93559335B98CD9F2890F8",
-    "hmac_key": "321262ACEC4F28F2882C4D744EEAB2684933B3954E4C1D33C462FC28C0FB913D",
-    "adv_salt": "32A5",
-    "plaintext": "0DCAD7F90214D1FEC607125A95BA4A0FE8A4",
-    "ciphertext": "AB0DBC14F08558984BDE7772AE0D88C01E47",
-    "metadata_key_hmac": "134A10970EA11BAB8D2BB41B253D91B488F0F34ECFEEBA74C5A4D314D39B386D"
+    "adv_salt": "ABB2",
+    "ciphertext": "C4075AE6956FCF87B34F8E4508C94BEB414AACC0A386D7",
+    "hmac_key": "8DAECC5753E47A2440DD3050160DC457F3AA0B6724AC5AD8277CC2AA4E9F422F",
+    "identity_token_hmac": "9556EC591F5D4B96CF0D27E081274D32EB6B842AEBC0F1440180B80E44415435",
+    "key_seed": "930D256D59C815A9511F10774CE147419026A620F262F5237FE1251B93505FD5",
+    "ldt_key": "20FB37DF50084F9267A8DC7A9B7BABC66611469FF26480807458619D63275A34C37836D07BE5D47CC03348A8A8BA96B41DD39A518B6E1AE4F44CADD7763DFEAE",
+    "plaintext": "4171C59B3CD3F41D44E18B6AB8876873DDCD25B9EBFFF1"
   },
   {
-    "key_seed": "602741B32088CB24744C42AE8A04F4FA1D8AAA395CD14BF794785AE906EA31B5",
-    "ldt_key": "9E5ECC1A36F9657D73D7F3609BA66A102DAB5D0343D641A07C5019CC86B813C25EDD9B6363BDFEAAA3403C061C8E84C6F334E4485A5EE988404EE98DF2F90CFA",
-    "hmac_key": "546DFAB1F8DCA2B85752D25B5C5AFDD1ACD8CC9B380920C529C0CE70C0B1BD55",
-    "adv_salt": "49C4",
-    "plaintext": "1B9CE96F10D577C41A523A2D4D75F2820FD811E14A3AA4",
-    "ciphertext": "0317B287D59B35AA1873CF36B3403A7F9616FB0943BBBE",
-    "metadata_key_hmac": "47FA521E23291DAFB3DE6121DAD692496392E939E03368506F66D701B4C64CB0"
-  },
-  {
-    "key_seed": "C43B0C050E0BEAB285191146BBA474189B253D78411FECE31AB5B6EE60253E19",
-    "ldt_key": "9BA205AFD03BF4BB04AAD928C7B6EC29D2DEDE9C98DAD5C5E036E7223D450BF3DB6FDE175317A6A1E26AF0080E5BF6A637A9C1AE0AFBFE15DF5454E0D454F138",
-    "hmac_key": "3D97AC87FED97DF4183ADF57F341159C847BE13712B82491C8C426A0D5AD7B3E",
-    "adv_salt": "9F08",
-    "plaintext": "F2B228C059D4CB0F7100D8C0B5C78EC9DB7E8F141E0BB9",
-    "ciphertext": "EF71E826D9243560EB3BDB751BD5679872A821C2DAB9BF",
-    "metadata_key_hmac": "C255EA0EECC08AFCBB47D033DCBD78F7E30BC0CC70F21754A8C523BF17DE3E9B"
-  },
-  {
-    "key_seed": "E3CB42EE350C452AB1D96DFFAE79F48F8BBFDB72149C21C803A4B50B8E1BC4FA",
-    "ldt_key": "F5836F573D7FBCFE83CB42451F1A50A535BE11AE71F9BE3DC92A430A144F3641F9AD55B29420F09AE423D6B2B64BB57AB4394EA8CBA8C2FC294E419D80CC61FB",
-    "hmac_key": "17B23A47EFEE4FEA1DA852F13EFDCD928E03C0E38BAB4F96B6643DD975A0ADBB",
-    "adv_salt": "0950",
-    "plaintext": "5655DC1EECAC7CD61B2DAC897DEA832EEC27",
-    "ciphertext": "82A8293E6CC28C0A185145D1077BEBE76EA0",
-    "metadata_key_hmac": "D97EFE52AB79CB84682B1AD131F1E46370986C1F24C819FF7BC2598FFB911051"
-  },
-  {
-    "key_seed": "DD858E3C02C5381C800757E996D2FF45ACBCB00A01A21DAFB07FDC3005348E11",
-    "ldt_key": "BF41D6D21D47EB610362613179227B851BDAF8F41DB4D82644CCCF0D1B2AFEEFBF91B3AF386D7741CE4535C6219C05C89FFEBD4F8C796E04744386736F9C277E",
-    "hmac_key": "7CA36BCC67F15C0DDCF071873C230F50468E3AE48F87F82282A83FBFF377BE55",
-    "adv_salt": "ED85",
-    "plaintext": "A9164F9B6C63C4C34E3DB34DEBD9BC6237",
-    "ciphertext": "346F3DCE5DC5778E368546F7CDB1295BB6",
-    "metadata_key_hmac": "B953BB4678BD924EEE6C47505817E1E31EA0FCD21995C66299EB43053DC0F753"
-  },
-  {
-    "key_seed": "2C8EAF66387544DC932D47E665130C17AE350BF726A1378ED17E08DB17E32019",
-    "ldt_key": "C1615130E0EF37C63115F80E8821C5306657E9886F37D606E3F6D4A21FA1055513D759E5F0F9F8B21EA215F690A23B7BA4BC78E56767E05523ECC970D5A7FC10",
-    "hmac_key": "090843CE6052B02B53F4D1B479623014360C9EBD3112FD72301C2698AE55087F",
-    "adv_salt": "B698",
-    "plaintext": "5CA984CCDD4AF7C3B24C92E8D23A74446D1E647E",
-    "ciphertext": "4784388C98FAED88883F171E2FBAB3C3D078B573",
-    "metadata_key_hmac": "9AB7AB93238D92FE02DEDAC29C775BF9315F3C696079DD72BD88C39EFF7AD0F7"
-  },
-  {
-    "key_seed": "BCC181A2A53F1E9A2B17DFFA213BA37BF056D2C1FD74E7F2D81A963654C5840A",
-    "ldt_key": "7B06F03E6964E98D68FD2D03CCA2F029E8A355DDC944132B4B36CB254F975A5B10DE495AB16919493E202B2D74924776DD675A33DC09104EB7CF3CD3094A6E31",
-    "hmac_key": "D651C7D566DDB461C7CDF0D6D0744AD6178658EA4A118465DA88052D9351F3D6",
-    "adv_salt": "FE1E",
-    "plaintext": "2EF24BEF273B15A558A8C1BBD415432AA4629D7A0CDBC9512F3DE2",
-    "ciphertext": "F51E9690D234726CF856428AE1373FF1940B778A8ADA54C2B867A8",
-    "metadata_key_hmac": "04F92ACD86FC2ADD7D1DEB7FD53D7E051F8E3E72EBED64515C1EA83DCDE19982"
-  },
-  {
-    "key_seed": "85787F8AE79A46639CC2C9F2B4B15956C7B92684F5D0F76547A7290AD87DDED1",
-    "ldt_key": "9396EA24153EA5CFD00969F044045B8665E89CE9C142C64B7BBA8C64D27C8A2069479AB3D69DC7D01ABC9DE07AE02343DDBD06379A297B83FDB8AF08F846E425",
-    "hmac_key": "E651170724729345796297608F276F262898CE7A66E9E948185818039783088C",
-    "adv_salt": "4D4D",
-    "plaintext": "EC0D0628250421CFA7BEC2EA26A7B435B4BFDF4B",
-    "ciphertext": "31CCAD29DA8E68183240107590089717669706CC",
-    "metadata_key_hmac": "EE39C3235CE44E3C991025B26E1159B784D46AC6F1BB2919BDB1E64759B7F478"
-  },
-  {
-    "key_seed": "808A56F86617F0A36E403CB6448531A6C9A41CD37008FF1CED445DD60C6E806F",
-    "ldt_key": "5841CAD4189E1396E561800917C835959E27378CBDB2AF33EB85116A1F1D4B1F9D2DCE40C5B4C5C12884E3D217000DFE1EC74B1AD17F34AEAD8DA086BCF3EB27",
-    "hmac_key": "ADE211D812886EB8D68406DF336CF870056343BA4F100948EB559AC4F917D2B3",
-    "adv_salt": "F7FC",
-    "plaintext": "85528B7B4972632BEFCE2D7C620747C0BC4F7B2C7008A9",
-    "ciphertext": "09C2D98933F2A751F548379D134CBB090F982F524DA2E0",
-    "metadata_key_hmac": "E561E0870430A5DD125F5E07A1852EE41E11F2344618A0F3268CD3A9CB7A0CD3"
-  },
-  {
-    "key_seed": "CD451867F408CE5CA056E3DDBF393B35FE2028992C896AB15B131FDB146A4769",
-    "ldt_key": "B3DD3D25B37B532E81979CC31C569399F098955E20E69A066D2533243AA32BCFC2D632387A63340D0646B227AA8C7CF03FE2A73D7DBFE5B7F3EB61C5453141BE",
-    "hmac_key": "E84ED8653B911533417D6B408EF5BB2025965C9B9291104BB44C00FDF19A3829",
-    "adv_salt": "EDD6",
-    "plaintext": "09E16923D27569CE6DFE8B8A3180D942734D",
-    "ciphertext": "7DF216E4FF81D7A86A064FEB71B54F8892E5",
-    "metadata_key_hmac": "15DCAED4AACE7F57D8FDD76921E60C110764E895FEF98ADBA986A239D98EB18A"
-  },
-  {
-    "key_seed": "7ECE23377DB5101629A397DB13EE9238DF6A707B63E59C214574DDECD28BD77C",
-    "ldt_key": "E5EDEA1C2057199A79C34AD5F16996927FDF147021EA2900732D8D90FD061EBEA850D755584DCFC419CC3AAA48372BB63C555EDF7B414D7AD5224F252E77BBA0",
-    "hmac_key": "A8BF38403D85F2D77B8F96B01BE5C2EF2E35B0CF4482DB66F5672BA407B343C5",
-    "adv_salt": "6390",
-    "plaintext": "9D6329A4603170AAF549CE2E49FCC674B9AC131898FA14",
-    "ciphertext": "0BD5FE354CC50817D766DA59E359BF06833FA85595DFDA",
-    "metadata_key_hmac": "48B79D27909917BD5326C04E7D22546C8777276179B70B7F0C8C818EE067E06E"
-  },
-  {
-    "key_seed": "15D576484E9214D9FAC931EDC67E091420EBBB7BF019AC4855B758E85D3E9412",
-    "ldt_key": "308E191F21847FDC8319EC46D49FB4B5593D351187EBA1C457EC08ECDE1234B4512A73082E845B307597E2758DD05A2F159D95C309DBD7E1BBFAF026E9F675F3",
-    "hmac_key": "705E54F5C58D5283231ACBF76CC81D1D602D1400AF5C1B6ED88B00E972BB995F",
-    "adv_salt": "95C2",
-    "plaintext": "5EE8F009FDF3707221F764A57B1797FBCC8803652ECAC995F2F422B65E",
-    "ciphertext": "C3F567F4CB422B630E520FE921E10176F3DC751FA706AFF159DF7D16E4",
-    "metadata_key_hmac": "089803428F05209A06F2DC80ADD9B708AA3F590C20164914C42A3F1E8F3D0A91"
-  },
-  {
-    "key_seed": "C1332895B13DF2ADCD2F87EF068BF9ADA96BD121447A2083E85BCC430F9D1680",
-    "ldt_key": "46BC3E82A6192F62AE5FB0DF1F84D690432D9A3883B6E755E5F2BC40A20C56E4E699DB37C5B2DC7552C58DEE84CFCDED48EB4FAEBB2782835F9432CC9F19BC96",
-    "hmac_key": "F75B7EC653D60C812B580405DFC9286E79A673597DD68AFA463CB8EDFDA2A5F8",
-    "adv_salt": "AA7F",
-    "plaintext": "1385F3E5B01E7667A39491CB78323792605041CF2AC8",
-    "ciphertext": "13A0B7C490E8C88160A80F667EBEBB89B6867CB7010A",
-    "metadata_key_hmac": "3526AA9EC81718F0A7354BA0C97DFC09F6151A1BA94B07C9088C2DDF6960CF89"
-  },
-  {
-    "key_seed": "C8FCA816F18F73E3F2CDA9156F5FA2D92FD91053ADE867F0230DBA4E2BA1E3D7",
-    "ldt_key": "8D820238E3F19CBC416753FF3239099DC5985EBE202780EDE7B6E920BD97421ED97D1FB2DC91321E2A702BB32DCFA4EE8E064B1B949C6EA21E828DCD617F30E8",
-    "hmac_key": "AB4E747A286393A9A8D4D2F54F8D4BC3EC07B6268D47362ABA14F037B80338CA",
-    "adv_salt": "876D",
-    "plaintext": "A16A9A2052ADB3E6AC2529CFED7EE211E58185647817E697A1570D",
-    "ciphertext": "48316F94CCBE262DF3FCF476CB8A0AAB475C9936B5DF0055738684",
-    "metadata_key_hmac": "917FDBFA129F192BD9616B44165D2202F063BD466DBF9B4E3C3B75568BF76282"
-  },
-  {
-    "key_seed": "639D87014289A8E028BCE63B1513502616ACBEAAE521919E91693F1357E62E0E",
-    "ldt_key": "61B7EC75F4F235D492F3AC35C514E0CBB6E350F85885E6FFD2297465958497E8EADAB8A6183AFF23BB69271F4500B8B1BEF9C6F7C9696588F8369664FB79AAD1",
-    "hmac_key": "4DBF3A9E6B37A014DEC9A15C73B000F10F7A1AE631BA76AF7DF3357E70980669",
-    "adv_salt": "2E76",
-    "plaintext": "45C44EB1CE23718F084A97F47FBDDCB1",
-    "ciphertext": "A0925BD133932E2467CA67274227FA55",
-    "metadata_key_hmac": "1F076DBE5AE63F71D4D5B23D99FCCC2BBB7CF73F22EDEA0B1C6A58035867D519"
-  },
-  {
-    "key_seed": "773E33067D43FC523B4305CA3578AEEC24F8427C3B3959A347EA4B7E50A0CA1A",
-    "ldt_key": "20F2936DB1EC57420FB7F9C24702CAC1706EB3E2BA44B80603A1511A32A29379A78E8B96A532D1A02B83590CFD2295FC5E9E327E9ED521A47103F9B934AF9AEE",
-    "hmac_key": "FDB438716D1EC94C1E0B704160BDB87C293DE9ADD5D1C1370A91BFE6FD2426DA",
-    "adv_salt": "D9A4",
-    "plaintext": "377F605140CD99F7CFEE12D57478DBEA648F55FF925E0B",
-    "ciphertext": "B0D28A4B39A01596EA5363374D0022BF17CAD42B28F175",
-    "metadata_key_hmac": "7BAFF574972DD155FD382CF6C055FDA328EE16C6F14552E4564AA76165AD9EC8"
-  },
-  {
-    "key_seed": "9043994ED8E3A349A66399E3D047EF6AEFCE13DC97D3BE218306B2B2C74F1838",
-    "ldt_key": "BE0EB7D950AAA7F181BB8354D988AAD2B7292F0DE1D08DA041C559443615D13D335ED6094EDC03F96765A4793213F64378EF6F3B59B15F939FFE095864CAE903",
-    "hmac_key": "2D1204DA558E5D4CB76835307D85D27A2515AA6841901EED119CD54571F17235",
-    "adv_salt": "0247",
-    "plaintext": "CE4A19AFDE5A8B0D45AAA57D63F43AF2DACC59",
-    "ciphertext": "65DA4E964B043D38A74F9C9C50AA3BD0A52AFE",
-    "metadata_key_hmac": "651BB173EB8AF1477B3FE8497AFAE9DBFFB6F9A782E8789BAD2945F229B0E96C"
-  },
-  {
-    "key_seed": "33EAAEE040953BA534409FA76C8E1007CE3E96589AEFFA84CFA1074FFA796C8F",
-    "ldt_key": "F4691E3AD571F4D034CF12FC1926C35A238A125E6BF1FEF0A60F414072A9374654C98368EFA72F43E1F721984584343839C3F773D2BE2D7EC113D5C45FDC998D",
-    "hmac_key": "489876B3B546151944E9BF68F313FF85A57E4F0AA10AA937AA01E5F38629D8E7",
-    "adv_salt": "1FCD",
-    "plaintext": "CAD15F38FEBA1822DF2FF59D9D957A1E45F969BA2CD6EBD9B21E1A77ADD5",
-    "ciphertext": "374BC96155E859C94B54724C63EAD5AA48EC1EAF80DA58645CB67D1596E4",
-    "metadata_key_hmac": "DA193D7A30A6A1BB378086C7268B588F72FEAA7C4D55E23BA6D6D8370D0FC6B6"
-  },
-  {
-    "key_seed": "DED6991887A8DF6FEF86818BFA5BFC500EC26A8A5AC46AD0930372CA853171EC",
-    "ldt_key": "7E450BF4E4FF15A0170409A3C9ACE09A56B68B5C767758E2D3C0092CB5E2496469381D9F672C7FB79A377B0B8CE1ECA3A1758AC53200EE5AF9B5840756D168ED",
-    "hmac_key": "6F2CBADD9A623201139B518CD064B84335CCB95C51880FE1703D8165A9DB73FA",
-    "adv_salt": "93F3",
-    "plaintext": "415A1BBF0375F9BAEAD6C8F12C0871991D2B9769E0C0",
-    "ciphertext": "C319812E0958DCB530583AA0F86708C5910689032E2B",
-    "metadata_key_hmac": "608A1CE3B8549B766BC485325CC22A4AA35993D8258A04DEEE1B4D3C3A2E24E8"
-  },
-  {
-    "key_seed": "8F06814937CC1CD3DEE445766BDA35802F1F90DB041696BCE49C9EC57A5EADA8",
-    "ldt_key": "D10BDFB216C67FCEF131FFE348A709DF30FA6DBC3DD3EE24871D164DD2F57FD5F4F02DF7F2C3749C29C75C79D9308AEA094B7CDCD49736F44E178C9E000954A9",
-    "hmac_key": "D21C39E1A8DC4CBAE82347B4FFCC25555545CC5FD81C6FA44C53A13E1DC18634",
-    "adv_salt": "6C7F",
-    "plaintext": "370B54A52F182467FE71B162EE0B7B13B820B6066F00",
-    "ciphertext": "565661CB8811F9FE7F76050884763A1E18C3BAD52061",
-    "metadata_key_hmac": "79B8C81795E9BD20CE806A3D043A6B09BD2ED0E60776718C70A06583E8123AF9"
-  },
-  {
-    "key_seed": "F9BF622E0E202B09C7064578FE5D476152BC66E22027F9CE1EC93C3598E023C3",
-    "ldt_key": "6ECE9BD2599C407B78833FD38EF430916958136DA9DC298899380DE639FE5BC28CB73A9422528836D7F4C4333083CC70B5B209A82817A9BC598EDDE465071A00",
-    "hmac_key": "8F8BBCEAE4BA15C98BFC13FB9E6128546F8B35C21A0436F9A268F8E0B3C750D0",
-    "adv_salt": "DE64",
-    "plaintext": "EF149B9523C59260F9F1BE6E5F398A67462D148D54E8F736C53C8DE9",
-    "ciphertext": "472FBC227FE51F9665473D5E4766EA430B19F8D41B4EC681ECDCE762",
-    "metadata_key_hmac": "EF530F925480E6119B9756EDFF83D85AB8B9835B7C91B4CE73D09EEBA986F595"
-  },
-  {
-    "key_seed": "5A4535D6C65E6F6C4331DAA9A82AF970C08EC5494997334E549B0BAFD8BD0EFB",
-    "ldt_key": "A49522B39E6F1056481CAC2813290655A0E3E5C5D2B6690ABAEEBF443C69FCCB2C92E13358939D5BF76492FFBAED6597B4FCD1FF197C3C20E14F957BA955D388",
-    "hmac_key": "A0CFB7E0D37BCCBC50338B76E303656B4AF45D1E4CC30D30D89CD457F7B203B9",
-    "adv_salt": "35CE",
-    "plaintext": "95928C9A06AEFBDA6A7CCD95D5EEAD60B088ECE67A",
-    "ciphertext": "DA6ECCA8646CE06BD84BF18B112FBDA195C9096CE2",
-    "metadata_key_hmac": "CFCE2919AB5341679FABFE8BEFD6855E4EFF443FF352872DF0F67F90B9543D5F"
-  },
-  {
-    "key_seed": "A1114E41B51E081227AB06CDB0E5239DD13B0E97B31E6EF8F8701A4B91636E30",
-    "ldt_key": "9441F2E6D59C21AE024DF96AC18A0BC769D0F166C4B68A5F1F926F53D2DB81A1D4B9414CFC365702A082F123F7E489EECA948E3DC8B78E7CC3327BA5C9AFB79A",
-    "hmac_key": "ED4D3D7846C36C9A7A29A55E304EB86DD67ABD8C42DF57C7F2704C15F450F782",
-    "adv_salt": "2FDF",
-    "plaintext": "59788107CA48F947B2855B1771A09C45B97B4C024198679C",
-    "ciphertext": "75928E663962012B017FC65FA716FB2C03AF4E6767650348",
-    "metadata_key_hmac": "7732C6810745DB6910FC1E4821F965405D9E48EA675AFC93F893EDBCA04D564F"
-  },
-  {
-    "key_seed": "4DB0975D2F6D6B3EB12BB3BAB460F4386D5E88AD09E0B457DC3A1C55D94751D4",
-    "ldt_key": "BD51281E7EC90564A5A29F85C8CE6244035D29261DAACD4D81400FEFB365A43B56AA8FA651F83CCC314B6B84F8FEE157ECD56AADF1ECE204015BB75840FF9348",
-    "hmac_key": "E2DB87BB9D7223BC2D71EAFC91FA66E11CAD6EA0BBB186839333FE4279F466B7",
-    "adv_salt": "4E0B",
-    "plaintext": "A91467F408F444CBED194AC5307EF4181646",
-    "ciphertext": "B97D2305E905784DC692289E2D64023C0D56",
-    "metadata_key_hmac": "3861175C0B2B74A0DE6F1F39CF59C8FE678AE79B6253BC48E008C140EF10C481"
-  },
-  {
-    "key_seed": "BA648F4E0AE9AF10F05104F2FBFBCEE0BB1FA0355555680C571132181D05F1AD",
-    "ldt_key": "896ED7242321DE997A3401BE142A595016F3C54642C6274925208BB790C6CE89F2534BC74B5629A147A2990E5CCE163CF1242AA5D3C888B760A3091653C8E1A5",
-    "hmac_key": "73E65E7E0EB5B624AB34163A2FEBD7388786D74E4E080DA8D2D292935183F058",
-    "adv_salt": "4653",
-    "plaintext": "BC2FC7A078DC86A8511D8E332380EF95A839",
-    "ciphertext": "FF06B9495E0F16393DE9B7B64F409A582807",
-    "metadata_key_hmac": "5F55D0F79D6CE882DDEEC1ED316E4A1732CA9D3DDC62B6B7EF38D005DDED3A26"
-  },
-  {
-    "key_seed": "6FF5DD9A8638451D09EC42FE83C60F743A4F14049C3EA102184F8192EA0792BD",
-    "ldt_key": "1D767FB92E69710B6D0FBF602BDD07F78FA4B6C8B32AEEF1433BAC04C55ED6D0CC3F96D2843E6A856ED3210B5AA1B243568A8E4A66B947AF4C73C0401EF57906",
-    "hmac_key": "CB419433BF50E8EDBB7D99757E4F6B44C08F1B34B39F43ACA6DBA1F1E342687D",
-    "adv_salt": "431D",
-    "plaintext": "208A5B5A5E3FBD6B966A48450BAE7BC142DDDBF0E406BA0373F1FD",
-    "ciphertext": "0BD3ECA5EAC8A1CE354FD6673AE0FD4C5FAE891D17D1243F89F16A",
-    "metadata_key_hmac": "2ADC08969BCCCFAD5CCC65C5EC5EF23E153A854AF479D82A26A68442BC40C682"
-  },
-  {
-    "key_seed": "007179CC6DFAC2206616506969B8B4523DEF33A7484A8422420387CF517EBD6E",
-    "ldt_key": "6547DD1C3A839BD25719F7086A39416A2A5F2127F04578F7116654A22B65A20229A2C89FCCF1C5A3AA90FC1321A393517C05278843B0D01996236CD747EE2F0A",
-    "hmac_key": "A93B1379F258266D371399EC911827037CB0F1A896419FA50F66B638D8B56498",
-    "adv_salt": "8CED",
-    "plaintext": "EFF413EC2429A863FA47ABC38A679DF714D7335F86E811288F82590C90D1B9",
-    "ciphertext": "8F2C11EFAA18DA47CCE6BB05EE70B7F4FCBF25A51CFF41F440A0B938ABF006",
-    "metadata_key_hmac": "1C8910D812CDE5B90D80615B93182708BB4E32A6D29DEE96590DF47B9AD5E2D5"
-  },
-  {
-    "key_seed": "8EF6C0B1B0F45A0C7A3DC587544D5AA38FFDA1E4486680CAC2570A679ABCC80A",
-    "ldt_key": "07A9127089B82445D88A4F5503F909654EDF75D5E56B5DA358F4C0A3E45312D84CEEB8E4DCED559DA036DA236352116691442CC07967A79F73CD47EB510E755D",
-    "hmac_key": "9C64EDC9C36ADB8F20601DFA9DD9EA6D116A19C3CDCA6E69A5F0DDD4A0B6D31E",
-    "adv_salt": "1028",
-    "plaintext": "2AF3D2C7D0CDC8EC583521C17400F4D3",
-    "ciphertext": "5D92FBB35A5A032ED4042E3803A646FD",
-    "metadata_key_hmac": "E376FD42A95C169E444B04AA336053445A3B29FE947A52F0F907982AF03CB879"
-  },
-  {
-    "key_seed": "45AC70DF90E61ED6FDE2BABD2DE9DE4F2B818D9A1D52CE8CDC56F53F0D6397A0",
-    "ldt_key": "BAFC221A84DCCA1B5ECFEF66DE66497547131E8C4548B6353B013111E76E02447429A86A71BCC50F02576C4E75FE789C5D0226B979A97EC8FC63600F14AA281F",
-    "hmac_key": "C30D9ED8BD299EBB5CD23F86EEAE1068635347D7B5061FBD17C34CE9A14382A5",
-    "adv_salt": "830D",
-    "plaintext": "4F4B6CD3BDAD13388F03E295A29734F1AEF1",
-    "ciphertext": "F1AD9D75E6013C892EC19A466BD346732C35",
-    "metadata_key_hmac": "3D042C46EC4B53B9BF2C301C02BB137179037B09529F12A816D04A8DBC94863B"
-  },
-  {
-    "key_seed": "9C5113AEE454EDFFCAB89C356831E822F76FE28154990FE13186438663D75A3E",
-    "ldt_key": "24FB6B83E745E019E071368C902F2ED6762D87498A4E14AFEEF4210BFB2A9E0E9953389C9D5E0D60BB337801C1DE1BFB658BBAE4E87FA943BF6FBE824E4798F6",
-    "hmac_key": "AA068CC06CDED1EB64ABFF4B28DE707FB2EC585231788F3C319B7651563CD17C",
-    "adv_salt": "A65C",
-    "plaintext": "104736EDA44F39305A5CE472049989DAAF9972B35DC8",
-    "ciphertext": "532D6EFA1313457BBA6EE436609DFC11DB7B4343B781",
-    "metadata_key_hmac": "ECB5D9170FE03D317E5A3EB11C4BC5F8F8123A7A3DCE3E68CD4B547707DC7C74"
-  },
-  {
-    "key_seed": "29C157553B7C46F058EBF29D6BEBF05CCD33C943AA2BDCB5510B372861CB5F78",
-    "ldt_key": "A3544DC50715B43F8EAE5A307696C47C061CB518BA11805A7161877FAF26D1F2854FEE2D0249852FC2D93699C945A74A6E007ED2C33A3B7054869BB2DC951BB1",
-    "hmac_key": "B9701768F5AC2E7FE0FE2784412877AF4124B7AE53E68B4CB849F81683E3F057",
-    "adv_salt": "D44B",
-    "plaintext": "BA6825C31151EE0722F26847F99281307EAB86791A9C61C2DF7A7E0C",
-    "ciphertext": "43635813DD9653DBF32DF9CB0469065CEA7C6ACBB125D3AD9EEE658E",
-    "metadata_key_hmac": "7E27C5D993C851B84ADA7F432D513C36E8F6D491CE47FD1B0C06825F04F932B6"
-  },
-  {
-    "key_seed": "B0EB2DAC5710C79EE1932CE8F17FE4D366547E7E2175C1A712E1F44D4C2E2108",
-    "ldt_key": "6F74ED8ADF4005EABF1E018BC8569185508A8D954A7607B68CC3CFE98832D0B2210D4C069C0F5FA6AF7208287C2B068D67730B8B8D82B95B47AFFB178677A9EC",
-    "hmac_key": "FE7F4D7A058B1D6E15A70EA481C34AA0F0D6A44FE36382EA4BFBC3C7E43A7298",
-    "adv_salt": "B2DF",
-    "plaintext": "EF23D6F6A1CADE520DF4C25B4A2ABF6779A9DA4B361F6DF6FB",
-    "ciphertext": "0D79F1106EF4E722B2C8AD1AFC5BFB1F7ADE166221AA5F8016",
-    "metadata_key_hmac": "B721CA7742066F65D798FB714CBC55B64E57C9BC7DFF464C2571DB78CE88280F"
-  },
-  {
-    "key_seed": "009A9F4A605A66554743DA0E493F9A33C48973888D68FEC628E449393E5FA3E1",
-    "ldt_key": "415C4DD2919CB1F8285604FFA82E5FAEDE8A4F186F77370A509BA6FD4E3155AB16326C8DB36FDD685252530D041792A368C092DD9F4F020CDFA8B42487660188",
-    "hmac_key": "734CDADB4DE10FD4F9159C523B0B62DAEBE653A982693071520D6386DF4979B0",
-    "adv_salt": "46BA",
-    "plaintext": "87D22D460F1178F26FB8B537374856825563264F3908",
-    "ciphertext": "7FC280353C298F5AB2423B0CE4C2E756601C4A1C395D",
-    "metadata_key_hmac": "6DE60DFBBA508C7A30059582BBB6D74C2D4B14AC6C135016C304026FEEEE21F5"
-  },
-  {
-    "key_seed": "9E86CA768B535DB61C0BFDBC1FB5D0F28B5499D0D954DD90FF3DDFDB90084C07",
-    "ldt_key": "939105E8DA13970026CFC2E622D8880829FA89214F3DA76A6BF46A0A816DFE2633877871D2CF4F093BCAE6B40DE7E9F65D0522F2577943B040BFCECC44BE7148",
-    "hmac_key": "B68B131100130CF46E88F47301774490B99DDA1E397C09D791FCDB0F5B3F4CA9",
-    "adv_salt": "D581",
-    "plaintext": "8485BFC1ED2472C01D6AC692C7011A92C5E4B11E",
-    "ciphertext": "35B09B08491CB095260EE18F971B42A7EC8408A9",
-    "metadata_key_hmac": "739FB784C50F16B18DD48417E91447AE7E5AAA085F217F3C32CB9070ACC0AB69"
-  },
-  {
-    "key_seed": "DD5BF59A0D1FDE574C417CAA8D9B154A16A70310AC9ACD548F01AE6F48AF0A2F",
-    "ldt_key": "6A8139A5BD21C918C47F00DE8C711A8E19B87DF67BDA7AC3F91613222365FB2ED0248D574BDC0C6CDA67E7BCCF961944E818DE916A3DB44F7CF75FBA2D339255",
-    "hmac_key": "C0F6FE0B6811C5DE637B0F775D7388F12A6AF805C9E0B962C21513C4A6519891",
-    "adv_salt": "C7D8",
-    "plaintext": "5C155BDB14AABAEDA8506B901F62BFC06CD1B24AA2C9CDD2C224",
-    "ciphertext": "5AB031D04796DF77F2892C4926E90D2DEB1E317642161FE53142",
-    "metadata_key_hmac": "D1F6F0E2B8D9F1760FD3E76B5952E56A1442F2FDFBA2BC6ACB038A2554D0189F"
-  },
-  {
-    "key_seed": "A9302C3763D1B55F5F1086D4255088942B2AEA3794FED75D9A02272AE2A460B3",
-    "ldt_key": "A909626CEC0F439B0AD454A66CD0748355DA03D56D69309E86B89D34C06A021CA7DA2A55A5AA86373484C4B110019C54C84F1D19E2CB7CFE3309F98D24E9F78D",
-    "hmac_key": "2C67C798D5F2DB996C6409ACC8E3C712FDC2735261F18155BF9A4F2F23ABBEAB",
-    "adv_salt": "4D48",
-    "plaintext": "67C49EEC0D0B2189F7F57AB202C70ED3BB573A",
-    "ciphertext": "BA6C447C9DCE15414014E1F6EC1175695D07F5",
-    "metadata_key_hmac": "ACDB3D18AFB857EAB7E9AB74C72C8B8C0796CE5AC81793E7B1AFC29B8ACABA1A"
-  },
-  {
-    "key_seed": "4FDD1EA1F83E8C1E076E152841D9D7060A25C2CD08886D91AF5407A54837782B",
-    "ldt_key": "C5384D76C40CC48114BAA1E2D7E5F4260F8D9E8324C7D358316D2045C7C3F50DFFFAF41DFBC8A6B5D8D5E6B3A08B85FBB1B6054256C5066522C027C7E9501DBF",
-    "hmac_key": "66FDFCE8CAE843E394B3B2DC07359656C07E5013D7CE65A75B62458288420A88",
-    "adv_salt": "CEF4",
-    "plaintext": "F99804807B744EF30BC27605453B918A0CDC314B",
-    "ciphertext": "9B6568B0BCA8AB1C8D15AEB5D97DA2740CE94E01",
-    "metadata_key_hmac": "D31CD9242164684A1A407B30EE1ADC1440AC2D57A8DAA4FE16813AB58AFAED65"
-  },
-  {
-    "key_seed": "18039C5E631E718706E5E4D16E9743774C3CA224827B14B831DBF73E3B0BB89D",
-    "ldt_key": "FC4544B4C389D90E145ED6FB0AF7B65598A72305EB6CDE719E50998A54846487794B7AB83E065A488EEF049FFBC173BA3C46FE61D41B8C5D1B8FE8D95369D691",
-    "hmac_key": "26A5EE999C16AEE67BFB7D0C99CA798A7C336210A464D3873F81A194ED7E6EA7",
-    "adv_salt": "09BF",
-    "plaintext": "642530D92AEE6E1FFB643141611A54063799B2B2FA3BB0612749E11C1ED2",
-    "ciphertext": "6862DC411093C22159CD52C078892BE2B44DDD51CA80271D3AA86FD9015F",
-    "metadata_key_hmac": "D83621ADCB72B4868FC98888436BBC1CB298B8189196062D8C5481A1D0C36144"
-  },
-  {
-    "key_seed": "07E84066C58C06C51EC8B808170FE789532CBA93C6582D0E754BE6B22B14FE71",
-    "ldt_key": "A31BF22254E1AD18C8EF1D435BA3943568E9E5D20BDC4D4679F34CDDF0931DEA62871B0F8560AFD4EEDB81680720EB3687D8807BB621251743415DCEB387CC10",
-    "hmac_key": "9526730144BEE056A80AC643CEEA02CC05ADA715948BC5D9FBAFE7CE57A241F6",
-    "adv_salt": "45DC",
-    "plaintext": "049476A522B205FA1A4FFA1D11B72371188596BCF23675EF339B",
-    "ciphertext": "DC896B566CC1F42ADD142657DE80FCFB22120F7C5A38564E3F3A",
-    "metadata_key_hmac": "E863A720661C1E596D0DCFAEF4E606B5EAAE99AE7C1435EF2705A5819D0542C1"
-  },
-  {
-    "key_seed": "3129749A31A2D287D0847A668253135CD00CF0313F65256BAAA0982B0916742A",
-    "ldt_key": "8B8CE49C6467A653BFDA93865DE10B97D5618843E5107B7A416B4B841150A45356950A78DAD9773C40A7F342B341A04514EE6F37BC822D69915D01D423D863CF",
-    "hmac_key": "1C4D6B48E9E6FE68ED0B83EDF0AC11B1844C8EFAD0841FC6066BA8180F1FE42F",
-    "adv_salt": "BA10",
-    "plaintext": "F1FFEE839C1ED6B21262C0DB7408BB26BA",
-    "ciphertext": "8CEE4E47ADF425F010C2C5FA8FBCD33E2E",
-    "metadata_key_hmac": "0309251E455622EAA04B5E54736068A8A1940C868788FAC2C205A1209A8277FD"
-  },
-  {
-    "key_seed": "9FA2A639EAA55D338CEA530C924BD191A1CC99CF727E9427EE59E291F679FFFA",
-    "ldt_key": "6EB3C48821C6FDB8ABAD5B676CA872912CEE0DC934B0012841D0738C5F1D751E1FD9FF27903C48BB6B1495D62A0109DA5BA1D594AF894CDC93FFEE5E7DF9C36B",
-    "hmac_key": "7410489E590776BC309BA2C538835F0A7138C46881074FE3D89A0A34B8DD964B",
-    "adv_salt": "0E73",
-    "plaintext": "D9B608B87689E69393CBA7045CBA2675D5210FD650E8967A1200",
-    "ciphertext": "395248FEC028BEE9FC2E94F0A7DCF1DDB14C0D9160F93B27BF44",
-    "metadata_key_hmac": "F880F33851F564CE5FE2D9C99CC3E75FBA95A52BB44755D6F99B8B00FD9ACC8D"
-  },
-  {
-    "key_seed": "EFB1AA2A2F7D37EAAA53416A15B0F595C1B1785700C33685E30B3BA08E409C6E",
-    "ldt_key": "2DF7BA528C2E07FCE74117A645D060CEE32F2734E1012CA4CD57E2B97F7B5C168D70060296B2FA47B664E040CD7ECF4590058503D3D24967AE0CE9FC6901B72D",
-    "hmac_key": "36D64D8224129E300781C7256A05956BC61F6488C2966DB79D75E50F02E22DBA",
-    "adv_salt": "F5FB",
-    "plaintext": "CD7050E3B6568748C6D3B1141651EE8FFB74C58D67",
-    "ciphertext": "DFCA49BB63A411FEACB0582A548E80C9AE030DDA58",
-    "metadata_key_hmac": "329A9CF2F51A7F8518F19CB80E3DB6C2157D8F4BADBD466C3510196D91F02F85"
-  },
-  {
-    "key_seed": "ABC26C4EDC9AD2E8414156FFDBE43DA525724F9C5340E0849F8D884CB52A76E6",
-    "ldt_key": "218CE9DFDF7D36F52A79A69D06934526E2FB1B23F8F4A2BB3191270B65642D9D81F959533DF0DC10D9D9D5DA482B2AC6E648662A15A38A5D7E551D791C8854A8",
-    "hmac_key": "694AC6E4B3626FDE141A592D658D917C53CE135A83DE39A7308C6AAF5D800F4E",
-    "adv_salt": "CECE",
-    "plaintext": "400F3055B755F217E37B1BDA6B9223199833B8384921",
-    "ciphertext": "86D1030F1E356691F1FF69B5E11EE37A7857F540B482",
-    "metadata_key_hmac": "7AF560570537967A5110F475B8AF8D085E452523EFB58E2C5665B109527CF752"
-  },
-  {
-    "key_seed": "27AC86A165B6C6170123EB80412BFAC95B4F14718F7C8B73F0EF84140386A9FB",
-    "ldt_key": "B23836E5CD10B4AFF120E576F51827952492634C12E8D8ACD63B3173EB2B2554CFB9A2879E9ABE990386064FAE52C6AFA4C5A62AB6B7A3907791C962AD458A5D",
-    "hmac_key": "C17707BA5D666817C2C95164E5B048AC816E65961ABB53A2709937E3032133D9",
-    "adv_salt": "F5FD",
-    "plaintext": "0F0B5BEEAF33D297A944115A4D1C06FA2FE6E3379A",
-    "ciphertext": "B729C506A6F6DF9C07EF46E4A29164E9B6A1D03936",
-    "metadata_key_hmac": "31DDD00AB86582E2664500B7B76A1797895A508341B9BBC20A20AE3FF72ADC44"
-  },
-  {
-    "key_seed": "F578B69AF49988BF7C2FDDB5C70C65A99BBFDF435D76E195934CEAA6A7D9E724",
-    "ldt_key": "D627666F52B99ECAA3A7CE145F1DA0FEE74EA601B263A64E5336A3307EEAD2D6CDC241DB163EA08B70A0B3E6C0DDDD846CD5925A0087B897E96E1477E937CA78",
-    "hmac_key": "7ADF86C014B307C4C76D6AA545F34C885CBE45586CCCA91BA444D9D839EA0657",
-    "adv_salt": "822B",
-    "plaintext": "05D4E637910F7636BDAF10C91C8EF92B1D0E6169",
-    "ciphertext": "43C0010F5154A64828D869C1E8D78AA43C6CD6D3",
-    "metadata_key_hmac": "99BC82BC668CF625E2AE0C57BD87A5EE34DD6C4DB06B47D64DC734CAE3C51FC5"
-  },
-  {
-    "key_seed": "7F5230AB4FFE1F84F9CE47EB0552A7C3EF609D1022A8A63948882F4C01535229",
-    "ldt_key": "FC342ACD336EE593AB577321825B3692D1D3D0784DEBBAC1F4FA23C98C5523AB04203F86122BB874987062DF9F94F91B875C2FC505998D07779349C7811BB475",
-    "hmac_key": "D62D38D7E3DBC68343AF3053E872AA59D2AB568627ED9DBFB84E1A5D29443F0F",
-    "adv_salt": "403E",
-    "plaintext": "C57CA7E2F5136CA9604C96D8EDA58DF09E78E58087A5B07838",
-    "ciphertext": "C330D4F9C93587A1CD30B269654FB34C113D4AA0254E7137A7",
-    "metadata_key_hmac": "AE6B59CCEB9EAC49D550FC756FBE278215C92CA503D72AFC6BD5D7887B5D4868"
-  },
-  {
-    "key_seed": "417D441008248EA41F90273D48874CF56A9DAC55270F5959E210D28D77AB0857",
-    "ldt_key": "2C28EFF0F105DF2ABB5EF097A1DD3B7266582761AE4BA1413A3C1C05203CF08E5D9A69AD9C7F0DAEE94BFEDACB926F0F6E6C1C460B3257FDE29E53FA637FA3AB",
-    "hmac_key": "773DEF77549B95A210F4CEDE4A0DA2D819A1A744B1BD7DA83321199DD11FAD62",
-    "adv_salt": "CAEA",
-    "plaintext": "0AC0691AF4BB582180C62183B345B241F6F1",
-    "ciphertext": "A6841FB64E1F975B1E33AC1115BEA5C54731",
-    "metadata_key_hmac": "4EB36FEB8FC7D5B3C1594FAA66837FE74CAB1EAB5805B40C68134CF96BDA8D91"
-  },
-  {
-    "key_seed": "D8BE2DEB66079C2A189BDBA5ACDA0CE82C52E804513CCD7458789E38BA81CB06",
-    "ldt_key": "64EAFA3B3394895597807680DB7F1B8AC7172495375C0547577415110C8B021902C6A31AAE1D288594D0BD59396072877633DB1F3A794DC9F9549609CAB703E9",
-    "hmac_key": "7B74179C3AA756E8A538CC1692F943CFB6FDCC89C7B39715042DD210D8333580",
-    "adv_salt": "4073",
-    "plaintext": "B7E33A6E31B663ED161FE93A7094BB3A6A",
-    "ciphertext": "B57378C629B1C32A257E6686E8DF988F8A",
-    "metadata_key_hmac": "02DF9779CA339B433AB29E36E81DF868CA95BEA952ADF8E2E07240CD63783510"
-  },
-  {
-    "key_seed": "6EA44D999C40873243BE45A05C3B9018F4001829341067BC146F4DEE92DEF40F",
-    "ldt_key": "34B9DF4B38CC3D8092B944C1A53FA957F0D655250113BFC82E94E06B2F1631EC9EFCB8AF36038B6289DB7D5EEF67BA75EF358007D3F6A1B0B2BA0C836326E6E5",
-    "hmac_key": "ECF156444A6B6DF4206076E4B90CEE5A6F97151271F866B7947B2B816D8BBE33",
-    "adv_salt": "8130",
-    "plaintext": "EF938897F1CE69ACF8682432D676D657C03A49754489B0",
-    "ciphertext": "E788FCF88F97736EBF9FF8B33A3D05698709C053977EA3",
-    "metadata_key_hmac": "A24356616C577C504CD564ED753E73F513F770175265DA809C90DD8C98B60766"
-  },
-  {
-    "key_seed": "B5FC1961F611B50A2A9953E7C36D271D08220FEB4986421A2549AB1100418F5E",
-    "ldt_key": "A8E12CBF2C0BAE68EBFED064BBE586247ABD2116CB5289E6E98D2B2ACE1FD48109B9877081766330136895F1FD6F585FBE3F5F0EE36E8D4F4DB64511266EC732",
-    "hmac_key": "851E5CDA65FCF29727A9E964D35A65AB682D2E05858C25FF70428719BB572531",
-    "adv_salt": "D8E9",
-    "plaintext": "8E1698000B95B7B6FAC61F5DB07049F00EC8D652304E94EA",
-    "ciphertext": "E5ECC2FDB01C052E5A666A131F160B263FA48A2B2725B56C",
-    "metadata_key_hmac": "3C54F7FB95594654F7EF39804C9CE54CE390A862C24921781B6ABAF34E960A20"
-  },
-  {
-    "key_seed": "FF14DC92A2C83279AA4E8D766C754026842A66A881E03C11A56D25ED84C1BEC3",
-    "ldt_key": "690D6F6D509994CA4949FB7ABC137B34B1BE1FE69DE6730632009DDAE117E345A693D3A579F781D1542CFE2F09C03D6FE5444B5408D612C38A3FC9636E39030B",
-    "hmac_key": "B79E2E333CAE27B5B2082ABE06F9CC314EB1A6D6D5D2EF70CF63E4B7E30C579D",
-    "adv_salt": "31D7",
-    "plaintext": "54A39E955424818A57DD2805AEC1498E4AEAFCB811F7674DD8A6",
-    "ciphertext": "88D16368537C2BC70D734AFB07323AAF37D51E3995BEB444FCC4",
-    "metadata_key_hmac": "0B77F68966ED8A0E76893CF64C90BE597268F8FB56412027D2BD7B765FC87BBE"
-  },
-  {
-    "key_seed": "C4473B2E6E2F31C7736B118AB5D9626B78CAFAB5480C64FEF40F6EA23DBE7A3C",
-    "ldt_key": "C657825718A2C746EE1C90C18A2CE9B6991C634A1C5828BD45510B8A6BFEF7F204EF2EC7C1402C5193F94691F0D2B2FAD84BB4BACCA761C72B1874E289210B44",
-    "hmac_key": "96BEC806C7FC3EEA8B19C179DED4A2EB226B1E8B3385066CFD24FD9A837BDEDF",
-    "adv_salt": "FF46",
-    "plaintext": "A3841B00F0220E916B5ACB7A71EBC7B833108A66618304201F22C7AADA",
-    "ciphertext": "9E0960626A61097A1C4AAF5BCC94B90898FC14D8B51FE0617E4B6BE251",
-    "metadata_key_hmac": "53E4FBC415ACFB8FD438618F6D815B6A8DE77344922DED695FA12BBC9B5FA44B"
-  },
-  {
-    "key_seed": "474FECC88AEC0CFFD59038B1E48667A15F1983EED00388BA3321B78EA28758AB",
-    "ldt_key": "9EAA8D17CC67A89956041175D5171BB0DFF028C5E8416BF6E822DB5BA09E2BF20A45811DF8001CA0214523292EF22370C91957CD8A5EC05F5103730AA13E1CBD",
-    "hmac_key": "FEEE6A7BCD8411A7FB71A8EEDBE9CD0684C44AF977A93807878D340A001DFCFD",
-    "adv_salt": "0C48",
-    "plaintext": "9B1EBA3BD9AD1F7958C66E906C8DB21F5AAD8E358075",
-    "ciphertext": "D50230DF83D87F0CE0F37B41DAF6AAEFB1C6441E270F",
-    "metadata_key_hmac": "A2F7759E8193B3AB8EF0D76BBFCAA514C9C6096E81FD497C08DB5098642A4CF3"
-  },
-  {
-    "key_seed": "6FDBEF4C15EAEFF76DBF6458FD5B6C15D6A1CE68348B0CBD7820AEED7E9A4E35",
-    "ldt_key": "DB7EDA05A1DB8DA743EA148B1A6F0EDD0130D966881DBDF830581DE9F5222E78C7459F0D3C3A0E9D73F1038B6D23BDD7D64BCE352F3FF2FE115AE5E65F31729E",
-    "hmac_key": "2E60F73C97AEDCE38C36582822BDAABB612D79845F7724B0BA576E2DBEB8A2B2",
-    "adv_salt": "E99F",
-    "plaintext": "D60F851EC8897A4B8DACA7DEFCE0E4152EFA9C54",
-    "ciphertext": "2E16D4480367179CCF871E14E38CBF2FA2171134",
-    "metadata_key_hmac": "B8BDD63EDB687A7B8EC7B8BC8967A799735AECD7E95553B6D32D46973177D3E4"
-  },
-  {
-    "key_seed": "F516557262D28E66F09960BFE8564A8E1CADC43DBAE7CB0E82C18B2BB004ABAC",
-    "ldt_key": "7CD3494F9870628BFED5FFEC6E621C94B7A1ED4BEAC89C64168CFD3B7110A36F2E0B3CFA1C2C33644859514B17C5DBC7254E177ED5B4D4BB5526355C84EBFBB0",
-    "hmac_key": "5396B5EB45B5BC6E30BD4F1D7D692ADD9CE70418C8B4C5C3B2194E1BB3327E89",
-    "adv_salt": "C01A",
-    "plaintext": "F002C1693EECC0C20F773241EB3FB3C4",
-    "ciphertext": "BFD413373158A3D367C73937E9737405",
-    "metadata_key_hmac": "9F5F641CB02A81FE6D997A87762E663352988DD1A09A093AD2CE61C0C25AE325"
-  },
-  {
-    "key_seed": "1985D7F9F9A11F655B3C3EB9A81B9E90A1C959CC1427CBFD618316903A51DAD7",
-    "ldt_key": "A73191FC81DB46662B43A789A685AAA43390AE39CEB11C0C76D43F2E6B6BFF0DB177736F004010561FEE902400904A5315A28402CD7444AABCDC82581822BD14",
-    "hmac_key": "F44B2E47D31D57FE69C622110D03ECA496B3AAA6D69A590495A6B905130E5244",
-    "adv_salt": "85E1",
-    "plaintext": "465B4E165C848033E2E2EC74831ACB11B9EF",
-    "ciphertext": "E135AC0571809DFA95E34512560A06BE1947",
-    "metadata_key_hmac": "DF267B79E8A22CD628EA16C01A64D93603C900D2B388E581340042FEFF746B19"
-  },
-  {
-    "key_seed": "9AF3762CC76A2A6203862DE8D2FA67CA0A73C01075039185177DF0414D56D421",
-    "ldt_key": "4764968DD0942D35868079E0AD4A1BA71C8BDC5D256E60C8E57AD84238BAFCD2277C80BC1EDF683C7BD26149680314A79FC319FBB4D90F1537AE421155C98B71",
-    "hmac_key": "EA15DB5934168ABC0623D13E74AA98097BE49679B953CECBE96A50D02AF9ECBB",
-    "adv_salt": "89A6",
-    "plaintext": "B134F7F24998FF5D1AC5A2815B0EA229",
-    "ciphertext": "53A50DD852AD8110D429CEAF2FB3EC97",
-    "metadata_key_hmac": "C69C88641A083EDD5659788CDD5D3A097E12A687200B84899D8681A8F7678653"
-  },
-  {
-    "key_seed": "76FE0B927C490E294DA486F85AF5DA0EF5CBD230BA8652BF3271AD0E8800B5D7",
-    "ldt_key": "CE41FDAFEC4492D121A51419118BB84B2ED584C4AC5F8FB0FEAFD6C89C8E293D74B1241C99F53994254753751F486F013BACAEBA763C7FA90E4AEEC759A540AB",
-    "hmac_key": "64A57BDF04F5C58FCC5BD089DF52327FB4BA704C1340E9DA91E2AA8C81F33D6A",
-    "adv_salt": "4A90",
-    "plaintext": "E29B0C659BD28FAD0E037818E6A8932799C2276C6E114F5F7DA2",
-    "ciphertext": "99DB117C981C745A22C8E2650F5A900E2FA9C97E65511F1D9E23",
-    "metadata_key_hmac": "3C362A974C82E7449CC0FC9DBA40A324879F98E6CF7BA38CA3698F27BDDFD426"
-  },
-  {
-    "key_seed": "9DCA3F0D2FFC32BD5C68951801F3557549E1AAF83A21679C2E6C40EAA0DBF6D2",
-    "ldt_key": "56ECF11E51221557A982192A8F5A3D36F5C29A536FC878DDFE470EBA0BE41C53C7B8817C24EF68EE70017D76EA7E44455438FB14BAE69B224ADDD6A3FDC306AC",
-    "hmac_key": "3B9A4A380E58FAB2442761DC37F6F59DF05CDFAF09422CB48FFAA6383C6F3361",
-    "adv_salt": "A4A6",
-    "plaintext": "CB6409DC49FF98448C088D4A85CDD6DAFE00C9",
-    "ciphertext": "613B69F24FBFF245023185D0910147158051B4",
-    "metadata_key_hmac": "648100F3DC460E5ACF5B55EA93724C3A6171C7AB188D9A90335E1A28D96BC43C"
-  },
-  {
-    "key_seed": "25B06DB454B451879690D09CA4ED02860760E1CDCBEE695A61A2B81C3F386C31",
-    "ldt_key": "A3CD706DFFCDB4B801D147E9742833947FE9595A75A411DB407EBE5CAA84CC669001725060D6C29F50004242DBB49E13A14DBC79662E253943CC1AEAB6612297",
-    "hmac_key": "367818C87FBEEDA9AF02EDDF6794A4FBEB24FD1889AEDA80614AAAFDA0F39E0D",
-    "adv_salt": "B7FF",
-    "plaintext": "91D4E70BA0BA41B5168A89BC88C7BA1A42D27EDF4AECDEB29E",
-    "ciphertext": "CC6376D78FCD2AAB7F2B3C288BDAE96938C69BA2E17E82C7A6",
-    "metadata_key_hmac": "EE553F38B53CEFEA846416F90466FCC2C828FC549AC0CA22EDBCCB3664B58EC0"
-  },
-  {
-    "key_seed": "1A9E7CF67D957D6C95C4911B7F13B51B03EC8E32A6452E8AE0F5CB5C237A9F58",
-    "ldt_key": "F0628474832A206D611FF26047B9278C20F051B814C304ECC3C965C9304E8899105C85C2F8AFB290E0331B884D2CD6E7F4383CFC2214810B3DD83BBF58E6EF58",
-    "hmac_key": "A0DF5EF1724CB80A2486FA2B145535EF01451A42AA5A62F10DBCE6C55DA055F7",
-    "adv_salt": "54BE",
-    "plaintext": "26C389453C0A3456D8D18CDAF04CAD2FD8BB3A0D2528113A3BD0",
-    "ciphertext": "1C1540CB04BC79693A9FA3CB3B9E8B4BF415BB327D92641E7204",
-    "metadata_key_hmac": "E52635CEE4261B063C3D8C0DF1C0A0F040CF72464DF03924051533BFDBC3C1DA"
-  },
-  {
-    "key_seed": "DFDACBEA9B418D02783890D8A66D81061C9F1F09B7B0E5FB11D9C7F9AF35F4AD",
-    "ldt_key": "DCC6C3DBD5EF8F7AEB877DA03BDD7DC945063E79A5280C5CD8EA518721EF2A4AECE83076F17A6B38EAADA6987E3C556B3AD1FB7D8E9BFBF63A2AA84A06AE59DB",
-    "hmac_key": "73E4E1ACD47A14115E5611804F7FD90B014DB454049FA6D5257B0B2F96292046",
-    "adv_salt": "642E",
-    "plaintext": "C3462E884C31AA6FBBAE14F8122B770F8EFE2CBB268026B283",
-    "ciphertext": "E2FF6E14DC25671EDE98BE654CAAEC991D52F34DB57A7662E4",
-    "metadata_key_hmac": "1D1F7DA5CE37C71C6FE067FCF8FD3BFCAC432F9B2F2D06C0F5E62483F9F1D0C8"
-  },
-  {
-    "key_seed": "5FC38477B52B0DF63A72767E8C7F84486589B37F310E1C81D6AB646C557BE94B",
-    "ldt_key": "26313C24B59A94E324988C760095C13AB03A75CB3410E047530177E9C999B76160D699814FA4CB9D800962B25805CA910EA3870A5347230382A2069DA5B87A77",
-    "hmac_key": "59ECEF5646AF89D199016B71BA556742CC361C3535E1B076818E474CEFEC306A",
-    "adv_salt": "0683",
-    "plaintext": "53FFDF24B36D08AD8BC90CCE772ED7167116F2AAF3375C87",
-    "ciphertext": "FB2B693185BDFB21DBD3BCEBDE0C88D057C8058500D2258A",
-    "metadata_key_hmac": "39578AEBEF9134108001199C0F5D99EB698986B2546A514EB95021E9F545124E"
-  },
-  {
-    "key_seed": "7BC0109CCCF62E83C50895C7E43A4E571DAA7AE8E65DB1D1EC6B3D0933A71AAC",
-    "ldt_key": "13F3CB016E1B394DDC3FC5EFF350773995EB6141F9CE4990C908817B901BED2BFCD9729D3B798929B23FF752F256505315AA79F2284E6A1B07554BAB192A9654",
-    "hmac_key": "84A6B032A7FDAAE377AC1FAF708107D765FEA8F9A050D8F8A412A6E2F3F08D87",
-    "adv_salt": "29BC",
-    "plaintext": "7A2F9C221913A54540F9386C20594B8B807C0B411DFD",
-    "ciphertext": "551EBD847E4F22D868AD37F1694AB7D5FF0A0114A4C6",
-    "metadata_key_hmac": "7ED99D0E49848BBC2A423E5ED9C3DAA1C29F1A9222A2FCE31FE19133214781C6"
-  },
-  {
-    "key_seed": "6556168FF9D91B457BBFBE45618C6066EE1173ACF423277876D0803764B378A1",
-    "ldt_key": "F6346C417BAEB4E4B83205503F81995AF980B546725B5AF8BEF3D66E7BDDC4C9D10C9E6274B2BC37F245D32BE9816494575D33735D0E977FC0D808589DEC51C7",
-    "hmac_key": "B685C810EF6A30FCEFC6B94560CD351586D534C98822A48882411B01D77AFFE2",
-    "adv_salt": "2EC6",
-    "plaintext": "AD9B7773A268252FB51D78FA76D1CB67A116FEF859",
-    "ciphertext": "0326A51220BC1AA9AC4CA940383EDA8F52F7EF9B68",
-    "metadata_key_hmac": "8D417A9AFF37F839EBA324B565BAEF9A46E8313ACFEF69B286E5D61F189DB28B"
-  },
-  {
-    "key_seed": "F6D1B4EB6C21BC252E825A854DABBC246E330428BB06FC8DF5EFC0F5B5889389",
-    "ldt_key": "0B96F3063EC9E34A6BDF15AE7B674815B5B17F56ACD7EFF47DA2A5AE7D689154BF4E59955A1A79CE2F456286839CDFDD14B4A0EFC1C2BD3460DB8315E3C40D6D",
-    "hmac_key": "368E36531A75F11087698461D3615845EA409507B6C9A119B6D50D632F6D7BC8",
-    "adv_salt": "CF7A",
-    "plaintext": "9C4125F3175168B367DD6EA3538D9D9EFA41",
-    "ciphertext": "5E890D2CD7595B8171E7C50CFA3097E69C5B",
-    "metadata_key_hmac": "7171FB1B8F2158A50DA45A423A42BB72E3233F3D8C808D8D0C6DC2E1E2D6895E"
-  },
-  {
-    "key_seed": "F6C5D8E0D76A826F04E4DAFE0AACB059BB5940CDCACBFD101FEAA4172A140AA3",
-    "ldt_key": "D790D1A0D421EF9031D83033B2A6C7A1C6AFD7AE0EA9B9900BD6BF74E7038CDC5A36FB04548C2C833F3458E4D773092356A1053184C0A10B67143713A1208F06",
-    "hmac_key": "12C5A42D2BE721E5BCD7A5C374EC4789720DF2D5A6B71157D2D2E6D23661CFEB",
-    "adv_salt": "34AE",
-    "plaintext": "50FA211D4BADCFD3E6965500B45BD4EECC2E8874EF45FAAA8938E7BF",
-    "ciphertext": "BDDB582623B1B58C597D42F549CF3640261A5D6FBE88C5A2AC2FFD5E",
-    "metadata_key_hmac": "BF2C041FB3CCE7A04AF3425E6ADFBA23E30543C7C1D928028652FDF6F2DDACAF"
-  },
-  {
-    "key_seed": "10285759BBA3CB207EC111088EBC49D081092DE814F1C9D890E11963475BE0A3",
-    "ldt_key": "B1A6F6EFBB88624AACF468774C77389B97A49D93481338B11D679A3F1702937EB044BA5421D81EB2027489DD73DA13E45B10FE29792E7F9530AE06DD332AE0DC",
-    "hmac_key": "361C19E2B28C569B81E998F28D0CB80FF5BC914737FD335A045F5CA01C721C80",
-    "adv_salt": "5EEF",
-    "plaintext": "218A25CAE4B4D5BA6C12641906C9AB50A51D2323E71B2D9B464D43B5",
-    "ciphertext": "2F934A6E4466228DF0F4D652F95E5650A4B9B9789CA4D78B949B7919",
-    "metadata_key_hmac": "9AF6B6E76D98AEFF48249DAD198B2358512A10C8C2EFF566120FEF2429DE3955"
-  },
-  {
-    "key_seed": "220BA7962D605685F38568E09EA5098A19AB872E170583F910379EE94CA6DA1A",
-    "ldt_key": "93F036E1B7F015A73B47F8E6C16AAC1D5C6BC179FE6759C453BADF5367F0619A6FB1025FE0CDBA7745FE83492D345AE5024431EEA1EED8218D1E960B54CB4278",
-    "hmac_key": "17FE7160E653F4E0FA0F09DD66AAD32CBCD9F9F6BF83E2CE40A99428079ABADA",
-    "adv_salt": "8110",
-    "plaintext": "4CB2A60F7C9A1B845D1680C8B9375C51E6CB44E61886A3D0306F",
-    "ciphertext": "7FBEE40B608D1D7052CF17B65323050FB0DE90DF6B38A8DA5EB5",
-    "metadata_key_hmac": "59F2040D895282A92F6531596BD351A41E717D5CA1764C8CCEA8983E905D0708"
-  },
-  {
-    "key_seed": "DE213CB6D5B8B7129DAA8E26E27E6A0CB1930A01CDF9704F575DBD76413EAFAE",
-    "ldt_key": "27B7F2836600D37B972D9A27280B42FCDC15CB731FA1F4B3A3F0C374E5E4AA9AB5FC246BBDE00F768A8CD5E304B80297A20EF9DD499531B3E2C2CDED0D540FAF",
-    "hmac_key": "BC34959C3219C01512CDDD44C862CC9F011AC0F23C3B272224FF02724EA07D52",
-    "adv_salt": "8B44",
-    "plaintext": "9B24865ECD76F202D8153F696C04C1879B1E3784D6D49E972CBB3A",
-    "ciphertext": "9BCEB06CE8AB8F2E72FAC2966B523640C37D76DB2B2F16AEFD0F8D",
-    "metadata_key_hmac": "94FC4163DCD7FF3EBA6856784E89A291862F204A7D08E6C32F9B4E6DB6BB1DDA"
-  },
-  {
-    "key_seed": "0ECD554A2A723FE725CFC2705C80CEC856C0DE19FB6BD7538699B3C24DAA0E58",
-    "ldt_key": "FC21A6BCBEADABB421C2CD1050D49D6CB3B7CAA2948E08948236624DEB01C84DAFFB364E6FCED0D97A554697EAD8F1F9B10B691240E874D123C6090EF591B14D",
-    "hmac_key": "F77F5C04535B91E72F42C2FBB9119D11F725485C89CD00101B54C11620B1D859",
-    "adv_salt": "6C04",
-    "plaintext": "72D73F9B2B97C231B84767F800F208F40FEE06B86872543908BE2C",
-    "ciphertext": "756D7C6D7430162D032730E90CB7C5EF67E512943835BC96511406",
-    "metadata_key_hmac": "8DBAC803E7B33983AA84C8B4E46C6166DB7EC8FB9945C42B322FE3EF5293BC25"
-  },
-  {
-    "key_seed": "D7C8847C7E8FEB4DC442983177006DA3175CA0BFCE52F0D985F96031533252C7",
-    "ldt_key": "BEE93D5D4B768CEA9B85A7E8CCC16FE5E00DD641CC5B705D87B45460B2D6D5AEC3772808DF95BD3711C3B5820674B30F953FF79F90F5CBC5228412CEEFDFD381",
-    "hmac_key": "9FD237BDAC010B287790CFEE3E8B68D398155C7F0949160E0E93AD7A36F647CC",
-    "adv_salt": "9A38",
-    "plaintext": "8E559E7BF8732B31D60C3EFA73223EAB696FF05FCE0B2B0DC0FC9ECCC374",
-    "ciphertext": "165A73C30278CA40D354FCA5B7FA2F59B03C8C210034081C2DDC18FBE930",
-    "metadata_key_hmac": "95EFBA62585B0118AAFAF540AD9BB9D6BE0132D5156307BA67B1FFB0BBDC107D"
-  },
-  {
-    "key_seed": "B00C7848F767F1FBB5A0225C372BD51669265000F7EBA2A9D625D067DC1EAD7A",
-    "ldt_key": "7EA6F36DB004694034425D297C579A7DCC33D6729B139E77461E65C774A94120FFD58EE9D9CD0044CE0BB288C3B2793323DD7EA112ECAACD788B44E28080D70E",
-    "hmac_key": "ECE14011D70AA79E61490480DB5AB1C5ACB53EDE41A50A5A225D6F90E2C74521",
-    "adv_salt": "4E1B",
-    "plaintext": "BC2DBA155EEDED7C96977A0B0CF5FBBC79",
-    "ciphertext": "78C1642FE8C19833B2E7AED931A293B292",
-    "metadata_key_hmac": "F7B56B39CB7C6B3F9D3F2AE89EF989E2F664A1C25D2731429CC27B21EA59EA56"
-  },
-  {
-    "key_seed": "EFF15E04EDB3E9CBFD83EAE7F382157FF3530FD7E3F8D8A54A0A363C9C6470FD",
-    "ldt_key": "DA2189DCD00721E778F6AB10606D850FAF706A5559ADE0A42325AACCA290C70AB6B33BA9EC9E306EB05ED7C663112D269ECF3826829C1065E81C66ECDF583AB2",
-    "hmac_key": "BB5F27D52B36BE2D25C86852447F4DC8C2E2DF177A09C824A06C1AC5B88CABB1",
-    "adv_salt": "A192",
-    "plaintext": "5E87F5E0390DAA539CB23D30FA108B5E5E",
-    "ciphertext": "81B198280231AE878252645A008ABF41E6",
-    "metadata_key_hmac": "8E0FCDF5A803ED0A0FE5C9AD1AC804021AB639DA0A9FEB446AC92C574C42C65E"
-  },
-  {
-    "key_seed": "7A5B7C7EEF6FC42ECF811708D4CC50937D1E8E34A0731872300A7A345A401DF4",
-    "ldt_key": "8684BDD009C393BB0D75BAE4DFEE01678E0C47E637280DA920A07C2AC22180145DE8B6417635014852252BCEC39541C16B61D7A35626313DD3AE55F700959E0C",
-    "hmac_key": "9BE6EF13D2F5DF52362C5BFB12EF09100F8813389F67E52C667C20FEB919EB7B",
-    "adv_salt": "A942",
-    "plaintext": "F27F913224CD9BC58030F048018DA4B3667B57CD1CC1ECF458A927F5D0",
-    "ciphertext": "7B99EB504853A634337AA496326F73DDEF2D3ECB167CF63296978086EB",
-    "metadata_key_hmac": "3D3BE659C937DFDB2C6A4F46F9442C598E0D9B40E157AA13DB32173ACF3DB2C7"
-  },
-  {
-    "key_seed": "5553AAD088C78EDE5E514B5E26661460D80D08E01FEF68B405B739563FDE1EB6",
-    "ldt_key": "2A5057B7BCFB1B7C0CAA2EA015D4E12FD89265FFF6CE3823646A00C7A08935DF5E5A29840BD122F63F63C18A797B3E9602335DB08359CE05D77F2FB6C57380BC",
-    "hmac_key": "75DFEC14AAA30E575AD765878A7C176A6BDBB4161BCF290C9E43C7318BF22995",
-    "adv_salt": "EADE",
-    "plaintext": "5365B2E71530B9C234A892168628B7DC9B311EEF7C1BB4DA30",
-    "ciphertext": "C51ADD62C74475554288701418DD7D452EE98DE6167DE974E9",
-    "metadata_key_hmac": "C01B4C26909491F1F5CCE36C1F1C1B2D9D016D0733A03A4665FD8199C77832A3"
-  },
-  {
-    "key_seed": "342998189AAC53997615C11612B54678E8E9473130526C8D5CA96DEB4FD09952",
-    "ldt_key": "90D25EE1ECF1583D205C967D9261ABF68992DE0D764F839B146DA4F4D602BA585E41BB6BE6D5B2E9E84B1D55A99D54199DB03F46658C29D7721745F49BC0659E",
-    "hmac_key": "8DE8826FA0A83FEB3021CED9328AC6A0C524359315C07DA092A348B75689A693",
-    "adv_salt": "978A",
-    "plaintext": "57100E74E9354591C38865EE216BD6F484",
-    "ciphertext": "AB4AFBD82E5DCEC98D78F2860778ED9F59",
-    "metadata_key_hmac": "A796997AFA2C79726C851F15DA52F09018094BFCA62DE9BEF97459B291540D57"
-  },
-  {
-    "key_seed": "9B1AE59DDBE239E528F9F5C55AFDCDB149EF23D969139E7862DC7D218770DEF9",
-    "ldt_key": "9304F19DD2FBC4F32A385354B194D7D218CBCF179969BD90D16765ED59C71B6D38D972747455A19D148C6EE16029AF512338AFC484189774FC95951610A78C81",
-    "hmac_key": "8DA66E56659443CF2E7B91DAE76CB36FFD42CBBB10ED13B210E86574FE19497A",
-    "adv_salt": "CE3D",
-    "plaintext": "66F3A9EBB3C43FC6CE838B75129B30A6F85665A4E9029582F91C18AF",
-    "ciphertext": "6EEF47363208CD36D82B6602061296A397DA03CD733BDDE5E9DE1F00",
-    "metadata_key_hmac": "1DD6B7C062D32E173D8CA2E67F67582961852179ED024C40987B5254D7A50E95"
-  },
-  {
-    "key_seed": "5A0477CBE783AC090752F8394AB04DCDB79D999E1FEED9E5695172A345BC541D",
-    "ldt_key": "C6C3F4BDD7B4A036F7953DFBC3ECC22CBB8C55426AB21AFEB6CA45965663482EA97EDFA5A9045D3128726CD276054689A9CFB150C55953DDBE197D52442DB16A",
-    "hmac_key": "AB97F7D5A5C3205AB01FF7BDF493E2362974EFFF5A651EEF4F2ECBEEF18A0FC8",
-    "adv_salt": "61ED",
-    "plaintext": "EE7981B54E1C17379A9E232ADEF0AF0354FC7B49610F7430360385CB",
-    "ciphertext": "A6A5C88FE8864D61EEC83F47B2DC37F053BF2E5968DA36EAA7A187B3",
-    "metadata_key_hmac": "60193EA52A588B2E96B48771772318274A150485B12E4390A30F603612514DE6"
-  },
-  {
-    "key_seed": "8378C9D73E14B679A3B3321F347D185ADCE9AA1824A1D9C0DA1089B6B663FF53",
-    "ldt_key": "F9D2090BB4CBC921E7AB7D7C37D9D988D53DA0562256353002E753E28FE473DDD7327EF546EEC5BC81E32C6F074DC21E9B901BA23A2B1B66CFFA12452669EBBF",
-    "hmac_key": "E7099C86A931996822238596F0755FA6F9AD36AB1D79A56A7B1DF9A100ACEE66",
-    "adv_salt": "49AA",
-    "plaintext": "E7321E12D949C101CA8D7724C1C739068339EA83C6DF7FF23B6722",
-    "ciphertext": "DD266DE58B819279AA5B6DF6C815718FFD9F90B3CE1B41CE47FFBF",
-    "metadata_key_hmac": "895995674304845A63AD2FEBDB297E92756705A0A4AFBB6D63A8201FA9433419"
-  },
-  {
-    "key_seed": "6519CD0C608E0C26F2BB415F84DB1C06E38040908C1AB1CBA3107BA723803102",
-    "ldt_key": "FCCC5250AC180FE5DFBC6367F20AD7D01D9093065264B9BDA4FEE93BD6FEDB4CF975AAA2370E26BC5BB45922DEB7D9F70C1774F3C99663C6C7361338750C93AD",
-    "hmac_key": "5B859752B379C6A0F6DCE584EA8D269EE0D05EA427D2273F477938202C21410D",
-    "adv_salt": "1BF7",
-    "plaintext": "B9B0E3752451B61EE55191B06F132CE2B820E6C2B7E3E6E79EF9381EBAE5E9",
-    "ciphertext": "5283C33EB405CE3C446F55009EE7C2B7437278AE93053E4A485802110D90F0",
-    "metadata_key_hmac": "1C3B91B68D6D3877942FE09D81FE14B8194E81DF9F5BE5D64DB48E5CC6483AD0"
-  },
-  {
-    "key_seed": "3080ADB8C31C7E86F143516E7AF784F5C8384492484DAE622EF38165F1D616FA",
-    "ldt_key": "C315B24FB8D12074330B44B79E5D1A55AB2FCDF5DC657F94C668269EC6A1B438F43CB997F2EA99BBA7BCE882BB1599EEE2186FED20F3B2E9AC060900896D1FB9",
-    "hmac_key": "96C5CD3A6F2E1EF2F4E4E3343130D3D6E8DB4E42EF827A9F18EEC1CF3E55D4BD",
-    "adv_salt": "1A2F",
-    "plaintext": "E6FD8B3CA9C82B8D1D0BFA1442BA8426A821C6AF8498FCDE337A0C190CEDF0",
-    "ciphertext": "7431DC4FE965D7FF3F7E24C35B373AD9F0E62151C1C7F659660B3918E00D2F",
-    "metadata_key_hmac": "88B0C48D71E99C49B7D4E7DDDAE5055EB9A653335F3A9D0BB11B0654D18C7833"
-  },
-  {
-    "key_seed": "D3328C7913B9871305EE82CB1F2C9041B870A2E13DDE9DB6875301A14053B417",
-    "ldt_key": "7DE72172FBEC90BC3B6D1EA3D8337C61CF4507761BB0AC07F65EC0E78F9FF0C5C31873F917146FFF7B0896E051B3BD91B9EE6BBF427D95457E5AE321480740EE",
-    "hmac_key": "FB780A0BDF5D9984E087F68F67EA5623ECF76CDDD01329A56DBCACCF26F73E40",
-    "adv_salt": "DCC1",
-    "plaintext": "A59B5F0D5EAF92089A1730DA4682A3496E54B64CBD9A807EAA3B",
-    "ciphertext": "AE664C161DB820537E64F09C548B69C3C092F636E21389307114",
-    "metadata_key_hmac": "A2BC9EC43EDC2D499A6880FE4F27625FF49D1A495B849EA708EEA92304229BFC"
-  },
-  {
-    "key_seed": "2803E63904B8A4E5AFEDE903AE77E6962E2E299F5DF01BC6209419873523596F",
-    "ldt_key": "CCB93C500CD50E2F6CAB777CDF5394ECDA8EA693714846BBAE0E5E4A1C0E959146330C3E6CE82233768ADADEFBAAE9BE922CE2F5995335308B9D6A522A122FE4",
-    "hmac_key": "6175BFC44BA2CCFF262B01E1E20FF8968E53B355BF670DF9D2C22F3174DBFA98",
-    "adv_salt": "C73D",
-    "plaintext": "9014E2E9C22C6D9E1AF3241E6851B353850E390281F5483171",
-    "ciphertext": "A143626231C3DF6F08A1F7C0916F3DB97F7195DFACEB8E9728",
-    "metadata_key_hmac": "139205577C16628258160C63520323131D52C4979A308243EF2C6533C3E9ABE7"
-  },
-  {
-    "key_seed": "C3D424CEE12CF6A80292FCC88963264DA1CF1D41F88C7B4CFB7F38A18BEC123C",
-    "ldt_key": "EB9ECB3EBFC49B2BF206542BF49583168756650160DD6416FB31BD31BDCA2EF5920E803DB5B07A51637AAAF6CEF151A7A14AFA201C9B5CF56BA645B1D8D19282",
-    "hmac_key": "23559CFA05E4ADE5389657DD8096ABDC8A790AF1CEBA0E17EF5A1F8D033FE6E3",
-    "adv_salt": "BBF9",
-    "plaintext": "B90FC627E9FFF36DB6A5C238504A501F8C247B41A433EBAB",
-    "ciphertext": "BCF8E38285A87704A542261EBCC3992C66CE39C52901103C",
-    "metadata_key_hmac": "80DA7A5ACB7DBDB8F107925C892D9C09F1E56296675637DDA3601A77764489A8"
-  },
-  {
-    "key_seed": "B459A0BDAE3FABA1A0483CF35E2521751D24409F2216B1C74E5A19650F3A3949",
-    "ldt_key": "1C9FB660F68D61A43FDB5E8129C8C37ECF3DD5D0818FFBBC58C6E531C4810F4D49ED5D2143CB52C23328DD20A721EA8079A93CC3396EAA3AD2B70BC441182ACC",
-    "hmac_key": "F527F4313DADA66F7A003DF492947D52C0D54EE49FCF5FE48D5F8F5FBE9C1D78",
-    "adv_salt": "730A",
-    "plaintext": "EE2F0C9FB9B2D4DB8E0F531631FE8BCBFEB927F025",
-    "ciphertext": "442EB7DB8F4EA34CE86BD8CA8EA4C65D94E6E19F8D",
-    "metadata_key_hmac": "2599F8E42889630E883BBF901E5D531AB15EFBF13426B8913E07031673B7BE07"
-  },
-  {
-    "key_seed": "49C0D2390E790C20FDC10E43CE517F7E3C8D41153C73A579C0EC74FD4C395941",
-    "ldt_key": "9BE90C02466954980B34A7D1DBF89EDA5095F2F68E917E0FA69F18FC62E74C25AD79B613F24C870B7CC368790E2877D8CBA2B747BB4F0678D615B58D6D5189E7",
-    "hmac_key": "87B2B86C8D1B86B3A4C01F6EACF91621ACA29AE73E4DF48CC7D7712C701CEE40",
-    "adv_salt": "EE34",
-    "plaintext": "950744D835729C77480148DD09D69EE20B162D8323",
-    "ciphertext": "F5955F45D7BDB42C320EC3795D52F2F72CF3B044BB",
-    "metadata_key_hmac": "C85280A1BF73031514C82D70CDBEBA53B415724FBEF00A8168745F2237128EAA"
-  },
-  {
-    "key_seed": "0E7BCEFA797736D9F91BDB8293FF51A538D665CA79AF62AF4893EA83A2C600B9",
-    "ldt_key": "8D5A795EF8E36C6D7A943F3B171B98D5329796E1C2D535459744E85149D1D81151C19A04C5C38C2132A71F533CFF0DC5DE15F803A1AEF6FE1598534BF2A7B542",
-    "hmac_key": "53EB6926B56AE6EA948729B0196039EAD92AD9564A7D1F7FF31B4A4F5CDA35C9",
-    "adv_salt": "6A26",
-    "plaintext": "3E94D9314FD1E2BD2AC61C7CF03DD3EB5CC516B0679C",
-    "ciphertext": "6B6CFE9BB8345ACB4B6C7402EBAB6F10CF9D2C930033",
-    "metadata_key_hmac": "9A6F3EA0B48FADCE594AF0351DFD31859F9DE01BBEC53B38A6B4620D86195F73"
-  },
-  {
-    "key_seed": "DC310BF157F0378144A7DEE76F5B95ADF3A7A754540BA7312553D81624AFA1B0",
-    "ldt_key": "44345912C50AC5A7C244ED31D7CF8FAB80590E4363AFB307255F0FAA5D40DC8A0E777E3B4BE153232F9E8F80355814933362C60F90A4D33368CB86E1665264AE",
-    "hmac_key": "A046F6ABE8E0DE35BDBF9B574E97A70CF0C61D98A731F81B68FA79B497602768",
-    "adv_salt": "342A",
-    "plaintext": "84195311841AC7F5544E44C7077F7F76E4",
-    "ciphertext": "9C3FD5C099447E92B60CADCF012E305008",
-    "metadata_key_hmac": "3212463C6B39DAD5EE0581870F9EC642E9D42CA5C70149149437857331152AE2"
-  },
-  {
-    "key_seed": "8B7FAE7704B7EA2C6136FB5D18217490C65900008CE4317A75842613BF3A072D",
-    "ldt_key": "EFB0CA7F9009F3A9EC5776847A4DA88A70DBF28D16C6B7A2EC121C0F7F45E88EB238BD2481D21A675F97C0F0876F27FAB5BD5B1B7A69BA7A70F332F19BC3AA37",
-    "hmac_key": "821FB7E321CC7F3BE691AE146665D3AEE0BC78EF7230B0F1F6A3A59336C6C439",
-    "adv_salt": "C70B",
-    "plaintext": "778CEE9F3BA2EDFF226F4726628130293BB4",
-    "ciphertext": "5CA6A77F633C09055061928ABCEE07C9FE65",
-    "metadata_key_hmac": "8853181CDA041C90AD6ABFD39943FF206EE15D52D49A165968B0EBAE3F1E9949"
-  },
-  {
-    "key_seed": "72ECBD3EC62AD51645F38FE2FCE9417602C5C0F9F0CA738A35C41BCC19EC9C5C",
-    "ldt_key": "67DDB32739578926E265C0C58FAE0A0DE3C790760C6DD1FABB8B5F24CD862A26B7AB84AC7847B65787339C444B432EB7525F73F2EB71108AA9F0363885BBD742",
-    "hmac_key": "72209D27956478D557A5A75E2F22D76152365FDDCD0608C5B79ABC0232F20856",
-    "adv_salt": "762E",
-    "plaintext": "247D04D1501578C3A2DF020E3738C224AB093577E2DC11F4EA2E",
-    "ciphertext": "4FC96DA6EE8388A22EDEA71A0B6E24B9BFF4E22092B13E02E187",
-    "metadata_key_hmac": "FBC931FE290F92097F690A02B11D69424C4325216AF2EA7A65168658FB371A1F"
-  },
-  {
-    "key_seed": "2D6D2DFE7AE6EB7FCD45DC6F56A6CCFAA11F2C8AF5295606C715850F1271A5A3",
-    "ldt_key": "F8C3EDFBFD14F23CC84FCE85BF5CDCFF54403F25E936CBD3022E55C00EE27623DA0BDD8FA74BDA3278371073A4E72B54C3774E014A905BC5E98E58F884DF9240",
-    "hmac_key": "3985FE8E243BD65DB0AE4AAF3185E705B75AED530C02A20B45306AF640F34B01",
-    "adv_salt": "9433",
-    "plaintext": "267B1E7491E02C1A2CAAC5AD4B7F322CA63CAAB688",
-    "ciphertext": "28190D998D15F1D090EE151710DA6F0C7CEC7891A4",
-    "metadata_key_hmac": "B42BE1A6889A7C28348D831673BB02370B54115B7C67A0B5E374ED1E2BE38217"
-  },
-  {
-    "key_seed": "9ED92E9E5DB3B44FB6567FF3C9C3458C4FC3121735035BF8E22E6E5A4D8666A3",
-    "ldt_key": "CE3ACFFCE7613342408FA6B3E77B1B87C820DB54D6A01DA651621F98BB4686D85D7DD6DA7D7A1B365D2AF53F35157F87273A4B27047EF7F23B38FE878FAF0012",
-    "hmac_key": "D262223CF5C87749958EF430B6E0A3740CB5FEBBA47B0946AADAAD739C344CEE",
-    "adv_salt": "9AF3",
-    "plaintext": "AFBFDB0819E093AB27853D2BA662D162DDD2C76115D29395BC34CC1D235661",
-    "ciphertext": "B702F556B5C400CF72DA68CF031FC048B609EE753549DCD0BBBF3A9870C2C7",
-    "metadata_key_hmac": "C775E00854CD584161F3CA8BEEB489C49CC3FCA7EB072C8007989A12F3EF0E6C"
-  },
-  {
-    "key_seed": "29160E56B32F35828CBCB90039754D42E8A95514068C3760F8A63A4DCB32D030",
-    "ldt_key": "DFBBA94D4CA6CAC685ED90B8C11EE6BEABB2F508A0C3AF0A9DBA8505A24E9E97B1D5813A79AD5934C1E5030EEF56663A6F98D0E5B2E2F2E1DD9943DE7BC0B021",
-    "hmac_key": "1057562971FC6D0A0D2E07333530E41FFE50EBFB48119DB8A5C3F827464C2841",
-    "adv_salt": "5EE1",
-    "plaintext": "57867C339A963B8A5E271B3E8C3ED9C4D5FF42A9DA58A2",
-    "ciphertext": "D7747A26F1AFFF363FD8B27ADB19295DD633084D1303DD",
-    "metadata_key_hmac": "7FB95D5FC94C8961A95FB07BE8128524A847EA9353B1F1CB585B38BE60F30137"
-  },
-  {
-    "key_seed": "B07121F9309C6A32A3937D28CB73FC80DDCA48F937CC9819E437A14FEAEE564C",
-    "ldt_key": "5BD101F34BA505417DB39696B39705BB2AFA5BEBE4BEADBB0FF26B4244940F2B34C28E8B7E95087751F00D2A72FE044906FE536A07A846EFFD67AA6771C6B81A",
-    "hmac_key": "8DFDF658D3BFC4E482FE25C11FD81F3F9B71C20FCAD59CC2053EB3FF23BCA90B",
-    "adv_salt": "D433",
-    "plaintext": "F46AD7D78292E7AF94603AAD5615B42F8FF117F9CA7AC56C55189BF30ECC",
-    "ciphertext": "0BA3E268A0BA62413AE63A2DF4DF4465A3DAB09E7D87FE5FEC79B515FC52",
-    "metadata_key_hmac": "91D65198349B8D5AAB4BC20D5C5EB55B8DB1DBBCC8016390078343108DF48CE7"
-  },
-  {
-    "key_seed": "16EB091B388ABBF530F7C3DDE8BE8A4E2F36FD738B0C520B2B8873D5A39D7EFC",
-    "ldt_key": "9684092E2C949D1FA7BA03CF5EFA43926743761DEF9200EF0EDEB1DA82945F052D4EB19D262F10B88AB19FD853FBE2A80B14AADD8612CC46F6A95E4EC33CBB08",
-    "hmac_key": "7E6C9673B48E9EF9E18DB6F77FAD4A66C564A5981BFDA6481BEC0B941266A879",
-    "adv_salt": "836C",
-    "plaintext": "A51282B8D1A391F93CED3997D983F0B3397DD22D82F1998D82",
-    "ciphertext": "EE7988BFC2E2F00A293CF2BBBE1F3A059AE22C376C68CC070F",
-    "metadata_key_hmac": "98E45E977982DE8232DF201C239353A289A3CC74016924AB5DF2BB8AA766391D"
-  },
-  {
-    "key_seed": "593F713B1CEEE1AAD58F05B739FB7D09DC36217D2338EE5A685295F7F084875B",
-    "ldt_key": "6E8604E1E78CC0FB19B84913691F26CC1C33199AA539B8FB99496D5038018B57CB67761DB438FD50D7ED186E215E322B536C8BD948F0769FAB910308CC54C5A1",
-    "hmac_key": "65DD06E50DC75C0DAA4C3201E0CD845099DA9405F61D4B471EB94812F932E553",
-    "adv_salt": "396A",
-    "plaintext": "DA236C79191CED8BFDAAF9EF4E4B0BFF4EB5F2ED6A930937D6B573E77EE1",
-    "ciphertext": "A52ABB15DA485DC6E1FC850413A875D79D3CB9C8BE1891A5837ADEA54B33",
-    "metadata_key_hmac": "C3B795131B2C970F92BA425666FE389CE11ABB52291F69D723E75BDE1D9F5064"
-  },
-  {
-    "key_seed": "C19D74623B265D8D7F07CF5B9F099B76899ABBBA1DB819747909F2190EA25EB5",
-    "ldt_key": "797BD489E98DCE10E524AE9E1164A0F68FEAA98B0F4F97CD0C84FD3D2AB2E14CC276F3818CD565298244A13D3A490DF5974B72BBCCE294A2EBD0C1E24C5A010F",
-    "hmac_key": "A802F477F587F9D821BC493ECEE500B1D887119F1EBB1886284748D36F84D15A",
-    "adv_salt": "676D",
-    "plaintext": "D85AD63AEAD575E455BBDD8B2E5A42D18E87A8",
-    "ciphertext": "D3BC41EF27122C40B3D7CEB7445BCA3B3ABBBB",
-    "metadata_key_hmac": "5D220323813E77391183BDA887549D48BEEBED35111D6893ED003B7863389F68"
-  },
-  {
-    "key_seed": "7BF3623220392AC5B804A005E6DC96A6EC32108FC22EA371325129A3B3C0934D",
-    "ldt_key": "484901A37BF4E872385A32AD47652A4289ED23BFBECDF7DFEC7ED6087F57FF5478E7B16323CCEAB9369FE99F43EFF332C1259C8C5375B2B274557007538AB73D",
-    "hmac_key": "3CE9A2B879697EED758D331DB9FA887478266041FE76C377C7BBB97C1A2841E3",
-    "adv_salt": "7170",
-    "plaintext": "570E91C5673CD53E569555B65DA830E78EBD6D7201DAFC94C899E1",
-    "ciphertext": "805DAEDCCE7DD993B5C894241FD07A7B6FE0227F8249AED9960D3D",
-    "metadata_key_hmac": "D741CDF71C6B87C90934C5C69E50776E9DA12A7046E73AFA8B9DA4E39B6DF366"
-  },
-  {
-    "key_seed": "0B4244B762772BFF2D25B6AB24C11E1CBB862405DA2207F5261FD00D3FB2F41F",
-    "ldt_key": "652713E03311A04E642497AD242CFDBD85EB76CCAB1D0514F0424D08CBE5A24C4860B09142548AF9F27F54C060A8A3EC352ADEACF35D92582500104F27A29590",
-    "hmac_key": "A6E2A5880FB0A55EB630DEC32097D5489EF19F4CAEA7B63E8FE2F6020FB454BA",
-    "adv_salt": "9DA7",
-    "plaintext": "EA154B7C6378AC3F7F73B081DD747B98B23C17352EB1ACD548CE78F124463A",
-    "ciphertext": "79F1A617F91E96AF2BEFD5E94524EBE8CE632E385926E69AEF006C46C26155",
-    "metadata_key_hmac": "E5BA00E38C5A01E28EAC7E969824D76A6F34D8ED7F9EA8E20C9ACDD3FC4EC3D5"
-  },
-  {
-    "key_seed": "F8E333AF5E871B1F0854914E14544C9F2697CE374383911C73C8FBAD8C3BE5EE",
-    "ldt_key": "A38E2E39FA60B949F1297FC3527926963441FE5A5004F0AB90BED92D229488C9EBC12C27B55ABBF8D81E10C2318677A81B1490749AC3FA23034395B8897BBF82",
-    "hmac_key": "F9BCBA55316A4DCCE7B2410E2886C5BC8E53DB210FC170BC53297A1823F78CAC",
-    "adv_salt": "D4B6",
-    "plaintext": "C749BA7F2512B6A7259269C1610770ACB14751298FF767957055",
-    "ciphertext": "FB01C67DA9E157F9291E28D33815DAC5C3AE12E7BA2FB0C5320E",
-    "metadata_key_hmac": "0C05F69D3E64ECA65B789FFB4AEA169E967C25C55B389D743A83737F66EEDCBC"
-  },
-  {
-    "key_seed": "E5F9C362D4FD10E0DAC2A4BE4BA4EAFBBC5C4AFB3CA9820A70CC498B0734D4F8",
-    "ldt_key": "E3A1F5E8028DCDB21CF5E3D200BA4D728A36602E195B282B12FA5340EB4811EA336C6FBB531C28405B9994B9F55616C8C70AE3E9F5B0F2AE9C3FF5844BBC411A",
-    "hmac_key": "3D5980A8A1F905A098834D4C1E65DE9D3B341A95C10FF34B2AFA0C34555FC920",
-    "adv_salt": "34D0",
-    "plaintext": "394B4B158FCE09D1C525D965DE57A7A2707E",
-    "ciphertext": "66F01992CB72AEB6A4AF3AD8B29D752933FD",
-    "metadata_key_hmac": "02CDE2667F28741DA9CF4DD2A315AA088631012B142BC93B562A1E5F93DB6519"
-  },
-  {
-    "key_seed": "BFB715370C8047D29D04F0FB33145DE2D623E3D47CD0C939225FDD20E5180BC4",
-    "ldt_key": "A93BB3594B683FB42863E88C23EEDBE0624DE923B1B946639EBF81288408CDD9CB25F9296C73885CB316E3EACDAC80666325AF8FC79386B8BD0BD392B02BC62D",
-    "hmac_key": "8CD11B7D362BC3134AFA622E973B831640BABB28AA7F2552A0C8DBD399348C43",
-    "adv_salt": "FAB1",
-    "plaintext": "BB4D8B51DE4FDF3AA54992151E6F528D",
-    "ciphertext": "67948062505D79C2BE4245F2A500935C",
-    "metadata_key_hmac": "6DA14EA7C97FAD70D6A274A279BBBF5784A40EC9226B2CEBFFE2F2F780C57607"
-  },
-  {
-    "key_seed": "33BDB4C8FF7F5E4EA8FF024D8B50CA23BDC925F469D196C7A06F572E3B1D5B67",
-    "ldt_key": "291ADC21691BAD3974A8B7B9898D0D2A0B9EF76E96B5B46B762D447C3D42151DEDFF7A1E34F3E0E3309F0D377CD12AD2537ED00D951DF8D99F969FE04979D55E",
-    "hmac_key": "906C6E4C7DDDCB37D49B7989FDC034B00C858CEFC6E8CBD95A2E42F2315691FD",
-    "adv_salt": "EA16",
-    "plaintext": "072287B39625D9FB3C6251B5E4F87B4E473307BFC66C4AF57AA4DBBEFB0F32",
-    "ciphertext": "6AE99A8C1F11C308918297820F1E98ACDFA7369339553A95B2CD44C5C6E039",
-    "metadata_key_hmac": "41507F00272A4395C00B1F1F95A3BC62F2DB8B397D508D287BE1546472BC997E"
-  },
-  {
-    "key_seed": "1DE7BE2135369F3554E58C0AEC11B28EB1082E47E5373BD6C33E27BEC88441F5",
-    "ldt_key": "8DCA3D017E1AF638901A7EC4F3ABAEAB5042AD712821052B7C9482C0D132C189BD8DB3CBD84F4683E948E5B0ED5A57354012C1C1B2BABD63CA2A1740382F3926",
-    "hmac_key": "83D9696758DBA51296E7F7CAF9A883C5DD29C2E228D2639B8DA4293AE5166ECE",
-    "adv_salt": "D9A0",
-    "plaintext": "56B94B01FB85F26DA07AC9871EC0435CB0BD2EF592AD8AB2",
-    "ciphertext": "D00C10110C4CF9E10294C41BAD9E4B2F656B92D437E6715D",
-    "metadata_key_hmac": "78DDE51A9D2B4FCF329ACACCE9996D4151B9450843A5B6FD234EC7FBAF76F15D"
-  },
-  {
-    "key_seed": "8D30198B0BE682C467BDE246C0B5D17F269F5A5CAA1BE03CC91CE53DB89F2065",
-    "ldt_key": "9CE88CE3DE410005309A2F8050E65331D2DC2F43ED63025975B1B5E11CA00EFCA4E800B8454B578CFA426721666883FC49C1D828B34C9FC4D359F2ABB8B0816F",
-    "hmac_key": "8AC75FB664DE0D535B1649598A0933E07B15CC23E0CF20EC3DB58C67F2BA20ED",
-    "adv_salt": "2383",
-    "plaintext": "3AFA3B57F287BC133C50753860727341F8C76304",
-    "ciphertext": "5EB125210C7F4A434AA65F37061A1D17EAA3E3E5",
-    "metadata_key_hmac": "7B387B56051C2A1C0E7DEA987B3BD41179BE3594C94D3B2C16B54B8214E6B3E4"
-  },
-  {
-    "key_seed": "938B3FD1182C66E7F3BD1C57C79FF3FC689ADC74582EFF313BB088C7D35E29F1",
-    "ldt_key": "553D293651004B38DC2DDBF30B4C3EB88BB2B52BDAF2A1D16E9A59197B41CC2F828D60448D693CDE6F269806F1F0D7E6926BD7E3E7256A0A54B440E1F7E6E765",
-    "hmac_key": "0A6D801912A13D138DE7888B57D5EA2A6B09264EEEFDBD2D0F90A4D838CDD98B",
-    "adv_salt": "289D",
-    "plaintext": "395E83ADEB08B0B351943B05214342EEFF8C58A9A460FC86",
-    "ciphertext": "B28472A8655F6ACA528FD326BF78032749159C02E67EEB7B",
-    "metadata_key_hmac": "B54FF0C1F02E244D283D30518DD29F39628C7261A33E4EC73E23D45BBA271637"
-  },
-  {
-    "key_seed": "45A3FB6F4926BB2CD90E9A2219AF199CE6E22757951F3C534B97993049439BBA",
-    "ldt_key": "D5FE7EBE602FE69EE6C4CE161216B379C2E1541E1AEA42AC7180D0CEF246717E550A1CF18D2F9F36EF68450CECD38F77F8BD7E98851BE7F4CFE89CAC8343456F",
-    "hmac_key": "AB03BCE03F01CD10ABE61101BD662EB2052781BEC469A8797983D11A026621D9",
-    "adv_salt": "6268",
-    "plaintext": "FB8071BF75C87DE11ACB1A66B98CC640B67A6A6BE6059A0B5C264F",
-    "ciphertext": "F35F1F36EBCE7A4564D5E1025EFF20EC05EBB683DE5A4F0E066C43",
-    "metadata_key_hmac": "11C4918F0BBF662778B74461EA270416F54B0936168E5F068BD8BA605257D934"
-  },
-  {
-    "key_seed": "BC3C628222F52DAA7DF389745DFCEF6C9FE1A4348B0260259044D83252229F89",
-    "ldt_key": "1EDB931C068100750CC1A2003C8EAA1B395DD61F0DAC1A19A6D2C5CA743366E082D392A50B5D338816FD9EA5D683ADD2EB8052BF360607D164ACFBC380A56F24",
-    "hmac_key": "8AF7E33DD9A1539042CCE8236479A8C209D24CDEC6FA46D8602497092339F3B0",
-    "adv_salt": "96E3",
-    "plaintext": "12E5FF905D0EB084A17EE6203F7A041EC8DC5D65E5EE442E84EB11FCDFAB",
-    "ciphertext": "297D54356D7DC53C6B52185E92E810B4BE02919BF84C7C60810007660216",
-    "metadata_key_hmac": "E4C26C3280B1198BA1BFB3AAD1E93D39B45B38DA0E14649EA32F2FD1B6E3575A"
-  },
-  {
-    "key_seed": "FE59E191E1F37C4895B5CB9A6A2BAAF4D3759A4806D41B04710CA1B02F134BAF",
-    "ldt_key": "D510EB1353CC86205D7B400536641509974D44F6B90D2F9FF7F4969DA495EBD2C27AD260017AD1D5963ADD51FA7344BA1B6387DB8B33BEF7DB773853EF45222D",
-    "hmac_key": "E02610EA8A327590E20764D2F4EE4D84584459AA674235D01DCFF39AEACB1BB3",
-    "adv_salt": "30FF",
-    "plaintext": "04C28324AEF8E9F7ECDCBE90DA9F44B3C421F663C13D5708DC57",
-    "ciphertext": "6F3CC44F51BA5A13EBACBBCE7F01952EFBEA29B861F5FF9ED4C4",
-    "metadata_key_hmac": "561E4B55FDB4D3776F1F4FB2EB285CBCB059EADE18CEBF26A0E432DF712CB8E0"
-  },
-  {
-    "key_seed": "F1BCE3E694D3E705D4A2A19F75CF82695E21ECD91A46B299DC84E2CF107882F2",
-    "ldt_key": "CEB2A23ED176C85C2FF7BE55FAB82496F7F164CBFE4C7F71718CACAA83B14820290F98F20BBD6B935C3DB03B85C5B86F464D309B171AE489A265E78A04D92E1D",
-    "hmac_key": "F1558C37EFDE507E43211379F127B673EA7F071A82F371B0B2D2A21D7DB02B6F",
-    "adv_salt": "8972",
-    "plaintext": "2C1751CCCF9D86EE6C90876C8242956A5CD7E1",
-    "ciphertext": "E09B67166DBA26E96852011D794F7E25A8827A",
-    "metadata_key_hmac": "FEFBE1CF5264D0065B12B300E33D61B6AFE1B1FA45BECDD0EBDA3B8599CF2F41"
-  },
-  {
-    "key_seed": "EC22763A7622C4DB44CF765844E3F4760B331F56B9E1FA18F503252D2A83E4CF",
-    "ldt_key": "00EFD0A14327AB1565E25738ADD19A69ABFB7F198A5F9D4BC36321955FB1AFABD9D542AF0A1D434A714B984FB3DF5ACA6FF55C866D277398D76D7646AD6F60A0",
-    "hmac_key": "BDE86FC86156010C74FC6E28B73FE6381FC7D6BEE356ED3CF7A0CAF1D6D4AD8E",
-    "adv_salt": "3818",
-    "plaintext": "D8741C307DCB11972F993E89824E3CF4",
-    "ciphertext": "30AB89B502BEEB1E117AF6F150AE422D",
-    "metadata_key_hmac": "17A5E8E4F289549AA9C4C168FFC52B4334D4BA5642DA922D72192872BD29E54A"
-  },
-  {
-    "key_seed": "6BFB5D9559236AA523B36B188FDB1AE6B5E38B73D40DCA7A5C3E0054E78C56A2",
-    "ldt_key": "A603FAEE39356225A060A05067944537F0259259E3ACAC6A1CA7D07AF1956128F1CB92D2F8A8DCD03F94665C685B8567A4BCA039BB3DEB3CF6550F9932A9FD90",
-    "hmac_key": "9CAEE450D7E3384524E699BA71BF89E80D5C249F4E096FA6340075E146AC50FD",
-    "adv_salt": "FFD9",
-    "plaintext": "75D5BB5845EA6038750E5BAD00FEFE436CB36B7779E4ED882EDF2B",
-    "ciphertext": "BB4E1D807A49C43B70D7F44DAD18B34F53686A8398B5BE42945B2E",
-    "metadata_key_hmac": "07B6D518CC5092EC628DCDFE72372741C431E01E333E6E51A1E6EDE6135030CD"
-  },
-  {
-    "key_seed": "2F96CE3443E163E296F7E8788536B253445D253C0DE5D259B6FA79E6514037D3",
-    "ldt_key": "7BE961F2FBB8EC6CF4F4BC3B08E3235584E359DB19B38664675946F0D6395CEA72B0784F33FF429D440319CD3A3C019C84857DA0323B2AD556758A84714BAB91",
-    "hmac_key": "1F12DBEFEF1940B31C3C8DD86A1AA321FCC017D291AE4EE7329CE5D1C77B0118",
-    "adv_salt": "1A9A",
-    "plaintext": "C9B01829CBD1D71BD08704C914737721FD5A68",
-    "ciphertext": "325FD73EA2805026A763A2B38E2E73CD0A1A4A",
-    "metadata_key_hmac": "6823AB7F3B05C2DC43FEFD1B0841914CB66039FC4DD4378D699EEFC8BC2F987E"
-  },
-  {
-    "key_seed": "4A88D683C9602276811F22E8A1B1A386C49D18438A8774EB68F9E1F47DD60A4F",
-    "ldt_key": "B85C7730669DBAE4E813FA0D1D317DCAD6B3F44BDFE79ADABE5C3B1EBFA21DC7E1E100F88EFEFF99DFFB221095ACFC73808F223416C1998A3602D7A7D22F8C1B",
-    "hmac_key": "92288B7DF8AA76139D235A58014F5328A37A508CA793AD52259CA5134F964078",
-    "adv_salt": "956C",
-    "plaintext": "2E22E38D3352091630AF0212FF2E8E3B3BDA4D73980DCE18519C935CF3F648",
-    "ciphertext": "DAE85D75F2E9EB829A0048A28A21F08AECF692E4A7D2C664681FFE7B625C77",
-    "metadata_key_hmac": "4FFA58C296880D6C3D3307D3EE0E1642CD6B398EDD36A44B68FAC60CA9F8A307"
-  },
-  {
-    "key_seed": "107C2FBD9699F8A8D5CBFB34A595B168F01C6D84E4184D267B2A557190CBF3A1",
-    "ldt_key": "D07881A7CA0E220CC94CA8C13DF3942CBB7531C669730C4025BE564BE06D9400D01BF6EE08FB0E08F9398A8ED88045BED02A606D5CF51491A4E30724F4CCBAFE",
-    "hmac_key": "88FD72F6574A0CFEA7DB62714C03865768526A226F9A0349A9D94B692166CEF5",
-    "adv_salt": "C970",
-    "plaintext": "86DB18B1DEE5FCF1C62B6EB89418D31D0E8CABF8BB59DB42A78672BEAD",
-    "ciphertext": "B10F0285EB9FA1FEA581A130BEF5266F3574BFD8411D90762847C37595",
-    "metadata_key_hmac": "9BCA0211FB695146728CA6F0B1ABD243527EC9F577F4CB66838677C89FBC9F09"
-  },
-  {
-    "key_seed": "B669795FC94A32E2CCEBE3E8CD7636A53F37C7AF7E8F03811E5C50E507B1CEF0",
-    "ldt_key": "E7BDC44FFAC5A842DB4BBE84955B3EA95F7652C7FE388C206021E592A4E63ECB09B56A790261AF1B3F727160A2A8CA96CCE9CE751B8CB97B570DEE4E8B904846",
-    "hmac_key": "86278DD1C9BECAE43AC378AF37689466A281686A75D0793705856E39132A38F5",
-    "adv_salt": "AC11",
-    "plaintext": "32FF82B1BCBF2C8D2DA1986FFA6C909A0EB4DF17",
-    "ciphertext": "507E2FF5FC4C017695228EF12ED482429B5F4483",
-    "metadata_key_hmac": "4F1F2CB7BD8638842DAC9277DD0F498874425EC26BBF113D7D499B078846096E"
-  },
-  {
-    "key_seed": "60DBFE2A1B840B7B1F04DD9980885D8537ACC25B2FA210F5B494A81FE54EE81C",
-    "ldt_key": "C8C8DD7BBF2430EA3EF2F01C3259A4E6D89068D5468CBCD063485DEFD6A7011D330F461BBEC8750E02FD1C297D344C80F22F41898C9910CABDFE8B2DDD9A5A47",
-    "hmac_key": "9F980970B706235E2969A181D14FD7BD1EFB44BA981B597497B2686C9CD8A16D",
-    "adv_salt": "2385",
-    "plaintext": "6AD53BD901C67A9478E365A42943E2CA2DC6A41B9CBBC0B44715829965B7",
-    "ciphertext": "46BF32DA2A259572DBD41BEEC104ADB7DBFADEC7B0B8B0EBBFA2455A6AF0",
-    "metadata_key_hmac": "94976AF3312558D902FC77B7807386F1D163270522A285C14FB106C2A890EECA"
-  },
-  {
-    "key_seed": "9653C6869668736549125E0F043308C09C33B1D8680932CF879A651D250D92EE",
-    "ldt_key": "EA277E82879A79A2D8A0F9750EB54C8743C75E3A1F9CADB6BE90E91DBD3621127295198F9A40EAC2F8949DA1F2BC1AF15C91F5BC64BFBAAA58FAC71A602DED2D",
-    "hmac_key": "4E25E4C1DBC0C4AB3C35AF2CB71F2F49B48A4E797E982A69DD008BCC63288A22",
-    "adv_salt": "42EB",
-    "plaintext": "7FC211D923E0FDEEF1A133CC4C8A54D755FEC60086122E18A2C4FC6C",
-    "ciphertext": "56830E1D523544F48C60A79F7DC2658D4711A69BA1021C49801F2F8B",
-    "metadata_key_hmac": "CB43679649B990619458219EF3BBC96FF58D01AC04453D84CA55111A1FBE51A9"
-  },
-  {
-    "key_seed": "B660620A749423B87AB51CE3A9DBF52D3A49C4B4E9704CC491420F3C02673D84",
-    "ldt_key": "1C45A44A706B7CA5144FEDEB205E763EF6FA2A3D7C8C962EB4449458F3CE84D074CCFB6D64CADA87ED3D2B1BDDB915B04AE9A70E0732424A520AD2F2912EC148",
-    "hmac_key": "B19D5B3A46DA48B4A9C5BCF1017AA86BDA4D89AF45789DB90B3599DC72FE443C",
-    "adv_salt": "F778",
-    "plaintext": "AB7AE9B7E6A1D1C256E5E4F44E88FD638C8D4391",
-    "ciphertext": "36892DA56C467D9E87CD96BCCA33C17D1AC36715",
-    "metadata_key_hmac": "42036409DACE84B0FF0AB021E649191A92F0B383421F48F77A86D229FF722279"
-  },
-  {
-    "key_seed": "58F6B511B4E669AEDF3701BCE8BC36F84BC6128280EDBA2889C9A20D482522D7",
-    "ldt_key": "83BA91BEEDB08613F78D4AAF4B285B0F8568BCD26F0AF5DB966F4D940EC674F9A77A22B3107EFB1609F8B558458C70D78193709AA204D5E62016F200B9B63E6F",
-    "hmac_key": "04041ED30C1FF71C51727DF8A6929257FC8A4E8D4BAE0E67B45153F598115579",
-    "adv_salt": "F166",
-    "plaintext": "11055806D37FDEE2634CAF81B6772788",
-    "ciphertext": "3154AB15C5FEF46C91504C32F561E925",
-    "metadata_key_hmac": "340A5DE98A25C36590E6C9FD70E7F33FE29C00DCCDC875F596E3870A3F7C1D6A"
-  },
-  {
-    "key_seed": "6E37788FB48A39C046E2026A6433E1D223BC90F1D4BEEDFFCED9F266B6032906",
-    "ldt_key": "D522AAB7AC074824F08F5967F322522447F794188A439BDF931CE1EB13ED3A3AECC757A68E34B9010B5B86F4E10D60355298570F71C9E714A8150CF167156CBC",
-    "hmac_key": "5F74717B967AD6B134546677FA20C3DC05EBAFB9748A3B7E2369663B11DC2AB8",
-    "adv_salt": "9DC9",
-    "plaintext": "916091AC15EC4E55EBD5BC22C613A49B659EB19A5242211F500FB2F67E",
-    "ciphertext": "29796CE84C4E554E97ABB5A0085772D067AE41C2AE256C894CF9ECE691",
-    "metadata_key_hmac": "7B20F19C37450647BFD03C7F3B1EDEFA93E8CC0CFFEFF346BFACB12B67B58D76"
-  },
-  {
-    "key_seed": "CD8A564B17E9FAD9A3BF046EB56359528BFAA8FE18A6C0E1A9451846D94E515E",
-    "ldt_key": "5F4D152E81F2E076137B43C6E66FA2F2ED4ADFFBC12488212DDC81436AFD3E77BD57D0AF7FB3D12FD989D31CF74D983F1C5EF02D998B2BFAB6699B94964DCD42",
-    "hmac_key": "FE433641EAE1BA1B4B834AB8D3F0C34ED903A48F566439A0F4D0AD0D5A54BDAB",
-    "adv_salt": "F138",
-    "plaintext": "2817177DEAC6DD1D7C30FB23CC2100FA734BE5D0423B5A53C63ACFFFD0382D",
-    "ciphertext": "5773C62B9EB57C23D724706D93F7EFD8D8ABE7FAB86591760CDB93D9303882",
-    "metadata_key_hmac": "68A929D5927F49AF93CED5072E52A66E99A5A2E51B371A7E1781AA6DF44D6B03"
-  },
-  {
-    "key_seed": "AC1D4BBBB9C0922D3C6FA64E52BE3ABEC411DE0DA32AA066D2D5A60F6D803A0F",
-    "ldt_key": "78A2FE1A5CEC40E33E35A872FEC2902EA5B2E3C99C32FEF0500B6FBF082FCE661D2C52F2499CC3888FF63065AF79C3508DAFB7C0F911C746B95B5AAE403B6054",
-    "hmac_key": "82E9718FA9261F37D7D6729B4C05AE1A046182EF3B5B3D430387E6801C805E82",
-    "adv_salt": "296C",
-    "plaintext": "3A717F2F0568518635B3E2851BCA6D151752F1607F02588E8E1F77",
-    "ciphertext": "E05DB0E55C5FA750E7A2481A44E1080F9D49855523FEC5F5C01D35",
-    "metadata_key_hmac": "E395EB74DDECA7699C112CAA848EE2124558843F44C5E1C413121A47B563334B"
-  },
-  {
-    "key_seed": "8D531389F16845D838FB65633C53BEABB14EDFD3F46B4D59866EE13E63879D2D",
-    "ldt_key": "F1B16139D909BD76F86B2AEFBFDF035C0EF7D5B5C2980DAFC4824A1479D908F8CECA968A14AF6C659E47C6A10C3AF22A3CCF092E7929BF764158105A25874F6F",
-    "hmac_key": "DEBBED4EF9C2FDC50AC25E977FC3250ED187CC8AD5F067DC01635B23A9E93FB4",
-    "adv_salt": "FFDF",
-    "plaintext": "38430F14331A2D48355A1C9F56A1AB0BA7A27EA132A7071E09",
-    "ciphertext": "C3F9DB294E6A735FBE527B0C79FBA8ABECB00B31CC7A67B2A7",
-    "metadata_key_hmac": "95DD1FB3D3A595FD72C041B3F79074C3214BAAC2C18E483CFC4206D64AC4B64D"
-  },
-  {
-    "key_seed": "08C051A56A4026807F185AB8B1F9D6D988B275FEEA0E407136108C707158964A",
-    "ldt_key": "84FE0436136F9934A17ED5660A270E6F54B4E24F0969ACC5B84C6078DA720BC103C84EDA3B93C466EDCCFEF85B5D4C462F12423125B05324856CD6C784E086B9",
-    "hmac_key": "72BF447F5C5DA8D513080157E7676EE756ECACF346ACDF9A1D561DA359CFDEE8",
-    "adv_salt": "84F7",
-    "plaintext": "AA39C2A62A38B2B0340CFBB3A5C4E988750DDD",
-    "ciphertext": "49C146946B7A2D0A8A71B5E171BCB36429256E",
-    "metadata_key_hmac": "FE8DA88D4358BD47959B23268B7770C1322E1FF1E6E8E1DFEE90ACE685260E57"
-  },
-  {
-    "key_seed": "83F48A68E7947230E032BBC32641F52278B85CA39CDBDECCCCB0F4BFE7F0E77C",
-    "ldt_key": "2A2012F1AFB7EA08CFA3B81E6DEABB492850F7E411F4B7FF0588CBC94B2E0D47C10A267B846996AECC466E81AB9A26509D1BB8139688CAC1FB7DFE269B92A2E5",
-    "hmac_key": "AAC0D02AB54DA3F59EE5165D6267BFFBFA441B4C038F2FACBC8C47D09E7B89C6",
-    "adv_salt": "59E9",
-    "plaintext": "1E1099C949E8833EC2EEF7FB9114C51F48FE",
-    "ciphertext": "F8E229DEC34641ED97C53AC106B9D4C10C16",
-    "metadata_key_hmac": "6A14F39AF418A9E90CB72E5134B9E192204D6BD4A014A73EE4B3561D69F9996C"
-  },
-  {
-    "key_seed": "25ABF61989684166CBBF871E353B74F53292B922D3812BC0FDB23D13CDC3C280",
-    "ldt_key": "311EC9F125A3987903450884C49DE77E27EA7A476F95128CBEB2D2EEA928D081BC8C7EF80B3CBF62C3227F6E56450DDD2DEB355CADD8884B96A0F72C874B0500",
-    "hmac_key": "23989F83F9D09F23D2FB15EE7C52362D23616C1266CB3397612CE7C11B6F5E54",
-    "adv_salt": "6094",
-    "plaintext": "3A9CA922DD4CCC25367907942D630BF0C3E7",
-    "ciphertext": "3BCB1D893925FC1087E97ED294D3346791E2",
-    "metadata_key_hmac": "348BBAD5CDF30488980D723F5FF74221168432D8A7D6A068D4AAF6CE30B92EF4"
-  },
-  {
-    "key_seed": "7E870AEE530314D6A7567BDC977EFD10A88FE5C8FBB4BB36B930B3AC44165DAA",
-    "ldt_key": "C2494B6FD9DF81656BB26A24012A63606365F9A8520AEE8B83855556AA16FC6B7AD913EB71F5CD6EB2D50BC7FCA736AC397FC5086985AACEA81C81AF87BBA281",
-    "hmac_key": "E2099534726B7394D79012E3184611784E190FF77A1D1BE5725EDCFC869ACF6A",
-    "adv_salt": "AF79",
-    "plaintext": "66932F49484D0F989AD55599DC2169EE311FE20E38B9A16C6346",
-    "ciphertext": "01358398248454C6B1AEA844D6CAC1758D2ECE788AF8455055F8",
-    "metadata_key_hmac": "61B14A3C1DE1BD60BA060735FA8A36EDEAFD8602B1A0832CC29EC3D2945B9BD8"
-  },
-  {
-    "key_seed": "C7BDCE87471D280868EFAD6BE11D01784A17DF2E21B2C42F40E645ED53515A04",
-    "ldt_key": "BE67CF76B8DA9C1CA61F483E516640060C7E6AA6123BB96EACA540EBFCE809DBE7121C7B2D7A8F836C9FF8E937EFDBDFF949BA8D1D379F8D503C428AD80B62EB",
-    "hmac_key": "20868F5516F580BC39A7DF3411D4BD749CDDBB4C8BB5E1012E94021BFE89464A",
-    "adv_salt": "7DFE",
-    "plaintext": "69CEAAC45F821278B72E282177275B8E9944FD6754D437E0F3D4",
-    "ciphertext": "00A52D33E34C2DDBC1CF8D60957EA3E4A8A2F1B9EC96DD74A5C2",
-    "metadata_key_hmac": "8D1F86151A921843DB626DAD5A8A6025DF69817DAD2464BE3B6BE20A978B9339"
-  },
-  {
-    "key_seed": "E486CC16E8200FA38014D822284E1DFD6FC672DD76AA30A15402A12868C456DA",
-    "ldt_key": "5FA855391F68B0B58C3E5BEC9887E39A3D7C0EC29CCAFB7647A5A91CEA73803F3419CCA956A4D03343FD65F36EF520FB888590ED2B6B2EC1A16B30870431C5E8",
-    "hmac_key": "DAF4E7480BA7C1DDBC5322BC6B7814B2191114E963DDEA50B16A831451CEDC6E",
-    "adv_salt": "0E60",
-    "plaintext": "1C3FE453F872B66095CD3CB88BB6FAA77D59AF",
-    "ciphertext": "ED89FB6E6FAA21974A4A403E7E40EF92ED5F74",
-    "metadata_key_hmac": "31463EE4DB1DA018FC809A8B6BB6A36F151219C237F8ED8C10E0460DB666355A"
-  },
-  {
-    "key_seed": "9AF41893CDF256D94F514EAA0BA39FB189D8AD01205AE4110A398C0066C9A7E3",
-    "ldt_key": "9FC81F3E1159A25C0ADF56BDD16307F2AEC45BF5FB4B10A8B4E31BE30FAA6915C33BAA7CB538427B63CF4831EF87969931B153381E734E66160218C55C53E2F3",
-    "hmac_key": "55E88B5DE0531B144F06E1CC8625DE501997BB6B4B77E31A5136EEBD18ECEC3A",
-    "adv_salt": "CC47",
-    "plaintext": "7C0A44ECB3BDC4E0BDD02FB438F4BAF2C449ADE8A93930",
-    "ciphertext": "DFA844BEBAA59BEE88E6518DADDF665B28953EA74DFF50",
-    "metadata_key_hmac": "453356617CBA9A6A7C211B496BD7C12D0439E906160AD93988242B85A20C5C6C"
-  },
-  {
-    "key_seed": "982013E258D8D4CA7E3F932B3B4EC4D9D0BB4F59E773BA088C5DB261993DFB64",
-    "ldt_key": "B812087CC7F516F63D744E5316BA2214BF3314D394997A95B334F364AB3268792807C57EB1BC6944EAA19A3D168BDB15A2E9DB6FFB026DE8A58AB61211A806F8",
-    "hmac_key": "3D0188404BDAB7F999076EA9321A4EFC39A55002DEE7C622AEA1E8E1F7BD0115",
-    "adv_salt": "19F2",
-    "plaintext": "4D21D4A45BD54B82F33ADA83DDFDCFE38AD3",
-    "ciphertext": "5AB2669B6B5F3CDE17B853DD9CBF9D8E486D",
-    "metadata_key_hmac": "171BCF753AEBA62AA690554ACCCAC49DE5C7C4CC4BF95F1A6639D5E7054BAC6C"
-  },
-  {
-    "key_seed": "4831215C060EC4416486BD0D06BBF8AE1C87CBED2C686FAF78FC143BD4854A8D",
-    "ldt_key": "FB4A94B958A53E8E81232A2288C9B50ABDBDDB405BB9FB27FF61BB0127280557F4462FC907ED63021DA56E5048D3209E495A8BA674423522E1DDF8EFD2992074",
-    "hmac_key": "C17765625DC10B9915731342DD4574429C87DD3C6EFD5022C9C4C34595966C96",
-    "adv_salt": "37C1",
-    "plaintext": "30A6E08285A9DFF8D5732E12986438F3A543D88751",
-    "ciphertext": "5BEAED03F32825CA05C6AEEDCFE8650E80F98ECCE5",
-    "metadata_key_hmac": "F363C143814A9C3ED6236DA6F712E936C32A9E2C6D773F2D033311B7EF9E39F9"
-  },
-  {
-    "key_seed": "BF7F333ED03DACAB5398B5E68607FBA1B84060860A11AC859686499D199108F1",
-    "ldt_key": "76A979188272D1A0DFFA3E5F4C6D6BA6ED03259AC3AD0548CA673FC6758627E5276461C18FCB19871A7E8754FD248CC68107F73D788A08783B8C1A335B742121",
-    "hmac_key": "57B20A2325977E08DA51B649FA12B5E15043951DC941BBCAFDB114F544F54092",
-    "adv_salt": "0BB3",
-    "plaintext": "F903ACD0008F8DF45ABF1A73BC406622ACA5AD99EFE76AABD2CD608E08EC",
-    "ciphertext": "8C9F277FC17D2D44FF96D9D961685DEFC16ABFBA5D13FB75C8ABA0FD3155",
-    "metadata_key_hmac": "24D2D21C385EBB9097D0812C0A184E7B01A27490DBC30F95E1BFCE7D7C00FA6F"
-  },
-  {
-    "key_seed": "AF8CFDE1ED1EB5A6819878AD5899D5AFBD9F0DEDD87931A919CE4C562216014B",
-    "ldt_key": "958930672D654C4591C793752A25E8EE363249AF86488B8031725527E9CFD878C6931B44A219B1B7DD7DCF53C8529E024D89F4BCD5086C009DFB79E130F4BB18",
-    "hmac_key": "77C306B7F177F9158CAD803A671871CA22C2C1CEA9A37B71FE79794E1D1176D9",
-    "adv_salt": "AC72",
-    "plaintext": "8537018520FACCFCBEF1AB47A942E1B0C8A3D0",
-    "ciphertext": "0325C312805B932A9316EB5701099E54FE47D4",
-    "metadata_key_hmac": "60148175D0618530DECABD4799ACC71F171B6D354691E97D1F75BD6DB8F6D475"
-  },
-  {
-    "key_seed": "3F92B8CD12296A93D972407F6874A9B0B275FCA1D7128774716B9B44AF0D93C9",
-    "ldt_key": "C082B62526B0354A6EE8C73D673EC64172C43072B43D47CD43E78874ABB5FE678771D852E847B07029AEF65C61C10CE82A927C70EE9FA5D00BA91711CF36091F",
-    "hmac_key": "53310D7BC015532A1EBAC43E1B0F0897D85547A8A6086807FE027F5928607FC8",
-    "adv_salt": "649E",
-    "plaintext": "2CB8AEDA871C03D1BA51DC69745046C12C650EE6A4B22D12CE78FB",
-    "ciphertext": "59FC27853BB2E2E4CBA1FC875189DC73CA1D33B3A2DE23A8EEBBE7",
-    "metadata_key_hmac": "110111ECAFEA433A6FE6B9C48B51F84301B286E7723E8F42ACE24067C3669FB0"
-  },
-  {
-    "key_seed": "A6950325CCEA52FF5CE7452355FBD77AC51821A2C0CC6659258590C8D92ADA53",
-    "ldt_key": "64D45B4CEA1B625AD2966C1670E4BDF0C7205F2A959A6DBE009AD340DE5627D41700E342BD0C1DA21F46656F570ED1230DF80E84A365391167160E2860B53CEF",
-    "hmac_key": "A6146EE57806226CF7B8B108C52229AFD8CFDE8ED4432E32C9BCD2513BB90A55",
-    "adv_salt": "B6DD",
-    "plaintext": "FA35612EDB49394BA17595F222FBC2754E0A419A7AC0C7688CBD9E9B3C767F",
-    "ciphertext": "C9B7F258E25073D5F5405D170036933D949838E7308C3BCBCFE6DB6E8F67DD",
-    "metadata_key_hmac": "A0D791952CDE3A40D7FF5621A00DBC148776B7837E97C588699B5B319504A516"
-  },
-  {
-    "key_seed": "617DE18D574E856674621B463EAC834EA2CA1DDD33B62EDE6673EAC03F48DB01",
-    "ldt_key": "7A824C14B59C7B7684BE92648CE9DBBE893226A3260E00A3445D9EE1F8ED795C53C013DEACCB49D55A02CE84A317F5F22DDB5DA33BF2D716C0B7D9F6853F82BC",
-    "hmac_key": "285B5A229E5F0699D14801BEA4BC6165778054C8E693A7436F235ADA01041BCC",
-    "adv_salt": "CAE1",
-    "plaintext": "4029B5BDE7AFB3C50B047A5F601BC25DAD35C329A36D",
-    "ciphertext": "3DE0E528CBF25CA1AB98C6BA250EBF8CE00D06F0FC61",
-    "metadata_key_hmac": "BE3E3849E1C920A170CF6168559FB9B197B38109858F3EFCB68279C21C9D2685"
-  },
-  {
-    "key_seed": "8E2B98943C6C16A32693C58A1161AB7BBAFAF00CFEA1097B5806A317ED13A813",
-    "ldt_key": "8FC56B5C11558E6B652F8DBF0041C2CF4593B81E155DA4CCBE49609AC0E09486AF6610FE6E5ACBF0936D8D0EC534E96AB10CF4775F92B5053BF2FB9D7F5EE7C2",
-    "hmac_key": "044BBD941C6E6C0EEFF8A0C555E93473D19F6B4898AF5A9BF70FABF23F93475F",
-    "adv_salt": "8CE7",
-    "plaintext": "B92509CC62B827A735FA5775218564BB",
-    "ciphertext": "67854E6A4DEDBC3ADD4EA4A0E853D0B1",
-    "metadata_key_hmac": "E560F853FC05FF7EE8EBC9269CE0B49C0491FD73E29322D575227615064044DB"
-  },
-  {
-    "key_seed": "36AEB76E3B45B994361A35371FAD52DC728A90FEA7963A9408FC6ACF70F54193",
-    "ldt_key": "D30E4FB3D1E5401DF4F4EBFADA78A25DEEAE3C19DC2B3ED09415A2FFB48A0B6DB39F23462FF87E29C6D9AEF763B3E32B99C620AA8E8D756EC80B404E557082FC",
-    "hmac_key": "F3305A1FEBD6801AB41206A92142CF84F2B327276095A94006375B2B1DA6E224",
-    "adv_salt": "9B1A",
-    "plaintext": "C26F86704676F7B25CB64001686B251CB9",
-    "ciphertext": "907CA3B495D61F3826956618D65A622B0F",
-    "metadata_key_hmac": "19A23CA6F848521C7DE11588E1765466B0F2851262770322D5B9B83AC582A242"
-  },
-  {
-    "key_seed": "389075E418B6A1C619DB81C3A25F768F8C33BB8E5DFF09955A87B6A32B715B35",
-    "ldt_key": "44E79BCDEB998525CC68C15C0D379C913C99E6880B15B80EED54B820ACBCF6041B3B7DF4270FD0441CEC05ACDABDE9A073EE324F4E9E1CA26D65F46CC379535B",
-    "hmac_key": "9CCB4BA78D70D25D9053213A0E1748C994CA38CE5A42F7A903BE6350B4828B0E",
-    "adv_salt": "B201",
-    "plaintext": "51B6999CE384C3C3A6E8407FA2FA3781B54A3F3EF6C61F47C7925CAF",
-    "ciphertext": "703417211EA1B58674D06DD811327227337A59C751AD4AAFF37A3380",
-    "metadata_key_hmac": "A1445E361BDC3C7A737B3BEAC511C6D0FD9782B6BC0EF2254CA77C1489F0F3D5"
-  },
-  {
-    "key_seed": "F671C931D88D78D9CC31118092D123E86926D025E8B3695926B7D0876B6DDDA7",
-    "ldt_key": "18669548D3BC8236C019C944B6EFB8B60BA8AF3DD1F8F7FED3151AAFE4D56070EE99AF0685CAFA72EA60761C65E753885D77A889B0290F554875B37F47652D34",
-    "hmac_key": "D05B047B4F212CCC5A392378611522722A7B062E65B188F6B3B44E6CE391B477",
-    "adv_salt": "B347",
-    "plaintext": "D5B5C84033C34A70EB023395C045D649BDFD79775B7E350F021594",
-    "ciphertext": "F0DAA688C904859AB1DBAF8B3AA833BC13F9A6056BE9B937414DB6",
-    "metadata_key_hmac": "932CF0BC690A307BB5F02FCC388EA94DE58441BB8B132D16815B522BFBFEFC0F"
-  },
-  {
-    "key_seed": "DB1C9AC98E541C8CC9C72E6C7F66238D343A91A6A3A33EF2F7546AB4B8B7107A",
-    "ldt_key": "DD98D681661D81489C2BD6129E6D7F667305F808D3275B66342D6A47FDE7473A60ABE1666C177383C4C45CCF0A6823E79F269F8B491E8DFE2D0306046C3A0848",
-    "hmac_key": "1FE0C9FB0DCEE2D1B95644C4B2E4F88C9F72611AD9794A5F18F05BD726E103FC",
-    "adv_salt": "E878",
-    "plaintext": "3FFC81E390AEACBB5966561B6726C6949D1E11367F870513DB",
-    "ciphertext": "F9FC49595EFF1AB575F7682526CD036824C06AE6402880695B",
-    "metadata_key_hmac": "7E52B09D92F882B675288104C25E17DD8070CFD78CAC299BAB4A903367FBD4CF"
-  },
-  {
-    "key_seed": "6E76B182593F7B2DD6FA658E31CF5DAB5F5DDDE891A4585B6372FE6B3FEB57FC",
-    "ldt_key": "01DEBFAE8E615BE20A0C581A3C68A03205FF0E79D148E1402D5D5FD1948A7D26FC5AEC1DD9F0BE409204BDB9915EDC01F738882F4662322A4905112C1B2520A8",
-    "hmac_key": "EEB2093740E790CF3A0231BC06701F97BD9FB3F1F4EFA5E4F4E7771C8A97C7DC",
-    "adv_salt": "56E4",
-    "plaintext": "FDDA577E1B84979DB007BC7A3FA1A6D2E68539149C88204FB063",
-    "ciphertext": "905DA74F695F60AC01DB7B83AAFA2108CACDBEBB534ECFFAEB8A",
-    "metadata_key_hmac": "1EC6EC98754BADAFFD56BA5684A1C6066589BCEF6E3E8B073DBA049E6974F293"
-  },
-  {
-    "key_seed": "06633F915364EE7DA5032267B80CDD9673C1307E0629BFB5172EC4BC1890128D",
-    "ldt_key": "1E807E019B269BDE087F7394D73647C1082FFEAFDA1ACEE060E24FB80862F3117E2A64E93549A98668776A2F0A693065AC03818F26833D7D8DB5AFB14CCD5D6D",
-    "hmac_key": "8A4F56C0A75EFF12CF5035BB934D29D26F69DF10368A7ECBF6E930EE42254397",
-    "adv_salt": "9BEE",
-    "plaintext": "C24669C503C8F725AF5002AE03FE3FABA3CA1328B902FDB174",
-    "ciphertext": "FFC10045E9999158B05869CB81391D213C0BD179771D42DB9C",
-    "metadata_key_hmac": "85BDFA0A3290DF03B08DA1DC895399D4261EFC63CEB3B9535D001C1E8D9745D5"
-  },
-  {
-    "key_seed": "FDABB479A313776E95440648BC65B72394CD844601074628D4AF98A9FE796A10",
-    "ldt_key": "EF6D7D9DBF5853F602AB40088C44EC77BD960B19B89977610D09EFC6966BBC8AD1D3D01C529D9AFD9C6A098F876544C4EA3075ACA78FE48E7F440BB62AEAFA5E",
-    "hmac_key": "D64C0D5308A0464CAC76D3D4E3DCC2189653ED32C0406283368822A76D7DD8A1",
-    "adv_salt": "60E5",
-    "plaintext": "082669393BA15D7929EACB4AEC506627DFBD37",
-    "ciphertext": "C5E74FD9A5616AA14D03AE2C6CBB236AE58644",
-    "metadata_key_hmac": "93547F72D6E299FA2FA0DD443D94418F3DED56ECE4D9413C5426794621778057"
-  },
-  {
-    "key_seed": "A3707E6C0C7947ED67E1FD8E47CE9B43A0ABE539E5ACA5DD056A9D940C612E2F",
-    "ldt_key": "884D6D9CE55DC275244B39489B386352C4ACB6C73202F274E8B1E68D1614CFBD61916E72C93B5369EE0669E5DC15110C4DF8C3BC6E74F715C0549D26112F5974",
-    "hmac_key": "E3144304EA455E4CAFB2B0B92FEE7FBC5A7063B80D7881B08C405573487733D6",
-    "adv_salt": "3594",
-    "plaintext": "3DC2614ABEC46AD9245BDFEDF3E90AB5DCDD54BDA57D591AE41B075C81",
-    "ciphertext": "0966FDF24C0774A04FDF4295AC2FD6085CDBB83A2FF13265071A57A914",
-    "metadata_key_hmac": "270AC4FCEB6A68B2AF9E453355E472F3A826F86880E7376B9428B8A6568E5CA6"
-  },
-  {
-    "key_seed": "9FDDB21FF1B2D5EE2B1C70280757C1AC4B0AFAE49C7451D1BCDFA2830F272910",
-    "ldt_key": "0F8101DC92E00FA2903774FE987214ECEA4D16B036F23CD2A5344979983D6F2BB84898CC466A2A65D05F336CC8F7252EC48965E4D67CEBBCFBB04B21B48ED143",
-    "hmac_key": "066010A645379580554905B125B611A2EF57780C27C6F3628A2DC88AD132D9F5",
-    "adv_salt": "7BA0",
-    "plaintext": "82A976792715216048435D7A5E28263FD2B458C407449014",
-    "ciphertext": "071D6F551533BD63BA43D8BDAD6E530F7F377FAADA3EAE4A",
-    "metadata_key_hmac": "DBFFCEB7CD3896814DC58E17A466434A2BC791046C0C4E01B1E5EDA3D000F8D2"
-  },
-  {
-    "key_seed": "AB4D2463F367B5BA727CBBC13FA9C72AB706F201BC64DE192DE1BFDBC219B7BE",
-    "ldt_key": "E16D3C6A029ABB4B7D5163A6A25B0ACB78E7971B5F08C3051D3006474D509743F88ED8833551E255DF3968F7F24E7C51124E2E87CA67F73AA4872C3880ABC470",
-    "hmac_key": "D826BB876AFE81B61F6BA5C3393F6A4DCF973D2D926280A1573008218983E8D0",
-    "adv_salt": "95A6",
-    "plaintext": "607FB840E8B048F4D4625FA0E5D743F3602EE464D43BBE14BEBFADACE2B7",
-    "ciphertext": "5B784C3857B316026480CD50FE8C5B3655CDF92217A2DCFB66786BFADA50",
-    "metadata_key_hmac": "E85C2D425E9227FA22516AC78E9517D29C48EF1CA281C1C07BFBE0BAA415CC2F"
-  },
-  {
-    "key_seed": "8D20F9697E22B91CF93AA7C352B089D9A3297354589CE1A53B9B40F879C72C48",
-    "ldt_key": "D2854282B9675BAD6E79BB1D65FF19A6B2E0B7308FBBFB0C5781F4735D6C707C5514EFE445EA6854690BED816FE34C1B47B38372CD36849CADD1764A0BD24ABD",
-    "hmac_key": "CFD97E20543B0E9AD692E7381B1B42C774C70B91BDF39072706CB97ABEB6C5D6",
-    "adv_salt": "CD47",
-    "plaintext": "1F2F14E5043E6A530CB820C2758B9D448B74227A97BD2E0BEA934564",
-    "ciphertext": "580D1F5A222A20031D2D3F44B763EB815DBAEFBDB8C457B323F6D933",
-    "metadata_key_hmac": "A44A6C8830FD0FF0485B590EF0475B1F5448C5DBF7E13F7C20F68B13C8F64697"
-  },
-  {
-    "key_seed": "F6B71D5239CD5AAA18564AA774D7B2628877D82BC68D6E0A13E624EB9451F2E1",
-    "ldt_key": "60E1C3F87F89B8EDBC2B45FC64378D01F4449302B84F7BF7FCA2A3012BB72FB3792CF55EB840B8843A84908586411B3324AD7BAF42E6C2EC40BE1F4F412B98F2",
-    "hmac_key": "C081F2666BC84CD686727A7431AD0CEE91179AFB22FB828624C52D1E0789E521",
-    "adv_salt": "48EC",
-    "plaintext": "1813610387059591A293B9D98FB30D9D0D",
-    "ciphertext": "FB831DF209BEC56D426A05EE003A2FF34F",
-    "metadata_key_hmac": "9FB36CF49F3B795E939B77A007BAF7FA991B98C8DE1B7926F809E2642C92B2A0"
-  },
-  {
-    "key_seed": "10309B258D701F43599041C06DB1F3CE87D4A9B4CEC36414EE0D7CE255F58442",
-    "ldt_key": "63B92505655987A94A7E3BB6A36FC9141D94BEF3072A77D414CF0B05A080C1981C4FD35BACDE42DBBD6EEF20ADD063721404C921CBD13AB29AF6C614DF559130",
-    "hmac_key": "8C62F3E01B2D15A9BA355AAC65A1A687FAF4ED164C824AEEE528FEDAD2D15814",
-    "adv_salt": "4A5E",
-    "plaintext": "A70A217C659E6F0B650842272DFD5406380D2092058B758A0DB610E89256E2",
-    "ciphertext": "D0215D41075D0B1B0E4633FA7329268E0D8F0C4712EE8C3123D102D680B3AF",
-    "metadata_key_hmac": "D71636B40DCD5A88674C1289064092F56EBA10FF80262C0D4060D75F5D03C9AC"
-  },
-  {
-    "key_seed": "319AA8669DE0409951869BDD8E187364B5E573FF4E9D74615651A3888E5B56B6",
-    "ldt_key": "69E63159756AE45791F9759493EC1671E7CF5945829E5D41EDC375220960F6B46E14D1BCCB5FCCEA476441811492F0E70492574047B8554D0FD5EDB134E42410",
-    "hmac_key": "5564DF9C3C2B6096B6A38F2AF78A1A663D9941D67732D36D172ADEF63B69654F",
-    "adv_salt": "28E3",
-    "plaintext": "39F0F9DCADB181522561E84FC46D34E3FCFE00BC12BFAF",
-    "ciphertext": "7B0F9B1191859FCA946F4721427C7BCADBFB5C0A2D23E4",
-    "metadata_key_hmac": "4F8802A57F2E9F82F7FE647CC1E30890E8881C7EBB2C8986BD87944013E78CC5"
-  },
-  {
-    "key_seed": "17F3FA6C10FF6A77579CCD13E27AAC691AF8FB37D6CA844EC670BBD3DB03D155",
-    "ldt_key": "CDCA67E54E546D5C8B8AF84782568C9DF533597BCB808102CD1F2D62C6689AFEC7D3893D52BAA826C7B263936635F89495BA755F899A434554B10604398A954A",
-    "hmac_key": "97030022D81816F751E0FCB0EE9DD05839647DB0750C938EEAF5AED0AA3FB00A",
-    "adv_salt": "242F",
-    "plaintext": "8074DA96478FC4E9ECD3F59BFE13B0EC573D",
-    "ciphertext": "329F0E00D0F2212614C251CA8A8C50EECDC1",
-    "metadata_key_hmac": "C8F96DEE6E11AEFC53199F00FCA91E314CC1EA713DE70F05A775671C12C36A9C"
-  },
-  {
-    "key_seed": "964C27CAA6BF2F825E41EA1F620E71F758E1D36FDF38512F5DB0BDC52AC3736B",
-    "ldt_key": "D3D8A93A8922B4274ED6A725F5669AEC307F523B0F6DBC778002559512E9CEE4883F8F30A87083D8F060F1745D6E4810E576C45ACC4D01DF0CC4DDED984C8713",
-    "hmac_key": "DABCD3EB3BA36EB49A3DF8FD227CC3E85180DF2896B19F12ACC33D073E293516",
-    "adv_salt": "B068",
-    "plaintext": "AEA44E41A42514CC1D6FAC97E781A9C407E1565F484FD077FE47",
-    "ciphertext": "75A0B45E8DC44D1861000EE619F8C14E3F412CEDD553231F5177",
-    "metadata_key_hmac": "295ADA83D819E4525AAF23E04BD2A6DA0B13E1B77661F95CD005CB9561C004B9"
-  },
-  {
-    "key_seed": "4131694084A0F5262C980FE2906317562C10192185D04D1B75C26C22E686BF7C",
-    "ldt_key": "C4C6D1438EC865142134800A830C8A9282BA65D23E02BB3DE2AAEC36CB0D4B7615C1E0A959D70646FE9870F2900FCC56CF2AAD47E49318CF11097A4DA7193AAF",
-    "hmac_key": "F9AB08C53D16EB0F1276D256864F7062F688AD79C476BF320911444567B7C0CD",
-    "adv_salt": "884D",
-    "plaintext": "E842B40BB94678DEB414D41B60FF6056EC62598A",
-    "ciphertext": "B90D4049D479B7413A7F197C72B26F31606A6744",
-    "metadata_key_hmac": "AC26DB08DACCB7C09831E60A0D35D32992A3F87C736FD68D287776476E83EB6A"
-  },
-  {
-    "key_seed": "67221C5A236C00EA542ACBC5F8B5E1A80B26D0DD3D0322F811165E58C312156C",
-    "ldt_key": "38F913FA317282391AFA810437BE8416AC2E3046F7A156D36E857D4C016F68F0D89A1F0162A3A36238EF920648033322ECCFF6F5535FBD7E9A673E3CA17F2D38",
-    "hmac_key": "45813BD36DD6911DDE03AD1C7897302F9D289731D15CC7A96A15C23ECB1CDA53",
-    "adv_salt": "B19E",
-    "plaintext": "1C5AA1067AA3B115EEA5D3E3E84BA2B11FEC946EA2A3000DA92CF9",
-    "ciphertext": "089C8CB3869212FD06AC69C53EC3AA1BE6F05418FBABEE91A25095",
-    "metadata_key_hmac": "3CA1371FF09C3ED2863D8CDD8DB242D8BF7DA729A9AA397AA41E13BF5F1C9110"
-  },
-  {
-    "key_seed": "12F66691E13976E6EFFA1052C7CCD589063B3E3958489DEBF28C0B978F7AEADB",
-    "ldt_key": "BE49ED6D0CE4F218AA43FDBEDE9DF1DF61F4029ED04B47715B35EF39AF58A0C2D53952DB7D5D382F2ED481B3D1AF2650C17E03E0D360394D4730BFFD18CB9CBE",
-    "hmac_key": "D70E3A2C91CA7BCFE12F6E399849D798E06822088351038C8AFCA2B9C88C4184",
-    "adv_salt": "D093",
-    "plaintext": "DACFD4AB31F83481E90BE4A24D5D1DA8AE9AFF",
-    "ciphertext": "36411E0DCF60BE64F20AE1B3CE78D04FA1BC33",
-    "metadata_key_hmac": "89D101967A03B09679F10FA8180F0B0C93FB4C304DC5B43052EF416BA712C245"
-  },
-  {
-    "key_seed": "14360985FA56AEEA01295504B697CCE71CA97F320AECF67CCCA057B3E6B8E1CF",
-    "ldt_key": "6A9161F3DAF50D1852B6D076B015C07469A92ED8A098AF98360D7D651CB3763262ABCE1F23308102CBF72F358D15C2E36388004BEB177D93DC804F835DD165E6",
-    "hmac_key": "B1DA99E46F5C4802DC600729025BE877DB66BDB0C2CED86C42B96FA89949EB5F",
-    "adv_salt": "E3FE",
-    "plaintext": "522C385C5BE019169734BC78060CB9BBAF",
-    "ciphertext": "69C9DCC4CD2AC58FE60463C786268385FF",
-    "metadata_key_hmac": "49F80DDAA5E01DB556F9C675558B5733DB710B94FC545537F9400904406C87A9"
-  },
-  {
-    "key_seed": "5CAAFC9531517CA3FADF7937D4E3BAC96A3B927EC1DD30E44E55F7F781FF6675",
-    "ldt_key": "081F4D3B01082263E3C72AA40A02979FE308D2D46BB182FAA58790654E7D72AEE16054400EA773530D92A2195A037DBCF47D48E59B636C530BCFF99BA72F2157",
-    "hmac_key": "056363C59E47FB4D4D9CF0D7BE8796696A2B91070DFEA7D7FE197B63F0F623AD",
-    "adv_salt": "456C",
-    "plaintext": "ADFEDE24989BE8C725A2DE6A6EFC62EFBCE7C155",
-    "ciphertext": "F7E6DC4F7449E2C77D62AE1EACA091C8B2A0B41E",
-    "metadata_key_hmac": "C3516A1F01D8126FFD22918F85B97E7BAE27E511BE13AE1BB248F93497E09649"
-  },
-  {
-    "key_seed": "0861E13FDE5F610055D2FE92F147C7792EED119EC6787B375F91246E583F46FF",
-    "ldt_key": "6C5711C97FC4B4E2565C9186DD0D01E91F9661BD92C408EB7048C658CAE9E32FDE306E62123C4708D340FD7C6FBCDB35613854457BAF42F9B72317325D0E99AE",
-    "hmac_key": "F35750F1B6C786CCFBDC8314B46E4393DB5D8D52A799340FE8D4C74A74CAEB3E",
-    "adv_salt": "61F4",
-    "plaintext": "25A29E046F7178A54B52CE7AE9FC5C5D6D4D",
-    "ciphertext": "722940807F6E2311D4BE22263489A4197672",
-    "metadata_key_hmac": "E9282C0B3FEB515298D39CE65680CE0CB1874D01378122C658705682A6B9E2E4"
-  },
-  {
-    "key_seed": "824F5BB7A6DEEC43DC784F1C0D2BF162FB9ECF77A8FA0B942030AED0AB41CC3D",
-    "ldt_key": "18733F13E708B6216C5F3826FEA2A82772B93CD8E2E40B620082DA32683318B26A6D41740CB11D2E7665F7439D4D7FEEF4B4BA31E547F5A917BD48872FDE198C",
-    "hmac_key": "D5B67686860DA34EDE5A5F1FE0A7999FBCDEC0A7E7F4A3CCA7E480F65DB75976",
-    "adv_salt": "80FB",
-    "plaintext": "18D3BB1FEFACF5318DAF518BCD53F679B5F90CA3D245745687A31A316EAD63",
-    "ciphertext": "DFC2B0933B9FF52BEF254683FE1EE7D4B7248C8573C6267CD0BF44D193D47D",
-    "metadata_key_hmac": "407CCA730FB95A64283777A363CA3B43AF9478CEAA52BAC4907FCA3B783B460F"
-  },
-  {
-    "key_seed": "361238F7C3CC6B4CE9612E38AC7B139208DD3AAA50E39AF703D68D83A2C2887A",
-    "ldt_key": "FD5B076CE5A0B6C5DC5F5EF0C2C7F23685ADBD429504B24ED906D0ECC4B1A6DAB4382532086FE35C9B7378B03D3B69C7DF7F29C9822D9050D3B8F40F0D98D3DD",
-    "hmac_key": "B8EB344B03FEB7A27A30854515A7CF4DC42FBB2057023321E0A0E1DFB5199449",
-    "adv_salt": "8012",
-    "plaintext": "E12A66BD01BC858B59D808EFFFCE87B6FF2DF3712B41A11A9BA323",
-    "ciphertext": "2F50633541704D77A97363688D071395AB687B26AD282CE39CEEEF",
-    "metadata_key_hmac": "7DA0CF023BE8C09B06A086C99850D691F832F4B868874D87BA74F50E0ED3457A"
-  },
-  {
-    "key_seed": "A104A99A4AB90E10ACEFFCE0D51828D25C06EC0966E9FFD9EFE51962AA26C739",
-    "ldt_key": "A91E76856009A8EDC71E653E4F4A9BC0BB78EC5E429DECA5CA7A073B09FF40633D03487022876EAA6E757F39BDE654BA386612470F4CA01AD9F47A137A190DBC",
-    "hmac_key": "E4500584AC08831C48B62C58988F3BEA6FC8D03C4DB4E4B7C17D77C54DB63114",
-    "adv_salt": "1579",
-    "plaintext": "8E6054C4B53E03581B9B1714BD22593762B165048E54C0",
-    "ciphertext": "7A48138BA8B2562153E039E2B45E29570F7311A073681C",
-    "metadata_key_hmac": "403CEB92D349AB8A4CA0A6C8C96760232FBA04C8401F10BE308F1ED364542ABC"
-  },
-  {
-    "key_seed": "493BBFAA9C6957F17A93E4097DE5097DD63E3EF6BCD1125CC358F9A6B6E1DA14",
-    "ldt_key": "D21A883029B60B08FFC5D4EEC08ED47EA86578EB50D6812B1928F9FAD1BB2FEF253426A1F7D84913166F76ACF82CF6576555E1CB42D976910B6F92C165A48791",
-    "hmac_key": "8060C30A4D7DC672626461653788B56B73C2F8C035FD061FDC7817F3523390F9",
-    "adv_salt": "A1BA",
-    "plaintext": "3E99E52496E838A70A5B57E7DF237E58DF4557E3",
-    "ciphertext": "77798A6827527FC66970CCEBD643A08E3CAC090E",
-    "metadata_key_hmac": "1735F7C2BE88BE734F77043D1F1B613A84824CE21FE0A4CCF1E71550E0654841"
-  },
-  {
-    "key_seed": "A1A1BC939762245C70223C8CE227B61F3DFD89992E8464F22D3DBE0D498887A0",
-    "ldt_key": "3A7E084A470EB974216E6180A31E5E8980D9FB5FEE342AABBF41B5F8D6A6525B279A0D5EF8BEA8C315C5B8C4605DF4D69F679EB5419C8E6C99DC85AF6AC67876",
-    "hmac_key": "9CB48820D7E8D680E256C48A7C7A021DEDF1B7F02B7A9094EDC911639250F8CD",
-    "adv_salt": "98E6",
-    "plaintext": "73A7B437A17454C8BF34A1FB582E45A8162E6A36C788565E7948A3B6BF",
-    "ciphertext": "8BC2F1EF3CB36AD65CDAD1B35388413375807E65AC2416CC68D20A8574",
-    "metadata_key_hmac": "13164D584E2A4F86A3E3AE2F0DBAACAC9C4164311EBCA4EBC583B63E172E5E4D"
-  },
-  {
-    "key_seed": "121B7BEFFAD0D37500C1C822E374996E019EFF17838FCEA37D9C58AD30136BC4",
-    "ldt_key": "5A656392D62FF8F0381F14A16D6441C68B5EDBEC3B119B59FCF464C12CF27606110A296B837420F8570357D3EFE269321B72D75D45C344E10B4DB2C8E248EA2E",
-    "hmac_key": "B000E9A48241937E4559949A02B8FA78FD46F4190F4297814305CF1A0BD720F6",
-    "adv_salt": "9759",
-    "plaintext": "5247E5AEB960892446421DB1015BBDAB6E2392D88E",
-    "ciphertext": "D110708EFD5FE1C0CC82A34E980923A02AAB7103F1",
-    "metadata_key_hmac": "52133F6FC1BBD9AEA4805C3BCC76666A03D23EF58A5E3BE81D6A6BF4EDAA2FA8"
-  },
-  {
-    "key_seed": "3283BA6E329F70ACF8ABD7C63A2FC7ACDC80EBC0E1737BDC65A6B5B5EF5D86E1",
-    "ldt_key": "DEB59C6003F26311905F0CAD108A9087A736828956EB2F928EF0FB92402C02F5EA94589A0607DFDBB392B26FE9029C1E11AF5E40B799B72397B76A09825CAEB9",
-    "hmac_key": "A3F0B2459D0B7B8DA6F3961E927D82352ACF411C7EE14AAC215385EC091276E0",
-    "adv_salt": "61DF",
-    "plaintext": "628F6FD1578E761FD5AD0AC525371CAC5B41E72677E8EDD97745E46369",
-    "ciphertext": "05A924C5247E2CC787B704FBC6966449611CA9E5D61FF3A7BF7867F26E",
-    "metadata_key_hmac": "B31D61D1CEE354952A519E19E92DAAE3ABFCB3320F74672C19C5736CD280A63D"
-  },
-  {
-    "key_seed": "EF919FA12B5B15BFC62D5586AE1ED5323DF4470782AC548362F9C62181D1C9EF",
-    "ldt_key": "53BAB9765840DA3CAEBFFD5422146BE1D566D9698920757B1B5378F93447DF2F1F5BAB7E2BEA7A97718F4F1D323141D5E3EB3D1682CB6737EFEE711C9E353DC2",
-    "hmac_key": "BCA9DA4053E80799C743DE5408422E28F990582BAE83F81FD7CACD9814B5D749",
-    "adv_salt": "2AAE",
-    "plaintext": "3C67FF1DEB70CED949A6D210A5D979CFD3227299F03E385E4F415CED374F",
-    "ciphertext": "C8D8E5BD8F0D49762C35A8D4259C64C35D5C0733CD6B370D9DECAB955EE2",
-    "metadata_key_hmac": "89F1F8BB8859DC5956FE742C2855EEF0EB95F57C174A80B1499547EEE43E587C"
-  },
-  {
-    "key_seed": "B0526E489787BD01FF7664E3F8C219A71C3DDA7B3DE7AA4509E6A3B01508D16A",
-    "ldt_key": "9C9A9D6844FA9678C2F839A339E038CF6DB0A1E028239D67BFDB23FBE84042CA7E1FC8DDBF19B06515CEA2DB2437699CF1F8CFD6C45E896317BEF67F916A8519",
-    "hmac_key": "0E67540AB6F91B30E6A41BBBF26A406213AE98C1A247242D0113EA3701A3530E",
-    "adv_salt": "373F",
-    "plaintext": "0D5EB90F9B5B2EC2647F11762D58571E426A4852",
-    "ciphertext": "37C284E4194ADBD968FFDF4E966B01B9ACEDFF8B",
-    "metadata_key_hmac": "07EFB90AC463F9193894306983A81E8EEA260D451B9F572EF03DA6CB5A50D94F"
-  },
-  {
-    "key_seed": "BF0546F7590534B3821388F8F5961F0554C5138FE4C6D0FB185BB2AB01F7D215",
-    "ldt_key": "50B0D627DA5633BCD173D0BB243B4CAAABA8F8ED9319719E78FE32F3319311829472C2E5B4B951D7E2EC7F60ECB29A191955A062B07CC7DF0E7D7CC27C478A31",
-    "hmac_key": "3D87AAB29C200A92B4DB66640ED7EC91C44E7A1C79D8201EFDB4E11036572566",
-    "adv_salt": "5DD4",
-    "plaintext": "1118063B217817AA0FE146259D46CE916808710589C01D21EFEE9B3D",
-    "ciphertext": "8F714851FA0EFE27F18257F518E67FEA494AF899C2843FB485F1A301",
-    "metadata_key_hmac": "4C279037AEE50554B7C88A6A418415D14471E5D667D66682621B3C5F2C959979"
-  },
-  {
-    "key_seed": "5B05A47AB82F86E8E555BA28C93A2B5BD3919EC88B8FE13DBC3E000B11BDBC7A",
-    "ldt_key": "1C4EB7C214F4673DA1D789A054A96081F0D52C064128204C5F39294CF95BF89B42F1D537AC992F23BF40B3BEC22FD2D757FE8B6ECB3888D2A1D171F9B2A688F3",
-    "hmac_key": "32A5D4ACA2FDE2F18544A0DE369B489F36451E75A1BF54EE2E30982D0EF63A07",
-    "adv_salt": "F8F0",
-    "plaintext": "DF3730EF735DFF609AEABB5880EE44FA1860ECFB971C6B24",
-    "ciphertext": "BCF079AC32C7FFF6C2C1D8F54CAB35EB6B4DAD8C9ECC7DE0",
-    "metadata_key_hmac": "0588473D41C7197589AFC3EAF61FB7C02C7EA8FBF25CB7AB1241DD2E78F8B145"
-  },
-  {
-    "key_seed": "016A954E67167DD3C6EEDF2201F07C51A95DB91868753CF2FC4AD3BE0EB58CEA",
-    "ldt_key": "6D269972E508187094ED221F529A9B6C923BFEE2F35912BBFD3C0E1E9A97F71766A2309603CD63CCB85DD369A30A88BE54D3372100860ACE92014F5194A32341",
-    "hmac_key": "CFBB2DA33EF9F573C5094A572C408719148B7CF31A2CF927F682E36F025670D5",
-    "adv_salt": "18EF",
-    "plaintext": "3C000A877C960594EE99C3F5D8AB04FB5A56CCCB3A0B7640",
-    "ciphertext": "C7AF1071460C8C732160254E1DEF4766500C37EE246AD71E",
-    "metadata_key_hmac": "F01F98FCDA25180AB59437E5508422B04498A00F987A5DF890DFF83342CB16E6"
-  },
-  {
-    "key_seed": "1D45C25E8C05DF39422A8470B062E3D3602750C31BC987576FFFE8F416F6BE2B",
-    "ldt_key": "470FB25C112AD1E5D5A7C23B028A83A53FD3C0A49C94DC9FEA62A6C8A6B44487352A3084E9E321A92D21FF6B5CDB5BCEE8D20C3B342F1EB79F7ECDBFEF5953FB",
-    "hmac_key": "66D362DC79BE14FCA6A867D0F57F511097E09A520D2D4661F2AED055429B3D4A",
-    "adv_salt": "02B9",
-    "plaintext": "70FE775E7053C4500E1E09546871E8C56C83D4088855C1E2BCF79179",
-    "ciphertext": "61FAA929A050FD83C356FE82EDC6B256445BE6AAD4DC59AF79C3B778",
-    "metadata_key_hmac": "19138BB4447855BCB6F5CAEF83590BD64D3CCA580139DC49E642D924860517F7"
-  },
-  {
-    "key_seed": "4079E48E0C54A9DCBAA6AE5D3D5E1F637BBE715645070F5EC8C6223C099FA200",
-    "ldt_key": "D1F346CDF8D5BB69C38905FA3063D32C9638D2A68CFA2D6A0FFF2A58F860490DA2C251C147423A8F2F4FCE252985EF27B730E637F5C680935938EAB954AB9CE7",
-    "hmac_key": "C735DF5B94B8FA5B5A2ADA614220F92C5CF8E0FCA99CB3A4D750E1ED2A039291",
-    "adv_salt": "A397",
-    "plaintext": "A72410DD92A65CCC4888931049EC0468E015970E",
-    "ciphertext": "009257F0EC78B65A8B72E25E0E3215A0831C3623",
-    "metadata_key_hmac": "7565AF05C99D2EAFCEA91CB916D423FCED6C8162D41304CCDE1968FE408D6BE0"
-  },
-  {
-    "key_seed": "88A424FD954EBE8DC69E9E30567CE77BB1B53E1E41A50DC4656C69A15F0850C5",
-    "ldt_key": "4B5BEF639496BFF747FBA58EBC073BEDC5D039F46A521DD2511478751242A90C1C830647CE0892DABD501E1E7BB4A710E795822C6BB20EA2536F0381810BE55E",
-    "hmac_key": "C6D6ED6C2334032ECF3A7EC2CCCB6561D3C7B537069F036BCB97E07D4479B41D",
-    "adv_salt": "315D",
-    "plaintext": "BBF8E1F2B005730BC7674EA7D1DFF3AFF1C5E4",
-    "ciphertext": "48B50ADB7E937959BAA183739D0A89F4A69355",
-    "metadata_key_hmac": "A2C6011E155A45EFDF6138F038042164AE016E498A143283691D79C5BE32DBD6"
-  },
-  {
-    "key_seed": "863275E1E0DEFF0D0FD958E6DC5A3761E147270C8C8F7CAB31A03D4616F58B89",
-    "ldt_key": "35D4491ACF0D60D7163D9353A2CD150085CDE38B613BE14774CB0BD21D4B1A7424D3A2E9BBE5E7386E471BF0BA9BB952E3AE23A7073AC2C639B73A885E0AF543",
-    "hmac_key": "E1443390930FE359DE459A8D1DA38B2991D47C5F14F3AC6C7AA030608F579B69",
-    "adv_salt": "E0AC",
-    "plaintext": "803CC93E2E79028A2F7296D4FC882806D2B430CFA407631662879C50",
-    "ciphertext": "694088F041A81951B222EF17CE1ECEB37D3AF87D6B380BA0A70F2BED",
-    "metadata_key_hmac": "2F435D6B95AD5C0EA3958A58EC6F62BF04971DB04D3ED8B8C0ACB041FCF6E592"
-  },
-  {
-    "key_seed": "93BE31C376BF990F9B7034BD9F47529F30F697EFECD4306E3696BE5386835E87",
-    "ldt_key": "0A406E34A9837F09EFCCDC7F39BA22E444BAA886B4DBCFFD4766BAF934C66920D29D7B3AB531CAA4A6986A29919968608610C3DF26C075C8324762AE7FD90B5E",
-    "hmac_key": "D5CF9D473D8DA0D9A9109B94EAE16476165CCBEA5FD233349E0DE96BDCF8CCD1",
-    "adv_salt": "66B1",
-    "plaintext": "0F42AA0A9FAFA18886917149BE8B3C714EE062",
-    "ciphertext": "CBD82E0A520ACCA985029E0AA1BF189F382355",
-    "metadata_key_hmac": "19A86C5A7B51805E382AC7708A57726FE77C76BCE55DE91D936BD027BD2B1689"
-  },
-  {
-    "key_seed": "82F95337DF211FD4E70D1CC2C368AC599A34D09FC0B83F86A3F8B6D6E8653BD2",
-    "ldt_key": "4BFF8FCBDA91DB38F96E500E2CA2F4579F02B14AFB20D97DAC5CFB0EA36C4BEF68A1D3A9F92720845DF021DA8D2696F3C68C3ACFDDDC5CC60A41A8F312E66C4E",
-    "hmac_key": "9258C9C8CD52AD8716B4DBD190EDA023D2D4F4D4BA98A5F43A882274B06E2E97",
-    "adv_salt": "3502",
-    "plaintext": "ECD5F6E936557169BAEF1A1ED8C502396BE78AC5FF41DF18",
-    "ciphertext": "3E61F4C2260310F5E7DC3D935C7AE90912ED45CF5F60C8B1",
-    "metadata_key_hmac": "E0549A2681AD6FFD30EC26541F650A2E7644C78C02AC6CDBC086CEC3F27C845A"
-  },
-  {
-    "key_seed": "D08D49FF0EC2457AC2B47DD5E076C243DB9CF938EC63EE0AE9F4D0D7BC43AED3",
-    "ldt_key": "293A8606CD8E064A2EAC787255B00BB3662A42127ED09B32D660B10341754388B53B6F17A7621FF269B27B2B780DECCD11E66633D461D29EBCFCA62748BBA705",
-    "hmac_key": "8BE0557DA21E1F3B747B3EBF15859841F629F44DC30DEDA8A9A586E5D976FDEF",
-    "adv_salt": "D0D0",
-    "plaintext": "B1F527E6C259C2781855C2E1E297A01099C0F80D031705248936BF20",
-    "ciphertext": "AFAAFA362084E8B77486EE59AC75A7646EAACE5494ECDE40EB4411B9",
-    "metadata_key_hmac": "B3C654410B5C661BE11298BE63ED92F4650A691D16A7A2C313854FF26E2BA9B8"
-  },
-  {
-    "key_seed": "521760699E3BCB6251021ABEAA830B1F67DB167D20455B19AD07F24BE483EFE2",
-    "ldt_key": "3044BB7DD4CF31C278DB63F877A10CB5CB81766AF22CD129896D0D62162730B50ACD56C5063972C6A9BBED8E01DE0B6C7B3F766E91AF73B851AE44B18EF972FE",
-    "hmac_key": "0213386D6DFBCA6EDD2DB08F4EE70B887F004441A54401FB83BFED71938E67C8",
-    "adv_salt": "89BA",
-    "plaintext": "84D603076EC41D5B6BB74189C99D746FADF7A6555D0CAA0B",
-    "ciphertext": "1C8AAFD2B9737BF33D62EC516EE7FD5BC1A7DB3E2D6DC45B",
-    "metadata_key_hmac": "CDDDAF18EDE36FFE9598CFA4B20115C8A0791326DC4A33414B4E93B405620583"
-  },
-  {
-    "key_seed": "11B8B00D034B9DF61299B2F29B03A0B08124D1A937AC31FBF93CAEAA8F85CF98",
-    "ldt_key": "45C93BA189AFB8E02399E779C4B775E049C9A63930CBAA189FB84E2C224FF1E440B751DA15C380E4FF8C044DB43CE7E833CB096266B3368316F20416DFB57A7B",
-    "hmac_key": "B86DE2F36332C247C765A22CCFED050338CBD95D514893790EB9699C4203CE4F",
-    "adv_salt": "DD44",
-    "plaintext": "8CB48D8D29E58610F7F6AED0E4A214EAE07982DE45",
-    "ciphertext": "26CB68CC2DC942CE7A89C5A0A78099AF417FFC0C41",
-    "metadata_key_hmac": "F49A777771265636ECCD5D341AB712955D86171203F789DC6081D7B3397D6525"
-  },
-  {
-    "key_seed": "517CCA5DBB573E8E9BA3061ABF8B971308A13FBD0AF869F8A5FFB74406E38DF0",
-    "ldt_key": "01AC8A445ECEE85A7E47397D9E6FDA83D2EA705E1920739FD94C96C82FC50E2E0B0F9DD26E06C2D6622D0488E3D9241D33D70EA125D7FDC901B4355B7AB5E05A",
-    "hmac_key": "1EE05ABAD13ABDF7324515B027A3703BB156111FE3F7062815FE7CF8364716ED",
-    "adv_salt": "9971",
-    "plaintext": "718AB14FCD50872E689F89FCE176581FE2B521BDCB85E0667583",
-    "ciphertext": "7F1FE50239B5FE13E098D9FA928B617A748A81C01BCD74FE53EA",
-    "metadata_key_hmac": "BC53EB7BE36084F0E5C03123F9D96828F3182E035A20C877F9B91A837E383FC4"
-  },
-  {
-    "key_seed": "184EE744A6A2D5F85E2471FB75CE2393550AB2BD625D45D0B626B6A328802453",
-    "ldt_key": "724A5561CA95F06957A90573F334A24C35C757F0FD6474E377A68B01BDD6D3C99C8569BD9D073BD78ACFA7BC1D49D0069FA345D38A01BBEF1EF566174702F9F6",
-    "hmac_key": "7155DA1938B23B4309E3A8219151EFE732EDB4B2510E8EEE5331B4849D1A10C2",
-    "adv_salt": "39DF",
-    "plaintext": "40640644862F8D4C5F12E848B749EC94C2A07DD182",
-    "ciphertext": "E6DC4A99E93A734B6096D626088FB5240A105B7894",
-    "metadata_key_hmac": "4919CAE2BE79680549C55858B16EEDEC91D531A88860BB4FA2FC4B9EABDAA545"
-  },
-  {
-    "key_seed": "CCE8B060344E2C562C6BD07C3E61D14AB33350FE7C9ECAD8DD932A1C4636659F",
-    "ldt_key": "4A9A66237239D55FD96E7059DC9C6B21AD9D6BB7019C352B62B210FADF9DD6BE6DE8469E9235BBA9A225A29C3CD82B324019309D93324A14A56E56C31633C4E6",
-    "hmac_key": "327D980355945948B4673D9110FBB960F5080FFF9E9FE2C0CCCC9CAB17016C3E",
-    "adv_salt": "6A85",
-    "plaintext": "F862FAB1CA3D9E87CC9F168CC747A81AF9B4044E07730FA6130909E9E6888A",
-    "ciphertext": "0900AEFBDE843F82F7B7CB9B80B5B3D3A3C724EEBD2A296C782B80FCA0BC5B",
-    "metadata_key_hmac": "7A370EBF0C39BA95A9C4D86FC67DF3EA2668DAAE637954A7A25ED2D3E8BB6F89"
-  },
-  {
-    "key_seed": "BF2D1F6B6E0AC585BF3ADA122F9E5EA2FE0FC417A2318EEAA10237A87E636487",
-    "ldt_key": "F0C23320CCA42CBA4DF4FE33F09D38AB98EAFE2256937C38D6A9207A43122535133B407842CF2E8DDE687018413C3FDD7E38951EB858060C17CE8E2760220324",
-    "hmac_key": "02F49A21DE768BE1271597C2264EA7934C062FC34FEDC559E94CC7F2937A50E5",
-    "adv_salt": "842A",
-    "plaintext": "ED816815D9A9295F02B02086C743C9C451FDA84FBF784A49FF",
-    "ciphertext": "FC09320F13D88D9217DFC63256D97D3CC6C324552D06625594",
-    "metadata_key_hmac": "AC4017FC376EE3A0B8BD0C4EFDAC9C861DAF8B8E2358C04F8D4E709ED2E7387C"
-  },
-  {
-    "key_seed": "C93683C86F62EE4BC02C6D7492677DE0DD018635FB42ADB94B2EDC9BF2530891",
-    "ldt_key": "8FBF58A09E057DF5FF33B00DDDF53A1CD1A59F2C79C8E34DB7862EC87849D997AD643A3FDF0C6A6A5C865CBD82CDB9DC9AA4537AA4D97516B7DAB71A4B604C2B",
-    "hmac_key": "A629688DFA58DC48BC00E9D2551F330CC215AEBE1DD870C1845F9986D205972A",
-    "adv_salt": "3E7A",
-    "plaintext": "35C533E05B6729ECD42F50F788208D2B6718E3185B33",
-    "ciphertext": "9DB621E8B9EBC90FA7C3510FA6BD3D3D1BA260FD6906",
-    "metadata_key_hmac": "A09A9FECD04BC4BB66C47317538F49657CC932CC25B5E60643DA5B14E064892C"
-  },
-  {
-    "key_seed": "61A4A878EF91F9EA94C700C94849874D3415DE7440FC15B7884E227D1DC1A5A5",
-    "ldt_key": "DB3339DDA7CC4ADE9F2988DDAF92C9FC1DB2D0B87750D5435A25C5D0ABE27C4E368FB68D27116DE07A198CC61B3A31D7D0BCE5B8A501C4C59E6025FB848BB6E9",
-    "hmac_key": "76990316DCE0BE4F8794DBB52842B5437D96E5DD3D49398D63D862F68D1014DE",
-    "adv_salt": "33C1",
-    "plaintext": "1DBE8F58098F3A06F6F6CBA2D7A6E65217491783A5D4C59475A7C27B40",
-    "ciphertext": "E60BFCD9FD955F9D0DB1D524B758490C213971ACEEC48B149B048D87BE",
-    "metadata_key_hmac": "535971167ADFC167E91D11ECD8422739E7A15FA751BCF173E42557AA1578CFE8"
-  },
-  {
-    "key_seed": "A298E4B46350537A38408F0FA075D65E8F81E9BEAE562D480FD9664E98CF8F2B",
-    "ldt_key": "8978D10F567CCEEFB6D0D19D5E72EBE8CBC3053F917EB6E4E2136DF955A492CE250B9B3B3D0107A80D2F68B75D7FF7532223EA174CDBBCFAC9183F9025CFFD99",
-    "hmac_key": "8D165A6DB03554DD46483F915CFF47001853973140DAE9D1BBDFEBBC56577135",
-    "adv_salt": "50DE",
-    "plaintext": "EBB25578833B3D7B24457CFE8BF06B0B382D",
-    "ciphertext": "033263D9264CB66C088DC9A10CD9CAA1FDED",
-    "metadata_key_hmac": "BC7FBB0A989FD95FDC28A4354FF9268C726A53E7A759DFD7468077806E1A47C2"
-  },
-  {
-    "key_seed": "53B80C4691384A1D479EB68CB55772096EEB11CA2E1FA35A2ACE999A7758CB6F",
-    "ldt_key": "28543FDE924317016A04FA6B43CCB16BA5C97DC322A97C041842549D909526FAD717969B8BFD617C32ADB6C184B8CF167FA25727C7BCFC3E91E18350FD86AF2D",
-    "hmac_key": "A5EA03BAFEDE6525860E63D548CFF9D712527D1625698626EE30314E78115819",
-    "adv_salt": "E0DD",
-    "plaintext": "0EB1D50AABB50EC745AC8A13E8BA0F0E132069F0339C8A6DD7DA",
-    "ciphertext": "236D656E4FD7F3C08EC6BB93923140DE5A1B12400225E7A0D5EC",
-    "metadata_key_hmac": "BFFA3A512F9CA607352BE92335D02EE1273CF008F8FE1F1752654D364B2E1307"
-  },
-  {
-    "key_seed": "0DDD8C420432B91B6DE1A2D6F0F78235B0D551452A50584F07AF5D198CAC8BFA",
-    "ldt_key": "E8F608B4A2B2C992EA1BFA82D99EC4C6339B1ED050AC7B9D7EF0A4FCE806947685DE83EAE9875DA1445F5C338512E677E30589DA7A8C3684DA48018AB2F68F6F",
-    "hmac_key": "D26A557AD5FBF38425DEA0760B1E8C5909A6D4B8C204AB95CE7477E531E03354",
-    "adv_salt": "8B71",
-    "plaintext": "B048E510CBADC24B14539533BF5494FF49F2C3",
-    "ciphertext": "2F782D87EBA2E7E9C4A0F77DC590DD19745E58",
-    "metadata_key_hmac": "ED761A5953DFA5A8FA6A005F96E94EEE234C2ACA6BDA24138C0C45BDE95E1FE6"
-  },
-  {
-    "key_seed": "FA75CFACB662E891B6414794E0DB2FB4BC655C34525A23CDCB0A2E0E85F0D3B3",
-    "ldt_key": "43FBE39E37AFCBE9675353529C7DA24E29747C8020570BA3411D8180F73AFCA4F990BA56F70B0F968E9417D7439E0085CE7204D6405BA54D5E13C14D5E34C5CB",
-    "hmac_key": "4D525776364E2BC6F1C5496BA7870E5546844A2B903D5D755592178986657F41",
-    "adv_salt": "F145",
-    "plaintext": "795740E52EFC0116F7DB7BD5E06C941AB40F45B7BF43C30A1459AF3E",
-    "ciphertext": "8222D51F1133C66F465C178C6E143405550419AEBCBD16F64BC42B57",
-    "metadata_key_hmac": "5EC58B4CF605F3A8C2BEF6BDF6D5D433479CE0EB89D7AA94C5A31D6FA62BF7D8"
-  },
-  {
-    "key_seed": "D81F137A8A592F5939DBD29612D4AD4C041D85F97D25A6AF5E2AA0732AD5BB7B",
-    "ldt_key": "A2C2EB3CD286C292EE346EBA49520957F6E614897A8F62FED22825B85B3549509C468731A4B02A2A8DFCA42C06E80DD7B6871CEE493E874AE3142DB499E3DF8F",
-    "hmac_key": "D1191674844A540B185E390DB8B54BEA11AA29B29FE368719AB0BE396AA26E55",
-    "adv_salt": "5790",
-    "plaintext": "1E0F660610C7DD20815985F7416C0A4FFDB6FB976FC5ADD58BF09A",
-    "ciphertext": "F976BDCF23C7B104D73C9C0349C2CF39DC50798376D722B602B8DE",
-    "metadata_key_hmac": "54B3AC82E8C3326BC4A32C28D58978A4AB01E5E565877C9A9BED7FBBC5BE4E81"
-  },
-  {
-    "key_seed": "433500D60CE472A1B0412C1DB49129E8E3ECCB69DFDB2071BB213D79C032B649",
-    "ldt_key": "42137CE289F09CBA88A147D3587CAE932AAE2CE51603AE5DD256ABD3A32829ADE7276E447FB47051CD23B5E3A26D0B12480DAF79EB0F67F74221A1B4E6A5941A",
-    "hmac_key": "23C59E52C6EFDAA59C94D588BD0D9B08E51B28218E60508CE7B2DB8424C6FA21",
-    "adv_salt": "E058",
-    "plaintext": "7C9C51B6273C3D129DB1185E63E691D3BE2F34F84FB7E59E3BA25C4580A4",
-    "ciphertext": "31DA8C38F317D49EE84696E3689DF4D7574AF0AF0935F181EB415359A0D8",
-    "metadata_key_hmac": "7FECF203B6F8BC19C6220DF71CFEECDB3933182D59F13BB64690DBEFA4905B6D"
-  },
-  {
-    "key_seed": "AA82ACD315420135AFD50956D58C741AD038C1CB4ADA9107E62C544299E8683A",
-    "ldt_key": "3A869E32C2D2D0549CB585A85256C24B9E38066B4F970FF221B500E77ED87057FE0A312BE868D9B2FF2AA428850EA878D1BAAD7F8C484786A23ED8574A9EDA73",
-    "hmac_key": "04B2AC14A413C82D59A7CB32AFF74A1A0286B35C79A84B6433CFE5B547B53D1C",
-    "adv_salt": "7CEE",
-    "plaintext": "7E30FA659ABC63016F9475E1A43F9C6BE6F63CFF35D5",
-    "ciphertext": "7C026E54E258A22E2FB306FAEC611F3A27022D8B842D",
-    "metadata_key_hmac": "BF8EB548A58FB84810D5F89985BBF146219A9F4B6D6A88976098712D8D14BC52"
-  },
-  {
-    "key_seed": "AF2BD2E9B622AF968A9E28F2D01DC6125600E2F2C919AE48334A6BB334F50681",
-    "ldt_key": "D2BBB15D29036398CB23FC21E76B3F290B2B6D8B35A2DA87D822BE554446AC37FF6E1178CAF3D3CE33BFA6A23C7216D21845F4B633DF1D886C27DD7848A00DA1",
-    "hmac_key": "AC3D67D7B80CF27FD06227DCA2A866538CE5381F6D91C1B4CB02C63D67D5D391",
-    "adv_salt": "DD61",
-    "plaintext": "DC0F8D81D76F502DE49B8DA6CE0BC92A4F85",
-    "ciphertext": "16136543D9435ABCE30BB6697D0724FFA2E6",
-    "metadata_key_hmac": "50BDA48BDA68014C2B79912CB5A78496F02A7E9F53083E287F5F793F8B56C834"
-  },
-  {
-    "key_seed": "59D1CF20DF00B037EDE35BFF9C2D81916C24ECA0CB2AF45F8D5D06771146CA25",
-    "ldt_key": "435B0CDDFA496C3DA6E878E970ECB03199777DB9B550B24593D14B46CA0D4A5C97AD3F24EEAF69713D1DE1978D899E82EAB4582740C87E40D3A886E51BF5C7F8",
-    "hmac_key": "4928D4497E1B1A6000CF7F5252828851FBDE7CDAE1FF94F120F7853CB8C5AFCC",
-    "adv_salt": "80C0",
-    "plaintext": "05E40AEDAB61D3640704C79A414D4A33E1FF26B0330F",
-    "ciphertext": "299B3B353281727F86451A1AE59551DDEE62DCECF28E",
-    "metadata_key_hmac": "A871AC9C5D5FE41100F24595F76264299737B9DE7B8AF0DEAD676EF9F5720B03"
-  },
-  {
-    "key_seed": "2D74CAAE7632991E402C74162D00FC74E2265D4871B4C111EA8BC7C0E8FD2BE1",
-    "ldt_key": "9765F667EE3F8B8AA71EB0E9C32E52E3B40543370DBACB49426DA778EFC5FE8F686B033AEF2D7C812D372864800466A213B3588F24757C1879BF542DBB8772A7",
-    "hmac_key": "504E5B2C9489CEF0694226A8FCAD0C8063C2F036237BF8B7F0671FE38FC60346",
-    "adv_salt": "8ED7",
-    "plaintext": "F9606202547972E7B5AE3FE384B4CCAE39DB36855CEAE900F0F0",
-    "ciphertext": "15232259C054006C5E89CC14639C5015827E4D4FBF55C8240E7F",
-    "metadata_key_hmac": "CE489D80B643563798BE809C94D7A999FB9CA939F5AA5B7EBAB0EAB388FF54DE"
-  },
-  {
-    "key_seed": "5CD613D109C542459ACA3951FF5F604189ED8CFAEFFA098C20F3ED079EB32BA8",
-    "ldt_key": "6C0A9F9767AE7955820A782B8316BFAF9C19C0340BEF88EA56BD7183B3AD36EB10E6C68A7FB2F1777BDDCB33061E4EDB2D5D217888E6561A106B7CB4E7220728",
-    "hmac_key": "57384357B50B14AD37868828A49C736E4BAD95EDB5383FEDCF621CC8EB573E2E",
-    "adv_salt": "BD33",
-    "plaintext": "2AC767D3A0758F2A3DC8944542C667E10A73",
-    "ciphertext": "F570C912A712DC6CB04F265E5BA15EB02C37",
-    "metadata_key_hmac": "C9C1040E72EA6FB67791E341E6719F2B14CBC1A8548F078F73F3A7A8D2C6FB41"
-  },
-  {
-    "key_seed": "0A544CD0DAF4EF5DECE34A6280AEFF409D2051AB2AAFDCA7391B39B88C9F4C07",
-    "ldt_key": "023581D6D30E5D936FA2B412197EAB6EAD5F0752F74E0D5CAAC57A5690108414235CFE0E34DF176ECA8518B4B9A3414466E73A9125DDF7CEEE546561D074EC3C",
-    "hmac_key": "4FFFD0CFE5B6933288346DED3EADA6F011F1E8BAD400282CEB53C318710C4175",
-    "adv_salt": "EE60",
-    "plaintext": "46D08772F5BFED8CC74E63E416166F0870F50E25B3",
-    "ciphertext": "C243A96B6F5C7BDFAEAE49DE62D0EB20FDEC8AF155",
-    "metadata_key_hmac": "5BA8DD187189BE60A71EBD4EA87044E7D1459C529B5056FDFF710328ED8FD687"
-  },
-  {
-    "key_seed": "FED59799667B62FF95C0AE5855BE981A03017356EB9DB2832DC61AD91D09C8EE",
-    "ldt_key": "F0EF9BE489F809B06D8F63AA60D581E45569B97E891CA517AEAD9D7544992A0D3BF8460DF4C625B511FD21CEC7B6C4299DD8E3D4EC1E6C7C144148FD9286B5C3",
-    "hmac_key": "D2E7A262787F60E9BDCF746CECC80753BB9AD621C293E151DD338B536FB5F237",
-    "adv_salt": "A49E",
-    "plaintext": "874981095C3E8D802B865229B6DE0188B794D59D",
-    "ciphertext": "4F4D01BF0ADE89BD44EADC09F48D2CA2ACCEEA57",
-    "metadata_key_hmac": "E65BC7BF3DC8FE3EB2C16B03C8EDF6A40A6EF40A3CA4C25F23D67E95E7465F62"
-  },
-  {
-    "key_seed": "9F909799C002F7A7BD4D7334A13AF9D6E23D22D045624C8EC6AE1B351A90291F",
-    "ldt_key": "8819682BF93D0863E6FFBDE350D390EE42669A293B08281C7C79655F440ED73132C145142B5BB4D25876B71FB9DD43CB6563820C61A968DD0E3646AAE284162A",
-    "hmac_key": "14DB97DD2C418D68E32B59A92725F17EB329D58E700B5629FCAE5DA6E0C3C917",
-    "adv_salt": "FFD7",
-    "plaintext": "FB1701FEC5B4CBAFDF5A466BD5F03A8A55",
-    "ciphertext": "8574791206F55805D0805CC8B55FA27306",
-    "metadata_key_hmac": "3080E8846D6C5FE00E48AA116A69DA2B2A12A963A5ED099E193ACDA3EA8F85C4"
-  },
-  {
-    "key_seed": "E6AC538A534AE767E973EE88870C99B895E64C90A4D35DA089BE222A1390A9A2",
-    "ldt_key": "EBD509985542019467D4D2DD645E75A85510D3F80F13C57A2A3A93136345C1BEE714564F63D1B8A6ECA4C5441AA65F3FB90260FCE0CBE0D71A83F6487A935F7F",
-    "hmac_key": "E9C658B89C127FA335B5288393338E425B10D91B0A6D2CDA3201E937DF005089",
-    "adv_salt": "9BBA",
-    "plaintext": "D3D8FD6F5F87E6495A1074BBF96893DD0FDD4D4AC7A646D71E6032A940",
-    "ciphertext": "811F782FCF532B8CE2DD7D9BD4FD2E1BC53F3CE766CC806F5C22EA845E",
-    "metadata_key_hmac": "81ABCA37FBE03E4A93BAF5B39E1E3CC0C5B8860CAFE27F2A2DB5941901151A18"
-  },
-  {
-    "key_seed": "318829968E74DAD7AAEF1E0A38B75090BBFD615D498BB0D3329895B19A99AC0A",
-    "ldt_key": "DC38FD22761C2AD95226DC62E59DB6B4F2A123E8B7F0B051190AD15A3D43FA615CF7EF343F9C71ABFA7DDD82B11B75F8A6C9CC60F637195E62549A4F901791D4",
-    "hmac_key": "110B7F59B772E542CEB985D76597C3292E203A8F07C3B61BB16DA496A29287A2",
-    "adv_salt": "6828",
-    "plaintext": "86EE8C8CCE12728BDD3F1D4981AAE9BBBBFAB9B0AF19268B1C76D5",
-    "ciphertext": "5FD08415921FA4EBAA96E5AE431046A5EA303644EB697E0130F296",
-    "metadata_key_hmac": "F6DA270AB6BC300455B1B8ADF4E735D9A6424F3483282BA3AD7C6E5E6416602C"
-  },
-  {
-    "key_seed": "280522A0E9A7D6B93559616E459677950385E57120976CB16143ABCD016B462A",
-    "ldt_key": "8DF52AEC6B9151A538696F1A6D5C909B183EE5ECC965FF857E23B2053CAE6BD48A9233AA15168E1FE6E34BFED7C08A5143250C642F0D315D2BA3D6751B4CBEA2",
-    "hmac_key": "FF0DE7047DE672CDEE76DA1F2AB7A4F6037C8056B87AFB01D23775154CFEE49E",
-    "adv_salt": "D224",
-    "plaintext": "8654C8C1662FEB3C4081908308079B533C6EB7C482",
-    "ciphertext": "77749830BB6B9368783A0170D9A09923C0DB7215AE",
-    "metadata_key_hmac": "2965D442FCD5C276D98973B74C2CD551F5EDF365691EFF05DF27EA21570A8AD3"
-  },
-  {
-    "key_seed": "44E769CFDE90D9F3EF56A653F28ADDAA06B2CA60EC7FF422879ACAC2CFA46111",
-    "ldt_key": "BBAC7B253EF46F3617DE1B9FAC5131135F2B213AD6A20ABFE422D9CFEC5EC85320DC4FEA089AB4D7DF679CD2507B9BEFD3A86468BE9E3929572F684AC6A320C3",
-    "hmac_key": "E41BB366EA27E78220208EF81F75CEF9F6E462E5C2F981EF58811C60B9663499",
-    "adv_salt": "3D17",
-    "plaintext": "A155D6D26B2F127E2FE82378B341D2FDEF3E16288ED7D3F3AC60",
-    "ciphertext": "8EDE7296186394BA203754260F747EAE8360480CCF21D0B6DD8F",
-    "metadata_key_hmac": "74009FAFC9FD260F001569709C4295991C242B5A61E6757AC708ACC3353CBF5E"
-  },
-  {
-    "key_seed": "AE66C35F7D04021A5C698C352F4F0E9D6BC341054228F6FB9244B777C31779B4",
-    "ldt_key": "AA3F95B3A0E64A2C4C92DB7683E4A9FD6B5CAB46650C09BDCDECA9DC8767BA9813814FACEC94CE675DFC3550B238A069CD1C575B43754E86B1C2447647D31FF5",
-    "hmac_key": "FEFFD7822CAD0ECD152DC7D05BD7E928F97500EE40995B30DB922489213180A7",
-    "adv_salt": "7B92",
-    "plaintext": "84F5E66A4A74125DBFF956A02C3803AC",
-    "ciphertext": "D3DB7D16AF86E6BCC2B517D58648875C",
-    "metadata_key_hmac": "8E2F1AEEB263AACF0282E9769916C5A265CA1F44BBBA0987D828410A1BB12321"
-  },
-  {
-    "key_seed": "35B9E153848ACC7A635654570B9383281080B8534BDBCEED1603A6F8C6D5507C",
-    "ldt_key": "9FE7252B04B742365E177DC551968D561DC229A579D5D300DDBF64F5AC4972CB1F68E04B547E63497A5356B34A34F73B1202BEAC45527423DBB839D218ECD30D",
-    "hmac_key": "2B95CF79638C004CA750F26A19226A29697D1FAFA2B4F3BF8626F8678861C13A",
-    "adv_salt": "6B57",
-    "plaintext": "35AC4FF891D302D09A2FF9B0D79B690BB2C1C3C62ECB8CDD6C9BA116",
-    "ciphertext": "9CF13BABFBD80C501125B19AD377B6257191404D3EFE5B41D9AF7F09",
-    "metadata_key_hmac": "D3C52F6F990063859C79B0D3C92210DDD4B9CE9F15DC889CABDB87F3613F733C"
-  },
-  {
-    "key_seed": "687532B1C30FC1EA07FC5E016895C91E6AEF0DDE518672CE737BD4DE49848C7B",
-    "ldt_key": "1F338AB5C464BBBDB21C74CA46824422055F2D936E5C8E7CD9D070AD2B94D8C2B143BE8ABF4E5011B550F36C702E4355DFB2E2790B3FF94B77BB8DCD66EEBD4C",
-    "hmac_key": "00AB3E77B01DDBFB63C0DAE4F3629D1D36DB38A80CB9EDC6D24D48A9FB2C22E2",
-    "adv_salt": "3CE0",
-    "plaintext": "CB64C632A684EB164D597CC76A77D37DAEB7BDC3CF0564E4",
-    "ciphertext": "CC05C33E64E9070A0BDDF3EA43B3E07B20ACBCAF7887252B",
-    "metadata_key_hmac": "DEA7BE9F28AAC7BCD020EB419A7382896B6F326D3EE31AB8CB5120FE17ACDCF9"
-  },
-  {
-    "key_seed": "1076CF7AABA9FE8F884DC407AE488AFEBE10CBBB638D9F30171F1FE11CA9E490",
-    "ldt_key": "6954D32D9BE52A9FCA5868AF05C31E99B23AFC57F35B5B85BF85E881884B6B0B7925AB1AF21C6F0B70D139558FEF434DC55A42190EAA5DED0903A99D101A79E6",
-    "hmac_key": "EB95583262E1F170102BE84FA40A8602D81FF7F4284A81136BDA6C44E6421908",
-    "adv_salt": "083B",
-    "plaintext": "AF30C1BE220AAFF63753CD60888CE865C1AB3962C7696D60",
-    "ciphertext": "49C25447783F57672C331C9CC44AB38BE9E7C08BEDB25E65",
-    "metadata_key_hmac": "98D61316118887FFCBC232C0201D97C1B6F1F1208D46786349031081FB736851"
-  },
-  {
-    "key_seed": "E540DBF513BA5D1D660FA9BB7EC880B13B9525D415909172C4FB6449E1091C3B",
-    "ldt_key": "641FCED17EC3ED765FC414C6310A994E141E97FC01674C04F5686D48D33CBC92929734DFEEEB95D0461AE32043900D001DFCE6ECA701D9355AE713CDC3FC671C",
-    "hmac_key": "44E3408B3F701FD8E22D0520B7B5350A7DE5623A2293AB3DBD4EEDE8C06F805F",
-    "adv_salt": "0A21",
-    "plaintext": "DABA28E713234BC8A65B5E6A9876EAF443985058E704E9D5E6",
-    "ciphertext": "C8D35138CFCBB94F1728BACD1431A53F443715333D7EBE1195",
-    "metadata_key_hmac": "322E76085DCE4370101F0FA295B2F7CECA5FF72EFD74A8A5D36F1C86ECE65D9A"
-  },
-  {
-    "key_seed": "1A0258CB8FFCF6B1A4060754D79300B678395BECF87F7B291A81660A73A45DA7",
-    "ldt_key": "F74BAB65280D7C964EE58219A8B03699D2B8B3836466698BE00264EA10D5193BB2193858AEDE740D559D60B0016D21F85CCA11E006D9587D5A56813363ABEFB9",
-    "hmac_key": "7E74C500A645FE5992A8F5C72156B7A8E7E35E6FD8E24CB5B0F5E87ED928C82D",
-    "adv_salt": "C93F",
-    "plaintext": "A4B100A7F1EFFE4CFBAED727B0116008",
-    "ciphertext": "FB3F0705BB236C87E99D98FD7ECCCB2E",
-    "metadata_key_hmac": "B281E7B6C8F510047D3707C84FF05AC1B7DA7B6B2C90AFA43977608BC889E3DC"
-  },
-  {
-    "key_seed": "CE96629C4259457DD42183D24D651A0954568EA47DA08BA41540665D74592BB6",
-    "ldt_key": "13224342818872CBBDF1EEFB4E7260DD673CD601298D06705ACB56391FF6FC44B5F4F2F38898CE23538152192F54C9B89B9CA8BFDB6D144BEF24777F0D469DC4",
-    "hmac_key": "C77A50FB84F7C0F35264E697CDD070AFC17452B08E5943A6F98A59539BA5AD67",
-    "adv_salt": "EC98",
-    "plaintext": "CB57670713089CA8EDA983C4C0212DC1F699FC0C6A5B",
-    "ciphertext": "28E09ED5045AC92F7E08F62283908CA60C4EA8558E77",
-    "metadata_key_hmac": "0B714038F04BAB7ED853780BDE53496BE9A883CDA8EC4B7CB7BF2E04E62C6790"
-  },
-  {
-    "key_seed": "8CDD9F5539AF7EA7ACEC71C0D43CDB1B3358F7FFA7AF5DFB77C9796F9D108830",
-    "ldt_key": "02123AE1B3033A2C4832936464714F4BF5DC16D641839B451FA99ED98076909C258244FDB7384361991C27FF34ED27B3C93AAD0E22B6E6005CDD7C8794145F00",
-    "hmac_key": "9AB04D6FF1D83EA7C7D987F19595F9E3037856B3EFC34AA74C30273867850549",
-    "adv_salt": "D034",
-    "plaintext": "2FE3A1A36EFB21407D313E64C8BAC756A1",
-    "ciphertext": "89CCCD395B9BFE3C0BA49F9B9DCB529B1F",
-    "metadata_key_hmac": "C89507EF56672FC994F5FFBB93F099534D3201D6FA97122FBD5D2FF279525C0D"
-  },
-  {
-    "key_seed": "F3CAC3D370DF389BA4E949320C7AF35347CE90B39864EB28F1BA7325966163FE",
-    "ldt_key": "0FA705023E801CF9626F0B0B29446A1D5E3B7EBF8DC4228535565F8096D5EB374061C6AC89AB77F8FDF15209A49DA4162EC3A7731B695C8BA97E6A6A54AD927B",
-    "hmac_key": "94509AEADA6A469B8AE4A4CC57771F386F4FEADF8E073A758DCBDB484C9C1472",
-    "adv_salt": "B505",
-    "plaintext": "C9CEA0BA6FF74A45345DA313F27F1E78206741AD1BEF",
-    "ciphertext": "458A3FEA342BCFA9563FF419CB1C11CAAA18CF74E4CF",
-    "metadata_key_hmac": "3EDF761D6F862AE3C4D6AE1932E5DEB75DF16D060A3413F3160B859EFC8C2335"
-  },
-  {
-    "key_seed": "90738CB1AFCDBAC6A9A01C668340AA0C5E388E38F4BFBBDAF3A2F1E4D00230A2",
-    "ldt_key": "D470FB6A06FE70104782740EB5C10BAB3C19AB59767C5AFECE2201E433DB125D2D4C5672596242445D829EDBC89019E2AB449E87C0A9FFDF9DB15C228D7FA643",
-    "hmac_key": "24C9F3B88BE8BFB2AD54387CED3DBA80B470BFC04A0F1FC38D3C1D818247251F",
-    "adv_salt": "14D8",
-    "plaintext": "6938B0CEE011BC98046AB7D8D906E9272A9A",
-    "ciphertext": "5329FFF7838E166A72B5264D9426BCC777DF",
-    "metadata_key_hmac": "D58E27099B581E6357CFAEB8FDB74750646C4EBE1BC8BB756FD7A2112482C12B"
-  },
-  {
-    "key_seed": "E167A2FDD3D194B125091811557C166B51286A0EF07B428ABE7491B1F7D4B95C",
-    "ldt_key": "A857775D84E7E944A0D8646FCC65A1F68D5F9416711926C426DD13C870B345AB51D5F5C67602E326A52C02AFE1AEEEEBA6FBD5B96DFC684FF672BFD7D48BC0B1",
-    "hmac_key": "A13BD102F4DEAABFCA6A7EAE6A8737CF2003BBEDB67636733A8FC1B477D7578C",
-    "adv_salt": "D1ED",
-    "plaintext": "4D2D74C7B1FBABC53F276E2054229FB59756B13BF8",
-    "ciphertext": "C8DB5D3877EC446D84D2D4C2F5BDCF725F573B82CF",
-    "metadata_key_hmac": "FE8A0D0AA4586C3AD93556FD260ED5FABEB0BCBE21B656A09ADB7AAEC13873CD"
-  },
-  {
-    "key_seed": "C2D55EDB8F61B628A694759CD39B5DDF3D0AF8D728654BBD7E2EC1641E30D584",
-    "ldt_key": "260CE6B2300C74F630074B946A98A12196EBB271849396523E75C777F5580AE4F853E7C462B1C5E95C14BF910EF567E5C72D69DAD21372EE79AD1B567D0673EE",
-    "hmac_key": "AADD4EFE6EFC77D76690E95CBBB7F9D8FBE78DB96BE4279099933431CA7C0CD6",
-    "adv_salt": "549F",
-    "plaintext": "585C510BC7605EACD72E32F06C634937A2BAFBC7780963F371",
-    "ciphertext": "BF2F3C7F84088E8F718AEC12C03B0EEEFF0954262B98A8239A",
-    "metadata_key_hmac": "1A5F73FC9A79547EE81832EA25B3C13A883CE2E1F6C9B4C1C8EA00A2C5D402BB"
-  },
-  {
-    "key_seed": "3B1DDC15E0631966EAA4E68F314DECB49E0A08F2620A4AE7B824D60181DF3BE2",
-    "ldt_key": "5B03B2A87E537B8FE4B0C0898FDE2478013225B121F225EB23998694073CFB1099944096368B5CA7E3750A0E12278479FB336155F4B128A810DF04A100486A73",
-    "hmac_key": "31A368D150A45F2D2A61AB1C1A10128AAAF46D1F6C55F370BBFF9894CE0C453B",
-    "adv_salt": "0B30",
-    "plaintext": "D01BA2A3D3791D476ED7920D9C8C9FB59CCB09D9726D81B18103E7F53BFC",
-    "ciphertext": "13FDADB34B3EAEE768A31FDBBECDF1B9D803C5A0F3503E66C4DDBB566A66",
-    "metadata_key_hmac": "10F474C052FD27D591BE50A2258F749AE1C322CF31A577F601ED10AC92F0BCDE"
-  },
-  {
-    "key_seed": "D05A40EB2A490AFFD6C9080FF0B246CD746D7EEB1AD5A989D2B70E82AE1AC245",
-    "ldt_key": "7AE2D1B2E56E73D68FB690A93180C30062CD08506A455D5940D7C0101F03EF48B91077465FF77AA0BDC56F3ED4619D0A126A2C19FA77315F24FC42CD56AF712A",
-    "hmac_key": "D26C90D9A22DFCB2BA4DD1C2FCF846DC6D34DBDF5928AA0291FC86C38BEE70EC",
-    "adv_salt": "878A",
-    "plaintext": "E41F56B7469491404C9085AEC86C8E818D",
-    "ciphertext": "791714106233445CC920E4B5E04BC78800",
-    "metadata_key_hmac": "850BF2B0F3F4E4A20CDD64DFC2E451A636EE3FFF64CB58D035647FF41F316127"
-  },
-  {
-    "key_seed": "C1D8FB7C8AEC3E472182033DF3C22486D85D0AD814A4E66ABCCE536FB2A809F8",
-    "ldt_key": "86A0CD0A7892C19CD539045D406101699423BE2E09C0602936C03ADE82202CD377AEBA7C13E8252CEC8B7ED2692F9F8618144C117E3EFBDCE16C34B03EA181FD",
-    "hmac_key": "975C3E0F9F7A935042ACC888C27BAD5B372C5744D8A70886CDB89633A947F4E4",
-    "adv_salt": "E796",
-    "plaintext": "4C632A0EBE20C353422EA7E6BECDF2187061E47B27D00FEDB5D237B79DCE7C",
-    "ciphertext": "DFC203F3CBB6BD68737632B76A889FC2B4137EABFE15FF0C89AB78226A4DBD",
-    "metadata_key_hmac": "6005486AF7B630BA2A910C872F84005298C66ECA82E52646C719B42A6E049ABB"
-  },
-  {
-    "key_seed": "7421221A0F0AD0E1E40B807C29FAB0BD3ECCE688A7AEE3B80E758BD19D4D59B4",
-    "ldt_key": "F455AFCDEB473EB52482C1B91CCF1934B6F88EA0E730EB9F4D0C0A843DEE28913E20ACA037F0E074F1B51295FDB94F3B5808A8D19CF88BF3827DA2838830FCD5",
-    "hmac_key": "AF1A1D886594E6245C51D04FAF0B6A5806D1E9B43ECFEE7D49EEDAB531BE6EF0",
-    "adv_salt": "E14E",
-    "plaintext": "4601A3D31452D93823C7D559AC189AFEAAA9F088AD02",
-    "ciphertext": "07E5F4AF8FC2C0EA45D1D2B87CC41992BC7D296D0A43",
-    "metadata_key_hmac": "D7DF9CDCB61C8C362994DBB895FA97A7D5D072CC203F4BDCB28888E884F1A4A5"
-  },
-  {
-    "key_seed": "2CD8CD00A308856401C0145E13A9CED9CAC6EF683BC3844AD943D9A5880D87B2",
-    "ldt_key": "50B75DBB72D50FC8855416F54131002F29B443DFAAB3B04D65562AD79E52E983D452359C85809D9CD5296D36E0C961A97F0903623C3BFA8FD83AB405FAD1D7A9",
-    "hmac_key": "30650BF4F161E2C0C3889466CE63FA9873EBB8E3ED44FDA2694DB232ABA80097",
-    "adv_salt": "67C7",
-    "plaintext": "FA83732566D4C7F669E235EB65395E9EA20CF710182C1C91AFD56E4F",
-    "ciphertext": "D5FC0C956FC70FE612244BCA166E0F7251B4985FDCE2EB37ADB9492C",
-    "metadata_key_hmac": "ED4DD7A65B7A9395E06046252B259B01E4FF4DAA5F31E3B4C887CEEE8BD8C422"
-  },
-  {
-    "key_seed": "961EB7E66755382EF32B91C881D1B370CE8C84472AFAB8730CA72431CCF5709D",
-    "ldt_key": "D20A6B1DCF00991DC6428BC18539DFB54CD4C7139D174D279A184E7124921E52AFA8203AD69E8C344F8DE98AFF20A46F7ABD784FBF7282CE8E2ECDDA130C0616",
-    "hmac_key": "BD12138D7A6759FE88B57A2ACAA3C2AE059FD223A7F768FB14C92247480F45F0",
-    "adv_salt": "6127",
-    "plaintext": "C2EB434C6012DAF38B420B10886DD4FD0B4F47A4C46248B3647E9DD2FF1337",
-    "ciphertext": "68B23472CC617EE2524CD544553E372A4073943EC98578209189E80CD972E5",
-    "metadata_key_hmac": "85B4C89F930106E07EC02C74C6C9A367FBF755D81C3FF360D4DA4F29C33EC151"
-  },
-  {
-    "key_seed": "EE97B09C86EC831939381B61A2BC9A8CB54D7D6687F7FD500DDA53C04504C400",
-    "ldt_key": "076C50D225AC3DD54E785DA9C10B29ACF481868A590D6E11458A68253039E32D99FCAF2CBAD329909D411C329C0F2BE7B03E674294BC983F5B1EF24C1386734C",
-    "hmac_key": "C602826941014B984F279A3BC71E938B0A2441520777F9CB904D66F4FFDD2686",
-    "adv_salt": "4707",
-    "plaintext": "07B02E871111A6AF85CAF0CBF4D2E2A6930C405F535641",
-    "ciphertext": "31F1B4569BF4830BEB16FF2627E27CAF73D100241F2D26",
-    "metadata_key_hmac": "3A0B7F8E3527682993B7B168A8B6C81C16CC6E41E90A2395AF25BDA99A9DE9B4"
-  },
-  {
-    "key_seed": "90B1F3C4E5E3563776576A41F54FA8F8FEC39ADEFD7151930A6F71F5CDE1EAAC",
-    "ldt_key": "23F9BC3BCF20BC098A85807C8E645196E4414E7A1087752A0E5616F59ECDC1BD6A773368D51E6CE75D1BBCB761201BC127F1A7A23C6ED791DF3BBE39C2498A99",
-    "hmac_key": "97A26BBED95B16D4292F5191C6A7B24E2AD2A2A1185BEB3880C0168578E8708D",
-    "adv_salt": "A852",
-    "plaintext": "4E97A89CCBA3AC8D5C722F06C6DBE3AF7F068FD7",
-    "ciphertext": "0887E7F846287FC4E1533DE351FB7806C036CA89",
-    "metadata_key_hmac": "F60321123B682D9E8035890BC2F00EE7C263CF198DA707B639306EFBC7F4E625"
-  },
-  {
-    "key_seed": "E0B3093E67FA08DE9808AE03EADE5A2F6C52C64E6C1D24D209F8BF01D5C3B216",
-    "ldt_key": "71722699582831C8E407B0E8C2D55843747E180920EBFC05EFEFF2FE94C57B9514F196E6F8C837831580F721242E60232819BEC77C50E4373070D38798CB066F",
-    "hmac_key": "A8FDD59FFFEA81F66F03E18071954CBD48697DCD56D61B38DE5F0295B04E0A76",
-    "adv_salt": "CECA",
-    "plaintext": "C228D2B9F0483026D26010ABF7EEB54FA60FC0BF607F7F3C73721757C13AB1",
-    "ciphertext": "8DEDA4974DEED69DA408D36C85AE1753DBDB8E57DE92D6DB419C9DA4C359DE",
-    "metadata_key_hmac": "78EEFFDB4E52ED95F569AFE6CBEAF1152BFDE33FA4082FFCA081B50E1E1EDF59"
-  },
-  {
-    "key_seed": "3D0BFA5E1D7958861EAC95B30C156F8DD930F2782F53E5FF949A0A403B423DCB",
-    "ldt_key": "C3FFE404D89D676A1914E50DC99A946AE7A7DF295593617A8C57B75BE73C5633F9C6511921D17217BAF92FA5561548031CEC00B060C431A5614E3F4BA52F089B",
-    "hmac_key": "70A971751BFE0E714DC85968C09FC4DAC29CF9142500AA4BDDCDCEC85DE0C4F7",
-    "adv_salt": "CC21",
-    "plaintext": "4832A069F8CFB5B21FB1D82E7CF9BCB42FD58580776795935F638B42",
-    "ciphertext": "EDD7669796B5D75F82B7FD1463DC1ED7FFB188ADE2DB85B2F46B5941",
-    "metadata_key_hmac": "1456E2336700FAC9ED4BB80EE85CF82F70A1A3BB72CBC34E420A17CB7F1B1A92"
-  },
-  {
-    "key_seed": "BFE9774B7CD737A03FA3AD280C1BE486CF0C83F3CCF24DFB01EAB69DD95CD6C5",
-    "ldt_key": "9EF46C8221647475DC974309BA3DA5409AA911207CEBB303E47639CC8D6B9CF15653B9676D7E93ABB82ECA46593156BA08D25E903417B1D221D1963937DF5A24",
-    "hmac_key": "E164FC152DBA6C35F277A5924A156D25FA10099DA607E318A43447B1DDBD0691",
-    "adv_salt": "F508",
-    "plaintext": "169FCF6B075E8AC5527E5B632EF377DF7745DF0229B788664B52F0",
-    "ciphertext": "67287D2CD908E7A0DE65253DA8B6B9010AB975DA5D97E0E85B3E69",
-    "metadata_key_hmac": "CB23A8B0C9823C44C095FEDF0BD001EC65C33FA0CDB6C3A27835527157195F9D"
-  },
-  {
-    "key_seed": "FC1B9AFCDA4B0DE4070B4865590D99C1B51AC16013F746F726FB00FCC490F5A5",
-    "ldt_key": "3DA1E02171D7612CF8DFCE3164158ACD84E53B3A798CC6E211D3D8E3A38796C4E632B1DD1C33939DA0191C55D621CFDCA72E21F8DA33BB90A71AC58847540930",
-    "hmac_key": "FEB10E4A8F5B536345299B277C636AE33EA21D4906D32B517FF9D84F86525C03",
-    "adv_salt": "FB2F",
-    "plaintext": "622025C907045A293B6927E386B727F592388070",
-    "ciphertext": "E68F9246E8E9068958F10322F77B066EBE0AECBF",
-    "metadata_key_hmac": "6FBC0B2F408A933F4147485BF8C1EF82C734AEA63E586337F6BF789448EC8AC2"
-  },
-  {
-    "key_seed": "82DAE4924C598235701F58712FC1EB2FF71064EA21D77A497ED141C4739945E1",
-    "ldt_key": "CF5C7363836BDAD761955D40A5F4D2603D6EDD0929DBA92F13DF876355F7AAE043D41D3616047A03043E87DBE3025AB6A9244F9A4CA23DFC85FE68F45537458D",
-    "hmac_key": "7BFF0CA343B4922CBB930FEAF6383D3CED9B2D82F5A20563F5D7D3D876881BF8",
-    "adv_salt": "7048",
-    "plaintext": "6FAE845278FC72DB91A23582318ABA051A6593DB2294",
-    "ciphertext": "62821D5B511ABF78E0EFEFDEF3C1B9D077C488E94F26",
-    "metadata_key_hmac": "B46CEB074D0DC925BD81C0A9C42DEB23FD601D9CA19D751B9DDCFBEBB477C828"
-  },
-  {
-    "key_seed": "AFA2295B197A4F7B02148083E172EAC8C990D103EE110E5FD59D46A63E880A4C",
-    "ldt_key": "706F784A5BA178B15D0E73CBC5CE3AD0BC2D804AEC44C80A6AE7AF3826884F1DEDD40069E1EE47861D1FA8F33E810D02CF1056FA35837B1D617326DE56865CED",
-    "hmac_key": "9B460C083DB1DCE94932C59CDE980C91AC6DC325D27C6F58DB472B3C73C68A59",
-    "adv_salt": "33F1",
-    "plaintext": "DAD0F021D7D6EA6CDB5727BD07A47772EDE47AE057",
-    "ciphertext": "41703414EA3DB54FBF51F8B683F251A390B9007312",
-    "metadata_key_hmac": "BE86C7E61CF3E9B69CE5F839A383D0BE36EFAB76607CE6A9E114C23D752DE7A4"
-  },
-  {
-    "key_seed": "E77B0126661C7427A10C7C2DC888E3588499A4E608312189131652ABEF068378",
-    "ldt_key": "7C87B6BE29EBFE7C98879A755A4961A92CBECB276A4D5470EE270E0B39A6498CB605D254A3F51C9B050CEEB26F612E1033998A8A41ABB945F04337450E03CD62",
-    "hmac_key": "F34D7F614979EE2B0152D10561A8E8E791513D5CCE2715F159F472793BFFD6B6",
-    "adv_salt": "B04C",
-    "plaintext": "EAE41891217622C50954AEB273E5D1451A8F7DD53436A234",
-    "ciphertext": "1510060D88AC5720279401FCE44F6B6D669DB022191DB4AE",
-    "metadata_key_hmac": "6873F823783696ABCEA38DA650785FA304F9B96459280463F9889974AB4F318F"
-  },
-  {
-    "key_seed": "49CDECB2F2B9B4660142A86CE287EBF3004E94712D109EFBA6B7D32A3DE0BAF6",
-    "ldt_key": "B14E48697D6C005FB9C3E40B2054FA7252B7367E5E2FBE288858338C455DAA0B597DD27CC8E889C110549C7F98B40E93FC3373432D84F64C383257693F4CDBFC",
-    "hmac_key": "BF3A3BA5CFF8C9FDBF352704520304ECE0B2927D49EBB6E70FB83270C541F934",
-    "adv_salt": "0257",
-    "plaintext": "C4C77214ECA31A2095CDC93B97A4859FEAE4EC057E81CA5B1B",
-    "ciphertext": "4D69A78B88829AE1F21D402F00098D4D7FEBF07BA8FD4EC570",
-    "metadata_key_hmac": "7B136B9A716FAE1B92F66C418526B9B4606017CF0888A3A3709D0BF367B0BA15"
-  },
-  {
-    "key_seed": "5EF26FDBCAC90F504A294B0C18D077ADDA6D28BCCFE9E6D9E1019144E63E82C4",
-    "ldt_key": "4A2C98CB7211C55079CC9F33E150DCCD00EB7E1BDCBF623A1344C341D733B71BAB51FDDCB7E94C98B83C736F501C39E383AF7FED8C7E24575445E0251688B71B",
-    "hmac_key": "10C7C6D54416C6A4B9A016200484D256DFBE3238E3DE89015756219CFCDBC9EF",
-    "adv_salt": "6012",
-    "plaintext": "0EDBBC19BEF8ADCF812FFCCDE89FBDF9BA2734CCD97570",
-    "ciphertext": "0CF336739B9A76887A5CB3056F895DA3B071B6942735B7",
-    "metadata_key_hmac": "59335C71AB60D82F6CC8C7439BE3442AC17E3756CA3F2AC0C0F5F3C74C975BE8"
-  },
-  {
-    "key_seed": "ABB7CAED17658A7808C8D1BAE9BD851BE8F6B3E0417529F717CF7544FBF19CFE",
-    "ldt_key": "77A9EBF2D3C0ABD4762FF5404627CF0C73ECA59308C36B889F391E49B36AEF94C12B7C6B594261F94B217AA4D0C9B91B1ED382614245C4F449150C82CF39BDF9",
-    "hmac_key": "FA4707B6E99215DF51D4A1B2A5CE6BBB2BD820A0AC3C7D0223F47A99B3789742",
-    "adv_salt": "70EC",
-    "plaintext": "F4D6F632628DE2A5E863916255D5C91D",
-    "ciphertext": "92B542A685DF2636F0CDFAE8170760B0",
-    "metadata_key_hmac": "0B1D475B89137AF6861E8F5BF2C3EE730665A8905BA670E892012405E2D316EA"
-  },
-  {
-    "key_seed": "D6E12F6F00FAEE25269FF1ACBCAC2D63CF6A7ACD0B12A11BA84BB00F259F9E64",
-    "ldt_key": "08F4FC8579D835028A44BA9B384DF30CA5442C46F0E12361AD827D966B50C9FCEB7273C06AD0D389E634A6FC42DE00971F582043C43019EBB8C180957FC1E502",
-    "hmac_key": "78251AAD644F9D46BFA4FD665DC4E0061B6443538E064D1337C6B80372796351",
-    "adv_salt": "D06A",
-    "plaintext": "6EB100EB8083DE9FB18D83D89E321FECEF1E116C92F156457B3FF6",
-    "ciphertext": "A79B09CB86570BAEE1A9BA8FC3A54AFA49D5315092A35AB8B40E02",
-    "metadata_key_hmac": "158DE0182C312D6AD29C85DF0CDD61E4B41283339D33F44F71BB86F39AEACCA9"
-  },
-  {
-    "key_seed": "33FEDC3705F223EAF65C20AF5859C6FE4BC2BE03A76FD233CB3B3C755C19EAFF",
-    "ldt_key": "73832B0FBC059019CC3B69B7C01402297899803228C3BFEC0FEE44E4A8CE9362127FCABEAB35FFA99E53E51CEAFB52AA2ECDD98DADC68E11FE32B2B691141B42",
-    "hmac_key": "70BFA7E804F711750BD51A8ECF716D8828F34642C8718F3BE3AF04BF45D39E3B",
-    "adv_salt": "CCD0",
-    "plaintext": "83A3554F5BFEBD3AA5CD63033F1F84A188428FCE31E28C7B",
-    "ciphertext": "F61245C747B02E8A31F20C5C418C100779F3304C4BF35D00",
-    "metadata_key_hmac": "E42B1FEF138C0BD47792602506037A6B6528C6396E2C25EF759245D88BA9C4DA"
-  },
-  {
-    "key_seed": "B8FB96C60B745FF482A56D79AE96C2011CA53EE99CB4EA416D2081341AF6BB97",
-    "ldt_key": "5281D0CBCDDA4B9CE9649DD4C20CC65D21917FBF006A874774CC9BD2BE5DAE538E7AD25C7AEDD24D25255F5BF4335C1C7101FE475530BEF3FD34A599686A2EB1",
-    "hmac_key": "E5001573DCB0E863FEE47F4477AF9CF601C1488DBC8B18A1048F10F4C2DF96B3",
-    "adv_salt": "739C",
-    "plaintext": "4C01C0FC54C46EC894E31CB7A3921E4A1C61647A1DE7",
-    "ciphertext": "078A0A557B03E5B08D34CC068A228BD4076C35784D62",
-    "metadata_key_hmac": "5E65DD3CA805C6592404472BB10A04370D4E9BBC131E8D91F1E8EC7872C81819"
-  },
-  {
-    "key_seed": "3E336BE86F7158546D178E5D3745865476DE850174E4EDF93C225CC5B127DF59",
-    "ldt_key": "CAAF5ABD33E017967D0F86484CE494E76A00E7FE1D481C851133C9711B95A4C397DBCCA43DDD69A90DA70CF7DBC9C86385244968D640A93AFB1DABB14A017712",
-    "hmac_key": "A74F14F57A80A12D95CE530A4CBC0443F9A6BAFFCD0E401183134C773E3A19EF",
-    "adv_salt": "1EE0",
-    "plaintext": "BB1B2619872088EA0E3D47536E4AFE08E66B",
-    "ciphertext": "0550A3CFE54B801588F4B99477BF29435529",
-    "metadata_key_hmac": "CAA1272218E865B040AD16B0CC155CC29003372C513D41F291ADE90F32A7E2DF"
-  },
-  {
-    "key_seed": "C5304C20454F1AD1EA6F1BB2AAD90C32B21BABF043A8CD5F8AEB5806AF9DA30C",
-    "ldt_key": "23A194FA43C5CA91E5CBCF05C0729568F3E0374E2DEC7D124973498C16134645FF48590CD4B05F6D3C416AB95F1EA5005755D57DD8FBABFE2E20C731E29B2839",
-    "hmac_key": "5E4F9CE97D3DE51E2E0D9C20C2EB528286B259C12C311BCD67995075EA5230FA",
-    "adv_salt": "51A3",
-    "plaintext": "91FA8DF0FAB24EF8A69CD3DAB86C5833",
-    "ciphertext": "A960D8AF967257CB67C20C5BCD6604D6",
-    "metadata_key_hmac": "E76660ACB28395EA5E88DD38D585C7C1EEC29D107F3B5F6C5A8980FF63EBB7B6"
-  },
-  {
-    "key_seed": "7D8E85630617FA51F2A6620B7B3D6C82CA9CC9685DD1D0A29E659A3397D871EE",
-    "ldt_key": "744289C1913C05990FBA3D066A88BE2BBAF45241AC470A57A37E2077058B048CD9183C89D61CE8E4CCE409825760A121C65B9D2F5A9368E2A88D53340B0944C5",
-    "hmac_key": "A54E10CEF8ED1EE553113F7580767CD833CBF02A705A92DAF322D696B2B91F25",
-    "adv_salt": "AF3F",
-    "plaintext": "6FD2A80E81A51DBDB404188DE0D92FD57AAC5B",
-    "ciphertext": "0ACC3BB8B7735307713ED4A17BE8B78CFCB729",
-    "metadata_key_hmac": "A159B44F3DDBE83987AF66D786D8418C7FA0AD036F4150FBFA2581AF26540400"
-  },
-  {
-    "key_seed": "9EDC5C552240AE0B1DBE3546A9977AE1A78419E88F0E2ADCE71588A9AB771D15",
-    "ldt_key": "E15CB36AE550637055BDF0B0C1AC3D090F930531BBE11561C3EB79D291EA8DC77011933C945C3931DBC6C06F63DCC14856CEE2456CE836B97D939E83623A8859",
-    "hmac_key": "96A7C51657FEBB26B5C222633509B49BD0CE08657AE6F1D22462722C02FF6800",
-    "adv_salt": "07D4",
-    "plaintext": "4C7EED55AC91676F1345CB2ECAF18CE2EF16FA26378C63E6AAF973EAF055",
-    "ciphertext": "E964835FE749A6EC67CDD7C8CE659DE998AC244999D0BFB9E1101D222FE0",
-    "metadata_key_hmac": "BB41BD052705D128D72974CA087A286AA4D71823912477821215CBB7E344E39C"
-  },
-  {
-    "key_seed": "2F0A7502AAB91AE51E454AFF3C9BDD456038C067927AF5A1F59A42B8AFBE1324",
-    "ldt_key": "27F7A0F3E9BEC8576855279F97F447F14277670426B033D098FC177690F4027D532B4194E21804D6F6BAFD08DEFBAD8E33F981BB9848E3A18BB8B8D30A143106",
-    "hmac_key": "5C54CED6C94657A9FF1B83F28CB18A42C3A7F64FBD8152F32FE3EBB3B5F7E982",
-    "adv_salt": "83C0",
-    "plaintext": "002F8117AC623541B57404FDC17FBE7A7A",
-    "ciphertext": "5D463DF93F8ACB5A855AD559A4A64709F1",
-    "metadata_key_hmac": "BFDF51FEAD1958FEAB8F9804AE00DF89CFE09F404E0A33DD3715E966B0FF8122"
-  },
-  {
-    "key_seed": "E9D8098A1D31694FDE114CF400F7764AD8C0FF66C489CE968DC9B7F85BCB6F29",
-    "ldt_key": "1A795BB7754A1A31A996EC6030233FB84B4181BA661B57AE7B260C319E5EF336F221481412F840B7AFF5465D21EA64123232826F14F826F10ABA4B6AA44D0ADC",
-    "hmac_key": "0BA616BF14EB74E612C6FCE4FBD88F73E95979D4A44C9FBD25ACE1C4E0868161",
-    "adv_salt": "6092",
-    "plaintext": "1A9CF3075EA6BDF46397A57F0A41A95B860D171CCE953DC8B164",
-    "ciphertext": "839AC0809777EEAE830F82C8A1AF249362A88A8AE04EB602A682",
-    "metadata_key_hmac": "BD3DEC90EEA8EEA938D7B70F3E4CB03985DBB8A4FC429820E7D04AE44355F3EF"
-  },
-  {
-    "key_seed": "DD22C884AA53111113EB1871AA76F4AB1E123AF3DA235AF41E26C2761247035F",
-    "ldt_key": "119CFCE6CA9EC347F888D48DA820672E76E950EF1139DE4E1BDEC1B16F5FE07CF7A1CDC71AAC4924A2A261424559D8726D9AB07056E1561F76C36CFA06B2B503",
-    "hmac_key": "EB93CE75F37A29618B11E4BDA7840627AF0AB75A9E506B248881A5E5E3CEDBBC",
-    "adv_salt": "E99D",
-    "plaintext": "A566BBECAF6B53D98F48E3768E3C4FCD17FA02AB734ACA89CC55E8",
-    "ciphertext": "56AF055A77CEC1CCD64731453E9F51A3A240CBF7956C8D6EA4A7EA",
-    "metadata_key_hmac": "785124888A2E5C6C91F1168F85ED5C8948709203D55913992DE0753BD5A2C424"
-  },
-  {
-    "key_seed": "72027EE164E7FFE5B33C93DBA101C199FF334A23BB8D5FF08CA90246193C3BC7",
-    "ldt_key": "1391D93AE442808B2C6959B795E4758E1110A2AD154339B7519D48756E95FF6B9CA7FA77E3A14262D7C6972752C24EDA69349B67350EF8C8290E4FCD4162BAF3",
-    "hmac_key": "8BF72986ECCC3C76AE633DEE05E1216F1BFAF4DF9AB01B251F83DF6AE8D23EC1",
-    "adv_salt": "36E8",
-    "plaintext": "2E6C1172AE990F9ABBC99E8D6AA4FA026081AE7B3777C8",
-    "ciphertext": "DAF3A5C9929D8528CC0FB806AB8FF3FC12FC5325068AF7",
-    "metadata_key_hmac": "058AAC41C80C05F1EA135B0DDFE1C045B63D2B374546BB4727AD58F7D5384209"
-  },
-  {
-    "key_seed": "08370BB7E2746114C0D804852CD16233CEBE79649CD0894BFBD90DD8B2466687",
-    "ldt_key": "A71C6B8922C28557DCF3745959FC18304B114429210AA9D1D35F447A7B7441952D051D139C0A56A3E238C8F28165C1AD54D5923652B105FE1EBC96DBC0D9A58E",
-    "hmac_key": "2BEBBB42C76CA69E1E113CAA7F8C3F69480E37EDADA82906CF262011F246CA41",
-    "adv_salt": "3265",
-    "plaintext": "2C3AA9912DFC6FCD751F022456E01D93107F3C5D4C",
-    "ciphertext": "7602E0B3F97FFA39D8B3D65D2728AA181404CAC20B",
-    "metadata_key_hmac": "6833E4FE6D719ABABDB8B174B6B3555F85398AB47707B213A8AABCE2416A0401"
-  },
-  {
-    "key_seed": "F5444EC6BA43146942B5F030F68C17C2FADA2E254301F17D4E5D49D89AB3BC48",
-    "ldt_key": "3EDE8097F486AAC75C15A0EF41ACBE4B03BFEAD57A0A4672886BF2A4BA34E9A969FC36060833741A9BD789C39356ED413885EACDF6A0428362BCF7DB7813628C",
-    "hmac_key": "22563337EAB8375B41EF2C7CC64BF7601EB3078AE2D9D9571BBA4141187F825C",
-    "adv_salt": "F10E",
-    "plaintext": "18B2C0DEDF90B749FD5CE7CB01F3722565",
-    "ciphertext": "3E753C44334CF07794D7873A0CE293A415",
-    "metadata_key_hmac": "83ED296180057DD15A77268FCA4DA0D37EADAE91131E16605827337B96318A66"
-  },
-  {
-    "key_seed": "B943C8D6155B53C203E332355CB329D31E28EBDA8C6718EE6280D366E89FE592",
-    "ldt_key": "AA9A6DF6D3FB6739804D6A34DE6B5155E9FDEE5045328CB9288D8F6CC0D1FDAFAD6C778EF2A5DC054046BFACBDC5B7A135AD89FEF0AD7466AB509F8CFED6FFD3",
-    "hmac_key": "DFD05D449A45276E5338E23059A4234B73DDA785BBC6CF7C2878FDD22936E7E0",
-    "adv_salt": "1C2B",
-    "plaintext": "ACAC175BF4ACBEA53E878C9830040AF5122FE5686F2F840DAC45",
-    "ciphertext": "B180141F52F59E2F8A3584395AF96F189A3538BC429890A9844B",
-    "metadata_key_hmac": "425DD0A91CF7BBFEF03FF25908BC83967D978B4A4062FBF9732767986D6DAD85"
-  },
-  {
-    "key_seed": "8CE06788376B0F29A61254B3947C09B169D6A3DB105B1ED640A6876B6B5CB18E",
-    "ldt_key": "17B8CBD01F10EDCBAA9D94B82487142FF22899A3B09C76189737DB0A31CC8778FCDE98EBC90512A9998A8C113B01C270EE46C9A19221413F844E0068169A7C81",
-    "hmac_key": "A09521F10A21C680D426A4C306A6C6C283B7CFC8B935318AA425C1181B940F66",
-    "adv_salt": "7AA9",
-    "plaintext": "BD625EAFA89F56091A5EF8B6A8364735785C2109895CB375469BA3",
-    "ciphertext": "BC94290458636AC9FC480582C6DEABB5AD4323BC4D10362A74FAC9",
-    "metadata_key_hmac": "A4F3B70D27ECE46376EFBD49394A464EF561CB3BA46F044D3D2CE55297E19DE2"
-  },
-  {
-    "key_seed": "0B04668FADF3FA0F73FF6918544470590955FA30D551139990047F1BCE8838CA",
-    "ldt_key": "DDEE63D033D1EE3EA7F109E65CAF7324F10AA2CF311B085BB59F0F256231175CDBA6D9BC1C0B81DD15549DA5919915716066342FDFAFA2C9B7E3B5F0791D6CCA",
-    "hmac_key": "048FFA66EDD1C4DB6657273D5FEAECD80492D1D00250F8B0C5DF4B56519FBA75",
-    "adv_salt": "7266",
-    "plaintext": "90C3E1BB82CD5737B438C91BDF8B0228EBDF6BEF",
-    "ciphertext": "988DA579499FE0736D6D5C251E051612080DA671",
-    "metadata_key_hmac": "92A81B7CD801716C336E9E5FEF64B7EE2B2DFCA4D53575DDD76A3029CA2AC9D0"
-  },
-  {
-    "key_seed": "49C8C4A431A2E01DE854151D3D337DAB5F9C9D8D8AD3B2C775FF94FC570D27CC",
-    "ldt_key": "09C2E5E543710567B739C1ED0B4358B0CCEB8FDFBEF3F59B97300DD1B0B4E7F29E4D0C2F1E0A129B21CCC9BD34490B693309C6E1EC6FD8EA67D21D52A07278FA",
-    "hmac_key": "0E01AEEE004555F1FEFC9CE595D8B63368EA1FAFFA2042E8CBB54256AF666018",
-    "adv_salt": "1CFC",
-    "plaintext": "8C5639623074CA6E565855F80E57B78B1B63CE0DF851A0EBCFAA",
-    "ciphertext": "B4D655F34DE514064A496A3D72DB5E1D9D68B0994BAEAF308445",
-    "metadata_key_hmac": "D93C61B9DCF9754D406E93FFE8620E5AB31EBD2C1FA736FE60EE8D67DF5C6447"
-  },
-  {
-    "key_seed": "3BED7222AD9E096EB49C6D0710BF028BF2267F53A9D5724E31F470848A627669",
-    "ldt_key": "7E7F49AA201451143832E3B868F2C71CC3FA37A92A0D39CB401EF1DD1DEFC1CA4E16E72CE72D04180019EA02847EA4D537DB90C6C8761FC8CDD3F5346D4EF0DD",
-    "hmac_key": "CE7D36E89FC7F46E1AF8AF08C2D3263B33BE88CD0634F5256635D3083AA3DC9B",
-    "adv_salt": "F3AD",
-    "plaintext": "A7189A5E8E1BBB745424823F2388703E75D4DA0B0A52CB4AA26A855F3F93A3",
-    "ciphertext": "5617A9ABB6BCF3C7D4C2265BDA4FE80376F3FA5830E1664DF53FED228AE275",
-    "metadata_key_hmac": "14FDB3DB7DE52D2622FAFB810545E7D92540E2F86B6536F926E2AA24DCD2F8C8"
-  },
-  {
-    "key_seed": "2192A18C6776FAE8AC7D9D9121CE294CD21613A8647B9EA1705A6585C550C398",
-    "ldt_key": "DEAE697B586F054E174FB6120981945C2B35F8B2A488493E7F6394181B8301D1CDBF14C4063D34DA2B86ACC5D83254029F04DA91BDCC8D7FB513045067106274",
-    "hmac_key": "53B28F7F5496579FC377DB08740E197A515356E9C39D1334CCD5AEE6B693EE77",
-    "adv_salt": "AB44",
-    "plaintext": "8FB87C83B36F8FF88F70001C78117961EEB97F10072C722995770A91FB22BC",
-    "ciphertext": "7D8AACB23ACFDD7ADA8838956AE109D124E45B1468A1570E2F81D51A18F534",
-    "metadata_key_hmac": "1224414289B1D981F35D8833DDC9C2AABEB4EB2578B5033A784CC3E7F21FA16F"
-  },
-  {
-    "key_seed": "690895E0A5EC222CB659058323865FA32C27C64B98FFC1D75E2A1F8D216C7454",
-    "ldt_key": "AD3A82914B649DF5F4FE58F221F686703BEC6F3417AEF7A6AD0B9939DDF82EB8FD928387FF09BF0AC06A61DA60CB3CF947157A0FF1EB36165C67748229D99F0C",
-    "hmac_key": "97FA5FF2E8C978FFFAD438E3307C2A3EC8E8F9AF37E2E943562BFF9FFBF9F353",
-    "adv_salt": "CA89",
-    "plaintext": "B7C8FF50A1B64418FC8907489B0AFD76C9D6E0E746D6",
-    "ciphertext": "DF168092CA200F9439B3D2CB65CBFACBF94956805999",
-    "metadata_key_hmac": "BCDC98AADFD5DDD75B655888ABDD084623BF9AA657553218B02243D0D72A094E"
-  },
-  {
-    "key_seed": "6EFAFC8A2B67A573D8386B82219B1DB6D1DEC72A684B2AD08F38796790545C7E",
-    "ldt_key": "02D510206C3677918DCDE9581AE6C979E51BE47ED70E6119A6837197619A5F8251740D6E1FF383710C24032A7AEB21E7D46ABB18E47C74F1BB64CA714D311035",
-    "hmac_key": "57EA566577E2CE06CF55D79AFA153ACE4B0B39092A89225DDC91760AB92346E8",
-    "adv_salt": "3858",
-    "plaintext": "10E157598521A3CCE5CFABE235057C7B569592",
-    "ciphertext": "4AC6741D891D0E09D9E0DEFB81FF2421B9A9AB",
-    "metadata_key_hmac": "081CF6AA6FAFD27C0E27CDA06796FB5B160B03575F8B1820EC6B2ACC68ACF57E"
-  },
-  {
-    "key_seed": "240D21616C0322C8A79E261FB1B06C9EA51ECAD4725C9DF014112B42E5070963",
-    "ldt_key": "3650F42E5787CFEE207F2B786DCEE041AE1DE12484E81CD09A1BA49DCEAA7B38C797A0B1633794CB9E611471BD78D251FFC2FCA2B2491C73A4695E1CCAD7213E",
-    "hmac_key": "F7B6BF48015AF668EDCC0A85DA1E33D446E201C15A4F70FD539C30EC7ACFE6A8",
-    "adv_salt": "1D17",
-    "plaintext": "CDA7CDECA0C584DF9E2F9864C550460BEC6E1FCF63B168DDC62D",
-    "ciphertext": "DDD038E31B2C3E1C6DB66DF4F7B03CE7237D744CB3909ECEF225",
-    "metadata_key_hmac": "8AED7FC5A7A63BB8C91C89A3B6F7D5DFB0AE983D82C3209155E9977C13B03DB5"
-  },
-  {
-    "key_seed": "60D8D4726733DD96CC9E5FAEEECD4196E56FE78D3B9AEB26F177B089712ACB34",
-    "ldt_key": "475E701D85BD3F4A1908F7870A4E39B78D16A17C6F05F203CDB8CCE8FAED2D8A22052F9FA6B4231DFB7ABF2DAD7634BDA0F0F8EBB59D2C4067772B89B2B5D443",
-    "hmac_key": "5F8DF4E0D61820FD18EC2B0434982A3ACC571D8354E2FD6F4404D1AF8AC822BC",
-    "adv_salt": "41DE",
-    "plaintext": "3D8CC8FFFBED83787AC49AF00BF84C5490B9683F8161237726",
-    "ciphertext": "CF8B5DE010250E4E1A80527E158E3826FB48849405EA452D08",
-    "metadata_key_hmac": "F28EB59F905CF8BFFAB4F23FB0FCDC82B1AB924C8FD7CAD0E6CFB879C62749EA"
-  },
-  {
-    "key_seed": "0E8C64270A07290A9DC0EFA078AADD96E9D1A43D103B856AA1B8DEA976F8F772",
-    "ldt_key": "7BA2957697E3150A70FAD3B30DDA9F834155AA7EF40D3903DCA5C886E363276EC5A9A93658A3E664614355680C7D5A79094C2B3E83A1C41DF2D8477FEDE9D69B",
-    "hmac_key": "3CA4A27153D5191C0DF96409700FB8732AA5DF9DE2167DF8B8712BD29B3800F3",
-    "adv_salt": "6B0C",
-    "plaintext": "588EB15D532943DEE637F9DE79C2BF96FA65A60FC5291EBC09128B2C",
-    "ciphertext": "3D07E71ED78098251B8459EB228DEC462E5E728D86F3582C4F539072",
-    "metadata_key_hmac": "FAF4E049053E3C4F0EBF797D9A7472F2712540764192AA8F433D4AFB94B22E09"
-  },
-  {
-    "key_seed": "42F8351A1D69560CD77080D521CA43C5AD2B24D27511B4789805788AE0BCF490",
-    "ldt_key": "A05B8EAB1E8C8BEBB7A9DCC2D77D10808228D001E04CCFC2632492CF03A2098C17C84F963447E8CE52D0AD502E08FBD110B5D95A119A857F100ED0125630D68B",
-    "hmac_key": "AC5AF0BC153DBEF528AC24830ACE0B353D2AD747FEB6CD6DFDDA28D9FD73B6D7",
-    "adv_salt": "978C",
-    "plaintext": "AC987AC3A3845214668FF091FB092D7C9C6872A8C6",
-    "ciphertext": "651FEC99084A6219E9527C06A25A5DBD96BBE1D9C1",
-    "metadata_key_hmac": "40AE7AA8F795E8903D79BF5EC589573EC14EDF944A5979C039E0767278B4B416"
-  },
-  {
-    "key_seed": "5F516164549CE07A61448BF6EA3B67E76808EB48AE9E23F5B9E613B3BBC45B01",
-    "ldt_key": "2F4638A5887B8C79A03E9F9A70C8F180521E1C89C296C6C0570BC30F9E209939C925ECA0A27B98BBC3598C0D643F2769B57C19E388FAAB1330D51572E919807E",
-    "hmac_key": "E83E459818F888B5FFE944CE4F1D1756D2A085FAB3792BB9797D235288CCCABE",
-    "adv_salt": "EC6C",
-    "plaintext": "E0A9E1D478B779FBED7E380BDDE984AEB0776CBB94F1C708",
-    "ciphertext": "EF04BA529DBC8203952A5F2AD743A41B676A0DCFAF6CEA76",
-    "metadata_key_hmac": "76AE54AA59CFC319AB05D759871CD008336553942FD2A642CDF3F45F8A42B81B"
-  },
-  {
-    "key_seed": "91F1D07519065501D139BA56F55F6C8CEF8C85233516B0B936E5899CF72EBE71",
-    "ldt_key": "1A82F91509DF4EE2E741F3F924AA7C62328B93C16065C0B6079881976B9DEF32D41E4CF1F49659D7E1AF844CE256B6CEA3E7D0FAACB9B97E2C6767D2B8E40A6F",
-    "hmac_key": "238A78E2EE24D904B795861B48AA742BCAA972F243B83B7B2F1158830A793ED2",
-    "adv_salt": "7C93",
-    "plaintext": "9C0C8F74EDCBCFB5E649542B4B04AE09",
-    "ciphertext": "ED697BEC17E787A69B647720206A0032",
-    "metadata_key_hmac": "8D787A4933A982DAE8C76C0B8F04E4AF6EB045C83B93D0BB56F8A4569C5CFC3A"
-  },
-  {
-    "key_seed": "7EB08E042F563BAB1B08C337FB5E14F4F0F354250E971BEE538001B0A158701A",
-    "ldt_key": "57DBE2259A6768BD83482FAE8DBCD1E31AACF13D5022AB6624DE7D9AC4DC998F89BD40B9CBACEC52CA3ED1E32F850DCD7D54DBAA7A5578AAE432ECFE37883168",
-    "hmac_key": "0F1FDA0693BF6EC9E80520CD9806000E5783EA4E09E53A708703FDF721ABAFB4",
-    "adv_salt": "46DB",
-    "plaintext": "581DFF6D784E78297EC25706288487AF",
-    "ciphertext": "3B911D1850819B6BE678262388279C4A",
-    "metadata_key_hmac": "97D98399A449715181459285F5392DF0293B06B50E5215B59EC59D0652092FAF"
-  },
-  {
-    "key_seed": "BAEFCFD20965FCB31EC2F5CFEBE82A1EDFAEE89B05C8E1A311F4CB704AFE12D6",
-    "ldt_key": "BF8CD1566616D6B4035F0949793763D15757E349A0C94117B36B984D94D222634D3ABD0B1833DDAD4B40B0B2D4DAEA7E74E841E615788FF41EFB4C327E72AD1D",
-    "hmac_key": "D40E45D2A0469BF466683300C941CE16CB1D87A9F5FCCEF01D2BB820D7E60B81",
-    "adv_salt": "BE51",
-    "plaintext": "25F8CD05696A1F6099E6B2DE452268F075",
-    "ciphertext": "7B10E66E465C2C1A39E3DCC44F0CE7D488",
-    "metadata_key_hmac": "326475EAC59CE0AD57577BBD956E70B020992BB137E49DDEA8881A9A44171AAF"
-  },
-  {
-    "key_seed": "F2CA31F350654CDE5B74EE5F935C4A89D0C1A0DAB7EF4C940F45B3470B42AFE9",
-    "ldt_key": "A1F1F050B609110C19AA8235E15CC371677CB635D33E3C957F6AC1EEBD669055E2AD6589D1E5B866CF82E15CAF7110E68E6383064DE8841CD6B3FFB1F17C6C7C",
-    "hmac_key": "6415731461E9996D3F9D0D17048C174A063C68B0371E7DC7C6BAB8D82D87595A",
-    "adv_salt": "A847",
-    "plaintext": "242FF7536CAB0CB695C3BDCD5AC4E1B362CAF4",
-    "ciphertext": "BA117E5881029A9B26BD648573DB9B4EC36678",
-    "metadata_key_hmac": "B86B1FBFC83B0847AC8993FBB4FCB0978D6EF2E18D163A3961D9D2A9B32B2377"
-  },
-  {
-    "key_seed": "764FD24C13DD413756DCFB503B37E896009615A12AB362ABA3516898D5449E4F",
-    "ldt_key": "E34DE7A73F91507B51E1E0DD90067D1D32B10359EC5B23FBCC01FF7BA2347C845F2F2FDBD34ED4F5F888A8266FA9324D979089DA3CF79A8DF7EBFF33F8E5E5E3",
-    "hmac_key": "55A7F81A9A3FB890D9BD10D02353DDB7BC2A9414A924F42B43C23820A38D9DB9",
-    "adv_salt": "A1B6",
-    "plaintext": "ACDC0775AEC80CBEE306C5F36F8BEF3F66",
-    "ciphertext": "CAB049A35D67ADCF8BA23FF998ED67EDA7",
-    "metadata_key_hmac": "E61FB791788A97BEA07650424CF5C897CB06665D96F8895AC7D3080E352ADC26"
-  },
-  {
-    "key_seed": "A7ECE890512661E1BCAF21F66F733E7C06A12498DDA70EC36B72C7A202B2883B",
-    "ldt_key": "D70CD4BA93ACDF5EF1B6E2D20EBCA2598D7D1CF1B22C3122D680C97ACC775BD20A927C4FAD167C8EDCB0F0F995682DBEB0A411321816BC849B71A4A969A4473E",
-    "hmac_key": "F7F38B6400532458E17684828BF6D9AFAD7A9177B3EEBBA1227DA1933FF0EE15",
-    "adv_salt": "461E",
-    "plaintext": "0BC11C317D3FD1948B8783C9C00A7C1E37E7027DEF80AF892CEBB3",
-    "ciphertext": "5A59E809B78706551C63F32A0BB3686DBE0542D7881DEDB605281E",
-    "metadata_key_hmac": "9F4877F0F0847E54B9FB0E571B57BE77C86B3916833006A11CB167490589CC83"
-  },
-  {
-    "key_seed": "5F6A6CF006136009DFADAB2F093FB7F136D765DDF5A2C81162D5A1B0DB978EF4",
-    "ldt_key": "31461D55765AB8296F890040A8B496EF8A11583CB2A65D985C89B8C07B5692E1D74005C621F2DBDFD9999D27A931E63DDAC0E647DC812AE78AC6211D365CA6AB",
-    "hmac_key": "6BBB59A6D1741A403106DC173FA2D7138757FF2ACDBA25BE9F9AFE83F3CA29C3",
-    "adv_salt": "49E5",
-    "plaintext": "E176DB23CCF2B4276F45C0A032F3EC5CDE",
-    "ciphertext": "EBDE72956456209FF27F57FCE5539FC655",
-    "metadata_key_hmac": "97F3DD6FDAFA648CC9372BB4D58F1E5FD763F7A79DAE21725351E577016BB302"
-  },
-  {
-    "key_seed": "DC42457FCDA6BC6238F94F141544401DBF37CCF75D171E0424B9E5A104C1389D",
-    "ldt_key": "5F5BE22616E1D10A50C7E50D96E7CDB312AAA3811AF691ABA49290AA4653F95B3D8C95C989BA527C07CED74993025BD2BAE5B963A7AF88B739DBA504F5E0F7C2",
-    "hmac_key": "C7C972C1800D641CF902DF6D742D85A07B0879EA9F57CEDD461211F408066EC3",
-    "adv_salt": "5D45",
-    "plaintext": "9827D36FD0D50EDBBC84430D4E6D459019E52C6B53265FD085FCC03BB5",
-    "ciphertext": "5DF869641548FE4EDFEA08ACFE87AB3AD0EECA666B14BC3741553C6417",
-    "metadata_key_hmac": "3CB01C6B898D25C2AB6141B05F165C971DD03D283455417D590EFCB119091824"
-  },
-  {
-    "key_seed": "6E02B012D2C249B2B3A8A187AFFA634A3D696814165D091294733CD0927CB79B",
-    "ldt_key": "15C6536795D92A5C8CDF035FB945B095791FD54F2D42E0C10A7A544B58D03E5DC9FFC82E90B925A7FA906E588BCCE1BF022EECBFC8BA5ECB534344448DE2EB2B",
-    "hmac_key": "34E596C2F8FD02E55267B6CF77FF6C9C4BCD70C4062E164518909773F648FCED",
-    "adv_salt": "377D",
-    "plaintext": "DCB1F0DF300EEDAB51F7EB13E7B1A34ADC87",
-    "ciphertext": "56ED8FFFA1F5B5E8D14FE31151D9B9FBCBA6",
-    "metadata_key_hmac": "B30E6439D5E73BFB2575201D3783D56D16A55F14E531ACBDDFDF22A8F50ADF68"
-  },
-  {
-    "key_seed": "6E7FD0556DF2BEE0A5E8248E6BFA09777B09ADC99FA9248AE0A203BD5E72D546",
-    "ldt_key": "4E13BF3B07D851A2452215C0BA7DC3680A81E75D9740D2ACB7E423EE16499DA8F9AE0A248AAC834870A83DAD8F556BF3E7F7EAA84F3210F7BF88A87B1E2CA4BC",
-    "hmac_key": "FC9B78FBBC96AC247F87109C8DFBEFDF72FA5DEB5E7EF96ED32C5060E5AF4C18",
-    "adv_salt": "F269",
-    "plaintext": "6009A2AC45C0D72067E2590D93D656898999336182A9BCD158D6A7D4469AF3",
-    "ciphertext": "48B5502ECD3B4FC6D5C55EAB2CD5B7C598B69F8E052C13177468E28823BE96",
-    "metadata_key_hmac": "2F7A1BFCA065947FF4B5A3727E56AC1673F7FAF45144F8FACBA428E9EBDD2691"
-  },
-  {
-    "key_seed": "74D951408C8BDF525D1D122600DAF1CF13379E219E8F08719025ECBB4E958823",
-    "ldt_key": "25ACA926D0CFF03E3E4035ECF250927610D16E65E78FCAAC886E252053C85F3D819CBECCCDBEEF9EA9FC7286B51EBF945FFFC7AC1666BCB0DA6FFFB3052CE3B4",
-    "hmac_key": "E519D08BDE2252F9E3DB11A175D8E2501F4F2C2744D90D585B35253EEFB7C254",
-    "adv_salt": "8EA7",
-    "plaintext": "F429B81E6551A1DFE75B69E5005C3442",
-    "ciphertext": "DDC34E29EF3EE45AE97EB004A139CE61",
-    "metadata_key_hmac": "986AED3A4CB8E42128404558252C29659C0ABB2485C0EF58A91944CDE00C6EED"
-  },
-  {
-    "key_seed": "5015B6F3FCB89171769E165F0E843830DE54F1EC1165A7D263DE307900E90AB0",
-    "ldt_key": "DB2DCA12614D43E732D4B086967E15C20F673B49BCAC18F49D51889D90B77FC9A0C3CEC3CB1E9F7B0747829FD48B3E7A8FFB5540E02DF4A4F71EF7B70E03BF55",
-    "hmac_key": "62DE7C9DE3694C01440F1C3556DCAF94342993B529EE67C84DAD118CF09BE050",
-    "adv_salt": "E58F",
-    "plaintext": "AEF9ED706F57423ACC0E1F8C1BDF842B5CAB1596198A75BFDCB0C69B0936",
-    "ciphertext": "371B68A65E93153D072B54294E661A02B5F9F4141C698EA7AFC948F5DDF2",
-    "metadata_key_hmac": "64D84E07263743E75FB9B7A3E87F1D52E76204C6DB72FDFF4E5F809A66AF6586"
-  },
-  {
-    "key_seed": "D598D9A12E4F0A8A488856DBA25696EA28947FD029EC2A592561B857D1DED9FA",
-    "ldt_key": "3E8FB54018EE15E884ACFD49D0240347FC603675F33F8838D105FF54A17B7FA2A5A7F4998AD8BCEC49910B3B0532445E4312A704FE3D84648844A688717E2EDF",
-    "hmac_key": "2B93A7972EFEBB2E34BB47F1BB10A5B4EA6D31BD2D674203EFB254FD0BF6DC46",
-    "adv_salt": "D530",
-    "plaintext": "D28432AA51B8AEA7BC1BC033DDD4BAC4EB9CC293E1B491720E75",
-    "ciphertext": "7C30ED5B699760EDFF31AB48D13623F248A2DD3CD6A361B5897F",
-    "metadata_key_hmac": "D41699ED342A9CD6E34CA6330747693D704A68D22FD46CE3C75133C97669121D"
-  },
-  {
-    "key_seed": "A9ADB74C0E8791A68C9092989F1C6C0DE5D614865FE5C7DE91C16041556E09C7",
-    "ldt_key": "B9C152B54C4BFF46C2D352D1E071748B4A0F0D6FC22FA9ADA2A88837294D4D6BADD4C8C13F1A52EF55F5181CA2104B9DF6C3A5B26ED4D4BF45905F93E9BCF51F",
-    "hmac_key": "E6C27FD7B3731A0337A8931652EFF05B95B051EBADF4CCD4276061018F290582",
-    "adv_salt": "47BC",
-    "plaintext": "540B9EC366CBA5BE0437722C0ABF1A30E3CBC81DB3729689B6882A6B9D",
-    "ciphertext": "2BE37903CC20ED58C5ADF967AFFB4AAD095013B527F61DD5BDF89F920B",
-    "metadata_key_hmac": "DB5D13407A407CF26BF0313C0F11867BE773FF7299291458BADD747F09912DC5"
-  },
-  {
-    "key_seed": "BEAE67A8AA47199BC2626358AA92356D7473F60A73DC30265DB47A707005498A",
-    "ldt_key": "D8CBCB1562E9946CB438F42AAFC07FB595EE60793D00FC0681D186A005097C80F95DFA32FFEB82DB16C9FB1BFDB9D5B9BE160FF3738C627D9757AEBE7AF8D91E",
-    "hmac_key": "0729DB3AB37CD96D1661EB3DD8116CAF20A260823CA388A67B04F471701E3ECE",
-    "adv_salt": "2950",
-    "plaintext": "8861EFAF5A608F48DDBC8DE9435CFB36050B912A183F53310496D5EB",
-    "ciphertext": "043AE474186769BE54CC26C25475F48B09ACC8FD108B95840E20C564",
-    "metadata_key_hmac": "1557A1A917F983968F88466017507F840F8E388D3913F438D73A02ACA566B48B"
-  },
-  {
-    "key_seed": "4C20E2F0BD4B9341954EA544D3C1BFF1BC1AED34D9EFF6255E15A33D3DBE670F",
-    "ldt_key": "2C6E9939B31CF4765BDA652A30EA2CD707D5F72C15BE420D2F63CC0DAB2B00CD6302FE19994394E7B693F19BFF9152901CAAF18D28C4BD1E1951BEE884BB254F",
-    "hmac_key": "D94029B956FB35034E791AA9FB9766E397A2C57D3C5E83D74E48076DBEBD55B3",
-    "adv_salt": "2793",
-    "plaintext": "4D8315DE8F870E78BDFF588C7162616AA8855F",
-    "ciphertext": "0A861982E4B1ED29DD0A3C779F8A0B8AB5BAB6",
-    "metadata_key_hmac": "F4B14B89F1BC2D21313F1EA5F5D244C2FD6EAA53081FDC09A0902AD4FA44A2A3"
-  },
-  {
-    "key_seed": "0428D107F0FEA47B7021417DC1DF0607572FBE3EC7B97D1FD8F3679A926D6C27",
-    "ldt_key": "22691BD03AFAE4FC52FF1FDEF7C671E60F4644675F27F9DBFB28595BDDD81A8FAE784C70AE2112057AEC12D09F20CBF5198CBD53525E78DF313360C3D49FEB02",
-    "hmac_key": "0E5384D5D2241FF235CCCFACD5919E8879F70BCB7A900B42FDA0E3F03C86EBF0",
-    "adv_salt": "FE63",
-    "plaintext": "4D3D218F5480049B1918C912A10ADDCD3B",
-    "ciphertext": "587268C07E7CF0FF60A3FDA6B108DAF9E4",
-    "metadata_key_hmac": "9072E1C59708E079DB8B3789ED79DD90406EC0D9EAEEA56DC75DBC6A13AA9C32"
-  },
-  {
-    "key_seed": "EE24B4EC86854A85DFF752BD1270C1A920CD205E87FBAFAD14AF978B5F16756E",
-    "ldt_key": "E427890B5E32F9043D248E8728F3BBE1BF5339409793805BF4344F911E3348039FE966EDEDA82BEE3ADB02C4A9AFF042DD38E4E763A6A2B114460FDE2D5991CE",
-    "hmac_key": "57C8FA94E9775407CAC93D44CDC7F716B8A959C9AA3C6FF88299B516E2F74B75",
-    "adv_salt": "CD71",
-    "plaintext": "84066CE197807B83FE2B2D2892E4098A0425AEE5F4FC90AF4A94D358AB8CD5",
-    "ciphertext": "4A6BE739FA649F638A3A8094EEC4AC50D11A327366D05B5767DD604B787883",
-    "metadata_key_hmac": "88D3FD6B5D9EC9DB66B3155A1F7B4DE660BCC60853661658E82B549FF9AF2669"
-  },
-  {
-    "key_seed": "7D21D8BDEE373CA89E39EE7C9DE191DDFFB0992918AACB07EFF882084985BACB",
-    "ldt_key": "6BB9C764A30CC4988B129DFC2402550791B6CF5099BEC18BAF1B19F36BAFF442BDB96053F7B987C2D882ADB049EF28ADAD7358DA038D85B7417A2F034800E157",
-    "hmac_key": "3C8443399346EC1094135DFE58EE65658D48A8B93F8DA066F09A435A70D31DF7",
-    "adv_salt": "CAB6",
-    "plaintext": "B33DA61D07BAF9B8F5AF7E504297BE12C79C938FA097B31A41297E9165",
-    "ciphertext": "5703407565A8FFB6EED122A705C9AB9DDAF22541DB24D261F696716B4C",
-    "metadata_key_hmac": "DACF0D1AFA10BD4A2786DBDCC290FEF7964357D70855DEE46981D32ADFFB2D57"
-  },
-  {
-    "key_seed": "C123E2FF68ECEEAC94D594907CE6974A36F4CC631A0BE0CEE93BAA6C15FBFFCB",
-    "ldt_key": "316F01C153E839FDF26FA0D74C10A13D18E8DD7581A58C4567211F823E8911EC7AB560E67A504C5C48EA79D9AD4662F32EF10EA2ABD72C09507A3DF0F3E4EA8F",
-    "hmac_key": "5490A44983A6CCFA1DE2C76DB797E37E60A09AD702E7F86A77340E3F5987C5DB",
-    "adv_salt": "9023",
-    "plaintext": "9B3E8B549CDBB75AD0A6F090979C6E24AD",
-    "ciphertext": "975D0BBB41386C42FEAE347B54242D7FED",
-    "metadata_key_hmac": "1EC47F888CFEECE4E291E2D3FC3B6F87E2C11DE9DA47199788E0014392E79B72"
-  },
-  {
-    "key_seed": "2B2C67B7D89E349E24C4F5E1AFD45FF95A1B2A7B830E595BC20C1A46068BD07B",
-    "ldt_key": "A883A57B567D2E962423204782D9EFC1DB4641E5293FC479E81417CC4EFFE4FA0F87C3491967C895D516E3B66EFDFAAF4F26F918ABD4FEC56EF3E62E0CD3ECD3",
-    "hmac_key": "D39A023C47FA40C496B4EBF2C1219561ED71C66908457A5997C775BB1E02FF29",
-    "adv_salt": "0F89",
-    "plaintext": "00F4A71C855F57F6EFAC2E7ED25214BCB1C15284",
-    "ciphertext": "6BBE11EBA309FC866465D350F25272BF0EC444EC",
-    "metadata_key_hmac": "228AEF9DF006B5BFEC0A9FFE3625F6B235825BB6EA05A507967A29B942AB3ADD"
-  },
-  {
-    "key_seed": "91E81AB6F70CF4121D6FE686CDE42376D55F2F174C3DEA72A2946183EAD3B5E5",
-    "ldt_key": "37B5CDE4F6F72C3F218377D2EB79D0F0E294FA1CADFA8F3A8F172D54EE8D88109466726D1CE45BB6841A289C32E425531D6F924198995AE792EF588C42058C9D",
-    "hmac_key": "C3C4D8918CDB5F9EE2B35467EB69E13209C688EEC0BF63DFD899ADF6DAB03B1D",
-    "adv_salt": "D1C5",
-    "plaintext": "7B1365B8BEDE25B035574DCA9808295253254E37D6AAE8DBA1C4A581",
-    "ciphertext": "5FCD025698AFA623C607C407E82A5C98A2B64C79D43013C774FF1423",
-    "metadata_key_hmac": "25ABDF4C2383532C25A1AC8E806E05EDBBAA6FE842C56FC7F88D2CE75EC8A6E1"
-  },
-  {
-    "key_seed": "EE6A34E568DFBF3A5A9B32D70D94B9EE942ADDA00686772B82917093A86C5289",
-    "ldt_key": "41DC0598E99AE88D2A79CA87260734BA5E57DA6C90348EC54519574C9ECF23333B930D3EEA7F7FA9DF2587BA1A06BFC2DF380FC36B7A4342F8AFA6BB124B4D97",
-    "hmac_key": "CD4642B18BE5FECC7B1D921D47F2D554C55B5370A0D78623C432AA8A1048CE75",
-    "adv_salt": "96CC",
-    "plaintext": "5464BE51555FE377829DDD56FDED674C593F39E98469899AAB9700BCEC",
-    "ciphertext": "0E9802D69238507017A128C0F62CA5296C5503B9D7F733179C4DEED7C4",
-    "metadata_key_hmac": "6166BD27C01EC510EB4980665D1EBDA18B19681449E737D01E638A948CFF1F8B"
-  },
-  {
-    "key_seed": "A1C4F866463ABEAE3B89695876BB45C99D23E3ACB1D068F87D8DB31F341D895D",
-    "ldt_key": "0A33418E578DDDB639E805FC13C86B0C705E228D51826FC24B9FF4BBEBA22C1EA83FF06ADF09A447689DA0047BCEC8314831BE2101C3DA1CE73E0AE02F202720",
-    "hmac_key": "E5DF138BC655A647097FCDFFFE015F537541B9445CC940E4D641A45514F3DB82",
-    "adv_salt": "888A",
-    "plaintext": "5FCCE80B66016827725911C5ABE47A390BF69D",
-    "ciphertext": "5415633EDAC291005852AF1CA6583A95906F34",
-    "metadata_key_hmac": "7BB6CD4201900FB4F391F6480ED3AA2A78A25DFA7BA8A16689CA340C07CD305F"
-  },
-  {
-    "key_seed": "D8A4255015CF363A556B381DA4790049C061ED7C11149F0AEE1E9141CE03AC6A",
-    "ldt_key": "B1190DDA0098411D337011AEC09267B11E68405370DD91D351F6BE67C824363320D2DD23A0B590CD20039A730A0BD6F8FA90175DFDF1A5A98603832F4B5181F5",
-    "hmac_key": "6D1B62F94A91438C33F3273F207C59E0AB08ED0EACEC4F1418926866B5C861BE",
-    "adv_salt": "BEB6",
-    "plaintext": "9CFE0C86BB50E90886500A72FAEA88323B26E7386BB4",
-    "ciphertext": "18777B2C7370AC03333F5A05B9DBB83C0137B9077347",
-    "metadata_key_hmac": "F26F84D886B914A646923108D1A392EDDA0EE1BCB56D5E899768C3B2A335A435"
-  },
-  {
-    "key_seed": "75578C951F23AE6C3CCF311D68DD3C524D65644CF9A82C6398ADB4727F5ED46B",
-    "ldt_key": "20C0116F5AEAC61C6C4E3CA4B7700EB756DA622AF5F4DA51D1D65ACDF9370A0476182DC58348701B99A2DF4A0E9E07EF74DC19C689DC147D11ECA05F8B0E34B1",
-    "hmac_key": "69619FBABE43F9E9CFE6B64D9564672B729671046F5A78769B29464A64F111BA",
-    "adv_salt": "C570",
-    "plaintext": "1488352BC697DA5C71582EAA21B20BC9998C31",
-    "ciphertext": "84B518932415BF7E07C2A2460B2F36446F6FEF",
-    "metadata_key_hmac": "9352A755CD0A8064C976391DAEE7EC78527040090E87B32F17B3745311C36257"
-  },
-  {
-    "key_seed": "83E1A954689B3BD8AFBF0AEC3EE3EC1B657BF743FE76413874035C7995F2F960",
-    "ldt_key": "FD0DCACD090CE274BDECE08B0BAD561A49208E0B3F3032470DF8626933FCB130C4E852DCB4AEAF52B1CA2DF3A0069600BF002CC3EF68A26046A10AD2CFBBA3E4",
-    "hmac_key": "CE59C98C8B3AB7AFAC5257375102F2305D8B4F00EFB3E7D40E7623923141E78D",
-    "adv_salt": "6D48",
-    "plaintext": "F97B454F80F09422EE81B8FE503A326EA5E54AF6A462049B3D8AEDA0",
-    "ciphertext": "4F10B0C217DB9F00CC5F0691244AC05AAD9F08475BFC91A06C6E5A0D",
-    "metadata_key_hmac": "21CA22611549B11E465BCA95A74DAEE7B93DD317498D51594C7D3D9DD45D8289"
-  },
-  {
-    "key_seed": "05F9E0BF2EBE1CB1E20EDA23E96A63F4BE0D5B75C011C4C8282D927B598C5263",
-    "ldt_key": "F7BA220C37CE316DFED9C7EFC4FBD2FBCC492B0270FB6CFBC1BA52B8232BA30DCA53DFB98A66D0E4FDDF1125BBA5D5C16403BCE3F1A48C2C76C3088ACAA3DD5E",
-    "hmac_key": "F221E83C2C2EC96672FCB4F15D9012D6584FD3AADC3D4B8401AD18FB058340CD",
-    "adv_salt": "3151",
-    "plaintext": "1A1B88D288B24E30B539BC482CE7FBCB087989FA263BB67C",
-    "ciphertext": "9EB0EFC18F8160CA53E3C2F6969709CDF95FC41BB0EF1053",
-    "metadata_key_hmac": "1B7C7D810EB0F2812FC676E0D26A6968E0532F11CDA74674207C3E33DD08C4C6"
-  },
-  {
-    "key_seed": "B06D3B5E08517380C5D05E74D6C2CF9064E1330367FE57E32069471297EED707",
-    "ldt_key": "6B6D3EAF8576B12C7E48499F0D8F771425A2270FF97517FA515B83A546264CFD48DC0D87C860606EAA033E2FEA773822486C18954EE6EF850DBE20332C1A4AAF",
-    "hmac_key": "1EE07B7EBF0D85AE3CA14AD3D9050B399A33E8F692915FA6F7C420D5A3664E0D",
-    "adv_salt": "83C7",
-    "plaintext": "55A8E4FD6C23ADF7E289A49B71A654DD8BA050C922D89CB4D2A3EC1A07",
-    "ciphertext": "5C4539AE4FEBA4D4FD85946B6CBA331573EA19AF2828E61F2291CBC39A",
-    "metadata_key_hmac": "11179074A88D201DE936BE9ABB924AFCD791F7DB800FB9251A4448EFA4BEA4CD"
-  },
-  {
-    "key_seed": "C9398C533D870FEA8A29F98EB8996D2BA92AAAFB077106232365AA5E4EF0646B",
-    "ldt_key": "16FA6B9971E2EBDAB2E8BB92A081986E010C7F386D6DFE60AA7F220EE795F87F59AF06FC0CB5D5B7EA75B8A37C2C5FE993B17395D08870011A2FAEE733978F2E",
-    "hmac_key": "C2282F638AEBDA6B6CB83AE8B1489BA2C3B20552F9123D459A348E4F943E2A0F",
-    "adv_salt": "33B8",
-    "plaintext": "B56E6E43F2D788B6F217F1663AB8514EE70EFEC7AD869AC5261E5682FCD9",
-    "ciphertext": "35D05E1A88A98DA68A2676681A3495A6979E6944B8AD988263CEAFD2B618",
-    "metadata_key_hmac": "B6F26E91255AEC398D66FB7EE1DAB398654E543A78A719A44BCBAADA7770A96C"
-  },
-  {
-    "key_seed": "1A999861541652049627B5DCD0E2236AC38C4B2CA22290F43F8D998B2E9B4549",
-    "ldt_key": "A8A813AD61E173CBD1724174BE86F41F7359560C27BA4FE3DDB99FAC7D8F9B135D7D6346D15725767B1E90ADC11F0801925728298F8850A3DEB6CC05EE0CE337",
-    "hmac_key": "3486787AE01E544A40F361473BDD8A0DE037E4EE38C2F0F1EEBE2878020342E7",
-    "adv_salt": "C8A9",
-    "plaintext": "0728727CB69CE3B4D08D099D34D6B32E398FBCA49FA8AE",
-    "ciphertext": "6B8108D9BFA4C1FB7F8C2FF38DE3301B45C35BFD2BE5A0",
-    "metadata_key_hmac": "A9E4AD26DEE22F766BC1966A4C493F22BD3CCE921E460A70ED08A4582AB9534C"
-  },
-  {
-    "key_seed": "F005F6CDEA5C4CD79FA5CCADF94BB2232E42B65EA39F5A9081922E549B6C0415",
-    "ldt_key": "0628D0A94B9FEA0AB7E70961E597753B5C9F957933D48AE4ECAAAFABCA46636C30AF9D3C5E193DAB854C1FF98FD63FA440CC5FA4E7343E88385C1BB2D928506E",
-    "hmac_key": "2F9B3BEA74B33186D4423FA662131C8D3621C0018B9797629D176F9E58023190",
-    "adv_salt": "CBAB",
-    "plaintext": "84AC4A1F18981A7FC7280E72841618C5C801899E",
-    "ciphertext": "B8625071F50C5B735F2CBCE1732C01FA68FCE684",
-    "metadata_key_hmac": "C2D54C7E0890506A757511E66E9B49FC0839A35EFABF430A3B793AFF68D0CF2C"
-  },
-  {
-    "key_seed": "8F6FD538EEFBFFF826070841DF3B7A574C085F294AD59AEC77A3D761220F1347",
-    "ldt_key": "CB48028D27A83F82AC955EB015C2427F250265160FE720F498113A0356DF38078D11BF84866995185F8C89A6AB2347BCE54D56538DD3B58DD72B4D4C1DBFB109",
-    "hmac_key": "95D248567D50E175A6DB7300C0BDE8DCC9B58833DDBA791F458E8656249F9344",
-    "adv_salt": "7D9D",
-    "plaintext": "E5B8D20B29C2B14898568EE81763EE2D7733A6FBAF95C0A3FB345816070259",
-    "ciphertext": "4ECBD65B155B3B8066FA968CCE9FE28499EADF27DC7A9B8A2B0F072A10B704",
-    "metadata_key_hmac": "9420F0E1AA0390F1478EE5AD093CECDC77A545EB0A3CD32CF9681525000F5E3B"
-  },
-  {
-    "key_seed": "777E054E4E9D47B655E8360B43F29604614558622F0BD3C9E2D45B0A7D9D9B61",
-    "ldt_key": "9A93510F97B92E02C654232F2D22EE5A460EAE5115B27B1196016731B0E1F3F0388226805CFFC8EE744856412F866F7E9C090BDE960DD100F489B978CF3C50F5",
-    "hmac_key": "15E2921C229F0F91BDE0A0C87C1965EBB631D1E8E39898DCDBE8532ACB5365E9",
-    "adv_salt": "4791",
-    "plaintext": "EE7A2F25F8E682EB2A9AE573D680F3935A40F3F5F0C09849",
-    "ciphertext": "D48581F2B7F2D96D8223A947E92EE90AD058663CDA950BDB",
-    "metadata_key_hmac": "952CC226F4A9893D59C4E306CC8BA738D8AB37703FAA4422982CB656D1CDCA61"
-  },
-  {
-    "key_seed": "60837595D85FB629D9C94564C683F91170D9F1120A126BE9BEF9FD198E712E81",
-    "ldt_key": "9A53A09457D566A1AB52E1B73F7F005C3AD82D8C9B671742C6658C97E794C84F2E84ECB034EDD080C36B5051FFA8B948D25291001E5AF5512EAD90FB54AB1555",
-    "hmac_key": "5EDED01DADA1BBC98C7596D5D299C2D1C902C8B63831DCF4E7668BD7B8AE02E2",
-    "adv_salt": "D105",
-    "plaintext": "46832328CC47013E8EF24208CB027B2031FDF89A1A1A",
-    "ciphertext": "FCFA6EB4D3625B7FFA881388C17F6A6750DF527C4C9E",
-    "metadata_key_hmac": "DB6F8E7099F77322B98AA90D085F2CD1229AFD4B5F9592D3C69F93E4FA6B0379"
-  },
-  {
-    "key_seed": "F24BD20F93B9F0CDC351E890DEC97565B469517C96DDF73AE3120EB1BF34844F",
-    "ldt_key": "66F36CB0686BC1972703A65C16A3BB8DE8E964DFB7E22B2B5C871BDCE870E46BECD135B1867687BB3865148ACF8D4EA4F8217941C21C657E1B51825332AE3B3A",
-    "hmac_key": "8B073262374BC7FEC473CA681B3AD64C3AE244168806F8B3333D7B055BB63993",
-    "adv_salt": "36D5",
-    "plaintext": "13AD32D79E13E89FE6068C2639D0A979DFC817ABF5EB65218B",
-    "ciphertext": "EE6A0C64E67A56416ECED8212D26A8973D9B7A96749E470F77",
-    "metadata_key_hmac": "FD13068038BC515477E7E3D01AB86CB3E93884A4AA04DAA4771AA702A38869B0"
-  },
-  {
-    "key_seed": "74F59D94AF08B4B8AB90E8D5BC160F5DF494E3BD34238383D0BD18703CE07F80",
-    "ldt_key": "E5A98C82CDE85C7AF8BED6D7D95AE2968A182AD81A06834ED3AD6FF8849CC2FBE296C6610018D7DC7040CA1B553A10D00F730A6419E26BE425F4EA060436F290",
-    "hmac_key": "ED1FFA052E38A9A0366531AB6E25E6102B67362B295C124A308AD3D0E98B1C94",
-    "adv_salt": "7CDC",
-    "plaintext": "EDC98AFCD4EE6C877AD7066092E9F605FD37D5",
-    "ciphertext": "1756BA7EB250C8B22473B5F417FF130059B965",
-    "metadata_key_hmac": "BE98F9AEEA113198E2BAA617F5A3EAA65B96434B4C3B6C7B22270F08D03BA565"
-  },
-  {
-    "key_seed": "50A876AAA9F8292147A039FC3E5AAC00D796DEEA9690EEB7A1F866056B272041",
-    "ldt_key": "9664B36700A5B5B0A04D6C3DB8E13E283793CF0616C5ABE0C26B87BD88DD6399959F7A76621CC66A9DB42868489C22EAFAEC1C739FA279CCEC7167DF9936C2A7",
-    "hmac_key": "8219103E7D149FF294B8DC21413AB8A711565C32F10EEFD2BBFF5FA39DBB026B",
-    "adv_salt": "F185",
-    "plaintext": "3B01D21EE1335C2961FCA707B9CB8AB6EAE05C9F1BE9DC3E65EFA67D6623",
-    "ciphertext": "7BF18402B2AD55BD4375E3241FDC995EBF8BCDE50EB9AB53ABDEEA883A5A",
-    "metadata_key_hmac": "C225A9BEE2A7A1C387D6E531685A93A3E63DE2AC0FFED415FD20AF973D6A2021"
-  },
-  {
-    "key_seed": "7D0093DF47228EB2691A090BFEBCFCC8F9E0B3E43FDE8BFDA95401F77EECEB8A",
-    "ldt_key": "8C59879B57A3DB8FC0B8FFDC4A16FF67F86C4A9F3F9E9183070E063B6CAD120AC97DA26B458391B2D25FE65625578ADCC26552B03247E7502986E10FBE347A9D",
-    "hmac_key": "6C66B9EC7C0D201CEBB547E46E641237FD3E7398D7D48B751D981C100C700B26",
-    "adv_salt": "B84A",
-    "plaintext": "669D7A54C2F14B36FB548C832A4A059551D4AF17FB591A68",
-    "ciphertext": "11089171A34F04BF56AA50D68C05BBF6C8363D6713E26920",
-    "metadata_key_hmac": "BDA541B3F1A92DF61CADBFA8E6044F40CF5C4E9B8EA9E2D286CA3785E63D8EC3"
-  },
-  {
-    "key_seed": "8927B5F048DE267FA27E92F311E6C3BCB1194BF61D84C254928BAD9C0701ECD4",
-    "ldt_key": "D1784B9250789EA84E380FBCDFD22CC056083D9FF9B7FD5B121A7935D33C01FE9A754024EB157F011AF36F1B9C3BA7910B802EF800052D8B01FB5B6DA0F5E1DA",
-    "hmac_key": "AA2C9402AE194A0840C2633BCC36B92EF14C18118C5A16FB6F9759FC03F1DFFD",
-    "adv_salt": "A406",
-    "plaintext": "76F04514F35F08065D95152315BE28AB78199784",
-    "ciphertext": "F4EB95F4D9ED24BD4F09377FF8E346E835E71E72",
-    "metadata_key_hmac": "2A34C9D12BC1C1A3C042AF24C62FFC724A233547B25183A890948DB0ED410E99"
-  },
-  {
-    "key_seed": "AD74C330596E696ADEAB9A2326F4868D96BDF091E1F627FE9C8DB408302860B8",
-    "ldt_key": "EFC11D296B8DE9BD0F9938AB79C082F8A1CB70E99BA23868C192AB18BDD7335C38353046E9F9E170CE1864929D7A26E37686D2D730A712E3E1EE01997C8618F4",
-    "hmac_key": "527C98ED723EB94CB2553F3A5BA140EAF6185EF41BCB277081AC1F5B31346EFA",
-    "adv_salt": "0F6A",
-    "plaintext": "139503855EA1C1B7F2CE931658F7C14B",
-    "ciphertext": "81DB57063A383C64545D8DB9AAAC3269",
-    "metadata_key_hmac": "A78BCA4C18924966424CD15A2171FE6C8F03CFE528DF26E91DDC8DB7AD23D9B7"
-  },
-  {
-    "key_seed": "7649A2379FC17BCC384ED99312C1C8D0AC41E5A3EA5A360AE529E8B2261E0D9A",
-    "ldt_key": "198CC9B6D844AB0A84720CD233C7F00453077CD1433D393D8216F2B9174794FBDF3CA972F46E5F2AF67E9F3F4D2E5CE62A1C90B6E44FCA21FAA116DF1C0813C7",
-    "hmac_key": "2A2F072409F89924AAD7AFE26786BDB94AE2E2E7800EE70F02D92530316679EF",
-    "adv_salt": "50D5",
-    "plaintext": "0745372B0D70EF170B7313CD01EE17DE46C5E051A6",
-    "ciphertext": "5CC44403295D7076D6AC112FEC1157603489524D32",
-    "metadata_key_hmac": "D91DD2DD184E0488F50A410775CFBF3ECC71FA1C937080D2639AB85922E72A3E"
-  },
-  {
-    "key_seed": "53C687E04BFDCF68AE40C325A93403F6015B5018D8760E1087855EEABECD19A3",
-    "ldt_key": "B062AEEEDFD4B60B92628D7A86FAF9D5D4C4E5D9690767B149EBC67769EA0BA1ED993A6A3FF8EEEBDF6180CC90EFA68D1457EF15AFCD6091F1B16DC1B9CBC84E",
-    "hmac_key": "02089D1F302A82EDB1D45A2025924FEC0BA8D5BDFD20DE67E7C7447A24A0106F",
-    "adv_salt": "1B8C",
-    "plaintext": "F3AA313737A8A88B621E16B5D1B69AFEA0C0EFBBAC97AAFBD9",
-    "ciphertext": "7EF8464BD3C38BEFB7D7F02B01D9FA021785D5B0E461D74F47",
-    "metadata_key_hmac": "219AE24B436C915BB07CBEDEC1CFD0576E9EE9ED89F6C07BDB66E0BDACFB1C3A"
-  },
-  {
-    "key_seed": "5EFE256D37F99059E05CCE98C91D8FADBF0F061E9BD3B376E80447E6C25E4248",
-    "ldt_key": "0721773F9CD5316A17C039BF9AAE52DF2A9F4AACE58CE86EFD1DE6DAC555C1E787F22470F3E5464B8FCC518BA2D80D83FD966B2BC45EBDBF9868DA5EA6C28AE2",
-    "hmac_key": "59BBEC4E253D0FA3054C9BCA71EBACFB6EE42C05E0E6C1A1C14C2B0FCC7E2D7D",
-    "adv_salt": "9536",
-    "plaintext": "92B2C0A7DDBC3FAF112B39D70150DFFA5AF52E9B4ADBC684ADE2821F32AA",
-    "ciphertext": "CC47D95B5A23C5FD1F052642291E8D6F33F23B0DC1169122DC1ABBC2CDB5",
-    "metadata_key_hmac": "F23C783AF1CF6A0A95FB2F5717E09F50304E76E0655E5E6263B84BD1D359AACE"
-  },
-  {
-    "key_seed": "64590CB65521409A16FA0142F16C2350D4E178C24FA77BEA5D14A7DF4D1D7B10",
-    "ldt_key": "93FE8120B98DFC948B4E42A883EF866E3BE29557B54E9CF37271731973FFC8FE0601EC6DCD1883E6B7912041545407E047209D319CA4C57BCC466589317649F7",
-    "hmac_key": "4B28172CD3030B80276ADEDAC85869C4482C5287EC1F01E36A22751991D31BEB",
-    "adv_salt": "305D",
-    "plaintext": "F4821701BCF875B3BEF2AE661F71D1A1CC8EA1A6B63E8F9C",
-    "ciphertext": "D127ABD7C34A2332A4786A1FE59A5D9D210A70F88441FD6B",
-    "metadata_key_hmac": "50A682324878F91C2116B8887E20DFE9FF9093A1FD53D48D58A2FCB6C4F37558"
-  },
-  {
-    "key_seed": "7979DFA5770617C48C39D48B1FBD64696A74AB82AC17847B1C13538DA1E6C234",
-    "ldt_key": "2F494794600809117F931B90A7E7019D6B253CFCCB3E8C0691BF59213C3772F75277CBEE0CD443C7B6B68052A2E3D139AD3E7C728A23452291EFDB348C47D1AC",
-    "hmac_key": "9DF39A32A8BEDF4E8BFFE09AAF61C0BAF2A1B2446877C5BE70AE79BCFEB089B5",
-    "adv_salt": "4644",
-    "plaintext": "9F814D4BBE16AAE87430462F1A7CB2897839196C7C",
-    "ciphertext": "43A656D2E2ED336A6ADD86DA0F841857E3267F5F0F",
-    "metadata_key_hmac": "D49688B199580C37CD2D67D62F81EA7C18B41A3815EEA2A2D415CFF03E884F44"
-  },
-  {
-    "key_seed": "BDD0A68D2B25032DB53FD4015B182F6590F95D92316579AAA2549DBEE309436B",
-    "ldt_key": "CEC5F5C2D9671A6B65839370E07DC7EC9A069D4F36169A35D1002C869FA70CE2A1DF1DC3B5DC94F9F9C3FAFB37964539CBEEC38B59CC202D2693E21A7D424EC0",
-    "hmac_key": "0D3A4DAF27CFA08B3E08F8D4977773DD5002D5A3DBF0C73F5EE43439655A69E4",
-    "adv_salt": "274E",
-    "plaintext": "5223AFF8A98FE589A49520C98C37E6159D7BF41B73C697D6287E",
-    "ciphertext": "1E90493938BC297E539DFD4F11B0B4CA17F7517A2A1221289CF0",
-    "metadata_key_hmac": "04782BC0AF49D03FF62B08E5DC656FF9B764178C2E1AC122E2079C2424FC97BD"
-  },
-  {
-    "key_seed": "8B3DFE65D57FA3333A13BDB5B91AB5D1F6D18141AD4ACC3A508852DE8C161046",
-    "ldt_key": "F4BC7BE52635084EB8D416E1A8A56DB127EE7D01B23AE935DB485963141E46D2C08DD0F7D571FBC679C60CFC296AA223DD7D2737F4AB16F974D951D20B32FD6B",
-    "hmac_key": "AB56C4D61C11D5D44F6AECF314E2C1007ADE2442293F4C0F62B4C66EC80482B2",
-    "adv_salt": "D03B",
-    "plaintext": "FCD4F39FB98A19E96556EE968278F96BC143BF4E2AC5EE3A7FF6A4",
-    "ciphertext": "46D9E7E7A91A891B8A7BEB61FCD19E2BCA61DD807D29286D529ED3",
-    "metadata_key_hmac": "145148C46BC3E355777C7BCE1694FA6D930ADA73F828C3EAC3D756F1A519C884"
-  },
-  {
-    "key_seed": "86026B320DDDED39A605D69D60617C6B2FCA91C3C56FF331BF05F6C51ABC431E",
-    "ldt_key": "999B5C2E8EDD3A1BA956D046A697F47708D45625076FFC1AEBDAEDF7919681A00834BD8A9E5F47BBB13F7317D0D8A27BAB632BA60598074DE6AC132BCB93FBE8",
-    "hmac_key": "BAFD9A03AB8D977CB058CB99C742EDA9E295ACC9598A08E05AEB9AFE4D83AB74",
-    "adv_salt": "65F7",
-    "plaintext": "49562E3817E27EEC4218ECF02737C5591EFC6EBA7E",
-    "ciphertext": "8AA5E712B6B68F6BD8E43C66AFC092F7F70880F654",
-    "metadata_key_hmac": "2B602D3DEA760253D2D80924E6A83E7FF48C1C0F31A635EF8B8A8333B1176354"
-  },
-  {
-    "key_seed": "2680FAD88620C707EF5E04C0B0AED68379F9CFEB108A9FE86E38756F3AE82B14",
-    "ldt_key": "2305211BA329041A58B9E7F7D96DC136FC0A81CA566480873175F39279EFB0583C2882403B38FB9B7BA1F3DE8C0B500C306D9DCD1AE212D93260A4A2EBE42037",
-    "hmac_key": "2DB441BC80B1EE16897F77EFAD57BFCEF638B756EE0681E20D865758B0785D23",
-    "adv_salt": "A53B",
-    "plaintext": "CA22F3F10ECC21286044F85308DD1A36225EB06F318A62E2",
-    "ciphertext": "1B7E3D6EC50515D17BEB4D11576F51DAC3F52E84FBC74DBC",
-    "metadata_key_hmac": "7F3DCA60378438BAFF2D2BF529EFEC3366864382C1022E6530A283B0B964CEED"
-  },
-  {
-    "key_seed": "DB2AD3D8D94E698A43AFC03F0A81205D6C6DC80D08C05CA80504D2E46FBF0B6A",
-    "ldt_key": "7F73778DDBCC4E86EBA264E9865ACEFFB4ECAD01BC24CEFEAC2615B6F83D21849E976751818C280C06C446B8725B456DB9B9DD88BF760A12596D438F15BD75EE",
-    "hmac_key": "C4A64729CA08DFFB6E8A7AB92DF03C295DE7A34762150EEDB033B891A9E12660",
-    "adv_salt": "DD3A",
-    "plaintext": "0D9139631372A349ED682389B37E6CD4C753A8ED6C6BE8E8E632092C0C56",
-    "ciphertext": "BD97B92A8EC5AA9A64EC22786538D1A4D101257AFCDC2FEE94A813C95D09",
-    "metadata_key_hmac": "47BAC1161AFEAB767F0D9E4C31E65190D7D63B3A652923BC844239B7A65CCA10"
-  },
-  {
-    "key_seed": "6A8E2419B9C33CB50277295749BC8416BF5BC53734ABFC8C3FB5835DB750C8EE",
-    "ldt_key": "47E8CB93888664EAC1A75B677B1229ED52F9BD57C9B23A6525D497560D831D5873C1AEF57152F97DF0B11712B037924A4930A989ECC715BB69735B7BBE1040BA",
-    "hmac_key": "F1200A73068F7F97608065FD0A63A531F12F0D5D6B316A009AA3633FC06BB622",
-    "adv_salt": "0095",
-    "plaintext": "BD8463AC015ECC567E98298AAA43B4CF985256F430CE40",
-    "ciphertext": "F23D667AE8F34C70FD5E1A60F7E76EA32C756910B2DF62",
-    "metadata_key_hmac": "B8D9EBC74B2410C22236AD72A06276C9873D876575D2A8B166598826E4BC1EF2"
-  },
-  {
-    "key_seed": "447CA7AC4EC4AD1D34D077FAF0F712F32490A5F175F6686B7CB279B0D1EF6F59",
-    "ldt_key": "69ADC0054773C67B2DA71B39FE755210EE5902EAFDD5C459AC9F0BBF4C670D104252A2939ABECD63F651BACBD4BA9CD2F7173970522CB9DEAB4CDB55CC77215D",
-    "hmac_key": "7E2A0336C7D5D1006779D522D840DE08B623F42640F28736DDB6C8D9683213F1",
-    "adv_salt": "AD8D",
-    "plaintext": "DA502E834EE6CAE72683CB69EF30635AD24F59851D60242A0C071FC1BC",
-    "ciphertext": "741DDA4BD7C7169ADF907C71A31908D0A2E7150A2DACE4AB6B820A9BB1",
-    "metadata_key_hmac": "832E565CAA78115F1CB3EEB0D3F445E58115AA02E973CC0C1DBDFE17B5E84BC0"
-  },
-  {
-    "key_seed": "8E61ABF357541542C882AE154496B26F9D372B3D10C08EA89337D3EAB5D1E19B",
-    "ldt_key": "D259C70B97BC97B53A69A2CAA8AE29A14E00D9052B023CFD0FCB51627B23BF41BD73BC345D483D55E3A96986E83702AA9F921B0F13E916FBF916F84AACB49103",
-    "hmac_key": "3EA948A39CEC88FF2D445DFB5EE0F59FA43213C646072E01752F8582B8616CBC",
-    "adv_salt": "673A",
-    "plaintext": "D416E8684BADF6C6E9F8EFACE100CAB859FB",
-    "ciphertext": "BFCD5685E421AF7ACC2C7F598297620EEDF9",
-    "metadata_key_hmac": "8E7A56D294210B30A1B4A251A95F92CD752EC9C6CDA3065F5B4A991768F215CC"
-  },
-  {
-    "key_seed": "449A1D16EA9ADBC4A2AF44A53E2473C99113CFE66DDB93E351B0E14140B83881",
-    "ldt_key": "40650D278B91D804BE0D9EA4F5D4A89D5CDF9175765A84093F36809B4868B76D08A17645540867143DF93623DCFC38B1741DE2D9176EB1D45DE70F5FC339F869",
-    "hmac_key": "2A5B0EEE434721352A2E9806DBF5D4EB7A43E5DB3AF54D9A47CB7BFC67BF0330",
-    "adv_salt": "CFB6",
-    "plaintext": "8D328EB10CA6160ED71F0EC159931F939826FDD2378C4BD6F974FD78862F",
-    "ciphertext": "A6B2A8084EE4A30AC6DF3274E5F120F0B978E7DDBD087A71091842E63830",
-    "metadata_key_hmac": "8FB61E59723D39142DA9C7E143DF2EEE5D759171615AE0B43CF2C3838C660716"
-  },
-  {
-    "key_seed": "BFE6A29D7441AC00C35E682669C6B2325B4CC367986A6EAF480DDC41E739BCF1",
-    "ldt_key": "51A8536DA717B0615F8BBB4B9FD283645483432076E404E0CDBEF17F44C695001207F5E90F9BEA9C7620A592B1A3A76BF3A48D50D290A6B8DAE18DD049B52911",
-    "hmac_key": "74266AED9E569E3B4FE366DBBAECFC87B0433D556483D8781A1DDBD93C383D08",
-    "adv_salt": "A2D2",
-    "plaintext": "6C8E5D0796E02C038C6D25A2998C9F9B",
-    "ciphertext": "A2D8DE0FCED3FEFBDE44D26D9B33C366",
-    "metadata_key_hmac": "998AE16410607A68B197B084A724D4E8B645EC2DB46F532A3B268B41660A9780"
-  },
-  {
-    "key_seed": "14675C8D509F1C9BA8F21B20FCC3F3A64ADE514C855A2FE9D40D30F0528C39C4",
-    "ldt_key": "EC3D89187D94A21BA267BAA8A31D73762A7E5E124DFFE9E9C63AAC620A7E74E6F97099E8C882C6D3FD02ABB30185E70305672C889A5F61B5AC10D4EF3549519B",
-    "hmac_key": "EBA940DD1649A200EB310340EE57B004F61C2FF41F59456FD103A56FC53B9D7D",
-    "adv_salt": "D9FE",
-    "plaintext": "F8A1CE521C4A43DA283B7B9D08F665A9EE31",
-    "ciphertext": "BB2968F8E2861639629136B31AA471926577",
-    "metadata_key_hmac": "803B4F21A55CAFEABF53E090F0F79F0A4DB71723214AEED719908B905B26E483"
-  },
-  {
-    "key_seed": "4BA996FEC8C6A4017A87E3E32DECD8961DDF206C4555E1501216C10C804BA971",
-    "ldt_key": "F07212C784C6D6FAE257C889BE9BDF9A3B1B35979ABF0F722FF490E95CCCCFE26EB91F6429818D49DF1C67814509A55046D670E379EE3FD26AA11B40F663E017",
-    "hmac_key": "85BA2AC220066E09866A29B2452078651F28CBDF3068211ABAC84017218D9576",
-    "adv_salt": "D9D9",
-    "plaintext": "9D5E90E9B4F45BCD4081E08332788BF804",
-    "ciphertext": "47B069DEC4E944CBD1F52E8BF5BC20A2AD",
-    "metadata_key_hmac": "9B8AC2F67DA6B24B163FD82323A71E822B94F9DC0D75D43472FA89AD46DCA753"
-  },
-  {
-    "key_seed": "55AF213D2853178F29F6C8F7E6CA2EAB13B5C8D2C5FBD156245F9106F34A6FCC",
-    "ldt_key": "6AC9F5AD053645993FE1FB8BFE62B92F6DE5FA72C5ABEEC3026B8724D0F0E37FAA79C93B9D5D22FE304E3109E32C720483F26706F98E0371391C94AD32010706",
-    "hmac_key": "48F8258BF98C92A3C15AB67FD073A498E5B8F17E7208F57B32A72AA790FF3E45",
-    "adv_salt": "3E3C",
-    "plaintext": "01A6023AD07DDB0117B7178994D571B0D39ADAFFEDD6A5",
-    "ciphertext": "B07EF173F1E9DF3C893705C3842018B4CA49BFA1C3CCE5",
-    "metadata_key_hmac": "6A26E7663F0260C89F32B1F903AB1B8720C6932F524A8BFFB774390CAC1AE582"
-  },
-  {
-    "key_seed": "D30DE8937872512C3C1AF385C79CFC51DD692C27EE0D114A980D0639BAAFE585",
-    "ldt_key": "1DC7203241B5887272ADE390EC10F413EFD566CD676B4BC2A53E6621E1570CF6B401E2A76C0F827FC7E11FC496E8E94362217CBA881538945A0D59FF4EA84AA3",
-    "hmac_key": "6CB4D5700E6C923D393F7D4A12DB26BB7297405C57FF69DAE7539874A1A6C847",
-    "adv_salt": "E77A",
-    "plaintext": "EA8F563093ECFFF7BF3C751ABAF402B3A1B6",
-    "ciphertext": "5AEC616534C38A32B5746D9C85C388575BF2",
-    "metadata_key_hmac": "93F9F68320220CE723583C25E1377EFEDC315016882382D55B366B787BEF3EF4"
-  },
-  {
-    "key_seed": "75D1FF8290AE38CEAA8E14710994CE07BDB6377A77D86A93699127D7D0D5D240",
-    "ldt_key": "E0CC15D3F2438F44E4ED7F6A775EF97A1274AD1CA4109D31406FFB4B7A59D65A5F928286D2E3884111CA15586C3B803FAB77249AE303DBE136BDBD493382BAA5",
-    "hmac_key": "39B17C433601AD44119FA3C54825CE4BD4B2723170F4863F67A1E941ED8AFDBB",
-    "adv_salt": "6611",
-    "plaintext": "78E14770A3E0C8D21EBB6EC1A0A1335B7E83",
-    "ciphertext": "1246E5060DE56AFCDA4790B9E13E14669CCE",
-    "metadata_key_hmac": "2F603AC08134D5E3DF511B94DFB5EA82FC52D2270B814CB280B765E6A389B0B2"
-  },
-  {
-    "key_seed": "6AC0862C64F27E75C518BEF4573E063617F0A1E1C581ED11111338A16FBC1717",
-    "ldt_key": "2BD89F7DA6984B44BA013B9BBF8A9A5C5F97FD3C9DAB15A14F5B03305FDDD1807B08752F72AF883B9EBEDD57E93C764581508C768928DBA04A3BCE11354B60BF",
-    "hmac_key": "63C68D4AA358B9890B34B55DA9D6130DDBC518B2E7760B00148B4DA56FBE6361",
-    "adv_salt": "3EA1",
-    "plaintext": "34B32B27597BBA55EE23E7AA8E90712C277D2C4A9B6DBB92E9657C",
-    "ciphertext": "28302F2D55C4DB0D9F71318D886C094F2F6FF4BB8ED6EFABB1774C",
-    "metadata_key_hmac": "7454C2E431FDEBDE6A5C1A2BAB3BB7039D87A0DD030995C6664BE378D8A0F2B8"
-  },
-  {
-    "key_seed": "88FDD77B34197BBDE3CC14C16303251D9181A76536C29991863EAAAAF1F40721",
-    "ldt_key": "DD7A188B0E94BBE1EC023D8B71F28E9D8544AE02ACEA30FBCDB0303D2AF56754AC5ABD2169915DE2B532A5DAAEE4B13ADC24FBAC31CF05A21102D58B724BCF95",
-    "hmac_key": "1E12D7838F469C39A3EA9282405349BCB68399F280AF213811E57D52F30AC81D",
-    "adv_salt": "B482",
-    "plaintext": "087526DFE3D3334962F8CA4B998009602CC37057CBEF619F8CE914",
-    "ciphertext": "F5D4509D6078D1CB847095474BE5BA2046D9856E27DC32B671D5B1",
-    "metadata_key_hmac": "E196214CE72F93298AB49C41BF123B9228302456C8274D9C5606BC5B2FE7BDFB"
-  },
-  {
-    "key_seed": "9E6C0A2179AE92FC60D643993C9B5542AE2BF20E919CB45925CA78CC35A9D940",
-    "ldt_key": "965F3E30B055488732FE7ACC205A2746231536C2A388BE05B49BBF0D8E0C6E5FD10D5DE8E61AFB9A8E5E380680494AA521528E394FDF128E95544C2AEC9C9570",
-    "hmac_key": "52DEFFC49F6D304EC1AE2148A42CF48151FFD84327A992038FC8B30CD62461D2",
-    "adv_salt": "5C2C",
-    "plaintext": "7B549EFF6E1B23E801C4A7E86B1D8D6C",
-    "ciphertext": "397524005B8C859AB7437C03A7969D87",
-    "metadata_key_hmac": "86C8DB2A741B5F4F4877776894D124180D8843C9F7B11F32981685797A17E105"
-  },
-  {
-    "key_seed": "01C7B5FEB270C8B5F67F7F798039301E50FEA859DA976BAB451478054F61E60A",
-    "ldt_key": "98E30FE5DEF052FDB673E2256CB5E5D6D77E9BC4474C347D57DE5064B17A20542C747E624D4526E711694A6FD0C93C6C13A3071B0DFFE5F77809D24D3EF616F8",
-    "hmac_key": "E246C2771C7B6301421D4C899C112EA5F7A30F44CBBE4BC2F9F051470FD68036",
-    "adv_salt": "660E",
-    "plaintext": "4BD3CE67C5F2ADC18ED14B684670638A85D3BE",
-    "ciphertext": "7AAD2A9F5CBCF1171D18BFFC4EE6401E844FD6",
-    "metadata_key_hmac": "C67D9C1B24F31DF322487D0189CA4D8315C6A4E2377C4A240987BCF0609EDF72"
-  },
-  {
-    "key_seed": "F4904DB0CB13270DCD9C13DC6A14B2E3B503D4B9FAD0B1A8C2770B2E05090E9A",
-    "ldt_key": "97092F492BD2239364727408B91712492483EDA86875A326D298DB66BA205E2117AA6DC9A8FDEEB4473EB8C460BB19CB3111C8BC3E149ABAA06B90C64999D7A7",
-    "hmac_key": "1F20AC6B6E2FB0471E43E4B1DBD285315E50D4C11A269197AEDB76CC06DCA8FB",
-    "adv_salt": "CF5E",
-    "plaintext": "2EC7E99BBB3A704467843FA7664C8E148B6C59F6DCB00CF99CB070315A01C6",
-    "ciphertext": "BB16B8B67492AA2B227FBC4261F880D2F5598E582E7F304CFD213BB84A6E1C",
-    "metadata_key_hmac": "773FA4CB401A01F5BB395A1BD6C3124B8B4273F2CFD12C07C7EF3776F6CDC61A"
-  },
-  {
-    "key_seed": "EF08F824062BEF0A8379885E8A2531D369A7F77FCC52DF981CF45C8975BD803D",
-    "ldt_key": "2CD67013A053273F3290E94327DB3779F8D72BC0419925AE248EA6DE838A2A9169085166365ED0726F2520920641860207A65C2B1776D36AEDA0FF253B6390BA",
-    "hmac_key": "4F1673171A760C49728C6D42623D440630617659CEAA51F4406F4200BD0BA554",
-    "adv_salt": "682B",
-    "plaintext": "DC728BFFF7D9C28C04C194CE8E655DE5C72A68C709C1EE",
-    "ciphertext": "9FC226D05CE13D4CDC6675B453124C58CF21CCD8A6686D",
-    "metadata_key_hmac": "5F0BD38A5FEE13E89C572D3BBCD59D6906A8E2A02529E78160B6B0B665CA745B"
-  },
-  {
-    "key_seed": "516D4CA6FEF5788DE764D7829AEA6CD87B66F3DC677E440391A39C2C75F5899A",
-    "ldt_key": "FABE68B1BD045ED62A42A478983D0B5496BE7094AEB10C579FFB72DBE52B432A91A4BFD1CC867B70D8BFE542AAA1F70B1D1890A96742B60C3105D0DCC26DCA9F",
-    "hmac_key": "84B1A7D838FFCD52EA99FA2CE46540C1A49473DA47D865A4859124040B3DEC3C",
-    "adv_salt": "62C5",
-    "plaintext": "5C262484CE732258C7C97EB7CAEF655A5C",
-    "ciphertext": "5ACCFC002B86B8EC3E23879BFA4A2D6A23",
-    "metadata_key_hmac": "A600A7048E963214232AAB951D5BD884833B6207433C26B07485591E332EABE4"
-  },
-  {
-    "key_seed": "758C9C6357A68FCD50EBB014972FD60D66D0C2EA6E30D982AB492DD313A36FAA",
-    "ldt_key": "C63861C29A721E1464BDB8099AE4429A9E4A6A0AF623A9BD22E2A72BB6EDC52ED634DFDC003C1C03BB39123A52BBDC32430AF54B6D5B5A9BEF490047282864C7",
-    "hmac_key": "5E3D6B561624A72FE89707F9F95BFA7863737FA85134C14026902D24FEDA442B",
-    "adv_salt": "D2FB",
-    "plaintext": "89B5B7B59DC93D5568823BC33E0FFEB5B020",
-    "ciphertext": "5D75ACC238F0501D30483E07AC68CCB5E4C2",
-    "metadata_key_hmac": "0CD25D3F2762C2B5CA1E46EED22D39B4C97F08EAA4AF77FB40AB952A4C9421EC"
-  },
-  {
-    "key_seed": "CF7B614C39F7C9D809D10867B102C69FA1F0BD2EED5E1C26A1D06291839AC194",
-    "ldt_key": "E4F98D2A202F1396D39B2B5A97CA978E2FC65ABB12302A06287E48975B0CA92BE0F2E0C2E4E978E4D1A1D83F1DE80D588AD3F246BC0D733536B3994700115110",
-    "hmac_key": "5496B1E574C92348AB6703BA4D70AAEF53466CFBAEC879B972DEEB9E27523EAD",
-    "adv_salt": "183E",
-    "plaintext": "298D7EE333860ED9A72CEC1E6EDDFEB923FB6EB48BC484B085895A5DB8B762",
-    "ciphertext": "B1C3E51BF8A181295157919A52C60DC02B3F4E33F072F7BE486340F88835A7",
-    "metadata_key_hmac": "3DF8E873299A132D7D0B5A0DB1CFF485CA89ADE72C8A8CD7AA5ECA4BFEC0B992"
-  },
-  {
-    "key_seed": "A8F3550C8371089061BEE8B8EDC64F09B0F58580343FF879A554882DC9FAE616",
-    "ldt_key": "342F4120DA408811C9ADC0A7F85C331C30B55A33E7D425ECD104C62E6D52F716DCAFB9F44EE29BC4EC81559682572030EA32A42237F3652F76648AE6E345D53C",
-    "hmac_key": "DB63910B29395E65D5C5DA1A172F8246BED6B349E22F4B2C963F9A0B09E8EBC8",
-    "adv_salt": "B16D",
-    "plaintext": "76F91DFDC368BF8312BDF3AEBE961C817A41DA1930299EF6",
-    "ciphertext": "8F0CD899DFF12A11843D48288E066F22921D374C38C44B66",
-    "metadata_key_hmac": "6EDE5622C333A0BC90CC266F7DE76A5F3050D36B59844F78088D916E43EC9D11"
-  },
-  {
-    "key_seed": "CEC4E08D1497E1273DCD56114CE693B46C5E883C90369C4D544D4BCF43E1AE35",
-    "ldt_key": "427044439ACC208943B46E74C836E35E370C5FA45C51451FE51A372AB326B2BDEFE92949B6A6B9F23D865AF1CD08688740E430831CCE09F44AAE7A51A1D1B874",
-    "hmac_key": "BBB2D08F9A8CB36DC8C4D2444080592B79F8B0F243A474E53DB36A17F1E2D757",
-    "adv_salt": "205F",
-    "plaintext": "3B66942104FA7299AA5FE643F85B3BC2084203095386E06AF42844AD",
-    "ciphertext": "063BC36BC6DA593AA7DA9D271293A4FC9BDB23F7A50FBEB55F7635DD",
-    "metadata_key_hmac": "B1E26FEC1DB4CDB1AF8AF4CE130E6947ACF8627C4BD253D2A90FBC8469321A9E"
-  },
-  {
-    "key_seed": "A938967874DC769310EF18AF3720212C1A0C317C4BF2B9259C5D0F25A974782C",
-    "ldt_key": "190AC6E0427292B54E2C8AE522D2FEDE65FE7EE51B1030B282869C243B253856F720713E5F1D695A9FB84F1B3B41B5B03F414C39A744C64EE084AB782A7C8737",
-    "hmac_key": "FF4309DF5A621410CF29A79D888BB996AA6A1A8DC97317654500D02B959DBC1C",
-    "adv_salt": "B0DC",
-    "plaintext": "C653B412457A1E824D35A702341C62A6158B0AF17416DEFD26C45AD4B68685",
-    "ciphertext": "F904FBA6CCC73F8678BDB66FEF230A1BD939CEE951CF5F0A309C80CB7D20A1",
-    "metadata_key_hmac": "3E0EC6995B3CC21278583C36EB3131CD286A914372FDFB8BC60AF6A4EB21CCBF"
-  },
-  {
-    "key_seed": "C4A063F997A8BEE12EF03654D5AB2103701DFBC06178CF024813563D60376D82",
-    "ldt_key": "75F588B1D0CF0E6EBB36AD844FC64D70C095E1FEFC7A08FF6D6D281D0DEC16A84AF0122FB5F9AF194087E48539394F8F045CD3E73D9BCA8B9D6E81F56B10B40F",
-    "hmac_key": "7D410DE31312FDD5BD22C27B5AD5406EF865981A55DBFF7CBF9C50814533C8FE",
-    "adv_salt": "BAB2",
-    "plaintext": "A9A15C93195D562D93FC862450B2CB51D5713FE1802A352D6A7FDA",
-    "ciphertext": "32EFACD41EAF68E171FB5B69EA53E8FCC448889069DFF470274EB4",
-    "metadata_key_hmac": "29FB7A5F82F38F5CDE0BD87C3C0C9F4F5D5C0FB3E94692CD85FFD9DA6D986CDB"
-  },
-  {
-    "key_seed": "C59CF43DE156A32EF85FB2947202374AAD3A105A173C6B1B50D59498E7BDD500",
-    "ldt_key": "E82A096577F720A8A992118378C10A3C7F770A7199FB5DCE9D479827F83B6A9B2654DDE018A56466E4DBA5BE06A052282BC05673641B91A37A1543843614B9A4",
-    "hmac_key": "0B5E15435209FF9070BD66C848518C86E0D2A062ABCA4260ED996B902E8B3F1F",
-    "adv_salt": "D85D",
-    "plaintext": "60DC2EC2E103235C23884E45A9C2EF427A2E98D3923B576F0C1BB58A",
-    "ciphertext": "9C4AA1A4604FB62790576EE6031576C8E9FC619A2F9E0D4349663839",
-    "metadata_key_hmac": "DC075B77FC337232DC9395A38F3EDDA35A822000B6784231D7CC23511333DB2F"
-  },
-  {
-    "key_seed": "A4C12AED69924E2223FDB1D8450A0DC6F82783E01521CC0C3C96E112FE000FF4",
-    "ldt_key": "5C526E81E863FABAB12F1ED6CEC79BC2A79239BA1E2E53270EF9E5C7B5F11C5ECB94DBCA6133DA2901D32481ED0CE15FDD1ADE9856B1889453FAB4DCFC57345B",
-    "hmac_key": "CD5891750C1D996F42B1D8DF01757164A8CD8811B36EF3057FA99165DB369A81",
-    "adv_salt": "457F",
-    "plaintext": "563AD9669BA177ADE51EE76AA74606746E3830300C43FBA4C6127F",
-    "ciphertext": "9F725A80C60503920F816FE26D86C020AF959DE72819C389A274DB",
-    "metadata_key_hmac": "F78E71F1EEDA9856D047B6C5B034AA4911121C24DEC49071F3FCCC9D56E7A239"
-  },
-  {
-    "key_seed": "50EB78011D344239B5A672730375635028EE53B4BCF39F1EBEE2AFA0C7FD4DA7",
-    "ldt_key": "C9EDCA4D84F83300442259B72C913B2352E974EFEA735984509C7C3C89511140113DAE1D6592422CDDF1380A4CFFBC2EFC547ADD64226F4725250037CA7AEFFB",
-    "hmac_key": "A7FA7FFF75FB13F7CC735076BC6639EAFAB3B2F7A3876055A5769DA10DBBAFDA",
-    "adv_salt": "FDF1",
-    "plaintext": "4C9469FA5E6C3E423D991170E7B115CAE5",
-    "ciphertext": "54BB9C1AE09F6369A5C02E5EE582A5DEE8",
-    "metadata_key_hmac": "08A663EED16372DF10CEE203C8472FADC1B3A082ADB5F9EB54D1680389F2A7D2"
-  },
-  {
-    "key_seed": "27D53BCE7DACBA568A39A1685F1645CD36AC503AA659A83EB0559F7AE9F33C14",
-    "ldt_key": "3A4FCB00294BE8BC6913C53D92C13E51D9C871165526F32187E555B31022C3BB26A60AB46293C947CED45165A6FD08A7B8113E8783BC7555A5E883F65F5E76DC",
-    "hmac_key": "66F9A512346EDE252444201B7EEE77E2A2BE2574F97092971DC1C27DA3812E4A",
-    "adv_salt": "9107",
-    "plaintext": "2C01BEB9AB5896B9A0FAEBAA5498C5B9A11938379AA709C386",
-    "ciphertext": "E52A5E350B96CDEC882EF5557A3B35073A01921E8781D24CAF",
-    "metadata_key_hmac": "4C8DEF8F58FD02CDDDE33615AA6DF31DF5AA06F2C44B6F7AA15512B4ABCE2A07"
-  },
-  {
-    "key_seed": "6D33CB29D83E251854842D7C5FDEC63D72D40A5CF60535E663350E79DBB3A25C",
-    "ldt_key": "F713D6D7957387932E6AFB47128E764DD8147CEC7E4222BE94BDF7FCF36AFDDBD735C5FD7841BD965800F6EFB7FA81F3514C58A536864B4864056824AE5FC9D8",
-    "hmac_key": "8E1668B71BEBA3F0D410DFDBF89E21AF816A5A2D0020D81001A976BE50380401",
-    "adv_salt": "3E90",
-    "plaintext": "C61353BD81EC324DFC41C3A1B0388450B94B19DCAABE01515373FDFE3BDF4B",
-    "ciphertext": "4BF8452123D8077E6548298DDCE40FE81CDBF7CF6E71C42EA41D3E6B8FBCE6",
-    "metadata_key_hmac": "2A8B59911F0B67E1B2AB133F1DF940CCECEB7D2149CBE6B68B7AB61E9204BFAD"
-  },
-  {
-    "key_seed": "E95A2C13227685460E6BBC7D00BAE0027F3BB83C89A8D7D9DA75EB2E7BE24C5F",
-    "ldt_key": "07DCF561EB4F767F9A1706A5F870C734461757EA1E0B053767605FF97152C8014C23BFBFFF875ABAFD43E2900F813F212ECF21768970A768B0DF3530F2473FAF",
-    "hmac_key": "ADDF6476780C628CBBEECCA568634C08A5B7BE6B8A50A67EE239B6C33E4EE994",
-    "adv_salt": "693A",
-    "plaintext": "7C73167198F8892EACDF490274257147BE8DF0E1387E7AC4",
-    "ciphertext": "D6B8DC7EB1701B7C44ADAEA9CCDFBED04AC6708ABD1A0C23",
-    "metadata_key_hmac": "440D97504963E18A99CCDC247B74916B80FAF5BC350872BF6365DA6A5334D23D"
-  },
-  {
-    "key_seed": "7B7F89ED9698576B9E30CB45E8D002EED1AC98346BECB30259100C730C51B289",
-    "ldt_key": "E858AA16B55C883F9BDC8EB94C25D17792D53D701844319FA1B854439B66FE8B199E5B78BA0B4B205F9D3170F2D6DC82333F8007680AC4F4B8E835C5DEEEE981",
-    "hmac_key": "997AEE99FD7C55377EE7217B5929F8DD078F68D8B2ADF87FEFF1941954F7BBBF",
-    "adv_salt": "5D20",
-    "plaintext": "4C87F23FF747A320E0F23BDFF990DB02E944AE75B2F04EAA4693C7",
-    "ciphertext": "5CE9CD9CCD7F347BBF6D8058AE3BF6BD9105E0F5018076515A40F8",
-    "metadata_key_hmac": "605AF3BAFE459C6A3BEBD895692BA73FF7387CA41E8F600862853EAA1C09D92D"
-  },
-  {
-    "key_seed": "6735B5573164DAE91ED565C002E77FB95A4C1113E9BBA940C3C114FA7D35CFE0",
-    "ldt_key": "7815EA6D91FE6E2AB65CAE36A3EC31057E7EAF0CC9985D83D582DE9AABAE07B991A4A4E1361C163B016FB779D8E06034CB638F2882691AE6E098F6BE8D493AAF",
-    "hmac_key": "4E41D32241A202F0C4D4E6E57492BAA3F2BD9BE277707B49790A028FBB83D26F",
-    "adv_salt": "B94E",
-    "plaintext": "DC766ACAFC7839796FC4CB092BCEF22D57C2B3B934CB17976776C582",
-    "ciphertext": "DBD0C3B395801B837F3DA56962386491455E741F9009608E12B67F58",
-    "metadata_key_hmac": "1BEFEFC89AAB43D2B10C7255F503307616980A873520C758DDCC7D1751E53A0B"
-  },
-  {
-    "key_seed": "B5C2FAF80689B16BABA1F500BAC5A0D907836B2C2BD48482FB52CAAD13E93B99",
-    "ldt_key": "0F77697600CAA022B28741FE42D64F32C35FB5DC4E766EBF3CBD75EC0ACA564553C7A134BE4CC8AC1A5D150B6AEC18AE1A86EFAF14EA3F2E837C0940D3118EDB",
-    "hmac_key": "F2C8D1A0A7A0AA4F5879E1980D30D38D5921B6FA1EAE8D37F589ED6932F0EDA5",
-    "adv_salt": "93B2",
-    "plaintext": "FE7D74A15EB5BD5E1766E76DE7BCAE615434",
-    "ciphertext": "B13912720CA1F0F40F31C6CF76F9DB530F16",
-    "metadata_key_hmac": "C6F8B4B3D3F3118C444C6905B6717880A34A7A9A15E46B1C94B9C8B2577C82EC"
-  },
-  {
-    "key_seed": "673E1E6D508B013132C409CDDB225CE851E2A1CEEA62C4241E63D41B85BBB420",
-    "ldt_key": "FFF7B05202440B7EDEB45E6B04D27C8651EDBBF2AF1F7A1EA28EFBE4059D80DF2AF1E8AFFF0522D0057D543EE9E18019745311C527B8FE9512D3796B7B90B976",
-    "hmac_key": "073A222A4DB5FE1F62B73C76E023EDA3EC4204AF7677543020998F454C4B30B1",
-    "adv_salt": "0C07",
-    "plaintext": "69DA685B42999676CCBE99073CCFED628C0376B125F6B5496B78B788ABD380",
-    "ciphertext": "E35B1BA68A8A40B8B9A9CBB2F145862506D4FACCECEE38FC37DC4EE0B7420D",
-    "metadata_key_hmac": "3E22A0BAD2D95A023EBC67CF26FA7774F60F0A9587DDCE0C50DEA930D1221F41"
-  },
-  {
-    "key_seed": "2A473F648120ACE551296E5F1266964BC978D9CF662CED89DBBE897CF97998D0",
-    "ldt_key": "F1D9A4C4F389D899357445A9CDDD7AA053309395801A8C85EE1AD3485E7C8D48BEBD36DF5531E8AC18F248744DF36F30DDB5F27109EAE58005587F6A662B5529",
-    "hmac_key": "0FB6CC7B6DA175842AF6B7C17770F74026EF4DA2369ECA21153456FFCEFBA0DE",
-    "adv_salt": "81C4",
-    "plaintext": "2FA0659EC174DB284AC1B07274E862BEFC38",
-    "ciphertext": "76F551451F9ABFE686DF305375C2522A7F79",
-    "metadata_key_hmac": "76DF23DFE3A042FED827F288D8BEC0C85DFD48955283864A1CFC80DE1410F314"
-  },
-  {
-    "key_seed": "0CFD4B3DF60D86C2D5CFB127375A5E709FC9A1D38BA481BFB708832059D07060",
-    "ldt_key": "DE876355D785107000B445C72437B19F594ED1319482701CA67A5835A2B82096EA1B7E906F511CE8DC115EF8275B3519744B0490F8C60B2FE2D4A91A488B2669",
-    "hmac_key": "7A7490CACC3C85B6E928E05CB34E3B42BDBAC5F51B2D200E846AA79382A36757",
-    "adv_salt": "8535",
-    "plaintext": "F60CA2F3F309D84720DD859265AB8C0BF21B21A7B4",
-    "ciphertext": "712A8FF93C2B67E2CD5CDF78A77260F58C8C409F5A",
-    "metadata_key_hmac": "8F0CEB0724D4C9689E9EE0BF071BB8286F14912EB8ABCCFDC666BE235978CB0F"
-  },
-  {
-    "key_seed": "637E997D7775FCFD647ABC97E5F205395081FFA752CFD8EE44DDBA22DF14C00E",
-    "ldt_key": "71A3170FE7684E36FD017B8490831E7B5EA24009D6AC494F441D85006C9C7076A47586A6BC43BD5A31E1C70EB9F4BD2F186D935D10345E4F006DC400845DEC79",
-    "hmac_key": "FE7F91371BB8CEEEA765089944543F3389C44431BF670A2943A6C9657C9CEA85",
-    "adv_salt": "44B4",
-    "plaintext": "A1CBAD09790C0302AB3CAB721094A285E7A6",
-    "ciphertext": "700A33D436DF6CA3D1B1E55CA1011F7255F3",
-    "metadata_key_hmac": "163E8E307FCEA7394D487D98F76039AF24648CE14200913C4CDA3DC4E3B7DE4A"
-  },
-  {
-    "key_seed": "454F7C4FDD05C3D7EA764FC8E988E836040D5AA71E9FC008316A56947C20FD9D",
-    "ldt_key": "071D5BD7EBD02F9FD0A914B726A055EC3174349EA6D84F9DAD2E1968CDC754D2953CF003445804E2528F8A3E17BBACFFBC095B09C4E7ECF361EEBC8FCD03FD88",
-    "hmac_key": "40258B6551067277782207D800EB3DB47059EDC830FE2A3246C82800AC93E0AA",
-    "adv_salt": "E880",
-    "plaintext": "A7C046F982D9FD6B21D3A46924570B90C64494",
-    "ciphertext": "0301037705FD66CF960A4C81FDCBF5C8F8DC73",
-    "metadata_key_hmac": "C278DBA0E3C2442469306211A58517E65AD0E823F887736F72ADDDC72E8EC47E"
-  },
-  {
-    "key_seed": "D093E0138C4A14E63567A46E37BE38A95A933E2B18FFD593BEF05B85E0EA68AD",
-    "ldt_key": "90948ECD6E3F08F28779605A62AFC7A4E12597C169E77E0DB269DB251A5D2BE6227A49974B79F3CCF14CF8F635ECC3EE79DAFE98AE4305FA3E7BE4A693040208",
-    "hmac_key": "AEEF514026B55FBD72224A96638493F83808CE90BDFF8499216CFC2D42DBFC57",
-    "adv_salt": "B1A4",
-    "plaintext": "858A256357724C43E415C3475B165A34C3AC9D19423B2506C53B",
-    "ciphertext": "7FE1D27B67DAEC6829AAA383A8804E73E8A7F30C1EF8919ED380",
-    "metadata_key_hmac": "CCEB348AC6ED5D37DB1820EF0E088213A71D94BC32FF93DC6C7826BC60C61B4A"
-  },
-  {
-    "key_seed": "80F2AAE12395E3C45FBCCCCD4CE458571AC2D3B5F3C9105298A0DD4AF317D879",
-    "ldt_key": "2ACC8ABF12B91584285D9217BEC51965647BDC25C38E17AD2ED19E139C656AAC431FD0E469EEAD559CC4A801B3EDF474A207E2FE57D5471F96D37E8662B77014",
-    "hmac_key": "F46746E3E06EF3A7FAB7005C249AEEBE9539E3F4C264CFA90AB61D956B00D430",
-    "adv_salt": "5498",
-    "plaintext": "E50C9E12135D4C7DE7D8F2C2F9A16658D01884",
-    "ciphertext": "B896FD364CEF99762DB1650370063F805A8840",
-    "metadata_key_hmac": "BEBA925F567BA2ABCDC3B9F5C92D04A0D7BFF3206448C15623E563B7FCCFF276"
-  },
-  {
-    "key_seed": "C96E4191747A3C54A5CA948B13CF3684CFE72376C48A944792DFE1CA8D68E49F",
-    "ldt_key": "85E7CE34452DB08C1092DD77849B13EBE15914A40660C16A91B2B26C21452331594D68F8920900E147A6899B7CFA63779F9D96A8E819C1F6D4611CF3D0EFE974",
-    "hmac_key": "AFA3822EAC8C7129315950CA2548DB5605F2CA378AD85E0C9151ABFCA816B7A7",
-    "adv_salt": "03C2",
-    "plaintext": "596D0202882999D31346C62E5DC1A7AD4F30427473E99A144BE43A",
-    "ciphertext": "BA8CA0694A4B92C46BCC64954BF0F0B77CFA8D071FDA015F98E39C",
-    "metadata_key_hmac": "76A8F3B97E05C47118B56C5EA74CE0BCCAAFE05C758B8B60D388DBD425309478"
-  },
-  {
-    "key_seed": "BA7ECB1E5C504F05039DA4D49A249131E5ECA00048F5B167F7BE7CB0F157C1B7",
-    "ldt_key": "D7C9D80FB954BF225624BA6A5AAED8EAB9A0DD87E08F2AA6735E4DD820C57A6832E7996FEC6885BCC768878B7180561DF95A1507832C98FA0378141DF7247764",
-    "hmac_key": "1612AC7D901DFFAA784F653CFE72C860F07D02D9AF3944E575392342EECE8674",
-    "adv_salt": "7C01",
-    "plaintext": "2FDC4609B6CE14A81DEBAF4E75410614668FA832",
-    "ciphertext": "8F726C4754B0A4063F66C3F17403B6F205548AE5",
-    "metadata_key_hmac": "7124713D3393FC2127DD5807AB32A4FF9387FC413D6F09E6FB384BD93663F125"
-  },
-  {
-    "key_seed": "6A262CB3E191FC39C51A0E668E86559FF5921C45343023E7453BDFB00FC666CC",
-    "ldt_key": "DB977C5B5D21458CA2C443EAFFD6166C8747F3365329BE64E1A64B65625E976436F667BF1CAA6C37B8373C2CA668D3F0605C50146610D0C192CCA0B8FBC40255",
-    "hmac_key": "0747CFB1BFBEFC45F41F2789B0868D6C01660CD37F8006F40AA2004372A05546",
-    "adv_salt": "3913",
-    "plaintext": "2D9A7F0A1695C480881FC6E090D39257564551",
-    "ciphertext": "8FFC40566A06DA6BF601EF2F7D819E3F1C6B8C",
-    "metadata_key_hmac": "2AFE4B48FB0D88225DEEC8067C47ECB280CC3B82D05CC3A5A200D96112D2BA5D"
-  },
-  {
-    "key_seed": "76CB008E2E1B57DD9874055C581A63FC92D9E9800FB63A0DB8749448A6814E1A",
-    "ldt_key": "B5ABFDB94FFAA9933392FD04EC0A79FEB1F12082FB14D45927ED495F33303BAED6FEDC64BAC2FC6DC39E3F96F79B21BF759B6AA8A28BAB7E0C12D0668A796BFA",
-    "hmac_key": "35DAECA2FD83A07F6081812821327972D968BF5642CE9081BA08B5D30706DAE4",
-    "adv_salt": "3AC5",
-    "plaintext": "548886B7B5D63513CAEC671632ED252B0B3D31216A60E7F1840F36EF",
-    "ciphertext": "BE79E939E021C2F93978A03220E2E106B98C2E1AB7C81F3E8A564BF1",
-    "metadata_key_hmac": "9EE785528D80D745CB0223E4BFAB2A72FAFF9C6AC999E81DD82567C42A093B4B"
-  },
-  {
-    "key_seed": "734A5E8CEFCE9DD5EA33A0D218D22B591BCA71DA64279936D33BC06572CAE44A",
-    "ldt_key": "3C2BE602C84B67E195D251C657B5488C50F6B683E9EFC5B6691148A53C63A2C237A833805EAA9875E8DB1D97A746D22E9933F21FB7D84F8140985280D1D3DF1D",
-    "hmac_key": "BB98542D66C10FDD4E6FB3A5230D4369B17A48C60BC14D717BD542A8A131DFC1",
-    "adv_salt": "DD2A",
-    "plaintext": "AB1C28BD51FCE36F10D09E48261359FADB02B0D8FCC01E53423E25",
-    "ciphertext": "677A41B92A745B7B6EE2305D6D2DC67A9D68C61B0E1E09AD12DAE7",
-    "metadata_key_hmac": "C3C99E314D2E8073F2D2E6E77A65FD8CA966D77B3ABF8CA3E6B119A0F5E980C8"
-  },
-  {
-    "key_seed": "AF06B6C79D84CE617AB5F2B490BF47B9B004B9B22346973BCD28075F827ECF99",
-    "ldt_key": "3F9FC45F4D363DAE68DC68017CE6A50379969FF80C95043D3F1AD7D164EB3A22EFB0A6BF5B7C5F32F27B8CE48B9C8C74D4751EFF15A5A3F366A83092618707CF",
-    "hmac_key": "8745D4E53B92E1BE50BDCDFBA6BCCACEA494EB01B9E11D93A5CA20213B1FC24C",
-    "adv_salt": "6D2F",
-    "plaintext": "EFACC5C8CEB14C77DF12BCFCDA089B28314458A3763415E50F1736",
-    "ciphertext": "D46F82F4632B5D5A87A1EF6AE88F1E988121EE919BB0AFEF8F368B",
-    "metadata_key_hmac": "28CD7B159662B6BCA85C1A88194E807287813E3A865A5DCA755D27DEC02468F8"
-  },
-  {
-    "key_seed": "4C88A40586DEF1E66FA06B376E94EF9CB0FECE6FF56D7D835CD5CA8633635E3B",
-    "ldt_key": "B084E839128D948FFDAAC25A10504AE6903899794FA31B17BCD301A5AF9496D8D74D231C07578A81D76FD43E7F133804462BED251DD562C82C8649322DAC37DD",
-    "hmac_key": "46C76D514FDEE225F7DF352BE4EBECC06EA9B981FDF241D137D77AED1D2EFD18",
-    "adv_salt": "E728",
-    "plaintext": "741215B7CEFDAA6BEF56FD3FFF8D12E9758BA4DAAD649630106F673C608153",
-    "ciphertext": "5315772F06F221277DC651FDEC639507132A13CEB75B181A455945BC3BAABB",
-    "metadata_key_hmac": "6C508E562A98C11B851A7451D55FD588DAB288EF91A59D74DB3EE7A75B1F7928"
-  },
-  {
-    "key_seed": "24F6FC4ED7E0A9738F9EB21B3247E49F5395A97A824108C5A962996691D51D8F",
-    "ldt_key": "836B00EBE1A2F5BB21B2A893ED31682E04AC369CF0963D6C0770355CBBBA37342AFB958C4DA2639D0C6B281F624C592BDE21727C16884D7BA21711D1707069CC",
-    "hmac_key": "29FB41D5F99AAFAF3860D7BA28AC448B388E64D643240E442C254D42D6AACC13",
-    "adv_salt": "E44C",
-    "plaintext": "7A698C0F1BF667DC0DA7940D5AA0B4276FF729",
-    "ciphertext": "4BD450836E530D172B9FB13F39E1CF154CFD4C",
-    "metadata_key_hmac": "03D0668BDD212753BC833AA080F209A21B5D8132747608D753ECB9CDC774E280"
-  },
-  {
-    "key_seed": "5C450E249D3939EDF8CBD3139DD3C432EAEA58BC15D812245D4FDC89F4219B10",
-    "ldt_key": "6D34AA5E251A577541F23A7C8B18ADBEA94E918DB1ABF496117F273A270C6EFE1143EE0FD89A3B29DE381EB692084DEFF9C31E82D90E52C8AB9079AAD4DA65E5",
-    "hmac_key": "335B67C2D915D37ECF2F775426571A389DF28D1E948118F944B3818A54C46666",
-    "adv_salt": "A823",
-    "plaintext": "BB455DF842180ACD349CF983717B03FC124FDACD16FFDDEA",
-    "ciphertext": "E7783B07DAED46AFA5776B676241C523CADEE551D7F5BAC0",
-    "metadata_key_hmac": "B10C97AB9FA38F309EFDB36E8C33F70DFD0398CA311EC8E5C44D16FE57DCB19B"
-  },
-  {
-    "key_seed": "0ABBD703E3F87A84F814A82D0775EFF810D0CD766724AD253B5D2A7E0E684550",
-    "ldt_key": "59D3BF27B0D72DB394BE06B0A758512997D3AB6C66B57F719723DF2B399AE219156C2AF670B261982010A8F8A91F7F359CF9DDCF5EBA20BC8D91647FAC28AD39",
-    "hmac_key": "D66245FD1EE63399A3E471ECB420796AC0D8C1FFBF67AF6F7D055A3EE32F7EA1",
-    "adv_salt": "D866",
-    "plaintext": "F2BB657EC7E3F7583B3BA4A8BDBEDD26E3D7699FFC2E65153E847AF0",
-    "ciphertext": "460F6850A542CA64A68A727824B9E053519EA01F399CAEE9CADA3024",
-    "metadata_key_hmac": "75D5D1B05A1B9B43ADDD5D9E1FA00FC76CFA56CAF94EC22FE3445A0BC60A9AF9"
-  },
-  {
-    "key_seed": "16C1215117831095E9B4721996091C58C9240A82FCF2062BF59ACB4A5AC02AED",
-    "ldt_key": "116D8E1C85D0F4F32599D636BE3F57DF9DD0F08B102930206E6EF8ACC41D86EF868CB4A4E08DB482ADEAE610E1ED6A36F3C1C5879B88E786E03AC29047A4F1CD",
-    "hmac_key": "FE801751B81E2DC203719AE83F773CDB5123C60E2F32F18CFF22AE1E945BD0C5",
-    "adv_salt": "4805",
-    "plaintext": "711B2DB12CB10ADE43838D7838DFB2A7735705",
-    "ciphertext": "D299FA3B8D21B0FB0DDE2FF30AA5CD1DDC8D1F",
-    "metadata_key_hmac": "09FE282BCD62C2872FF1A2A40857E55198E99B163A8B2E74C5956642A1CC6303"
-  },
-  {
-    "key_seed": "40F4674089D8D8F56CF4293BAF5F0FC36E55908623542080765E7FF5CEDFF491",
-    "ldt_key": "E29BF5DA37D2817BC44FAB3DD19333BDF6A455986C81492DC0E217402BEC6E013877620758F795BD41C8D1A1F89E8B72F3C42E9ED6705BFBFEACCA6E95EB9781",
-    "hmac_key": "016941DE37724E5066144373E82DB9BBD3D71A7912F889F68C3FD9CC4D534605",
-    "adv_salt": "0DB3",
-    "plaintext": "9EC1F5AF771E22F48D20249F90D4D9B62DFEB1321A101E9D73DC6448E4B5",
-    "ciphertext": "1F5D3CD8EAB5D8DACC42A839DB96C0DA3362FFC11A8ADE1C59540BDE4FD1",
-    "metadata_key_hmac": "FF37DC79D505C2628601DCFED56CF750E00A3F0C9DC2206CD8918AEB2C991EE1"
-  },
-  {
-    "key_seed": "09A1AE393888C67C96792918CB3736DC2F8FA41387DEDCCF512D971F42ADE5DC",
-    "ldt_key": "907C4CA4714B49BFE50A05C48FA228F40E1181DFAE1362B9DE4AD5CF705EF627B6CBC1B8D8AFC606864147C38BDC986F99F1AA21BFD001A5297A5436C4218C61",
-    "hmac_key": "ED09C00530E639C727F5EC52CB9F8EDD68A754E73CA44672FC4B52A242187AD6",
-    "adv_salt": "49A2",
-    "plaintext": "03E17167B0304F9FBD8DB82B8189842DB3AA3D8681C3",
-    "ciphertext": "1891DF1BBAFBBE2E2F4C862C22282018B639A1603F2B",
-    "metadata_key_hmac": "AFA4D96BEFE2E957E0E914946301729C029F8957CF5AD5D596A902627EFA188D"
-  },
-  {
-    "key_seed": "AF9DB6A25A8A5F0D23600D835D7111DE4E9B63B4864E65BED255EC2919C36E74",
-    "ldt_key": "B6B6A945F85914EE11E44665E0D2BF71714FCD49BD8B71B0DBC88B3A8301070B6DCD33957F9CDF4A7006F0ECC02D4F943F697A1D2E39C6825B0DE4D8E279F45C",
-    "hmac_key": "E63E95AD895F7C47DBBBDFE0E9F0966F68FF38DFD660A7D01F47C9CAAD32FE17",
-    "adv_salt": "71F9",
-    "plaintext": "6751240C54EF004BC3FA9C33E9DFC119055AA739F10824833625CA4E0216",
-    "ciphertext": "1364EBAD093E6C99789FC2B73F4E94A4CE02EE86434F44D630BB6D1B80BE",
-    "metadata_key_hmac": "683BF80D592878CDF4B44CAFE3DFBEC8290E549665A8036B5B4D079C1855D27B"
-  },
-  {
-    "key_seed": "379790A3A16688F9A9B467CCEA5F7A326C24C943FA548762927D262357C2A4D0",
-    "ldt_key": "1F02E51C252D4F0DA772E048AB8D1F1940E911D7074876049F2748BA5A2D5E0366D4534280C5CBBCDE5D9F3A5AC2B6C7E4BDD971491E34E37CC7AEC003E2C45C",
-    "hmac_key": "70B0D103E3A17AA9F9C654D0AE9F32A7F24BA8077EC1B1A7EC946A73E78D11F8",
-    "adv_salt": "7AF6",
-    "plaintext": "39D25AB8EE2ACF8D6B925DB13694B813F6E9E3257B9D6E623FFE3BED8000E9",
-    "ciphertext": "5E54468B2DF8BE02F18F51BB0CCCC0CDD188C2DB524BC48AC0C8AB7E546F2B",
-    "metadata_key_hmac": "B1C909F56C97606FD765A2307EFFC78E083152BA417D379BBE45F65D53E9605B"
-  },
-  {
-    "key_seed": "7244E9537F19F09B9E0B3B20EB96BED6DFBB284C6C1823D249A81D644181D41E",
-    "ldt_key": "46C45C77471DE10B1FC096AF2F1B4E50BF91E70A315E9F1DF80930994AD7B28887C991FFA51926E641FD0885DE7DD716CCF779CFF77111444B2DF878484E3A8D",
-    "hmac_key": "327900FAD35E7BC55FE085C6C1E1AAACFBA57B7D87E7D009BB1E216A64A829C8",
-    "adv_salt": "4428",
-    "plaintext": "76F9D208707E3576C1DF5B960E9A554FC47BB2D1E616A0653B2827B3D13B1A",
-    "ciphertext": "038B6EE79D23F216B980895560F7287144C4B355014560DABE07D32119FA35",
-    "metadata_key_hmac": "FC4700ED6C39BF038DAC105AB927205E6B4C89219B1612E61D109098E2178F3C"
-  },
-  {
-    "key_seed": "A6D61C0AC7BCD56C6C66199C2929A1389C1878EAFE63076FFA36409A43BA1622",
-    "ldt_key": "CAFAFB9562C5A9F387F0A8C03D0E8AE3013D806486E2793CB4A76DC646F391CEB4F6DC28313C102E3414005414EB60664E3B11E93BC222ED1B1BA9594AAEF59B",
-    "hmac_key": "810F8F1379452CAB6D3FCDFC19D70C2EC1EF166009B5EBEDFA1924D39DAEA3D7",
-    "adv_salt": "03F9",
-    "plaintext": "E0DD037EDB87219B892C9C5A6E87C53BB1B730B8139D6ADAD200F7E4E2DF",
-    "ciphertext": "B3FD41D849FA82E9F3A0B32DA870ED2098290B5AB6EF5AD70260A9799C48",
-    "metadata_key_hmac": "0FA6709E1302364486231A1107AC491627615536700B48C314610D70EB4A6ACC"
-  },
-  {
-    "key_seed": "E1D2410675097312A2DCC79F898D619E5334FD1CEC801C4C92598C2E481EF1E2",
-    "ldt_key": "AE7B6D9FA16122304F87C2C188ECD2397096236D2C6A6C866C92590012DDC6B56144B67B67F3C6B3443AC2305F1996C1525CD2E2391D6217DD98C48AD3F5BC0F",
-    "hmac_key": "1B7EEE453F9EEC77FD0CB9DE5EB1A62423B01A5F7158BF18DCAADE4BC1E9D3B7",
-    "adv_salt": "41A2",
-    "plaintext": "506C68217AF1FDE8240A5C581499647766061D845D7959",
-    "ciphertext": "4D5AE7562E3F2A08B8011B7BDF9AB1BEBDA3B292735091",
-    "metadata_key_hmac": "3C85C985FE3CB1D007A6E006E9FE0ED392F6721D460A14CC9F4C45D94CA26F11"
-  },
-  {
-    "key_seed": "4A2CD5102101DEE0B9C7A5A0A3D90A17C6EB62D9FDE146492B1CB2018AB73F66",
-    "ldt_key": "AA5DE1D69FC58B0A102AE2F5185B57FA4391878B929690AE700A108CA027F9E10853E8CC884697364343015DFC5645743159040FCEC57C115B768155F7F2011F",
-    "hmac_key": "734F591AB362AF84FC1ECB2F20AA3A5B898ADFD8262E7BF51D4AE31199E324D9",
-    "adv_salt": "FA7E",
-    "plaintext": "F0DFE7532B16B3D85B31429FC849FCD5152CD16714770C0600900EF2C2",
-    "ciphertext": "3098A07ECEAA126C8D0CF51A5EEC21CFFD4EC4D87ED6D370F3249AAAD1",
-    "metadata_key_hmac": "69E5A6EFB346E39B928B380EFCD0DB3C12171C022CDA398D241E7FA75F3E7C2D"
-  },
-  {
-    "key_seed": "AEDF65C5101442019724D8BA5A19770BB4E4B42ACA063CBAF971EA9D389F3055",
-    "ldt_key": "05D0A74C199E27F29192C6CEC7DC956D87717A96EF7DF42E61D6AEC1BEA892B406EA16336F015B8C48BD7D117C224D7F5D24E9C52B1D2741AC3FF19ADE5BE38F",
-    "hmac_key": "FC77044E4B28313A8C93DCDC6C94429ADDCEB92DB790D7149B5C78D277E18D5D",
-    "adv_salt": "BE34",
-    "plaintext": "FB1E97034125E50B7DAB98B5FD7AACC7EE06E8CCF7F44C77BE3009AAB4",
-    "ciphertext": "D559FF803F17B537C041D522CAB6E8CF6B72C2FCF217ECA62267582D15",
-    "metadata_key_hmac": "702E274ED9C9066C50D0E14B33F3AF6A3437165A3EE58F253334AD0A715DF5FF"
-  },
-  {
-    "key_seed": "F859152456CD31A181628E7C987F1FF81DD5063757F5AEFDF3996103478EA458",
-    "ldt_key": "A719D3F4B3DCBB6ED0A9A64D85041636DFD5B2932A08E4B991D98A11E52CF26343DF496B055FA67E52971FB76AA634E8525B4C3AB49A770E094279F257CBDB9D",
-    "hmac_key": "3805B71A01CC6F4F3F04F5A855DED524B1AFAAA992F25AB6F58A67FBAD0D3410",
-    "adv_salt": "3AB0",
-    "plaintext": "190A6DEBA8A10730B7FB590DF7B5643E5EE5AF0FCC3F216549C9678B99FB0A",
-    "ciphertext": "3A11A58519E7550E85D9FC159FF99FC813FE0CEF7A81E4726DEF8E758DEFAB",
-    "metadata_key_hmac": "B5F0F0476D7875FECFF4A81A465E32FA42DDF11DB2DC7D56CC43A3010EE94231"
-  },
-  {
-    "key_seed": "9BBE03E7F339F608122CAF6FFC7C42F84F13E2E60AB8D35678F1186EA48C73B5",
-    "ldt_key": "D20DD49BBE002E51FCC736BEFA614EA57798AF503AF91FA49CE58752556DE744E8BCE9417C25A14F4A4D518D6373FD6DDF46A9D1E5C9C77EB880C65672DBC2F3",
-    "hmac_key": "61A3E0DA748DA8C6950027C5C88C2DEBF730D34D363F2689C556001409B81B25",
-    "adv_salt": "7A02",
-    "plaintext": "DF9154B8C961A5F6B5962461C96CFCCD898F4FE1",
-    "ciphertext": "A7228D2DFDD4BB799B82F0B0742079AFD404EAE1",
-    "metadata_key_hmac": "6272353CE4F74C61571A21A536A1B3F72693B318865DB9BDB6234B755C7B89C9"
-  },
-  {
-    "key_seed": "3379C24083B6D7F1CF2E19BEC671062FC16B05D05C0316CDFD8125B52685BA27",
-    "ldt_key": "28B693F96DE35E98588FF911C4D7033D495C150116786E2DD2CF0A65F61439DF2C28ED20D327537159C9770336946F92446337F448C28D81F00271871E557D22",
-    "hmac_key": "E4E45C15C730D3B65994671F11EA6AB78ACE91E79CD546948AB64CC74CC70CA6",
-    "adv_salt": "7042",
-    "plaintext": "B906E13C3DEE4CC03203C7CB8C320524CA",
-    "ciphertext": "C62FC94C2BCE15064DB15F6E10E40679D5",
-    "metadata_key_hmac": "A6A1F42E3B0CFDC4B6328F759DF98E8CE576C2B858E477D616C32A02907D0F67"
-  },
-  {
-    "key_seed": "D4B7DB2FB9D813475FF7C49910225821DD841A82C42A49E7AF938430278AC86B",
-    "ldt_key": "656D63EE5608213E2D2AD2EDCCC84228D1139649BB0B5288488D7532D087526DB70454FEF78DBA5C69CA506A0385D6F693E6128F8EF0CB5A01E418243CA32E1B",
-    "hmac_key": "8143D313C451175B1AC1FF43C8EB95BA4817F8220F890D4D2D971D9192115DD7",
-    "adv_salt": "F65D",
-    "plaintext": "690114D595228CC8D7DD1C575CA072EF3FD5",
-    "ciphertext": "E1D6D17653346FBAAB461DDA2632C6528D72",
-    "metadata_key_hmac": "64B76FF204DCB39B7074281894CC092A016234E7AA68BCA117183A67B4D82D92"
-  },
-  {
-    "key_seed": "C05773CBE32033FAC35ED3E6466C1323AF867CBDDE0A694937F2BD49E7F43328",
-    "ldt_key": "8E6838C833AD2533EC55858956B731F32CFB82CA67541F9B5C24D3B39EB4DA9D4CF01B1D9BBF5599CD6745202D02EE6C482B93B87B3011B0BB95497F25A9BD98",
-    "hmac_key": "C4CA1550C81ED8EF579738AF75851AEEC86B13F55C5DA9963DBFD0B076F37F78",
-    "adv_salt": "2237",
-    "plaintext": "ADE80DC4D6A916B09DC82B59DDA7712AF85B522CE3CBA2D52C255B82FE9053",
-    "ciphertext": "87A0404911184E56309EF8F19210D4044E3001AF15F6E33B60274159CF62B0",
-    "metadata_key_hmac": "9F198C89108CAFFAD798C53DDA166D565B5C3047BB418115A8557F797126B308"
-  },
-  {
-    "key_seed": "5880FD7D91A93417C1FFE405EC94A19A6BBCCEABDD48D3BC826C6F279F104AA5",
-    "ldt_key": "066461A5D072A0EC2858792B929F5876301CF7C6145811E4EB6FF26A391C183B5D4574C0C13C6F87AF7C40C8F79F7D3B89575B6FF9465333530FFCC1DE68E34E",
-    "hmac_key": "CAB5F8751672EFB798DC8693B2C967CEE74C5586582B9C8F2948A18CA32CC2B6",
-    "adv_salt": "9CDE",
-    "plaintext": "1B24CFCE639A3F09B2018A7D96034474CAFA49E28E08",
-    "ciphertext": "9404526C7DC6AED251615018A8FD5A39BD552CF88689",
-    "metadata_key_hmac": "F09654197E670DAC944E0C3AA8885A40DF6718261841CBD4AEDF28A47CB93760"
-  },
-  {
-    "key_seed": "103C51037466A5C87BBF1922B48608F383B454B711CD37002A7D37431557BB17",
-    "ldt_key": "6F4868EFA86DC84B1EB724C958C564324940E3B86D7A22BDC1892A7B61D4B6E79FF1D2BDDEF162EF61721BB754E30B2494B053B48316BE7908A5F296C4D0F61F",
-    "hmac_key": "018E9A83ECA054D5AAC2C71FECFE7B3352F585D0CC2B22C2854E85387436AA33",
-    "adv_salt": "BC4D",
-    "plaintext": "7502EAA2616A58982892655C4E90C8BA677D1B2BA6",
-    "ciphertext": "4C47A408ED86802FC9EBB5451E495095F1E967FFDD",
-    "metadata_key_hmac": "00F05326596F613BF8618381E00908494CEE2122AA57E96D5579356034B6BA6F"
-  },
-  {
-    "key_seed": "19DD07C711165600E890266BA899E298CA1ED1B385A4F135493A7862DAA791FB",
-    "ldt_key": "7EE579175ACC9B076AA964EF815B6D34A4557083747349FA3853A32819859FEC9581C30AB2D8A520881F2DB1496C39B15EEE9CD70C81EB2FEB0D823CE5367694",
-    "hmac_key": "7B46AF4E0821AFC7260427EFFA2176F5D5F9B3DDC04D7C6C62F085BC1B8BA38D",
-    "adv_salt": "94BA",
-    "plaintext": "4F558DBED51485869A106CDB17E9209277314015D078286106",
-    "ciphertext": "335C337C57589E2CE14AD2D6A62588DD87B12D1ED87D399728",
-    "metadata_key_hmac": "BD6DDCFDB96F3033E408133E9F4649F69FB198191D7EED711021079FCD4A0732"
-  },
-  {
-    "key_seed": "4C24ACBE8DB04793E4C1C930EF7E1699343BA6CEE1A77F7A3A7080F7E251B12B",
-    "ldt_key": "4AB3E205D5616A66E3DF018A9E5097833128C778EDE59956AB0F1FCBD971102ED3DBB8EC74C775ACFFBFA71CD0826703414962EF6AE830865F03F89730CEF8BA",
-    "hmac_key": "DEB362F55D37409F8994BC140597A2B3B869E97AA2B3C68C887EC4E92C979E5F",
-    "adv_salt": "49A1",
-    "plaintext": "1E9265A4D08FE3087F99771E6F0E2F15E30A2F7FC13229AD7FA6",
-    "ciphertext": "96C260845C844E2F9F97A44178961FE54B1C3D0B5D486B438CBD",
-    "metadata_key_hmac": "9D264877E55B60AD23B434F43BAF63F413B4E7D819AF86B782C9C7025695D367"
-  },
-  {
-    "key_seed": "BD25F6AB07A65C22FF54D1F5D756483AD58531A4B197A08E815FB53EF0F18646",
-    "ldt_key": "82D32A9CFF0A74480A64F2E11ABACF8FFC634D88E1691B59CB4196F7013288A8949C9A3A937197FB4DD75FD0610BFAB2AF596A87129C5486E6BC9DCA39CB9CB5",
-    "hmac_key": "005333AC607D1782F4C15D328559F6A7D77A12EC58DB0E5FAD697056EF7B44C4",
-    "adv_salt": "446C",
-    "plaintext": "E80A906468574CB89BAFF8E52A1D51667D7FEA6F99",
-    "ciphertext": "D16305F7524DB1C300E6F54E58A23D0702E991E8C0",
-    "metadata_key_hmac": "73F68E09B0C66A2301CA983A0F97BE2502775A4E6A0FC079D27F7B0B098BCB02"
-  },
-  {
-    "key_seed": "FC418978C0096847756F5530F89D00063F542E32B79579C002C07951435B34D7",
-    "ldt_key": "E73FA0935EE81321345637014FC3AD59363999DDE09627A7BB3CAED9032A268A4AE1D087F8EAE7D78AA1827541DCA66F99B5B310CC228E7F6E7A6A999A4E0940",
-    "hmac_key": "1B15832662B35FB725833CAB4131A05485B7AB975CE42D6D23BC2262546D6A97",
-    "adv_salt": "BE70",
-    "plaintext": "0450BBC8C51A4EBC692A4C23BFF4D210C4DF13769B",
-    "ciphertext": "0DCB71960A1F52C03470830C48FCE8A465E3A581E0",
-    "metadata_key_hmac": "E8E6DCF05303891D8B17AAE678FA58FCB0D368B7B7322FD39A98CCA0A7BF24AA"
-  },
-  {
-    "key_seed": "2041180A29376678BA3151D8147C030511A73DBA688F75B9B8DF5CCBCBC1C0C7",
-    "ldt_key": "35BE7EB20EC673E9C12424863CFED35E91126EA77DAC8776256F0CB12918397FB459714F4ACF35CB3F69E0BAF7238934E556D6B7DD0B5A11803DFF5E1A48488E",
-    "hmac_key": "BDDF2F2E9052098ECC8D8367E57A3ED601AF07B300EA24D5CDEA34EC7BEAC3E2",
-    "adv_salt": "3B5F",
-    "plaintext": "794AE946285A473064DB37339A086869BFD1715597",
-    "ciphertext": "02F228E175B8771467BA176E1033E1420168B518CB",
-    "metadata_key_hmac": "E7031B66347209249DF4FD89D8DB2C106C0DE93343E35EC8043C1240CBDE03E7"
-  },
-  {
-    "key_seed": "0DBA415E4560AE4A11EE9EA226E8F0557BDF49FC3087551836A41B45063B7CD8",
-    "ldt_key": "5F49DF5973924250986B9C0C1DAB8A05A04E37A6A19FB95588D93E93678A5A1AEF02A3D22819C0BBE403EB2D69B87F71CD001C22EED3255B966F771839EF4F19",
-    "hmac_key": "CEC27E2CA4842BD6BEB45A338B1E4AA9A4A3B4C218A365D9EB6A6653CEFD558C",
-    "adv_salt": "A23B",
-    "plaintext": "0D83837FAC4E96453C6157ADCEA227E3FBA12EB208D8D4AE5C",
-    "ciphertext": "BAE54EC7093C124986CA0CCB2E946C87ADCF4EA04E715DF0DD",
-    "metadata_key_hmac": "7AD8ED9CBF46444E8425E3A5D1C3C1CA4A1BE1305C337000BAF1AC9A177B700A"
-  },
-  {
-    "key_seed": "BC06E8EC0BA473633FED05A99FFF8F319B5F39390062E1A8E214ECF89960BEDB",
-    "ldt_key": "23EE6EF691DB54DCDBF43692677C8081167AAD38A64B8E00394A86057186FA816F4377F68F268AD5EDE58120BBBFAF87F2AC58ADAFD9DA6FE04FA698466E4785",
-    "hmac_key": "1D023E11431A19ABB0F4A998F7C63737345FE05A93C94F05D984BC35A9B28FE9",
-    "adv_salt": "A8E3",
-    "plaintext": "2692F054D5BBC048E1FB17C93A3A7EB7F8D395C4AEC891EA62AF",
-    "ciphertext": "7C876CF1FFB1A4B8491D3E2D7568C4051EA1533FD1402F3AFA99",
-    "metadata_key_hmac": "B0A56DEC8D01319E85DBB0F11EB0D83B9786C40F586B6E8E59323D4D92272057"
-  },
-  {
-    "key_seed": "94729F5E0F0DCC59434B93A0E0C873503747FBD39EB607C2ECC157A624C0FF71",
-    "ldt_key": "886E23DC1E672ECDC4444D462A9C7607022C9F049F312B9D56A4F7A80016383044549AC8F0D8B39F09E6BE341B9ACAC094176077C11A8B4AD9498888E744DA92",
-    "hmac_key": "17B67DA2B4BB2A3492A8412149B28C4608B97B58937D319A638F97D5A04740D8",
-    "adv_salt": "B2DE",
-    "plaintext": "C22F66F44DD1F346D53A32B3B86F73D1B6F8CC347EC6BBDEAC",
-    "ciphertext": "2ADD9C73EF5AD3A1D42DABCD759CC8BBE6019500A723D50A30",
-    "metadata_key_hmac": "FBEB951F1751A8912E7BC9BD30DF3650E14E0E44932F6931E81BD9D4EA82D796"
-  },
-  {
-    "key_seed": "97853552152F9C635D179E68B151CEF51DAD18179A908D0AF77E431A8FCB2A4D",
-    "ldt_key": "655B2CD6BEF4DF670E969A9719F422662BB7550D8B81E7D7E47815C13510FA1AA66D8D2D6884EDD3FA0932E664A726D0EF8569888250B933EB982D0BF8F5CE01",
-    "hmac_key": "56BB22A258C6853321CE093F9DF6B2B09BE4850103197D46963693B468A3550D",
-    "adv_salt": "16B4",
-    "plaintext": "6E7BE9F6C7B5E8DA736541F6DFCF3B192A3DD80673534A285EDE",
-    "ciphertext": "1AB71BCEE930128847F36B784A8DB1A29CD98B4A39822697CFC5",
-    "metadata_key_hmac": "5C5BE24BB0A2C79196CBF3E24E309C139415ECE1548D91AEBF8A319B1902B071"
-  },
-  {
-    "key_seed": "1AD10AD3882870D10712A9984F92FBA3CCDA6029A5473F90B62A3FF9DEE4ED23",
-    "ldt_key": "00F17B5F6487F2376F19196B905330C9CCA23244D6A0BAF3CAC724BF19226A0185B2783608DBA6A31CB4A2FA8C2C51B7D9632763773BC9CCCB399D69A62DD1A8",
-    "hmac_key": "9AAA6EB7664A9AE3C39469753CD27E3A28B1BD55A9BC2986497BFAB9DAEED6F5",
-    "adv_salt": "0910",
-    "plaintext": "747E8ABA603A3694061C8BEE2CB0FB665BCC4577A8CD87A5775B6ADCD30555",
-    "ciphertext": "51D689AA9EB209955D24F77402D1887D73AB0C64F8AF44B161ACF87B8F915A",
-    "metadata_key_hmac": "0E61826D0787C0B56CE2D4EC6412155EAE66CB475CB5E0EBF28CB9B433B59483"
-  },
-  {
-    "key_seed": "ADBBD9D7DC78BEEB41DA9F651FBBC63900D3710AB350C7BF6B86B105A9529383",
-    "ldt_key": "AD2E0EDE35E70949CD1BA26C33E2B6D2D74E13DF583639E7FA90338847CF7DD4BB23ED634F9BCB7B9C1F882081525ECADD25E010899436B0C57B5FAF626368B5",
-    "hmac_key": "C270CF2D5C108729101561D8649B97A83088F639ED0B735B8405F4A1F2F1F9A7",
-    "adv_salt": "6C68",
-    "plaintext": "F7C24D90CEBDDD06238F4C21F925C3E7AD0F66E2D8FA5F1DF63A63",
-    "ciphertext": "0E22356768057409EFB7DE829ED923E4DEB21A1727DBABB4D7AA28",
-    "metadata_key_hmac": "86E4A13C61362E747698A54267540422C5DDDDB4A7727FC09812F2E521A30E69"
-  },
-  {
-    "key_seed": "1A47B9AB0958B40D404ECF8DAB40A5AF0379810CC7ED13423355D7C4823993FF",
-    "ldt_key": "FE177A0CFBA6E16C413E3C9C5C92E73A8E29AEC3B1B80774F1A4DA648F88C701BA4B6F0A02B4A7AA85113610F21A3912A33C9D1A3F9D1A4CF4E6177ED7E334CD",
-    "hmac_key": "C03D5D6FCD0A6B5643DEDB532E36F7E333F0E39552BD7D7108030D04E3072920",
-    "adv_salt": "3FB5",
-    "plaintext": "98915F57FBE4928B727441B471351C0DBC8CC3AC7D9DEF47FDCFB454",
-    "ciphertext": "82EC5640AE09757F7C5EA7664D7ED8B7565D828D299D39F43FDF2D58",
-    "metadata_key_hmac": "5C4AF378097BB2BEFA10AD9237E84C0EFAE9C93C39A4CA9F57D72DDC67AD5076"
-  },
-  {
-    "key_seed": "3B58E5F94544D95B3125D50E3269F098E8A6EECD22159F6FD87DF83C43B9B926",
-    "ldt_key": "00AECC8B7B786EB7115033A26DF5DE7D4E58172546C8F6F3528B58A4EB4FF0E30277C5DA73FC1F0F5821CFF61AB5F4E2E6BBF769EC9BA049DB4D489CD9ABD0FB",
-    "hmac_key": "92704BD960D4947751CEB1B956AC1E091F8F0867ABB594F701105B734998959F",
-    "adv_salt": "0887",
-    "plaintext": "893ACB5F973D542AA82494226310077D7F2E7E07ECC3489A",
-    "ciphertext": "FF2CB47ED8E2856634C93F8073A392339721195200EA5137",
-    "metadata_key_hmac": "DB37F8521FB8B20B0F27075A3C55BBD41C82C81B535B3522D6F93417EFA0B3CD"
-  },
-  {
-    "key_seed": "0034249B125C14969C055AB6E8AD4BA5C84E22C966B5042CC0016374408F1B6B",
-    "ldt_key": "A54165E007DFA1D1222F8561023610E65DE26F2F3D61854A0E92E94C029409201CCB3DEB76D9F8442C1BD016AD9CB5A9D0818A95A7F64073BF265826DC8D1C24",
-    "hmac_key": "79E59673CC35398924A8C083006BA047919F097A1BA2BAD17E6F52435E7B85A3",
-    "adv_salt": "DB1A",
-    "plaintext": "A0C671ED2604B5714119F46780F0B954676E4A68",
-    "ciphertext": "D8621C93EA8E48913B6A8CEE365ECD3CCABC2B75",
-    "metadata_key_hmac": "6736A4C00A729980D08669F2E82860F0C3F9B5FDED7E1E74B0F83B4A0EF475BD"
-  },
-  {
-    "key_seed": "55457AA6F640F046C1895D40A87D376907E8893CFFAED109DB1F4A30D001435E",
-    "ldt_key": "41B152812D750C330B6547B0CBB733FBC5B09D0F114ABC7D16B05C6080D8CDB25C838CC9727DB7AB15A50C7CFC09AEDE64AB0AF097E262EA08F97CA21CBDBCEC",
-    "hmac_key": "F12AF2C68C4D5442675432C3869B910A60A202FEDDAB503023B3B8EACF06ECE6",
-    "adv_salt": "560D",
-    "plaintext": "4253AE1DAFE28B8E50EFBA15098B93532BE965EC8AF86CF8DBA72363BE9330",
-    "ciphertext": "703A4D5320918CCE049D980BB7F8D47EA381DD2DBD65B5B83D4FE73CC10BBC",
-    "metadata_key_hmac": "F3346173FF7EB6EBF58DBE71DE9BCF946A1EE4176DDC42AF5641446846807C44"
-  },
-  {
-    "key_seed": "FB650DC96511A841BED5A1E0E2354A588E776BD7753ACB1F8363941F93602011",
-    "ldt_key": "B73FC2649AC4BA1FB8A85AAF60395B10B473CE3473CCD7C13E0A8531E4DB43D5ADF9A501247FEACB8467C3BE6494C00687FB482392120D66589669B4F53B6FF9",
-    "hmac_key": "14343C6976F4CFA3DDFBC4A0938EF962E8B98AFA05E9E1508043497220E8CCBF",
-    "adv_salt": "AB18",
-    "plaintext": "837B426E7DB843BB0846492BF2DA7748E916",
-    "ciphertext": "C23C23A0EAA4736D6829B2DE63AC71B0BEF3",
-    "metadata_key_hmac": "EDBE5C4406A4A0EA52DA4DF82B1B024C515BB664879D3A7FA291E48349D0C759"
-  },
-  {
-    "key_seed": "A36881200B80C508BBB899780C65614070D8E0997194EE3F6D98F23421B4FE49",
-    "ldt_key": "9588F448D9E4B82C81F4847495EAD92E4109B17BE5B89132A0AFC7625E5486939AD5B0347B086C353FE84DDD27019415915BCC9987769E77BA49D7388D43536B",
-    "hmac_key": "F83C76FC45AF40B179119FC6D048FD78AF5A08512AAA20C5F0C206463B35F9A5",
-    "adv_salt": "6750",
-    "plaintext": "A207FCA9EF439528FF350019B680F75BD4C7D8A4",
-    "ciphertext": "68D0D588C7E1552B306F08BC8D7C74E24933DD60",
-    "metadata_key_hmac": "E224004F5FF136FD43E448BB8504686E05C25DB926D972FED62645183FCA4C5A"
-  },
-  {
-    "key_seed": "3AAC65BEED446CECC1127EB05936F9F99E68F6C30B80EACE4C8164FAC73CED77",
-    "ldt_key": "2ECEA9B27E4CA5F34BA9FDF2D4FC470072B76C9A679169731B568E8645B35486FA2FE400C2D4FCB3487A52391C5D7EB3130CD6495043350C1D5AF8BD2FF6F5B9",
-    "hmac_key": "81D255D34D3158FBFEAB4A4BE471456E9431F01A191E3567B86B7DC14494DB75",
-    "adv_salt": "AACC",
-    "plaintext": "8FB2EF0D058113BCD6CA3A0DA4702430FBA623CF93DBEF3815BD167328EA84",
-    "ciphertext": "8D638D964B3FA5DE2282900DC6AEE6EFBFC2762E4C64038A8930C53F3026F4",
-    "metadata_key_hmac": "CC4A52B10E528A93D651FB9A5A631AECE7F19601E5F7ABC86728CE849B132134"
-  },
-  {
-    "key_seed": "86CED9BF7C9A488FD2EF946CC901876DAEC7AF7F0C74A09B0379BA12F2296CCB",
-    "ldt_key": "B3E6E90D7759351935D01D50E1873E124F731A1FC3BBAA9DF8311777936399C8A08D3C0CA11813322FC74F9916ED6617E66FA63B312718BB1E8D7D0A8E62F4EB",
-    "hmac_key": "E5968C6F4A11E1D4D9F54255636037C12143F1B500D9CABD99FBAFB2745D5367",
-    "adv_salt": "ED2A",
-    "plaintext": "E5D96EFDB293CF2525230EC26215671D9ED8FB8F2FCCF68937308B679B9F",
-    "ciphertext": "C4D352D54F8920C2704BBDBAD3E32BC81BD826FF9B2C329A9A5BC4A3BCD0",
-    "metadata_key_hmac": "163F4A8C5AAC4F380D601C6C233A886A22FCFA8250DF9D778FA5FA3B5EBE79F4"
-  },
-  {
-    "key_seed": "5844A8BA6923B40A13266B3A5C87C0B3279B6B433D4A7E7B4A67146E0A7584B9",
-    "ldt_key": "E1AA576C09DC8153B64DE1DE9995E3A0B653EB33F397E66F85CDAA798E020E65BFF05D312CB5DB8F2664F1A6860545029C92A6D16973B62D25E43CE54904888F",
-    "hmac_key": "AEEF23403DA56577CF8D6CD29A19F17BA7FAF98BFF21A6DC3F6D5DFAE3A66BCD",
-    "adv_salt": "AEDA",
-    "plaintext": "099BBF9126A7BF666CBDAB15F16A3AFB6E096FEABF8FEC7F96586F58",
-    "ciphertext": "EFE54E8B8D1D4929365420D4F1B00BE2EDDEC12084AF888B4C25A349",
-    "metadata_key_hmac": "780651B63468124B784530D35C24936A6E59EA5727F616BA10DF38D0C0640AD1"
-  },
-  {
-    "key_seed": "18810CEC96F7CF1DCF17F785FCF6E4B851C259624F99887BBACF34991B0E4029",
-    "ldt_key": "DDB1213996D3E8DE633F438EF570A9CB023DA04333371F34AF35754A43C5E511FF3D018D09F7054A0BDD6571C654A60768FABBCB09FA6A01BE98DA735D74D560",
-    "hmac_key": "6DE18531295E609DC755FA12D98B44AE58B90301EDDEAFB4DEC8A3C5AF306F35",
-    "adv_salt": "4F87",
-    "plaintext": "7465CCC4D2D461F0589C54D41243657C61D880",
-    "ciphertext": "7E40DE8C7319BF5895234E15EC0E8D6FF97DB1",
-    "metadata_key_hmac": "5ABB0A9776B5706C1D60B2DEEF4FB311E1F7D4BCCDFAAA5FA7FE56963D7857CC"
-  },
-  {
-    "key_seed": "48A447100312BF866C96BEBCD04AC87BB6988C4BDB7F8DB12AA05BA4CD980152",
-    "ldt_key": "2048C23710EFF15C8826D2815933822ADD0EA25AEEAA3862FA2D491425B02A7CA64BE39B8BD0DD91547E8C4BD8094DF1DAB8D8F017FC5BFD548C661A648283FC",
-    "hmac_key": "572A5B8B8B3FF304F9FA05B67597043925D6EC4E74A00A12CEDE2ED9D1DF79FD",
-    "adv_salt": "6F55",
-    "plaintext": "7C37B54E81BB800BD98BEC561505036D43",
-    "ciphertext": "D7E3EDA6A499FB6B809C94CE9F36E031DA",
-    "metadata_key_hmac": "C2C54F1B79BC288D97E7E4ADB83091ABC7D3B400F2CB9B1ABE08650484EACC44"
-  },
-  {
-    "key_seed": "6011B4A7C0618AB3D4E89DE71FDF5B6A9B0701144C954227BA61C2D0998829A5",
-    "ldt_key": "6266D2FB2B8CBE45701D1E170E12111E6FA07CD31B6D833104263122D9938FAD1044B8F727111DF64E1F82C79C097D544924EF61DB1683B9201DCDF0B5BCA8CE",
-    "hmac_key": "12E40307418404F91E740F274F79180DEFE22B6C42EA1D14DF6BB94BF142F85B",
-    "adv_salt": "095A",
-    "plaintext": "73373AD60B09116818EAA23B76315CA202A49D1A34518ED3A1685711",
-    "ciphertext": "B426BC7BA1E9554E07FAFBBACF6AA73D9D1D469BFF5DE009B7C8124E",
-    "metadata_key_hmac": "8E1E55E57883CEB2736DBACE6C04DEF4D280F4B9A8F69DC52504A55A8E381B5E"
-  },
-  {
-    "key_seed": "B5BBAD9EFC6E2BD7A0C45C35042688F482CB873928C18AEB5D942565B18E759B",
-    "ldt_key": "80A136841226F9BF8BC1ECD3622189EF736067D622337F8A42C6911F85F99DB5A2E6D9850763F7301BE6CBE4C19AA7BB47CBE228E09877C97AD975906B7EB109",
-    "hmac_key": "4B92067CCDC4E3061C490DFD54BD67DAC1C0D51B397A55D22663E3ED1DEEB88A",
-    "adv_salt": "4099",
-    "plaintext": "A6095B50EA5DDCB5CC28B33F418140A81BE5CD072C914A3DE2F415272478E8",
-    "ciphertext": "EC0C166253B01EC19BE4D9C0B74B182BF7653D3611014BEAAFF77BEFEDC6C2",
-    "metadata_key_hmac": "9F4338DE4B1D6EE486F6D11EEFD38836C24FB5CD94F94278DAA469B57786C303"
-  },
-  {
-    "key_seed": "136BBB96D2AF5550C57ADFB430735BEF3DC3ABA097D5140DA03D9303B1C5F033",
-    "ldt_key": "4DA15F9EE91E5B1E0ECDD1ABEE7A20DA93906CAF1D3265A4E2FA965AC038A7D52DE8865EF45135F22B1D5802A27775621EE6E9377D16DE803D6EF9A6201F5434",
-    "hmac_key": "43155F21AD97A0863DDECCBBCE8D4D7C97BA460C4BEA7825E889B9D3FE3A609D",
-    "adv_salt": "1022",
-    "plaintext": "C9934998AC1B1C055974618CA5BEF8BA67E31813F3CA8FECB6",
-    "ciphertext": "83BD9843A8944AF4FDD07F31B4BB591831B66B190B365199D4",
-    "metadata_key_hmac": "9B267AD54AFB8C9AD7DF31AB6DB159939660BE17FBAD1102EF760CC824906875"
-  },
-  {
-    "key_seed": "A72ADB129B57EB50466073803CFA5F228799BCCB56B51E1D1308ECFA978209D5",
-    "ldt_key": "F8DB3117A89F29C02378CD6C56D8754C2642AB68980BA6CB473052CD5885142CA951278EBA85672FE178DD056DBF8EC178E53C8265C568D2DFA86D5C0C9351B0",
-    "hmac_key": "332086A93EC05DBE56A0B368B9AA9D71EC5F893B8979CA274C3A8CF18D9CBCAB",
-    "adv_salt": "8B1D",
-    "plaintext": "2E6425A8E6C5C35EAC94C8956389BE6C428E6B71C2DF89",
-    "ciphertext": "28CA4987C93EAA81A0AD725FAEA4997050A59F1D6FCA9B",
-    "metadata_key_hmac": "6A344E851CD0D2636CAF0B63CF336F266C42B0D63A1BD9A19AB9B9D37D968350"
-  },
-  {
-    "key_seed": "E1A6C0886D409A10C782DAAC6AC3B092A20B9AB6848D9009833E053A3B32E2EA",
-    "ldt_key": "0B2DC44E38E77542766EE7DEF544505E362CA34AE2AC000CDFC631D1D5DCC0A20182936C8893B785A58B644C3F1827744209E747368DDFFFC0F4D6CB81CE7B76",
-    "hmac_key": "994D2DEE6B51B93F382BE2967D26D2883FA53D004B4EDCDA753340BF4816C1F7",
-    "adv_salt": "E9D0",
-    "plaintext": "AD5045A208D68370264DD1870E99FE894A",
-    "ciphertext": "C9B2470634D743A9C262D6B91DEF47FFB6",
-    "metadata_key_hmac": "5AD0D6BBAC361C553505ACE3AED2AFA72448755E07D5534513F0ACD5AC1D777C"
-  },
-  {
-    "key_seed": "B4CB7A1612DC27664F812D5583A84514B522D8F4C8A67F315D0FB738B14C4840",
-    "ldt_key": "1B8DE912E11FFEA471EFE665C8F543A45AC6F94D0EC3C21FC562231AE116E6AF05D0AA66EB3AB02D2137F93CC62C6AAB88946E7FAF1D8B4CD7EF19B45A24ADC9",
-    "hmac_key": "D04F4BE422D7138EC4C1BB479CEB2D7B5F0CEE87A7ACD675B2C96F00FFC33C2F",
-    "adv_salt": "BD20",
-    "plaintext": "0528D4433F11A53C94DBC6590395F05881E7",
-    "ciphertext": "A93D5E5DD8873410CA0A828AD818C3211B77",
-    "metadata_key_hmac": "0B4F88AB71A780B90A54904D868C33F33B3B7FAED0B8C3806E051D0169074C27"
-  },
-  {
-    "key_seed": "4437C6B000E28D91067704EDF087FBDDBA235DC00DE2815469E0E6899CD04BBE",
-    "ldt_key": "61FB5F999BA7FF93FA2A9799C888660F9324661DE1DD20A732D467348E5CEB451D222C6210968FD139BBB1E803AA91D315D1B2087BB2ED867E116C74BC9DAC90",
-    "hmac_key": "83CCF0E05425F30EBE6D9FB2600B4685630BBC05FEC12E72F79388EE4BD063DF",
-    "adv_salt": "6317",
-    "plaintext": "8D2461F68BF4EEA399297E97A23BDD000B990D42C68862E38F0215",
-    "ciphertext": "F062CB1F7552C6E703C5407B934E74BA1B4D365F7068E66C1EF4EF",
-    "metadata_key_hmac": "A3FB697D608EE59E62E21100212139351814D09A3BD6E173B2EB15D5B619BE14"
-  },
-  {
-    "key_seed": "3500228F5C517580C4B666FCF0012A701EAC7CA13F0DD3B52DD9CF825B3E6AA5",
-    "ldt_key": "FC214F58E66050E9F735C368F791D36D5B88C81C8E5E1F39BD90E6F60A2C97C939368B80EF5A3D5AA4EEFF271A98D5FB9B4CB35632EF9B40138480E846D0F000",
-    "hmac_key": "EAABBA44E248C573DAA041BA3E3A8B55800274BC7EC0F0D562F65AF46713F491",
-    "adv_salt": "998F",
-    "plaintext": "1F63EFD8CA6C056B2DEFC12F3D7122754ECC8E1E",
-    "ciphertext": "2EA554485BD49D859EEB73891E3971158C8700BB",
-    "metadata_key_hmac": "6ED873A30DCCD7C7CEA004F7502B7F45930DCD0E214666B71EA0FA218F201530"
-  },
-  {
-    "key_seed": "7CA62F11ED9EF8B92887FEBE67A18EA5DA89967A8F9F62CA53ABE599D7C6DDFC",
-    "ldt_key": "5FFD5F9530F6D75B096DC0BE00E639366C85CC8B67713C4B3820A5D4F50D46CBC8FE135F2FEBB00FB25513E8EB8799C57C2950AC302562545D2B44C3B688B56F",
-    "hmac_key": "47851C20EB7C6FBC5FD08C71967B08C287297B8A45E184A317B9B1832CC34707",
-    "adv_salt": "DB0A",
-    "plaintext": "B67A0D7E898411E0C116F810579DF4AFE2DE4A4C20E039",
-    "ciphertext": "317FCCA9F5A713E8F541C7180385B19DDCDAE9F78E21B1",
-    "metadata_key_hmac": "78D9F4E9A50F3D2E7A24E93FC54668AB1FDC1EC23E41AC3D4CBF724758DB4B0E"
-  },
-  {
-    "key_seed": "BF598A4A835B0A7E9A17222757A2E8F52986AED0789886D10A05ADBD27959A38",
-    "ldt_key": "A22ADA1E227D9EFF9393C31B800B907E3910DEE1DD72D3EC39B1EC4844745D6AB4828A02F2762B2ADC33AF7EF94BEC8F0C9655536359980E342783CA31647FB5",
-    "hmac_key": "5D336540AD62E8151684EBAFCE140BDAF1ED1A7F84BCE7A866E413E13F5B9EF5",
-    "adv_salt": "452F",
-    "plaintext": "00BCECA59C0DFE0079E540ADCAECE3B7317D6A",
-    "ciphertext": "68CA2F327B8A61372376B265B92676C8213334",
-    "metadata_key_hmac": "843F297E787BC13E9528F4FF27E8E02D3BD1DBB7672681B1A0E563FD652F1470"
-  },
-  {
-    "key_seed": "D9E312575EF085C178C53FC0FC07C8A36DDEB8379B8B7FE8DCA2FE4A4FE66459",
-    "ldt_key": "3C4C2F908DC0B34CC58D49F032F1D797EDAEC61E3BED6FA795952429A304285B1BA56B1EA4ABD562930B360574B2345C4A49E5665DE5B73704BB83949111EF65",
-    "hmac_key": "BEA649F9A160DCDA5E8DAF03D0920E7C7D49EE19235C77745A1AC007274B7F62",
-    "adv_salt": "6C92",
-    "plaintext": "A0FED2ABC74D08D3F5F67E342438BA85F826D6F27A9D1DBA",
-    "ciphertext": "45C95EE58DCA8BE9D404422B47CD050CBAA44B6D6F4B2D61",
-    "metadata_key_hmac": "76630985E02B91012AF9DF1297DBB2E57B251F098656874E2799E62DD49CE5CE"
-  },
-  {
-    "key_seed": "0977BE1D83362E1C9E9C19085BBB1B9D8B9B238B9D725A534509C8B141F5D952",
-    "ldt_key": "CBB12180142008ACA58F8DB2988C0808B26500035EC26721681AEEA2A8231D00E3BA443204921E9836CB1325A8C872518FAD3E950F0C634B7A8B57BFFF280C8A",
-    "hmac_key": "925FFDE1A489C7602FCB4E0143099CF854E1164561BEFC5E267398F20168D586",
-    "adv_salt": "9205",
-    "plaintext": "DDD43ECCB002CD1A6C8472484D0D8256B3237C01",
-    "ciphertext": "D06F11B545EFB472BED34A7F95C57CBDA8BA8834",
-    "metadata_key_hmac": "9DFF4D616CE50665B79B1F921624A97DB55C8DBC40E0378E060A60E55A9F46AB"
-  },
-  {
-    "key_seed": "1C4F80B40B6F1322CE743AAC976B7DF1081A512E8648DE8584263A45713BB4C3",
-    "ldt_key": "50AC714979125985143D95393F55AE570F680BAACBCFABEB064190673D78A46068729076E60AA03485DB1F5134C7A466EAFE8B31C10316757D65991893EC5693",
-    "hmac_key": "7013AA48E9A3574810E1D53B78495D37BC41443B95C38D2A22124325511D46DB",
-    "adv_salt": "3424",
-    "plaintext": "5E047CBF75F0492EC057B9222D56AF8B0228908BFE857EA349BBF3",
-    "ciphertext": "27177F6AECCD598EA2F1028E037EC82C7FC172D94640288D9FFDA0",
-    "metadata_key_hmac": "40F70383E050A746CF70B8C045E87B8CBFCD61F3A0578C2D6E54118B24C77D52"
-  },
-  {
-    "key_seed": "53487B4FE763937DB3B5676D3099A65B47E8C7769760F9DE17CCFAE477688D9B",
-    "ldt_key": "4125C4AED983484DCA9BBAD115F9726DE8704F60AD2605813734DFFDBFC04B5C3F175796E6BF869D4F0C1B71DFFD8767B855631ABD951FFB5F682146AA110E9A",
-    "hmac_key": "5504B15B0D6A5EB2BDCE2379CFAF8B0A79F393C3D706B02E2DB0B8FEE5ED5630",
-    "adv_salt": "5985",
-    "plaintext": "C7AA4F926925E7C32B0097852B43BBDC6EE657E665B5",
-    "ciphertext": "00B93FF52C9B252B26292837735FF51F08F5AAD71D87",
-    "metadata_key_hmac": "33ED2236B3FD60FFCD79D0D0FD2BD7615522B5727AD01BFB4482E2E9C071F95F"
-  },
-  {
-    "key_seed": "96CE52083A5A57B3EE83B2CFF216EC74B4D543CF348531B5D2AF0F22C5337938",
-    "ldt_key": "8C8A19C50EF15ED3F45E45B901A26B1EF2CA2F735B64EE56590561FBA6AAA34E3A01BE98BF21D0BB7F8C15B0CD0FAF673B9881EE46EA3BDD96CC179F3FBB75C5",
-    "hmac_key": "4E13FE8104E508E90C40B5E6798F8D2C7C66BAE2FB7D72172CB28C671ED91E71",
-    "adv_salt": "C9FC",
-    "plaintext": "9C5AA3363518A76FCA6F78A9A04ACA721C7686A5129E",
-    "ciphertext": "3A4720BCEAA132E763130979C2B667895D9A7144A43C",
-    "metadata_key_hmac": "E85B555DE907FBEE223ED40EB3ABB951B622348723F9C4AF1B857AF652518E43"
-  },
-  {
-    "key_seed": "09914153EB8FD12E76E4C688D472FB3AFECE9092A5E569E95EDBA7C9CB060CDE",
-    "ldt_key": "DFD7A41FCD2E25C3CA25D5705B43AEFA82AED9C012F8B9A8A2D3CF4124420C79CC6F307A05B64A6DCE03D229DBF5B6F04751B117140F8258FE02605D5E16CB3A",
-    "hmac_key": "89B1507C021A21B16515F6783CBD811D194466B91D2204A80A93681B5902A92B",
-    "adv_salt": "49FC",
-    "plaintext": "367C77FF79ACE756BD9D6AAF719DCEFF2FF487E94D0660",
-    "ciphertext": "C9A57EE439E3E15FE5104DE3FFFB188C5678EC18B88F80",
-    "metadata_key_hmac": "D47827E3405E97253B2FFBF73DB2A4D974BE5D1AA2D4692794056A6D1EE409F9"
-  },
-  {
-    "key_seed": "FE6B24AF4BD63BB961BE9E2028E915EC18B74F09A787A1D2EC09C8500D35A2D9",
-    "ldt_key": "AB86D686F522FAB3834EE921898D1BADE9E1B301C75623C1E30A12A7E50091224F0010AEBD1801FE111E48D8F99FC3C44C0268277F5B0CEB55CC5A83231FCB50",
-    "hmac_key": "C2BCC8076C7C57E59313E29393D54786663FD545D695CB369A6F91CCCEFA49D1",
-    "adv_salt": "BECE",
-    "plaintext": "E9D0C502DD9A6A82040EAD81BC9DC8A5",
-    "ciphertext": "8EA434C4F4DB5EB8BC863299D85198A1",
-    "metadata_key_hmac": "DC52B449E596BE9897D7676C5964CF0E6FC5DEA7E95C5FC08731AAA26EAB9F79"
-  },
-  {
-    "key_seed": "D1ACBFF135447C0C8CF94717523538467947B56195529569A4F8A169B36E6AD8",
-    "ldt_key": "4E8B09B73126C3107B2B66B8785B6C71BBB0FC29C606B3F641E439C5EB58C0EBF356024097B3F31288124A757D663EAA21A677272C50CDF8D7D64CB5F05DEF45",
-    "hmac_key": "0831A40375B7C1612A606FA8EBCF8125BC6E19B50EC94F64985397FDCDBB8BD6",
-    "adv_salt": "F5B7",
-    "plaintext": "27D5412177348E37C3B1C89CA3AE4EB4",
-    "ciphertext": "8FF6C2131447193EE02CD925E1048538",
-    "metadata_key_hmac": "EA25CE77A100FF07AE5D3E13AECA7E601A8050C91077C7519941A45CAA215200"
-  },
-  {
-    "key_seed": "8DDE63E8193F2C54E07E30D63EA79005B64C4310A31502A0C6EF724A7D005EFC",
-    "ldt_key": "6D8BCBD0229FAB2007EF3E0347CD8414FD59D62CE0673575390CA674AD4F243A9B918F34F892AA7A1CF352C7503EACC9AC2ED2330108D12525104C73A65423BC",
-    "hmac_key": "04B016619CD33C8D5C645E581CA9E8E2F3BCC9934401B446D7515099464D0D1A",
-    "adv_salt": "2455",
-    "plaintext": "B86C750825BB8631F571FD09AFF714832BE98F6FEBE9BF25C43E3D4B3F",
-    "ciphertext": "A83995D9FA47C31EEE6945ED27CBC122D4A50A7291EC9CA8C6CA6AE666",
-    "metadata_key_hmac": "5887DAFC1430FFEBA2A4540A36111CE6948EE1B4AB796E668949E8B67B27CED7"
-  },
-  {
-    "key_seed": "B70DECFD384BD1CA6DD39DD7D6569A17C00AE084BBAC82A2497146D8E094B84E",
-    "ldt_key": "E45A75100B04409B36070A5ECB2A0F11BF1216FFE8803802C25876FAFAF8C68A980AE757C6B070FF9E300B6B14E86A727412BBBFE6C421E4F3E8FA6763C4F7D9",
-    "hmac_key": "6C853B742EEA1EE539CBF4290111711B8233A022C246C3D1E3026461A2DFB0CE",
-    "adv_salt": "03D9",
-    "plaintext": "FDC02E629DF6CABED313D140211D94C0D89D0F287CA7E0054DA2C8",
-    "ciphertext": "9F95432869D3DDF9A1A1F7FF297401313C9F25E2247C1DCBC64E75",
-    "metadata_key_hmac": "D0A899C00E9F2777FDC3E1BA7CD9A8E674F1263D34CC3A017E8C2B1FD011E425"
-  },
-  {
-    "key_seed": "6F2C0C5D3755C70E2321BB72BAE671C57A32B66BB6F1EC65EFE4EE20B12C8449",
-    "ldt_key": "A8A0DDA015D08740BCE112EFC189DF6DA001327052636304B1ABCB14F1C93D54D7C1A6FC5CA4D0746D19816F7E563F7F0B7B03ACC92FE1557F98C4AF6565C673",
-    "hmac_key": "9AC72A8585BB66338301DEDA6DE9631BA1974BB8FD92F11B4164081DD5C9AEBD",
-    "adv_salt": "76EE",
-    "plaintext": "C04B935D3CCE7731E3770E51173FCA86650B3AF95276",
-    "ciphertext": "EC2B82DC913347C43BF8CED67FF6CA288751E853E7E5",
-    "metadata_key_hmac": "9394740B566FADE591DAF32401485D8669E9890CFF76F6F2CB96606353986BFB"
-  },
-  {
-    "key_seed": "6EDDE96FCDE49C9F543C58453CBCE54014A53DF604C013AC8B8E56DC416308EB",
-    "ldt_key": "364C570E73FF40F476373829A6E9290F58C1930104524DA9C248B2FB37277770693FD3A5C234C87DEA87EF4EE972BC3F024F3654079D4CAC9ABA633668C3C707",
-    "hmac_key": "59A3A61F03ED79AF2BBA9B1EEC46CFB8126744B5CB92847E26635B9B21DDEBB8",
-    "adv_salt": "FFA5",
-    "plaintext": "B4E6EDDA091C2BC88DA3037ACAD65FD4F2AD",
-    "ciphertext": "819533A059FEECB490CD1334F9CDAC30B716",
-    "metadata_key_hmac": "A6BCAE247D01F70F76C69E7AD0C39DD108D5CE8A5CD1D069B0A2D5ECA33D9E96"
-  },
-  {
-    "key_seed": "2FB448C0E6F94054D90E6923760D11AAFCF58A340F8353A48B9B95055E9B407E",
-    "ldt_key": "575B5B0E20F5FC215D445A8555A4D10B2F347021F52D373B8001FF3323A0395100D0C25D3F1736D80103495321C0F6DA15C2AC9AE2563C6D701CD81B09C6CF57",
-    "hmac_key": "209B89A340DED92EEC8643D979B1D5B5F9AE65DFB65B3DF414B10F1AF8AFC4B3",
-    "adv_salt": "6437",
-    "plaintext": "6BCBE18F9DB6D133FE1C7B21D955C464",
-    "ciphertext": "18783A571581F8492AA852E5185ED1BD",
-    "metadata_key_hmac": "A6E52A9BF231AB8F04EC4949CA45C116896D36810843D1EA630795360A4546D6"
-  },
-  {
-    "key_seed": "3641F0DC64B45C0D2485367984CCEF2CF174ABE0E6D0BB8666518D4BE803FAB0",
-    "ldt_key": "42EFCC4880378598D34A51398759AFD9B0928E165C3AC53DCFC93792E5ED2C9DBE6ECA49A24B0D7CBAAB623D294DD082936FCF02F4044023FD2C826D1A41BBB3",
-    "hmac_key": "4A5697C22CDB879341AC594927CEFAF420172C4C3F29838ABF14280F707C6392",
-    "adv_salt": "5AC3",
-    "plaintext": "235EC0B6D300EC9CEBBD98755E8CC323",
-    "ciphertext": "03B020336DF0BCDB2511CE9AAEAFE8C8",
-    "metadata_key_hmac": "583FF8AE8062764675C513674CE42794CC2D3EE086E51BABFC565BE9F3BBA133"
-  },
-  {
-    "key_seed": "685BECA6B5014A5DBB070913A8D614BFEB6525BF352EFD3A7BDE7D0ADF0EDC50",
-    "ldt_key": "6388A77AEC7CBCA7FCA99E7157583F4B7E6BB600E257DDFF4092D909F28C5BD70F93E68F707DBD20EBBF881D8A2EA46F4F2A1DC9492115554C1D5573A4A60081",
-    "hmac_key": "CD361086080117E9E28D669FABEB67F08A1BE234DD04611F62E84D8CE2F6C6D9",
-    "adv_salt": "DE31",
-    "plaintext": "2D07DFE1165CEAC2EA713707F6C36C3C6C6546A79BA91ADC05555CB2",
-    "ciphertext": "3F327602B2C3CBE5F8642975AB5A0784D2CDB2B8BF1C27BF711BE5E9",
-    "metadata_key_hmac": "9BE6EDC04FCE101B576A36F8F4AC314167EFE50ABC237AA0F9C9ECF01C3027CF"
-  },
-  {
-    "key_seed": "0019B7D1FB73E265ACAFBB59FF225BA9C94108CBDEFAB4E0289E3F1DC8FCEFDD",
-    "ldt_key": "3F1F214F47956344279BAA9F7C65A59641B27B2E4590D61B5E94A7280A8AEB6842EB5544B0A2A4F53EEDFFEC35EC0C901C77E8E54787D8958542D0A718809DF2",
-    "hmac_key": "13D9A2C2C917058A252851D47ABCCB004A5F8FF4B23CAA2AE98859955AA3207F",
-    "adv_salt": "678F",
-    "plaintext": "03FC57DDE4A9650C2E5A7295AC8161C416FF51E674DEDC4B3DD281C0DC6A",
-    "ciphertext": "B81F9283A26F50E301B8F7E9D5D913DB1AC3880CADF9E0ED6921BF4B68C5",
-    "metadata_key_hmac": "0EA5A3D7BFDCAB5340C756D2BD6ACC70A3735C090C58E7699AB37B7FB4C58DDE"
-  },
-  {
-    "key_seed": "488D217A78E794D2564F491ACBA28B0ED034A6541906BD375728E914D95D8AFF",
-    "ldt_key": "BE46614BE993DE7D226411658345B391019BBEB2D9A0DD0935FD34B0755F330BCC1D0CD1BD61FE81AC111333398BFDDB29B7D6ED2743CEC687F0A6A583FDE36F",
-    "hmac_key": "67C099001CB579A20688FB102A37B73F0465FB60BEB6EEB775ACC00804993065",
-    "adv_salt": "E73B",
-    "plaintext": "A4F4CBB2D2B5B8A19CC04723A6C7703F829D7842C57CB239DCFA0D5D",
-    "ciphertext": "9B72548B9E48FFF51F438F8B10806555E15F25CBD8663B24A34A06C8",
-    "metadata_key_hmac": "F7679F19AE53AC432FB8E869B7CA73B716A8FC5B541A0AE2B8372533F96457CA"
-  },
-  {
-    "key_seed": "485D8F445B7B2C5E745B9B609CC0D244431579C7970060DC19CE8EE7A7426BED",
-    "ldt_key": "F075E4B12A85ED273AB7172B3589F86E6DDD5748AEDCE7C3EB4E51C5471669CEE271FE0DB894C93AAAA3B4C0DD724B71CCF4FA2B71F2BB74B1584E936C023732",
-    "hmac_key": "56232640F0F1DF1D3A016DD36FCBCB653078657118BA89CDD642D0AC167E53D1",
-    "adv_salt": "DACC",
-    "plaintext": "8D23CFDE6FACD0CB652539D88F7FC4E2C423DC48C4941C7B0EADBF3926",
-    "ciphertext": "508E83A2E42DA37DBE1691DBCFBC8A6BCC82C89EDE451EB742F0E39444",
-    "metadata_key_hmac": "ABD51FD47ED471A1C3E1330AE968D50114829FE177608AEBEEB32285C122E614"
-  },
-  {
-    "key_seed": "716163538E5A30BC0B729AAAC08414840FA0670026770932E959D024C0E5D5F4",
-    "ldt_key": "06BE7195DA1F38BE149AAB2EAC68B0D696B4AF445018D24BA5E6760D057D8A9055A594F561752F6155A06697865005C0C75DC7B4B0BF58D457E6540270302426",
-    "hmac_key": "5A5D09412CFDAD3A5D45E5380A523F294657770770A44BBC35CFFF414B46E39A",
-    "adv_salt": "0FEF",
-    "plaintext": "535A76826D569EB754425239A6B06442FA6F2CABC86E9D75",
-    "ciphertext": "51A5426141361858F37284EB7063B9DC72548CFDC08746BE",
-    "metadata_key_hmac": "6DD2E45094D6B3EBAD158AB704650959A1B540AA3487E3A1D502B29F3C3CAC18"
-  },
-  {
-    "key_seed": "33BB71AACDF4DAC6C324C119930DC5F2998CCACC71F6FBFB0D01B599718A5379",
-    "ldt_key": "96D9303DFB67749BF813230BCAA0A5D47155774C4B60676AE25BEABE0A0956B02F236066D8A31B0A6A76A50030A239A1F9A9BEEABBC1AD88BFF752F8E3479009",
-    "hmac_key": "9E6F8014FCDD1907BAF7904F0A201F47C1BDF4BDDBD1B955418E1018427E02C3",
-    "adv_salt": "6BB1",
-    "plaintext": "47AAA95EEEAD354B2B66430652A98841E98BB3C219A48043",
-    "ciphertext": "9EFF3A52E1B8C1E6A8F1C8BAFF20D44A5ABCA86138BE92A3",
-    "metadata_key_hmac": "DA5702D6A807202BB8E07E947DA8B0193F2457F5D9461D02EB27206B7199F47C"
-  },
-  {
-    "key_seed": "232013418F8B9031EE4F9751505640DD4CB234EFCC785DE93F52CD70BA444162",
-    "ldt_key": "788D8B5EDAD8B6C5D44B7F1B28B2A06DE2D5759FB27D7A7648C53DB87B578459BE0265BD6F6A32AB277555D9702EEEE2BC49C5F6042C45BD212598A8897792D5",
-    "hmac_key": "BFE3BE2425415CA34F28827BC365DCE435417B7CF7DF7F1E803288125EE094A0",
-    "adv_salt": "F31E",
-    "plaintext": "E07EFEB6550045244D9827CBFB89891C0FD08029B56B",
-    "ciphertext": "320357CFFE46D4547F4853066F795596C2082091FD43",
-    "metadata_key_hmac": "A5B0D5F6C746A49C9C07ED720E2C701054971EB221516553D4D5856880C59E50"
-  },
-  {
-    "key_seed": "23DFC8F778AD70A4C0C884AC825DCDAC443C1E579813E4603B38A60792411439",
-    "ldt_key": "1FF1F7C52647855CAB71E7863BA7CFF87892279FB3565A434BCFBAE75974987744A4FBCC0452E61DAF3D101BA4642027A31779215721470FBFD42431B4C1C6F8",
-    "hmac_key": "7188060CF40F2AE1C6C1299490C40724B1A457A8D35C092B724F65BCEDEE9D95",
-    "adv_salt": "5542",
-    "plaintext": "1C6D4D247AFF8238480811001C5AA4B0067AB30C389046",
-    "ciphertext": "757BEB6256874C7B2A98B3DA67F55B86C80E0136F1E220",
-    "metadata_key_hmac": "A6A0FFBBD2F80EF58F9EBAF9ADCEE83427A69B2F05997B5BC072121F036C932D"
-  },
-  {
-    "key_seed": "875DF83728064FE79641949D18C2C61C9D34C13F3FB7AB8F239419058E9F9374",
-    "ldt_key": "684E1C82DBCDE7A034C4C5823AB0AF4F2FA9ED2F9A43065E5EB7EC17BD51FD1044FBB1B1C3FF920BEC1952F395C5061DD5AB5AE3F99A8998E69BC45827E61028",
-    "hmac_key": "8A50A84361709FF3BEC15A32F90385C70AA220F3B8809EB0AF2191DE941F36C9",
-    "adv_salt": "7216",
-    "plaintext": "5B2893F2D895E7BE7129100F96032E7CBB9D1161F2888F7A376EECEF4912",
-    "ciphertext": "639A0589557F3B11A52ADB7407EFBD77BE8250EA6DDB7E333B0E164609E3",
-    "metadata_key_hmac": "18CB527EC194B70A260DE822B875EC91EF25024A31324E43315F3A3541991898"
-  },
-  {
-    "key_seed": "65219AE898F2F96E915A029F7E6710F4CDB346EC316C12219081351FEF7A8281",
-    "ldt_key": "B315231A39CDE17C33755E1FEC5B1DBA05507E4F7D11B90F4EE47767B59ECCB57888FCAB4AF8FF8328320A023A18ACAA516C88786E0ED28B6C61262B4918AADF",
-    "hmac_key": "B433027A9F895373BBC791DDC0B8B497D1BF04837A65998C0EC10436B52044AE",
-    "adv_salt": "C0F6",
-    "plaintext": "CF1955300119D84EA6F973DBAD44A200B174A9BF3A5A2149D1",
-    "ciphertext": "597DF4D1EE2A6E7EFDDDCBC3E0C7AFC6F025F53D8590C59169",
-    "metadata_key_hmac": "FF981CECE4C1478EE589AEB939CED3F38E2F29D34F7D09F62B492B7C9EC45204"
-  },
-  {
-    "key_seed": "BAE9126590BE96CFF06E1544A2E8F48CF6B4A5B11CCEE12E2444A2C6BF741C85",
-    "ldt_key": "BDA2F3EB007F968A0849168D8112F6B3E792136501024DD838AB308A746D79FB5EB543B5EC9E3A2DD820E198885B847564B513BCE03EBE51C3FD4737E1B63D3D",
-    "hmac_key": "E878372A53B7BE8D20FD7E103A2643825772595FF0B8C5AC978FD5367B4DE35B",
-    "adv_salt": "0390",
-    "plaintext": "28FEEBA8F984FF274C540B0C5D2F2D68443DF3DEBE43FE5B",
-    "ciphertext": "6832BF61CDBDDB038AA95005792E058BBD2DA9C2E8CC64D5",
-    "metadata_key_hmac": "EAE978A7DABD6320E3FC862AA71C9D6DE9628FBF3881EB33C78235F5E4C2A34C"
-  },
-  {
-    "key_seed": "109C2F5F20AD4F553C85F83A182A9D62851C95E69491C53B557A5BC6D90AF8D2",
-    "ldt_key": "8A3AF1C3855297D53CA1E6703A3F7CD5CB15307008A6DEB41FF717923571A75B678EBC74F0F6DEA14841A1B2B5BDE921FB893BDF6B3B1389AB4348B9B077FE9B",
-    "hmac_key": "1EFA63683DE03AD5064A6EE69A703258DDD915266782ADA713B40F621E97A689",
-    "adv_salt": "BDC9",
-    "plaintext": "9AD3F734157C17F1C829CBBB7B430190FD9B180BABB4E195CDCB0BB3E320B2",
-    "ciphertext": "DF537630E902866F6A2073E52064B2209BE1222419F11F8E7C477B0C290D25",
-    "metadata_key_hmac": "587C7ACBBC8EF7CCE44AA449610D88F203A5E2FA1B5CE9457F89238C7CE1F2BD"
-  },
-  {
-    "key_seed": "0BBC490848590D4E07973036F913280A7A34E55EAF14B878C07B80E9D484AD3D",
-    "ldt_key": "E6BA55F41BE5D38F1570F4561602E02B225F99D122B433B4C90646AC2017CE07C8864FE1B36336E2E40B36FDD32F2D3F21E84BE10878200E352E326F6906FA93",
-    "hmac_key": "11D10505FC70D15013A546E32C6EC475DD5F8E9C6BF1AA9DA1BBFF20A703C039",
-    "adv_salt": "7D9C",
-    "plaintext": "1D83A3FF4BE2AE5BB09598EC28E77D63E73EB465DF28A4813D",
-    "ciphertext": "B7D30ED112ABE6435B08D936BB7159AE441B6B9367CBC2FE33",
-    "metadata_key_hmac": "CE7B627C481737ECC02486BC6C128609C433EF4E73F7CE150B2BF80437353A47"
-  },
-  {
-    "key_seed": "CD96D439FE372D567D9F0EFCA420D0A2B32118BD30A8079A9F3A62190CC1BA0E",
-    "ldt_key": "9DD30C9B7FD8A11D9278BD1E6EEBA4A0FFB78B3421C38BE0F10FDF064FB498B66D1DD6EE7193B2E3B05E96D9AAD6BE3BB089F29155A740B26A9F73852CE7965E",
-    "hmac_key": "A56F9DA0A354559145B13427F306B6E2E6EC8393F158838226EF24149B4B015B",
-    "adv_salt": "5132",
-    "plaintext": "F00F56084310A93D97046C66F0ED55342C1440",
-    "ciphertext": "3A53A5FEAFA24F76BD376E44E55863E97C707F",
-    "metadata_key_hmac": "91F37258D9F2B052E80EA96DFF9257D36D8D6E59E0F0F42C86070C132741E1E6"
-  },
-  {
-    "key_seed": "6AC0B7FA31FE70DE036B27ABB6532F3E10DADAA08F0AB48DAB6FAD53A0F931E1",
-    "ldt_key": "129862E2C4DD74B83885F72AAE50AE59A15C92EAE9E709EA79BC9E1EC1A4DE0094F52B790F08296EA1451A440A96B3161A0AECFF0356AD2C5C0C7EAB3D079131",
-    "hmac_key": "2A425FE28BA08A90BDDAE9530D99B1EA68EB50498FA5CB4CD1F66F7EF96305BB",
-    "adv_salt": "B882",
-    "plaintext": "936CB92494022DE43070F6DAF16E1D7DA2E3CA",
-    "ciphertext": "2DA153F11E24D634FBB51FA745836A59F94F04",
-    "metadata_key_hmac": "3A9B7D8F90BA011B74F7B8040C00DAAB738BB6FCD37DE15F3DCDBBF1E63FFBA9"
-  },
-  {
-    "key_seed": "09F39A3914C684F46C5F06578671766E978C50FD36685B81E3D642A656AF40BA",
-    "ldt_key": "0DEFC9F52C67EFCFFA6FE18E769807594A4E42855E7DC5F753A389BDCABD97E73651D4D5696CB353950303C5F82AFBB05BC2CEE2C2F70A85D694D14863086D23",
-    "hmac_key": "D3534CCC6D4C50C7D368F917DAF9962A12B997D4C3D06C1DF663D4B79C36F322",
-    "adv_salt": "5A17",
-    "plaintext": "5A662BE3A033831FE0B8956E1A762A8DD614",
-    "ciphertext": "061BB8DEC19F63C11FE26D5632C76A5A5DEF",
-    "metadata_key_hmac": "D3957BD4E2CBE0846BDCAD8ADBE67FC48CD21C6FECE10FBF84C90D0CD9114BC6"
-  },
-  {
-    "key_seed": "08CC65E59BE9634B5936A72589A61C008BF420A274F34430C50A1B838BC01CEA",
-    "ldt_key": "F92F338C61FF4F9F0CB3A1DA8C106A95BD361B0224D960175A6D09E0EDBB21F3AD8CE85169226E1ACFD7FEC5D7BA3940806725CFBB3C1050A5A0F38A90E9AEF7",
-    "hmac_key": "CD1C3B1E2F824475577CD4D15877A2A76EBD0F837EBD6BB9BCDA6A704CB5AD1E",
-    "adv_salt": "5970",
-    "plaintext": "A5E1068684761A0D70271BD1FE3873661B932B",
-    "ciphertext": "E2C22F7FA98BF2401A70A034FDBD4562B8A4F3",
-    "metadata_key_hmac": "A69562623B2C229FD9244BCB74521FFF607BA4A9E4708EB43E4E5DE96AA62A46"
-  },
-  {
-    "key_seed": "5D05F0C3C565C04174FBAD35825C70EAAA93EDCF6863D63596A9BF34E52364AC",
-    "ldt_key": "F3FA9145A23FF47C252F6E000A2FAD8A238944C72935C095579A615855A11C1A8AC8ED5E0266DD24A2CF1741D9BAD88CFC40519D28B8DA06BFFBBB39D4D7C7E9",
-    "hmac_key": "85AE2EA76A1DC03FF6D159C41A156B4BA8976F74F1265CD6905F71FF9917F247",
-    "adv_salt": "0A50",
-    "plaintext": "0A129F5CDA6ACAEE1BC5641A71A4568ECB5CD5",
-    "ciphertext": "6B2BE584850E7D973B1F516A6859C00F455456",
-    "metadata_key_hmac": "C69F4FB11E701E21096BE0EEE182BC1743A4D863A0F37953C64439DA1C9D4AEA"
-  },
-  {
-    "key_seed": "17FC70D50F24295D49A988C29359E028B6BA3B75EFA77DB8A3B23FE2B03695AF",
-    "ldt_key": "E7558B69103897B40C4569145D74E28AE29A96AA79BB1E487A7160710D82373A5A0F1D8778DC1FCD240A5D5EA93242312FE7D752AFED80B3E70046BC960C9B2C",
-    "hmac_key": "C1A66841CFF728515AD0B8C9E77AF42264AF8BA0CD1A9C3362024CDA68B4C086",
-    "adv_salt": "11BB",
-    "plaintext": "6FB7AF0C98551542E36C8EBC5D4C25D476B91E0ABBB39227DE",
-    "ciphertext": "0E847F778E48B88FAE811C2AE2BB51246036B2B51CA4F3D0C8",
-    "metadata_key_hmac": "0DC922B4E275B3782046CEE1927E1C4A0D570AFBD9FDF46973D7653AB6505E27"
-  },
-  {
-    "key_seed": "83A488370765A337A4664233541ABAA97BDDDAE319D27B5C1E04E98DA84D4749",
-    "ldt_key": "D6ECAD695D630564E9B1B679365173F5B7FEC0E8A7F9C08810DE19C7EE3D69398C699912E1C4DD750D0A216685D6CF3E537444B929816943F768594294DD29A5",
-    "hmac_key": "3F5E333F6AF76CB35D472350352D6BA8657BBC96480CF7DFFD44F15815C9E622",
-    "adv_salt": "8FC3",
-    "plaintext": "3410E19366142F47480308BC477D2218",
-    "ciphertext": "7FA427047525AB940C045A6C0DF92866",
-    "metadata_key_hmac": "61E663BCD496891A9343771FDF3C4C563D47C7DC43CD11FC16C1306948669986"
-  },
-  {
-    "key_seed": "B72DD63FA9D8252BF9014F95D839A4456DE0365C02823AF867C95A39F77FCF55",
-    "ldt_key": "4D4643E0743CA07CF45F61C8497409B46623C063D2669A34096BB81CE15652EB79A94AAFF31641ADDBF6509C705F6FBBE935A6C3F6A9A7819969A1CAE8A9950B",
-    "hmac_key": "ED9C18B859184698BF0DC9873921BF728541E2A2FC6AC1557683AC7713276A61",
-    "adv_salt": "3A6B",
-    "plaintext": "C8DD3D2910AA498BEC6F06707D6CBA6C06B714929C69D663",
-    "ciphertext": "FB89683FF049F0BC29903547C231A54CED45AB35B9DEB5B7",
-    "metadata_key_hmac": "927BF5AA80EA9FA2A1873D9A6A656AC9BC75368520D88C458E64B7715C0614EE"
-  },
-  {
-    "key_seed": "950CCDBBC52B8907EA6A3826F08F5F7AE9B7460CE0796B2BAE9B1E3C27536E58",
-    "ldt_key": "D8AC57AE1F4F96C166B606DCC9D5D161F29DB96611EEF8AB10B6FF43718CD98CEBCA0F29BEE7BDF2CDEBB43630E0E9D40031290A7791055F8DE4FBC657251849",
-    "hmac_key": "923E93AE40F806A497C9D9AD1C047BACB1D96A10ACFFA45F90D2825B9DB81016",
-    "adv_salt": "EB6D",
-    "plaintext": "D6C6DFC0C7AE5CDCE58830B1FF4869FBE79DF2B61A63E700C3C0",
-    "ciphertext": "E92E58BDE7FF8C5817601FA3324F49FD981C8ABB3A3DB1223643",
-    "metadata_key_hmac": "6136016E27B91ECC645ED2A5EFF0E4F2C39464D56A462234EEC9A162F6004417"
-  },
-  {
-    "key_seed": "B83A0EE452CA424AF473112778F4CA91F5CC4F86A4D3DF69C48FF5D125F20747",
-    "ldt_key": "2D65D82EEDABB27501C1645ACBCF2BB0B4D1D48125B179A20444984C0D9449FA779F70E82FC8CA1AB1F83E0C7CC2774EE589A36FCE75BF907C601633942B87DA",
-    "hmac_key": "3F837B23AF92192D5F1AD978A1331224CEFA584F0B22B078DF43E8DB7CBEE483",
-    "adv_salt": "9621",
-    "plaintext": "2243131E829C96967395D38A43D04E0080",
-    "ciphertext": "A2CBC17881AB92B05FEDD78AF267056AAF",
-    "metadata_key_hmac": "8DF19D5C532747BF2E6144C422456F44802ACC9C701F5E40ACA30CE72DB674CF"
-  },
-  {
-    "key_seed": "087F657C20994BB468B4D1A0554DDAD7F62FFCD626112104BAA5EAFF2EB32A04",
-    "ldt_key": "71EF74B6B1485D7420F59D0FD9D9193138722159855D1750AD3E62467DC42FFA156A7E77FE1A7954FEEEFCD0A3872F37AEF600251FE5816553DBB0F978D14961",
-    "hmac_key": "7795AE62264FF7A8706FBB81C309C912FF248CB858BCAC99E1443BB7F1D60294",
-    "adv_salt": "67C7",
-    "plaintext": "5E2042B43DF34018929A46527B4D822B4AA4C60EC638",
-    "ciphertext": "A60C54419E962CE4C7AFC12047939E3FF219D2520D58",
-    "metadata_key_hmac": "67047E51091EB0927B2C98A442A95233B289AD364842547038EAAB94D7A1A55A"
-  },
-  {
-    "key_seed": "22740ED4FDE15B558864234FDB558B107BAD955DC9A7DB8552E3080CE3C27C95",
-    "ldt_key": "B9B8C45E60AF71A00F770DC18D9E9C5452D52EFE41E217AB51F6DAD6169D756FA58AD7D55447D16B5A72CA20F7BC584CC033C0B4DB162267DF17CAB217099F51",
-    "hmac_key": "9DAF080402575FC679A9683740FF68C2467DCABF70570A7C32D5BBC8F538F697",
-    "adv_salt": "FB59",
-    "plaintext": "D8127D2968378EF5C353ABED92710C430749C65834AAE01C",
-    "ciphertext": "8B834EF490189F091F2E92322E80686D0A9514D84D8D1A1D",
-    "metadata_key_hmac": "28DE764F1F61C810E2B99F5733CCBF683DD7264587C598CE3F586B619716C555"
-  },
-  {
-    "key_seed": "83B9B1B616864B6F5FDA22E1EDC9F8D21306B67F539C03284A17A6ABEDC6E3CA",
-    "ldt_key": "B34074F91686F78A6EB198B07695A81B48AAF40FC88CE86C7BAF0F224126C714EF4CC26285F582DA58C2B7B32A58E92ACC606178C7408AB5061DC9B31CFEBCDD",
-    "hmac_key": "1BDAA56D0E5CE06940CBC8EE9A3CCE1ED4C89C72E95ECD56B0941DC099BEDADC",
-    "adv_salt": "2349",
-    "plaintext": "7B18E0F8718B3F8C48E70709B2DA75011838E48A3D2D9252D7DF",
-    "ciphertext": "A25A5F69E9E0FA7F6FD44CB4D0A222A8E46869AECAC407391811",
-    "metadata_key_hmac": "D9825A3CB4DAC96669E1077E32262DD8172553438BF9661F68ADF497ECC35F8D"
-  },
-  {
-    "key_seed": "0C54D42972E04ED6DF6AD67D7335CF041801FEEB27D4C1C4295D27CC39F97E44",
-    "ldt_key": "5420CB503AD81D477B3809899E5A776766926D60C06DA894A2DA06A0E898E19294F78921BFE7115688D34D389DE790843EEC2E119E075768F434EEFEBCFEBCF9",
-    "hmac_key": "6346DCDC7DB968D413C1F25532E4C813428B858C835E8DA3FD3ACB56A120CB01",
-    "adv_salt": "701E",
-    "plaintext": "4183540891E4E06BA22C7900ED93E2367E1EFEAD7C6E56552422",
-    "ciphertext": "7F8E10450986178E2A3D7F0DC5F5B9A61B39062BD4AAF7756581",
-    "metadata_key_hmac": "877068F263B8A8C4D1840D11690D47DABF1115EEEA3DCC04ECF8D901C8C5663C"
-  },
-  {
-    "key_seed": "53DA12943D8D712ED58F30272C0DCFC5701CD65F0983430509338AC9DA9F3EDB",
-    "ldt_key": "C0902A392612ECFE565D8BACC7D2EC977CD90A205BAEB0CE6FB172BA7313E4D9E90029D80183475005462508200C698C0064069BAE43BC0FB79F6966D24C74EE",
-    "hmac_key": "3A51250C6BB80349A408338D6E71605DF2E2A81E5C33FF44CC4417E5E0542BC8",
-    "adv_salt": "7D6F",
-    "plaintext": "9219E2FF4234E8FD54ACFD6707CA2F01B14466A615D7BB4CC2416F",
-    "ciphertext": "D6C18DAF849217FDA4A1DFFBD04E35700DECB6F45D3B7A0DAF4AB7",
-    "metadata_key_hmac": "4EFB7158895B1118259C54CC78E31BBB248052CB7AEDC48EA3DBF9FD39296A9F"
-  },
-  {
-    "key_seed": "1E6AC45BBF875C46F03D16BFA541F3C181FDC51C8F00277CBCE9735A04496AD5",
-    "ldt_key": "D1226CD808D45E56287BDC9F6F5C45118A83E977CEF98D5BF35E39D736ED9E8A027D9D8F91CB87EFAAB5875A76EAF426674D0DF3041ED675EE487B01B1C600CA",
-    "hmac_key": "DD7580EF1D7F8D122F5432B33E4C2911FB521597592DA98E417214187A445566",
-    "adv_salt": "DC95",
-    "plaintext": "A1E76EB381C8AD7047F9B2E67C2789EA93EBF172",
-    "ciphertext": "E16CD310310FD8457ECB96D1450FC0C096387CDE",
-    "metadata_key_hmac": "67AD4504949AFEC5C4B22146D1303075AD944E8EFFD79DEEECAEFE07BED7BAC2"
-  },
-  {
-    "key_seed": "FAB8E36C5FAB89B4A67C62A5ABB644DA927D761C73DA1CB752BFEAE067A26285",
-    "ldt_key": "236FEFDC6DFD2A5005E380B19E29AB9C381B352E2BBAFC96EB39256B4F895E8251D6A4F8BEA0A37995E44B6C3F590D672CE8834588B61DC72442404D4A782F50",
-    "hmac_key": "85EE128C20AD8BDC2438B9226AF8D63B7299C430374987C9BA4F4ED377E5CAF0",
-    "adv_salt": "E201",
-    "plaintext": "AE2615F362002880DF066101987A5A247FCF0142FE6F4FF62A8B3659EB",
-    "ciphertext": "A51E655F1F9529D04969FAFB37B2D210C6B0100F5AAF7958617EA36A64",
-    "metadata_key_hmac": "2763E57A98D0A8B527492F8D6FB15890530F1825D6C8059AC0876A42A0F84B70"
-  },
-  {
-    "key_seed": "8CF75065654AEDFB02C685D64A1CC210E762407359B6DF694D97529AF486374C",
-    "ldt_key": "E6B41278D53C9E476A4AC0590D6EA278E575796F1BF2F3E022F34781712214ACC076DE96F426124ABA1FE86C8B01B54C1F96D98542584A20C0D8ACD69C6EB64B",
-    "hmac_key": "BD06283AF2F0E776B88A753F20FD137A9CE1D213D6A893250E87ED014ECD8B54",
-    "adv_salt": "BDC1",
-    "plaintext": "4E63C5C8671534EB2B4063213D826D63C3E01B0DA9D4300676",
-    "ciphertext": "4CDB57F79D08EE51054B7B444F73ACBEEBEA44630EFC68108F",
-    "metadata_key_hmac": "EDE114A439EBEF9746C26641F2B4BC245FA140729FE3D405AE3CACD21F1722C7"
-  },
-  {
-    "key_seed": "17730EC3D96E1263359B9CA680A08D56D3FC5056700FCF09F798163A250A4EB6",
-    "ldt_key": "8906DC897690FA9525907A8615412B7EA3398330EAADEF9E81E66EC60C57B06E278247B555260F9FA08FB8DCA30B029CAF2824F0FA3BACEC9286AC86DE6E781A",
-    "hmac_key": "AEE16B10773600CF83DF5438C878C6C6C2B88F800C0A86EF7C33D612230C2B8F",
-    "adv_salt": "F273",
-    "plaintext": "D1753D0D1C946B0675E0840CA9959B48FC",
-    "ciphertext": "837B7931396D61BFC40CCC3EA214F70213",
-    "metadata_key_hmac": "128F337336B15BF3217DC2E70921DD43B7187E72F9EBF20F3B078D76192F95BB"
-  },
-  {
-    "key_seed": "97FD64DBB28B443B0930485D40C765613D56E0CD0A4644D2D99A9067483C3633",
-    "ldt_key": "61091495FD4558096DE0C41BE4B1A9813DBB412CD427D98F01E78394264ACCCAE582A3BC844AF73E959BAE7046833EE1C2C4CD2D6B2AD8E80232E18A2D286961",
-    "hmac_key": "37F9790651A5AEA01F66202839BE7FF6FD3C7220A5A2B4A37BB58A1CA6582502",
-    "adv_salt": "8EF6",
-    "plaintext": "57D3ED36ABA8C351D67AA527DCA7C765F34C0C9305133BE1",
-    "ciphertext": "5CAC5FB471CE1B42C971A05759BEA52F8EE020D3DE4CBCD0",
-    "metadata_key_hmac": "E2EE7CCA97C67B77EFA1649079D9232ADAF8E01FFF19C481B15EB4B48D4D2E03"
-  },
-  {
-    "key_seed": "CDEEB09CEC920BD442BB0B3C38E1F3D324C444E2516D701B454C60B35FEDCA44",
-    "ldt_key": "320A5EEFABE36D8B311F5B25CB697DD0505710A21E342EF27CF81D3DC7C3AEC5AA64404DAA559F14782B0C9DE40D9B50E1334683E3224C248A2C243871C8E3CE",
-    "hmac_key": "6798896B6D3FE3A4D4E1F3078D3CE048B5C7BB14502A5AF467315D591414F34E",
-    "adv_salt": "D554",
-    "plaintext": "D5B92B58F17876513109A4F9A51C046E595345F87FBFE6054F",
-    "ciphertext": "93F45B6957AA4E7F99E5A5D6E2193111459644D663C8DCE02D",
-    "metadata_key_hmac": "BC2B7855C12150A3AD611E352006FD420F6AA49D490E131D41F4BAA09554B2A6"
-  },
-  {
-    "key_seed": "546F7254270CAC17DEF49C7B77BB84A16957D5EEDB95A35C97EC05F9A1CEAA24",
-    "ldt_key": "92DEA1F347BC8675FBCF547C12C287B04DA4B40E93E2A0907FD0DD7E341E5BFD2312D343345C3487357632D956CDEE0F5375D5BF60A0BE9E6207ACAA23741467",
-    "hmac_key": "2D331256BCE9432E34A97DBE670523A778FBF614E1099698A5CB8F5D07198BA5",
-    "adv_salt": "73F7",
-    "plaintext": "66F72BA85D73126D5BAF9C7A0F79D2E72D9937A63382F7154170",
-    "ciphertext": "AF1BE54DEF9C33E2911171200C169C2CE377A7A8E61570DC7F80",
-    "metadata_key_hmac": "BDCC86C3E7260BDA5044A61BB60137BD2250D0CF46BE119E1D416A891319FFCD"
-  },
-  {
-    "key_seed": "C0EBCD9AB79CDE3CC1ED4431AEEFDF545248027DCDE79188B88A8C00BD62A93D",
-    "ldt_key": "A0B70A0D9F9F19EAEED65C8E39CCBFE3FC8DB8D8A9E3FFA5AB27A2553F6DECA76652E481B77B6AF88A05F715444521F277B4511EF5D632B72956089EE728EFF7",
-    "hmac_key": "CB3236217C641E142F6B5D2DB1EEFF7CEF884071D75C71B4EAFD3836375CCD4B",
-    "adv_salt": "4B03",
-    "plaintext": "0314C8AD613D415F0381128238DD4D4FD43B",
-    "ciphertext": "A850382DF4F4B9C09518AAB95EF8E6578FCB",
-    "metadata_key_hmac": "0B0A39987EA7B620008F587D5E89811D3F7F72F0E7F015AF3D7B0985D62B1A68"
-  },
-  {
-    "key_seed": "7579F96F2D8375DDF0E1CCC2DB73000868593D17ACA3F093091905EED1F9909F",
-    "ldt_key": "85AC4E459B6D5CD94C0FB8D736F7E83C964BEF9503809AAAC9397E2D808E12716A4F15E31134E6B4F845B70112BE998A0D1F89A1D3C971E83AB976087FA81CC0",
-    "hmac_key": "216D2C0FDF58F6D59E1BFDF1B008FBDCDFD1394D1110F79A75D4EB7D49ABE671",
-    "adv_salt": "AF8B",
-    "plaintext": "4E97AA3A8FC3BEE7E767C1FF9EA619006493CD",
-    "ciphertext": "9026B6301989334B38E41EBA57516A311EAEC2",
-    "metadata_key_hmac": "2D0604BD8450F53C0F4BD6226497F5A9344A6FA5D913D95901C0FF58984AF963"
-  },
-  {
-    "key_seed": "B36D9182A1048F7C73E3BEBCD3A7B3A124C9C0ACE373DD4B59E0A16C71FA77E5",
-    "ldt_key": "356AC88D65AFD472CFE5E3F91AB0A6573404D3920C56A51636D0A5309B1B354E73915D3A9EDFCB08EB0A16BA997ABA8E28B3D0E977F57998C6DD2EFE5466C8CB",
-    "hmac_key": "A305DA096E08319A4803A625C42696D4FC295361B609493EDCF77381C43FE834",
-    "adv_salt": "C0B4",
-    "plaintext": "FE978ADA025F90A85B881AF60A2869C1",
-    "ciphertext": "7E71427B133726743A2E25301EECAC81",
-    "metadata_key_hmac": "83BFB085184BB8C36F59B8BD3E0C9311E0938F727942767FB0E071376DB3A517"
-  },
-  {
-    "key_seed": "CD09C135BEDA8C109B152D9CF7F88A9ABEF5F4481C7488B883849FCD8F2646C9",
-    "ldt_key": "0D9F677ECAC732585B28759417855AF329A78590D49D9FF12D0AADD2B32781F0D9269746AE4B6728D2EBFF754122CA622C41023F44A23EF95DF60A0344B10066",
-    "hmac_key": "080DD4F47372D1E2436EC9571D0FFD206F7742270006CB3BAAB8E792CA1FB3A5",
-    "adv_salt": "D631",
-    "plaintext": "ED3D4DB1A7EB9237FB426029C6D49E45F150F7D710",
-    "ciphertext": "83E1F6AAD5F042C2A0C22287F3585F4D18E3EB0036",
-    "metadata_key_hmac": "87F0CE7CBB559F477FCE464486ED86D6A6DB6103C4806273B34BBDBF0AA095A1"
-  },
-  {
-    "key_seed": "C09DBAC2B1BCBAB3D28B1DB92FE144717BCCD1EDC9103846BDB26D990D9F4B14",
-    "ldt_key": "68CEBB6BA3B08D27B8F47EB8239BA99C48C996283412830FDA984F5B7B1FA5E275748A94BBA20CDF3D85E046C6ED6DFCF2D30ADFACF3A8DA78E8517624E48D55",
-    "hmac_key": "7D6A1F87DBAB2F7BD8DC3F8B14F23E8AB22A222C0EEB69BFDB03DB4ACBDCBC4D",
-    "adv_salt": "F42A",
-    "plaintext": "0A6105CDFAD35964D8350847A787A9444778DA54A8511B76DB5CB399E64E",
-    "ciphertext": "903DD4A1499A7C5B3909EF18F724029A59739345CDA2A2C2561A6C2E4482",
-    "metadata_key_hmac": "3A94E95316819B7E7D72DC130C48D04E341B2AA17A81C78090BBBF0CCCAE78EE"
-  },
-  {
-    "key_seed": "5442790FF7A9FCEAB803FFE492A02C0A77673CB8BFD44FB492B8AB9B1904CF2C",
-    "ldt_key": "402E8FCDCF7F998D019B831196814899A00F45F2D0C15A1BECB5F1229961B3F2E55DDC8FB99BC522179C84ECCC51670E97A301DF6DAFAB79B38E785FE5340EF5",
-    "hmac_key": "E20CA41C6D208FD704092DFC6FE8C92AECD8E09DADDC881839551E98361B9AD5",
-    "adv_salt": "9E11",
-    "plaintext": "ABEB4926C4EA95BA3248E47992D1BCEDFD451CB61D916264",
-    "ciphertext": "A3AED7A71ACA88CB7A88E192BE7DA37AA201B18D02E55374",
-    "metadata_key_hmac": "9796EB7412E34F4E7F9BBA3A5472160322260C0D0DCCD2689BFDDAEF4996DB4E"
-  },
-  {
-    "key_seed": "3B87F7C71E02C5C0F6A16F6AC78AA9521412662AA59B8392ECD75C491FBF01E4",
-    "ldt_key": "3687A6075AD15455F9000009362B7E822CF7720BEA598B9F854F834DB5D304569C8B13E2AD25BC1B5C309FED0C21AA8AAE0E2EC88FA4C6615DAFF5820B0F7952",
-    "hmac_key": "2DB6782D5EA8A24F379E388B3152D43E74D36D79E45F76F63E567D4B77580889",
-    "adv_salt": "EDC9",
-    "plaintext": "1125A3102BB3F1EB5F7EEFDBE0AA7F090E23DA8BCD08",
-    "ciphertext": "4E44EF9D0E422340999130852A639A0135A7CAE80AF9",
-    "metadata_key_hmac": "E13C7E584F918F202F96DB447CA0BB57433E9232D1BFCEB4B093DD2E4F9255C0"
-  },
-  {
-    "key_seed": "76DE95A76BD4D22B185A05245FE756896C06CAC50172CEDECD251A6DCA14CB0F",
-    "ldt_key": "9FA6AEB9EAC10DEFF68A8F2AAE2A10E1F6E5A749747337EBA2546178027508C402BA8C47CC1D97CA8B91B017EB00BA9056C15D1F008ADB4F446C00E13FE8160E",
-    "hmac_key": "B331A07FDC05AA8A9100D4DE7914E1127BAFAC4256147A5419ACF46229B80451",
-    "adv_salt": "2DEE",
-    "plaintext": "F7D625CF3BFDD089FB96A5BD65CFDE75D354",
-    "ciphertext": "174E63C64180EA078025CA9F65A7FC330356",
-    "metadata_key_hmac": "DD891B35565C42F8968A28C059C20250476E90DB46A666F429D25C1706070900"
-  },
-  {
-    "key_seed": "8DC435F28B2814A95817D20F82AD19929EDD04CE6174915A38A68D55BA19C456",
-    "ldt_key": "8B6B64B8D7E041703D2D917EE972693DE9A8DBEF0A5846760F98CFE1E3641BA823F4803C2C2FD8C18B584D422192EA004D906FA232972231B21C5D7CA854C0CC",
-    "hmac_key": "9298AF5DF70C371D15C4BC85D44056D082FD30971BFA8D5BBF8FBAAC502624A0",
-    "adv_salt": "6362",
-    "plaintext": "13B76A7869694AF0F30AF57FF629D4E68513C6",
-    "ciphertext": "B12CDB5633D37650ECB922F1D1CA6DE2768BA5",
-    "metadata_key_hmac": "CED9332E1A888F4C9EB126537A98D2E7DB795507AAC88D9EA78E9980C2906B17"
-  },
-  {
-    "key_seed": "A750D50F6DF9AACBD29A7FF6AB3C0E4C35E722432A051657B6F10641867230EC",
-    "ldt_key": "364B9811720C285488D1B613AA5DA0BCF5863AA1578F331A861B100830CF086B674A9C78769056C4B8D2FB2DE8E8A917F85B536C408E9AB86D2522E8728475F0",
-    "hmac_key": "5D617FCB6A3B9F8A334297FD136F12FBDE1F52FFD7DDDF211320485ED0EB9F07",
-    "adv_salt": "7A4F",
-    "plaintext": "FD94DB89F51B5E009998489265849007D5D14114D6FD",
-    "ciphertext": "E1A6627D1F2C464583BF4BA37B997DB7D616310052C5",
-    "metadata_key_hmac": "EE227D9831233372FDD260B0CC08BFE9C17F186B53509B491113A676BE6D3BAF"
-  },
-  {
-    "key_seed": "CE558F42E9C5EC0A24CD5E3120E94FADE5BB53AB08CD965F485A44483D9172C4",
-    "ldt_key": "F93D7783046FF3A5B71DE731B7015E236E5E93A89DC6018B9D7B7BF8FB3BC6CE66BA065BEF5EBC2A8C0CFA76D85BD3FB77E12363A478388515EBD2AE737C5723",
-    "hmac_key": "1FB0C4076FB67BFCFBAF262A52DACF1A8CF31E7DEF62E39CC58433C80418CBA3",
-    "adv_salt": "6748",
-    "plaintext": "09E3FA552BA39072C14F59A9C1D7560B4EE042F5",
-    "ciphertext": "992C208FEA31EA2C09B1A27D20F333FD5BA02684",
-    "metadata_key_hmac": "93CE4B293C12CADCD0B9BD222DC17F358A9F5873F124A605B571818AF7D8C771"
-  },
-  {
-    "key_seed": "871A0CE542912BE9D05297BC11036DCCA916AAAC6B64226867CACE2446F93C4C",
-    "ldt_key": "8C1D7F631CD9486D3AA58D6CBDB54BF9AAD43210347218F4681CDADF82E79D70387BF444AFCB3778205E9A460E9834D7284C921EAE62A079F69959C7DDE15564",
-    "hmac_key": "EE64AFD968DCC9E5C1C339E005442B30F8A6124ECB84B0DF015AE5BD0B3F7F77",
-    "adv_salt": "D9AC",
-    "plaintext": "BC22B0EC3188B9F4E1C47235D1ED55C559CDC426CBCEB4C8CDF76C86E5",
-    "ciphertext": "68FD17A33C0B395CFB5162FB41670C5AFF0914A2FA65D7C03FBA49E03C",
-    "metadata_key_hmac": "4E6EA4CAA9E9DAF62D939AA3459D87FEEDFC7F5E3F96FE588A82201D1DE34A0A"
-  },
-  {
-    "key_seed": "0B1834A1CAAD9A446B96DA719E4EC817F1DB6DE42649416D5911A88A91D8D1BB",
-    "ldt_key": "D3F6A16BC42FB6E27CD897BC8AF9EA945D5A353BDC5EB781EDFDE6E16CA8718B4EF49F9FDBF2FBFAF8AEB7596A85EE288089AD445CEE74DC42579576710A5E7B",
-    "hmac_key": "C6DA07445FBA6B06B5840456B345F6072DE1D256AAA0AB0BF1D66728E49797C0",
-    "adv_salt": "18CD",
-    "plaintext": "57B8BED51AB39E290888DD88A457ADE59E",
-    "ciphertext": "B98ABA498DD64891EC4A524330F488A403",
-    "metadata_key_hmac": "99BAB3FB6F027D2B8FD851802C210E0BD6381B74C7877D54DFFDCE9E74E4AFC2"
-  },
-  {
-    "key_seed": "95F18F784A69A10B7A6F63AE5C29EEE3DE033CD592707B9321A94705126887E2",
-    "ldt_key": "3F6F0B7E227A25AF15DA13F97B8530769E773D7B4AFD8B92A14AA7B75519F2B4FAB56767B6FCFC42151902542BEC23FAE133A79C6BDA3E63FDD9D8B041711F78",
-    "hmac_key": "0E08C9B0AC72EF8AD892658736BD93EE2F87506C406EE1E4E63C73CD3B6F9DED",
-    "adv_salt": "26C2",
-    "plaintext": "D9D0E3A4C12E2AF88AF51C74A898DE9843B707E9",
-    "ciphertext": "EEB57A21E15E485939975CA3D0D893D2B8E8E238",
-    "metadata_key_hmac": "5032FF4FA4AC443962F57E271FFFBEAA3AF5D8E947F5A50ECE4F224FC0503080"
-  },
-  {
-    "key_seed": "695711816115D8719F85408548466F0E02F00F787DB12927E313DD9AC6CDDF75",
-    "ldt_key": "7F4A2174FFF86EF906B02ED7FA6146DE550F235A0D46EEFFEBDE6907AB094659427D5A5EA92302B62A669EAEA9034EFBB1A0295AC66F6906192109FC9858714D",
-    "hmac_key": "13FD534B4FB1013D6BCBF51825E3503048729794FA4AF57A21F92930BDB22B5F",
-    "adv_salt": "5987",
-    "plaintext": "60420D199842E47384A6760B99625537073113",
-    "ciphertext": "FD44230C154B407F203B4D068930418CE0E715",
-    "metadata_key_hmac": "FBEC4C555DE7E1AE2B0947969D3588F12EE5057CE0588CC9A1CE655A1263CAB6"
-  },
-  {
-    "key_seed": "2E3F37B0235FE80471FB00EB4532B82EEFC634AD2EF7524ECAB169E0C79800A8",
-    "ldt_key": "5F1F00DA0AD4C64A8D0E59EC43B7DB6F6B14F455E4C1C27D1CFD81BF13D5C46F56A2CC80399248BC36E5B1774B8A02BC938F453C2E1B7D1B9882DEDF0B00C41D",
-    "hmac_key": "D7C6167DC04D9EEFA230EFE173B0A9ABE905C2C86D5F9B11B9207A0742B4D8AF",
-    "adv_salt": "B8B4",
-    "plaintext": "F7A09923A8CDFB5C8879E8EF96566180E1EE1103C123669849BB06385343",
-    "ciphertext": "A9E265E00D392435DB745962F8128C1C3ECB4B87D5599A985E420B5FE1DA",
-    "metadata_key_hmac": "B4564A26589F605669E51B58E5A9F46E997A4D3A62650A60AAB1E8DB76820A50"
-  },
-  {
-    "key_seed": "765465BA434FDC5F5A13723AFC20E96BC3B1D86887C77B1FA8030C254EB92B9C",
-    "ldt_key": "8A7CFE1760993E348A87488A539F0C35D4B4F9B533DEC0A228211871D06AC7FC11F3AD456D8E0F108F27848711B2FB181112741B6E6981EC2709BDEB6383C307",
-    "hmac_key": "4B5BE48B79A53E8E61960F7BCE547416EFE321C2D06788360EEB554E1ACD3E39",
-    "adv_salt": "6F80",
-    "plaintext": "1A0E0C6CFC3A150CAA4D4D61E8EB4F34AD9A3AD36C15DD1D77229B",
-    "ciphertext": "64B7A5FE6B7C20F31A874AE148D4C97AD2E1CA693ECB288C864FEF",
-    "metadata_key_hmac": "966A4E64102369E0AA75FFA0D6772C90D8D3C3DE3483C3DF384376EE82D760A3"
-  },
-  {
-    "key_seed": "72381291A62DE7DFD1CBC6BAB00BFDC5B46C2EE09C8FA278E42FB8DC42F1E960",
-    "ldt_key": "DA6715B676BAA8DB71D1F0775AEEF2390DDCBDE6ABC971899CAD0ADCE3027891073B76B94FA1A12CEB522085F78B3A0421003144F5D8B50AB8DEA5B69E696525",
-    "hmac_key": "B889942846482EED3E216C47731B035C38B9CFCF46D0263B989DF6A3A2C4445B",
-    "adv_salt": "9C9A",
-    "plaintext": "F711B422BBAAD271F03BDB9BDE48C3FC723C2ECBA0F5",
-    "ciphertext": "095B6BFC2CD29E3376651AF2EA8DED4460D4325B6905",
-    "metadata_key_hmac": "5E45EAF3F20A15A6DE3436DA60F8DED969B0111DB634B1F11FDD662C055130E0"
-  },
-  {
-    "key_seed": "8E35D1093AFE39355128F04C49803450F4543DB0537B657C69C222300C72F751",
-    "ldt_key": "77C0FEDEDBBCE0D3E50987909D77BA4D70CC9C9C8B6112BCFD9ED4B8F5EA617CDF252E08052DCAB3B3D3C18F56C7413682838B32D415C55BB3F2B8039E01FD56",
-    "hmac_key": "5339126F2411B984EE956DBF1A4C265403297D133D67605A9C29ACF897043870",
-    "adv_salt": "DEED",
-    "plaintext": "81F0B5FDF2B425AF0CDC80B93A7A624F",
-    "ciphertext": "2312202B81C76EABE4E4B91C00136D23",
-    "metadata_key_hmac": "605CA950B80BB0E1055FEA70C503B263D7C919003001C5B8B54B0D5815F4BFCC"
-  },
-  {
-    "key_seed": "1B0749CD1E244D4DEE64603769A689537C26B50BE53E37E4BCE1D3E10F582EDB",
-    "ldt_key": "89F12E3F45FE4B05B82D9CDEF43BC09CF87EAA058D0BDD4634BEFAD13D8F87B6F67C38C055AFC31BA2521947A8676855F757FF8C48AACC8430776F54E19341DD",
-    "hmac_key": "229D2FC414AC6756483E0E00CA7CBA34D04027D6D489522A97BF5E33F4E80FEE",
-    "adv_salt": "E2BD",
-    "plaintext": "B94D6C9912F72912851AB750BBDE5927",
-    "ciphertext": "486F60AC756B2C82E407232FFFBE7199",
-    "metadata_key_hmac": "C2964971446F3B1BAF92B561DD49412268DFE419747C5A8465A3A10721A5C1D8"
-  },
-  {
-    "key_seed": "E09A7417E9877F581B2CF38D11D8D42F2B733C0A905429CB3869AA5727E68813",
-    "ldt_key": "96CC063FD11ECD5FF34B9581446AB922B1DC781CBF8A2A2494A9A8085F5354C44F59985797EBD94219BF3431C50593B65C19E1551F2BA6DCA2920495344C4E85",
-    "hmac_key": "81A750CA4967F0518943225CA422440439F71B14FE49044D1AD02AFB0828C279",
-    "adv_salt": "E60F",
-    "plaintext": "7EF48C4B1E3EE85E663E8C091975ADF32E52E0A4B7B5B3D9BC279CA1",
-    "ciphertext": "1760320AB9DA11842111E829802A0F85252CB54EAD7F12A1BABB616F",
-    "metadata_key_hmac": "3F6487B3DB83085F97BB68E6D1BE556541909A650481942A0708C0100DB90A72"
-  },
-  {
-    "key_seed": "0D7C20ADC3EE47B9002FEEFDAC1728E552F93390B1F8961869C9FA3E20D7F657",
-    "ldt_key": "BA00D72C6D037D752B6E164FA60F060293301D22FE1106F8C921DB29ED31C05471585DC8B0F7D5C491C9DCFDD7BBDD14E7746F822D4503F8F65E95284115AE2E",
-    "hmac_key": "B11F2F2E03F7673E79DA13653AF3A4F76DA34CF0982B8028B143BDB8D3153F89",
-    "adv_salt": "05D1",
-    "plaintext": "258428977375A4EF69D086F2B6966A43299421",
-    "ciphertext": "C583A87CAC695D77C04319C0A3557DD2CDEF25",
-    "metadata_key_hmac": "C63B92D769ABC05808E5AF0FB9820928EDE040FBD81374FDD87DF4391C1A513D"
-  },
-  {
-    "key_seed": "377773727C5306FAD0604DF77A07E9B4FE95DB4594938FA5E36FD05C67004A9D",
-    "ldt_key": "6F5EB812C88F1EA7A94D50E04AF00D8A818B231DEAB0D7A24458C8BFBBF5169EF5B79876C40867E1610102BD45D97B0F3CF5854B7F2808B25542F9F7C54BBBE8",
-    "hmac_key": "E61CE07FB912BA94CE24826E99AD7CF8DFEB0A84A97119176DBF539C63E80C21",
-    "adv_salt": "78E8",
-    "plaintext": "2E0A8022625345195F6B93C3BE8F5295F1",
-    "ciphertext": "AA24A9894EA51840333321662DE2DD997E",
-    "metadata_key_hmac": "4B3E7C26BFA8081C3DC82B243DDA891D1CD86A8EA7CD789C00260CCD9B9439BF"
-  },
-  {
-    "key_seed": "1ECDF7739B0CE4ADEFED5BD2325DD7539D0A872AC95C1A16A77E56E3A20CF760",
-    "ldt_key": "1407D76C599A14A143446553628B4F8E3905872EDA141F245B148E69790CA5EAFAA7190B0C33EC21AB326EEA4D209914FD8D03E42E8669C04E5C1F7B60CDBE14",
-    "hmac_key": "BBD069445ADC2C90A6D9625A148B7B9A42B4A0161F9F87576FFD41AA995A2C5A",
-    "adv_salt": "F96B",
-    "plaintext": "A933A95E1D009CF57F083C8244C5FAAEF2E2AE5414367FDD6FB9",
-    "ciphertext": "44A5E01FC95050FAB258C1BAA50F115B10B65AFA5E309148EF68",
-    "metadata_key_hmac": "9408F5718C1BC41B68DE68B38AEC980EEE7880FFE2C8DCB42E3721448EF25DDE"
-  },
-  {
-    "key_seed": "0B5B97066B4F30208B2342D39589698A1510246523220EF552EA0D292C9B7C28",
-    "ldt_key": "390E0246C707E055145F47522CC549AF6BA75CEEA06603002CAEC2226EC95FE54129CF95702C484BB3B09A80486E06E1A564825B3797A6419ACD5414A7538871",
-    "hmac_key": "990000714F5E6696C8A1796C927A103B3C4EEAF3A130318B69F459E0661103CD",
-    "adv_salt": "C8EE",
-    "plaintext": "BE3196B51A58523F04D2897AB5530E22469A08E6A9",
-    "ciphertext": "3B7C6736F04F41E0F80EB346E0F36377E08EC3C8E6",
-    "metadata_key_hmac": "A37DB2B4480C73FA5B9E8663441A96C4B4FA1248A543CAC51BB26E5316E3982A"
-  },
-  {
-    "key_seed": "D18D84D25E161E6C99F6B883432A2615FE20C0820E5B8750578F77028B69EB92",
-    "ldt_key": "C08AA8161F484A47F912698A13246999AABC278B48268EB7DDB7D23DC6EA6462B3294D56522588D4F6F4E3E14CAE92150E47E8F9E009B6A33FC70708793826FF",
-    "hmac_key": "6584A1CE18AACEBAA3E102D9BE51B36E2CB9D798195D4EB20DEBCC6DBD8095EA",
-    "adv_salt": "E9FF",
-    "plaintext": "8335E424E057F7366C7AD90AB69472CDD0",
-    "ciphertext": "1FE2CACDC1ABA883BEE2F464B9DEBFB955",
-    "metadata_key_hmac": "1E8B1B63873F20CBA369615FB029630EEA11EF14D4AAF430370CEAADDA9036DF"
-  },
-  {
-    "key_seed": "E89BB8A66D843D3A7FFEAE2DF3D711C962D3D2D9E34DE3D3BB0A8287A4C8F00B",
-    "ldt_key": "905307BCC3F3C82C84F12A9807F8DC1E7E419643A54328D1D5450D51A689D7264429CE85803E22EE21F0C167A010554BCF3C01791BCBEDF3BDC1F365350366AE",
-    "hmac_key": "79D5113BAC3A1FB4EA76909781CBB4DC06BF89043C373C9964489D175284937A",
-    "adv_salt": "D29C",
-    "plaintext": "9CE911AF013A1535A373C84F32B08837F110A668593974492D64CA3B7CB8",
-    "ciphertext": "1BF01FF3EE22F3FA0ED7C3FA6D5BE0716118937894FF2C0C5C0974BFD8C9",
-    "metadata_key_hmac": "A0ADB400E1CA8CDA1D9EB544C148742298403157A73558D6C0384071172244E6"
-  },
-  {
-    "key_seed": "F580346EB8AAD458E302FB36FB9AB5993719F0D2EFA51FCBD5DA69809D61FFF9",
-    "ldt_key": "96C8DFBF3172CDAA12AE1E74DA772548B54A044F71E05642D954E1CD7CBE063F7C4BB3ED5906004A4B602C3BBB27AFD73D58891142D4BA2947B21B2BF269668C",
-    "hmac_key": "A48116ADBC1C5E620C163D0DE9B4F6CF37EFC4181F38DA6F575BE6CE8B75CA5B",
-    "adv_salt": "6858",
-    "plaintext": "B46BA10703C5D2AB82FECBFD6EF00FFC75840927DAD75F2F75D134D4E4",
-    "ciphertext": "6F59244143F1A032FD15F056B34A744EADB18855FC44CA83488C1D3C9D",
-    "metadata_key_hmac": "FFE03B929D4B11AADA631FC9115E754CE31155A0A795BF43F85EACDA2990E566"
-  },
-  {
-    "key_seed": "93588CF34CE0255B2D35E75C948FBDE8C9B03807353B39ECAB013C8023C66F57",
-    "ldt_key": "84956017E5F5475AF1D43479DC65263437B3167EECB672F46169944062D7256880B99143B350C02DF23ACD415EEA6B5F573DDB65F399E25606CF0BE12CF400D7",
-    "hmac_key": "0E1B448C6D28C2A492CC93CF32D9107A3E897E8044561DF6F2BDF87DF1C5DD8B",
-    "adv_salt": "992B",
-    "plaintext": "076FE4F24088FCCC07B0154009C5B4078C9784D0952FF95BF4",
-    "ciphertext": "31796F1230A90B220990D0F10A209F80EF7A9AE01AD2ECD2CD",
-    "metadata_key_hmac": "44C00FFCECCB08934266C91A4B7685186EF150D405C5A91842DD2C6E8F789E02"
-  },
-  {
-    "key_seed": "2E86ACBBCD7FC897FAE73F3A2E9297DE135C2FBA92881E8004C24080E1DFBAE5",
-    "ldt_key": "7E40B0C48425927E5789BFB60F323DAD9698AA522281A684FF92EABC311108DC3C6D957CC7C4CAFC2A9F19C1D59F6F11BCD76C7D464B0856CF81BC822834AA86",
-    "hmac_key": "B5FA05B652BA6D2668196DA9B8D0AE6CDF2982503FAF000B90DC52A2473D98FE",
-    "adv_salt": "D06C",
-    "plaintext": "D0B9C548A65940C763D95C3A561D8979962E4BDB2C2E",
-    "ciphertext": "625CF9643EC69F218278D1F2BAD2A0154BF6BB2A8A26",
-    "metadata_key_hmac": "6CB2C2F544FBD75C3D875414DDEF01E2BFF2D1B40758C1672573A5E0213B8CE1"
-  },
-  {
-    "key_seed": "2FB0EC5CD9F469174A96F1C14171A8E6C67BA0D56C2FC3A00628B713236B9E4E",
-    "ldt_key": "EE3FBBA11905478EA1B1353DEB10634F195F38FA133D53360C25607D58AFA6FE8A88B9F8C698AFC45A6B429AB0F68A9BC6B56EB385715DB2F27129525DEC665C",
-    "hmac_key": "0637BBCB8D173919C986710AE11CD6EEB83E46EAA5CC5C429CF46DF71A8F6058",
-    "adv_salt": "BEED",
-    "plaintext": "02E4A96808C95765A922CFB0D0D9ECD5C0B49ED3384DE6",
-    "ciphertext": "4A690E995A954F7CFC7F13C5F5ED81EFEBB4230D733C2A",
-    "metadata_key_hmac": "D25F03795C05EE864A4312CE1088ECE931116CC078FAA191C21657E256369833"
-  },
-  {
-    "key_seed": "37C170756E293F3F6092FABD3EEDCD9DEB2519610214B7C9D7C417719BE5C6DB",
-    "ldt_key": "FEBA46FBE1CB8BAD3AFC628E8A6A778863511EF9BCE2D652B9A4BA49003A76750BE30417558931ECF3DD2E636E7F2837437C094652F80ABC40BA5CE3EEF67449",
-    "hmac_key": "1289ECB15BA013589A2226403D1635FEE9A72BF37D7FFA9F284E14C645EC8DE8",
-    "adv_salt": "075C",
-    "plaintext": "6391F65414678673BE8927A26FDBA669F3C256",
-    "ciphertext": "7445D71AC6C0A8C838E4A55D5A4F6D13CE9FFA",
-    "metadata_key_hmac": "5E9307F5F712F0F32699F093B769B8D2D84B2DC597F4CE2CF4E1DA0F171DCE52"
-  },
-  {
-    "key_seed": "45619F5CEAE4E2ED2EFE4A90CA066D369AFCE0E480BC244C790AA37BA8E70657",
-    "ldt_key": "6F19141F07631BE959AFE8192B43AD51F84F3531989F94FEB68A347CE1265435E5DBADC6B8C3D495780487EFE89AFFF7E585D26E3DDAC8CF89A34992AD4A4EC2",
-    "hmac_key": "49A10BBEEAD8F2D1EF0847DC2312A02FE9907EA220440F265D9D976E29DB9CF8",
-    "adv_salt": "FAFE",
-    "plaintext": "AF17661A188E8D91698FE12DCD5577F9B560C562A3FAA88E134C30F17E",
-    "ciphertext": "881080FDBEC85E7EF269CC29F0977B79C8386E848878531F3D11881945",
-    "metadata_key_hmac": "9C789142AAA3372632DF23547C07A06FCA18B01EFC9009ACA64983470F410941"
-  },
-  {
-    "key_seed": "A393B059FF76B55997A6428D587F0309B4AC707792D5DE91184B73C07068FE14",
-    "ldt_key": "526DD74FC034373B40E162522C7479C014F1344136DD8B30FCA1E1D3AF92EA971951C6A1CD0D28B3097DE4E03E59053A77D7473CEFB31911934DB1B81878BA3B",
-    "hmac_key": "07BC114B204E229117AEFBD1ADF520EFE2145D043CFBBDE2B9C5B1330EDA331C",
-    "adv_salt": "2417",
-    "plaintext": "92099CE78A30E61F84A476F509CD2ED46BE9F43D7BE338E9FD3EC406",
-    "ciphertext": "6EDF8279E45D9FA0A4B76864BD69AED8E52601192A330BA9AF1F58C2",
-    "metadata_key_hmac": "6009E34AE82667777158A2D51650AE2EA688867F0AD4B0DEBEBD3FA6B5E723A9"
-  },
-  {
-    "key_seed": "67967A06AEDC8E27B759454391BE5BBD5BA255A2EE23C40A9891317646B0B4A9",
-    "ldt_key": "47CAC71B39CBAF5759EACFCCE70D8644C0AE66E20F58A779C3C4AC6F10A23A9148C8755D25219FDCBD5D8CAAA8178606DB0A0A1C14F3EC555628CA4F4DA4C207",
-    "hmac_key": "2C5976A0843A1B73A9EB4E223EAFC414808D5A6480DF301F7067DAD2AF4CA709",
-    "adv_salt": "A223",
-    "plaintext": "ADCA492DA05FC0B2A2733E94819194B3A34E55",
-    "ciphertext": "4A946550724ACDE397E4DF825ABF8B4F7114C5",
-    "metadata_key_hmac": "A0EDBCF865034CD43DF4A545D0F7BCA967C564ED9E22BA1BFF690216816D5794"
-  },
-  {
-    "key_seed": "90E39F77C5DB43864F1F88A72F0C882BF78ADB49C04ECBC022467D612E283A13",
-    "ldt_key": "435DA1AFE7BAC5D20D5C4C741CE67DAD6893EEB367F9D30E0CA0F09176A3553CC3E0DAA6F5A2C7D0DAAA8DCB2BF6F38E10C06568CA03C3F8315D7635FF4CA4DA",
-    "hmac_key": "946F01A314CEB6AF1A852E70370C3072F65318D3A3AEA7778943AAE81B614E9F",
-    "adv_salt": "0CCC",
-    "plaintext": "A4F9CA00FC38B02FB88D441C26B097B7F27431",
-    "ciphertext": "C835E4918302374E1346A8B975018D16051020",
-    "metadata_key_hmac": "A6206935E82C2FA100EABD8306F0E5C78E17A3FE1F9D7C4387E9DEC4667A9924"
-  },
-  {
-    "key_seed": "E8D9B87BF76DDC286DE441207E885CA36DBDB519CB7219BF540AA658700B7B2A",
-    "ldt_key": "77275D5B7AA5E9BA852A7D3B09FAB7BF58A259EF133B794D444B6E69F8185DABCA140174BD4DF7A3EF0B3977E404ADDCEC8EB9E6CC9A8B4ECD663CDA32966E77",
-    "hmac_key": "176FC5689F1FF116CE5E401EBB9B6D3EC4698A6340C97D8B96A6FF7E82FDDFC6",
-    "adv_salt": "EB0E",
-    "plaintext": "A5A56CE0819C10334DDE2E425F82655EF6D6F768901D81",
-    "ciphertext": "365D853B73185DA663CF7D7D51A86436306057093896B9",
-    "metadata_key_hmac": "79F8C788707F75963428C2E4771A906FBAB25F42704AD61C09129B97DF28BCF5"
-  },
-  {
-    "key_seed": "7367018C066EF078B706CD8DFC2B4044DEF189682651ED9093475B667DB2439A",
-    "ldt_key": "97A061889422F49AFD64C156588A0A81108932F885C7577CEB91D036D643442E5684806E3BA6A5BDCD0C11E54FFD820F1498C8348012A8536C5D1E99FA3EED6E",
-    "hmac_key": "02030AE5A2A63059015E50725C29F7166E5BA1A4B97D80F0F32CF04308242661",
-    "adv_salt": "86B9",
-    "plaintext": "39C22AB75E16F11F539E3ABE96761BC4F2B17A",
-    "ciphertext": "76E96E8A879E01D3A70B24E689464F241BFFE1",
-    "metadata_key_hmac": "FFF91740C364D52B8C244B8AEB77365CB289A40E279F5497DE9098CDAE7CFEEB"
-  },
-  {
-    "key_seed": "81C509C483D7A1C427DCC5EA2E05CBF4588FBD984B5219DCEAB11741FE761DC0",
-    "ldt_key": "64C2A9364F5792E39D244B8FF3CB23F6F0593596BFD8CEC89FEF9BB2FD4772B4D708018427DC658A2E1855C7DEB9B3892B991ABC02F0273394BB92D4FC32B945",
-    "hmac_key": "6493D527762752CE49DA792118247B05AD5DA653994309DE64BE6098BE72CEF5",
-    "adv_salt": "E1DA",
-    "plaintext": "3A1B6581737EAEF0D68EAE52E57BA1B989C18E59F12336F4",
-    "ciphertext": "FA57FC03D94B1910FE96C7F303454BDC96C293BD1D80DA21",
-    "metadata_key_hmac": "C8A153B4A0D12079F8EB03F44F8551AA59C25D7BF5C5CE25FB08883895887AE5"
-  },
-  {
-    "key_seed": "98E720F7BE4AA07F9B457E53E87602FD10B9198A0B67194E3685AE69A6CD8061",
-    "ldt_key": "007422437E2CA078E7EF7870E497B2F7DC9161EC79FE046ED1ADE1C29D26404113B70C2180CA36376D25C37088DE4DE2A4E7213CE1AD50C2D52182BE5FB80737",
-    "hmac_key": "21FEACD34FF6D92AF01D10B2940F0E2DDDB373048CA9FD16E010C330CB31030B",
-    "adv_salt": "A13B",
-    "plaintext": "934B45CB11DD77578E4AE09AD554B4EEDD14AA1BD022EC47FBCBB0F2AD3CDB",
-    "ciphertext": "8CFE890A7864CD65091DDC85F7E0CD9661474622EDF2C0D7D44A1EEB31501B",
-    "metadata_key_hmac": "99CB7E7B8DA309853B0E34220D9A2B7CEBA5E4E5421A1DF459028015791BBBB8"
-  },
-  {
-    "key_seed": "869A1CAF84F8C60AF1573BD15118E6C83711314495ADDAEB86E4A10FC9BBBD4D",
-    "ldt_key": "CCFA059EC7059537D0C7F627331C54EA43F4FB2473CA24288C3436C6F496E3A403D1F31B4FAFA9AEBCEC663F82F0E54D8AF59A718CEDA93AA4687F79EBF1D52A",
-    "hmac_key": "D09919AA79B853568687340D28461E8778C6450A03098A4634F196FBAEF9B3DD",
-    "adv_salt": "7A78",
-    "plaintext": "8A5BA566981761C530F59FF7A5B76F7358ADA54DDCD6",
-    "ciphertext": "E2EF27BB3DBAEDA0CF508F9178E2F97A7DAC0846BC3A",
-    "metadata_key_hmac": "079AB6E5A595FC2EFC8413F6469E6D4B286A0CEBE67B2990B3B6B596D04E9DA0"
-  },
-  {
-    "key_seed": "5E114CBDD861FBB10B285FB3D995EBE57056240AE9FB87E3EA7868E52B9C6C98",
-    "ldt_key": "CC05778C76198E180288195C60A09E3097BF4179D28014AA5F399DFABC66C7403999595D5A124B56E66669BA63817D8AD33E4BDF8A181E7BD8AEB5EC0A0630B8",
-    "hmac_key": "71F06F85C029FC1D0EFA2F14B25E667140390C22681888FA4BE6565BCE65C0EB",
-    "adv_salt": "5523",
-    "plaintext": "E96582B1A180B733FAFA085DE5D6F942E6E55E2D0F6E",
-    "ciphertext": "7DF18EE13B77E39AE5C29B4421F3A64B4E0805BB2C21",
-    "metadata_key_hmac": "0122170D72BC714EE10582C5EECE1DA65BFE6C4FF396256CB5C122152DDF9C7D"
-  },
-  {
-    "key_seed": "D78D1699F639D9CDA4190E16841C1344AFBB635D46CA61FCAC4145C8C53697E8",
-    "ldt_key": "F950BC9C3E082799E7B2DA2130146AF309A4C5313B98659CEF81A206985FB06198AA034154D5E25FDA57237F522FA424D584837325FE0C66C06A80C6C8BAE57D",
-    "hmac_key": "AF5BBB5BF2D84C7E88102222A95F7FD566040EB6E1036A78A90DA3370123D4F5",
-    "adv_salt": "CE9C",
-    "plaintext": "AE6430E1C1C8525D7B926B8D93186DD4AB643B55BD3D01CC",
-    "ciphertext": "66A36F510CC0612AECC4864C98D42B5E9C304E0F3DE19AC9",
-    "metadata_key_hmac": "540488D963352D79575337773009746203AEA174E4DE49F0A33621872723A4A8"
-  },
-  {
-    "key_seed": "DBAB86015D39692FFC8B6EE1862F9BD6D2C8B28E4DA10D0432D1C0BD624511CC",
-    "ldt_key": "796693310002DBE4379A3927AF6129489F49E76134175135BA497C6B3623D2A6A53520F53A3581404D234ABE4270921FDBC5C72048E0D55CB30022AA56E40992",
-    "hmac_key": "9B95D807EFD135149898E701C2370411B0BFDF652C1D693C9759B20755909A12",
-    "adv_salt": "20F6",
-    "plaintext": "C5D10471EC644F0BE0C928CB0CE6504719F3839795F0",
-    "ciphertext": "D81B09EA189F737EBB92978AB0CDA63655B4D21DF6A1",
-    "metadata_key_hmac": "9751840D980E9B91097AA8DEF58FAF4D90F3913479CC9E96614348A892E6DBA2"
-  },
-  {
-    "key_seed": "20BA5C3A8BE93060918FBFE6CB2EF94B0F32ADDA922132930351580BDB1C7B2B",
-    "ldt_key": "FC7F8C9BC3B7D93DA88E0884C489C20A9BC51CAC2C195B3371A7F0995DA84AB9E528693F580A03F8F18F2DD18FDE0287A6B129AD695D9A2CB6631A4C85F6725C",
-    "hmac_key": "0FDFB5FA8B335DED4681FF8E7D232BCE07199A19F2CEE581A388F09C7065C909",
-    "adv_salt": "347D",
-    "plaintext": "8636C156B78738A6474D89AE036CC3BAD7B2EA7D6A1C0918AECD2E2AF5",
-    "ciphertext": "BEE77F1FA9DBCF3773B0B803BF2822AFA4EEABF610ADAFE4F69604BDBF",
-    "metadata_key_hmac": "45362CD8C05D6C4A0AAF9B7A0D4D9E9B7F268D56EE1CB9067BA06751F414555E"
-  },
-  {
-    "key_seed": "E1CABE143B819DDAF780F10E8DCB34557A110FCB16CA9C1208A9941139BC461C",
-    "ldt_key": "BF2AFAAFBCF85CF13761CD4EA9768DF5932A888EFD7710E5DE11475D2A59E377FD1E86248FAC144640BC8609FA105A64325FF7B586065E10BEF1A12EA839251D",
-    "hmac_key": "43C7D0D13D5A0665CA049A455DDAA10099FB9E0E0DF94EC9385D187254623F62",
-    "adv_salt": "941E",
-    "plaintext": "24CF3DB619D7E3BB10569664E2B2AAC98D7027",
-    "ciphertext": "FBD875C04016599BF12B11DAA4EB84B538F824",
-    "metadata_key_hmac": "147E5C30280A34487DBF757127CF1B38388A8D18D548E4EF9ACC86B8CC81239B"
-  },
-  {
-    "key_seed": "19AE5D5D8287F089DD52E258C4E144B6946DD1C96D0365DF2756088BE98AB4EE",
-    "ldt_key": "794AD4D1E28308C607A4C05DFB118477DF439D002CBC7CD0D934F0999E922C4D00911B9E6E811CE781EB95878D5CC25E15A503150D84AA701F2A6127F9480FAE",
-    "hmac_key": "FD43AEAA7EA6A89D6A70E70169CE79C7790DCA6182609158D1A28E0E57744B2A",
-    "adv_salt": "41C3",
-    "plaintext": "315536736F3F2410448D02ACBFB5344E03E736B6B3AFF7C2",
-    "ciphertext": "B0440FB32EFC893AC3285AFA470A682BD45A7AAE268B20D5",
-    "metadata_key_hmac": "3F9AAE9B5918C539C94D9248FA88216562C0387B20946E85C167E758C07A65E8"
-  },
-  {
-    "key_seed": "93CB8D5F88106CDFA7266973D06E3374948F29F19EE867AB45618375EFA7F742",
-    "ldt_key": "FEECCB7CECB48FFCDD0D3D4790BE89A62A91075A163A6F1B7B49466CB35AE6B609477E9BF1A4D31490FCF05DD943A0AFB43BEA1544B34BB44DFB0186C16D7069",
-    "hmac_key": "6CECD33DDF47CE8D66262B62EA863F797F6C94913EC9D9554819A50B747B8EF5",
-    "adv_salt": "68DB",
-    "plaintext": "3DDD2DB717B7BE43A4A951C4E324D0EDAFE15EA6A22AAFC96C5ED2B8",
-    "ciphertext": "D0E16957DFEA62294CF7BDBBE2C05F84AE95FD83A9CECCAABCB2BBD4",
-    "metadata_key_hmac": "52AA5B97F04C146E774BD92B35486C348A862F4D56535BC5EA3162FD6E8BC740"
-  },
-  {
-    "key_seed": "B7CA1070E8A3BD952F7BE104D91A2C20F1D4796491DA3783264D50F6102395DC",
-    "ldt_key": "FE724B6F0520D7C3472AAA8F6BAB9B10BF640F8B63A6684829619C1A0AA756DA372F638696656BC72517D5236118C089E19550BBB24A3686A8BAD9EC141CA471",
-    "hmac_key": "9160364CC294664D46E19DE48143060DBCB5146A1F73287AB9491C2ED2A452AA",
-    "adv_salt": "360A",
-    "plaintext": "3C79AB767CAF65DC785FAAAC1FB38234A96DE51ABCD1676F5A09",
-    "ciphertext": "86084B5E0879225A3E37A306ACC94466E7CB29E83585514D08ED",
-    "metadata_key_hmac": "E98060247E9EB1B20EE37884C2BC30605927D75272AABCF543A728F0E31AE0ED"
-  },
-  {
-    "key_seed": "D646BCB4030742672C4DA69C33CCBE4CDDFFE6F7483D784887775C3E265F4455",
-    "ldt_key": "E0E59C6E1A3C0744C9E2C1B3EE23F04CBF1D858A53F933298F401506C5780D1AAA4874889137DAA63E53791785F64CEBA791C4BED505F86B039DCC533D8C1133",
-    "hmac_key": "7934811F8EA0F86509A008C524D2F824170A380A1C254B5F9411CC1524C4FC64",
-    "adv_salt": "4370",
-    "plaintext": "6D6AE7D53207545ACB68BF4821A8A3CF",
-    "ciphertext": "1A04AB81A47C7B24A17C8285197D9753",
-    "metadata_key_hmac": "57E703B5F1867619BDA051C9CC319F6EC7A9BC7FA9ADA0FB9BA495ABD0C5C62D"
-  },
-  {
-    "key_seed": "EA933E50C4F55A849B72E75DF0D0ED749E1BE736B7D17EB3F473C5D2FF8554D2",
-    "ldt_key": "63678EF10FA39BBEA18869759D3AE0CCC622DE4020BA28435072EF06B87F7B64B563369578741C7A7F0F867979EE5BA3B826FC5C2D4D7C3C53C2F006EE2BEF1B",
-    "hmac_key": "A57BB05DC2A606DF280DDE9F0C1BA63EC656BBC1C4D19BD25F0088B7E1AF7FC1",
-    "adv_salt": "6E21",
-    "plaintext": "3EFB804E7034A3D0B66E1E1A393E56DAB40700194C6443302486AD46DA",
-    "ciphertext": "BCAF0F9355A703378CDD60C269E13D2AA533AB6FD9CA7EE4188E58948D",
-    "metadata_key_hmac": "4639232C62E7507052B441900E23C8658CA36CF8630FF7C475275D169A187D64"
-  },
-  {
-    "key_seed": "FD29F3B6EFE434E7211AE135BCFB24CB4731D3C9AF5A55BB40B097AFF8F190DC",
-    "ldt_key": "4425DB4BF28AE5BF322D6493D0375532E54518F1A503A18D51F8D95DC72F554D8C0204486796210844002147E67F3F999CCB4B58BB571A7D53169F9C4A5E4833",
-    "hmac_key": "D2B8B7B5B2438D8ED08B353A68F1CA27B5EEB8A6911C143462669E478F9B680C",
-    "adv_salt": "65D2",
-    "plaintext": "612405FB954646B9323C76E75463A6F5544905D16B84E01522F4201FDC18",
-    "ciphertext": "F9F16E4FA6202A2CDAC267D6D93FEF1DBCB6BB1C0E9B7C0A6F41AE1C9399",
-    "metadata_key_hmac": "A8D924AA9E363E0B272599CA171866B521BEC333E7BF1EB3C67C2602347D52C2"
-  },
-  {
-    "key_seed": "CB46C2066E6C3FE16D1E8FEE5A0562E270F1783EFCF28D7EC8D77F53DCD0EDE3",
-    "ldt_key": "C5A1D7F25C2A6EDF97DC26D4052239AEBFA3A08B972F4DFE6A0A7122532D8785E0C96895E8ADB0FA61A02A4D967660EF7E4DBB635711118E3511F3D8D697DF41",
-    "hmac_key": "1D9C6580305F3669EAF24EA5A09D90A72C3F3AB9021A8D5426BF2CE50BAAB274",
-    "adv_salt": "D18E",
-    "plaintext": "3E74ACCD8A88A20FEA62EC733ED66A0ADF5268FBFC36D794D1A1DE4D",
-    "ciphertext": "52433BE7AD16FA9CB74F12E69BB34124BAEE9517458A6442E09D5752",
-    "metadata_key_hmac": "3316BF15CC1E4CDDD5EC092A88EF8154A6F43E725E71DC6ECB4A4CD27D98F678"
-  },
-  {
-    "key_seed": "D0AE77D632C989982994F49442A58EF470A5BC5C526D38A3CFEFD94F9D160687",
-    "ldt_key": "F378CBE4511806A8F0C519624DC553344BEDBBF61A474135B77BB9760F964C9DC33D63012A45D4A32F95979AAEA481A44C3F49D53DFAAE3849564F1AFC89FDC6",
-    "hmac_key": "B078A96B7F1A4D5134B97C4BF3D436835F97A213C8BEA22D25BC5E4D16B78FF4",
-    "adv_salt": "D68C",
-    "plaintext": "6350114B94F70699EED4692575378BC0849CA57F8E30C3A1F4DB185D",
-    "ciphertext": "54A9DD1E2C1314B9726F5A440F0A1C6361C479050138A687FE10893E",
-    "metadata_key_hmac": "0BB547D578F7239770769E46E894D3EE3913D4BD819AE9DD21FE40C64473E9B0"
-  },
-  {
-    "key_seed": "A65751FBC4AF24E2B9792263F001E304724EB37502F387F2EAA6A064DD7562EF",
-    "ldt_key": "A4F7E615D946BBE528FACBA0C2745BDCBBCD0A16B46F060A555E09AD48311049EDCAFECF1E1F5D8D126B59F18E328C4E49D2056A4BF207B5DD8CB195FAA441C9",
-    "hmac_key": "D5D089E402662DD1BA5592D0BBACB1996402B940E80D7504C5D6255A9A726BD4",
-    "adv_salt": "C61C",
-    "plaintext": "B4D35A9043A4C2F033731640321E6B7D1F66B39DD0177A4A0874",
-    "ciphertext": "B9D9F57EE1F23EB4B73EC949EA9EF5A51A3518F95D8917ABEA46",
-    "metadata_key_hmac": "B464EED3A47C4C83F1DF9271268C212B8528BAB1786C5F19E5EA0F4CCE9843C8"
-  },
-  {
-    "key_seed": "DC35AA8B956AB62C7369FE1421C6C1DCF8D4A89D5F4062770DBC2BC7DCB8A182",
-    "ldt_key": "98233C8B7F8AED0C76E1284EC42D7723A09D741E2547B23483C334CA7E264CD068EFAAB36345D28605A180959F5338C543E8CCA9B355CB8434957B102684831D",
-    "hmac_key": "909F9BE62D6A59D87216426BABE9AF57C7160356C9885989D1A42D02181B2404",
-    "adv_salt": "5558",
-    "plaintext": "AC1EF76E499D137EC9F3191E9840CAA5BE5F317F",
-    "ciphertext": "7C966901C5F317F736D64FC2A43E66EB5C49556A",
-    "metadata_key_hmac": "C8E966D170968D075D59803E71933C049211876D1342B8F77F3F6FCCE9FD972C"
-  },
-  {
-    "key_seed": "FCF4779A7A385AC0293B1B34D62755CEF4C5E88976E1A8840B3DA7D61A27D2DF",
-    "ldt_key": "DCB2889329666B6AFE271AF8EA534F360DB5D59A670C567B87810FA0B9FEEED9BBFE453B2D70AF34B8CA71609A0B58E91B9C3D9FA4EFB4749C236DBEEE50C0EF",
-    "hmac_key": "F6BDD41995C84BBA81C75B1C3996577523E671916B0943B89424370D2EC4D29E",
-    "adv_salt": "4E73",
-    "plaintext": "F6AA556C2F71E94D3F3DBC5AD7D1E2C27732797F",
-    "ciphertext": "D8D3F3A5B4589D0E034F2710A81E81610EB091D1",
-    "metadata_key_hmac": "DC3CA9D9424CC5CBE87972D1CE69BDFA77DA387CFD7158E07AA298CF245E2F3C"
-  },
-  {
-    "key_seed": "F66DF6536DC842DCB7213E4B2D3BC65E7F339C44678B3065B84BEEE6875AD04B",
-    "ldt_key": "15EDB42867D0B6E7BC3C46D3222EDBE502B9E720ADA1723BD1868D16BBD12CBEBDDD95CD9B9E2C840BE95FAAD953F538C36A698254AFFF92B3E067DFD3545B03",
-    "hmac_key": "179E7E7E3040AA184323F846E19E829DDB11DD6B47637BF49783150294826835",
-    "adv_salt": "F75C",
-    "plaintext": "277603CAE5EFBA74E8521303A0CE661F34F344C4C0B1439EDC",
-    "ciphertext": "9A5A44DB5332510C1B9A0B1311C052578A0AB5C4951AB472AC",
-    "metadata_key_hmac": "4CBDD38306BBA9056CBEC40715E52C2CC83EB76AE733E371206134A45145801E"
-  },
-  {
-    "key_seed": "FE1F1EAA8A3BBA8B446651897DFBA2005E2D8933DDE70E661D8958F858B1BE34",
-    "ldt_key": "E9D189401214FD24087347E714F91542FAB76A65A40C4584A21CE923F5755CF6F7EA5554C2CBD3BE29BF550171604D2E0D56B6FE2CE37C726B566E60D4888C39",
-    "hmac_key": "31A53453352877B96BB2124125315E6E0035522C2F44AA6FF1314D0EBC8F6615",
-    "adv_salt": "DDB3",
-    "plaintext": "0BFA0BD8D9D852DA66D1FA719B48E55EB333E05C77516CE2",
-    "ciphertext": "031274D8E91C52956C573E02F8ABBF7B581788D77246A66B",
-    "metadata_key_hmac": "A6DE740A9769FCDC59EE09FE542FB9DCF30B679BCF1E56499869AC1F8D71B837"
-  },
-  {
-    "key_seed": "A41B8237B5EFA85322C6B295F9A0FD01F573C66F51D11C559713638860FADA27",
-    "ldt_key": "9B7284B6D26A35D1823BF0CACC1761E9C7B77536142083ED2BAD82D7B7F90D3F2EDDA9755A4C7D94CA912FAAD3023B25A226361A6F6D807B68D21D2E3F172D95",
-    "hmac_key": "210AA4A83DAA8F44AAB45C2CF45C642FC941FF8F9671F031B71B304C2EE64352",
-    "adv_salt": "BFFE",
-    "plaintext": "17FECC5D1CA89D84B293DFACC18AF52620691B2073576EF20F64718D9F",
-    "ciphertext": "D897733DD7C03D8EAC294656243B333682AEA9EB9B9D4724DA96D95C7E",
-    "metadata_key_hmac": "B7EA46EA057FBF194C154D2A7119826BEFDB6966B10B2B8F11EBF25873265BA4"
-  },
-  {
-    "key_seed": "F21553FFD8917B76CF1A1E0C1812D5057B5BB37A420405BB13D61AF0D5DB0425",
-    "ldt_key": "89ED1A2E28EB0BABEF410471BCF76040F0837A54077C21BA1A818C5760C3B21E2983A3A433F36CABE5A35B32864E1CFD95871E5A2394EDD304A86414B5042209",
-    "hmac_key": "61A2C6FB83B556097116033B799DBB18FAF0AD9B258886B53089F7330EB49759",
-    "adv_salt": "4D4E",
-    "plaintext": "889397B6AC893582C8C35A266485DE160D1E68",
-    "ciphertext": "5B5297F79DB633F8A39006CA8A3236B4C19755",
-    "metadata_key_hmac": "EB33589BFB99F02DECFFCBF51DEEEBF44473831E9A5EB9BD4120E97944FFB85C"
-  },
-  {
-    "key_seed": "33576D6CDC4B4DDAD12F5B0F5E3128DB8F521FC4489F8035BF9183A40DB97D97",
-    "ldt_key": "64BC452BABF0E22A22A6D2BEA939FF1905408A62E2B2D4311CC2EA5BEE606620FC56AE1A72007E815985C1EC11D5867F2734728AEA3E7117596AD50274066224",
-    "hmac_key": "8CF9B706F42010EF66E436D4F52CF07CB27CC39B9F64FF908DBC54BE3715ADCA",
-    "adv_salt": "9F30",
-    "plaintext": "F979E2241D6DCE47148EE498B74915D3CF3CDB87",
-    "ciphertext": "97D6DE5213BF3F18730B4A416F1F5CA8A82F7922",
-    "metadata_key_hmac": "C5169086AC1732F10BCE0171F0E6B6123F2694B9A54BB877350F0C5FB26A481D"
-  },
-  {
-    "key_seed": "44455F60F506EE74FAF74E8DA03DBA97306DB0E54435E90DFB5417BC4AA48567",
-    "ldt_key": "A6FBF80E245F9FEBD5B33A69BC6F7DC5FB376882679F24C8E684BAAE86176921284B46C61A5CAFCC04E30BEBD4AFD88ADF41996F967E0AECCE79E3C81BED5867",
-    "hmac_key": "7F4AB7EAB331023CBDEE664A8C1427F7456D9B51D0D117D676A349F4E23FB33F",
-    "adv_salt": "B80B",
-    "plaintext": "13A2BDF201101D16B2DD1F67F8289BCB578AB83986C830B9",
-    "ciphertext": "CC7DCA27606BC196908DB7829D8569038A4160E5C703D5E7",
-    "metadata_key_hmac": "8E3B70B3C84F19A0FE164A78CFDF50ED4B49ECB053B1E7B41A8E3C3137748AEE"
-  },
-  {
-    "key_seed": "620F0497C2E7C0923E7027DB0950298C44279CE4462C9A9FFC4EB5C0C6F24E74",
-    "ldt_key": "FFE023195807235A48D2570021C73BC9C7AE961B7DFDA117EC4DE62FD6CE1CD184504B465CD06FC9684DB580590F778BCB6A856FE3DA1D000EC53BB0E93DC6BE",
-    "hmac_key": "F13668CBA73DC4606600C72FD40D0286A9DE8802DE566BB66712D393F4BB76FE",
-    "adv_salt": "5DA9",
-    "plaintext": "ACAEC0B0773EEA127EFDACAB13904A4CB23557936CDF",
-    "ciphertext": "FEC2B147A8EF9D8D72205DAA62B3551504D0E5E06EB5",
-    "metadata_key_hmac": "14303ADB2D45E088A0BC2A96E9FCE4FA01B6DE35E1B494BF402B93805A6BF620"
-  },
-  {
-    "key_seed": "1D5A56B6B5D8219556A5C5DD55EEA7C56E7A914960573963E78B7E36F8B49100",
-    "ldt_key": "CE0A78499619B86F9B0BC17D223A1B531837AAF1AFFF3650DEBA84C752FAE2656F690917688AA28B70E818022161E917726A439A39E52A97E4B6E097406993A6",
-    "hmac_key": "7056E12E07CD3F8A51A93D6E5A1A9A2907082A394CA39432B905DA0B48437C95",
-    "adv_salt": "3651",
-    "plaintext": "E416F4C78BB5FCB7761368B082E5E2395E3D85",
-    "ciphertext": "48A4172F05FB1FC537151EE4A1EE7F7F37B42E",
-    "metadata_key_hmac": "3E22CD89BF2D694F402A66D6799A087A03751A4E8AD3327B0EEE4E76E8C34BD9"
-  },
-  {
-    "key_seed": "343596F917C924E089066303E58CF2FBE4CA7B4293AB79A71EDC6026886BED5D",
-    "ldt_key": "A5A59E049A67C60802D0345E08BDE961601576B830F1178BA15B03F9DABA5A0019A25A5B146735FED83F4E9D3C01007D252F103B5C674D3307800094A6B6A3A3",
-    "hmac_key": "3CB01FD40F194BD832D4F28620FB529EB142C1F51BB88241CF2549B41E192ED0",
-    "adv_salt": "30F8",
-    "plaintext": "774A7ED68BF415F37C54EF0061A5EB9206DF10DE14A8418E2B2D7DC8719681",
-    "ciphertext": "DB8830CA6DF1F4BA6DC6718596A437F748D13A7494EAB1E7BC639562EEEBF8",
-    "metadata_key_hmac": "2C791696B7948BDA85EDC4F97D983709A349CB9FC8AF2ED45987908D06AA064B"
-  },
-  {
-    "key_seed": "338F7F5FE5DE001FCB7A5FF0D2A70DE2A3B3A3F7B41A4BBD8D73F87C2E73AB3A",
-    "ldt_key": "277B661A492F908F7EB506DA5E785FB3D422F484B6A7DF2646525774D5C766590F5F5E6DA8F7216A15173DD91CD81A0970B0C49B0DF5339AA44B24AD94D3C0AC",
-    "hmac_key": "691572560BB82F332962CFFA08E371EA0EA88F628883FA1A39332A5523093663",
-    "adv_salt": "78E8",
-    "plaintext": "D31824E9D4E9A438B561CEE0DABAC3500489C8EC01115A",
-    "ciphertext": "15634D0F2FE02F4DDAB92CCCE4C0F3C6941A99141EDE38",
-    "metadata_key_hmac": "8B6C181D751E8701ECEC1F37857BB77C4AA4A0DCE7751D9CA42F0E94AE4BB3CB"
-  },
-  {
-    "key_seed": "F84F90B1D13F2A09D13EEE467A2B9CE7CEB236962791FF106A9539E967CEB625",
-    "ldt_key": "1FE83F9813CF84AFEC67435B638815704799E4098EF7588FD4B0E14D9898256A1FEC424ED650BFBC0BA99C4604462450527DD46787A525A6213E41969F0945BF",
-    "hmac_key": "55330CA8832B36F9B17CC2BBBBFEADFBD78A4BCDD2D9778B31CB79CCA690AE5A",
-    "adv_salt": "BB5A",
-    "plaintext": "E828F91058D295E6BF37115766CE369F199897C6E2",
-    "ciphertext": "DB181452D79A03D1A31F94C3546917B494D88CD216",
-    "metadata_key_hmac": "719935E3EB1C9DA6C9235EC8A0091BE8FAD0486AC081C96C7A31E435E145AF8E"
-  },
-  {
-    "key_seed": "D289D134C02CDB24C28BE243556A71DF5718818A827780987FFEE928A7D5D1A7",
-    "ldt_key": "1DB0FE1F97D7AB111652C98B580F48F134B71B967B0E68D00CBA3EA5DF7EE0530C9C127A064847816757903145D99A5074EDD3C18144AA156958799FB11C517C",
-    "hmac_key": "8E61F0963EFEE787B02CE1C3A0E86015AA0D5CCC435310E6AD14BF1829E5AEF2",
-    "adv_salt": "6E0A",
-    "plaintext": "E87954CBCACFF97CE4CE2FA5F8384DA6AA2DD81A",
-    "ciphertext": "C8802475CF6EA2EA71F2A7C83313E3EE549A2CAA",
-    "metadata_key_hmac": "DFE63756EA5C4E9FF97A61ADDC0CFF0E07E9FC149F6274B80142155B614C2CC8"
-  },
-  {
-    "key_seed": "63A44B4A2D3E81D00195D1C006986E0330ECBDFC05D3CA70530B8EF99137C860",
-    "ldt_key": "7F88032C2CBBCCC894198ED3759E126B7809CB39F5116AFD63E3B4B83FAECB94B7D4688B25F73A6086E60BA550AD8053CCC70769BCAEDF1AB7CA112F46DA451F",
-    "hmac_key": "CD4875290560D8E8F54E972627CD77F63240CDA9D495AD74B93D117AAE33C127",
-    "adv_salt": "4C93",
-    "plaintext": "459A3EB639253EFAB1F14F7B24EC036732D0994521",
-    "ciphertext": "F10BACA9AD20E9525499E79ACD51B0B0CDF59C2D89",
-    "metadata_key_hmac": "44B771C56CA3CB5187D712AB8B033B049F7239A5D38EAB40F1F8E1CA066C7B20"
-  },
-  {
-    "key_seed": "0671E87D03B5207B3970DC43EB05C6E38D86BB9841010369529E2092C364C668",
-    "ldt_key": "7216ED7C5FB156A854E4243A84408F34D1E9368C9399240E8E7739AE1AEBA335790BD2DE3D2B8D29627D1264C3729EB365B8540D447D1BD46C4078CF244E8B99",
-    "hmac_key": "8B35F9DA6FA6DD84B646FFA132ED4A0380C9AB31A0504BAB483D6D1A96AFA42B",
-    "adv_salt": "6F62",
-    "plaintext": "CE6467A5DE9BECEC553CFCB1FBC314A5ECDB93ABA5",
-    "ciphertext": "A63ADD1C0C87D2D81424397E0FA7AEDC2520EE8142",
-    "metadata_key_hmac": "6AFC2447C731FADA03531F1289A776F2FFCBB238F6A35D238ABFEC74B6E14F05"
-  },
-  {
-    "key_seed": "06E1781EDD38E90809FCB33CE7CC689EBBAE0C68C6FC66D58D3D7D4ED23CDF9E",
-    "ldt_key": "CE2282288488EFFCC70EA72BB5942392433BD99F26B62A2CC7B98BF1685CBFB4B386D548007CDD3A071F2FA90944E59282B0D7D8744FD7F050CCDC2A789A3130",
-    "hmac_key": "82024C7FA981ABC82F4E59C1DF298DCB82DCF2676E8D05B26C14078B7AADF06C",
-    "adv_salt": "A351",
-    "plaintext": "CB9440C3CEF90BB45C89F16A8B33478775B3C2435D",
-    "ciphertext": "CA2B2069C89B7C4F24E5A9BFB5E91D02139FF188B3",
-    "metadata_key_hmac": "08D9897B9E08AD1FEE910AA11F91EF1035CEB8232B7CAA00A5D55A27E1500827"
-  },
-  {
-    "key_seed": "0C0C617B25804C4D513BABC7A4506CD66107181187213D6A9F229C13EE9B7055",
-    "ldt_key": "F36A44BBFB40B88A43C039FC994EF3011946D120CE95D214DB65858664FFEF3684C5D42688E85610F02DFF9A4541F5ACBB10F8FDD537BED40905F5586A4D4AB9",
-    "hmac_key": "1128F3C5F11E7E06B2812C3435DB3C6EBAA59CA91BAE4388DA10781E30A35AC9",
-    "adv_salt": "6138",
-    "plaintext": "04812107FCC326D0BC12C6A6F76C422C38",
-    "ciphertext": "EBC81562653E5B8E86CE2B66EC69FC88DE",
-    "metadata_key_hmac": "0561137A058B8DC63E13135E8A4F39CBA552C43245904CA500035369E7A7E7B1"
-  },
-  {
-    "key_seed": "4F5E4D56119949738BDB7DBDF8158494BC073A949BD78075AC84859446F0818C",
-    "ldt_key": "0371A5EC3C37174F96E063D948EA8C3AE6566DE2224DAB31494D24AF1DA81429B4CDBB9F90BC181052FACA6ED3FB594DC0D53497A8B550F8651F28DDCF8B5F83",
-    "hmac_key": "D8251CCC9C4DA0E6B31B22D5ACAA460175F4B8DDC52A8346B7D6D3BDDB9E7937",
-    "adv_salt": "78D7",
-    "plaintext": "C211D8FBDB541B6851BCA8738635696FFDBC3B50D449C4B961A6703E",
-    "ciphertext": "0B30410B1E7885E84BB5F1FF61A397D4A92EACD49770F6A44DA70370",
-    "metadata_key_hmac": "4290CEBEE66DE312DA9974ACFADEBE7587207D5F9F893F2DC33C6FA50BF600DC"
-  },
-  {
-    "key_seed": "0B3D2F948D597CDB11ADFC802E1DBA6827E0CCC4D6649CC459B1DB7C6CF976BB",
-    "ldt_key": "A520D05C8D03859B4DC4218BF7E61F86CF5B4116CC959833ED158F64B4199F5E85BB8AE4FB72F8E18F67F991A7555D264F7D109157E7CA089489857C481C969D",
-    "hmac_key": "831161162D181BB9D02877D4C8F048376B33EF281F7C2D3C57409106F68FA3BF",
-    "adv_salt": "EDBD",
-    "plaintext": "A78301B95D56985B6A81A69340E10AE872125BF8F76898E7",
-    "ciphertext": "FE052157D4AFA6B46B19CA1EBF1CD1F500A844BC8C165530",
-    "metadata_key_hmac": "021272FD20767E16F0222DBBA83562579F3F8A7A9A6B86D41C79C188D0F43086"
-  },
-  {
-    "key_seed": "1AC2CE1A2FA2564AB3A0002858EA9E27D6341B5CC64FD22DA6C6AA07326D4E47",
-    "ldt_key": "81DC06F871C1A1C60B9907F55D95582E7E48B82DA79C3006A9F2BC633976DB58B98121EF1E0E1CF82A78BFFB30FF80127542E90114AEB9955B908957B5F1A382",
-    "hmac_key": "761B180FCF4DF2479C797EAAE1836FDDA1B173BBBEA7129E0F2A8C4B812476A5",
-    "adv_salt": "3383",
-    "plaintext": "ADD5480A412C9377957B9B515B3AF735F5C18CA3",
-    "ciphertext": "67ACC73A744516A9FBF7BFBACB98CECFEEE00ECB",
-    "metadata_key_hmac": "562C4505EE7EFC1AEC79474AE31BFA9CCED521CA5CCE16039E8A259010ADB007"
-  },
-  {
-    "key_seed": "D158900DDFB6C7198932B9E90ED920ED579E61657CBD6FCA17AF281FD4391F6F",
-    "ldt_key": "C5D84CD86F7017CB8147BA271AF2FB950D15CAFB36AECBE9FC7C51B1B32593DBD51503FD5C65F9E6E469FA2F4446FFF3262C049CCD0551B68D730C358F725E56",
-    "hmac_key": "73F15E0895BBA3EF37191484FCA3D885A71566652E9E3BF6D81F19C493659BBE",
-    "adv_salt": "6875",
-    "plaintext": "E258A1888314766D42C8C0D3DE4024BF8F79",
-    "ciphertext": "61B327579A311B0284B9083639640049A4E8",
-    "metadata_key_hmac": "C445DEEE44EE21B8F567BF2367A3FC1956FB0F1106DEB7E05C50135260CE40D6"
-  },
-  {
-    "key_seed": "10A7EC5CC4EB65A498706ECB585F880F833E243757C7462FBE6819CA6771ADA7",
-    "ldt_key": "4DE3F63E1088B1FD84D5E86468FE11CC217756F1843E47D109C69C273FA0F59752E52B32AD57A73492AA7240B3F7DD934487FCFB3547DB1D8932373FD7F89EB6",
-    "hmac_key": "142FA8A07692490DBE1B9E4BE43A7025849FCF9E6653CD2318D743E4EB8F904A",
-    "adv_salt": "C61C",
-    "plaintext": "F417E53F7D6539C14D7F5F99FC440908FC6FE7E9168288456551967F5A",
-    "ciphertext": "D98725AA5980237FFF6975C31B40B07C4F9F12B89C30191EB404BF09C6",
-    "metadata_key_hmac": "F0867138E739CD593334B0704E493E33511070A60E071A94B441F4826E012182"
-  },
-  {
-    "key_seed": "98D42129CD845E7F14843B9D9CCED882989BAA2BC9A67038F1132A567D58A91E",
-    "ldt_key": "C229D7C951FF00E05F670BDC877FC9703D1DA5C8D9DACA5611CB86A449D5A0714C54C7D7F9D8AF2BCC42B352D11EA1FB12107674208E972A38DB1B34ECB2B1B0",
-    "hmac_key": "040D8A7A9AC888C04E097AB62CBBC0F0E5D69692F20F186C837D91683067CEFC",
-    "adv_salt": "6755",
-    "plaintext": "0501C4D75F74EC2BFD9EE99473C2339E649461",
-    "ciphertext": "F6615A5B104736383A242938A9704E19E0B621",
-    "metadata_key_hmac": "445BBC855FE10FF5B2CAE5F6416B7A6D698930EB9008FC54B4FA06F58FB2BEE1"
-  },
-  {
-    "key_seed": "78AFEDA040450B152F85D4510FC718A059D6CE23C0794109B9362125689DEC61",
-    "ldt_key": "3DC90A9717B472F082493CCE5760044401E6C0C32496863981112A9D1ADAEFDD79FD421CB11170CF12DC7F3EB7FB0B85018E8A32E7391D25773CC9DC88AFBE5A",
-    "hmac_key": "890C9AAC2820FF2720611ED90B7E3DC4663F07966005F56A6EA9D167CF965E0F",
-    "adv_salt": "B397",
-    "plaintext": "951FF0A7A682651DAFDF47C0E19E1E56955B3877984D5D842A84133B",
-    "ciphertext": "E4D79F17E817D3852E3F5E9FE41D020EFF984C39BB39AA7614664B2A",
-    "metadata_key_hmac": "791D3A04FF1B72A68E9229F4D7E6CE4E771B01759A07EA940AF17449C72FBFC3"
-  },
-  {
-    "key_seed": "3AAF11C94241718B0879EB66ABECB032147CD05E423EB3B534BC4E747605468A",
-    "ldt_key": "757B05A0E86B9C0CF6BD13836C31BF66EB53C13A009E0C808EED2A0AE9AB6C55E580CC712DBD0A54C33CD8BDFD6F7BFC45580F49A68971A23B534F3A35638ADE",
-    "hmac_key": "9462EB8DE0A29A937A227AA1345501A687810E321E0C0E7ED98D6AA80484E9D0",
-    "adv_salt": "7ACC",
-    "plaintext": "E4B0DD617B62A9F2F4367ECE4D70CAC7E11E5FE5CC3ED12083E9F4",
-    "ciphertext": "B43DEA72A65B4C0D8E5F815F4F1F8AD56E1FF69DBF53CB7565F1C9",
-    "metadata_key_hmac": "ABA6B185C97738E69AA2D439AAB09F111F9AD0B6625D8D9079873F9E5F1011E4"
-  },
-  {
-    "key_seed": "1254B4969EE1573866EF96A4DFD1CC7CC50086AA06567279ED31E753A7512FF8",
-    "ldt_key": "0B0F718654A1B737BDA6696B4D7E0E8F34E80A5492539A0D69461928E8FA6E0147C3139BB9D2EBD40E6BC24631EDB713332F83354B7C5EB585B15E2A31539603",
-    "hmac_key": "331571E071186C989950281590C97134B0665B379E67BA173945CDC657EC8B54",
-    "adv_salt": "3C44",
-    "plaintext": "F59080AB834F6E10D2F9C2CE3FCA3FBFAB35",
-    "ciphertext": "52EB13D24215021A96CA404C7DBEBF1285B6",
-    "metadata_key_hmac": "245D7F20A26FA4A49A92C9E8ABD9BCA76639817BB2DE8DF660228C3129312578"
-  },
-  {
-    "key_seed": "A09389BB1599359C1A54F852632749B2AE314FC097C91FE44B1667E66070543E",
-    "ldt_key": "04548B847DFBFEBE23CE95E67407F5BC30AA6BE449E75499FCA5939A1FAF68EF8B495F65A6E043A146A4737C4F2C95B286B2E3AFE5A12EB78D9B9B6E58E729B4",
-    "hmac_key": "0E52834761585DF6EF60F873232D74D72EBA8EB198BCE30B58FEFD63DFA7F758",
-    "adv_salt": "8D41",
-    "plaintext": "63D67EA2B4D8360069FC8935AB48C51703E018630B965022455810",
-    "ciphertext": "BFABDE39D3F06CEA3408E4CB6596700B3E649ADB1472B615C23800",
-    "metadata_key_hmac": "ADA13495722DB9288C7C082DE9D457E721E6722790590B7F5EEC53CA33807657"
-  },
-  {
-    "key_seed": "CD9576EE16DA0AECD0BFD036353AA99F4F379E2F439230F38AE70E21A41E9822",
-    "ldt_key": "5FAD20FB31538ADA9C445FC4D441D372902C0E1A84F589B728153AF13C19364F3F8B593FE6C2EC50F6B4FACF08DAD3CCA85B4EF8BF6B73A35BB09F4F9D1F2A8B",
-    "hmac_key": "820F0F0B8B99D29BA6555703C39171A5CDF99EDFD1014289CF345EDFDD0FA8CC",
-    "adv_salt": "361B",
-    "plaintext": "8A4B3F4778C90DEA493EB2595393660C2A9A592D9635942FF0862D4E4FB0",
-    "ciphertext": "2CBC9C9F19C243B20CFDD57A09C277DF84E939F7ED2AB63642BE7B68D5BC",
-    "metadata_key_hmac": "F46A16718E7ADD5B1D72512F477D2CCA606E06588898524EBFE7E1B699DBA7F5"
-  },
-  {
-    "key_seed": "5A8687465EEF1596E3780067B4583A7FC4C4F260CEE84AAA0BB5DD9293D6CA23",
-    "ldt_key": "16A5125C65E844C3610DE38CF2E233121E6DBA36B33D3093AC737D6CEFF59C4BF998830E8FD0C52763AC09E80CE91CD8B5DF977182EAD82FC02A3BF00582936A",
-    "hmac_key": "4AFAD4D6D875B1FD17EC0270AD720842BDDE4BE82DCE2AF4B8FEE608FB1E823B",
-    "adv_salt": "DC49",
-    "plaintext": "30EA802B38189A793EC9A0556C8120BDB323EA2AF3AA441C1B5BF3",
-    "ciphertext": "B7977567D79D5668792212E0F1C4C4337AB9B354A17442E424EC61",
-    "metadata_key_hmac": "8363FC0C8CF602B0F0880C305E5B6D59E056D9F4B62C523A0A892F6A652F8DA5"
-  },
-  {
-    "key_seed": "1847DB43A3705D2EC3D22784612C77AF2C90E8C562FDCA4E1D0608D7FAF59044",
-    "ldt_key": "DCEFE1116D69A3A14EF00163297A61D9E5CFDD7DCBC57A918C5D34695D6BBDECBD5860126BE689FF17653F6A78D1BBCE50FC2452090D5680DD90B3EB8A168DFA",
-    "hmac_key": "4D2671B9F1CDC2688D2CAC6C713BD9B8D4BFAED2FA9C1C86CD0C1C89FFD07B0A",
-    "adv_salt": "6600",
-    "plaintext": "E0B7648B869F159FA32E7AFAA73B7FD2F8756B3773C0651DCBD9F0131555F1",
-    "ciphertext": "AB5944D92E32F9315CBACE0DEF010AC041228712636D06BA88573E189050A8",
-    "metadata_key_hmac": "1AC08918FDF79487DA33EA74523CDD0E31CA5D50657F75EB87BBCB5D44A51C18"
-  },
-  {
-    "key_seed": "8FD04A9A906A7FB6747A682C0D86EFA3DC353148C038470AA201D009945428B0",
-    "ldt_key": "6ABD509CC4CFD4899479DA3FD50F948941E6681943AB9A7D4B348787215809A1B5F38DA19DB1ECA132AF464D550703A3F21FB5DC67925CECCCEBCBED8DCD4352",
-    "hmac_key": "59E7493AF6F7D9EC5A5A18B02DE1B69FA49BB4955D871FCB20D6B840E8E65FE7",
-    "adv_salt": "E344",
-    "plaintext": "FD465A3957F9186D57E9622CD6901A3B1CEEA86639BA01240186679C2160",
-    "ciphertext": "D198B506ED55D44CE3B2B06F564EB41577E4A21BA56A4581897AA41E41D1",
-    "metadata_key_hmac": "E17EBE7EA3E31442C9F77484B2412B93B21035A09D2CF2B7D302F5595B4C180E"
-  },
-  {
-    "key_seed": "5D6901027D2CD532A9CCA83544CA250CE4FE48F6D2ED656D5F53E644693AF48C",
-    "ldt_key": "23C4450CAB72981CDEF0AF4DE884B96F2CF0410072278DDFE17076CCEEACDA62017F3C57087C30F3D135CF7A7BFC07310B6EAF35266C3713E324C1A4503C3962",
-    "hmac_key": "1F71218394C4A14E4E0DC8F35845618BCF661E43F7D94CD65649BFD7ED7F7058",
-    "adv_salt": "3FEC",
-    "plaintext": "055458BDB0922E3498E2D29BA1915BA2FD9E44BF0AF5E42ACF10D03263D42F",
-    "ciphertext": "63A18209632293B8FDCEBA574565385C14AA076082D46C1D5465553CA97191",
-    "metadata_key_hmac": "100E1E9585B0B63E231CC9CFEBAD3844F091F8B43B257984B807879BD84E036D"
-  },
-  {
-    "key_seed": "EFE159ED6B66C394C457F074EA67F073BB21D9AE07CF83C160ED0C1A94F6C1AF",
-    "ldt_key": "CDF76D8FF8E3F9C6E978067C0F4C431B394897682721234DAD7080AA095118CE1C39BCDBDB326E015B58CFB152DA9EA5FE064654DB8CC66EEF2ECE21491F7D94",
-    "hmac_key": "3DE7A2AA19F99374C637DA91F16046168905BF370E9E359C8338D979D84AC736",
-    "adv_salt": "0633",
-    "plaintext": "7BE0CFB84C85FC14073BD06D8969CDEF193A6CC5B7E799",
-    "ciphertext": "B03C781FE475220B173AB01853493C8ADD344868399711",
-    "metadata_key_hmac": "C6D156F2D028C16472AA9EECF256DB57494DCAB97399F56F9EF30ED1809F994B"
-  },
-  {
-    "key_seed": "A312E637DA32C34019ECF3417F6AAF45152A22EEA52E1B151616167C1B774F19",
-    "ldt_key": "9AB49588EBF4E929ACFDDFE2E8FC1AC12A7BC31AF2ABB44BF03B7070E19B32A39B41BF04F1CED0895F5FD3C7FD4BE5DA33B90AEA97EA0591C6798E56D4AE6332",
-    "hmac_key": "542269456FA4FF4D150008518B032F9C7D7864E907D88F367F85624C29BA1C4C",
-    "adv_salt": "4356",
-    "plaintext": "9D48B29E18CE314B8603581923ACC0228057",
-    "ciphertext": "F577503F99D7D42623A6064383DCAEC22AC4",
-    "metadata_key_hmac": "1A44E9E237457DC3D3A093EF67BB2B915A10F2A7A22386EF05B27FAF1005DBEB"
-  },
-  {
-    "key_seed": "E8D47D10575C5D1E3630D342626BE5CE6812B2E5B671323CFF42FE76AD265F28",
-    "ldt_key": "32F9CC9C4712DAB732229F54E0F2286B1EE5E10E9A19EC266C73773C84E9E86AEFEBE870E255F24964190D8FE49E90DB238FDBBDE525F9F28D7838490778172C",
-    "hmac_key": "8415654B8B4205F056B2C189A280627EE5F908434495906809CC4D3C06046E24",
-    "adv_salt": "E5C3",
-    "plaintext": "F816AA6A681EBD851CE3BC4A56F93AA384132A42DCA7C04DF1731490C90B",
-    "ciphertext": "2DC2F095CEB06C6E3081EC159FE13E97EE667857C37A21789E8714948C8B",
-    "metadata_key_hmac": "E3E8881DFAA94F0F305D1F43374FC455C7BEDAD8B5BA797752FAE7BD08A14E3A"
-  },
-  {
-    "key_seed": "4DF9A725DE24A1164A0627D2BE4BA6B8F3A77E47A635995A34089A5AEC6877B6",
-    "ldt_key": "3A582C64633FEEF62D3CB71D403835CDA34FC5D3B41DB2370AE9082A0146B1E2300AB9517356DF91AD544E70951C2F16C5E0FE06CC1702FFF6C9709232DCF569",
-    "hmac_key": "EDCC46D1F0A60F78E7F6EAF19B142A5AFE9F01C3F9A830CD2795D2246F93FF09",
-    "adv_salt": "F806",
-    "plaintext": "BC751733EB97C2E703EEF578FAEF3C437AFB4A434324DD1857",
-    "ciphertext": "4AC979B439952949FBA52435B0598AC3BCFFF7B07D33CDBE20",
-    "metadata_key_hmac": "21DF4EAABDC368D32D1AB8A0AFC56B6752CFDCF17A9DBBE4AF7C96C3BD7A6871"
-  },
-  {
-    "key_seed": "01510C4FE0D9308543FDF8B5CD9A01C60A671A408F9D3E0F844799139A7C8DBE",
-    "ldt_key": "08E67910642AA0B2EE1610F9B5EB7061FF8C7F1A94C61261C3D9A558C5E736A79F14381001657B97236B98BDA66DE7D914DDA4C860AD0E03439601A27D77FB34",
-    "hmac_key": "0B94AB4C41BD67AE18A13963A62294197F17C6047FA8708DCD75900E26836CAC",
-    "adv_salt": "CD77",
-    "plaintext": "25278F8BD0FAEEBB70FD18F0A594F887C2CABF4A7A5533BF14E66042AE",
-    "ciphertext": "87008C6E8EBB8215749717DA1F266528E83AF6FFE1E938FDB9B10BAC25",
-    "metadata_key_hmac": "E14A9EF23DE4C90F7F3910B069A20549BFDF1E2FD351697C7488A0243FD04DAE"
-  },
-  {
-    "key_seed": "03426AC28AB41BFB34BFC4395DB65D34CE1592C6637049BEBF285FCF6D4D752D",
-    "ldt_key": "F864AB194A8EEDAA86733A530CB239A2C6C11B1EB86F18A9F7340C52BFE1D5D3D479103DAA18A830718029961012FF2822572FB2A38661B9EDEEB5531F1E438C",
-    "hmac_key": "55F499D3D8EBEBA2D5849D86155D4EEA9010589013DD791109687EE09674539E",
-    "adv_salt": "34F3",
-    "plaintext": "D3DC2B17B56AF59D3CF9201681FBC37240E6",
-    "ciphertext": "320625C1CE2D1345E9A5988E837DB38E690B",
-    "metadata_key_hmac": "62F4232C63E05843ADF561D5FD72D3F9EB0F0A00AC94EAF67515271619F03C1B"
-  },
-  {
-    "key_seed": "08C01D4D30CDDBB5201C54E1646702BA1FED8767C690D8C312EC05F961202393",
-    "ldt_key": "117ADD7CB116BB4AD7C1873E41E43BBE115F47F9A089EF5DFDDF1D30EDA4D7397A8D86DBE1C21E2245A79F9E91CDA68F5EFF8CE68C18DDEF808CC67061709B27",
-    "hmac_key": "4650BF6DC8969E8D8878ECA7559F54DF24B3A4980E45B10F2B40DF56E18374FA",
-    "adv_salt": "3438",
-    "plaintext": "3FA70D0FBBD749AD720DF7624F209E1985678AEA951A3C640C62215813A633",
-    "ciphertext": "A732659057D8FD106AED3EBF8DCE945D16D681FDE67574532AA55B8F020BDD",
-    "metadata_key_hmac": "2EBAEBFA7F0EBB889F269F00BBB08973D13C07D1C4BB233245272651BFEFCDFF"
-  },
-  {
-    "key_seed": "294BDD21742BD87E8C031D4459C3C4D752A38893AAD3EA6589BD23D2572E6007",
-    "ldt_key": "FEF9E32525787F8E8D3F6823E924272F74E7A7DD9803324A553C2DB542FEFEFF828760512C5336ADAAC4B78708E8A7ECD5329430947D7172DC39B1FFFD173C50",
-    "hmac_key": "A72984997949D2A31D511BE11D857B39E983A58FF8D8CACF758D646CE1EA9D4B",
-    "adv_salt": "3893",
-    "plaintext": "E7C5D221E528F0A4076BE5F703B349804BD288A458B8B1FAB1D249",
-    "ciphertext": "41E4F42E2928990BC1AC8D5F286A6B841D9CBD0C46CB0DC8F4386F",
-    "metadata_key_hmac": "4D7718EA2A03065A867305D6D5608B295415953F7E051FE5BBC4CF386640AE3E"
-  },
-  {
-    "key_seed": "BAD140BD7F5A746587950B769BEE2B86C001A2541DEAF6A8BE3BD89037624187",
-    "ldt_key": "618EEDD9A044D8FF3ADF7E749B7C0212A6B412B617FD9243C13CD4AB1817165CE9994745CC4AFA37DB98896FEB0068C18E126E9A36D6555BD33F5F409AE64C0D",
-    "hmac_key": "052BAB530B00EE214BB87D3A7A46470D373749AA78BAFEF1AD2A64B0C8728C94",
-    "adv_salt": "1CDF",
-    "plaintext": "74B1AB802BFE75EF1F1AC4FB8AF7DDC0741D6AB3140F4768",
-    "ciphertext": "F51E89A9AE7D347B2CC08174D12B2B07247A40098348D657",
-    "metadata_key_hmac": "AC6B3D12A30402663791B9CEA541823F99EAC95F21F2D495755FFDBA8F43A5D9"
-  },
-  {
-    "key_seed": "AB936D1740465539A92961132253ABDE638C304135D1588F8157215CB291120A",
-    "ldt_key": "DB2DAEC702ED084B1FCA7D9F4FEB0F140F12B22F5C194BAF069E884296188D627E1C5C798C9F38F0AA9F9999FB95F6DA875CF49BD07AD9365F4D121C27873245",
-    "hmac_key": "0BB72516DE709C6403299B1800FFD09AA4B013A3948550654615BAE08A96CCAC",
-    "adv_salt": "737D",
-    "plaintext": "97BD94EC0E4C326BDB76403DAC23921D77",
-    "ciphertext": "2E5C007FB9E59BAA4A6A538D32B5E9246F",
-    "metadata_key_hmac": "48541FA93EEEFEBD8E36969957F326C1EBF9DA3232FD4288C144141D3F6876AF"
-  },
-  {
-    "key_seed": "3E7487C215462E75514BAAA0C14A4DD71D6E2DA001A31BD8A3D9FE5C6D962195",
-    "ldt_key": "6D7359744B5D4082F100A636F1E6FBBD0329913360B9947114B623E84F2C221BCE4D6219803FDDF3FCEE9EBA44C4630B852715671543AB241F497B2CC8C53291",
-    "hmac_key": "CBD41B6071A3C06581C1B5B3EF442DDF6D1591674AC97A39695B27FE296FD389",
-    "adv_salt": "7FCD",
-    "plaintext": "E03882ECE4B6F113344658F383B03E441B57D33C9BA1E8146CBBA3",
-    "ciphertext": "221C55F242986552EBD1058327D0DEB7032654057212C386FEDDBC",
-    "metadata_key_hmac": "6CC53357EB70F5C56C1E856EE439F1583D41B7037FCB072B041E2682102E089A"
-  },
-  {
-    "key_seed": "E9722D366DE0F0A0F7A11228E12434FEEC5A203EA4947555DDA1911A3EA0111B",
-    "ldt_key": "A8B8C662D022A5ADB541CFAC69088998CC8101DAB078DCE30FB2AA5E2DDC7CFFEFFA0FB331925044397FD0C2C6147C6FB21CFC0D64295D43000FDD7CF6B1EA40",
-    "hmac_key": "470953916FA2FA9EBB4DAEF62AA7ED658B03D25BFE529341F7B78204207CAE4D",
-    "adv_salt": "B39F",
-    "plaintext": "E918E8CC19B02D63DB53134FBD127C6255428B",
-    "ciphertext": "ED7299FFFA71919FD7B15DA880B83C3442A087",
-    "metadata_key_hmac": "A1D94715CEA145116325103C52F8A8D316FE51183BDBB132884B1F552320D96A"
-  },
-  {
-    "key_seed": "657F721007F3A48224D51CABF73C85B9CBD0A2F029DFFE519634577AF244D28B",
-    "ldt_key": "D5DCEDF527E99FD41790C0F3DE8AE36FCE74B789AE79A90F72C9D842CBE36F895DB93B0B2196FEF8B199C1622C6CB24BB5CE19DB55A0AF1E481E0B0315C0A2A9",
-    "hmac_key": "F8A9D5E1C4B5AA2EA3841FEF56AC0C7B78E9D573B463EF8B073BF68304C144E5",
-    "adv_salt": "0FC4",
-    "plaintext": "B2DC5FE7537BF4D0EB7325E4835C65A8A35A4AD9F0AA933688D0DC83",
-    "ciphertext": "D6187B040563C6307420E3B13DE6994962EB52974C82120A015E8CA4",
-    "metadata_key_hmac": "7CF960B3272CDF434605907361BC2B50570AF1D6E055989F41C957A806339BB5"
-  },
-  {
-    "key_seed": "292C5A123A43D0F42E90127B105318404DA371C996C4A17F5DD6C30FF7958406",
-    "ldt_key": "987A64406EDBBC011AE42F1A3D9DE509CA4EBF10143F3D5279F096E151C43A11BAF20B7580811345D49B9B9A5E5E2CD726D72C9B0BA5222E1E265CD5E1BF113A",
-    "hmac_key": "CF47390013455461037394AC76EC721743D13D853F3E9E44EF7B1D664B3AD5E9",
-    "adv_salt": "B9DA",
-    "plaintext": "525225673F0CC5E39A71CD89221DE13313BEA7",
-    "ciphertext": "06CA5248993A16EDB9B2B17043828FBBF10B44",
-    "metadata_key_hmac": "A3F213031DF32074A829B32C53CB7A518BCCB41A77FF2568083C992F457A727A"
-  },
-  {
-    "key_seed": "63FC259C4DACC377FFA83C5338B5BB5F64CBADEBA56424668124DB5CBD0BDBFE",
-    "ldt_key": "FE993C17559AB863ABDA0768EC49C18C5465257D8F13DDC39FDEEBC35F3B4359C374C8A011ED0985FD61B7179C89E3E91A04BE61E936B311FD783B81C8D068BB",
-    "hmac_key": "63ACA66A9AB40DC5F040B85709DCCBB1F88841854F6738EA597F14A5D7EB0D0E",
-    "adv_salt": "4054",
-    "plaintext": "6A90C5445CB4D7F424B53CE0A5655C84B39BA050B00CC6",
-    "ciphertext": "AA64031B3260E182AE8B0DB2E98460586C913DAE30EBE2",
-    "metadata_key_hmac": "C192FE644E52388C10DB86D8149FE52A4612C3B735FE6E52BB1042DAFD589C1D"
-  },
-  {
-    "key_seed": "A10EA2C864E46ABF4668DDF75213767674DF97C6556DDE65BEAB28329167A460",
-    "ldt_key": "326548F1A422A58F79E50DF5F0706115943029F567271583F6155FE2F3380674BE9EECDA0F5DF4817FFB17BF1F6D93B2CCCF61339D722326B3400540367DCE69",
-    "hmac_key": "9D34F06F4423EC5CA77EF649A9A1FD7BE435421AFCC624D23374D273E8DEE70B",
-    "adv_salt": "7356",
-    "plaintext": "0B6B2A17907494F0BD1929ABD84A0D63970A",
-    "ciphertext": "38F245C019AD4DDCEF3985E17B3AA6F5CC23",
-    "metadata_key_hmac": "C77ED847CF889ABAD73E5DE31D839E11F61B53D74DD830C522D7703DE9C935F6"
-  },
-  {
-    "key_seed": "3A1128D12AE1D86EC7E38F7734D21A53DF90CAFC0F77D9E4FB9935531D8E092E",
-    "ldt_key": "85F796AD7EA67077E4F37DD1E6674095E2520F678EF3C66ABFBA4A934B071694FE101384B7293CB5E0DC9DDAB6140D15BD0556A369780E4EB78D9D5F899177B5",
-    "hmac_key": "BD3105688CD274A3D32733856360ADD3D0FDF9F8EE3A87AEC93361A0FF9C5BFD",
-    "adv_salt": "F5F6",
-    "plaintext": "BB91A063B68707B5EB408A9456FF0B70DAE42FB71D",
-    "ciphertext": "7EF28CC2CB0FB35502225B878B7ED0FD3964139FF6",
-    "metadata_key_hmac": "E81803DCAADCD2CF4594E3E70E1423F89BAE8ED72C225E9864F784E6E511E1DA"
-  },
-  {
-    "key_seed": "99B92F70AC9546883FC032D413C7BA43251F5B1197157029158A273090AE4AF9",
-    "ldt_key": "081F9E2C943245B9A217B1317B077BB702C54AFEC867EDE7B69DFC4426A1F70F4AFB90DD095121CB41C3FF3DF39E7D74778DF51002651B9E7231FC47BBDE10A0",
-    "hmac_key": "2013269352BA181A1A8342DABA04615833B4EA47A1B7ED2E299CB4BFCD2C1C38",
-    "adv_salt": "7750",
-    "plaintext": "A9AA97C43D57268766AEE2E3B82CC3FE3F",
-    "ciphertext": "46CE4D6A84BD43EA523403CD81F7299E2B",
-    "metadata_key_hmac": "BC1AC137EA800966894058D6A4C1A3E68012A1B1D9AE67F035886E6E25A557E6"
-  },
-  {
-    "key_seed": "D9B623A3217BC5B9CB0095102D02E35F06EB9386E3AE563F6011D1300A27F009",
-    "ldt_key": "48E04C7769AD29A2D9F6CC9172E73ECBD0D9B8C21F1CFDD34D261C8F06AFCE890E36F9CC9CB7E4BBAD6FCAC8A6CD4F09D366E4E3D7AE0D6BDE26F8D0CED7EFB3",
-    "hmac_key": "3A9009BAF19D54985BC734A2F8684EC06E043EC6B747138C3FF1B96C78C3482F",
-    "adv_salt": "69B8",
-    "plaintext": "DBA1FAA844FA6CF0CDAB8EFB15EA792CEC60",
-    "ciphertext": "6762F4041339250AC4228C15E2564067A7C7",
-    "metadata_key_hmac": "82D5B0A87195734D9492C38C51CE1FF8A7C957FC69402E2EDC7F88B8094312FF"
-  },
-  {
-    "key_seed": "8540610609338C353920785A748CC82CE4BEA79838267C85E7A775B61EEC8268",
-    "ldt_key": "84E1F7A0A6B332F9E0F1540322B455C4C04A4E2346CD5F807A3A4A195F609440E33525F239E297F5768AAA589E78517C94EE9418EAC35FD51DBDE9B70866623B",
-    "hmac_key": "CA5EC32CB4FD314378C6BD4B101EA860674D00829E41D9F8388F064B894B90D9",
-    "adv_salt": "F555",
-    "plaintext": "F01F7ECB9DB990418271EAD6517B69D240F618291CA7E83A5E",
-    "ciphertext": "BCBD94D94F3A94E231419E5918131288D0E267806A8ED1C618",
-    "metadata_key_hmac": "7FAEF781D5087647F63CEF2161EE710F829CF9A6C3A3250F459BEA218921D4D6"
-  },
-  {
-    "key_seed": "AE5859A3A592717E93B8718FD1356F25E1A37BE52EAFD8F19D5F01D72C8D5D01",
-    "ldt_key": "2A2496A1E26EF4AD9BBCBA362C6E76F6ECFAAE36411F8C1EFB75ACA61475E52FBC6109980CFC640F1D15E1A6DC3185D72129D802099E8A435995176B20C8DCBE",
-    "hmac_key": "5EFD065599480E3679211A8E9C6176CAE7ECF6464E80FDC9E69F484698F22856",
-    "adv_salt": "F258",
-    "plaintext": "DE7BA7D89F79FBA0F385E83EA06904401369432F116A52C65012",
-    "ciphertext": "8B338B47491186F3D6E22D54D9D170BA86C7726818D37B6ADB4D",
-    "metadata_key_hmac": "D9490422CE5E49F346A0DD3D0F747D7CE2136765D835FDCF36B3F535D76A3985"
-  },
-  {
-    "key_seed": "92B9F22F44ECE464199FF2C3AAEDAFF3B7EBF1A25640216C4DC12701DAD31EA6",
-    "ldt_key": "33D565EEC55600910B9ACCC0D577CB676CE3B32172F6A4001B3E0D5FCD041B6368BD650193E3FA4BC012522B98B4ADE3F450AB6CB8DE6DC255B437CAA2039D9A",
-    "hmac_key": "47E3A833B777CBBD0435B64ACD5EBCD8180477E5865E23F7FB891FB9E9A4A9AD",
-    "adv_salt": "C9BA",
-    "plaintext": "62D57AE83E64A520E50F344F561DB4557331B60CC6F155E2",
-    "ciphertext": "6AFB293C8CD05172ECB9BFC1E8BC2D042E29EFAD51E32B82",
-    "metadata_key_hmac": "084E75DA1BF443CE36D659618F3169F76D105BF3EDD2D439CFE0D0066E3EF39D"
-  },
-  {
-    "key_seed": "73DEED4CC7C9842A0DE8DACC70E8B4A556AAD079E19DCAD4F1A4EC0D844C4B9E",
-    "ldt_key": "86FCDF0D697B181D82FFF10DC9E1323A96A4DB937477D863AA23E29B1E4C01450C5585B70F91EFE9793DBCD88144212C3F8BD001AD70B8E526DDFBC5B4C7AB2E",
-    "hmac_key": "91B8956841DAC3C63EE9F0F1EDAB92B3337562E47E94B22EE9096EEBFB280ED0",
-    "adv_salt": "0399",
-    "plaintext": "C2940DD38E71C8AD0A2E03FFF6E36FBA",
-    "ciphertext": "A51BAC7F36F58AE6B17D4F029C6F7D14",
-    "metadata_key_hmac": "100599892D986891734D80390B9CBE2B65C7CC351BDB90C35B57065950A140B0"
-  },
-  {
-    "key_seed": "D47D3EB118FC6310C7A9526DABCB0E4BA49588B357A98A8883142B23F0B47A3B",
-    "ldt_key": "E0176A395D1ECE82B324D23D7F2EDFFD563E36B488806E7F62F28EE11F19709DDF8E2D83AE184EF30C0551ABD99FF30DC2998B6637EE98F9DDF0824D20365719",
-    "hmac_key": "4C6C873F49F28AA342263ED3FCF9792815B4E08856AFF70BC4EB78E93AE950D9",
-    "adv_salt": "6350",
-    "plaintext": "B109D1014AE5F24A85D1DCEAD867672274EA7D6F6113C811696360",
-    "ciphertext": "3EAB6A7C151FA3D7FF92C2FDB036431CE042E8B4827C609CB60646",
-    "metadata_key_hmac": "1E0A2A08F07E766FEF889ACABA277D1241E5D745C903E56E97C6EAE449BD224A"
-  },
-  {
-    "key_seed": "1B372B508A4B3F1F970F861193609B4EB3FD65C62A171A43731E9BB7BAA9F878",
-    "ldt_key": "B304027A3E70434D7152342FED8164365CB874FD0EA9D4D9DB8B0571F876F4FEBC9FC13C2884FD76C8B5F1DDF9FE6DE9D98060ACC1273889B38C54B018C8949C",
-    "hmac_key": "426D38B9AEDB2D80F2C4B3037E5E1BB5E676ABB286CCC1174C7A0135976C3C2D",
-    "adv_salt": "A926",
-    "plaintext": "EB7A7A86C2F103A2C960D1CDC0BB3704370A449D60C93E41A0C4D88C534E",
-    "ciphertext": "51679BE7B8F588BCA4F7E48F9B27C6F8E97AAFFE76BCB0CBFE4BF198D760",
-    "metadata_key_hmac": "0DA1E0F3DECB3D5440CEE3D0423C3580A0C1606F98DD6F3C3978499D1797C9F6"
-  },
-  {
-    "key_seed": "754493F863A897595961A3764FD8BAD6E1E84E9FDA15246A47C4D133327B4C5F",
-    "ldt_key": "CDA99F0F898B4B266AE899861CEDBA5BD129EB23ADF3F177135E0FAE52B0010C209315C69A478B6B44140C0FC9BCF375EE292A9808C70FF49E87A2EA7DD6CBFD",
-    "hmac_key": "622E68EB5F4E2E992EB30EB45E3B35EF5E6DA45E529794501A272F0EDFA6E0AD",
-    "adv_salt": "A07C",
-    "plaintext": "F66887B63134A8B3E2C428F967FC28A40E2665187B9EC0F8B9",
-    "ciphertext": "B218E6D728D9DECB32790D32A5FFF66381A3BAA9F214D14F5F",
-    "metadata_key_hmac": "5D95EE76CCC49B67BBAB40F420D7A2ED2F59861FC2004674083BFAEE8EF09440"
-  },
-  {
-    "key_seed": "A267D3FE83CBD62F70D1BEC104E66E1999C73FE878E1DCA48D532B15887ABFDF",
-    "ldt_key": "39871DC7FA5144E3D0861B152DC43E8893B4B4AF88014275BD7BB64EA92B299F160623FD0A4618EB22ADBFDC1F99FEB9C7F967290D6894FCE4DEFC7B243ADF1A",
-    "hmac_key": "C58AA858D6926937696EABAC03D17A1BB7D9E6ED80ADA6EA25F7657E6AED0794",
-    "adv_salt": "F942",
-    "plaintext": "4FBCF881713DCEA28BF500425DC5A8592AA03FF50E17",
-    "ciphertext": "FD7E72EAF67E73448A42BCE1E55136145FBD5AB6A6B1",
-    "metadata_key_hmac": "5B0F55FEFBB282687AB7BC04E51CDC5EAD2FDF944DAF9238CFD38A2519C51D59"
-  },
-  {
-    "key_seed": "7590A9B827A09DD6BE252F2456F7700EA95BD03E3FBE909AC606AB6976EA5DBD",
-    "ldt_key": "8D9B9E904A63EDE22683DA49C27695D10B84CCED446D27D817F50E5CB1D2A708AFE98551E9CB92D9C452E7EB2393B2DDD0327D24AE46024D2DD68EAF5A321B5C",
-    "hmac_key": "91D3FE5D8A75F5A1EDD3D6DE51B8C0CAF0E6E579A6403C38C553F73710643AF3",
-    "adv_salt": "F8B1",
-    "plaintext": "54E53C14D0A4A72F6138A07CB9185C09B822A280",
-    "ciphertext": "64B55EB264C13E34ED9B0D296A3F85884C18162A",
-    "metadata_key_hmac": "33CD2E0D9FD1C0304E3EE973011B937EEEFA87C9C2BF2F138D18FEB1B5A54516"
-  },
-  {
-    "key_seed": "89D2FD1C7D9BE6B6BC0EDBA1DB9C6C3F5E53BB141D22E0AF1FB2484D1E9DED73",
-    "ldt_key": "BDA29601D9DB432AFEFD93B67F3867C9AA31C7CE75362684D1E6029EAAB2F4DC48812293970CC287241F3571BE3E3FE391163A0A2989CEC271ED16AAEE45B07D",
-    "hmac_key": "0EC5E31F828B81589454F5D166242548AAB88BC252F5E3DDC283C667400ADD88",
-    "adv_salt": "89EE",
-    "plaintext": "89AD1A1A9D7FE6BD87BAE793FE161DA4004ED63DA608EC4EFD5E4F13955C17",
-    "ciphertext": "70167D50FB6DD67C974857C23FA09168F9F984B0C36F91066181E4247EE5FC",
-    "metadata_key_hmac": "292DB21E56B265F8DD3081DEFB8540906EC5C6276D16F2306D79B522A2692813"
-  },
-  {
-    "key_seed": "FFECD4CBEF6576764BCEFCDC35D49F960F57C8EBBA3A7323E48359F045FD6C8B",
-    "ldt_key": "5CC1784EDEC21D04C34AEA949B3993BDBC007DD49BEBE6D21645B230500184E04E33CA0D098C5B51E3FFAFD18C95DFC70630D995DE874DD0E1BF09A8A15114D0",
-    "hmac_key": "66CB17DD1DD82408A7593463616D567B904107D2554D0750BF5AF527336A7545",
-    "adv_salt": "CF7B",
-    "plaintext": "F3FB937E5377F9DB4BD2D4EF2208165A6ABCE8641E349849F9503B6E",
-    "ciphertext": "F6D8945B2244345013B9C42EF12E33428B08DE08B67D749245D315F1",
-    "metadata_key_hmac": "5C8952B92EB8ADC934E67B68AC57A04172235C5803822F94F5A4D6EF02EBE042"
-  },
-  {
-    "key_seed": "560238BF4D1284A55155B642EE1CB6D1F1259FA3E6E412D69C61832DBB0CC844",
-    "ldt_key": "FE8A459B03738D1C600093921A2CF605BD9EF3153B80E43EC0A79B09544922482FB8633B7B5737C9B35D90273EF6071B381CD71D56F11525B57279AFF80D744D",
-    "hmac_key": "86E0E51264643DE2E2935FE97A50724B2EDF9B9E9D29768B8834C7EB2DE168E0",
-    "adv_salt": "C3F5",
-    "plaintext": "095ADA0280AC300F54924AB297504E2C9E8E80258C",
-    "ciphertext": "0C4540C7030EA877ADF9DFEF23787A2059D4CD971A",
-    "metadata_key_hmac": "C253686143599E137D34B7B367A99351156427DAAF7823BCD78118FEFD172E27"
-  },
-  {
-    "key_seed": "8D4A13F26ED5A961739C3EB2BFEA059BC99E44E7726CF1C922CF5220F2D3CDC1",
-    "ldt_key": "65046E006C2A7AE278CE1C099D4F07A1AE030D210D9662784CC8F51B065AA2AFEA7D4B8580A17929080B070252172637C9EBDEAF21A96378F2751456034C3481",
-    "hmac_key": "18CF2CF7420667D3A7FA6A479361A7F7E28161AAC5C6F84DA9C74EEE65B71EF7",
-    "adv_salt": "5436",
-    "plaintext": "B76F88C0F087756FC52FED378C69956785D975CA07CD6F56B5CE2564BCA4",
-    "ciphertext": "9B767EF93B9360A08F2E984D617446EF0B360C0FE1207C547664287EFAFD",
-    "metadata_key_hmac": "0114585B0BAC8F8EA1B88F01C3CB5B4D2F7A7059FFEACCF7A4F90505938D75D2"
-  },
-  {
-    "key_seed": "C03CA925B56D7561DCFE958604A65C9A2AF5BD32901F4DB58CECAA9DDE86A785",
-    "ldt_key": "35F77C5C186F8F6DE64355A6FAC478B0311097B18B7272E2175074B2CC6B8D4565D4B496383013C18C6DF23D84DA5A502CADE24A8393A519CE58CE9295DD7463",
-    "hmac_key": "A9F3C2102306B52825ED579E454AC2D3C97043BFA018F9FD3C8E0A84ED22781F",
-    "adv_salt": "17FF",
-    "plaintext": "1435AE44951D8B30AF9F82FA7099789149A5CD704C292D09B56C",
-    "ciphertext": "4E77C59CF06B94DDA606D8837E60E1765F2B832562DA329C4A82",
-    "metadata_key_hmac": "0899362FB0F8C6D052AE49C70947C5BB576B0E7A03A341BD301B175C7BDAA185"
-  },
-  {
-    "key_seed": "2DD921FAC7C94A14704DEC774BF3B966B6C605E359F8631B3F573AC88BE3A3EE",
-    "ldt_key": "6FCBBCC9738AED48819D5F410CC938EA41C5011B7C32B6B9C1D7F9F1699E7C55C7893C9752614226A94CBC441726D94F365BE068E5E641D8DF51D2FF14F183FA",
-    "hmac_key": "3FDF2E252A1D3DB8FC22F4FA3E4BB46E27FD9285F67ACAD4A3B18613C7429A23",
-    "adv_salt": "C63D",
-    "plaintext": "4E18456921ABB6CF637924DFACF7A9C3FAE58CDBBB9F",
-    "ciphertext": "C5A7BFDE9F216CBA9971122C3DF85C0BDEBB287222AA",
-    "metadata_key_hmac": "1C1CFA1B668CECE1CBBB580A84707E9B5CF30DD6F15ACF18741C52F0FE7767D3"
-  },
-  {
-    "key_seed": "DCA956B5EEFDA5B3C69921BC348279470281A2530BC257E5232462CB1FC8B7D3",
-    "ldt_key": "A5CDF7F81CDA770C3445302A6FAE352D8F7CEE51CF40D9CF2CE8B726187497F9BC4DA1A8B6A55F7895AE12032B6DD2FA9451382E28306E20672E0D0E04FBB63E",
-    "hmac_key": "E5008AE8058FDF4051ACADBBC844D6C985DE056450EBD7146CE188DC5869307B",
-    "adv_salt": "90AC",
-    "plaintext": "384E8805B425963315323F490816717C9A5A",
-    "ciphertext": "502B16CAE050677D23B192893E594D9E5F02",
-    "metadata_key_hmac": "4BFF98CC40EEB672706674F6D21C8FDEAD9B52C90C7B2CF501633848BA2F5ECC"
-  },
-  {
-    "key_seed": "40AF40B7FF79C424E70C7E054599A279C8431A8A512E775F0607726B13B3AE15",
-    "ldt_key": "35462ABCAA42D664176C8FAEEE17AD02448447B15DD737B4B755E251AB3884CA8EEC1EF3D4DB171936E0ADCFEDCC69A9C1B3F758685D126C2A4C1C129427B021",
-    "hmac_key": "E367639CB8320BF69D697FE3C7C0E5277671170E5214F4CA2352635242F6BF36",
-    "adv_salt": "2BFD",
-    "plaintext": "C5BDE767A457E7E723C6610F1C455F9DABBA438693AF9A642805",
-    "ciphertext": "4A4173275F50DA2D98A22343D2DAF2CF9C61FF087A3956613650",
-    "metadata_key_hmac": "B88209F17B55D83B71BA7980462B779C00424013C90AE9CEB12E0DBDFF064500"
-  },
-  {
-    "key_seed": "FF5C96B3FD32837F21B5CC7F9F9EAB1ED11F89812965C2B28AA81A536441EB74",
-    "ldt_key": "9893C0DF1BCEC6EA99CB4C8CADFC83A42030428E7E7FD396A63CCA339A33E845A17D17CFB321682897E5181DAB5815CE194FCF8C05B037125B1E81B02B89CDD6",
-    "hmac_key": "77F7CE078AC8BE9E03C40FBCB45AB62713EC512A733A58DD3CAFD176EB8E3B1B",
-    "adv_salt": "66F9",
-    "plaintext": "5FD020213436384B7302E92E202AE739A20BF68394",
-    "ciphertext": "838C667C5CF02A36FB8026DD2F3540F9672D3C1728",
-    "metadata_key_hmac": "D3DCFE7C53089590B94130A04E76EA89523069EB225A07A734AF1660B2A23E6B"
-  },
-  {
-    "key_seed": "6FAC7D59FF8BAB3DB3847956AC8DBD8B3519C08AF4C711F20D38CD3F567D2DE2",
-    "ldt_key": "D903C89A12961C3BCD38A3F19C8441ED0CF8916BA13B472E12474C18AE5C455088190BEB14245166D53AA16F98E72EF68CBDBC35E762D7CACA8E60CFB3711DF6",
-    "hmac_key": "AA70D3407F9776F3917982AFA2DD41029CFD07E819C4373C8FE08810DFC9D204",
-    "adv_salt": "F797",
-    "plaintext": "4C409543C6024CCA4F833B368B23E9B44AC6",
-    "ciphertext": "D3C399978ED47AC0C6BB5B5E57596B684900",
-    "metadata_key_hmac": "E530122F1737AB2C99ACA47BA0CE96F27291C34DD1F80508198F7408EC37D39B"
-  },
-  {
-    "key_seed": "CCF31A1494E92113BDD49360A5BB7FFB31B8E0878FBBB40BC844EE5087DF5DF2",
-    "ldt_key": "7CB2F134F970F0D286D67F2DFA24C286EEE2D83FB111238DBB86F0402651B520B14AC172E2E16AB5105213E5289F6D51566079F1815CB2C5F7A9E3BE57FD9F15",
-    "hmac_key": "C7670A04ABB7A78F7BC2456E91C5C717B008E2B063EA9A6CD763189BE4484646",
-    "adv_salt": "E4EE",
-    "plaintext": "BA8F7B5E0099CB0254B6747A3D49C06EAA91",
-    "ciphertext": "490BF53B189F05A0C937DE2D8A7F0D7EBD5D",
-    "metadata_key_hmac": "0C64AA6E76896D4A4603A4AE2FE2EAFB55E3B3535D71FC284D835C031E55DB73"
-  },
-  {
-    "key_seed": "3C77FC3A249EF67B375F97A0D14C4341236335BE81A955895CEB78BDE0E89BD2",
-    "ldt_key": "C282CBF603E1886AE361B89F87FBD22213E8B596EC303DD76A9764D4B9702389DE15C0212AC2DBD5662771A4B4073205BD4B28C5ACB9D24ACBB71DE21A50672C",
-    "hmac_key": "2CA4ACC4D15728C0BD9B97D9DED9C7002610BD8E7348AFD1FFE70A9EE0DD37AB",
-    "adv_salt": "C399",
-    "plaintext": "BB8B563AB689CE07F3AC5C836DC7AF75A150407E9AF7FAFD4A5E25",
-    "ciphertext": "A0EB44DB1F915D2E5AE43A9379FBB8F09E2AEE75D23CB48C4047A0",
-    "metadata_key_hmac": "35AD7B763E05D22D41BD8573CEF4BABB85640F8203BC944E50DDC193E44B2459"
-  },
-  {
-    "key_seed": "8260021B76DD65A64D8BDE991230DDFC682B333C51A5B975E1BB86B5FDC45F4B",
-    "ldt_key": "1019E0760F6E99414F88B799938456C3819A09DE88F1DD02C8AC46473CAF0D75BBBE3A9E974C67476BA1239913140C0F899B9635F72429855B9F686E4FEDD529",
-    "hmac_key": "202513CEBD157D750D437F4148334114B1169D6B08FBBD9F4C8D9BCA29A65D3A",
-    "adv_salt": "8156",
-    "plaintext": "26B9909FD80BBC3E9C4C7498AFDCA0C6F4ECB6462E9D0F",
-    "ciphertext": "BE9420D32E1B19109C6BC153773A4897EAD24FB543CF39",
-    "metadata_key_hmac": "D9E9C1497DDA5DC914707D14F0D475B37991C4A97D6CBB412FE4295E30A969C2"
-  },
-  {
-    "key_seed": "5AC98E6B28870A3B01BA153498F4902FDBC0841F3AA048262A82F6207DD95F55",
-    "ldt_key": "009867255A86E609C46E67B290F648B7E6B99EBC836E8C57506647C0821663B8BEFB31CEFC940A12738301260F77B974EC782ADECFF6ED8E3AF3E457D8F39980",
-    "hmac_key": "4CFB59FD5B32224CE9D5B20D7C5BA93B8A16CEDA3B1D44E1471425340CB2A61F",
-    "adv_salt": "4E72",
-    "plaintext": "C2664EDEA11131E747D4BEA585651ADA4B8125B3A38F5CA8FAEFD8",
-    "ciphertext": "B4D1B01AD0C7E3C8A71193517B3176A75B81049EE04F6DB5B18F21",
-    "metadata_key_hmac": "619272D9A524C2BD3D3AFCF8E356804A23AC4DF7FB041F1BEA35D77CC768A11A"
-  },
-  {
-    "key_seed": "ED0A64DC30DD3247C5EA07A27BE1F02DB8057D663C1A4C88BAD61433E05EF966",
-    "ldt_key": "924D5FAD57CE4E565F9054F4C22D028E8865F8378BF2C9135579086FFE45D305A6D1008821FCA9E83ACFFBFCAB395F55985009F1E4DCA4CDD45E331590BFEB2D",
-    "hmac_key": "AB32E92C7272EE428445A5BF592228D59606C1D2E9EEE1892386676CF3D70E3B",
-    "adv_salt": "E8F7",
-    "plaintext": "3F3436D46DA3900D591FA3ABC6B2290DEA3F10A0AF",
-    "ciphertext": "56A16B25B8267ED32C87B9334083581BE0BF1B5BED",
-    "metadata_key_hmac": "BA3289FE992B1F99D0338293547506D3CD7991F7A4FC02818BF918B7A4193921"
-  },
-  {
-    "key_seed": "C0197439FEC973B0DE7D6F6D365A2DA4FB4D50FE0D93888E6D734857EED03D29",
-    "ldt_key": "716964EEAAE8083896DB7CEDBF25F8EDDA965C6187B4A5FCE71730128D7601A2947B030CDD6B6FABC268AA696883FCB81332945DA5EECA2926D19EB69C87F427",
-    "hmac_key": "FA076FC072E2C3A3404D16A11285275D7F0C706DB3B9A73C4CDBE5CB6209C104",
-    "adv_salt": "7968",
-    "plaintext": "D63A94F053AE7F91C82EBB969894270D0C91A72BFA",
-    "ciphertext": "FE4C5EBF299363DE9E8366950896FEC5ED67088526",
-    "metadata_key_hmac": "43AA21A8E39F1B2B4AF529043D9C4585241DE221937EA81483457245D91DE00D"
-  },
-  {
-    "key_seed": "697C43D66FDBD83CE464BEF25465DC1D8B638CDAEFA077A26C19605B4B682DCD",
-    "ldt_key": "4542703B2CFF1FB68AA0C3D2E2B1DA51BB7FFDBF909E22D414A2ECD4C05B4CD569305B18F437A8AD38AF621624377327DB77E9D9E5F5A2AE1E5275410A72F399",
-    "hmac_key": "79BB8B92830FFE7D861D7E983F00747F11B27E7E4CD894AAB98BFDADF2D85117",
-    "adv_salt": "0C0A",
-    "plaintext": "D1DCA4BBA2FF7D3AF46325F5CCD5A88702",
-    "ciphertext": "FE0ED60EE668D3B17CB88D2092AA97D455",
-    "metadata_key_hmac": "1AC786DEB74DFB2E6AFF4B6CBCA604D9D17AE479F0D803D15B8E349A3E0C2216"
-  },
-  {
-    "key_seed": "06744EA084560A864AEB290170297DED5C75F501857C323BA8AFBDB98EC8A36A",
-    "ldt_key": "D512EBFB1CF9FAFD53ED2BD36B95311424DEC109D0736B1EA9E1DE6B1ACFD4B8C97361DDC8BE2BD00A8981923191ED8442C7249DEB595C8581BD79184FF86420",
-    "hmac_key": "D0855C61600EE6995650EE1CC1B6025A427A49797A3D28BCEC4C56F792A4FC56",
-    "adv_salt": "EAA6",
-    "plaintext": "A7305489DF9B70E57C6078EC6EC3929D08C2E25D7B3E3A",
-    "ciphertext": "42F28FAD9FF09D8DC1EE7DF691F9FA1073FF3069E500BF",
-    "metadata_key_hmac": "CACAE0755E5FB2B9252083B6E5B111974DBE8198E73DA9FA71F87CBF142946EF"
-  },
-  {
-    "key_seed": "6FA27E2ACBF24626C245DAB2F9F0BA09F14D3CAD3546B04FE69DD0AC4A81696D",
-    "ldt_key": "013516054A4C325FE76F96AC9CD773062A7A413C5DBAEDB9E7E52CDF15E4314DB9F9F9AE11FA4E958CD847F233AA9895D135AA9E39BCBBA416061D7F821AB7EA",
-    "hmac_key": "F2DD61A7A2CA13301E561908FA0A4855D261E1E9376CFFFFAF0A0B1182AFDCB4",
-    "adv_salt": "19BF",
-    "plaintext": "61B445648D1A7DF53C2C4D2DC56533D0AAFA413EB14C4A8AC4F575",
-    "ciphertext": "E6A0956FCCB9E2B9FE858870329DA6ED95EB05E7CDBADE0B738220",
-    "metadata_key_hmac": "5F218B368DE8AAECFA1CDF6AF51F1FC8363F6EDF962F6EC73FFE46BE420BF229"
-  },
-  {
-    "key_seed": "B38D67920F2C40376AB3FED7D17CE6A506113056878092435B2800F452FB3C73",
-    "ldt_key": "2CBCC0C06331A4DA6A89D6D71896C2A41952F303464B3CD7D3D40AA4F8ED40537ACA2DB1B25DA39BCD0EA450404394DA3BA381055E624A1A955B395C8F0F044A",
-    "hmac_key": "FD0ED8C3F2458CEFC7374500F1102AADC30C1E4B95FDE36B988B8F7CC21810ED",
-    "adv_salt": "C585",
-    "plaintext": "14BC22AB086A9C5CAEF9C8D017C7F27849324CED0C",
-    "ciphertext": "94073101F480749B2542BED8E629BD7B7DA8BEBB01",
-    "metadata_key_hmac": "1E8414AEE2B04DD29B49BE15DB713E63F0BB2D4733348E7D77CE8221D78FFE95"
-  },
-  {
-    "key_seed": "6BD37AB62944CA4CEC25D73CAAC6F3498B34D6C2390EBE17E91B19863FBCBC6C",
-    "ldt_key": "48961462E6646A3F481373B6CCED96BC20CDA30FB664597DD35CE6292005E5470CEF79B662CCE6D730765D0443B7AADBF5A419332FB0CCFC0BEA069F85A0A792",
-    "hmac_key": "478E6E944FCCFEA4D38EB03DAE59EB00DE7EFAAAE537F8EE6BDE87DF5ABD8327",
-    "adv_salt": "2751",
-    "plaintext": "952910F1E3DCB83CEEA55422EF1C2CA4E46B1A04D970",
-    "ciphertext": "D906530759DFCBFD38D97B09BB9DAB63D68A7B7B9848",
-    "metadata_key_hmac": "FB65431090B30F67EFC457BE2A449D644BC83F2D5F1C717171ECFCFF82676F28"
-  },
-  {
-    "key_seed": "A330634C116712EA137CE53722858AD6138F2CD90988BF158211ED5CE5182338",
-    "ldt_key": "8B4C490D6365F4A5A97286743D283E062A83D73DBAB6EAD6DC39C494F68AEDC0C00C58FEEA41E566EDCC4C3D63BBA6C1BFD904318060E945C329CC6F62F63B2E",
-    "hmac_key": "F25C43F900ACA2D340456D6840F1C3DC443DF2763091D939E93A53324D71E5D2",
-    "adv_salt": "4A3B",
-    "plaintext": "0355A87A074F53CD834D5F2238B0335D2CC277",
-    "ciphertext": "B730CE0C75511611828492F865AA841EA60028",
-    "metadata_key_hmac": "CB6CE48F7B1C5E06BB357EECFF068AFA13AF676DF66312968840E4D43037D627"
-  },
-  {
-    "key_seed": "69605BAF6A2299BAB7A3117F501BAB2E153517A0575932A25CDD173C77804DA3",
-    "ldt_key": "5BE1FAF5E9929EDAB360C5674506D8A931CD94C18EAEC3D0D411788B2B80A2C79814DF26ED3FDB3B125F378FA89411C7C6EA2CCD8A76158D9360952832E1F364",
-    "hmac_key": "CC227C88EF67D8AA4973349DB2896027BB95C1AAA02CF64F0F8D5014CD368DC0",
-    "adv_salt": "DACD",
-    "plaintext": "9D04514DF277767EC7259E463B0F768C1158AFE8F98451716FD8",
-    "ciphertext": "F6A099E137900CF08E4AE3C425866DE178D1C0FAD9E34AD5DEF0",
-    "metadata_key_hmac": "C42A041EF294EBE5148260BA7B3E79C516D0BB68866F67DFC676A4C50A7CF17A"
-  },
-  {
-    "key_seed": "A8E067D4DAF508F350A6053DD414B892F61022DB040BE37C41525E856042C043",
-    "ldt_key": "EAA8C9DB71F86D712FB5F37017AE755D13FF856BD8AAB483C9DAF1E057BD0C0DBB0FA91FC6A6F543AB8904AB427CE7A74EA033C30D08CD7F7090E08BABC83594",
-    "hmac_key": "BE0B4023C4F3EAF51F78C5B3FEC4265B6ACE0D1533F87800419CCE566FB65327",
-    "adv_salt": "4769",
-    "plaintext": "5A7359231F0D71558583BD2ED0A6132EE1B883A3E007BF",
-    "ciphertext": "E1B783C4E61A5D7E9C0A1C3D7B9FE3C1B3A32EB00B8BC8",
-    "metadata_key_hmac": "E77F1DDB4AB574B61ABC25A4A401D1B2D5C56208821C7969DBAA51E0AD8655DA"
-  },
-  {
-    "key_seed": "33BF72DF056F642EF3ECE670B6DB89AAACEC79714C08EBFA3D67D5337B10CEC5",
-    "ldt_key": "7E69EF6784947FB842CAC947892EB92E25998B4C351C52B47B1FF8BED34FD9B614E4B6C0DBC473B8403D9E83538441592A85A7DD623893CDDDBA93BA091843FD",
-    "hmac_key": "F79E99ECA90D7B6E1BBD7A56D9D162E4C997B8C13C3EB67ACFDF6C45435BEA7B",
-    "adv_salt": "0795",
-    "plaintext": "8259E9342572AAE69EB853F4ED235A4E2B352042225064",
-    "ciphertext": "AD48FC27DDACE86F2B30D057439BA73BE6B2481F801BCE",
-    "metadata_key_hmac": "F81CDA4ADF43CFEE4FCA7B5504741F3BFE4C777FE27B5BC9911E28F68358B792"
-  },
-  {
-    "key_seed": "23FD4FA8106C7B8A988BA2E9D7FF5960DDF21A93B153952F31BB1CD7C3D8FDB4",
-    "ldt_key": "7DC8CFC25C60B5F3D0CC05B0CA4CBF0AE292F78E8B25CF18241E10F55FB86C7674242A42DC27BC252922D9FCB8524BBDE9146BE4F2EF5B1D20AE9473DE54F12A",
-    "hmac_key": "81C08850212A281C3A1FCBED4E70A460A91A1A3C8DBBE77C5615DDBD3A1A0058",
-    "adv_salt": "5141",
-    "plaintext": "99465FF4B2E920F83F0FD5680C7AA33886C7DB872687069ECC7DAB",
-    "ciphertext": "FE2E63AEE44DAB9E4C8FAD0DCD5BD8E4C770292E344B4D07777617",
-    "metadata_key_hmac": "C8A25EC974F10643E7092B40A116667D302C459ACD15CFA531159E224A61D7ED"
-  },
-  {
-    "key_seed": "7B5E0E72796A7CDE98719D8B0DBCCC6CFD636C6DBE476F8A206FC8F78B12BA7F",
-    "ldt_key": "8FA267D43BFB36D59C970771A15B08651251F4F2DDA6CF223258469F883D1099BC8C0AF94EDDD649A4DC537E55A7782278150A442D516C61B561313787CB497E",
-    "hmac_key": "90C88F0C2CB1A762C19D250F3F7E1AA217059CCAEAD8FE13649FDD9563846EE7",
-    "adv_salt": "38A4",
-    "plaintext": "F23D41E3C96B3EDD64BB32DA96E7127DDA561CE75690DBC84313",
-    "ciphertext": "8D4479180E250BDCAF93A1C7254B12CEA1CAE90622D2C5617037",
-    "metadata_key_hmac": "8346376CFE03573F9105464067C53CE38C712FD42CE6367B5DC019040EE66219"
-  },
-  {
-    "key_seed": "FD6EF6225C64BAF3942E9CC8B8B2FB31333DD7C4CD0371D8E7A8BFACD67A5406",
-    "ldt_key": "BD5ECC8DC600DCF4BB0CF4FF255406325329EEB2315E9AF6826E95C00E118F2A04CD9801B87C108F098F77AAB3A6DB3CF7D1AFA2A4A44E698C9B6C28CDDE387F",
-    "hmac_key": "60D11CC44952B70588BC218186A89ED0F6C88DA4149C8F812F162470694250F4",
-    "adv_salt": "8A82",
-    "plaintext": "4CDD507A6EF676F63838CB94CD080FDC1FC415403217195FA2",
-    "ciphertext": "8D4A363B48EC2E7DE7F394BD19C68863DD8B185209538A00E5",
-    "metadata_key_hmac": "4A7AF9E6E406F73B01F669681064182BCF68F34EF4B2D7CF49B7A92E9BD847AC"
-  },
-  {
-    "key_seed": "C7D86CDFD6C4CC20A6E02F16F141526DA17D2023454CEE6B49B7DD75FC023B8C",
-    "ldt_key": "36CF3CF171CE8B6D96062FE3A388FA871B018880F639A19FA89B2012F43378C02437A2F1EE170EE196797897A88FB145D8CA1FF5F2220A6EE6C08CCCCBCAA161",
-    "hmac_key": "7FC77CEF16D868F5DF038049B1679244F9A926395FE78BC4E30BDFF8815D2309",
-    "adv_salt": "0F1D",
-    "plaintext": "AC56E8D0B4BD95E907E79160B5C9164A9CB6D1B632052A",
-    "ciphertext": "7507293BA55C77821BB0434B0B80F379E7E92230C5E714",
-    "metadata_key_hmac": "2B22BEED4D9C8976E67972769003479B96D65E348C67765538D53D0BF13F1959"
-  },
-  {
-    "key_seed": "B9EF34F19378FFDDCD6A037B700B7C1249CB8D23861DB2BE4A82F8B60B971232",
-    "ldt_key": "22A3598D49087AEBA3EB405F8E2BD1F5D3B493C73C6F04A208A58E250DB094E9925F0F5597D6099501FBE3FAC9C5F352D1A50A7822A619F972298451CBEE149D",
-    "hmac_key": "C20A8A2466913A13F5274B6ADC1672DECA17F8DCD93993ED259131B3D26247DB",
-    "adv_salt": "128C",
-    "plaintext": "3CB22D13BCF6B49ACBF0307C977BEF4AF024C5E939C1",
-    "ciphertext": "658816AF0BA9938CC80BC657342405FB58B4F5C69A70",
-    "metadata_key_hmac": "2C676E6FBA574FF9AF15F9E52742E43D677DE1EEAAE68F83ED265D62CB952748"
-  },
-  {
-    "key_seed": "F6E2BCDE6535687F2BEE7D31CEF07C094A4291368DC9E0D29E4E7A84743E0617",
-    "ldt_key": "F3EFC45D8829FA28138A943224D13E5DF087F4020ED84AFAA136ED4F8D1324F53F45ADA3F989B70BE718EB1FBCA33EC100D1FE2D84D2B8BA8FB2C862A4CB07B1",
-    "hmac_key": "47A87F2ADC7E47B0E40110AB53721539CCBBD7E83FE51E9CB027C9A8B9399278",
-    "adv_salt": "6310",
-    "plaintext": "BACB663633EC8A203B8E47CC78E2FC3C57A4353D0D",
-    "ciphertext": "2146B3391C532606207377D6DD7129C3BA2EFC54BB",
-    "metadata_key_hmac": "D98EF41807B9CEF28CBEEB1471FE28D288952F8724577C00E71583185C490953"
-  },
-  {
-    "key_seed": "BBFFC330C5116DA19A7C6A5E8B4316E6A5B2F1BC5F4D20D8C5860FD88DCEB878",
-    "ldt_key": "E87D1C153AA1550030306A646114F51AD0540E09FCFF9FE540733688690D03BF2602466AF95EA855B8BED74433A033E3C299BED3A38FE0790C631B0D04A04EDA",
-    "hmac_key": "62B2034BA3F1AF080C0669B5F0858AEE56CE7A8B33EFD59AD37018D5CEEED31F",
-    "adv_salt": "BD55",
-    "plaintext": "CFAD209C744144A5B30F993907BAE63FA71C",
-    "ciphertext": "181A4E56F70F0430D21D0DD4FB14A1B7FAD1",
-    "metadata_key_hmac": "28397F3D55EF8C0E29229EA51D6A7CE95C0803C5E149CD8C9AA550991D08A4EC"
-  },
-  {
-    "key_seed": "4FDFE79A62944A011A72126D4E68B5CE66169C91CA7CF5F9674193623C616FAC",
-    "ldt_key": "DD8C89D4416DB6F6A2684AD39BDFE617E37FB63FA61299A9DB89B72239FDBC977B0CB0602BA265AA59D8FCF97A89E0A0A51E0E160447358B0F44C62E187142A1",
-    "hmac_key": "8678D9FB5D39557B280134284789042FA9C7DEDBAB36197381499CF53366AA47",
-    "adv_salt": "68DE",
-    "plaintext": "810CD81E8E65E2ECF1FB83BC041E94E7E873DAEF0B64447D59BBDD421B",
-    "ciphertext": "3A1497277E3A3F20C43E245B947C34001CE9188AFFFA38F4FAAED972B4",
-    "metadata_key_hmac": "B2552A6641B7922F972D62FE82637170920586808086357A48BA3ADAF9AE7446"
-  },
-  {
-    "key_seed": "189B61463E970CFB7592E88303F0A8DC86CDDFCDC26BAF74A8D68D97C210E23A",
-    "ldt_key": "393558113FCFE50A4730736EF9EA91C9D5DE4CAE137BE0FDCC1094C0A899DAED1C421895C90DDF843BF2575CEFAEE46F070684A6A33397AF93CBD9D7FC1CEBB6",
-    "hmac_key": "D072CF232AD840B8E603F8E4714CB511117C0FC0BDE0F6A51C5F514C9C9FF428",
-    "adv_salt": "3AB1",
-    "plaintext": "97ACE46DBEB775F42AC8D738A254BEE3A6CA83062349861458F4B5745495E7",
-    "ciphertext": "FDE78BE771E2174F182BC153368533F4ECAB80DABD8C18A3849E9AAF0AF60C",
-    "metadata_key_hmac": "5B95374AB9E06D61E9C2FFDD88D28A9A78EF484ED7E2F3884FA10C42B82D43A4"
-  },
-  {
-    "key_seed": "8099925AB4138B87D1945B94B5C38D8E409392A59A0B8571C5D6F2F9CB35FA3B",
-    "ldt_key": "0A0E7DDC21515365D406EC8A51827740CCF512BB2AC70A85A9B14149706DDD62AC43ED590EC9A27E3560F8F9FB6B106E0F08ABA0616D7C5AC6E3F4F4B1D0F0BE",
-    "hmac_key": "734EB396DC44EFC2928F7A19707231B7B98B5BEBC101CE7C6E9561D130C4D8CB",
-    "adv_salt": "596B",
-    "plaintext": "64DD65CF714E17B310A8949C2B171F1F931CE19AAB3A8AC7C6F0B7AF",
-    "ciphertext": "9B8DA9C7182734B97C0E2BF2702689C95F593A61E60ED5B9FB2506F1",
-    "metadata_key_hmac": "5C45C2C3FFEA97083F007F084EC0A8E21D9C827AE8C15B3ED895A99B3158C986"
-  },
-  {
-    "key_seed": "27041C6BAC8D938489F45F322E201C7F77692E29605F162ABE43BC7DD443A922",
-    "ldt_key": "DA4669A8269237CD04D1E2BA5077C988860F0AE9BCAEBE0F72BFAF63DA6825D5AF7A8E7A2AB8A607C0434BC618158090E476EDF87B7C65D120E1BF4ED98C626D",
-    "hmac_key": "DA52FD9EF396CB4550143E011683735589D8D3B92FF6D1BFBFFE6E9B7C1E19CE",
-    "adv_salt": "0D49",
-    "plaintext": "97D24BFCA3E2414657302E6388E0CE5FE8DC8E5C",
-    "ciphertext": "F90608E4C090D7A4EFC127295EDC0BFB863422CF",
-    "metadata_key_hmac": "D1C309C36E66F33670D6FDA0EE05E0703669D7231A1415ABA0ADED32844F2A36"
-  },
-  {
-    "key_seed": "8D6A7B1B75A42EF4D0D765FC5E349BFECAAA5F2F44FF401B69A9A6B3A592C5A5",
-    "ldt_key": "7A0DB884E2DFCC2C902BEA55C4D9AE023A090226294DA6BDB13D6DB9EAE4AD4B836EAE4A76C1505BE7EB4C8200BE0E7671849A276A9E86AF9B8114B0C59A1529",
-    "hmac_key": "774BBB9ED55FF7B5F7E7067A93D43EBF39A848C053A6F7CAFB470765EE051562",
-    "adv_salt": "54EC",
-    "plaintext": "D5B99BD450181B46608DCBE86970A4461676E3350493",
-    "ciphertext": "72360EE5F4E00AD259BA7B27D44DFBAB8E1D7E87FFCA",
-    "metadata_key_hmac": "B6874A01C4C4134AFA8D2617C0E12DC5E2933DC50E4965EDB516037D389EFC51"
-  },
-  {
-    "key_seed": "8B35CEAED75AB9BC440007C1FDEB67BDE737C1BBC5A47151CED4AEC30E316A3D",
-    "ldt_key": "70AD11208A009AC067C91BE445D1161555EF0107F7988334885ABE4E795F44B913F8704825396F3619F0851F5462CCCC378DD3B6E58527290D2CDC11C2CFC408",
-    "hmac_key": "F86E2AC57F9CEF2447AE1861A964BD6438F3119032B62ADA5D3CB0DB426FA1E6",
-    "adv_salt": "10A1",
-    "plaintext": "DEC03FA063713924FC5D1B496C66188C73F61B",
-    "ciphertext": "91E43CFCA71C439A7B3833F44643A2EF96F796",
-    "metadata_key_hmac": "8DFAA644D383B7711E0915EB2298F50E68980374E82ABAD0BD6E748317CC2427"
-  },
-  {
-    "key_seed": "4B6529FE3922521F7E60426FEA8BB5AAB0F937697887C0A419CD7689F408EF1B",
-    "ldt_key": "DCB3850034660D35BD5653ABAC9B74D09312A129124BB088D987F2A226ADDDC6CE3C9083DDF2BF4E5F4C2DBA7F54EC28C7325DEF05B4A6F7701CD43E5A44FFE4",
-    "hmac_key": "70BBCB078B01A2B5D9AE6DB6FD3FF814D6858E8D1884052526B3948DE0159E1C",
-    "adv_salt": "1702",
-    "plaintext": "06BF92FEBEE1E3517771754F6B9E6CCA43A6B466",
-    "ciphertext": "1973E758175EBE6AAC496F454BA5E962620206B5",
-    "metadata_key_hmac": "BD57BCD419CC438CD03FA8C2DD95880248DC114FF8B95272D2A79F1AC30AC1AC"
-  },
-  {
-    "key_seed": "54E3854608122E139E0385AD5DAC9FE9B6632EE73DE765C15235BD2B71D54E4B",
-    "ldt_key": "B3E9D65E8A2CD6CBDF96F023ADCC82D446C22774C2472C7B5E0042A095A12279606B03BC4FA9FBF42CE1BFBA96E972E27707E9DBA555EF011CED92C138792553",
-    "hmac_key": "71929D03409011AC1217D222EED45EA0BE580E82814A00C03FBDE9BE40EEE005",
-    "adv_salt": "A6C4",
-    "plaintext": "63F169242519545BF6BC6C2B41F07D6532789928FFE91843F9197228EA",
-    "ciphertext": "C18A5C4DB3C1A5365A46E2866AEE404FE12CC6EBCCA12BBF061AA83822",
-    "metadata_key_hmac": "9E11391EA502FD1304D9C15A0FF8BB707571EE09E0EFB44A7D612C7E5216C9D6"
-  },
-  {
-    "key_seed": "A00D4DCE27F6531056891C93C22D2A61830191A296B5BBE3760D3B13FF548F61",
-    "ldt_key": "6FACCF6AA86CB8F0C05991AFDFD57C4A06C58DC6195E0BCFA426C66DE04A2779B62E7BA6882589B825F9192D31D3C91268D70D425F52F961EF3D1C9B045F01F2",
-    "hmac_key": "E19B1E9F5C3355A52C6872225EF92CB09686F4B3CFDC883B7A01DA60D897613A",
-    "adv_salt": "324F",
-    "plaintext": "C103C7B8183BB72CF30A1CCCFF6E6774AA08AC",
-    "ciphertext": "7D328033E813212D6DC0B577609E3F0696A4BD",
-    "metadata_key_hmac": "407C1AE2B01585EDED1FC57AEE81D5DB54A2111DE3C131E4DD61EF8F22FDAEB4"
-  },
-  {
-    "key_seed": "1CCF58E167E1A6D7BAFB4D5DBF7AECFE1A5B067FDE2F4FE1C6A7622980E39FFE",
-    "ldt_key": "BD52C9294BEE6999D032FF607ADD946DAF8A83122C608C473133D778D35003955F2DFD35FA136053C24FB10A40408408F5011BAE86544282D8C02B2B050DB963",
-    "hmac_key": "1CA6D8D35B2B32622CD6623F82476319AF0D0C2A59BB3AC9C9088ED78CD3ABBD",
-    "adv_salt": "A659",
-    "plaintext": "6F0371455A75B8874CA29067E946B1624AF75B0D2E77E569",
-    "ciphertext": "54125A4C0B9084F8205FDBB573BA70FE3D75682066FE4DD1",
-    "metadata_key_hmac": "174B80653DC7291F1824CF045B6EECEA5F66EF1FDE754FA3D42BEF0AE05CCC26"
-  },
-  {
-    "key_seed": "EADCA313267A42211A53EC7EC955511AA3F444D68A3E4E57019ADFB7F10B94CB",
-    "ldt_key": "D2461991FF65B9D300D378D6463D053CFEE4741F9D390249CC95382DED5EF5B26500BA102150750F6337D9E5EDF2D979B8BC0668C85AFF90DF7422512B6A81D6",
-    "hmac_key": "59D8DFAE1D603565D17953FF0FD0BC2AB914564EEE42A223429B91C8313BA1A7",
-    "adv_salt": "ACB2",
-    "plaintext": "08EF1F46D78A71F319673E8B33E147DC",
-    "ciphertext": "67390B3BB369F4B2E1997A2FB7D922EA",
-    "metadata_key_hmac": "434B0A94E392E8606776968287F22ADD224A6DE23F3F8AB39874111777870EEB"
-  },
-  {
-    "key_seed": "1A94D342C30CF5932A44139C85C581A8228B578AFB653F957BAE541DE577253D",
-    "ldt_key": "8C5E585691A61F6C5E87ACD997CFC85ABF402D2C9A0266327B7BBED2EC75B324EEBE7BE2CD604BF9874EDF9B022088DD4717FA99B667D6E4160023F967AE3C07",
-    "hmac_key": "07F3BDE3BBE5979FCAC7B96D93D596701454CCF21F569139AC613A74F9A4E2B8",
-    "adv_salt": "3A6B",
-    "plaintext": "8C83B122A29130586B7CE6E71EA61C2B7E2F274ED5A6B11B04B4B0",
-    "ciphertext": "FDC37C036F089E4367E6447E69F24F3FB7D030E9CE0FFEC54F7F6E",
-    "metadata_key_hmac": "494BD98D79B6A4B38BB78B2BF222AED6B7777786D39078934C17E63C5F9F2EDD"
-  },
-  {
-    "key_seed": "81B65DE1BF1A9E9A601BC8F928ED4A315B9FAC0B0919EE1E8E3F01454658FFD6",
-    "ldt_key": "0EE3259B8AF00CEBEF6D5891E1D5F658F90DEBAE478CF40C30DAE5199DEC7781CE445D3A59C9DC1DFEA5E19D5BEDB0E9A8BFC214BAF348E8C859B355CB2F2ECE",
-    "hmac_key": "E956219F0B6C7DBC66F6E990D34F41030337F56A811DC39493EE2887DA7D697F",
-    "adv_salt": "1892",
-    "plaintext": "D62AF513F9018FD945DAE0B78C4145C38E",
-    "ciphertext": "7D63B318D50A7DB01E3509E8911D4488BF",
-    "metadata_key_hmac": "C9FFC2BE2805B4967860F7B219839125BC710AF1AFC971A08DD1721BDF5E0E87"
-  },
-  {
-    "key_seed": "BE1097BA75EE5513684D071BBC909906C8791D6678926E7EE293742772A6E395",
-    "ldt_key": "71DDF7824BA202935E11501FF5B3D33114B614FCC4649A18A9611F75E65E101D53AE13D6538200D77FC0477E157898B7A562BAE4DBC0E4693C6F3DD48D206EE7",
-    "hmac_key": "E42071F1DE0ED1B358339C7DAD5697738973DC9BCF282FA5A182D3B86C1BC7F5",
-    "adv_salt": "B3D0",
-    "plaintext": "7749F4AFFA858AC1962C597E7323E5A495",
-    "ciphertext": "799DD1320252E90B6A89DE5F83AA9F79B7",
-    "metadata_key_hmac": "26CB000B6639FF02FE93A11DA57A2B2B98B67F450D46339BD9B21D693DEFEF5C"
-  },
-  {
-    "key_seed": "5782D9EC66D31341A49021FA62B60D038CB04B653A1C1FC7F28A0ADB7CC89E39",
-    "ldt_key": "D4FB828808CB8148FD1839E0274FBE3727A51C38FDADCF5C91D9F28EA0019A43E1A78E7BA1B4051A632A350A22A035668FF61EBB7D960D6426517954A556F940",
-    "hmac_key": "A7020D0F20CCB3183355A37774B0983D28BF39409C4562EBC6D52056D2E170A8",
-    "adv_salt": "2A1D",
-    "plaintext": "2098F09B094B82F479402565141E670DA2CC6A06E2F06C910C",
-    "ciphertext": "484856873B3465A14B86D158AA65F72E69F6403CB84232D34F",
-    "metadata_key_hmac": "B5BDA9519DD3E98879D8B0627EA819913B6C4458E04CF3BB803A39559D73D55F"
-  },
-  {
-    "key_seed": "FDC7CC332EB122DC5E3E2D2F8E45400636B4E7780FE7169004AA7E20C6B27130",
-    "ldt_key": "7EE39F738F481B4FCB8DD720CBD46F9CB9F424B75C0C4C25B0394767C00A7D5DC4091F377828A5276C61FB5D2D25DD0873B4ACEC4428CDECA1EA8178C9E30023",
-    "hmac_key": "8B27C2CA40A928E47B60CEE90E993CBD5E959FE471885A6C4717544F26276152",
-    "adv_salt": "A815",
-    "plaintext": "1EB3C2CE847B078594064E8A762558E428534448D3ACADB01C",
-    "ciphertext": "E509A43D2204C464AFAD4B9A6616EB44C52E160B13E17689AF",
-    "metadata_key_hmac": "0D25212C693FB0E67FE3AEFFC92E5328967F025A80DAC82BEA43A90BE46ED80F"
-  },
-  {
-    "key_seed": "6A8660E01FB845F36CF850711077B4C826142E2A0169AB8321D6177FAC49315C",
-    "ldt_key": "CC97042F35D91AB245E70FC2C6D5A5CCA40AC070EA7C8FCFDCEF1F9037C79B2B31D4EBD030F0C941AE7D29681A505340E8C8A7410A9A8546582A4BE7859657F0",
-    "hmac_key": "A925E11A741C1EE11E49A604C5FF69EA7C4FAEAE9D0044F783B75872BA2D709D",
-    "adv_salt": "4723",
-    "plaintext": "B0675497FED81C60FC9E76654E13B03CFB",
-    "ciphertext": "98278F68BF00BAF740642120B69AD28A5A",
-    "metadata_key_hmac": "B623C6CF484872985B8DD0596020D88EF03FBD1DC7761C80648C66FCF40480AD"
-  },
-  {
-    "key_seed": "B5C9178C083BAEDCAD97A7A4757B66C3DC64B03064D4F6686BE8C202DB1809AA",
-    "ldt_key": "356145719EE39F953142FF07290DADA51C4B4F442BC6F5D67D086C16EC875D736AD5E2FF44ABB344D1F5B9F1254777AE5E86FB767F5441ED3A4F6262635E77E9",
-    "hmac_key": "22A3AB00EE4741901558C6567F22A6A88FAF34FC7AC4C15EED7F6615DFC433AC",
-    "adv_salt": "421F",
-    "plaintext": "38A3F2DC36199F411EB565D12758DA9C51E38230",
-    "ciphertext": "15247ACADE7CA745F6C37811B99870D418AEDEB8",
-    "metadata_key_hmac": "CDD0ACC27C3DEABF89071CC2F2592C07E1009788B49EA7315208124250E4561C"
-  },
-  {
-    "key_seed": "07DD86FAB6F73009F610F5F3FFE76BB8CA257AD8A5E9B8D27DC8CCBE948193AC",
-    "ldt_key": "435CF9F5CF8075B2C227A9E95C35F747BD99A53594F96EF778DEB8BE8D988B901C4F98319EBD55DA91046E382FD5504F1373D504002AD943633896AC87A6ECB0",
-    "hmac_key": "4EDC9928BA58B8287E0FEE3968310BACE74C344227A2D3AC4C3F33CED21B518F",
-    "adv_salt": "4402",
-    "plaintext": "BCB7CABAB0B4EB14638E60766A517E80CFFE1EAA9BCACBB1",
-    "ciphertext": "36D2F9CB37FA4F45B373145A66DB0A4D3C89BB5BA39ACEB6",
-    "metadata_key_hmac": "40222A55C432B13508AEB67112ED0945A8808CFCBD29A54BBCAF70DD9FB1DA1A"
-  },
-  {
-    "key_seed": "B4D47064FC92CDBF3E0204AAFECA67DB2E1397189162B0693F59ABA9373287D3",
-    "ldt_key": "7776476A2E73E102B3F8E78448C9663FB7D12692DAC2988F029ED52D904ACF2476A4B85781CCED68E8C444A8F402C496E2FCE6082243FCDA7425C642CEFA13EA",
-    "hmac_key": "65F98BA0F2F2B46EC0ED7563B4955F9EA7142E810BF86F455FC86D1C1FEE492D",
-    "adv_salt": "3C04",
-    "plaintext": "63ACDEBCB100020E6BBE3E34E9B2A0F8EF862246E2",
-    "ciphertext": "C5379E612483BDF2220AFCDE21250E167749B0F7D5",
-    "metadata_key_hmac": "646B1C7EE957D187E4EC607E2BCCD9A6398241F8A7277677A3218F782AFB33F5"
-  },
-  {
-    "key_seed": "B77F947E37384AD02CE4CB1A181288A1A80F3D3968E73449BBF3B9B2C27EDA58",
-    "ldt_key": "621AD29E712745EF9821D30F29D1BD4E707200515F732E8ECD68A6BE6A0864E7E39A7586B78A679B82A87523AB0A02BEF3FE75275D4523CB269D591EF3AC36DC",
-    "hmac_key": "B4989CCCD6DA89C58DFDAC6A6AF7428466E018C00AF396E62317B36EE7A6A63F",
-    "adv_salt": "2223",
-    "plaintext": "C571D5F394E9640B495A9BB26A938900F928CE45BE00C519F342B4DB3F18",
-    "ciphertext": "DF5FE7389AE4D8A881AFEA097EBFCA7EBD81F8F507F308824102C316411C",
-    "metadata_key_hmac": "CA1FAB06AA37630DE95595CEEDCE1D291F51C10C8DC1876DA29C701756882AE4"
-  },
-  {
-    "key_seed": "F8D6F018EDF3CE25D03D13438A647E211277E1285D50F12F521FBF0AEF6C17C2",
-    "ldt_key": "BEA6A0848AAA447CAE44D403B60F4A94C3731568A87626583DC43DF1A1241D8A39F42842D590FB142BF9A2508D5B767F184742A977D6B458F037F80F9CC8E4D5",
-    "hmac_key": "D0E35F7B52D04FF1C8CA9F39E85FCA40DF6BAFD03268BA64FF12C51AA4C0BD11",
-    "adv_salt": "CD19",
-    "plaintext": "00985AD94B8D2C93EA3642494A5B0EF620BE",
-    "ciphertext": "AF7A3074242E41A5735AC3E823576BD7F42E",
-    "metadata_key_hmac": "E6D9B0ECD04A8D1C03DEF5F8AE5CDEF48B874D24DF5CC2937E2EFE0588ED367B"
-  },
-  {
-    "key_seed": "A9C55D893A3A40A15929B967FED3BC52B2A38195D7340EBB10B99E1942782FA2",
-    "ldt_key": "F8A20F71F22B6C1ADD9C5AC3524C618DD74EECE7B81BE0EB8D54F1962613C808252DE287825D49680208AFBC6D1D85B0E05B3602D58466158ED77DE09E6B2343",
-    "hmac_key": "605B915D16AD2F783038C935003237AE59E9123F35B2BB13287BADCEBC18A289",
-    "adv_salt": "934B",
-    "plaintext": "95216F3A7043C18D63195513FCE1C7C1FDA6545200B6B5E2BAD1822476C5",
-    "ciphertext": "FB4C08C4E8A1474185FD12274E5B04DF923C2E517035A54D74B5CD5D164F",
-    "metadata_key_hmac": "0091FFC18E5BF1C1C22E212161955A2DC9BEFA4BE7DE3F0D10B3751F83E8E36B"
-  },
-  {
-    "key_seed": "0FC8200E3516D1C17547ADD4496BA06202C7620EBCC06F8FEE5880130595D2AA",
-    "ldt_key": "ACB2F0E90CD88B29993F162EA049077D24B15EE104F8252408BECA2B9CA6A83B9B9016FC763ACF5127EE6C4B0C477851A166338ADB8FDCB4DFCFFD56E6743782",
-    "hmac_key": "FF74B5CD682C9C50E2CA77494394DDB74946141744A52CE6569EB61FE5F2D5C0",
-    "adv_salt": "5FE3",
-    "plaintext": "0662C83A21E9A604137ED3E4B476F1BFBF80712DA3F86A27647B",
-    "ciphertext": "B78C902BE09389E33668773351AAFF1AF8065F4E08C10C5C5CFB",
-    "metadata_key_hmac": "7F8FA03FD2E844C71B9D28AC291EA331DC1A25B242428D0F9C769CAC71FC4191"
-  },
-  {
-    "key_seed": "FB04727819609193D77B0B6669BACB4538817C41F0D75C43E5E04EF2ABC307A7",
-    "ldt_key": "D980A40193A2312B742441AA435A206CDD3381FB0BFDDFD5BC8F75F95AA0AE27563D43866908E1A1F7DA9B3CE4DC42EA0A93D167FF44995B0563925CAF798260",
-    "hmac_key": "33B0446E8330D843500A02DA179C2624DD283C821405B812980B1BBC720AAE70",
-    "adv_salt": "4428",
-    "plaintext": "FE1BEAD3B9682E051481BD5C72B4D571A465DB78B96E",
-    "ciphertext": "800C8077C465EAF7AC191D5C20978FA46B05B81F9127",
-    "metadata_key_hmac": "7746FD13AA424EAA1F1CC6F05D195CEAE45872C3F822E8C1BB0487CF18E031CC"
-  },
-  {
-    "key_seed": "3E6D4FF8DFD8FAC1AA76677789215EDC8FDFC88CF1143CA6DC1CA4D4A9E3F3A4",
-    "ldt_key": "B881F04DDE3BBF8E24C721D5FE186565819AFF6C50DA26CEB117B15DA21949EC9F601C37C9DC51C6E92DEE3D31F46297764A3B6BD56A63B17E448E621F7EAD6B",
-    "hmac_key": "3EEA351F7303E297DF360C4AC86DA755EE8724DB9D572A7516E8EB22561220CB",
-    "adv_salt": "3EB4",
-    "plaintext": "E54462C8E85DF7A358DDD7EF6134DF9779AED4",
-    "ciphertext": "BE71CF06B610CCFFA653F533ACE83D8234C706",
-    "metadata_key_hmac": "E3ED0DDFACA9D8A6945B83963C1EC4C49599BE0D5FA2079B34675AEBD21990FC"
-  },
-  {
-    "key_seed": "E52D51F47DB88BA1E0A9585E119F45F7061C4ED01901DB63D1B068B77F2FA539",
-    "ldt_key": "E81988DDDE4B0C3FC9A91E8A07FEFE43393FF0A74720E0FE06891ECB154E7F721D4221BD43E3AF18F6BD8645650B31FAF7E70BD7C52FF820202F57BA920C7044",
-    "hmac_key": "C4ACDE87225B126700F0C9036DD2CDA1497655EE3DDB7F01EF411A51D22277D9",
-    "adv_salt": "09A0",
-    "plaintext": "09E63DBBDB48FF0D0B7D32A1268B200E6E1987",
-    "ciphertext": "718657B2C4DAC9368253B5DE0126FFD5131C22",
-    "metadata_key_hmac": "70576A2E080F91CAD7B4B3F9E45DB712F4523B2707DB196586BDF45946CE6B24"
-  },
-  {
-    "key_seed": "B071FD22BE44144541BDEED68C9CBAEBE48B88FD6692F027F5AF1FCB15BB25EC",
-    "ldt_key": "5676B0350FCBDA91930AC19BDAA988472218C1D9E1224FAFEB082C8E049CAEB4BDB11249C40DA4105FE12CD6B23C8DD666465CFC3DC350BE62A338F3658CA1F3",
-    "hmac_key": "5F9A5EE4B1A55ECD54BBFA014DA8B054CFFFC1865E7B39E3359ABED7972616E4",
-    "adv_salt": "E4BF",
-    "plaintext": "1B9F88419F3C2CCFDFFF021113BAE89C7C232041D2AD85B96839E090",
-    "ciphertext": "9926F08F0A1CF58A5F7C97E7F2D916ED9463B3E0F5DF46215A0AF4C3",
-    "metadata_key_hmac": "751159135359FCC488029F43CB316B2FA26D0FDBED8ACCAA83EA2F4A8F8DB16D"
-  },
-  {
-    "key_seed": "61AE93DBBABCBD1160D595F3692A078E6FFB8BE572DD11E19507CD7BA37EDA5F",
-    "ldt_key": "768CE6268EA881104C3CDB24244DAD5CE28E1B96F78ACA154671AD68FC96C9990F353968506BC547A615C4A6464574C5EB52DB83429C5FDA56678E7B7A3D632C",
-    "hmac_key": "E1E1B97C2CC8458D9812E692FAD651E125B08D281BF6EBBA94CD18A80924FCBC",
-    "adv_salt": "6874",
-    "plaintext": "BADD300ED46C1A6138E478AB27015381D26709694640CB649CE31C6D9CFACB",
-    "ciphertext": "BEBAC204B309701E1BE26584C9CC920C5530FC92903E53E39DAF9A27049122",
-    "metadata_key_hmac": "2E6B2BE8E00EB55E18D0E916CADD88B7EC4872EB487808096E7B82927E440C03"
-  },
-  {
-    "key_seed": "828642B20291961F8938B4F80864C753CC18537BDC1EEF296D4743AC5FC042AB",
-    "ldt_key": "D468DFD9CDF067F701C8818F4360ADAE108FF5C3937A2BA5BF1C6E23A61810C666E5366457D0857A66EB0A47DD9F644781F07CB07881E5E79779832FCB71C490",
-    "hmac_key": "FE5F79F376BD8E8A38E774662F1C123BA9BF6FF210AE3D82F23741133ABF799E",
-    "adv_salt": "D4FD",
-    "plaintext": "A64B069FDDB299DFE6117B2B8E53550C576441C76A77BF6EE9F4AEFF",
-    "ciphertext": "1550E8E7792D7918A1D2CFCD0A568EDFCA72B6581BE4B937A682800B",
-    "metadata_key_hmac": "197295A9BB48590F804F2CBD75360B915FAEFDA7DFC2897C04846F35ED279B19"
-  },
-  {
-    "key_seed": "64D4CF95291870854D9A64B132AF05EECE52527D2EBF7AA44E3F558990EA4B63",
-    "ldt_key": "FAADF11DE2EE8B2428E8702E17CBD16118D153ED85BFD82EC0384DB687B86EAD71460E7617C7C74D7F87A4FAF4BEFF85EAE680D81480612932E28EB22EC2430B",
-    "hmac_key": "B9335023F73FE5D226EC79BC61F766A6D2D2017D3102E62C9701EF659D31123A",
-    "adv_salt": "2B55",
-    "plaintext": "FE39093AFFE36BCF5A70E64B118385C2F23C5E24",
-    "ciphertext": "3378DD86C535A6A57F46BA3337BA3FAA4143B40E",
-    "metadata_key_hmac": "D54EA66B4B72E8B1DF134AC6DF84885732EC3E53D3B1897AC22387E90C6CF937"
-  },
-  {
-    "key_seed": "29390F48881DAC83417F7E93BAA98F6208AA9BC843C4DF2770DD0A7717322524",
-    "ldt_key": "C7B6C721BC055963A631676239AAE3DC4B56355D93A79EF20DBBE08F2DB23632CEEAE5B3AB86663221773D2F9CC2652E7633B27D94680585DFE588A8BF0CDB22",
-    "hmac_key": "BD74E00EFD9CE8DBE2AF8C33108BB6E83538BC93E87889B46173D3DD66FF19A2",
-    "adv_salt": "166D",
-    "plaintext": "F165CFCD52972DA6EE83086FA4C2573A8EE36905A6C6F5",
-    "ciphertext": "7A23C2430565E17ACA32F8506E67E6619D4F2B5BC546D9",
-    "metadata_key_hmac": "B7CE26107122B7EC595FB52B8E54A9261A05E4BF676D15A7F4464E8F8867D255"
-  },
-  {
-    "key_seed": "817E52C029A497D13CE38EFE8521AC3AEDDEBF825300E35861001595FEE5E728",
-    "ldt_key": "47808ACA04AC4AF0DA650DE8B7D345F0B5CA8D779552ACCECE1BE594DD28A11BBD2EDAF4CD17A3D311DDBF4AA30040C576793A78EA4CC0CE2C5169FA48414464",
-    "hmac_key": "7D2593072EC564982C10773DE9617FA320380B0F35380E330E00236E83205AB2",
-    "adv_salt": "B16E",
-    "plaintext": "979F741F6384DBD02E30EB848AF58CAC0E825E6ED5BE670E08625B049D6A",
-    "ciphertext": "6550B23E88ECC6734F22A602592FD6ADEF21D52D40C125E39615D2285AD5",
-    "metadata_key_hmac": "27A60C66C3B48C98AA7C2A77C7F4D5CB249D0D690A4FEF0E674D709A64B9A3E4"
-  },
-  {
-    "key_seed": "9CBB82D9821CEA8BCF66833F7E187D3BD7630956FE85AD1D54326B50DBA51F5F",
-    "ldt_key": "C476A6B0C6D7465F848684657595E88EC794B6C5B19966C8BE00F623D72923B876FFE39B3C798016F5C5B3D6CF90B5F828FABDC78DA7677948A9CD3152CD0DBD",
-    "hmac_key": "D842D04C078DFDA7103E682CC220AF4A52218EF20E225A20E4610C53241B2730",
-    "adv_salt": "B6C3",
-    "plaintext": "B69874B965C6181E34A12E9FDCFC15A44867B570759878A1EF27",
-    "ciphertext": "F144E75EFF80AD96863BF5A0C478D04425347A6DB670A6B1C2D7",
-    "metadata_key_hmac": "7CA78DCB2E193F1F9B0E104A04D8FAD188CE31A5E6EC52BF274A9F38A01D72C5"
-  },
-  {
-    "key_seed": "59B092A37A79B0D1A427020B5751AEFBB19122ABB2DC451B54575012CC18E781",
-    "ldt_key": "4737ABD41647EFC55B97BE5C0E992D6C3D0D9A0444F09B816F70C599197C7A07E6DF7AB0AB790F250D2EB1B626B6E522A83BFAB38E96ADE6582CA90E556F7853",
-    "hmac_key": "CFA5F64B00040C786FFFABE5B634881A7E6D9957C1097F99522EEC4FF3B71186",
-    "adv_salt": "9F65",
-    "plaintext": "41C098BB1D6420043908C638126CD7135F",
-    "ciphertext": "A8DA29B3072AE8C85640C7CD51FD6D5928",
-    "metadata_key_hmac": "5F4A8B2F5DBBEC14CA0AF02F9D0C0B1BE967F4A8071F5F14C18131BC0AB3C583"
-  },
-  {
-    "key_seed": "22AC202ED8FE097C8825CA079D0DC58A648825FB9250E6D52DAED4CFC015937D",
-    "ldt_key": "28BAFE4E9174E7BB5056F9AD78308AD4745E49FD3C3CBE89AA670AB31A32A69E443E1AFBB9E497C2344990EA811C68FB62E34E176966B092C0707A5BA353FE89",
-    "hmac_key": "9A13EC5450F600BDDE1354E11FB54B65236243EEB2D012F2E3E21B909A8725E2",
-    "adv_salt": "5BFB",
-    "plaintext": "CFEA46F97492EBA0BC9C41F90B8A36F05EE5376D1BC5121B3146918A",
-    "ciphertext": "AC5659B294F628673E36B39BCE2A0FDBC7F28FBA2816774D58258A77",
-    "metadata_key_hmac": "E011B8F87946924EFE20FE9CADFD807709E67CF333742489E666901045A7E294"
-  },
-  {
-    "key_seed": "0587DDFE9A3C16BBF2B7F3BB29D4972A07FB1AE18387C20B4BC9C256372D97F1",
-    "ldt_key": "84DB60AD6EE51CBFEBFBFFE655A02C03810758DA394F892DA0817001021B2A9AD332C47DA62D617C23781D5BFFA21095F4F5D7BBF18ACF4CE96B4B51B90AD0D6",
-    "hmac_key": "5C39C26C51B027C5EB7921D4180CFA58B0BB65089712BE04F8C331149028C75E",
-    "adv_salt": "F5EB",
-    "plaintext": "4F5EE0490F73748A075556B53CD3CAC423D9E8B6557954F4974258",
-    "ciphertext": "8DFD658BB8D7EDF55651487E699760DBB7E0EA26E333AED7B915ED",
-    "metadata_key_hmac": "D7BE988EA13261746215EEE6A9A4328C04DBDD23FE1BAFB6CBCAD89DC5E31C0D"
-  },
-  {
-    "key_seed": "353356FBC0A7D3072B95CD06EF161B2E2671A5605ED895204822299DADA077BF",
-    "ldt_key": "3E93C47CBD43759FE750931CF658E2DD3FE6DF1BA927FA1C1A66D6E96BAF30BE6D4CDF5E1EB41CD797DC514B11379C4D6AD71A2B8E98494B94FEB3810B277908",
-    "hmac_key": "AD8FA4913963644D7C8F94C8A81CDFEE117F162BF34763F6B689848BBB8DD6E6",
-    "adv_salt": "6BA1",
-    "plaintext": "EE4D1B30A0DDB5ED135BB94E5DB5AF6A",
-    "ciphertext": "484731A93C8B923BB8A53DCA79E4F045",
-    "metadata_key_hmac": "DC75417B873E665D06E0C1A597BCCF9EBD945FA77918710C9C02AAFABA57FF4A"
-  },
-  {
-    "key_seed": "7FEDDBABBCA5FBF19085EFDF7FA449069C85BAC7F42CAECB8835EA6001064B2B",
-    "ldt_key": "B74C33B6186D08E721B87B613E0C918CD42DC3D2462EDA5ACC6EE1BA393DA2CA381C696575E6075AACF233544A05AC4D8A344D62AE29FE5699B909D1474AB1E8",
-    "hmac_key": "4410A7C075922CE821A7E32EB73F2594BECE696507EC3B31DE9A784685A41548",
-    "adv_salt": "CC8B",
-    "plaintext": "B90E090048412D9AD15738C13B42DB98E431A752278750",
-    "ciphertext": "4E3757534A4F9CF015564178C2C757A37D78B1299E8C3D",
-    "metadata_key_hmac": "4A09CE5BC6926AC6058095E3D8CF9E027CF3D34B50632BC22BFDFA291DEA5F22"
-  },
-  {
-    "key_seed": "7367DC37CC19142159373804965C43E9CA28AE843DBF542F2273EC8352CD4090",
-    "ldt_key": "3DE9CE878554D735593D98BFC7E6AA0CF8077AE0C79BF9D4D218D07595F3A3FEC332FE9100F95D7543DAF0DC9B48F931FA698ED2AC3D5D16D15F78CBC9BC61D6",
-    "hmac_key": "A23D66ABFD8EABE9660635249025DAD4D76E7D3B9EE76B6E03FE7383B8B2FE47",
-    "adv_salt": "CCF7",
-    "plaintext": "604310E1BA7E0921F0741CE25A0B3BC630822BEF8FA95D282A2B49",
-    "ciphertext": "94877AE1D36AB555C94B0DE4C12E6D07DC576302F5A4605C90F150",
-    "metadata_key_hmac": "1C0B94FE2B78CDDE0CFCD5C52FAC37288CC8F31D6502F0F0A8CC9B46136FB618"
-  },
-  {
-    "key_seed": "82D6B3CC208096CDA562F2A197EB0AF48526AA537FE696E7A5906FEB6A9D9BAE",
-    "ldt_key": "D76C5731D20F46709B4623DA5176A18362D4FBF31D0050209ACA5EC226495065734A1E5E4CB7279B9164736A68758154569AF342361BE36472B91BCD6357F636",
-    "hmac_key": "5B358CB9094F9A3AACC942A6ACD8B429ECB9CA9806245A53F20FD90C1DD7AF86",
-    "adv_salt": "ECB9",
-    "plaintext": "B614CCF667685C509C1E6F6007CE910ED3F4C10489B78981",
-    "ciphertext": "4087ED2E3DAA9CA529578A8D9BF7B82801B6C2F9993F9E01",
-    "metadata_key_hmac": "D3F2E705C732A1A40F561AE5CA652CE90FFD57F096BD0DD8693DE60E430A3850"
-  },
-  {
-    "key_seed": "C4AAF69D55BCC6809BA8047E46AE2951F9F9D1C2DA4D2EDB4497EE422449656B",
-    "ldt_key": "0DBE3FED8D595946E396085D37627D1DF502C62837C19E0C637378D7B113FE973039301AEDD1ADB5826454759C253194ECAE2F51AF658E24FF53B6DC0CAEBE8C",
-    "hmac_key": "1BA4C65445037AE9BA3E0B4DF02DC7284444724F01F046D1D27B55A373A26689",
-    "adv_salt": "BC66",
-    "plaintext": "02C2762DCD029C77B3680528879E928F2F",
-    "ciphertext": "421D0B4D61BA0E851D8C988ADDC1F7106F",
-    "metadata_key_hmac": "D41AC03BD3757F5722EA9178CB4A5F159987FDF4C8ECCA81019AF42430215704"
-  },
-  {
-    "key_seed": "EF6E76A452714F136B4BCA5E451532130AC5D54BE0F3E92BA400E822B5D5F663",
-    "ldt_key": "DC186901F3A8C80EACAF342B9738322B2DA0DAC2A9D120FBE5A5EA23B3CDA8DC84B169BF555A1FF97DF8401102A5A158B09A1C6D69736481D5F346749C64ABFF",
-    "hmac_key": "5BBB6C5A93CA93063E386F8DD215568F2F0D9F0204FE379C1640463330B4772F",
-    "adv_salt": "5AD2",
-    "plaintext": "1A4A986327D60F6DEA79523C538B8EB65ED0B98C88F213",
-    "ciphertext": "D7A1014E0F028131D1B68915AFFD4D32654B3F58E6B4A9",
-    "metadata_key_hmac": "994181ECDCFEA4919BEE1A1653F5A28B0B5EB6A08428999608C8F8CDCF273AD1"
-  },
-  {
-    "key_seed": "1FD6A210A15F7BB8F621BB455209783C7C3F97ED76091B5B73128A3DA89721D0",
-    "ldt_key": "B71CD649F8E878F541E26DFC7E9D2A268ADD1840CCBCF18D0D6D490C3C8DA1F1D8D757CE85AF2329A0FF6F00857637950A8D6CB150185DFACC6E7947AAF40565",
-    "hmac_key": "20066BCBA3ED8B17A8196406F9CA0321FF4E36D5869CEF40D5162641AD680D43",
-    "adv_salt": "B019",
-    "plaintext": "FC3A04D7DBE4298E89F2C55B9152C354DF3EFAB17D",
-    "ciphertext": "CAE8DBBD312ED58B62F3B0B5F2C3766C979B73F5AD",
-    "metadata_key_hmac": "5BEFB5B32828A6040761FED70437A20666E73E438CC6DBD2E2260EA08C40FA82"
-  },
-  {
-    "key_seed": "40796B04A81037C8DF0A7EECFF2A564D86918948AEF73824039E0502DFF24B9F",
-    "ldt_key": "D45D2B372108DA4BAFD1DB4AFA1E2A072D265A132AF2FAB10BB5DEF4923195299B21513BAAE0787AEF897DC6C4E3DC834E8B5B36650AF054724343B4A212D905",
-    "hmac_key": "02328122714079A49DD34A85287A93A70700DAECD16CC2EC334B35F92939FDA7",
-    "adv_salt": "1C7E",
-    "plaintext": "A7AC225B57D56BC713194CADE4C14DEC",
-    "ciphertext": "A09BC1E6E33B6D94DA83DC15609EF00F",
-    "metadata_key_hmac": "D8FBB71457FA8BB077DE75736BE088389F6937EEDEC83FDB915DE02AB6B73B1D"
-  },
-  {
-    "key_seed": "9266A6AF4A9BAC16EDE23A0991ABCF1BCBFD207B0C75EDCDC17026AE870E5AD7",
-    "ldt_key": "202D04E5717E093F366325B2D53C7099A21780208C164819B53A7408688F6DE1FA0FA9911A30CDD9C8DA9FD0D250D66D501AB085671FD3CEB49ACECC4F3FC574",
-    "hmac_key": "63C4A734233194AD20BA4B709A866B53FD8385A1109B1874B47C55D8A199026E",
-    "adv_salt": "B230",
-    "plaintext": "CA1EDF7C35B80DEADAA7F1FEF5D1E8B80B62912732",
-    "ciphertext": "E3E5FE59F681508AE5E89D4B4CFC0E1E2A76E864D7",
-    "metadata_key_hmac": "42D6811C114796FA032AD60EF690A55243BDC9F47BC033969C99A713CC8D6F6B"
-  },
-  {
-    "key_seed": "E9D71D9263432A4979FB8A909EC1EEDF8C5263BFAD9D0AB708CABACCE4BEF1A6",
-    "ldt_key": "D41DF680DB01DBEA500644D4EDAA1BFB2FD9C1E5F09CFC50B0669F9290EDAA7234AED1D2832E6A243DB22346829DB191B1E0B106573708E77ABED4EB2B937CF8",
-    "hmac_key": "9D067E24A5FFA79EB0651264DFBD2C16DF96139DEE2F4182DD93BEA3B43478AB",
-    "adv_salt": "17A7",
-    "plaintext": "D6418E0E2C7687F5F4AA42CA2A0702153B9F642494",
-    "ciphertext": "14486CE8E4401C765E9404A76A362C76E8D91AB48D",
-    "metadata_key_hmac": "93CBA7F6D5C459FF4263AC2E64ED865836A8897C11F20A09F748F39320A78ACC"
-  },
-  {
-    "key_seed": "182A610DB1B47A0E4FFA62DD17BFB4EDFD12C746B400404E3B08DC381970049A",
-    "ldt_key": "8A78B7701B7A0D700133A7DBFC5F887CCAD49BCAB67C896E32202C7DBE775215C6099EE9B0866DED3FA587937FA94F95B35EC100B42F749B8C7768264B00C586",
-    "hmac_key": "8C073133C495EBCFDE4A2F112531D9370DC1B29CBA2CDEC96ECDB9C3F90D4965",
-    "adv_salt": "E2A1",
-    "plaintext": "A27CEADE8B0B5F62644EC0D70DDCA0C826FEED598510F8",
-    "ciphertext": "3041284AF4D331E864DA9C517D4FFC04290281F736FFC4",
-    "metadata_key_hmac": "243CFC7A484FFB6862B69538B2CBCB1E90EDE91E594D8C25D9ED548B31D9F549"
-  },
-  {
-    "key_seed": "023B2309325EA8A81184CD8B43D24D43C24B09BC7C7736284849E688FB93AD40",
-    "ldt_key": "2B6EE387E86FBBB54E3F1AE88C0CD531806C7348E50551F90E857B5180E773027EB995378DB02C0F36F3F03B0F861E68D6B04793BAFD3C8477F9AFFB05701D4D",
-    "hmac_key": "1B91A69467B552C5F7504B2DCD0788F29AB7F7FE6112D96B529E5603C1F0A65D",
-    "adv_salt": "8302",
-    "plaintext": "168ABCFA4543F826FD99B738E0DF53F6100301E4FDE4760F37",
-    "ciphertext": "DBB976C88768AFCFF70E249EEC7D3F804EB57101C2CD0CB355",
-    "metadata_key_hmac": "243054355CC2D631349C6CF5451BAB3D03639A6DA36B26CA8682932DF5BE6F1B"
-  },
-  {
-    "key_seed": "C2608A5ACB87C16BFAF8F2AE92088076B65AA5987EC3637592B8839F38C9DDCC",
-    "ldt_key": "056EBDCD4E682DE1FA9C43BDA3F8D715D0B1ACE4295C2AF455745647420618416FC7FB0C7D86C55AA395740829080A91A45FD9EC82061F61C8D810C31638F300",
-    "hmac_key": "6FB4476757DBFFACF34854BD57FC6414D459951B6B56D15644806DA8DC0FEA3F",
-    "adv_salt": "D93E",
-    "plaintext": "A17DAE592FBB6C3DF1BAF7D14668F1EBB6DA919F67696E45B1C73F553BF4",
-    "ciphertext": "75C758D0640454385A60888556A185012B5923817D0524B5800EE15642E2",
-    "metadata_key_hmac": "56203005CC1F8542A56AB79D6C4DA6AE7C666D16331EB05D2CF1DF6DCF3899BB"
-  },
-  {
-    "key_seed": "EC99415836A71CD018FE253D06936A592CCD14FEE0ACC78057CF245CBD9BF69A",
-    "ldt_key": "29486562867DFAA482CFE845052DD28B3034B0522CCC6A0883B286EC41F52C6388FE7EF143962A8630604D0E4164566E920ADFC2AD950B281F9277FC592E99E5",
-    "hmac_key": "9BA951CBADA3DFBC74EEA42E622C6CCC862A64C71B3825B7CE266D87213668D2",
-    "adv_salt": "6365",
-    "plaintext": "9823CFB0C109E44CC231D89A8EBF12B6FF6A03D579",
-    "ciphertext": "A81B2F8688C563E0F918CE57BE04BAB25CBE6E3B08",
-    "metadata_key_hmac": "C98A745B649061123E20AD2DA609683FC9BC67AD694CDF8856C23D22DE6C6373"
-  },
-  {
-    "key_seed": "4A8000E1FF9A8DFDB1EF0A04D6E9523912E0E5A0FD90F4B8ACE2712B4D693944",
-    "ldt_key": "B19BEFF07C483C25B386EAB6C22C75AABEC196B16171D0ABC561610F2B9030F6F446236437D1E4F8E398A4DD1F08F01EB58A229AAAFC0D11F1F6FE5197BCEEC3",
-    "hmac_key": "C74FDA2826E06259EB44F16479DFBDDBE5294AB6C8208CC0EEA1F6AC889F105C",
-    "adv_salt": "8E59",
-    "plaintext": "B15F269256F5D40F8E2ED220AD6B8DFBCBCA7946A313C7B15FCB250FFA7B",
-    "ciphertext": "A520E9096C35B5AF65EA0288E4D9B5E0DC59E790D94CFBF3B88BAD4AE108",
-    "metadata_key_hmac": "9C25CF530ADB0054D44F4BD955050746CB7E937F3CD3104AAC530152973002D3"
-  },
-  {
-    "key_seed": "3DC8E89B729D04C912D224CFAEE6BA147FF4EB46665F2F68262DC42665BB6546",
-    "ldt_key": "5CB7DD6AB6443B38370CCB3FC5A618377A9917F725FF8C9B76239CA9C6E376054BE402BBDCEF6BB83F3DA8ED51F606D89C7DF6DA85E954D949689738E052B0DF",
-    "hmac_key": "3581EB8A202E79901AF0BCAE667AF7170F027927BA27668427EFC2C6024117EC",
-    "adv_salt": "D335",
-    "plaintext": "D4CA99804BC9A910004201C8B7590B95038C17",
-    "ciphertext": "BA09BD3FC063C81E3F72277458A7618EE900D1",
-    "metadata_key_hmac": "AB1ED3B3518AFC23B788549F5BC0B1FD6A201E97E278F817EA329A6BAB0309CC"
-  },
-  {
-    "key_seed": "0006CF5225E174EF822A30AB0529B69C108475DDBAA1AF43A44BF51C5D5DFA8B",
-    "ldt_key": "D92833285EC8FDDD39EA543E270852CF1DFEF74AD515E031D043167404A9A5C5C76210183496CE26B57AA958F5212C04CF316C5B124BA2CEE19FDCFB3A4E6F65",
-    "hmac_key": "CD5956A898365B6414ACB0D638EBDA608CD04FA59FDDC3A7CEAB493D43EE303E",
-    "adv_salt": "21F9",
-    "plaintext": "D8CA79DB9522E28903F1776A9671084425A33435318796B61720A5",
-    "ciphertext": "9F3CBAE4EB6F96B2EAB8435D9DDBEAB355EFF423AD14E928D55EDD",
-    "metadata_key_hmac": "4CB0388CE318A093938482D9708182DE20CDB1F501B9343C8FE24B2C2ABDBB0F"
-  },
-  {
-    "key_seed": "A35C0F85F77FE542D4DA5BAAA7E4C192E43F2B4769C9CF77C3BFE43610C31A89",
-    "ldt_key": "820DACB1C131D804BB7EB5F88F9C4424F6DDC2BEC47822DB14444AD7D9456264028FF618932076C1B127B221DE4BD85509F5B16C66AC070C113E94CB0C810C56",
-    "hmac_key": "94A5415308D1A6CF92901CC7169A16DF66C35DC04E79728F13EF8DD60AAAA97A",
-    "adv_salt": "75C0",
-    "plaintext": "97C8F490FFD81E751E58D14EE57320F87441CDBE7AE1E7F704",
-    "ciphertext": "778047CF7A20B43156AFDF2F6DB68A9354483CAB803E263473",
-    "metadata_key_hmac": "5F28CBB5A8CAF83F3C859C430162F1007E9E122FF3FDCBE1A5E9C7D7345E68AF"
-  },
-  {
-    "key_seed": "648D28EC0F235C056E19C50560E20C1C9153F1B1FD575ECBE61EC63C02946D48",
-    "ldt_key": "9F97C570D9EF9654996F39C42004E87A0DC1AB778FB80A6464018F809BA3C22CBB31C718F60CF337B4F3EF00A225E7F79266030EAB256718287A1375839420A1",
-    "hmac_key": "77D747DED1BDAE060B54AB5C14B2C505FBC1628ECDBC8A5403041EAB31B8CA35",
-    "adv_salt": "C47E",
-    "plaintext": "FB7AA8BFDEB67973132225B6628E590F06507F3224",
-    "ciphertext": "82C3DB57DDC5A82132950666E76FAFDE78915C525D",
-    "metadata_key_hmac": "76F3737220512662CBC506645D6353FCF625CB28D7F4749FEA2DAC66AFDBB4B6"
-  },
-  {
-    "key_seed": "AB266006BA27C9B8BDBC348E2F233458A47E1D081E2D8A7F75E1266BC3194A71",
-    "ldt_key": "FF0C1D83127BD01BDCA768A290EE460AF5AB7194713A8416C8F2B71654018174336F6947B3B65278D99B1A84F1FCF52EDCF69D54778F143EEF8039488D1E5444",
-    "hmac_key": "F0EBA62332D70ED32904E006ECD15023C74A2B31C6DD97FD71FAB9864B1254D1",
-    "adv_salt": "C6BA",
-    "plaintext": "C3D3FCEA6BB15407718A79DDB6E250C3AB1E29874C9666C9B9",
-    "ciphertext": "2ABA6882B6E4C2DC04DDA8837AE7526E4AC32182F245C5EF31",
-    "metadata_key_hmac": "0856FCC0CF22234B88CF87A3B28F9637B3F3021D6832ACE0831551201E12A18C"
-  },
-  {
-    "key_seed": "39422A1CB95CF979E27D915BEC424576212CFCB22478ED1DF6748D61001AC677",
-    "ldt_key": "04CA78ADF1E2A09A1C4DB8B3DABB5614DF1171A1908D1572AF42BAF252DC01DB76075F21F9DF2B2B1CC217980EEE430A5D2B699C34473D9B1BBCFFD21187F673",
-    "hmac_key": "24416CCC591932474B8CE26593952FC0B39842DA2C2D4496A24C7C18CBA078CA",
-    "adv_salt": "BEFE",
-    "plaintext": "992519171843560D593A00650010B419",
-    "ciphertext": "9289780CBCAD21A63B1331F4211B6CFD",
-    "metadata_key_hmac": "9C3383C57A759CFCEE5FDE6CE412ECDBB2F4B991EADCA608EE3D428B37D7A61D"
-  },
-  {
-    "key_seed": "AEDC72F40D1713CFEFC695094277BF4F09CD3700266DD5044B9AD4A30A4A7075",
-    "ldt_key": "B95B709E61493E41793C2BFB7352DFA905418AD306EA8D9EDE3CC7E0D751D91F430CF8821F8FFD291275CCF9ED309F914B74204608495CC9A011DFB04011846B",
-    "hmac_key": "7948CE0D76CA3A9885F14C3350111853030D260482D0A17EE9F5CF950E3FA7D0",
-    "adv_salt": "93E9",
-    "plaintext": "2CCF403EFC2BBF63CE6713CED63E5C722658B211",
-    "ciphertext": "DA024ACF0DBF96DD958A2B899556F7ED522807AF",
-    "metadata_key_hmac": "172D657C3C093D1171968D917EE56A9F18709C21E04B4864B5933BD231E1B24B"
-  },
-  {
-    "key_seed": "55F79B5A7A22E805336CC8F2910BB291FEAD07A7B85D88CF1101E652B889A578",
-    "ldt_key": "CA54FA3548DBCEF6807EFA9804CA53B382EA05CDBB7334846810481FE9F327639CD561844165EF1047DEA7E9AB90C7F92F4ABBE01091DEADF71B02B443868CF3",
-    "hmac_key": "44691723E9B6B50F4E5D05F8214864E7AFD29E9CDC8A29B593FB38D1146D86A9",
-    "adv_salt": "731C",
-    "plaintext": "7AB0D04C39CD376574505C3CDACE42FBAC93D00D9E63E918AE",
-    "ciphertext": "A1F451AE86A971B40C3EBBB301E1EC142B2B116C1BB5A7B4D9",
-    "metadata_key_hmac": "8DE18D0F719077ED04E28F8C83B6AC7116CD9FDC76AEC4B127092A17B25F7A35"
-  },
-  {
-    "key_seed": "E0E8F2C704B79101C4AE1AA6EA66DDFE3595442C77FAB77B08B5702796B8C80E",
-    "ldt_key": "4C3AB6C363C96574DE798F9297CAD532E2E4708ADE76D74465E09322FC9FC3A6817AC940A10ED4D0CF14E9D3435932AFDB7ED0526AE0BEBF47C81456CA9A6CD8",
-    "hmac_key": "C54A736A79A344E66E7B83F95FE44F858E7AC2C2E2AF79F9C346CE0E481E0AD6",
-    "adv_salt": "82AE",
-    "plaintext": "A9184787EEE39C0D471DE8EEEA9E1E7BDA0DDB37",
-    "ciphertext": "97F5675D53AF0BFAC6A58F6114C9DDFCE3DB0608",
-    "metadata_key_hmac": "1B6ADF08B7DF306C346A74AA74390BAE8DD23BC224EADFAC50B35182949D305A"
-  },
-  {
-    "key_seed": "3E045EC0E569AB8656C45B59F512E5B03DAD427D750F98DC6C4C4509B339A959",
-    "ldt_key": "6A5466A4C6A174D8B35DF32C2A08BDE5D4C5C9B1E779BD803FCE50FE190D82C327B50481B4E62D2C3902BEE106C6EE1946EA32B022B6B6557823270BC6AFBF38",
-    "hmac_key": "0FB3C7F3100261DA4420D5EC5B94C3855C82A7371D8A8F75C7E921CBA1154D81",
-    "adv_salt": "121A",
-    "plaintext": "40DB07B6329E6EFC10B79A004C8C544666FA13A81C96B37343",
-    "ciphertext": "CF082BA8DC959FED33D304CB5EC66B028329BFB3B2CA53184A",
-    "metadata_key_hmac": "E469FA33B76547861B26F80FB68FE60251E904F5997686A42C2ABE6E45AC6A7A"
-  },
-  {
-    "key_seed": "807DC7E3EBEF3B2797636D316401D339909EC63C23F9AF142BE19D7A5D4E9B31",
-    "ldt_key": "07709D1ADC769DA662876C45C89477D941B9C706CB11CB1FFB789BB3FA9410398321DC404CDE4CCE10801570CF3D83C2E88D5E8E12E5694CEFBAF6D67E9AAFAB",
-    "hmac_key": "23C7EB3B019361D502337C2035C571ABF7BCFF36EC2FA0B1B14CE816A6874E3E",
-    "adv_salt": "9E75",
-    "plaintext": "FAD1DE90363C6C1413EA1B1461CCC43F",
-    "ciphertext": "28EF13E59A2DAD573B0A1B0B927DF629",
-    "metadata_key_hmac": "8475DBA0DD7E6FFD7A1D9D0214B4A8EC4004BD7770422F37E68B2044569E1AD3"
-  },
-  {
-    "key_seed": "A62BE1A3BBDB99C67A9E0B9CCBDE92D581BB491A9332DFDE3A83D4DC8C821596",
-    "ldt_key": "9BEEF268C80A80BBD8B5151CB8BC6DBAC22E543BF65293E49864461AF501C9DBC56B84F3DC3495409D6D2D0F84329DB2A4A3F95F2902F46DCBC4D77FDCAB022B",
-    "hmac_key": "BE69A6D4391A1B7647D2A261F6FD19C807C03D7E873DC18ECA6BF7D7571A3881",
-    "adv_salt": "DA0B",
-    "plaintext": "C1F1DC66329A41743732DA0B5EC6C87771168427E60B",
-    "ciphertext": "65D9A77973EC29D6B3BC3E4A84E388DBEC013A63B8A3",
-    "metadata_key_hmac": "047094B27579869C1D81EA857627622B2AC6751E4E4F708FFBDAA844E34231C3"
-  },
-  {
-    "key_seed": "8225BC81B6A95E41A5FA6BE7BCDC4013C15B9F3E673EA91C99E4B8F9F9641F18",
-    "ldt_key": "88FCB6677A3C2565C40AB907496A5BD8DCB60195D4D5947826EFBA2E4407A2FAAB56E6ECB710ED440721BDA2E8CEA571AE28DFDF2D8AD0A0971BC21A853F948A",
-    "hmac_key": "F54EFD7864BF2FB87EDAAC78B187A9717AA7CE43BD0BDF0D6D83315D7D3CBD96",
-    "adv_salt": "86C0",
-    "plaintext": "8101E73DE0C94BA78E37CD8EA9CC6D0B3E52221166F1F3",
-    "ciphertext": "E5EC5C5A5BE3ACF76751F6E5C45910A3FBBF0A1039A8C3",
-    "metadata_key_hmac": "FD9FD785E938AC6719CCEA7AA30AB6B0AD34976AD53E902CDC9362F9DF978E9D"
-  },
-  {
-    "key_seed": "AE4BE66731EEC0ED59CC7F8E6A616D991326AAD5461806D9D8DDCC673FB1E74E",
-    "ldt_key": "7BBB3F3BB47613EAED7AF01630013423FB8FA2F0BCE3266D4C1C3358230B99FB5CC8C09D6CFDC712EEC9A3F8F2FF2915B0383B8CD2C21348C626F057F4F315AA",
-    "hmac_key": "8D6B94B9BD6DFE59C525F196ACEA419344A02954B274C0E4CEE516E5123B1E46",
-    "adv_salt": "C968",
-    "plaintext": "6BE1C9FD4B45E62BF3F3CBCF5A281ED900B1EF002755F201CF524F",
-    "ciphertext": "3CF6133D11238E9139329337EEED7E9C529A6D46C3506F7384295A",
-    "metadata_key_hmac": "73EDA04796A798B1D0A401560F8F569F1D8FCFAB1D1E4AADC404C02108D5C9FC"
-  },
-  {
-    "key_seed": "60763945AEC69521699FB1393024D7A4CA97FA63AAAC48B5507158909822B49C",
-    "ldt_key": "4624393C688256207C58D4EDF09234DD60754A7C11C5B6443BCA04CCA8194B7F28C02BB1A03CB0D51B8D57F53EA8A2DE2E2117C89E52B4B550EFE82437D32054",
-    "hmac_key": "7CF03F2124D4062F65D1F413E9A02CF91FD1AC9F4CEBFC2BCCE079EB3EBC902A",
-    "adv_salt": "78E7",
-    "plaintext": "0B9DC49C436D188F5E02A2F7EF72AA9E057197FBAAD81156DE40",
-    "ciphertext": "71C56A1CA97DEB8438D2BE29BA27C9D5AAB05810670EDF9B8FA3",
-    "metadata_key_hmac": "4DDEFEE27C077AED36492E7E9B490F81BBF921851BDD50E906F5E50E3538BBFB"
-  },
-  {
-    "key_seed": "ACB1D851F960017B5DCD5D2AEE38CE6221D22A2070592A7E1F2D56125DDEBE4D",
-    "ldt_key": "00B3A0B7A6017A6D4711257AEFD809A61A497F56D9BE053412589B284B3F8780E55D6ED711A10B6AF63CCEB8C493D59128460894B91CC07EAED4388EEDB90E4E",
-    "hmac_key": "F61FEA59CBE3E50C7570918B91C0C79C83D6D8B49F4C722A1A2DAAB7FEF93B22",
-    "adv_salt": "DB05",
-    "plaintext": "FBF9190FD2C1C63ED84BE2BDD4DA1878DCC922BCDC78478E25947344E58E",
-    "ciphertext": "BFFE6DE27A2648CCE6635A8DA00A559D7451F44BC1F92F10F3B55293189A",
-    "metadata_key_hmac": "708591407C24D5E37ACFB67A85E216D889FC6328A8371EDE4A48D6ACE2D38AA0"
-  },
-  {
-    "key_seed": "5892D24DE35450EFBDE91AB2B899337DC0FEB3E4E08F12AEFC21290C3BB0F5D1",
-    "ldt_key": "16B5A4776C7EB3FB15C122FF3C441B1216E8D81863D8DC22FFCB1606E9CB90FFB01205BCEFB026ABB6637DFE593AFA3B4DE8E5F67D7FCE8F7845C50EA73FCA10",
-    "hmac_key": "6B87C3F28E0B837A3642D3EA89A5AC66B6406FC6061B2A1230A6957E573F62BC",
-    "adv_salt": "7E5F",
-    "plaintext": "C70CFC105673CC008117C84C3B0B8B6B",
-    "ciphertext": "E7143E3EB9B085FE6CD8830C1AA85825",
-    "metadata_key_hmac": "03119A6B609DCC4A1A0671558F26408776CEF78962E84E0C264BDF71EF8A4C45"
-  },
-  {
-    "key_seed": "FCB53B88C29480D31C61C9A26C1FDBFB90F0C1E482EAF41CDCFC46C106D7FA92",
-    "ldt_key": "90740E785F31C6FD23F30EEF5A1BFB0DE75A1EE6D88CAD8FF84154132B6E41659F1CE14B28FBD09B671D2A2F789D33B94F3DE2C945A032EF8FD47A52B53A50C5",
-    "hmac_key": "F66E97715C702A60245C1E091082BCA6424B309C34D0A0267FCF41B2DDE10EAC",
-    "adv_salt": "F66B",
-    "plaintext": "024FADB6EA3D3841BADDA0030BAE72906B5E8E9A8B830CBC",
-    "ciphertext": "71A33F0C9E407FF1C68E25CA56F4D37B34EF1CF9DA2A05D7",
-    "metadata_key_hmac": "BE3C7A90CC50BA0426903025ECB40F5A1CB860C2F33D94847A5EA122AA919B5D"
-  },
-  {
-    "key_seed": "A3DE67DAD089DBEAA66B19E86D2FEA6687B14248838D5770243FB598F3C4A7E3",
-    "ldt_key": "3603B2E7AA69754C0B8D384EF0B23E6237556ED876310BAD17E1FD2C8F65B1AEBFC6769FB9FABC1EF97808591B9EA3E9A1720611F0620979ED3DDF1235E842D6",
-    "hmac_key": "7999C39246548C92251A110796666EE1889F756DB0EC6DF6B49D5A72699BDFD6",
-    "adv_salt": "337E",
-    "plaintext": "41D76111DD0F330918F3AB4E84B51EB9A2663C7D6F55B85DF58B2CA7",
-    "ciphertext": "72DF51254FFD1950A98ADA8075F6A93C442F1AD8CA7CF6C78F5CF237",
-    "metadata_key_hmac": "73442151CD271EC8795096EB3272ADFE33D86A774283D9BF209DC291C8327201"
-  },
-  {
-    "key_seed": "FC47F82D1CFDC44C45DBFD8B50FEE2550FB10F4C501A43F37F975F375169DC46",
-    "ldt_key": "8E92BA0192BF1CF8A7526A868AB03DF7AB60460C90890E9E3D5356978D7718249370906D430B548CDDE38CC79CC8A433997B656113D5C1449E5C92996B181ED2",
-    "hmac_key": "31B759712241DCD1B9BAA2932AE5E09D65FC7A8F61449E54E9732677D03238E5",
-    "adv_salt": "FD7F",
-    "plaintext": "0D83E137CA7BC4200B5158193B54FD0AD2EEE5A15A61F58E5402F326E30A",
-    "ciphertext": "656BB517EEBE4F2B48600DB4FBACFD5B188129BB288C34518A2CC82BDD42",
-    "metadata_key_hmac": "6120ADA9ED2B4C9FEDC2DB990924C33AAC3410E41ED41487C1CD7E52059434A2"
-  },
-  {
-    "key_seed": "75CDD577C4755E5A0740E8465AD380F178DBE0D64C14FBA56AEADCC204520F33",
-    "ldt_key": "68760B9FB6C651B1B4FA00EA2DD39FFECE7918B16C37C8E1AED39144178DFECD864450AE55F4F43C6EECF3354AD990A01802B2F16CCD3BCA5B2FF5ECE5D1F889",
-    "hmac_key": "8363C9EC9B08C3F5A36F81FBE6355B3BEAD57FDF534BC78A241D8D5EE09C0A55",
-    "adv_salt": "6639",
-    "plaintext": "58B1E6FDC85A719780CE6E55BAA86B69EBD383A9BEF3A128CB",
-    "ciphertext": "424FDA5EF7C74F2CCA242C4ED06CDF7F018C5E66C7D0D0636F",
-    "metadata_key_hmac": "18EF8AC0ED8CE9CCE1172BD4AED6236F0A4E4F1EE639326BB4D6B4E83512F279"
-  },
-  {
-    "key_seed": "964C1500301CDCC623F5CD0B1D7EC3D766A6409AD2D7C2380D15CD3D3A59C081",
-    "ldt_key": "0173131036E303C6BA39AB157529683888D4A3483ADC6C593A96F73469B4964EBAE07F195DE49C14453C962F197B6F4D302D0D9DCEF98A20B102FA99945CAD5F",
-    "hmac_key": "B7DA1265038925CDDB1C66C34F58B4ECDE531E4DEF61FED637877830FCD67B0B",
-    "adv_salt": "2D05",
-    "plaintext": "558A23FDA9CDFA4F8C4DA733C18FFD3350",
-    "ciphertext": "29ED3F11E844F8C5159597DD712FA7F005",
-    "metadata_key_hmac": "B914542CF3E6EDF434B1937A4EC11D792A487E0A2A2407D331262ACCDF23AB56"
-  },
-  {
-    "key_seed": "C98EA0AD4508C49C13886FC1F64FC531BF700FFD4643D1871AF50073DB70F7DC",
-    "ldt_key": "D0E28992554ED4D20875D2F18B3B0B6A3980C257CEF7C30ECF33DA9ABBA2184A2C6AE292D1CD7DB546D10B72837949B7AA066FF2AD20D341BDE483C7BBCD9333",
-    "hmac_key": "B4BCDAB34821AEBF3E5F62D64B89DE9B6EFDE9DA321BF8A7282E9BE1B2BF7487",
-    "adv_salt": "2C2D",
-    "plaintext": "6568B8EB33EE307B2D17E988F070425E433012FD2D6926",
-    "ciphertext": "C0D072E782E8657D4D1A3C94429555E05A96E508E1A724",
-    "metadata_key_hmac": "C575DAE69E903B641D0F065970B90481835F080D2356AD31F90851D0CC60F1C8"
-  },
-  {
-    "key_seed": "5068BA6E60D27009DBCF17FBBD2B4FB16F8E39BE8E08EC16655691C25791FEB4",
-    "ldt_key": "B574914DEC464D83F7FCEC28772C1079D7725E3E50B7CAD4B848908F0DB2F0C99DC89540F2DBE75BF010EA3251737EFDE7E673CCE08AA14273D50F3C0430B8F5",
-    "hmac_key": "70DB70FE8F8DDCD7513415F04E56FFEF713AE86B42A38AA324EA47F220D14E6B",
-    "adv_salt": "9E90",
-    "plaintext": "E725C15924229E8D3CB7A8A8B25BFF978A0887EC398CE0869CBDCD1E",
-    "ciphertext": "A168E685BE69D7A404743421580C99F3FBF3516D5605030BBC2B398F",
-    "metadata_key_hmac": "A7D3C12100FD3D35426999301D916FE8802BD87624224C6246DF64CEF1813E34"
-  },
-  {
-    "key_seed": "5C79EDCACA1D21B0994A45DAE06542883BCA0D8C04421A3809B11BEDA8DFCD54",
-    "ldt_key": "B786B17A1C9D89100AE1A4A624B291F60886631C85C5505C15A94754A03235D3D24867D08FAEFFFDC170052398BF78C52372A0670F90266DB4F7FFC866F80215",
-    "hmac_key": "571C8E8914F72F5A3A2D9B737FD5A090E42C5E1BFC1C9DCAC05635D47F8F31F3",
-    "adv_salt": "26FE",
-    "plaintext": "5D510AF1F8CB70050ADDDC19A504E9A8206A",
-    "ciphertext": "3956DA7AF1485B5CAFA372806FD0F0826A8F",
-    "metadata_key_hmac": "2AC2100526394E809ADF463BAF1639AF2C0714D5BCBEDDACAD557F8D5AB67638"
-  },
-  {
-    "key_seed": "EA2BE502D69156253AF8FA64E65C7039AFD10A5F3967E3BCD8803683E5A56DC5",
-    "ldt_key": "8C0BCD6ABBCCB13DCC92DC2E4E389E394A2C986DD33F1E11CEC0C53F9B4A75E70068820A7934376BE3F1D44320071E0F70A5FC828516088119CC90A59EAC7B46",
-    "hmac_key": "9CA3FD68708B906D46776F26CC37339E2F8CA5B2047E160C67E28CDC514B457F",
-    "adv_salt": "1DAF",
-    "plaintext": "ED01E3CDD1F0084D2B53904147FDD9DC5D8E68F7BFD8CFE5",
-    "ciphertext": "6735AA07367CF00FB6BBDE6C16CB0772589C64C0321AB78F",
-    "metadata_key_hmac": "C08A893EADEBC7444220E5B2C00076BEF31F0DF0A85BD3AAA571877247702AFB"
-  },
-  {
-    "key_seed": "70E69D3D65353E3AAB37DACA6F7E7619FB11AF47EA41F1801D0444B53A1EC877",
-    "ldt_key": "1D3CEE942FB6ACD5560A203EF5A22D6AC0A9DDFF66C4C6242B1934811E0CAC892247A258945BA677DAFD020AA2DAB8CBCF23B6594B9688F7D29830DA4107B49C",
-    "hmac_key": "C63FC0CDEA7ECAA9A05D0578DECCA5E3B539B892A44BF81A35665DA69DF46CBC",
-    "adv_salt": "A596",
-    "plaintext": "A6AD6912786A8F2413C2B71CF45060A419F3F085CB8F60D3FA20F033E1052E",
-    "ciphertext": "00A498DAE18DF3447AC0580A0E5D99D88B5002AF00E17D5CBB456BFD437D68",
-    "metadata_key_hmac": "7BC73A052E356779360968BF5785F5F24E429490C83B140263208D27D544FE16"
-  },
-  {
-    "key_seed": "95AC44DFECED7848D2BBDE48AE3644C643C159BF5ECBABFA397D6136C5A3BA00",
-    "ldt_key": "DE4AF5DA6B970F7990845837D06EC746C9833F272771F087E51E58DA39CA0C27EED3DA0F45432C0EC07D10DB1D57AEB0133D3A28D6B34FD9C4299852121C4CF7",
-    "hmac_key": "4EE27FABCA00B9C4DD92B94838B35F4BA456415F68BA968407D2D38FBBF86AC1",
-    "adv_salt": "F644",
-    "plaintext": "62417CDE107AE6A89F15BCF09BC2D56CAD43DBD6C4",
-    "ciphertext": "964C14893E03762FED52557949E1E67F3E477B7FF2",
-    "metadata_key_hmac": "EED2F02F9C579E936545F950F56B95CD0C033BF4359C1BD23224A37F3C8A03C6"
-  },
-  {
-    "key_seed": "9A1F41B0AC6959208C85B084EF2465CED116E845BFB3318BC04D3D4AFF2A91D0",
-    "ldt_key": "0963F3B352BDAC400086B79A8A07172E1A3D452EA85B0955E0D42923B13A6FB04EE16765FA9B001438CCD7799BD8136DC01476BB4D85BE129EAC71BF32EF0212",
-    "hmac_key": "A83A584B03BFA85341FF4148DF3F75D044BD4AB561D7F5363261E01DD5D59D0B",
-    "adv_salt": "716A",
-    "plaintext": "5BCCD691CC31878A7A557C688952D6641E9D4A8F37913E768C19D73197",
-    "ciphertext": "DA0845C303EBB48347A7F9C1F854721266C00574A0145EB099548A2C73",
-    "metadata_key_hmac": "01C60FDA43D3B64CECC9F05D3A58A43C464D4ACFAE8A3DA9161D40D7F94AC494"
-  },
-  {
-    "key_seed": "53BDBB22732643B193FD8CBAE1783519733E6598912E4AFE84F6E75122C4BF29",
-    "ldt_key": "80EAF347106A3FCA0A2159854923D4556307FC165F9097EA09CC356A00FB7281168B221318D0C6905AC614275B7E5405691EFF99BA9B751C2DC09155A4EB54CD",
-    "hmac_key": "F7347BF2808A389CD9E6DA5718AD89187E42F4D113B011B7A783F72814E9F852",
-    "adv_salt": "20BC",
-    "plaintext": "15D8E5A032CEE9A426F4BEBE1319B66C88D95B733103188E0313588DCA",
-    "ciphertext": "86E4B33EB62F4940FEF661B7CE8CB10F4F6FD9826ACC07BE615BB95DB2",
-    "metadata_key_hmac": "F172FEE4274F485FA6009EBD0D62F17E74E921BAFA79D88899E3078CD5751DB4"
-  },
-  {
-    "key_seed": "F5ACC9DADD3D04267BCAE2082578EA61C0F6C0267BE7EE187422E194914D5DE3",
-    "ldt_key": "BB3B714241A446A8DE5CE4B961911BFCD5EA42CEB785BF5FDF47F922107A9204075BA02AD360D5011598E697DBF440FE73BF8146848AEBBC4B5845459ED07EC1",
-    "hmac_key": "5737EE23F36ABD11975CA37D444409392E033C2E2951EBF569DCBA60EB671DEC",
-    "adv_salt": "8696",
-    "plaintext": "6D72189AA757296072E1301D281C2EBF2CDB589E9C7B4E4471E15DF3",
-    "ciphertext": "51EC36C9BA729F0C2DF4BC0ADA2C3B39359B408DBBE209CBC9EB8B09",
-    "metadata_key_hmac": "2485051451D77E97559C3E1518AD86F1F4D10DF855151F49DFA4011D774F0115"
-  },
-  {
-    "key_seed": "6783C15A78CE744C8C11306B938C660B291B59DE3BD36A39DE3DD467657F2FB4",
-    "ldt_key": "C84D751B752C5AE7399D125D081F1040FE416FF77683DBCB87481543401F8E59FC699BB0DF7CDB600DDEA21AB6B85FA12C8FE28A19C0A5891AB89B5EC8CCF848",
-    "hmac_key": "B84C3D612329F25B127FE18284510A908F6C1C9ACD5621C1E37AED36652B3398",
-    "adv_salt": "65C9",
-    "plaintext": "829452230FEFC15DB039063D2318EEC8A348BB1F",
-    "ciphertext": "32E19D511199B05E9812089BA1A08FD5986EA2B3",
-    "metadata_key_hmac": "870D01D40A6B8DCD39A7B638DCA32BC8CF7BC0E90B1641BC0051A7297103C3E6"
-  },
-  {
-    "key_seed": "68570C52852FC7954F372CC6B4552872AB1509F50A150523F4BA306B4C50D780",
-    "ldt_key": "CC660A62F836054EE66B265D8764F5359E3417AEB489E164CE7FD1438D77AD27611A861D5C352C1BF96337A3841A7016CA4985BD0D1BAD188EEF30DF5A5E8133",
-    "hmac_key": "4FA68AB3FBCF3529EF05CB3FEEBCF858D61004269E8F75ECB3262CB34CAB269A",
-    "adv_salt": "0E8C",
-    "plaintext": "6F4398291481C729C9ACC626D2A639DB7174",
-    "ciphertext": "CAD84F3CBCD28CE8AACA5102906E1BE33872",
-    "metadata_key_hmac": "84BC6A7B827C68E38858E2DE1149E8D939AF989DC7040C4473F7E03ADE429671"
-  },
-  {
-    "key_seed": "39CE44538302053859A5644BEFDA73B503795FF85A5E05E9C212C8806911A704",
-    "ldt_key": "1B58BC23C52F71AD309723B9F5D446F8D88194C80B316A9B0C9E9C78644D4771E8BD6DE52550673D95E551212C55F374F842CBBED5A10C32F241EDE1F4266714",
-    "hmac_key": "5F5790FC03CA669C92AD61A877D5CD7E561DCE1166A62FBE92809240201FA3F2",
-    "adv_salt": "2AA6",
-    "plaintext": "2D47FC43E3306D318E3BC1E0560F0E152A5C",
-    "ciphertext": "732124627DEFCD991E6D42ACCD8A370B2323",
-    "metadata_key_hmac": "0E8A0E36877C92BB61D8F4F85A7E0D02D973945694455D45132F83B806E8D9B2"
-  },
-  {
-    "key_seed": "3E1E131C0BE3413B9A98A219CC37B6CFBDE4653F758DA88B106402E3664ED94E",
-    "ldt_key": "63763C5633719656DDBF4CE8B941B2A7F3CCD952E63AE31AFC7C6985862984D0A427A605B8F58F9AFE0ABF992CAFF2DAEEFF2CDE5D12C29C1783C1D4DA87ED15",
-    "hmac_key": "CA5BDF30180EFF4970F82A5A8FF23A5C0C8D898FFD0937CB9F100E7F7CC09EB2",
-    "adv_salt": "0705",
-    "plaintext": "BEBEFF39A02B34F3B0A31CB9922D8B8967DF58175AD50626",
-    "ciphertext": "5DDE9DC2E6DE37F90ABAD3B3C12F38C3FD3403963E2B41DD",
-    "metadata_key_hmac": "461971F6E4AFB07BBA4D854E26125B2A84FF08A2BDEE9DD18A51ECEE070B8979"
-  },
-  {
-    "key_seed": "0EB58683B5CF94CAE8F55A4EA52179C034A9BB5255E8844F7E81B23F32F6A5A6",
-    "ldt_key": "D747228FF6B5FCE4C7816F2F5485673CA5948495DE0D0A786FB5AF66EB8231D3A001201129FB9C2E1D59E37AFB706A81B2D273A6AA790E259DDF223697111E11",
-    "hmac_key": "CF421B245B4927C7C9EC547DBEF2DE0FE60CB8066B4B48FD2D959DBE3BE5B135",
-    "adv_salt": "46DE",
-    "plaintext": "9677773693DCBF976497F1D627BBB69BE7B57433A2",
-    "ciphertext": "A06D673453F8920F82612ABEB9105C5A372EAFD6C6",
-    "metadata_key_hmac": "1C5B7FC597A70EA96879A4E355A328021517E8A52DB8E05CBEC6C2F210EE0C9A"
-  },
-  {
-    "key_seed": "703512A357CF1F2341B6EDE889D8307B5422B0387409BD8734267BE3840F33A2",
-    "ldt_key": "F75C2D1F0FA77227817F27D2BF50646A17E7DC139D0C139402799FCB990B8BEBBB11F9795F010A7407FCF2C5904F84B057099A8E00060A14DC46D27CB0E9B5D6",
-    "hmac_key": "091213A5ED437436F5CCFBD9708D95FC63103C737D187EA83054B159A9319DF2",
-    "adv_salt": "6CD6",
-    "plaintext": "B3A36BAAADAB47EB3A888C886CBE82DE584A860708523A47B7",
-    "ciphertext": "099F8D46E4FBEC2B2A480C496CFF3BF2E3464D1AC83A8ADA26",
-    "metadata_key_hmac": "D241EEF767BFD7695F0C7A2A93D847DBE4F79E2FE10A1E427DA5A7A9C81FA171"
-  },
-  {
-    "key_seed": "BC6D6CC4D0C775448DCA55337BBB16CC950E9BB1F537BF379C9A34153E1FCFBA",
-    "ldt_key": "7249BA6984ABA597609A304942255EA45D175E7E630F22071D494AD85FD500198572EF1B55061579C8A60042154832B664E9544B284245A7809F7E71634E6862",
-    "hmac_key": "AF1903DF4D7DD4B6981FAEB7668261DF02856934B72B23E21CB5F32F9940071C",
-    "adv_salt": "7A5A",
-    "plaintext": "B453B47C75FE8165ADB9A9C794BC91422955BD62",
-    "ciphertext": "229642496B0977C5EE5339877EB596B9A6112828",
-    "metadata_key_hmac": "68E4D0A891F049F151110B0897268607D117CE04E7BE86AD30AA65EDE1B76EBA"
-  },
-  {
-    "key_seed": "1D1B2722F3CD7184F7B55ABB2D5DE897048A9DB1AE4F53923646074FD651ABD4",
-    "ldt_key": "8FA39DBC12436D4C22EFB15B8071133F95E30D159B5DF923DCB56556F6C746DB83B5D2E397C27A8407633B6BD97AC5C09844A5F6FE63EE504E59C02153B9E379",
-    "hmac_key": "49EFB2DD19EC55928BA2DD0055290887EAFB81EC0DC8E58F7F9D0B4FA428223D",
-    "adv_salt": "890F",
-    "plaintext": "8D4030A8672074103B63D025CE23EFCC378DE5C31E2BBF147BB69BEDD90FE9",
-    "ciphertext": "EB90032A5802D2B1B863C1F0504D89C273718C567A47EF02AB1CFE0473DC43",
-    "metadata_key_hmac": "85255981481522F4AFA9D008A6307D34F52AB38DEB145C60F2836580725137F9"
-  },
-  {
-    "key_seed": "0EA7BCB5DB506CACD5FA1699E6758E7765383FEF79D153A6CB67C18E3EFB1F97",
-    "ldt_key": "0EF0AFD7C3329C05A941E5D062B9AE59841C2DA999DCA8999F4BEF8B1B9F8CE51ED9AD7CCB69124E0A85BA2FD9634E928AD96C0B81FB55B0D1D929C8395E2228",
-    "hmac_key": "F35887CA73B89610DC0CAF6E9C05F925F057FE0151C70F0E89FDE542ACF9F672",
-    "adv_salt": "E557",
-    "plaintext": "4213CA09EDCFC3C34DAAF9E1D624DDDF7E34EA55CEF20F57260A",
-    "ciphertext": "9D8E79B64F10A3763022A5CDBA43008D583B6EAAAC47DF64F429",
-    "metadata_key_hmac": "DDCDDD499DFD4A909FD66A26D420B627E4F8F9F855D870A774639BD3876FFBA8"
-  },
-  {
-    "key_seed": "94C0899E4868DC85CDC3DD9CD1A65BB1AB6BB7AF886733E04128528B7A2A465F",
-    "ldt_key": "0E9E6552B91C674F422EBB10DEE6172341AA1C7878C004883FF24E06C4F90475086AD232704B831A4012E8F60F099D250D53C9507EB232B734CD5246C3D35743",
-    "hmac_key": "C53A0D4D789182B586D67C128491E049E8A6B70D74ADAC5156DCCA369378CE98",
-    "adv_salt": "163A",
-    "plaintext": "FEADAC57B3E1C708290306BA8F657E1520C8280C",
-    "ciphertext": "3CE4F4228E22C8AB9603DE7D24474DE8A832E1AF",
-    "metadata_key_hmac": "B8482D5399DC21483CFCDC8B061722DC7E7A25A9EC22BA45891DF6663334A9B7"
-  },
-  {
-    "key_seed": "F370B420846D05786667A8860FE89A0106FB52BB0C14A10A9EE26F92008C85A9",
-    "ldt_key": "C905DE5A4D9A71D42DEBC09CA3551456B86692AB157637FFA69626D880791891C34F54EDBB914AD5799E4B919C43C205AAE659D391A6A48A01D16362F35E653A",
-    "hmac_key": "6F2BE000125B82A2E33DC8C9E43705D60526645724BB0AD44B21DA54B3583BD5",
-    "adv_salt": "E304",
-    "plaintext": "71CCB1045AE70B3006C31956BBE79C0A8DAF6CF384AE2641DF5AA56D",
-    "ciphertext": "36CCD64BCF938CF027FB6AA76F22DF8E15313033B2B2ECA5D8C62CAC",
-    "metadata_key_hmac": "656F0A8E99EBACA3C4CDAFBFE368CF1F7B1D6D21298B353588FAE6102714D3C3"
-  },
-  {
-    "key_seed": "F1F3A8615F2C5254F5705ED9C7B8DCC3E54EC6E26F976022A41FAEB58833CCE0",
-    "ldt_key": "B2F43E1E0EF3AC754BC7DBFB06150A03DECB6C13B2D59D372D32D1C4C95E12B17D05E104519226C34ED024AA42F35F97DF19228C7A960B3366FC81DED6D6067E",
-    "hmac_key": "46721F7126357AA3E1E1CF333D87CBE25D53A929EDE142A0B171661437FC65DC",
-    "adv_salt": "7375",
-    "plaintext": "5B457B5E1B1EB0C033A1EEA469BE0DA1",
-    "ciphertext": "0ED8CCF5D13E17B19C27E7E3E74547BE",
-    "metadata_key_hmac": "1A113F03BB294B74328BB167D3F86698BCEB4795D42F6CEF08463A36E0343E5A"
-  },
-  {
-    "key_seed": "352DE4E9A05C50430F06E1F5E7E305DC268154FD8B4474C82E11CCF326480B2D",
-    "ldt_key": "FC1A43A901581AD08C41BDFD5717ACA525CD62F284AB836FB162B7F201C4B88153DF23BD6E6BE90A1551B3F7C0BC4F21F764F4846C403972F6E87D5F50E776F2",
-    "hmac_key": "11F04E1C99267F9104136A72CC88F46BE4413596C562F9C26BEA27146C19FAF5",
-    "adv_salt": "7F58",
-    "plaintext": "64790AADB293C27C49D18289226FED7768411D0A22",
-    "ciphertext": "457C6407FC9CF2E7C1DF8FC907743A6002AF353453",
-    "metadata_key_hmac": "0CB22738098D7215F402CD5AA6E005FE47F72505591F93AB693EDF5FF9BAF2AF"
-  },
-  {
-    "key_seed": "66B6FAE0F6B0484EB7413DB51F7FCBAD1187BEFDDA329177ECD4B418A7CC557A",
-    "ldt_key": "3560736E3B71452A8935EF44775D1EA955B8C63268AD3A3DB58E4DFFCB9B3FD3BADFD7B71417BA845EA5EB88B2F5DBFB983CCD3AD3F39351C86294842660F489",
-    "hmac_key": "9095B581BE4680389A9281FD1FF244DB28BCA1F872EA4E00BBAE467D16B5683F",
-    "adv_salt": "E87E",
-    "plaintext": "C9374B60A3ED97BF64ACD5BF4CA75EBCCF",
-    "ciphertext": "9A0132A481D66099A9B5BF783BFE6E4616",
-    "metadata_key_hmac": "4A3ABD5626A0D2263CBA0259057FBD537B04B135D5674179516898AFEAF8781B"
-  },
-  {
-    "key_seed": "46F24B770B1ADE0340A91C81D3693BB5AB1900050912279F737AF059CE02C9F9",
-    "ldt_key": "36C3BDC7A4BB377CEBB55F60E9E7B49CAEE166968492384F1596EDEA86EE2EC9BA2EA6B52C5C7564028C9057CE6E0938DFE4935E0AD02D44FC98050675B7DD06",
-    "hmac_key": "D01263CED254613D29A70B1A64F0989C574F08125F27BBA1BB1AE2EE68634348",
-    "adv_salt": "FEBE",
-    "plaintext": "0C9E2CCD8F9790D33FEFD640621545F6",
-    "ciphertext": "812EE0C2A14C097DFEEAABD5EF6C387F",
-    "metadata_key_hmac": "7C7747CE38CFE5AE657EF6AAA504C47B5447E641B19248C4F3B67180E9D3FAB8"
-  },
-  {
-    "key_seed": "E71EDBE4DA0EA9CA5B4F083E56DE60643B96742B61C5948FA11ABE5FEE024324",
-    "ldt_key": "3F2DA23A6EFE5492830C82437E27FD26ED83BC22F573A4FA0B69129DB2E4DDDA60FD436DA8BC74F4380B740449AC54AD0E4CB963FB36C247216F3D2620ECAC2B",
-    "hmac_key": "65C79B5C0BAC21D938D560875098ED1CF44D198D765480A0FD9712FFA69536A7",
-    "adv_salt": "39DA",
-    "plaintext": "A6B6F2B67D86207B75568350E024ECAA971EA337A452C64CB411",
-    "ciphertext": "70A0F0C5798AA40E33E6E13570680070E185A862729EE27B45EB",
-    "metadata_key_hmac": "403423570D444DF6F0E23D5278F6E32598AF944DEB45119D10373C602E7EF470"
-  },
-  {
-    "key_seed": "4E72EE3AD7EEA1800CC1FD0FFA52FFBC956AD61B610E6D52D172151550CA3EDF",
-    "ldt_key": "017F11C19586862084AC204AE3F2A4E6CBD0AF118C500691B035DE40E34655750E60261AB3659F38F4F2839B1707D1A1E18B4E4BA34995030889DF3E42A16CAA",
-    "hmac_key": "DDAC2A8278227BD24601C4E7A60E5EB5202C6E08B3DB91163BFD8398C2F115E0",
-    "adv_salt": "11A0",
-    "plaintext": "E05B79A5E3400C9620245A24519EB7C644E1F75D3A7E661EBED2A7E05B",
-    "ciphertext": "316D773ADD8E7760838511133B0494F1E53E5C376206924BA988CEFA6F",
-    "metadata_key_hmac": "1D9EB4F8E2B8C0FF61A768F9D4DE38C50441ABEFA389C5C896B800B0010E54CB"
-  },
-  {
-    "key_seed": "29C4C4ACA1FE347FC57373BAE7B1608285BD124BD5D2ED1BCBE096CE34559BFE",
-    "ldt_key": "79BED0C41422EC9FC7FCDFD1A82F05FCCF4E2452720B22214D72C6B9832060C7C34FA7AF4203A5E71973EE0DC5FE4B6DEA39B2ECC4E6382CC227420B7A08C3A4",
-    "hmac_key": "74F0F33354E99AB617EF92E19126E6A23C5B7986274EE42EAE047879AABAF6CC",
-    "adv_salt": "CC68",
-    "plaintext": "19C23720321DCBFDA34B7CAEC42044C17C10",
-    "ciphertext": "C8B673F748D75F10860FDD68B0CECEAF628E",
-    "metadata_key_hmac": "DD707F9310D9F4F50CA323123C005021D62B18CD44DC1D2D7010F40E91BCDF6E"
-  },
-  {
-    "key_seed": "8AC90D32CFFFF80DD40A0E9DCB9126CC68074D963E2C6D9B9094B897867B891F",
-    "ldt_key": "712BE11E8FFC957C1AFB7889E7F26836CE216F17BC5833DB37FD18E6C184F48EA4561A44036E3527E5845283C00A88F29DBEEB7C92D11AC8049A83400C60A7C8",
-    "hmac_key": "61B07AD48CCAC2A71B1684EFE7E3D1CA142CF20CA8D390356AF795107D3F215B",
-    "adv_salt": "DE3A",
-    "plaintext": "F686116AE03B1982F591A80C702F5E37CD9767FDDA1A3D18629A28F5102E49",
-    "ciphertext": "7B9046041C3D232BF1A0513FF1CDA1AC4AA4205664C3F6DB560EB8BE668A60",
-    "metadata_key_hmac": "5A0EA84490DAEC977401C1DF0BDCE4D7F69A15A082943190DF6D30B456BECD67"
-  },
-  {
-    "key_seed": "A8CF63E3795D263AC7ECA88C9646EB1C20A0FB00C1A5DA5973C3D30687823311",
-    "ldt_key": "731278582AF984AF187232BC44EEB9C297A2DC0D54FB3948B2DF3ADF7D295AA1731BC316A1C95448800E2EF3604BA30CB616021ADA5AF671F7BCECE8D11B7FEB",
-    "hmac_key": "EFB2FC72054D63B14CB2966B174F328391B41D1B823F7A45598258857121BF81",
-    "adv_salt": "A42A",
-    "plaintext": "926736CFD879B5E15B5274341FC48863CF803CF0CB78CA2DFE",
-    "ciphertext": "EFA206EDC33DC9B6F294DF7FB0589297F3C70020326216759F",
-    "metadata_key_hmac": "88C796D31D909B1A958199EFE224524CD20ED2EF7F1219FCA961BAD79042A429"
-  },
-  {
-    "key_seed": "6CC2DB3AA70FF4258F8DA394570A07C6D7C62DD97200F9272EF3C504DDF663F6",
-    "ldt_key": "C067AE2090E91FD5FFD9BDD87316638266123C1DC15955E42C1485A93B857660236CE6C91F6EF4E781BABE5FD227A3269A8EAA77F97B41761C4F2484554FC302",
-    "hmac_key": "B83AB0B2AD00256C8983CE49D6A76B6D3028D64889E8EBA67DCB9C08F8AEDBD7",
-    "adv_salt": "BC3B",
-    "plaintext": "D54AB6B6A4BD0152C6AB414F8FEE8C44D44237BC5A1F",
-    "ciphertext": "3DD97FD1611D5B3D0579D9766E4C5D607709BCD0CDED",
-    "metadata_key_hmac": "F5C8FA8870CAE6C2C48C59478942DADF5E08A7CD1FB872613A78DCC69085D8C5"
-  },
-  {
-    "key_seed": "F3C4F33CDCE131D5DD270546F46E986717912EE27D246537CF7776C4238A04C1",
-    "ldt_key": "350EA4B98C555B9AE90900F547552580FAD3DB4C297FF3295602413FA7D6F6D905B735291C7EB4FFF834DB9601F479E2074F367BFC5D9F2F87B24489CBA05030",
-    "hmac_key": "1F0D7683CAFFF276AF4E63FD89028D69EE034F40CC5ADA7739ACFFDB712E24DD",
-    "adv_salt": "BCF1",
-    "plaintext": "DA459AC904B3634D1CDE2CF87ECDE60157ADDCC7F2F6C5",
-    "ciphertext": "45A3A3EC391FDFE6F56A592E0B428D9A200DCBA3866868",
-    "metadata_key_hmac": "FC1CE147E7A2C6CD10D9E52D4803C7C01F8874ECB52CB1E817E9C2EEEE9AAD3E"
+    "adv_salt": "BC3C",
+    "ciphertext": "E7BA016C044E5F65A32B7858C27BE1DB4B97640D5AC8F81CA166AA2A4AA955",
+    "hmac_key": "75A59414DD7AEAAE0518BCBD74A24850094E980E79EC63BC9B52349AB8210D66",
+    "identity_token_hmac": "FFC4D1BFDC4FBC90E39B089B90246AA5D32AB0D6407E0D1A2B327FC01AD2DFDE",
+    "key_seed": "03CCD2A359286FB1D0EDE3D61A340A2EDF0A7A9CC46F0F65CC2249021D650C50",
+    "ldt_key": "E1D3E029AE94ACB98A5B0FA63AD3FE4D52118E7D0615540C34B3427F3BDE06390BC26CC5303DB52260CD46696E9A245AC48B4299D72AC6DF95056A53A4004A01",
+    "plaintext": "63CD8D1E2EB4A2919FA24DB11E57C3F649D39FA14952A3FB0874A73B0B0CCD"
   }
 ]
diff --git a/nearby/presence/ldt_np_adv/src/lib.rs b/nearby/presence/ldt_np_adv/src/lib.rs
index 542d6a5..1d202f7 100644
--- a/nearby/presence/ldt_np_adv/src/lib.rs
+++ b/nearby/presence/ldt_np_adv/src/lib.rs
@@ -25,58 +25,104 @@
 
 use array_view::ArrayView;
 use core::fmt;
-use crypto_provider::{aes::BLOCK_SIZE, CryptoProvider};
-use ldt::{LdtDecryptCipher, LdtEncryptCipher, LdtError, Mix, Padder, Swap, XorPadder};
-use ldt_tbc::TweakableBlockCipher;
-use np_hkdf::{legacy_ldt_expanded_salt, NpHmacSha256Key, NpKeySeedHkdf};
+use core::ops;
+use crypto_provider::{aes::BLOCK_SIZE, CryptoProvider, CryptoRng, FromCryptoRng};
+use ldt::{LdtCipher, LdtDecryptCipher, LdtEncryptCipher, LdtError, Swap, XorPadder};
+use np_hkdf::{v0_ldt_expanded_salt, NpHmacSha256Key, NpKeySeedHkdf};
 use xts_aes::XtsAes128;
 
 /// Max LDT-XTS-AES data size: `(2 * AES block size) - 1`
 pub const LDT_XTS_AES_MAX_LEN: usize = 31;
 
-/// Legacy (v0) format uses a 14-byte metadata key
-pub const NP_LEGACY_METADATA_KEY_LEN: usize = 14;
+/// V0 format uses a 14-byte identity token
+pub const V0_IDENTITY_TOKEN_LEN: usize = 14;
 
-/// The salt included in an NP advertisement.
+/// Max payload size once identity token prefix has been removed
+pub const NP_LDT_MAX_EFFECTIVE_PAYLOAD_LEN: usize = LDT_XTS_AES_MAX_LEN - V0_IDENTITY_TOKEN_LEN;
+
+/// Length of a V0 advertisement salt
+pub const V0_SALT_LEN: usize = 2;
+
+/// The salt included in a V0 NP advertisement.
 /// LDT does not use an IV but can instead incorporate the 2 byte, regularly rotated,
 /// salt from the advertisement payload and XOR it with the padded tweak data.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct LegacySalt {
+pub struct V0Salt {
     /// Salt bytes extracted from the incoming NP advertisement
-    bytes: [u8; 2],
+    bytes: [u8; V0_SALT_LEN],
 }
 
-impl LegacySalt {
+impl V0Salt {
     /// Returns the salt as a byte array.
-    pub fn bytes(&self) -> &[u8; 2] {
-        &self.bytes
+    pub fn bytes(&self) -> [u8; V0_SALT_LEN] {
+        self.bytes
     }
 }
 
-impl From<[u8; 2]> for LegacySalt {
-    fn from(arr: [u8; 2]) -> Self {
+impl From<[u8; V0_SALT_LEN]> for V0Salt {
+    fn from(arr: [u8; V0_SALT_LEN]) -> Self {
         Self { bytes: arr }
     }
 }
 
-/// [LdtEncryptCipher] parameterized for XTS-AES-128 with the [Swap] mix function.
-pub type LdtEncrypterXtsAes128<C> = LdtEncryptCipher<{ BLOCK_SIZE }, XtsAes128<C>, Swap>;
+/// "Short" 14-byte identity token type employed for V0
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
+pub struct V0IdentityToken([u8; V0_IDENTITY_TOKEN_LEN]);
 
-/// A Nearby Presence specific LDT decrypter which verifies the hmac tag of the given payload
-/// parameterized for XTS-AES-128 with the [Swap] mix function.
-pub type LdtNpAdvDecrypterXtsAes128<C> =
-    LdtNpAdvDecrypter<{ BLOCK_SIZE }, LDT_XTS_AES_MAX_LEN, XtsAes128<C>, Swap, C>;
+impl V0IdentityToken {
+    /// Constructs a V0 identity token from raw bytes.
+    pub const fn new(value: [u8; V0_IDENTITY_TOKEN_LEN]) -> Self {
+        Self(value)
+    }
+    /// Returns the underlying bytes
+    pub fn bytes(&self) -> [u8; V0_IDENTITY_TOKEN_LEN] {
+        self.0
+    }
+
+    /// Returns the token bytes as a slice
+    pub fn as_slice(&self) -> &[u8] {
+        &self.0
+    }
+}
+
+impl From<[u8; V0_IDENTITY_TOKEN_LEN]> for V0IdentityToken {
+    fn from(value: [u8; V0_IDENTITY_TOKEN_LEN]) -> Self {
+        Self(value)
+    }
+}
+
+impl AsRef<[u8]> for V0IdentityToken {
+    fn as_ref(&self) -> &[u8] {
+        &self.0
+    }
+}
+
+impl FromCryptoRng for V0IdentityToken {
+    fn new_random<R: CryptoRng>(rng: &mut R) -> Self {
+        Self(rng.gen())
+    }
+}
+
+/// [LdtEncryptCipher] parameterized for XTS-AES-128 with the [Swap] mix function.
+pub type NpLdtEncryptCipher<C> = LdtEncryptCipher<{ BLOCK_SIZE }, XtsAes128<C>, Swap>;
+
+/// [LdtDecryptCipher] parameterized for XTS-AES-128 with the [Swap] mix function.
+type NpLdtDecryptCipher<C> = LdtDecryptCipher<{ BLOCK_SIZE }, XtsAes128<C>, Swap>;
+
+/// Range of valid NP LDT message lengths for encryption/decryption, in a convenient form that
+/// doesn't need a CryptoProvider parameter.
+pub const VALID_INPUT_LEN: ops::Range<usize> = BLOCK_SIZE..BLOCK_SIZE * 2;
 
 /// Build a Nearby Presence specific LDT XTS-AES-128 decrypter from a provided [NpKeySeedHkdf] and
 /// metadata_key_hmac, with the [Swap] mix function
 pub fn build_np_adv_decrypter_from_key_seed<C: CryptoProvider>(
     key_seed: &NpKeySeedHkdf<C>,
-    metadata_key_tag: [u8; 32],
-) -> LdtNpAdvDecrypterXtsAes128<C> {
+    identity_token_hmac: [u8; 32],
+) -> AuthenticatedNpLdtDecryptCipher<C> {
     build_np_adv_decrypter(
-        &key_seed.legacy_ldt_key(),
-        metadata_key_tag,
-        key_seed.legacy_metadata_key_hmac_key(),
+        &key_seed.v0_ldt_key(),
+        identity_token_hmac,
+        key_seed.v0_identity_token_hmac_key(),
     )
 }
 
@@ -84,28 +130,25 @@
 /// with the [Swap] mix function
 pub fn build_np_adv_decrypter<C: CryptoProvider>(
     ldt_key: &ldt::LdtKey<xts_aes::XtsAes128Key>,
-    metadata_key_tag: [u8; 32],
-    metadata_key_hmac_key: NpHmacSha256Key<C>,
-) -> LdtNpAdvDecrypterXtsAes128<C> {
-    LdtNpAdvDecrypter {
-        ldt_decrypter: LdtXtsAes128Decrypter::<C>::new(ldt_key),
-        metadata_key_tag,
-        metadata_key_hmac_key,
+    identity_token_hmac: [u8; 32],
+    identity_token_hmac_key: NpHmacSha256Key,
+) -> AuthenticatedNpLdtDecryptCipher<C> {
+    AuthenticatedNpLdtDecryptCipher {
+        ldt_decrypter: NpLdtDecryptCipher::<C>::new(ldt_key),
+        metadata_key_tag: identity_token_hmac,
+        metadata_key_hmac_key: identity_token_hmac_key,
     }
 }
 
-// [LdtDecryptCipher] parameterized for XTS-AES-128 with the [Swap] mix function.
-type LdtXtsAes128Decrypter<C> = LdtDecryptCipher<{ BLOCK_SIZE }, XtsAes128<C>, Swap>;
-
 /// Decrypts and validates a NP legacy format advertisement encrypted with LDT.
 ///
 /// A NP legacy advertisement will always be in the format of:
 ///
-/// Header (1 byte) | Identity DE header (1 byte) | Salt (2 bytes) | Identity (14 bytes) | repeated
+/// Header (1 byte) | Salt (2 bytes) | Identity token (14 bytes) | repeated
 /// { DE header | DE payload }
 ///
 /// Example:
-/// Header (1 byte) | Identity DE header (1 byte) | Salt (2 bytes) | Identity (14 bytes) |
+/// Header (1 byte) | Salt (2 bytes) | Identity token (14 bytes) |
 /// Tx power DE header (1 byte) | Tx power (1 byte) | Action DE header(1 byte) | action (1-3 bytes)
 ///
 /// The ciphertext bytes will always start with the Identity through the end of the
@@ -117,70 +160,61 @@
 /// `O` is the max output size (must be 2 * B - 1).
 /// `T` is the tweakable block cipher used by LDT.
 /// `M` is the mix function used by LDT.
-pub struct LdtNpAdvDecrypter<
-    const B: usize,
-    const O: usize,
-    T: TweakableBlockCipher<B>,
-    M: Mix,
-    C: CryptoProvider,
-> {
-    ldt_decrypter: LdtDecryptCipher<B, T, M>,
+pub struct AuthenticatedNpLdtDecryptCipher<C: CryptoProvider> {
+    ldt_decrypter: LdtDecryptCipher<BLOCK_SIZE, XtsAes128<C>, Swap>,
     metadata_key_tag: [u8; 32],
-    metadata_key_hmac_key: NpHmacSha256Key<C>,
+    metadata_key_hmac_key: NpHmacSha256Key,
 }
 
-impl<const B: usize, const O: usize, T, M, C> LdtNpAdvDecrypter<B, O, T, M, C>
-where
-    T: TweakableBlockCipher<B>,
-    M: Mix,
-    C: CryptoProvider,
-{
+impl<C: CryptoProvider> AuthenticatedNpLdtDecryptCipher<C> {
     /// Decrypt an advertisement payload using the provided padder.
     ///
-    /// If the plaintext's metadata key matches this item's MAC, return the plaintext, otherwise `None`.
+    /// If the plaintext's identity token matches this decrypter's MAC, returns the verified identity
+    /// token and the remaining plaintext (the bytes after the identity token).
     ///
     /// NOTE: because LDT acts as a PRP over the entire message, tampering with any bit scrambles
     /// the whole message, so we can leverage the MAC on just the metadata key to ensure integrity
     /// for the whole message.
     ///
     /// # Errors
-    /// - If `payload` has a length outside of `[B, B * 2)`.
+    /// - If `payload` has a length outside `[BLOCK_SIZE, BLOCK_SIZE * 2)`.
     /// - If the decrypted plaintext fails its HMAC validation
-    pub fn decrypt_and_verify<P: Padder<B, T>>(
+    #[allow(clippy::expect_used, clippy::indexing_slicing)]
+    pub fn decrypt_and_verify(
         &self,
         payload: &[u8],
-        padder: &P,
-    ) -> Result<ArrayView<u8, O>, LdtAdvDecryptError> {
-        assert_eq!(B * 2 - 1, O); // should be compiled away
-
-        // have to check length before passing to LDT to ensure copying into the buffer is safe
-        if payload.len() < B || payload.len() > O {
-            return Err(LdtAdvDecryptError::InvalidLength(payload.len()));
-        }
-
+        padder: &XorPadder<BLOCK_SIZE>,
+    ) -> Result<
+        (V0IdentityToken, ArrayView<u8, NP_LDT_MAX_EFFECTIVE_PAYLOAD_LEN>),
+        LdtAdvDecryptError,
+    > {
         // we copy to avoid exposing plaintext that hasn't been validated w/ hmac
-        let mut buffer = [0_u8; O];
-        buffer[..payload.len()].copy_from_slice(payload);
+        let mut buffer = [0_u8; LDT_XTS_AES_MAX_LEN];
+        let populated_buffer = buffer
+            .get_mut(..payload.len())
+            .ok_or(LdtAdvDecryptError::InvalidLength(payload.len()))?;
+        populated_buffer.copy_from_slice(payload);
 
-        #[allow(clippy::expect_used)]
-        self.ldt_decrypter
-            .decrypt(&mut buffer[..payload.len()], padder)
-            .map_err(|e| match e {
-                LdtError::InvalidLength(l) => LdtAdvDecryptError::InvalidLength(l),
-            })
-            .and_then(|_| {
-                self.metadata_key_hmac_key
-                    .verify_hmac(&buffer[..NP_LEGACY_METADATA_KEY_LEN], self.metadata_key_tag)
-                    .map_err(|_| LdtAdvDecryptError::MacMismatch)
-                    .map(|_| {
-                        ArrayView::try_from_array(buffer, payload.len())
-                            .expect("this will never be hit because the length is validated above")
-                    })
-            })
+        self.ldt_decrypter.decrypt(populated_buffer, padder).map_err(|e| match e {
+            LdtError::InvalidLength(l) => LdtAdvDecryptError::InvalidLength(l),
+        })?;
+        // slice is safe since input is a valid LDT-XTS-AES len
+        let identity_token = &populated_buffer[..V0_IDENTITY_TOKEN_LEN];
+        self.metadata_key_hmac_key
+            .verify_hmac::<C>(identity_token, self.metadata_key_tag)
+            .map_err(|_| LdtAdvDecryptError::MacMismatch)?;
+
+        let token_arr: [u8; V0_IDENTITY_TOKEN_LEN] =
+            identity_token.try_into().expect("Length verified above");
+        Ok((
+            token_arr.into(),
+            ArrayView::try_from_slice(&buffer[V0_IDENTITY_TOKEN_LEN..payload.len()])
+                .expect("Buffer len less token len is the max output len"),
+        ))
     }
 }
 
-/// Errors that can occur during [LdtNpAdvDecrypter::decrypt_and_verify].
+/// Errors that can occur during [AuthenticatedNpLdtDecryptCipher::decrypt_and_verify].
 #[derive(Debug, PartialEq, Eq)]
 pub enum LdtAdvDecryptError {
     /// The ciphertext data was an invalid length.
@@ -199,9 +233,8 @@
         }
     }
 }
+
 /// Build a XorPadder by HKDFing the NP advertisement salt
-pub fn salt_padder<const B: usize, C: CryptoProvider>(salt: LegacySalt) -> XorPadder<{ B }> {
-    // Assuming that the tweak size == the block size here, which it is for XTS.
-    // If that's ever not true, yet another generic parameter will address that.
-    XorPadder::from(legacy_ldt_expanded_salt::<B, C>(&salt.bytes))
+pub fn salt_padder<C: CryptoProvider>(salt: V0Salt) -> XorPadder<BLOCK_SIZE> {
+    XorPadder::from(v0_ldt_expanded_salt::<C>(&salt.bytes))
 }
diff --git a/nearby/presence/ldt_np_adv/src/np_adv_test_vectors.rs b/nearby/presence/ldt_np_adv/src/np_adv_test_vectors.rs
index cf176dd..488858d 100644
--- a/nearby/presence/ldt_np_adv/src/np_adv_test_vectors.rs
+++ b/nearby/presence/ldt_np_adv/src/np_adv_test_vectors.rs
@@ -11,21 +11,23 @@
 // 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::indexing_slicing, clippy::unwrap_used, clippy::panic, clippy::expect_used)]
 
 extern crate std;
+
 use crate::{
-    build_np_adv_decrypter_from_key_seed, salt_padder, LdtEncrypterXtsAes128, LegacySalt,
-    NP_LEGACY_METADATA_KEY_LEN,
+    build_np_adv_decrypter_from_key_seed, salt_padder, NpLdtEncryptCipher, V0Salt,
+    V0_IDENTITY_TOKEN_LEN,
 };
 use anyhow::anyhow;
 use crypto_provider_default::CryptoProviderImpl;
-use rand::Rng;
-use rand_ext::{random_vec_rc, seeded_rng};
+use ldt::LdtCipher;
 use serde_json::json;
 use std::vec::Vec;
 use std::{fs, io::Read as _, println, string::String};
 use test_helper::{extract_key_array, extract_key_vec};
+use test_vector_hkdf::TestVectorHkdf;
 
 #[test]
 fn np_adv_test_vectors() -> Result<(), anyhow::Error> {
@@ -39,26 +41,26 @@
         _ => return Err(anyhow!("bad json")),
     };
 
-    assert_eq!(1000, test_cases.len());
+    assert_eq!(100, test_cases.len());
 
     for tc in test_cases {
         let key_seed = extract_key_array::<32>(&tc, "key_seed");
 
         let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-        let ldt_key = hkdf.legacy_ldt_key();
-        let hmac_key = hkdf.legacy_metadata_key_hmac_key();
+        let ldt_key = hkdf.v0_ldt_key();
+        let hmac_key = hkdf.v0_identity_token_hmac_key();
 
         assert_eq!(&extract_key_vec(&tc, "ldt_key"), &ldt_key.as_concatenated());
         assert_eq!(&extract_key_vec(&tc, "hmac_key"), &hmac_key.as_bytes());
 
-        let salt = LegacySalt::from(extract_key_array(&tc, "adv_salt"));
-        let padder = salt_padder::<16, CryptoProviderImpl>(salt);
+        let salt = V0Salt::from(extract_key_array(&tc, "adv_salt"));
+        let padder = salt_padder::<CryptoProviderImpl>(salt);
 
-        let ldt_enc = LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(&ldt_key);
+        let ldt_enc = NpLdtEncryptCipher::<CryptoProviderImpl>::new(&ldt_key);
 
         let decrypter = build_np_adv_decrypter_from_key_seed(
             &hkdf,
-            extract_key_array(&tc, "metadata_key_hmac"),
+            extract_key_array(&tc, "identity_token_hmac"),
         );
 
         let plaintext = extract_key_vec(&tc, "plaintext");
@@ -69,9 +71,11 @@
 
         assert_eq!(ciphertext, ciphertext_actual);
 
-        let plaintext_actual = decrypter.decrypt_and_verify(&ciphertext, &padder).unwrap();
+        let (identity_token, plaintext_actual) =
+            decrypter.decrypt_and_verify(&ciphertext, &padder).unwrap();
 
-        assert_eq!(&plaintext, plaintext_actual.as_slice());
+        assert_eq!(&plaintext[..V0_IDENTITY_TOKEN_LEN], identity_token.as_slice());
+        assert_eq!(&plaintext[V0_IDENTITY_TOKEN_LEN..], plaintext_actual.as_slice());
     }
 
     Ok(())
@@ -81,23 +85,34 @@
 #[ignore]
 #[test]
 fn gen_test_vectors() {
-    let mut rng = seeded_rng();
-
     let mut array = Vec::<serde_json::Value>::new();
 
-    for _ in 0..1_000 {
-        let len =
-            rng.gen_range(crypto_provider::aes::BLOCK_SIZE..crypto_provider::aes::BLOCK_SIZE * 2);
-        let plaintext = random_vec_rc(&mut rng, len);
-        let key_seed: [u8; 32] = rng.gen();
+    for i in 0_u32..100 {
+        let test_vector_seed_hkdf = TestVectorHkdf::<CryptoProviderImpl>::new(
+            "NP LDT test vectors Y7yJTRXy+8saKiYSu76/TbabkAFhK55zF8QutxcQlGc",
+            &i.to_be_bytes(),
+        );
+
+        let len = test_vector_seed_hkdf
+            .derive_range_element(
+                "plaintext len",
+                crypto_provider::aes::BLOCK_SIZE as u64
+                    ..=crypto_provider::aes::BLOCK_SIZE as u64 * 2 - 1,
+            )
+            .try_into()
+            .unwrap();
+        let plaintext = test_vector_seed_hkdf.derive_vec("plaintext", len);
+        let key_seed: [u8; 32] = test_vector_seed_hkdf.derive_array("key seed");
 
         let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-        let ldt_key = hkdf.legacy_ldt_key();
-        let hmac_key = hkdf.legacy_metadata_key_hmac_key();
-        let hmac: [u8; 32] = hmac_key.calculate_hmac(&plaintext[..NP_LEGACY_METADATA_KEY_LEN]);
-        let ldt_enc = LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(&ldt_key);
+        let ldt_key = hkdf.v0_ldt_key();
+        let hmac_key = hkdf.v0_identity_token_hmac_key();
+        let hmac: [u8; 32] =
+            hmac_key.calculate_hmac::<CryptoProviderImpl>(&plaintext[..V0_IDENTITY_TOKEN_LEN]);
+        let ldt_enc = NpLdtEncryptCipher::<CryptoProviderImpl>::new(&ldt_key);
 
-        let padder = salt_padder::<16, CryptoProviderImpl>(LegacySalt::from(rng.gen::<[u8; 2]>()));
+        let salt = V0Salt::from(test_vector_seed_hkdf.derive_array("salt"));
+        let padder = salt_padder::<CryptoProviderImpl>(salt);
         let mut ciphertext = plaintext.clone();
         ldt_enc.encrypt(&mut ciphertext[..], &padder).unwrap();
 
@@ -105,12 +120,13 @@
             "key_seed": hex::encode_upper(key_seed),
             "ldt_key": hex::encode_upper(ldt_key.as_concatenated()),
             "hmac_key": hex::encode_upper(hmac_key.as_bytes()),
-            "adv_salt": hex::encode_upper(rng.gen::<[u8; 2]>()),
+            "adv_salt": hex::encode_upper(salt.bytes()),
             "plaintext": hex::encode_upper(plaintext),
             "ciphertext": hex::encode_upper(ciphertext),
-            "metadata_key_hmac": hex::encode_upper(hmac),
+            "identity_token_hmac": hex::encode_upper(hmac),
         }));
     }
 
     println!("{}", serde_json::ser::to_string_pretty(&array).unwrap());
+    panic!("Don't leave this test enabled. Meanwhile, enjoy the text output above.");
 }
diff --git a/nearby/presence/ldt_np_adv/src/tests.rs b/nearby/presence/ldt_np_adv/src/tests.rs
index 30bc7b6..a731762 100644
--- a/nearby/presence/ldt_np_adv/src/tests.rs
+++ b/nearby/presence/ldt_np_adv/src/tests.rs
@@ -16,18 +16,23 @@
 extern crate alloc;
 
 use crate::{
-    build_np_adv_decrypter_from_key_seed, salt_padder, LdtAdvDecryptError, LdtEncrypterXtsAes128,
-    LdtNpAdvDecrypterXtsAes128, LdtXtsAes128Decrypter, LegacySalt, LDT_XTS_AES_MAX_LEN,
-    NP_LEGACY_METADATA_KEY_LEN,
+    build_np_adv_decrypter_from_key_seed, salt_padder, AuthenticatedNpLdtDecryptCipher,
+    LdtAdvDecryptError, NpLdtDecryptCipher, NpLdtEncryptCipher, V0IdentityToken, V0Salt,
+    LDT_XTS_AES_MAX_LEN, V0_IDENTITY_TOKEN_LEN,
 };
 use alloc::vec::Vec;
 use crypto_provider::{CryptoProvider, CryptoRng};
 use crypto_provider_default::CryptoProviderImpl;
-use ldt::{DefaultPadder, LdtError, LdtKey, XorPadder};
+use ldt::{DefaultPadder, LdtCipher, LdtError, LdtKey, XorPadder};
 use np_hkdf::NpKeySeedHkdf;
 use rand::Rng;
 use rand_ext::{random_bytes, random_vec, seeded_rng};
 
+extern crate std;
+
+use crypto_provider::aes::BLOCK_SIZE;
+use std::vec;
+
 #[test]
 fn decrypt_matches_correct_ciphertext() {
     let mut rng = CryptoRng::new();
@@ -35,10 +40,11 @@
         let test_state = make_test_components::<CryptoProviderImpl>(&mut rng);
 
         let cipher = build_np_adv_decrypter_from_key_seed(&test_state.hkdf, test_state.hmac);
-        let decrypted =
+        let (token, plaintext) =
             cipher.decrypt_and_verify(&test_state.ciphertext, &test_state.padder).unwrap();
 
-        assert_eq!(&test_state.plaintext, decrypted.as_ref());
+        assert_eq!(test_state.identity_token, token);
+        assert_eq!(&test_state.remaining_plaintext, plaintext.as_ref());
     }
 }
 
@@ -85,7 +91,7 @@
 
         let cipher = test_state.ldt_enc;
 
-        let mut plaintext_copy = test_state.plaintext.clone();
+        let mut plaintext_copy = test_state.all_plaintext.clone();
         cipher.encrypt(&mut plaintext_copy[..], &test_state.padder).unwrap();
 
         assert_eq!(test_state.ciphertext, plaintext_copy);
@@ -96,83 +102,146 @@
 #[allow(deprecated)]
 fn encrypt_too_short_err() {
     let ldt_enc =
-        LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(&LdtKey::from_concatenated(&[0; 64]));
+        NpLdtEncryptCipher::<CryptoProviderImpl>::new(&LdtKey::from_concatenated(&[0; 64]));
 
-    let mut payload = [0; 7];
-    assert_eq!(Err(LdtError::InvalidLength(7)), ldt_enc.encrypt(&mut payload, &DefaultPadder));
+    let mut payload = vec![0; BLOCK_SIZE - 1];
+    assert_eq!(
+        Err(LdtError::InvalidLength(BLOCK_SIZE - 1)),
+        ldt_enc.encrypt(&mut payload, &DefaultPadder)
+    );
 }
 
 #[test]
 #[allow(deprecated)]
 fn encrypt_too_long_err() {
     let ldt_enc =
-        LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(&LdtKey::from_concatenated(&[0; 64]));
+        NpLdtEncryptCipher::<CryptoProviderImpl>::new(&LdtKey::from_concatenated(&[0; 64]));
 
-    let mut payload = [0; 40];
-    assert_eq!(Err(LdtError::InvalidLength(40)), ldt_enc.encrypt(&mut payload, &DefaultPadder));
+    let mut payload = vec![0; BLOCK_SIZE * 2];
+    assert_eq!(
+        Err(LdtError::InvalidLength(BLOCK_SIZE * 2)),
+        ldt_enc.encrypt(&mut payload, &DefaultPadder)
+    );
 }
 
 #[test]
 fn decrypt_too_short_err() {
-    let adv_cipher = LdtNpAdvDecrypterXtsAes128 {
-        ldt_decrypter: LdtXtsAes128Decrypter::<CryptoProviderImpl>::new(
-            &LdtKey::from_concatenated(&[0; 64]),
-        ),
-        metadata_key_tag: [0; 32],
-        metadata_key_hmac_key: np_hkdf::NpHmacSha256Key::<CryptoProviderImpl>::from([0; 32]),
-    };
+    let adv_cipher: AuthenticatedNpLdtDecryptCipher<CryptoProviderImpl> =
+        AuthenticatedNpLdtDecryptCipher {
+            ldt_decrypter: NpLdtDecryptCipher::<CryptoProviderImpl>::new(
+                &LdtKey::from_concatenated(&[0; 64]),
+            ),
+            metadata_key_tag: [0; 32],
+            metadata_key_hmac_key: np_hkdf::NpHmacSha256Key::from([0; 32]),
+        };
 
-    let payload = [0; 7];
+    // 1 byte less than a full block
+    let payload = vec![0; BLOCK_SIZE - 1];
     assert_eq!(
-        Err(LdtAdvDecryptError::InvalidLength(7)),
-        adv_cipher.decrypt_and_verify(&payload, &DefaultPadder)
+        Err(LdtAdvDecryptError::InvalidLength(BLOCK_SIZE - 1)),
+        adv_cipher.decrypt_and_verify(&payload, &XorPadder::from([0_u8; BLOCK_SIZE]))
     );
 }
 
 #[test]
 fn decrypt_too_long_err() {
-    let adv_cipher = LdtNpAdvDecrypterXtsAes128 {
-        ldt_decrypter: LdtXtsAes128Decrypter::<CryptoProviderImpl>::new(
-            &LdtKey::from_concatenated(&[0; 64]),
-        ),
-        metadata_key_tag: [0; 32],
-        metadata_key_hmac_key: np_hkdf::NpHmacSha256Key::<CryptoProviderImpl>::from([0; 32]),
-    };
+    let adv_cipher: AuthenticatedNpLdtDecryptCipher<CryptoProviderImpl> =
+        AuthenticatedNpLdtDecryptCipher {
+            ldt_decrypter: NpLdtDecryptCipher::<CryptoProviderImpl>::new(
+                &LdtKey::from_concatenated(&[0; 64]),
+            ),
+            metadata_key_tag: [0; 32],
+            metadata_key_hmac_key: np_hkdf::NpHmacSha256Key::from([0; 32]),
+        };
 
-    let payload = [0; 40];
+    // 2 full blocks
+    let payload = [0; BLOCK_SIZE * 2];
     assert_eq!(
-        Err(LdtAdvDecryptError::InvalidLength(40)),
-        adv_cipher.decrypt_and_verify(&payload, &DefaultPadder)
+        Err(LdtAdvDecryptError::InvalidLength(BLOCK_SIZE * 2)),
+        adv_cipher.decrypt_and_verify(&payload, &XorPadder::from([0; BLOCK_SIZE]))
     );
 }
 
+#[test]
+fn verify_input_len_const() {
+    // make sure it matches the authoritative upstream
+    assert_eq!(crate::VALID_INPUT_LEN, NpLdtEncryptCipher::<CryptoProviderImpl>::VALID_INPUT_LEN);
+}
+
+#[test]
+fn ldt_ffi_test_scenario() {
+    // used in ldt_ffi_tests.cc and LdtNpJniTests
+    let key_seed: [u8; 32] = [
+        204, 219, 36, 137, 233, 252, 172, 66, 179, 147, 72, 184, 148, 30, 209, 154, 29, 54, 14,
+        117, 224, 152, 200, 193, 94, 107, 28, 194, 182, 32, 205, 57,
+    ];
+
+    let salt: V0Salt = [12, 15].into();
+    let plaintext = [
+        205_u8, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174, 164, 0, 64, 200, 214,
+        123,
+    ];
+
+    let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+    let ldt_key = hkdf.v0_ldt_key();
+    let identity_token_hmac = hkdf
+        .v0_identity_token_hmac_key()
+        .calculate_hmac::<CryptoProviderImpl>(&plaintext[..V0_IDENTITY_TOKEN_LEN]);
+    let decrypter = build_np_adv_decrypter_from_key_seed(&hkdf, identity_token_hmac);
+
+    let padder = salt_padder::<CryptoProviderImpl>(salt);
+    let mut ciphertext = plaintext;
+    NpLdtEncryptCipher::<CryptoProviderImpl>::new(&ldt_key)
+        .encrypt(&mut ciphertext, &padder)
+        .unwrap();
+
+    let (token, contents) = decrypter.decrypt_and_verify(&ciphertext, &padder).unwrap();
+    assert_eq!(&plaintext[..V0_IDENTITY_TOKEN_LEN], token.as_slice());
+    assert_eq!(&plaintext[V0_IDENTITY_TOKEN_LEN..], contents.as_slice());
+
+    // Uncomment if updating the FFI test
+    // {
+    //     use std::println;
+    //     use test_helper::hex_bytes;
+    //     println!("Key seed:\n{}\n{}", hex_bytes(&key_seed), hex::encode_upper(&key_seed));
+    //     println!("Identity token HMAC:\n{}\n{}", hex_bytes(&identity_token_hmac), hex::encode_upper(&identity_token_hmac));
+    //     println!("Plaintext:\n{}\n{}", hex_bytes(&plaintext), hex::encode_upper(&plaintext));
+    //     println!("Salt:\n{}\n{}", hex_bytes(salt.bytes()), hex::encode_upper(salt.bytes()));
+    //     println!("Ciphertext:\n{}\n{}", hex_bytes(&ciphertext), hex::encode_upper(&ciphertext));
+    //     panic!();
+    // }
+}
+
 /// Returns (plaintext, ciphertext, padder, hmac key, MAC, ldt)
 fn make_test_components<C: crypto_provider::CryptoProvider>(
     rng: &mut C::CryptoRng,
 ) -> LdtAdvTestComponents<C> {
     // [1, 2) blocks of XTS-AES
     let mut rc_rng = seeded_rng();
-    let payload_len = rc_rng
-        .gen_range(crypto_provider::aes::BLOCK_SIZE..=(crypto_provider::aes::BLOCK_SIZE * 2 - 1));
-    let plaintext = random_vec::<C>(rng, payload_len);
+    let payload_len = rc_rng.gen_range(BLOCK_SIZE..=(BLOCK_SIZE * 2 - 1));
+    let all_plaintext = random_vec::<C>(rng, payload_len);
 
-    let salt = LegacySalt { bytes: random_bytes::<2, C>(rng) };
-    let padder = salt_padder::<16, C>(salt);
+    let salt = V0Salt { bytes: random_bytes::<2, C>(rng) };
+    let padder = salt_padder::<C>(salt);
 
     let key_seed: [u8; 32] = random_bytes::<32, C>(rng);
     let hkdf = np_hkdf::NpKeySeedHkdf::new(&key_seed);
-    let ldt_key = hkdf.legacy_ldt_key();
-    let hmac_key = hkdf.legacy_metadata_key_hmac_key();
-    let hmac: [u8; 32] = hmac_key.calculate_hmac(&plaintext[..NP_LEGACY_METADATA_KEY_LEN]);
+    let ldt_key = hkdf.v0_ldt_key();
+    let hmac_key = hkdf.v0_identity_token_hmac_key();
+    let identity_token: [u8; V0_IDENTITY_TOKEN_LEN] =
+        all_plaintext[..V0_IDENTITY_TOKEN_LEN].try_into().unwrap();
+    let hmac: [u8; 32] = hmac_key.calculate_hmac::<C>(&identity_token);
 
-    let ldt_enc = LdtEncrypterXtsAes128::<C>::new(&ldt_key);
+    let ldt_enc = NpLdtEncryptCipher::<C>::new(&ldt_key);
 
     let mut ciphertext = [0_u8; LDT_XTS_AES_MAX_LEN];
-    ciphertext[..plaintext.len()].copy_from_slice(&plaintext);
-    ldt_enc.encrypt(&mut ciphertext[..plaintext.len()], &padder).unwrap();
-
+    ciphertext[..all_plaintext.len()].copy_from_slice(&all_plaintext);
+    ldt_enc.encrypt(&mut ciphertext[..all_plaintext.len()], &padder).unwrap();
+    let remaining_plaintext = all_plaintext[V0_IDENTITY_TOKEN_LEN..].to_vec();
     LdtAdvTestComponents {
-        plaintext,
+        all_plaintext,
+        identity_token: identity_token.into(),
+        remaining_plaintext,
         ciphertext: ciphertext[..payload_len].to_vec(),
         padder,
         hmac,
@@ -182,10 +251,38 @@
 }
 
 struct LdtAdvTestComponents<C: CryptoProvider> {
-    plaintext: Vec<u8>,
+    all_plaintext: Vec<u8>,
+    identity_token: V0IdentityToken,
+    /// Plaintext after the identity token
+    remaining_plaintext: Vec<u8>,
     ciphertext: Vec<u8>,
-    padder: XorPadder<{ crypto_provider::aes::BLOCK_SIZE }>,
+    padder: XorPadder<{ BLOCK_SIZE }>,
     hmac: [u8; 32],
-    ldt_enc: LdtEncrypterXtsAes128<C>,
+    ldt_enc: NpLdtEncryptCipher<C>,
     hkdf: NpKeySeedHkdf<C>,
 }
+
+mod coverage_gaming {
+    extern crate std;
+
+    use crate::{V0IdentityToken, V0_IDENTITY_TOKEN_LEN};
+    use crypto_provider::{CryptoProvider, CryptoRng};
+    use crypto_provider_default::CryptoProviderImpl;
+    use std::{collections, format};
+
+    #[test]
+    fn legacy_identity_token() {
+        let token = V0IdentityToken::from([0; V0_IDENTITY_TOKEN_LEN]);
+        // debug
+        let _ = format!("{:?}", token);
+        // hash
+        let _ = collections::HashSet::new().insert(&token);
+        // bytes
+        assert_eq!(token.0, token.bytes());
+        // AsRef
+        assert_eq!(token.0.as_slice(), token.as_ref());
+        // FromCryptoRng
+        let mut rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
+        let _: V0IdentityToken = rng.gen();
+    }
+}
diff --git a/nearby/presence/ldt_np_adv_ffi/c/fuzz/ldt_fuzzer.cc b/nearby/presence/ldt_np_adv_ffi/c/fuzz/ldt_fuzzer.cc
index dbe136d..8e4f28f 100644
--- a/nearby/presence/ldt_np_adv_ffi/c/fuzz/ldt_fuzzer.cc
+++ b/nearby/presence/ldt_np_adv_ffi/c/fuzz/ldt_fuzzer.cc
@@ -82,11 +82,11 @@
   // https://commondatastorage.googleapis.com/chromium-boringssl-docs/hkdf.h.html
   // 32 byte HMAC-SHA256 key
   uint8_t metadata_key_hmac_key[32] = {0};
-  auto result = HKDF(
-      metadata_key_hmac_key, sizeof(metadata_key_hmac_key), EVP_sha256(),
-      (const uint8_t *)&key_seed.bytes, (size_t)32,
-      (const uint8_t *)"Google Nearby", (size_t)13,
-      (const uint8_t *)"Legacy metadata key verification HMAC key", (size_t)41);
+  auto result = HKDF(metadata_key_hmac_key, sizeof(metadata_key_hmac_key),
+                     EVP_sha256(), (const uint8_t *)&key_seed.bytes, (size_t)32,
+                     (const uint8_t *)"Google Nearby", (size_t)13,
+                     (const uint8_t *)"V0 Identity token verification HMAC key",
+                     (size_t)39);
   EXPECT_EQ(1, result);
   // calculate metadata key hmac using hkdf'd hmac key
   NpMetadataKeyHmac metadata_key_hmac = {.bytes = {0}};
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 7cd1fb3..65e0f68 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
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 #include <fstream>
+#include <iomanip>
 
 // TODO: get multi threaded tests working on windows
 #ifndef _WIN32
@@ -36,9 +37,9 @@
     204, 219, 36, 137, 233, 252, 172, 66,  179, 147, 72, 184, 148, 30, 209, 154,
     29,  54,  14, 117, 224, 152, 200, 193, 94,  107, 28, 194, 182, 32, 205, 57};
 static const uint8_t KNOWN_HMAC_BYTES[] = {
-    223, 185, 10,  31,  155, 31, 226, 141, 24,  187, 204,
-    165, 34,  64,  181, 204, 44, 203, 95,  141, 82,  137,
-    163, 203, 100, 235, 53,  65, 202, 97,  75,  180};
+    0xB4, 0xC5, 0x9F, 0xA5, 0x99, 0x24, 0x1B, 0x81, 0x75, 0x8D, 0x97,
+    0x6B, 0x5A, 0x62, 0x1C, 0x05, 0x23, 0x2F, 0xE1, 0xBF, 0x89, 0xAE,
+    0x59, 0x87, 0xCA, 0x25, 0x4C, 0x35, 0x54, 0xDC, 0xE5, 0x0E};
 static const uint8_t TEST_DATA_BYTES[] = {205, 104, 63, 225, 161, 209, 248,
                                           70,  84,  61, 10,  19,  212, 174,
                                           164, 0,   64, 200, 214, 123};
@@ -65,7 +66,7 @@
 static void hex_string_to_bytes(const char *hexString, uint8_t *out,
                                 size_t len) {
   for (size_t count = 0; count < len; count++) {
-    sscanf(hexString, "%2hhx", &out[count]); // NOLINT(cert-err34-c)
+    sscanf(hexString, "%2hhx", &out[count]);  // NOLINT(cert-err34-c)
     hexString += 2;
   }
 }
@@ -93,11 +94,11 @@
   if (!parsingSuccessful) {
     std::cout << reader.getFormattedErrorMessages() << "\n";
   }
-  ASSERT_TRUE(root.size() == 1000);
+  ASSERT_TRUE(root.size() == 100);
 
   for (const auto &v : root) {
     auto key_seed = v["key_seed"].asCString();
-    auto metadata_key_hmac = v["metadata_key_hmac"].asCString();
+    auto identity_token_hmac = v["identity_token_hmac"].asCString();
     auto adv_salt = v["adv_salt"].asCString();
     auto plaintext = v["plaintext"].asCString();
     auto ciphertext = v["ciphertext"].asCString();
@@ -105,12 +106,12 @@
     NpLdtKeySeed np_key_seed;
     auto len = strlen(key_seed) / 2;
     hex_string_to_bytes(key_seed, np_key_seed.bytes, len);
-    ASSERT_EQ(len, 32);
+    ASSERT_EQ(len, 32u);
 
     NpMetadataKeyHmac known_hmac;
-    len = strlen(metadata_key_hmac) / 2;
-    hex_string_to_bytes(metadata_key_hmac, known_hmac.bytes, len);
-    ASSERT_EQ(len, 32);
+    len = strlen(identity_token_hmac) / 2;
+    hex_string_to_bytes(identity_token_hmac, known_hmac.bytes, len);
+    ASSERT_EQ(len, 32u);
 
     NpLdtEncryptHandle enc_handle = NpLdtEncryptCreate(np_key_seed);
     ASSERT_TRUE(enc_handle.handle != 0);
@@ -118,7 +119,7 @@
     NpLdtSalt salt_data;
     len = strlen(adv_salt) / 2;
     hex_string_to_bytes(adv_salt, salt_data.bytes, len);
-    ASSERT_TRUE(len == 2);
+    ASSERT_TRUE(len == 2u);
 
     len = strlen(plaintext) / 2;
     auto buffer = (uint8_t *)malloc(len);
@@ -294,7 +295,6 @@
   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 (i = 0; i < num_threads; i++) ASSERT_EQ(pthread_join(tid[i], nullptr), 0);
 }
 #endif
diff --git a/nearby/presence/ldt_np_adv_ffi/src/handle_map.rs b/nearby/presence/ldt_np_adv_ffi/src/handle_map.rs
index 9a19562..669f2bb 100644
--- a/nearby/presence/ldt_np_adv_ffi/src/handle_map.rs
+++ b/nearby/presence/ldt_np_adv_ffi/src/handle_map.rs
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::Box;
 use crate::LdtAdvDecrypter;
 use crate::LdtAdvEncrypter;
 use core::marker::PhantomData;
diff --git a/nearby/presence/ldt_np_adv_ffi/src/lib.rs b/nearby/presence/ldt_np_adv_ffi/src/lib.rs
index 839135e..3ad6af4 100644
--- a/nearby/presence/ldt_np_adv_ffi/src/lib.rs
+++ b/nearby/presence/ldt_np_adv_ffi/src/lib.rs
@@ -24,19 +24,19 @@
 
 extern crate alloc;
 
-use alloc::boxed::Box;
 use core::slice;
 use crypto_provider_default::CryptoProviderImpl;
+use ldt::LdtCipher;
 use ldt_np_adv::{
-    build_np_adv_decrypter_from_key_seed, salt_padder, LdtAdvDecryptError, LdtEncrypterXtsAes128,
-    LdtNpAdvDecrypterXtsAes128, LegacySalt,
+    build_np_adv_decrypter_from_key_seed, salt_padder, AuthenticatedNpLdtDecryptCipher,
+    LdtAdvDecryptError, NpLdtEncryptCipher, V0Salt, V0_IDENTITY_TOKEN_LEN,
 };
 use np_hkdf::NpKeySeedHkdf;
 
 mod handle_map;
 
-pub(crate) type LdtAdvDecrypter = LdtNpAdvDecrypterXtsAes128<CryptoProviderImpl>;
-pub(crate) type LdtAdvEncrypter = LdtEncrypterXtsAes128<CryptoProviderImpl>;
+pub(crate) type LdtAdvDecrypter = AuthenticatedNpLdtDecryptCipher<CryptoProviderImpl>;
+pub(crate) type LdtAdvEncrypter = NpLdtEncryptCipher<CryptoProviderImpl>;
 
 const SUCCESS: i32 = 0;
 
@@ -81,7 +81,7 @@
 #[no_mangle]
 extern "C" fn NpLdtEncryptCreate(key_seed: NpLdtKeySeed) -> NpLdtEncryptHandle {
     let cipher = LdtAdvEncrypter::new(
-        &NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed.bytes).legacy_ldt_key(),
+        &NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed.bytes).v0_ldt_key(),
     );
     let handle = handle_map::get_enc_handle_map().insert::<CryptoProviderImpl>(Box::new(cipher));
     NpLdtEncryptHandle { handle }
@@ -116,7 +116,7 @@
 ) -> i32 {
     map_to_error_code(|| {
         let data = unsafe { slice::from_raw_parts_mut(buffer, buffer_len) };
-        let padder = salt_padder::<16, CryptoProviderImpl>(LegacySalt::from(salt.bytes));
+        let padder = salt_padder::<CryptoProviderImpl>(V0Salt::from(salt.bytes));
         handle_map::get_enc_handle_map()
             .get(&handle.handle)
             .map(|cipher| {
@@ -137,8 +137,9 @@
 ) -> i32 {
     map_to_error_code(|| {
         let data = unsafe { slice::from_raw_parts_mut(buffer, buffer_len) };
-        let padder = salt_padder::<16, CryptoProviderImpl>(LegacySalt::from(salt.bytes));
+        let padder = salt_padder::<CryptoProviderImpl>(V0Salt::from(salt.bytes));
 
+        #[allow(clippy::indexing_slicing)]
         handle_map::get_dec_handle_map()
             .get(&handle.handle)
             .map(|cipher| {
@@ -148,8 +149,10 @@
                         LdtAdvDecryptError::InvalidLength(_) => DecryptError::InvalidLength,
                         LdtAdvDecryptError::MacMismatch => DecryptError::HmacDoesntMatch,
                     })
-                    .map(|plaintext| {
-                        data.copy_from_slice(plaintext.as_slice());
+                    .map(|(token, plaintext)| {
+                        // slicing is safe: token and plaintext sum to data's len
+                        data[..V0_IDENTITY_TOKEN_LEN].copy_from_slice(token.as_slice());
+                        data[V0_IDENTITY_TOKEN_LEN..].copy_from_slice(plaintext.as_slice());
                         SUCCESS
                     })
             })
diff --git a/nearby/presence/ldt_np_jni/java/LdtNpJni/src/test/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJniTests.kt b/nearby/presence/ldt_np_jni/java/LdtNpJni/src/test/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJniTests.kt
index 07d29a3..e360599 100644
--- a/nearby/presence/ldt_np_jni/java/LdtNpJni/src/test/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJniTests.kt
+++ b/nearby/presence/ldt_np_jni/java/LdtNpJni/src/test/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJniTests.kt
@@ -22,15 +22,15 @@
 import org.junit.jupiter.api.assertThrows
 
 const val KEY_SEED = "CCDB2489E9FCAC42B39348B8941ED19A1D360E75E098C8C15E6B1CC2B620CD39"
-const val HMAC_TAG = "DFB90A1F9B1FE28D18BBCCA52240B5CC2CCB5F8D5289A3CB64EB3541CA614BB4"
+const val HMAC_TAG = "B4C59FA599241B81758D976B5A621C05232FE1BF89AE5987CA254C3554DCE50E"
 const val PLAINTEXT = "CD683FE1A1D1F846543D0A13D4AEA40040C8D67B"
-const val SALT_BYTES = "32EE"
-const val EXPECTED_CIPHER_TEXT = "04344411F1E57C841FE0F7150636BC782455059A"
+const val SALT_BYTES = "0C0F"
+const val EXPECTED_CIPHER_TEXT = "61E481C12F4DE24F2D4AB22D8908F80D3A3F9B40"
 
 class LdtNpJniTests {
   @Test
   fun roundTripTest() {
-    // Data taken from first test case in ldt_np_adv/resources/test/np_adv_test_vectors.json
+    // Data taken from ldt_ffi_test_scenario()
     val keySeed = KEY_SEED.decodeHex()
     val hmacTag = HMAC_TAG.decodeHex()
     val plaintext = PLAINTEXT.decodeHex()
@@ -164,4 +164,4 @@
   return chunked(2)
     .map { it.toInt(16).toByte() }
     .toByteArray()
-}
\ No newline at end of file
+}
diff --git a/nearby/presence/ldt_np_jni/src/lib.rs b/nearby/presence/ldt_np_jni/src/lib.rs
index e232c1e..334bcd9 100644
--- a/nearby/presence/ldt_np_jni/src/lib.rs
+++ b/nearby/presence/ldt_np_jni/src/lib.rs
@@ -36,8 +36,8 @@
     JNIEnv,
 };
 
-use ldt::XorPadder;
-use ldt_np_adv::{LdtAdvDecryptError, LdtEncrypterXtsAes128, LdtNpAdvDecrypterXtsAes128};
+use ldt::{LdtCipher, XorPadder};
+use ldt_np_adv::{AuthenticatedNpLdtDecryptCipher, LdtAdvDecryptError, NpLdtEncryptCipher};
 use np_hkdf::NpKeySeedHkdf;
 
 use crypto_provider_default::CryptoProviderImpl;
@@ -56,10 +56,11 @@
 /// Status code returned on successful cipher operations
 const SUCCESS: jint = 0;
 
-type LdtAdvDecrypter = LdtNpAdvDecrypterXtsAes128<CryptoProviderImpl>;
-type LdtAdvEncrypter = LdtEncrypterXtsAes128<CryptoProviderImpl>;
+type LdtAdvDecrypter = AuthenticatedNpLdtDecryptCipher<CryptoProviderImpl>;
+type LdtAdvEncrypter = NpLdtEncryptCipher<CryptoProviderImpl>;
 
 /// Marker trait to ensure above types are thread safe
+#[allow(dead_code)]
 trait JniThreadSafe: Send + Sync {}
 
 impl JniThreadSafe for LdtAdvDecrypter {}
@@ -90,7 +91,7 @@
             key_seed.as_slice().try_into().expect("Length is checked above"),
         );
 
-        let cipher = LdtAdvEncrypter::new(&hkdf_key_seed.legacy_ldt_key());
+        let cipher = LdtAdvEncrypter::new(&hkdf_key_seed.v0_ldt_key());
         box_to_handle(cipher).map_err(|_| CREATE_ERROR)
     })
 }
@@ -221,14 +222,15 @@
             })?;
 
         with_handle::<LdtAdvDecrypter, _, _>(handle, |cipher| {
-            let result = cipher
+            let (identity_token, plaintext) = cipher
                 .decrypt_and_verify(buffer.as_mut_slice(), &expand_np_salt_to_padder(salt))
                 .map_err(|err| match err {
                     LdtAdvDecryptError::InvalidLength(_) => DecryptError::DataLen,
                     LdtAdvDecryptError::MacMismatch => DecryptError::MacMisMatch,
                 })?;
 
-            let jbyte_buffer = bytes_to_jbytes(result.as_slice());
+            let concatenated = &[identity_token.as_slice(), plaintext.as_slice()].concat();
+            let jbyte_buffer = bytes_to_jbytes(concatenated);
 
             env.set_byte_array_region(&data, 0, jbyte_buffer)
                 .map_err(|_| DecryptError::JniOp)
@@ -301,7 +303,7 @@
 /// Returns a XorPadder containing the HKDF of the salt.
 fn expand_np_salt_to_padder(np_salt: jchar) -> XorPadder<{ crypto_provider::aes::BLOCK_SIZE }> {
     let salt_bytes = np_salt.to_be_bytes();
-    ldt_np_adv::salt_padder::<16, CryptoProviderImpl>(salt_bytes.into())
+    ldt_np_adv::salt_padder::<CryptoProviderImpl>(salt_bytes.into())
 }
 
 fn map_to_error_code<E: JniError, F: Fn() -> Result<jint, E>>(f: F) -> jint {
diff --git a/nearby/presence/np_adv/Cargo.toml b/nearby/presence/np_adv/Cargo.toml
index 9b576a6..5a72ecd 100644
--- a/nearby/presence/np_adv/Cargo.toml
+++ b/nearby/presence/np_adv/Cargo.toml
@@ -17,7 +17,7 @@
 crypto_provider.workspace = true
 strum.workspace = true
 strum_macros.workspace = true
-nom = { version = "7.1.3", default-features = false }
+nom.workspace = true
 lazy_static.workspace = true
 sink.workspace = true
 tinyvec.workspace = true
@@ -36,6 +36,7 @@
 serde.workspace = true
 anyhow.workspace = true
 test_helper = { path = "../test_helper" }
+test_vector_hkdf.workspace = true
 criterion.workspace = true
 crypto_provider_default = { workspace = true, features = ["std", "rustcrypto"] }
 np_ed25519 = { workspace = true, features = ["std"] }
diff --git a/nearby/presence/np_adv/benches/deser_adv.rs b/nearby/presence/np_adv/benches/deser_adv.rs
index b52f53a..0cc553b 100644
--- a/nearby/presence/np_adv/benches/deser_adv.rs
+++ b/nearby/presence/np_adv/benches/deser_adv.rs
@@ -21,34 +21,34 @@
     clippy::panic
 )]
 
-use core::marker::PhantomData;
 use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion};
-use crypto_provider::{CryptoProvider, CryptoRng};
+use crypto_provider::{ed25519, CryptoProvider, CryptoRng};
 use crypto_provider_default::CryptoProviderImpl;
-use ldt_np_adv::LegacySalt;
+use ldt_np_adv::{V0IdentityToken, V0Salt, V0_IDENTITY_TOKEN_LEN};
 
+use np_adv::credential::matched::EmptyMatchedCredential;
 use np_adv::deserialization_arena;
 use np_adv::extended::serialize::AdvertisementType;
+use np_adv::extended::V1IdentityToken;
+use np_adv::legacy::serialize::UnencryptedEncoder;
 use np_adv::{
     credential::{book::*, v0::*, v1::*, *},
-    de_type::EncryptedIdentityDataElementType,
     deserialize_advertisement,
     extended::{
         data_elements::{GenericDataElement, TxPowerDataElement},
         deserialize::VerificationMode,
         serialize::{
-            AdvBuilder as ExtendedAdvBuilder, MicEncryptedSectionEncoder, PublicSectionEncoder,
-            SectionBuilder, SectionEncoder, SignedEncryptedSectionEncoder,
+            AdvBuilder as ExtendedAdvBuilder, MicEncryptedSectionEncoder, SectionBuilder,
+            SectionEncoder, SignedEncryptedSectionEncoder, UnencryptedSectionEncoder,
         },
     },
     legacy::{
-        actions::{ActionBits, ActionsDataElement},
-        serialize::{AdvBuilder as LegacyAdvBuilder, LdtIdentity},
-        ShortMetadataKey,
+        data_elements::actions::{ActionBits, ActionsDataElement},
+        serialize::{AdvBuilder as LegacyAdvBuilder, LdtEncoder},
     },
-    shared_data::{ContextSyncSeqNum, TxPower},
-    MetadataKey, PublicIdentity,
+    shared_data::TxPower,
 };
+use np_hkdf::{DerivedSectionKeys, NpKeySeedHkdf};
 use rand::{Rng as _, SeedableRng as _};
 use strum::IntoEnumIterator;
 
@@ -67,42 +67,40 @@
                         ),
                         |b| {
                             let identities = (0..num_identities)
-                                .map(|_| V1Identity::random(&mut crypto_rng))
+                                .map(|_| V1Identity::random::<CryptoProviderImpl>(&mut crypto_rng))
                                 .collect::<Vec<_>>();
 
                             let mut adv_builder = ExtendedAdvBuilder::new(AdvertisementType::Encrypted);
 
                             // take the first n identities, one section per identity
                             for identity in identities.iter().take(num_sections) {
-                                let broadcast_cm = SimpleSignedBroadcastCryptoMaterial::new(
+                                let broadcast_cm = V1BroadcastCredential::new(
                                     identity.key_seed,
-                                    identity.extended_metadata_key,
-                                    identity.key_pair.private_key(),
+                                    identity.identity_token,
+                                    identity.private_key.clone(),
                                 );
                                 match identity_type {
                                     VerificationMode::Mic => {
                                         let mut sb = adv_builder
-                                            .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
+                                            .section_builder(MicEncryptedSectionEncoder::<_>::new_random_salt::<CryptoProviderImpl>(
                                                 &mut crypto_rng,
-                                                EncryptedIdentityDataElementType::Private,
                                                 &broadcast_cm,
                                             ))
                                             .unwrap();
 
                                         add_des(&mut sb);
-                                        sb.add_to_advertisement();
+                                        sb.add_to_advertisement::<CryptoProviderImpl>();
                                     }
                                     VerificationMode::Signature => {
                                         let mut sb = adv_builder
-                                            .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
+                                            .section_builder(SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
                                                 &mut crypto_rng,
-                                                EncryptedIdentityDataElementType::Private,
                                                 &broadcast_cm,
                                             ))
                                             .unwrap();
 
                                         add_des(&mut sb);
-                                        sb.add_to_advertisement();
+                                        sb.add_to_advertisement::<CryptoProviderImpl>();
                                     }
                                 }
                             }
@@ -112,7 +110,7 @@
                             run_with_v1_creds::<
                                 CryptoProviderImpl
                             >(
-                                b, crypto_type, identities, adv.as_slice()
+                                b, crypto_type, identities, adv.as_slice(),
                             )
                         },
                     );
@@ -126,10 +124,10 @@
     c.bench_function("Deser V1 plaintext: sections=1", |b| {
         let mut adv_builder = ExtendedAdvBuilder::new(AdvertisementType::Plaintext);
 
-        let mut sb = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+        let mut sb = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
 
         add_des(&mut sb);
-        sb.add_to_advertisement();
+        sb.add_to_advertisement::<CryptoProviderImpl>();
 
         let adv = adv_builder.into_advertisement();
 
@@ -157,20 +155,16 @@
 
                     let identity = &identities[0];
 
-                    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(
-                        identity.key_seed,
-                        identity.legacy_metadata_key,
-                    );
+                    let broadcast_cm =
+                        V0BroadcastCredential::new(identity.key_seed, identity.identity_token);
 
                     let mut adv_builder =
-                        LegacyAdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
-                            EncryptedIdentityDataElementType::Private,
-                            LegacySalt::from(rng.gen::<[u8; 2]>()),
+                        LegacyAdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(
+                            V0Salt::from(rng.gen::<[u8; 2]>()),
                             &broadcast_cm,
                         ));
 
-                    let mut action_bits = ActionBits::default();
-                    action_bits.set_action(ContextSyncSeqNum::try_from(3).unwrap());
+                    let action_bits = ActionBits::default();
                     adv_builder.add_data_element(ActionsDataElement::from(action_bits)).unwrap();
 
                     let adv = adv_builder.into_advertisement().unwrap();
@@ -188,10 +182,9 @@
 }
 
 pub fn deser_adv_v0_plaintext(c: &mut Criterion) {
-    let mut adv_builder = LegacyAdvBuilder::new(PublicIdentity);
+    let mut adv_builder = LegacyAdvBuilder::new(UnencryptedEncoder);
 
-    let mut action_bits = ActionBits::default();
-    action_bits.set_action(ContextSyncSeqNum::try_from(3).unwrap());
+    let action_bits = ActionBits::default();
     adv_builder.add_data_element(ActionsDataElement::from(action_bits)).unwrap();
     let adv = adv_builder.into_advertisement().unwrap();
 
@@ -227,16 +220,16 @@
 fn run_with_v0_creds<C>(
     b: &mut Bencher,
     crypto_material_type: CryptoMaterialType,
-    identities: Vec<V0Identity<C>>,
+    identities: Vec<V0Identity>,
     adv: &[u8],
 ) where
     C: CryptoProvider,
 {
     let mut creds = identities
         .into_iter()
-        .map(|identity| identity.into_discovery_credential())
-        .map(|discovery_credential| MatchableCredential {
-            discovery_credential,
+        .map(|identity| identity.into_discovery_credential::<C>())
+        .map(|crypto_material| MatchableCredential {
+            discovery_credential: crypto_material,
             match_data: EmptyMatchedCredential,
         })
         .collect::<Vec<_>>();
@@ -283,16 +276,16 @@
 fn run_with_v1_creds<C>(
     b: &mut Bencher,
     crypto_material_type: CryptoMaterialType,
-    identities: Vec<V1Identity<C>>,
+    identities: Vec<V1Identity>,
     adv: &[u8],
 ) where
     C: CryptoProvider,
 {
     let mut creds = identities
         .into_iter()
-        .map(|identity| identity.into_discovery_credential())
-        .map(|discovery_credential| MatchableCredential {
-            discovery_credential,
+        .map(|identity| identity.into_discovery_credential::<C>())
+        .map(|crypto_material| MatchableCredential {
+            discovery_credential: crypto_material,
             match_data: EmptyMatchedCredential,
         })
         .collect::<Vec<_>>();
@@ -333,6 +326,7 @@
         }
     }
 }
+
 fn add_des<I: SectionEncoder>(
     sb: &mut SectionBuilder<&mut np_adv::extended::serialize::AdvBuilder, I>,
 ) {
@@ -348,51 +342,61 @@
 );
 criterion_main!(benches);
 
-struct V0Identity<C: CryptoProvider> {
+struct V0Identity {
     key_seed: [u8; 32],
-    legacy_metadata_key: ShortMetadataKey,
-    _marker: PhantomData<C>,
+    identity_token: V0IdentityToken,
 }
 
-impl<C: CryptoProvider> V0Identity<C> {
+impl V0Identity {
     /// Generate a new identity with random crypto material
     fn random<R: rand::Rng + rand::CryptoRng>(rng: &mut R) -> Self {
         Self {
             key_seed: rng.gen(),
-            legacy_metadata_key: ShortMetadataKey(rng.gen()),
-            _marker: PhantomData,
+            identity_token: V0IdentityToken::from(rng.gen::<[u8; V0_IDENTITY_TOKEN_LEN]>()),
         }
     }
     /// Convert this `V0Identity` into a V0 discovery credential.
-    fn into_discovery_credential(self) -> V0DiscoveryCredential {
-        SimpleBroadcastCryptoMaterial::<V0>::new(self.key_seed, self.legacy_metadata_key)
-            .derive_v0_discovery_credential::<C>()
+    fn into_discovery_credential<C: CryptoProvider>(self) -> V0DiscoveryCredential {
+        let hkdf = NpKeySeedHkdf::<C>::new(&self.key_seed);
+        V0DiscoveryCredential::new(
+            self.key_seed,
+            hkdf.v0_identity_token_hmac_key().calculate_hmac::<C>(self.identity_token.as_slice()),
+        )
     }
 }
 
-struct V1Identity<C: CryptoProvider> {
+struct V1Identity {
     key_seed: [u8; 32],
-    extended_metadata_key: MetadataKey,
-    key_pair: np_ed25519::KeyPair<C>,
+    identity_token: V1IdentityToken,
+    private_key: ed25519::PrivateKey,
 }
 
-impl<C: CryptoProvider> V1Identity<C> {
+impl V1Identity {
     /// Generate a new identity with random crypto material
-    fn random(rng: &mut C::CryptoRng) -> Self {
+    fn random<C: CryptoProvider>(rng: &mut C::CryptoRng) -> Self {
         Self {
             key_seed: rng.gen(),
-            extended_metadata_key: MetadataKey(rng.gen()),
-            key_pair: np_ed25519::KeyPair::<C>::generate(),
+            identity_token: rng.gen(),
+            private_key: ed25519::PrivateKey::generate::<C::Ed25519>(),
         }
     }
     /// Convert this `V1Identity` into a `V1DiscoveryCredential`.
-    fn into_discovery_credential(self) -> V1DiscoveryCredential {
-        SimpleSignedBroadcastCryptoMaterial::new(
+    fn into_discovery_credential<C: CryptoProvider>(self) -> V1DiscoveryCredential {
+        let hkdf = NpKeySeedHkdf::<C>::new(&self.key_seed);
+
+        V1DiscoveryCredential::new(
             self.key_seed,
-            self.extended_metadata_key,
-            self.key_pair.private_key(),
+            hkdf.v1_mic_short_salt_keys()
+                .identity_token_hmac_key()
+                .calculate_hmac::<C>(self.identity_token.as_slice()),
+            hkdf.v1_mic_extended_salt_keys()
+                .identity_token_hmac_key()
+                .calculate_hmac::<C>(self.identity_token.as_slice()),
+            hkdf.v1_signature_keys()
+                .identity_token_hmac_key()
+                .calculate_hmac::<C>(self.identity_token.as_slice()),
+            self.private_key.derive_public_key::<C::Ed25519>(),
         )
-        .derive_v1_discovery_credential::<C>()
     }
 }
 
diff --git a/nearby/presence/np_adv/resources/test/mic-encrypted-test-vectors.json b/nearby/presence/np_adv/resources/test/mic-encrypted-test-vectors.json
deleted file mode 100644
index a7834ec..0000000
--- a/nearby/presence/np_adv/resources/test/mic-encrypted-test-vectors.json
+++ /dev/null
@@ -1,2295 +0,0 @@
-[
-  {
-    "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",
-    "identity_type": "trusted",
-    "key_seed": "F0ED9126768CE7DC685FF74932AC5A876442C4E42359A43F720A575142A45043",
-    "metadata_key": "45795EE4C6533A830886E2C5885EB9E5",
-    "nonce": "40E95D525FAEA1C1FEE39A8E",
-    "section_mic_hmac_key": "A22386E85112EF883218A5B75669B7102E017E9AA149F408A079F60B1D14B4F4",
-    "section_salt": "56F596D16E1F87B107EE86102FFC6D5E"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "F076F2B4FA1F3704DDC4EC5CD60C4657",
-    "data_elements": [
-      {
-        "contents": "B62889",
-        "de_type": 72
-      },
-      {
-        "contents": "68FDB8895CB7ABCF18390616113107B73321A905D22FDB99",
-        "de_type": 175
-      },
-      {
-        "contents": "D5A173DC337EFA759D443C50F14F25AC18A06D44C4",
-        "de_type": 354
-      }
-    ],
-    "encoded_section": "6D911000CB9C39FAF5D89CEF1FD7E313DC2F95D79004919332D945C714DE3B58B0762E7F9378D80F35D0DE1E8BD321656B270EA3379F029FB09E44AAFB091B0678A0AB69B055CCE5C2F576EAE52DAC0D6C11FD6A878ABF337B9647FF259ED24F5687641E8ED13A612810F22EC240",
-    "identity_type": "provisioned",
-    "key_seed": "907E34F78FBC8CA40FCA224C12B95BA2C4A0148BA0D19BDADEE1798579EF7D35",
-    "metadata_key": "B524D8ABD8E7DCFD13CE922F2FD9EFAC",
-    "nonce": "AD0AC7BE3A89262786A55139",
-    "section_mic_hmac_key": "690542812A39DB48DD638AED6021A250767F79604C638E361A8F7F5FE92C5E40",
-    "section_salt": "CB9C39FAF5D89CEF1FD7E313DC2F95D7"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "B9549633BA2531AEF16D592815C12269",
-    "data_elements": [
-      {
-        "contents": "BA",
-        "de_type": 79
-      },
-      {
-        "contents": "796F131D0D034508",
-        "de_type": 141
-      },
-      {
-        "contents": "0245",
-        "de_type": 522
-      }
-    ],
-    "encoded_section": "48911000897E4DB6C940976334871B64811D8379900416AC9B5760E31F037634EED3B9C81AEF75B5134E7065DCEFF8CF3197BD2242BE4E29384125B35FB9A4BB7D4D1A650F1E30DBA3",
-    "identity_type": "provisioned",
-    "key_seed": "E66AECA540D070C5E430CE461A3E209F0650AAF3E69CC79C20935DEA3D468A46",
-    "metadata_key": "B2AC31CB7B88B7FA165B5E41AE706E04",
-    "nonce": "E3CB89790FEA866E19F4926A",
-    "section_mic_hmac_key": "EE72E920E379F06879BD8AED8EF9BF8D5903E5CF94A83815929260521F84C17C",
-    "section_salt": "897E4DB6C940976334871B64811D8379"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "088B6339D6D727E797DD1EA3EAC999B3",
-    "data_elements": [
-      {
-        "contents": "DDCCC496B0BE050B7607",
-        "de_type": 850
-      }
-    ],
-    "encoded_section": "429110008F292492B012ACE2C0EF4F25E0901F9490022CF669461319C1165AA131A7BF2835C97FC3ECD77522FB93CE0675D0313D7AB03E230DBE46E286192CD451E9FF",
-    "identity_type": "trusted",
-    "key_seed": "CB61506281210764CE5E3504BF3A41AF69196369375BFE16245B0CA19421650D",
-    "metadata_key": "B9696864507A1DC2EF99DF5441AD25EF",
-    "nonce": "9CC88703DA8A3C82DA5DF32D",
-    "section_mic_hmac_key": "8DE5B98089EEEAB5ABB079EE0C6C8F37289CFCDA74B81BE4F3599EA7F10B3A3C",
-    "section_salt": "8F292492B012ACE2C0EF4F25E0901F94"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "142EF4EFAA03C54D43958647B4A9053A",
-    "data_elements": [
-      {
-        "contents": "B8E42416CD4B3281EE2EDB1B36B89DFD17D35C46B354D6",
-        "de_type": 441
-      },
-      {
-        "contents": "72787349FDC162A25D13BA",
-        "de_type": 664
-      },
-      {
-        "contents": "E0A29FC658F199AFF9",
-        "de_type": 308
-      },
-      {
-        "contents": "8859EB742D3B433BD0F992285B9EEAA1",
-        "de_type": 319
-      }
-    ],
-    "encoded_section": "7C911000DFCC6FC56956B0D99F2F29682117B0759004E48F8DE2D74AC7FEAFBEA42F808B4DCE7D17F2841097DA561701AF9F365BA4F702A4CA79D40F6F88C383487B6524BFACDDF294D2DE20CCD7B2BF3A765598144323933CEAD4C1A5C6522A867A5BFDF562A43C537A50A3987AB4F9AE446043117F7A20E1C41B398F",
-    "identity_type": "provisioned",
-    "key_seed": "311F871B4605A04B3310C13D7297AEE745EE105A5D116D05B114A39C2DE27B85",
-    "metadata_key": "18C51BF65C261CFB2F8106AA4C8476B6",
-    "nonce": "1F562F760A35CE9445D40B28",
-    "section_mic_hmac_key": "6F25A84CB39DACD4B9DC7BB4354EE7BB268FBB559121D0F530BE2FDC212C7478",
-    "section_salt": "DFCC6FC56956B0D99F2F29682117B075"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "0C3C901C96C81D5822A42886F20E4D24",
-    "data_elements": [
-      {
-        "contents": "",
-        "de_type": 437
-      }
-    ],
-    "encoded_section": "38911000B3AF643914DD58250EF9795F77B0FA3B90018EACECB07EEC9E2E72550CB8C373FB6EDA5F1CAF378E19C6207AE3C6F7CDA7E1321B56",
-    "identity_type": "private",
-    "key_seed": "39739EA99CFB0EF8200B08C12213FB3370A4DFB58CEADA17764FBCA9FC05B914",
-    "metadata_key": "A1F9BE98BF2BE21F318FF60776ECF594",
-    "nonce": "E067B8B38B20F64D35EE4B1C",
-    "section_mic_hmac_key": "DB710921FA3969941C33329A0FC10AED0EB49A7BEA627ABBAB6E4BBBF6886478",
-    "section_salt": "B3AF643914DD58250EF9795F77B0FA3B"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "0DFF79575A76A69E5545F23F2D568BF5",
-    "data_elements": [
-      {
-        "contents": "8D530D2FF57D6A089257EEA03F2643CCF76DF103",
-        "de_type": 739
-      },
-      {
-        "contents": "88E7ABACA5E9",
-        "de_type": 563
-      },
-      {
-        "contents": "9CA636C900FFAEB51060B40D1E60D751840CA3",
-        "de_type": 831
-      },
-      {
-        "contents": "8AC0EC13CB0B",
-        "de_type": 944
-      },
-      {
-        "contents": "4FD1B972DDA8376DF7181799B3AEC3D4FA3875CDA0B9F2A244085CDD72",
-        "de_type": 263
-      }
-    ],
-    "encoded_section": "94911000A089A545FC2F1E048CCFEFF6FC7530BA9002D0BA12200150BF15A06427841CC00E9CC99FC3032DDB93D46A96DEC5F92DD89849328418BB27225CA77542A2BBDE1896CF76D0143EA351786BC3DB40228363971552E8CB5BF440F6B6569B610D0F09B6E6B46922D86E1DB35A2AE0507A10268AD4077867FF55C7286971DB6BF27D01B68BEF04B9FE12FF660A0ECE154D2B27",
-    "identity_type": "trusted",
-    "key_seed": "5CE70AB764EE544F6A9B48791033EDB78D6541368BE2A02C41C93BEE27645D15",
-    "metadata_key": "29F72458A212E09F3E13A14D927CBB97",
-    "nonce": "C6002C4E093E3ACD57A0DA8A",
-    "section_mic_hmac_key": "BF94C21240800483EE76C69F94D1E7E867EFC9AB11CE74E672C936634C0F7883",
-    "section_salt": "A089A545FC2F1E048CCFEFF6FC7530BA"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "048C8515B3970ACA7E06DD671E758AB7",
-    "data_elements": [
-      {
-        "contents": "672D400589BB794E2A33DE53A62FF97458DAC28DF015F304CE9EBCE8CF",
-        "de_type": 502
-      },
-      {
-        "contents": "C2D9DC3F76FC72BE4BF7D3AFF43B1041B0BC93512BF73ADA95EDC4",
-        "de_type": 692
-      },
-      {
-        "contents": "B010CC19D7FB",
-        "de_type": 129
-      }
-    ],
-    "encoded_section": "7C91100095FFF79474626138B7A9200A474C449F900259C6F85AD235549231775D62E7E710EA91D16662378B15BD5C34B7D14543BB5F289B37E17599C0AF6572A0630356ECA030870CAE9697E92FA9BB0632BE009F643C018B1571592CB0750C158BE299806D0673779B000E047CF4E4D8BD732544EDE5C6CBB4F71E05",
-    "identity_type": "trusted",
-    "key_seed": "9F52A2F6A5F571B558151E84D8965C427733005A6CD580E66B5C398443BB7479",
-    "metadata_key": "4C95AE9955DE9A3684F52B00461035BF",
-    "nonce": "33CA4805B89AE4F80DB67BE3",
-    "section_mic_hmac_key": "4BC7E7FC685A2E3D9EC90B20014586C986E3054B456EC2851E6606AEDC3955EA",
-    "section_salt": "95FFF79474626138B7A9200A474C449F"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "4C9F52374665C592601F5B2BD404B94A",
-    "data_elements": [
-      {
-        "contents": "91EAFB7D",
-        "de_type": 663
-      },
-      {
-        "contents": "22027014F8D43A605E0C179A1E144A3058D7C024359E7A42B83DBA9B0F",
-        "de_type": 163
-      },
-      {
-        "contents": "0830ACBE1994FB",
-        "de_type": 291
-      }
-    ],
-    "encoded_section": "669110005723CCC591C04732B131D258FB5C9A219002E345F42E0D2D02EF57D890EE70F4A95BFE4C9651BF6E2067BD6707820636D5E484F9D519029D86290ECC42C846AF1D0C8D03BD155D791F47637F9F698A8B9D71E261712DE1F1219ED87F7F2045BC240A41",
-    "identity_type": "trusted",
-    "key_seed": "1D02A26B11FB561BD54036A1FA2B325E74D226CA72428C391650E5249B40E0E9",
-    "metadata_key": "702C35764676569F45279BE0BA2F941D",
-    "nonce": "08838921E1C57E275B554B1E",
-    "section_mic_hmac_key": "163F7952B1866F16C0E5CD3CA950D786E53F04C7CEEBA1946AD76C16185F52FD",
-    "section_salt": "5723CCC591C04732B131D258FB5C9A21"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "A3034AE0322F5E77ED6B21B5E5368108",
-    "data_elements": [
-      {
-        "contents": "537F96FD94E13BE589F0141145CFC0EEC4F86FBDB2",
-        "de_type": 571
-      },
-      {
-        "contents": "D301FFB24B5B",
-        "de_type": 541
-      },
-      {
-        "contents": "EA95F07C25B75C04E1B2B8731F6A55BA379FB141",
-        "de_type": 51
-      },
-      {
-        "contents": "2EFD3101E2311BBB108F0A7503907EAF0C2EAAA60CDA8D33A294C4CEACE0",
-        "de_type": 729
-      },
-      {
-        "contents": "B0",
-        "de_type": 411
-      }
-    ],
-    "encoded_section": "91911000DE2A89ED98474AF3E41E48487E8AEBDE90014C18BCB9F9AAC5C11A1BE00A10A5DCD2C49A74BEBAF0FE72FD5053B9DF8B9976C80BE0DCE8FEE83F1BFA9A89EB176CA48EE4ED5D15C6CDAD6B9E41187AA6316D7BFD8E454A53971AC00836F7AB0771FF0534050037D49C6AEB18CF9F8590E5CDEE2FBC330FCDC640C63F0735B7E3F02FE61A0496EF976A158AD3455D",
-    "identity_type": "private",
-    "key_seed": "959D2F3CAB8EE4A2DEB0255C03762CF5D39EB919300420E75A089050FB025E20",
-    "metadata_key": "EF5E9A0867560E52AE1F05FCA7E48D29",
-    "nonce": "23B64C2B1724E5AE9528FCD4",
-    "section_mic_hmac_key": "2F1B12002184860A496B2DF7FB743401BA8E8BC5F22F661BAD69335E99DB8A19",
-    "section_salt": "DE2A89ED98474AF3E41E48487E8AEBDE"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "15AB0E1BF3C908D5A92C9A52158DAC00",
-    "data_elements": [],
-    "encoded_section": "35911000172C7E49FB61097603390278AA4B48F8900477935610288681FDC9B7250022F01CFA66E59183F392C5C2485D8240ABC96D15",
-    "identity_type": "provisioned",
-    "key_seed": "C1FE7DD8760BDC37EEED65E07FF3F9DB9B2DA247260038675679921566A4466C",
-    "metadata_key": "D84140E3576B04F8D9290744C62D20A1",
-    "nonce": "A8FD0BF085BA1C3E8A23FC0C",
-    "section_mic_hmac_key": "3BBF201D9180707DDF5DA4321B477A2D0CFEFC00D72EE560F0E41B5178315366",
-    "section_salt": "172C7E49FB61097603390278AA4B48F8"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "2C8D685B3ED53CA46D6A6AC126596C84",
-    "data_elements": [
-      {
-        "contents": "E95043396FF5CE7FEA272F1E9D8A",
-        "de_type": 359
-      }
-    ],
-    "encoded_section": "46911000B9EB78D15B9493668E3506DD9E7A2DEC9001884E76187E89CB0107A5090D664508854DCF8722E98904E6BB57422710F3CBB3271A743BAE59467EED31F62B714060F040",
-    "identity_type": "private",
-    "key_seed": "0C43A03CC43B6536086910E73F4499C368BE47235FEA439124005FD91047C8DD",
-    "metadata_key": "6B8DFEAC098F9DD035F14843C3196CC8",
-    "nonce": "920205C1FC28E58575E08D47",
-    "section_mic_hmac_key": "5FE0D08732D5615AE4447E726DEAB0083D9458E66DAE596377DDC103C34A4442",
-    "section_salt": "B9EB78D15B9493668E3506DD9E7A2DEC"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "C56F4750DBCFE87ADD8E2159B3EB5646",
-    "data_elements": [
-      {
-        "contents": "148ADAFBA927A9A3D361CB34792A1FD1311A",
-        "de_type": 16
-      },
-      {
-        "contents": "37215F",
-        "de_type": 776
-      },
-      {
-        "contents": "5B75B7A4044E3100635709818ECB099706",
-        "de_type": 563
-      },
-      {
-        "contents": "4A70F7FE36FC",
-        "de_type": 49
-      }
-    ],
-    "encoded_section": "6B91100010510FF96F9DE71AFB125A00637D9A159002692F0CA5C9AD242760DFCCB7F9E445AB0545ABDD91DBB4E6A7DCAE21ABF58D01CC3C1B95AF9442EDF2FA1ED5C551FABF071981137AFB04F4B67F13D43CAA959F53819A4B90913C7CD4B945665E74898BDEEA50939FA6",
-    "identity_type": "trusted",
-    "key_seed": "18D77C2D69389B38F6430DECF46D34893C188EDF6F57E306BD159A441D602D68",
-    "metadata_key": "0DFE12776B497E72E415E41625C4A2A4",
-    "nonce": "A25A744723723AB492153CE9",
-    "section_mic_hmac_key": "B1BC6C3AC220AF74BAC5EA8A85692095575FA6C2172ED9E66669114164BF55E1",
-    "section_salt": "10510FF96F9DE71AFB125A00637D9A15"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "3BDA27AEC19E0135552AD5ABB02C45D2",
-    "data_elements": [
-      {
-        "contents": "92F73CEBBE55EFF2391445A2B96299155B78",
-        "de_type": 984
-      },
-      {
-        "contents": "8C4615561E9CF7589FA8E6F7B57893025DF55C",
-        "de_type": 919
-      },
-      {
-        "contents": "DDF6FEBA14ABB1B094A277D361A8C70B988E1010534FAFDF25",
-        "de_type": 676
-      },
-      {
-        "contents": "8650458E65DE07BFFCD2EC46E0D4D05609A66D9AB777FA341EFE3280A5AC",
-        "de_type": 784
-      }
-    ],
-    "encoded_section": "9D911000D86DFF361F5567606CCBAD126A86C01390028E06467447060E4E27C066C5B7F8B785D2476A0FFACC9C85C1E2211B7609D8A4A2C7036F982F923D1DFCF0161B60A3AA5DE2287B909FABE3E902EF1CCD45AB7D9CACE4FAB3C05CB9A00FCC5AD93E9E3B2B31A2ECC226D2209BF24FC7D13D0AE53C912D03DF47F1A4C132BF949C555E8D35436011B852DDB228F4964483F820DA360FFC9506087F99",
-    "identity_type": "trusted",
-    "key_seed": "A6915F7F275F268563F06B70294DE268F4C8FE2ED64831049F5071DD2D7AFF7F",
-    "metadata_key": "29B703C99077654D9EC2BD86066D3CC5",
-    "nonce": "ED125BA0057DCFEB89ED7190",
-    "section_mic_hmac_key": "1B8F822A197030498D51E0F2E4B9C7C306074D7C83C73CAE508D2FFA46EB743A",
-    "section_salt": "D86DFF361F5567606CCBAD126A86C013"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "9E629E04812454CB600A54C8837CC057",
-    "data_elements": [
-      {
-        "contents": "C1B396A29B6967ECF8673E009B2E7F94C4DECD7FA20C176EA0AD98B9",
-        "de_type": 253
-      },
-      {
-        "contents": "3FFEC56E8DB922CD8C5AB5E47452E571",
-        "de_type": 605
-      }
-    ],
-    "encoded_section": "67911000229151F0CBA214F520EFDBF30FB4DA43900287511210A4AF78894FA1DEE707C412015D271BEE2789F49BB5DC67D44E5B9E58B6421F4C834D13F29940800D31315840591BD869F1C2D2887914627DE54D90AA254C6CE1699AA9CA7198ADD71BDC46D91117",
-    "identity_type": "trusted",
-    "key_seed": "1C9A55E064B78E94CB6B070F433CA94D21CB3318E048A7A2CAA83FF958419794",
-    "metadata_key": "1A240C90D7E260458660C8B07F991AE6",
-    "nonce": "2BEB4BC7C05434ABBA19E538",
-    "section_mic_hmac_key": "050D12FF03D51C57EC4AE018EDC1896C38226E1A0481362E415CCB28F5A01B5D",
-    "section_salt": "229151F0CBA214F520EFDBF30FB4DA43"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "8C996EFAD07DDDED26DFDF98812CA923",
-    "data_elements": [
-      {
-        "contents": "B7C019E21BDE02221C04A4",
-        "de_type": 43
-      }
-    ],
-    "encoded_section": "42911000D1662A41EA8D74EEDE6BE6686D41414E90029CE0D1823D6C861F9B05C79235D6C1CB99634640B9BA98F7B3C72B67F242E2E40E36C937F671A88D63FF97C4C4",
-    "identity_type": "trusted",
-    "key_seed": "2027245C1810377C3846FFC4F33A46FD5F55BEFBB13C9EB07D7D5AD3CFAB48B4",
-    "metadata_key": "9F4AD9EDF0A6CAAD2E9E99871E4CE52C",
-    "nonce": "A71E97EBB5FEF5C10118B443",
-    "section_mic_hmac_key": "3BEFA04D9C10EC14E050D8106803C71F788E338E191EDFA21FC2029FE1A90922",
-    "section_salt": "D1662A41EA8D74EEDE6BE6686D41414E"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "AE8C6F011FE35C9C96FE8EBDE339A41F",
-    "data_elements": [
-      {
-        "contents": "607A867B3B29B89041F9",
-        "de_type": 979
-      },
-      {
-        "contents": "5E799499",
-        "de_type": 25
-      },
-      {
-        "contents": "92CB515B79EDD5E37C12C6B5864DF420AF7DDE1B5919331DA7",
-        "de_type": 577
-      }
-    ],
-    "encoded_section": "649110008A4AF96D2B686F8C79E0133C7A1300D39001533E4A9492889398E4BEA2572B455C201BE24382C5D0FCEF256113AA7541173BBC6A2B9AFC3CB2A133424BA12055069603174E36F3407444B5FBFA5BFAE8C8B6BD893AB31B9D869105014A931AEA10",
-    "identity_type": "private",
-    "key_seed": "F728E206C88A47D4C5063BBF54C218435A04B921E3D0E2774572D88024076D7E",
-    "metadata_key": "CD8451667D7BBD7EB4CD5E456ECF8167",
-    "nonce": "3F7D4273CDA786C66C557ADC",
-    "section_mic_hmac_key": "82B0C8B8007802CE1CCF367F07830FDAD2401C8EB5849613602E568E634DAFB4",
-    "section_salt": "8A4AF96D2B686F8C79E0133C7A1300D3"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "9078B570449046A4C6FD9246ABE3E17E",
-    "data_elements": [
-      {
-        "contents": "381690D8CB5DE6CB",
-        "de_type": 724
-      },
-      {
-        "contents": "905EB45FAD",
-        "de_type": 144
-      },
-      {
-        "contents": "969FEFE0",
-        "de_type": 302
-      },
-      {
-        "contents": "75202C93DD512023EFD3EBF3",
-        "de_type": 742
-      }
-    ],
-    "encoded_section": "5E911000F765D4EE37755137F6B7AF43D4ECF2CB9002361BF7AEC5BEA9E0F9790E737E559951EB09254D0B4304A81E21FF7C9B463A8AF4D29B35B6B909C255286B707B85F028D277148C6A82877695FF915BD0602946C37F4AAF6C1C315B98",
-    "identity_type": "trusted",
-    "key_seed": "DD3AB1ADE4D985E6D6E67F2FE19DD6A75A9F5B4560CD3C05FAFF8A88C283AB3F",
-    "metadata_key": "D53DBD8613161F1527385E5F27FEE124",
-    "nonce": "D30A2D8CEFAB8C91E0B1799D",
-    "section_mic_hmac_key": "EDFA40290EB30E6669C551947A061A587843CCE47F4620E51ABA239AB79AA739",
-    "section_salt": "F765D4EE37755137F6B7AF43D4ECF2CB"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "C2FF97FBAB946B17D2BD0CA0712FA7DB",
-    "data_elements": [
-      {
-        "contents": "B0F9DF275D2D5A37C95B9CC9AB",
-        "de_type": 114
-      }
-    ],
-    "encoded_section": "44911000EB645DE61C9BB4C40BC585AABA223BAA9004B40CE842866C9F317B5C429C501392E13495FA18E48C34BDABF4152C8E98E90ED1AFD11BCABD4EF60CBA9347FB0120",
-    "identity_type": "provisioned",
-    "key_seed": "CAB68F8D497B54A0B25EE602EDC659B8C12F9E9E49EC8FC2C2487E8FA955E606",
-    "metadata_key": "CDCE54153DA53A283DBF35F0D456FBDF",
-    "nonce": "D7FA723581DFA37F8CD7F284",
-    "section_mic_hmac_key": "EECDEE6DCC125C40BD1002DFBAA77C8D5F16D6A9D152E6524A13E213F5CDA36A",
-    "section_salt": "EB645DE61C9BB4C40BC585AABA223BAA"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "BD5FFED9F8FF99EFD9276BAE103C8520",
-    "data_elements": [
-      {
-        "contents": "A9F592F1DCA1C71CCF03CC0310DF337C9A",
-        "de_type": 683
-      },
-      {
-        "contents": "E18AD6552B4C",
-        "de_type": 277
-      }
-    ],
-    "encoded_section": "52911000A798801A6B6BB3B3CE199A04C6D39C749002F4A793DDD478205BE47B9D5A3D02821CFECD82A205E596D82F1CA5F52DFD12D2F160C20332DD75B56CFF48AFC079B80AB8151D3FC020D71FF03A220692",
-    "identity_type": "trusted",
-    "key_seed": "9DA9C8ADB2F20D379E5C103E92D60D4ACC946AD0DC57B08E71A6CAF174DE27FD",
-    "metadata_key": "32044EF9F7B354E253E7206AC19172BC",
-    "nonce": "FD61DCBF349648F6BCAD5FF7",
-    "section_mic_hmac_key": "CEF13D97D862C57A7CC2ED89FA453B5F331A39B46EF3DAED677C6354FCF41A99",
-    "section_salt": "A798801A6B6BB3B3CE199A04C6D39C74"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "AB8FC3047B5D459C0FE3A8D32DA1D396",
-    "data_elements": [
-      {
-        "contents": "70E9867134E018A407BFFA11681FE66225551030",
-        "de_type": 694
-      }
-    ],
-    "encoded_section": "4C911000F340B13BAF10C096314AD12554265BA69004D98C2427C11201067AAA42C262F78315F0FBD68F03CA256E74CD74CC39E3B0399E41E8DC766AB30241498364F94C204BA6F1D12C66C752",
-    "identity_type": "provisioned",
-    "key_seed": "9E50221DB4D19E3E2E6E0D61F2FE4FD765B0AB77D795B8BF9DEE945C9258D90F",
-    "metadata_key": "E59AB0FB0471EDE9516038F2B830FC8D",
-    "nonce": "D547910B27183848271E093E",
-    "section_mic_hmac_key": "0C67958E203428FCD8B023C2C97BEFD62A64E72DC0F3F9078AE494DA235E4E80",
-    "section_salt": "F340B13BAF10C096314AD12554265BA6"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "5E155066D56083328A063FBD5AFBF1CE",
-    "data_elements": [
-      {
-        "contents": "B2E8AD80552E7D",
-        "de_type": 638
-      },
-      {
-        "contents": "EBA00821E05107D40E6B3CAB8E86027D497F34670527D1D6",
-        "de_type": 337
-      },
-      {
-        "contents": "549E6C2D1425",
-        "de_type": 826
-      }
-    ],
-    "encoded_section": "63911000A847D51D7D049D481D63F14F4A8D01099002FC5925A9E42B41321E56021A3D01157DC10E00CFF86A66638C7313814DF85E2E050AC4235DE9634416B34AAEC4419D9F50D49952742723228FA5CC8A8981C6E5E71904FE5FDAD5111C3C81C88E65",
-    "identity_type": "trusted",
-    "key_seed": "BE5027936C60695FF4EA18AC1F81078221C50A8C22C98F36C9A5877D369E4000",
-    "metadata_key": "1B43759002C9095970DB092A0086FECF",
-    "nonce": "CCA806551E3DE4A2C7015C4F",
-    "section_mic_hmac_key": "B8C388D47A6B125B3E637DBE4281824A170687F5D596510B94A95DFBD0420A5F",
-    "section_salt": "A847D51D7D049D481D63F14F4A8D0109"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "BA3816EAD76DAC246880A27F137D4CC2",
-    "data_elements": [
-      {
-        "contents": "3909A77A6295",
-        "de_type": 706
-      },
-      {
-        "contents": "A360F1",
-        "de_type": 563
-      },
-      {
-        "contents": "9B77E78C7CB64198BC2E5E676080B8652C88A3F1A71B5B62CC5B87",
-        "de_type": 496
-      },
-      {
-        "contents": "DD4080284EEF0BFBA61283376C7A",
-        "de_type": 129
-      },
-      {
-        "contents": "D3",
-        "de_type": 362
-      }
-    ],
-    "encoded_section": "7791100034A1CA42CE8D750265DD13710E6AA8F390024301C25BBA38E693BCF7834E08B62F4B4569EC8704250959F6EEE5971C2E25BB63EF65C1CA1B20917A433DAA9379ADB3773F463BC8A81A938CB83EA4E1BBF4CB1A4409D38E9511B4660A5C5458337CCBD98AA3E3A41D88C98ED0A9D8AE27F28206D0",
-    "identity_type": "trusted",
-    "key_seed": "7052FBAFC59D8ED636F51F254C3E8D2429604C2A58A3A0A27C54C2D83B4D4353",
-    "metadata_key": "0230CE096D5CBE15B55F65F270D0D411",
-    "nonce": "51FD2C7EBA743483252DA523",
-    "section_mic_hmac_key": "C1941723E73EE200B78932065A479E2380FBEE95082846C9FEEDA9D35C13E84E",
-    "section_salt": "34A1CA42CE8D750265DD13710E6AA8F3"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "3F7BAA4E9ADF649F68504D2BADC49E59",
-    "data_elements": [
-      {
-        "contents": "C7CD03AABE413B68D38D5528B8",
-        "de_type": 784
-      },
-      {
-        "contents": "BA87E5F9338B888B0ADCE8C80BFFE11D2467303431",
-        "de_type": 902
-      }
-    ],
-    "encoded_section": "5D911000D22DBB4DD6D440F26183BD13D0C3DF6A9004AA523D21ABBF464C0DD1166437AF2EE5159310A4E2C500A84A7824DA48C2DB85C9BC79B60DA83EE61EDFBA617008A7340431E5B7ECB19E7F140D6F0D7E22589A51AAE13A00780820",
-    "identity_type": "provisioned",
-    "key_seed": "1E2D99F50E548FC49590D5E6C066B4DC955EF40DDEE79167EC359E551048E1D1",
-    "metadata_key": "83D500F6D6106BC3C2B17AE408023E91",
-    "nonce": "4A9AED02A25AF38FB2E2E2F7",
-    "section_mic_hmac_key": "B29B84CFF65C3EE4616A502AC61C029C706D6B1EDD39EDF4D76EA5A84453BF5E",
-    "section_salt": "D22DBB4DD6D440F26183BD13D0C3DF6A"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "5BA9C6F943DAF529109F4B100F49D597",
-    "data_elements": [],
-    "encoded_section": "35911000C739C6227A353DD9B0EB277131E35DCF900469ED27AFB7C387B5475E150CD81AEC4884892FD73C27B796E7685D75C10A7F71",
-    "identity_type": "provisioned",
-    "key_seed": "1344FE8EFB2E14874FEB0571343BBA222C0FD904204BBA55ED94F0CD28C26FB7",
-    "metadata_key": "DB1BAE09B154172E09141BFFE4CF561D",
-    "nonce": "108AA9D4432E50BCDECCBD6C",
-    "section_mic_hmac_key": "2E20757E70673D996C6D58E403A0F87CDE24F7EE3038C17FE07D435B44093CD5",
-    "section_salt": "C739C6227A353DD9B0EB277131E35DCF"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "39CD401AE8F611D1651E0FDD59BA8F45",
-    "data_elements": [
-      {
-        "contents": "F4658B8BCCCF49B7AB79",
-        "de_type": 564
-      },
-      {
-        "contents": "C9F2A6B90E08CC",
-        "de_type": 988
-      },
-      {
-        "contents": "820BE5C34A2CC3E9",
-        "de_type": 870
-      },
-      {
-        "contents": "0F02CD73",
-        "de_type": 573
-      }
-    ],
-    "encoded_section": "5E911000E6CBCB218624EE2DC2DBABAB38F837769002F43D65267B214BF792B14655201C57B346BF233AEA5FD07FC5163FE7D1659F6A8DB901B503D26320F94D3B25EC2647F294BAD0748EEE05FE24F42CFA240062F74715FAF8CB6873C017",
-    "identity_type": "trusted",
-    "key_seed": "D72CDA23016D822CFE1E5294916DC83CF98EC2ADAE9121F80803386DC5CF6E78",
-    "metadata_key": "47001AE1B7E542147BF57D1C62F22F9E",
-    "nonce": "B677F8EEF05E5C4FD69BC645",
-    "section_mic_hmac_key": "CD8F4F64EB964FF7E0EFE036347DD0007A1A763B3001F02361CCC3A680C368FF",
-    "section_salt": "E6CBCB218624EE2DC2DBABAB38F83776"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "194ED59DCF4144A697CD1AD63E33A3B4",
-    "data_elements": [
-      {
-        "contents": "D5156EF7CAD1096B1EBE82E0",
-        "de_type": 777
-      }
-    ],
-    "encoded_section": "44911000032224758D14BA41AF92F6D88A501E2C9002C7D754757898C9E34AA838CDFBAF155B30084E02BE29B58C33ACC954F941842FC053B0ADD4CA9F3D3618EDBA2CDA81",
-    "identity_type": "trusted",
-    "key_seed": "F77600BA15BA4C8B396C8A0C3B8ABECF2B064A045E583BECD39116C572E7AD12",
-    "metadata_key": "DB7F42852277D6552C91815416FCBF72",
-    "nonce": "5A2D8E75D0B7634F06D7325A",
-    "section_mic_hmac_key": "862D13102D1431D175E2E8B6ED0D30D99C6E43EFED3FCA6D14CFD8AEA3BBE15B",
-    "section_salt": "032224758D14BA41AF92F6D88A501E2C"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "3337A6A8FC085CDCD20C3F46E78E7AB5",
-    "data_elements": [
-      {
-        "contents": "70D83068132E37C88B72A4EE79D173537E8F",
-        "de_type": 277
-      }
-    ],
-    "encoded_section": "4A91100013D9056CD070347F927B0F615DDFEC549004C7894D8A1B5608C1EC182CC0AB86375EEBFE92E0A4EF6878AAFBCB637738E21F4EA1C9F76D65EB4AE29BC86CE43C9F49859C66F2FB",
-    "identity_type": "provisioned",
-    "key_seed": "1507CD75F60429CB9E63C4D4A09FEAC92E772223D9E95838E8784AE1969F3E7A",
-    "metadata_key": "481E466127EF90D5C53596E8552EC52E",
-    "nonce": "F7A694ED8C95C9D7A755D82B",
-    "section_mic_hmac_key": "F7355B49456F6C57FE860ACCE4A711214D71D5F08B574E5BA37E165B52000A36",
-    "section_salt": "13D9056CD070347F927B0F615DDFEC54"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "FBB1328618CCD896CC70AF937CAE8281",
-    "data_elements": [
-      {
-        "contents": "467333809F3B",
-        "de_type": 952
-      },
-      {
-        "contents": "50CE12F4FF7B16B5643EF1C66368DF27DEDF3CEC9B24A3",
-        "de_type": 300
-      },
-      {
-        "contents": "08443858DD0A",
-        "de_type": 124
-      },
-      {
-        "contents": "E873C2B86B0C5A28624DBBFDE0AE3D22D03EABBF5BA71EAD",
-        "de_type": 448
-      },
-      {
-        "contents": "F1447D2F9281DBEE34476C48B22351E8F27CB782A10A29FA69E5ADCB3EBE",
-        "de_type": 399
-      }
-    ],
-    "encoded_section": "9C911000E33DCEF3193A00CA24590E41CB1607C79004439EFC4200D70EB58A7579DEBD80F04B5E8002A076C37A1D6510BA493BBD60569363F1FC3FC5A379DFEF1BDE620330DA1C8D06AB062EB96C92762FE17DD96776B35D13CA41488C1C83F2FFE0D0EF5262B632D856D10CB666762BA5CA742A0E9E95C9931779E319C7854ABE32BE0480D1FA754E1EEBEB1521C148FBF4A70F3185091CB04D57884A",
-    "identity_type": "provisioned",
-    "key_seed": "D30C0A924112C23C73BAC89EC03E7F9EB81E26916553AB6556C1240C20EC37B5",
-    "metadata_key": "308A7C574957C93A9B3E6CEA175AB263",
-    "nonce": "5D0946C2808A4C895626EF0B",
-    "section_mic_hmac_key": "8421457116D650DB88EB790AFB90AD99C53EFE2F875528CA25ADAA615F35D9CE",
-    "section_salt": "E33DCEF3193A00CA24590E41CB1607C7"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "E03DF6ABB87D6F554B5BEC78A7E8B0FF",
-    "data_elements": [
-      {
-        "contents": "B94A3080F25FE185C6C36E9A21405519666684E0847BF36DD115A926",
-        "de_type": 503
-      },
-      {
-        "contents": "",
-        "de_type": 415
-      }
-    ],
-    "encoded_section": "5791100094339CFAA454A5D2E977F38C35E2844D9002E15B4F8862B4A3E4D5FCDDEC6FC36567A1E9A6EC6E2CAAC28CC1C72CC81CB4E190AD8C8C1997EDA2A8391662DC44D4D5B818959ACC574FA1CF7E99645C4DF138B587",
-    "identity_type": "trusted",
-    "key_seed": "6B1BDAC7E669C8EF0847707AC2365C0D3864B31D6F13C088D4630CAE016B520F",
-    "metadata_key": "A50CC2AEFD3369D7D1DA0F0D0E537F19",
-    "nonce": "0C4819136C0E397ABDD9E354",
-    "section_mic_hmac_key": "A1FC278429196727FCF080260972AC6BAE2670C7383C2E297D1721428168F8C8",
-    "section_salt": "94339CFAA454A5D2E977F38C35E2844D"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "D79C9AC03BE4012DC980193BFD6B9844",
-    "data_elements": [],
-    "encoded_section": "35911000711C9EC86B8A068AF07B7FE5C85CC9D39004D54FC742603A383E4F153DBB7462EA99ACDCA8E5B667784D8DDB99FE360DABB7",
-    "identity_type": "provisioned",
-    "key_seed": "6FC2E3B1B4E9D1A97ADE86D36D78F377C5AF881DE70D981C8553DB86D830F617",
-    "metadata_key": "FFC3763940BF40D4C90C1C9F1A108195",
-    "nonce": "5FB63D89B5C5D0A2908B0A62",
-    "section_mic_hmac_key": "3F353E77278CAC09E16D0F86B6723510700210D5C8A73E6EEF04921FDDE689F1",
-    "section_salt": "711C9EC86B8A068AF07B7FE5C85CC9D3"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "CDD28EF3B99341DA9DE1C30C254E4430",
-    "data_elements": [
-      {
-        "contents": "99CE21E4964A6A7C14E6BF387369",
-        "de_type": 361
-      }
-    ],
-    "encoded_section": "46911000E9FF177F159C1A081403717389F200EA9004505BB69C948A0A4849FEA66E702DB7500BC961E0151EAD701AA1BA69C29A1E2B363678656DB7E72D4A98AA28A8894CE84B",
-    "identity_type": "provisioned",
-    "key_seed": "A2A3EBFA3847569FDC2713784A56CD71A1DEAABE14190EC0B86AF832EEBBDD59",
-    "metadata_key": "4432862698BC0A055C41E6219F86C655",
-    "nonce": "64C1217301A32C5B82B94358",
-    "section_mic_hmac_key": "C78D782F47CBEC0D0F0470DEC491B8808A6C08E71F5C325E0B359551AE38ACA4",
-    "section_salt": "E9FF177F159C1A081403717389F200EA"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "F2912200A19661C7B3256F2ED8D635E1",
-    "data_elements": [
-      {
-        "contents": "7321",
-        "de_type": 467
-      },
-      {
-        "contents": "",
-        "de_type": 743
-      },
-      {
-        "contents": "B58EA8527B77EB7DBF5CF9D10748D743DF6E8F37D8",
-        "de_type": 592
-      }
-    ],
-    "encoded_section": "5591100029FDA91C6673EE9F5E81041BE492146890026FF0F473F33B7DE9BFA2EA578382EBCBE4227D5570F2365433A6A4751FD10BA87D17552C9315E4ECA5A45D4EA4F20AC118D0CF61A0E0E49E90C9658FF4D1CC6E",
-    "identity_type": "trusted",
-    "key_seed": "FD7967CD290B0ACCA1FB012C6B17FADB54CC062A92E4E3F095B6221265606BC3",
-    "metadata_key": "7485DC8BD4A36F721112C2947751F068",
-    "nonce": "CF3A08D31D6DB3F85ABFAEFC",
-    "section_mic_hmac_key": "DE0AB90DC2E5437C201A96A529552965E9113AE01E76E225E906DB2C854C4F74",
-    "section_salt": "29FDA91C6673EE9F5E81041BE4921468"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "425A003859115F90D385DED38FC3138A",
-    "data_elements": [
-      {
-        "contents": "FF1E4283ACD7",
-        "de_type": 592
-      },
-      {
-        "contents": "FBE130FC8F63BD7012",
-        "de_type": 224
-      },
-      {
-        "contents": "7DEDF0BFF3CAAB7C6EEB72A4F7B906AC50A92F6E27F0C0BE5DA402D6E1",
-        "de_type": 740
-      },
-      {
-        "contents": "B3929F152C596C0BEF4233703F00B6621C1FF3A358F3C86DCFB16B72",
-        "de_type": 312
-      },
-      {
-        "contents": "0F5043C11D1C467CCDCB83DCE0BE43AC1D94DD5611",
-        "de_type": 891
-      }
-    ],
-    "encoded_section": "A1911000D3F3AF795C519468EFC23F73081ADE8E9001D17E0B80D5304A3B3078ABD224B53EEFF7BC4D82C69097FE3731085ADE750593E3267702827CB9CD176EE205CCE43458E2AED220AABE1587FF7FEEFA571B629E2A205D207044EA651809B9F5C9102C35E0368A0C22CEA27DAF899A987DD40352EDA04C8DDC7ABB3B6F279C7779162B472543393F37A9EE4A9A6F1E6ED3EC8E46551715F1F254A13E0604C71E",
-    "identity_type": "private",
-    "key_seed": "CBD5BDDC7DF1CF4DE8C67B8C5D8B3936E4370BD2FB8674BDD032DAB651935098",
-    "metadata_key": "85F9B7DC74148374E28EE18DEB067DDC",
-    "nonce": "64822DF95082DD9FAC3ABF49",
-    "section_mic_hmac_key": "DCE1733FCC2792C2FC5E717B60BF387A153B202A6FF51896D67849A33D700BCD",
-    "section_salt": "D3F3AF795C519468EFC23F73081ADE8E"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "7B1388703BC7E6A02BC3FD5ADA646469",
-    "data_elements": [
-      {
-        "contents": "7D62FA053E4F6358FFE38CC63265B5C631",
-        "de_type": 618
-      }
-    ],
-    "encoded_section": "499110000FB390BC9AC7F8533F693FC4F1C9BD6A9004BF6D2926E7409C8B991DDF484AF4F056C15513C85442EF6D8CA694BDCCDA70B5ED790BFBF4F026113C0E71CFDE64EAB674438D7A",
-    "identity_type": "provisioned",
-    "key_seed": "2B56B30BAF8A4D98B8D2D6DBBA920C7E58CC2A308BCBEBC2444E695CDB84DB9D",
-    "metadata_key": "F9B398674610A9887928F01BFF47348C",
-    "nonce": "CA88F80E008C85191F0368E2",
-    "section_mic_hmac_key": "61032327C90BA52D7AE489911943770F4ADC0CBFF70B3C1FCC6C8CCF684D11DE",
-    "section_salt": "0FB390BC9AC7F8533F693FC4F1C9BD6A"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "C131B7DDCCBF6B9D6697A73183BB9C2D",
-    "data_elements": [],
-    "encoded_section": "35911000D6A73EDFDB9C58847962A374CD39EF8E9004564B9DA3B33DE90A04DB8E09C9F74E39B3974D254418BD31BEDAF2079776203B",
-    "identity_type": "provisioned",
-    "key_seed": "619CDE7F5C7F48C2A728740F73ED8BED80DBC75A8F80A210B7787A3CA7D99894",
-    "metadata_key": "894C9EB0A0CD63439D7D38D98271886C",
-    "nonce": "988D2E9390677C9D60D253C7",
-    "section_mic_hmac_key": "B9ECF2780619E5BB483F7E67D47D5F6C453260B05B1C6C8B9DE3A1AA200E859E",
-    "section_salt": "D6A73EDFDB9C58847962A374CD39EF8E"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "471C247B67E71991EEA742A60395E761",
-    "data_elements": [],
-    "encoded_section": "35911000AF15F574B747192E9BD4F216FA1B4B7790046EBE5844F9C2B310D203E1F7B096319C81C5F10A0173AA6130BBFFDE2C335BA2",
-    "identity_type": "provisioned",
-    "key_seed": "AC53B58E3C5655CFC99E96EDA60B6356B91878707EBBB72C58B40372067F2654",
-    "metadata_key": "B3C7283A417F10365A58239881EA3357",
-    "nonce": "36F08B54F7A71C96317A1379",
-    "section_mic_hmac_key": "C873EDA6670390B6576A70192A1CC51663DC71878502344B4E89570B4DEB8CCB",
-    "section_salt": "AF15F574B747192E9BD4F216FA1B4B77"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "FE9F6FB58671E09A1A982B395153F5AA",
-    "data_elements": [
-      {
-        "contents": "3EF87149ABACBBE77A658C12AF57C5CC23549860",
-        "de_type": 886
-      },
-      {
-        "contents": "2FA53563",
-        "de_type": 266
-      },
-      {
-        "contents": "07",
-        "de_type": 504
-      },
-      {
-        "contents": "D174D239EA526214B9F3DFA561C2E67AEB39CB5E8E45006EF7",
-        "de_type": 727
-      }
-    ],
-    "encoded_section": "73911000996E2F8D9CA965F32EB4884BFF25EB659002555A82FC0FCC2977E3F47AE4C3ED20652D644EF87BA79F9C7A4D0CEFFF756F6F3EA21275055C45685B915A6CF3500FEB68C62D6DB778D6037719D08E1CE1A47A60BFA574B92458EA9853CA12C6085EDC5B3357767D033BB8BD858C6C45F4",
-    "identity_type": "trusted",
-    "key_seed": "797A0E23E450BD746F50A900291EDF12A427207E9345F1ECA7F247754D4E3778",
-    "metadata_key": "EE52E516F455BF41B9FE45B525300D80",
-    "nonce": "8032538D846F75783A371130",
-    "section_mic_hmac_key": "7B1519118268CFD05BE2529648964A31267DE90B31858A541E3B6FA5B5A5BA48",
-    "section_salt": "996E2F8D9CA965F32EB4884BFF25EB65"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "259CA266C07D24489E2320751FFACA50",
-    "data_elements": [
-      {
-        "contents": "1293957F338220C0DFCF99BB988D4B10A2",
-        "de_type": 588
-      },
-      {
-        "contents": "A66E4E117DE27225",
-        "de_type": 527
-      },
-      {
-        "contents": "A371A89597B5908CF70EB5CD70",
-        "de_type": 348
-      },
-      {
-        "contents": "EF8C4B",
-        "de_type": 893
-      }
-    ],
-    "encoded_section": "6A911000CE373177E0843767D3CBDE3EE36AF17190041C77A0D1B5A6D84E23CEA79D55760F47327519B09C00EF56BA591D952D09BF7649316ABEAA1DB8CBBA6C60CB9800B14CAA592DC6BE015529375E516D04DB3364041E1CD3C4545F053AE220D2AC5D9A1D8F15A18A5C",
-    "identity_type": "provisioned",
-    "key_seed": "0C8BB4BAD43C47257EFE360BD5D5DBE4896E84694141ED2BFEC0489578B217EC",
-    "metadata_key": "0F57379CD24569CF8D875802667F8018",
-    "nonce": "2537E78DBA5A7038AD729338",
-    "section_mic_hmac_key": "FC12ECFFC1AA17039E7B4D5671BD50D23427170DE99771C2560D50630AAB4FDC",
-    "section_salt": "CE373177E0843767D3CBDE3EE36AF171"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "75678439AC49152DF67381CC8EE0C97D",
-    "data_elements": [
-      {
-        "contents": "AAF565A6056E72F5701175",
-        "de_type": 900
-      },
-      {
-        "contents": "",
-        "de_type": 882
-      },
-      {
-        "contents": "1BE514ACA5DB37",
-        "de_type": 958
-      },
-      {
-        "contents": "4E37988B6C6BEA4DB361B0ABA2BB437B4AD2A36629E927A798C952DD",
-        "de_type": 994
-      },
-      {
-        "contents": "7A7051FB81E50D6E053F5485690060BB125BF0B564C25E8141F18A96",
-        "de_type": 502
-      }
-    ],
-    "encoded_section": "8E9110008CF745CF3EF5643BF08137D12572917690017A062343BD2CA81B100320E63F160D60C0E4FA222E7E8D2DDEEF54E8DB3E6F1FB8ED544B20CCC68B7325C41E13169FF9243D361927D8B8D70CB9BAD2BF8D144A7E7181AD04B286370859944B00B285A07237C9B7C06ECA02F133C1ADB18676DB418CB0F625548CEE8D1EC557723967A2DBE6B6FDE4266E810D",
-    "identity_type": "private",
-    "key_seed": "B2289A540886CA52A77D25F5130E9772E28FCE89F62ECC13606D63DCEE99DA3F",
-    "metadata_key": "982AE1A57B20C2386179D149B6EF266F",
-    "nonce": "80CD1289986D617F60412E98",
-    "section_mic_hmac_key": "7568EA434871461F5D6FC2DD811AFD04A037DDA922D703E0C62B59BE3FD8463B",
-    "section_salt": "8CF745CF3EF5643BF08137D125729176"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "7BB7CE9DD41A841A3D5A7EFD4C320177",
-    "data_elements": [
-      {
-        "contents": "AFD0D457BF0EA8EB58FB",
-        "de_type": 663
-      },
-      {
-        "contents": "2E252A7580FA1B62063666B69F3C6332E4B8F9707C7A21E9E02950D9C5",
-        "de_type": 817
-      },
-      {
-        "contents": "B60A909ED57A8DE3A7860923B92A7D5F6A8D",
-        "de_type": 800
-      },
-      {
-        "contents": "B6C8C14C3304DA",
-        "de_type": 874
-      },
-      {
-        "contents": "44359C8173B6FB9A92391A0EAE5C6CED3795595BF808E685FEA3800C",
-        "de_type": 41
-      }
-    ],
-    "encoded_section": "9F9110007C40D5971CAF5E56EA8BF03ACE6BD22D90029D30A51708C5EF060F6B2302BBAFF4C86DD795B8E6C90593DF259C7456F78C3C538BB0F15EDF17939A6C167B07648A62BBEDC503DBAE8861E36C6D465288149E9E99BC0514AE21D81EE7A68420BED4EC65D36A625CAB083EF8AA67EBBEAE047778D2B1A6DBF17FF25BC013B70582694E68E21E45C2537CE508A82751F5079DA2D24255E6800D7B48241B",
-    "identity_type": "trusted",
-    "key_seed": "ED15F635173F72E83E0FECAE6051EE847DC187452B21CD45FAB6695D6B46FB6A",
-    "metadata_key": "FA3B46AB691AAA00B09C9CD962E3811C",
-    "nonce": "40AB25BE3D15124FDD3852C2",
-    "section_mic_hmac_key": "EA130B96787E966323391209C412448E67E2554BFC79E1D0A4C1D8F28D29840C",
-    "section_salt": "7C40D5971CAF5E56EA8BF03ACE6BD22D"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "09E7B0FA69B5C2148852706F8C5F82BD",
-    "data_elements": [
-      {
-        "contents": "8C43E3E02909D0E210F56F35F3A53ABDB0",
-        "de_type": 153
-      },
-      {
-        "contents": "45",
-        "de_type": 747
-      },
-      {
-        "contents": "EABB820A273B4051E4",
-        "de_type": 653
-      },
-      {
-        "contents": "",
-        "de_type": 828
-      },
-      {
-        "contents": "640DA947E4BA04CC0C40BE9FA8DA6819E90ADFAB03675A24BC2D66E541",
-        "de_type": 780
-      }
-    ],
-    "encoded_section": "7C91100049432641BB8252194672F33CAECB191190047289517226E7FBBDE5818FD68CEAE9209FF8BAA41136E4807358253C75094411B689D301DC04E3516A85D40B21DF0700D7B92EC91D1EEA36287C7705A61105847DD7D8A428E634D74A99BE9AD6EAA73D38FE8F69CE611C48AB50D989C8E64F6E8E01A44AD1A5B2",
-    "identity_type": "provisioned",
-    "key_seed": "B169199CF07A1D54ED407BF64823FF86996412B093B8484B37834D8924EF93D4",
-    "metadata_key": "9E2A47BDDF6F1B9BEA356760D0F3E69C",
-    "nonce": "1BFD42F51287EB2A9F5E1F4A",
-    "section_mic_hmac_key": "3BC6B7E8D9F2574AFDB2475C7446292E50BD425D7C29210ACBB02926A8BF3446",
-    "section_salt": "49432641BB8252194672F33CAECB1911"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "3895442EB8CDC0F81A3C413255B87845",
-    "data_elements": [
-      {
-        "contents": "8A78F02365C12DF7959D96CD0DC8D50C000230D7500F2773BF92",
-        "de_type": 791
-      },
-      {
-        "contents": "6494FA1960D6F8",
-        "de_type": 709
-      },
-      {
-        "contents": "DF5B46AF34309403DE697850F22EB80BEB",
-        "de_type": 10
-      },
-      {
-        "contents": "5BB5A41FF36679",
-        "de_type": 284
-      }
-    ],
-    "encoded_section": "79911000543133A572BF9818330781F72566EBA39001945FCDA195E5DD57E890340E1145DDB3E7F29F61570FFC84C816A65BECF670144691E6DEED393F745D1C2FCA8F81A02EB783E970DFF3182D3A0DA22DC6046EFA6AA8311E1B7A9B9B3397B1C7F2DA32F5AF4A59698565FEB006903A3A2BB2AA774E65DDA3",
-    "identity_type": "private",
-    "key_seed": "41A397419E6A7A9BAC80625800C7C5CFC5A9E38D334423670A10C95B46385944",
-    "metadata_key": "6DF6268ABB94ED2B7572647828350E37",
-    "nonce": "3FFCA5B7F335F4080095078A",
-    "section_mic_hmac_key": "8E01172131A0E1AF5B2E800FBB4F6B941561F6A25326CCE3FF8549C87F059B75",
-    "section_salt": "543133A572BF9818330781F72566EBA3"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "FE335C4919EB054F5D03A0F3659BB5ED",
-    "data_elements": [],
-    "encoded_section": "35911000CF8C1FF97CECF7F829F6C8A71E77CD779004792DD541DC78082D871E1191261E74A3B8E20A809244EC191525C631BB404865",
-    "identity_type": "provisioned",
-    "key_seed": "354C47FE5F08043C0739F02C7C491A5CDC2ADEEABE5C95CA26EF56D3301F32F0",
-    "metadata_key": "0CBB7E1212820A68D7DA2C386E9CA368",
-    "nonce": "D0CF770C2DF7104179F8FEA3",
-    "section_mic_hmac_key": "D94E767398418969D4D7F94690A8BCBE3E5D7F85990B958A83FF465E723CA19B",
-    "section_salt": "CF8C1FF97CECF7F829F6C8A71E77CD77"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "ACD71592FCB1FD6C6D8ED46C23769315",
-    "data_elements": [
-      {
-        "contents": "C8A12CCC17EE72A7AB87A19A",
-        "de_type": 38
-      },
-      {
-        "contents": "2A0E63613F23C6B46B0C7FA2F22112381E62F5B1E9",
-        "de_type": 129
-      },
-      {
-        "contents": "80F038E4063FBDCD0612FE767119359E",
-        "de_type": 564
-      },
-      {
-        "contents": "4DA8FAC9B8994DB6",
-        "de_type": 3
-      }
-    ],
-    "encoded_section": "789110000F3ABCDAB5986222DDFCBBB8FF38566C900428609F59EC9E471AA93544E7103F645AC96E4FAFC86CDD5CDA58B682D3FFFA015B6578E6C2E1C21AA406B7175401D3FCE1ABBE909A2A50C0804D4E1A8CAE5E27B1BED0823246EA34539ADC01915175F58B47E44680672B72EF54451DD632977507326C",
-    "identity_type": "provisioned",
-    "key_seed": "23DFBD6B52C3B59B254DB98D406E8479C25020C1DFA630A8BD2C89F88080C615",
-    "metadata_key": "379389FFDDE6252B5130D231D63472DD",
-    "nonce": "15F7BCC7074470D3F1CFB56D",
-    "section_mic_hmac_key": "BBD2B93E7B2477438E34C1205D0BCE0B365A7BC302EDB5CF0F82B6894603E24D",
-    "section_salt": "0F3ABCDAB5986222DDFCBBB8FF38566C"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "BF15896C0DB200DAE1115AC614084DA2",
-    "data_elements": [
-      {
-        "contents": "B874CE053E51E004F52641A7CFB2D3B0FD74B7DBE178776A671A0DAE2F",
-        "de_type": 302
-      },
-      {
-        "contents": "913E81800F04456267ABA9E4782859A4E1C7F9BA7E9F",
-        "de_type": 115
-      },
-      {
-        "contents": "803EF3051156701AB4EDAEF5FCB355594348E96537E5EC0571BE99",
-        "de_type": 353
-      },
-      {
-        "contents": "CE22",
-        "de_type": 749
-      },
-      {
-        "contents": "8BF83F936CA560AF25B8E0FD2C",
-        "de_type": 140
-      }
-    ],
-    "encoded_section": "A09110000909CE4451ADA9FEAE94AFC32F74DA8C9002500BEFA6439AF0E094408096D11836FF233FD696C45B67FC5FE377CA37A7156D30C7747C04731ED487D679E79747AEAB7B9264C1F02FF4B3B56A6282FF1BA99927C50B6CB9A5AD8300C58F0DA8D189F7B891E3165A3951B9587FCEAD411774E3C191ED51D02878659572425B45D0098395AC66421C05B5B7984A66B82115AB609678A5B2E8B109BB1DE8AE",
-    "identity_type": "trusted",
-    "key_seed": "D32BF06FA8E93B413768C8F0CD5422EC87FD4F07A83471240BA220E36153FA02",
-    "metadata_key": "1FE225A34E0798FF526E8B55F10AD9AC",
-    "nonce": "B54225F9D30F237B635A51E4",
-    "section_mic_hmac_key": "CE683F20EFFEBF4F062DA3EEDB52DDE50B32C8D63BF2B8A5551EDDD354940500",
-    "section_salt": "0909CE4451ADA9FEAE94AFC32F74DA8C"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "5C0FF3E5206B0B714A43B06D56DA4CD8",
-    "data_elements": [
-      {
-        "contents": "B83154F8BF3A58B9F7F622486850E19C9798FF7B0D2B57DF1ADB",
-        "de_type": 604
-      },
-      {
-        "contents": "2855D0AD4AA6C16ABB392AA9EFD8A0973235E86129F8CD652F1F",
-        "de_type": 362
-      },
-      {
-        "contents": "8744051612F79A9F05D48FEEF1F276B6039B4EA57B",
-        "de_type": 639
-      }
-    ],
-    "encoded_section": "87911000DB30A3E8AB8EB0FFE1A3C1C32107E97F900229702D3456ED474EB3634E1123F78A5EE68795E469986D32E140F7768372A49544565B77EDC858130D8F31885D21142238E6160BFBC4AA1E2C16617029B098225727DBE761B39D8C4F3A9B81DD79320B786B8A73AB12495BDCA873416489EFA679DBCF4D890CB24953E193DC729A7FB13CD7",
-    "identity_type": "trusted",
-    "key_seed": "CBD84DE224729EC23993EC958BA5EC191C55984C7565A37C202C66BC2F496DC5",
-    "metadata_key": "E2C1CC17E5DA3035D3A4A9D67B696A8F",
-    "nonce": "1769E6807F6DD41EED99365E",
-    "section_mic_hmac_key": "3B760F5F10A764D965E928C2EC75BF3C185BC1E60F1BC25E3A4F2A23B08275C9",
-    "section_salt": "DB30A3E8AB8EB0FFE1A3C1C32107E97F"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "EF4525C9E148E35CE5B6B1ED3EC7F397",
-    "data_elements": [],
-    "encoded_section": "35911000F37AF80B78106073DEE5B3E89CBE817690015242E1B8F8D5B0AD1A98BB81C0B7AA6E2F08129AA0AC99057C4EF93D009FACBD",
-    "identity_type": "private",
-    "key_seed": "45E271D048743BAF708F32EAC2E0C37B0AD50DC468268F45FA5A00F273710BC8",
-    "metadata_key": "D5BB4FEA17C7E817338B14C4D01F95AA",
-    "nonce": "9EB99045CCBB3943C3C85689",
-    "section_mic_hmac_key": "94142F9B2D08B819B2681AA5A8532AA239FA5D248DE1400308218E1171A19B2E",
-    "section_salt": "F37AF80B78106073DEE5B3E89CBE8176"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "D7B12DF79375EAD42C400EC4B32C845F",
-    "data_elements": [
-      {
-        "contents": "5EC3",
-        "de_type": 27
-      },
-      {
-        "contents": "9E9931D907110614B32CD6406AD1F2EB87A10F09B7E8E787112EED",
-        "de_type": 473
-      },
-      {
-        "contents": "D832A59D",
-        "de_type": 739
-      },
-      {
-        "contents": "F9A4AB6E8C79AA9949",
-        "de_type": 326
-      },
-      {
-        "contents": "DA3460FC544F1F95",
-        "de_type": 983
-      }
-    ],
-    "encoded_section": "759110009631F8139C4DBED9E68452694DDFC8839004ECDED2D0BE3DF39682EB8C2218A08B11479557857995B61401E0F13ACAAD078B464F1BC371BF9374A4AC12E24E92258EB2020E02A85BBB90568AB3BF976B36BECEC7BA4D0925816AD13747288FA4FF6F5533C22E342606A7B9929FA0A5672FAB",
-    "identity_type": "provisioned",
-    "key_seed": "869972ED54E5B68EF1C466213667435B7C08EBDDFB74F685ED7E16CADB6BD315",
-    "metadata_key": "4B943DF47FA8A1A5ED1983F6BA680BF1",
-    "nonce": "0A885C98D19D59923D1D4CE8",
-    "section_mic_hmac_key": "0FF1627C891D82AD29699A9382972D0D134EC708C71ABBB25F117C50E7564E7A",
-    "section_salt": "9631F8139C4DBED9E68452694DDFC883"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "D99D1918B0ECB9895C5079AC3708F8DB",
-    "data_elements": [
-      {
-        "contents": "9504B53F3858406A21CDB1A8E6E4726EE54D26EF868A1CD0FB3FB6",
-        "de_type": 354
-      },
-      {
-        "contents": "",
-        "de_type": 219
-      },
-      {
-        "contents": "E68CCEB0AEEAD509B8D936D690940E628CB3446E7A5AD4",
-        "de_type": 570
-      },
-      {
-        "contents": "27C89F3446D81FA865168C6942",
-        "de_type": 663
-      },
-      {
-        "contents": "A7B1EAF81C107EE58AC4433F9B2F9194A1C5B3B31F7DBD775E8C",
-        "de_type": 854
-      }
-    ],
-    "encoded_section": "9D9110007E27BC504F56BB2720C62A72226B8C369004EF6CDC81805C1A1B8C3689B047217487EF72A690D9EE8B7DB36DE15EBAF6746CD77D0E9C979344B10045AA56EFEC9ACE8DCB27C444163CEFE58CD7DB8F33F3BFB3F34483F7EDA5D0BDF0DFDCD63AD61AC00E548B445F480EE3F2B5F7CBC8646CB6C916CEBB660725C6735F7105F9FBFA765E51EE242D53E97F79F4FD2151590529DE523296437B2B",
-    "identity_type": "provisioned",
-    "key_seed": "C50E2225963721E5B6EFCE5B1A228B14BDD2BA34AA67E7225550B081BAD88726",
-    "metadata_key": "062D3B665FE311167539CBC5BEAB9BFE",
-    "nonce": "E35AE35D209210B3880DAAA4",
-    "section_mic_hmac_key": "80C63B2ACF7CF9FAC9852D0208BD4797B5DCDEF5A3F4BA561D07FD2ABEED9F03",
-    "section_salt": "7E27BC504F56BB2720C62A72226B8C36"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "905119285260089FF9BE8FF6CC825E02",
-    "data_elements": [
-      {
-        "contents": "C7773F175A4EE4",
-        "de_type": 167
-      }
-    ],
-    "encoded_section": "3F9110002F823378B9F59BBDE54978A162E849E09001CA8F84780BF04A3EA8FBE518141707B75121CDAFB8F0E4A24D354D5775AD46CD6E698AD77AC066B5250F",
-    "identity_type": "private",
-    "key_seed": "4D8C6CD25657A64D6A4FC29AA77B921362224AA73F33A03B48A547BAE062F80F",
-    "metadata_key": "0E77A2EF879199FCDB210192BA8F61BD",
-    "nonce": "71FCB2AE5A7E75EA035E4C2F",
-    "section_mic_hmac_key": "4C314CD5B4BA1DA13BA002B6F8B5B23B83E52A3151E8D3188B18A9116D393FB5",
-    "section_salt": "2F823378B9F59BBDE54978A162E849E0"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "58242E8A9A5B025E720AA40BA1CB864D",
-    "data_elements": [
-      {
-        "contents": "786DF190FA",
-        "de_type": 989
-      },
-      {
-        "contents": "C35CCE593DFC7F1B67D9FDB6DEB00A",
-        "de_type": 8
-      },
-      {
-        "contents": "CD5807FFDB0B16362997F91B45C3F1",
-        "de_type": 736
-      },
-      {
-        "contents": "AE7B1B9F94",
-        "de_type": 485
-      }
-    ],
-    "encoded_section": "6891100064EDA38990FF5640000A6F909B9B4C339004B2D2CAA61055C3A3B5B633A6864D112D20D55DD235B254BD1B4DDE1ED246B422D549F6ED9EBA4E61B144AE199F41786257AFABFF2D09F5ED1D551AA90897424C7DE78D9F4211352DFC23D15FA49BAF2AA3160C",
-    "identity_type": "provisioned",
-    "key_seed": "930ABBE8D4AA0392A66E624A958254C607E161BDB8F63C4DB39E049C200AA63B",
-    "metadata_key": "06190C656B704517A79EBE4A713C90D1",
-    "nonce": "08746932375AFD8D733B8CCF",
-    "section_mic_hmac_key": "E9018DBA2F5D458EBF7F765F5E0A291DC789BF054BDD7F21ABB08F3C9CE0C068",
-    "section_salt": "64EDA38990FF5640000A6F909B9B4C33"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "2E74FA5A2A52433667B756AB26FCD187",
-    "data_elements": [
-      {
-        "contents": "A60BC355C90F28BC4939E07D1CE8FDA9F3BFBC190219B42A0396EBA4F4C0",
-        "de_type": 59
-      },
-      {
-        "contents": "5D789F74C3EDE4BC72D2E79DE9AAF8",
-        "de_type": 990
-      }
-    ],
-    "encoded_section": "6791100074C764F6E930524EF16015CE7D9861A090016D3B7274B511B5F2C9F5A18052F777C5F86FA997CE3BC22C9C6C9A5CA085DBE6488B2A78C6A73EC270656AEACCB923F608AE77568EA51827AE96FF9E6CE819A78E0A4EB2C5CA3B4970BD181F397E3E98EE7B",
-    "identity_type": "private",
-    "key_seed": "2AF41D8404581C2831B893C7DB59D544B64CFB736BFABC81A01C1875066EC279",
-    "metadata_key": "CAAFC39835637CF21D4B75303E2CC3AA",
-    "nonce": "22231C84688BAF0FB9538C9F",
-    "section_mic_hmac_key": "795A1998284D2DFE1A0E29D8118E10D830AFE527DC341E2654B88F2C1EB1B06A",
-    "section_salt": "74C764F6E930524EF16015CE7D9861A0"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "7B2C7A448A3450294EDC49709E51BA60",
-    "data_elements": [
-      {
-        "contents": "E1CF16D6B5287453B1",
-        "de_type": 956
-      }
-    ],
-    "encoded_section": "4191100052105E2C85C91460D02A73EEF05F82709002CFD75D43FD7EEBFBB78A62325C09B1C96593C0ED6CA278F49AF34804167F948B99611B4C536AE40E3B74243E",
-    "identity_type": "trusted",
-    "key_seed": "E685FFAC5E3BAAF679C7991522FB5A6CEC3F100597766673D2746868D929D360",
-    "metadata_key": "6E6DBC6DD5DC80171A5B0DCD03F1BD90",
-    "nonce": "5677F857950960AC98F055B4",
-    "section_mic_hmac_key": "F47EFE70DD21595388F4747DC599502C15BDE2199335DF51DE8F50A3E6858EE0",
-    "section_salt": "52105E2C85C91460D02A73EEF05F8270"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "640ED142DA551CC82073E01140C9F6FF",
-    "data_elements": [
-      {
-        "contents": "CC848267E64EE36BD16459",
-        "de_type": 1
-      },
-      {
-        "contents": "13C03AE9514CEEA49B840E14C3D8FABB",
-        "de_type": 287
-      }
-    ],
-    "encoded_section": "55911000B89F5B7D2D1679457B14537AA5D43AB79001E4AE76D2D7376A024CFAE9AF27D5D51D4409BBE5B1F0ADCE4EA97CA14545F965091703432E7238CD345852880A893E18B154A85EC8F2B0C80B28C20376224BC8",
-    "identity_type": "private",
-    "key_seed": "757A5313F590A0A3CB7880514176C98223D8D89B6C366F0A9C3E614A95667FD3",
-    "metadata_key": "8E48AB97578B936E9DEF574317F5643A",
-    "nonce": "2F4D288A2F3C29654024DF64",
-    "section_mic_hmac_key": "87973F076C0DA1706FBFA208CF3680032F72EFD30A1F35DBDD720FEDB5A73FAA",
-    "section_salt": "B89F5B7D2D1679457B14537AA5D43AB7"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "25B113A142425E28DBF10262674CCB91",
-    "data_elements": [
-      {
-        "contents": "2DC3DFB78F9FEDA6140EFFF49EA5CDB1587B3B",
-        "de_type": 38
-      }
-    ],
-    "encoded_section": "4A911000BD6BA0CD360D3ECEAC02350FFA2287AE90048C31FDA2D808BABB89DEDDC545DEDF43A15DC84FBAF251F8553C3ADA164CD855C3392347333E6ED803F1B35A04CB2DE8491B0AD57C",
-    "identity_type": "provisioned",
-    "key_seed": "B94AF2DE34B622260AB6EB5A28897241EF07FD726FF6B46B09AB855B74BCCE21",
-    "metadata_key": "12188B3AAA6203B3D7B7FFF944B28C03",
-    "nonce": "27D5AA6EB9343720C99B48A4",
-    "section_mic_hmac_key": "F36CE020CBD605AB0D5A58EEFC5B13041B3E8EE37FCD23DA8768D05B7E969217",
-    "section_salt": "BD6BA0CD360D3ECEAC02350FFA2287AE"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "86BEC41ABB2C0615E293D625F5A5CC21",
-    "data_elements": [
-      {
-        "contents": "796D434C2A64E6542A6E8A182C29A6C0A0D10CFBE5",
-        "de_type": 697
-      },
-      {
-        "contents": "B166F37A451DE048DFFE08768C22CC9E16161DE1FC111BE153E74567A3AF",
-        "de_type": 786
-      },
-      {
-        "contents": "15F72C03BCD436FB515626CC4D45B007465E01F99F141A06C2452D08C031",
-        "de_type": 75
-      }
-    ],
-    "encoded_section": "8E911000D67B70722D003E7D47A47A965DDC27EA9002BC3C5974EFF692C78D91B4BC70B183AC34AB08D6774A186DB2EAE6518C429EF6EE80C853B3F36AFF69463401AB265A7841C55974F8307B351ACD0AFC676A7F8E3B6785354E8C8341AC1D159B3449674AB3FC1664DC6A98CBE7B365D5F29CCD0945B37F3DA5B15711A1554A8D20DE4E9C36F89AE8CDC0529F18",
-    "identity_type": "trusted",
-    "key_seed": "8D0769C53264F0E56940C303DC296FD0FAC215BB638670FDE74C2D254EC351B6",
-    "metadata_key": "6E402856AB9B61F6C85EE44427DC3B78",
-    "nonce": "76315884D2C7B9F90A9E40BF",
-    "section_mic_hmac_key": "E87538BF29EE50BAE15333A281438C3A5776A0544CC3AB1EF84612ED7D59D40D",
-    "section_salt": "D67B70722D003E7D47A47A965DDC27EA"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "F22B4181441945D8B70988072189C682",
-    "data_elements": [],
-    "encoded_section": "35911000324C90A945FF9A8AECA1AC7F71C9B7FB900106FABFF1B5D76193C4196B7CE59C3AAA8D2FD16CCFA6CF09E8C226BD7604AC56",
-    "identity_type": "private",
-    "key_seed": "BD5DE9F03A2FAA422C470AFC4F151615F326CBE3E60828C0ECF68CF1215BB760",
-    "metadata_key": "60FEAF935A8ADA1C80FECDDB8F4290A8",
-    "nonce": "74FF9C6FC9B74DBC58D80EEC",
-    "section_mic_hmac_key": "83F5E26CDD98C91D084CD544BAC0EB6C79F3B79B388575973C75F76C66671427",
-    "section_salt": "324C90A945FF9A8AECA1AC7F71C9B7FB"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "48543F7CDFE13F94F1FF50E76088C2AE",
-    "data_elements": [
-      {
-        "contents": "9AB990FC7EF63FC213AE08BC81C35EEF8AAE8DF403E17839D9F25C30C7CA",
-        "de_type": 731
-      },
-      {
-        "contents": "2D33ADC55687CC6D516326D3083A01DF79F7C20347BE2BA49726",
-        "de_type": 652
-      }
-    ],
-    "encoded_section": "7391100008FF6026C41A8392B3401F690B0FA3E3900456BD090474095981D8D630D9AC342F4DBB5653D8CC96BB884B960AAF63A142323A4C41B7B287DE61D6724BAE3BD0C9692357BAB8A84265443B452CC632A26228824ED3B278FEA3E89BAB1F849DC2D59B92F7923D886E39B641A0824C5754",
-    "identity_type": "provisioned",
-    "key_seed": "80D21E28838910DF3318A81884493AD8B3156E22B9EC94DF8E440963D1E6BB0E",
-    "metadata_key": "F588CA28D3A30ADB81E4CF0F154FC445",
-    "nonce": "E3C19711F434B76AB66ED99E",
-    "section_mic_hmac_key": "5C093723FBC9061053CBE302658A97A8641724524D38B88FA576563153E43944",
-    "section_salt": "08FF6026C41A8392B3401F690B0FA3E3"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "EC4C02761EBCE92561D9876B86277ABD",
-    "data_elements": [],
-    "encoded_section": "35911000812086DBDE127B30D0A49750706EFA7090023EAC44AC52CC19411C1EB8A3B054D3A4DA29BA7AB1F48B2B66D53A0E89BEEA81",
-    "identity_type": "trusted",
-    "key_seed": "F905BB1C2C53DE72F94DF4E6D2B06BFB56BE0B64FB9388FA64D0532ECBB2EA91",
-    "metadata_key": "3FA740734AB4B08F591E7A1678684145",
-    "nonce": "7DFB4025DEE53AAB242382E4",
-    "section_mic_hmac_key": "C606528062B1FC77C27EEF4A4C3C2DE18672B5334A4505036F0F7D8E3FED343A",
-    "section_salt": "812086DBDE127B30D0A49750706EFA70"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "08BF72906B4385228E63E345F18A05C0",
-    "data_elements": [
-      {
-        "contents": "9EC2961EB5315850F02F",
-        "de_type": 332
-      },
-      {
-        "contents": "75A55F90EBBF",
-        "de_type": 89
-      },
-      {
-        "contents": "5B1B5253D80289F8F01BE72974EEBDC3ED",
-        "de_type": 204
-      }
-    ],
-    "encoded_section": "5E9110007EAF5B0AF95F689E81F61A051D316FD49002DB318ECF2B751DB740797728EA96E14ADCE0B2BB27EBFC6D45312379906D6494808D7E9C8BA6242B4A02C4C23223112D77B94131BB9BE739661753C158001AF9CD0EDF73B899AA266F",
-    "identity_type": "trusted",
-    "key_seed": "EB8F85BE87A0A9347B3CBECE969B008A57FFEF6E4E8A95F734893EE710B27FE7",
-    "metadata_key": "825022A58B49C829B9B524E12AF0E160",
-    "nonce": "B5143876982CFC9AC88966E4",
-    "section_mic_hmac_key": "F649D8AF765E2E31FB870480932F4952FFCA19301F2FAFCBDB992928268BC8D5",
-    "section_salt": "7EAF5B0AF95F689E81F61A051D316FD4"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "6FA3188C366D18061138ECE126A97BFA",
-    "data_elements": [],
-    "encoded_section": "359110006B869584D8B9A2617A3911586E08679290028283D6FFC57D5D5FC3690502E95413F8BF29DF7EA841527BBBE6DE1C918E5B35",
-    "identity_type": "trusted",
-    "key_seed": "4ABD1D1549B74AEECA657499EDEFA7701D724FF427142DFA2ACA1A087455E339",
-    "metadata_key": "32152EC7E3BD86FB1D19DB1136E934EC",
-    "nonce": "5F7C76D24DCFE9E61B909C2D",
-    "section_mic_hmac_key": "89CC3C25CDCE4A6D36EDABF94C225087C455DA885AA113254AB3A71DAB0F27E4",
-    "section_salt": "6B869584D8B9A2617A3911586E086792"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "46C102A2CFEB44FD44236234AC618374",
-    "data_elements": [
-      {
-        "contents": "2DAED2D919",
-        "de_type": 752
-      },
-      {
-        "contents": "69642A6B9FA86810",
-        "de_type": 907
-      },
-      {
-        "contents": "C9133E66C06507782EFCA12CFD2255DD2AC3CCFE568E5B",
-        "de_type": 75
-      },
-      {
-        "contents": "11917BC9DB942E",
-        "de_type": 253
-      },
-      {
-        "contents": "B772136AF949D06B3C74EECB6A2877B73140CF7BD459",
-        "de_type": 741
-      }
-    ],
-    "encoded_section": "84911000D5B533CD04983822338C7E4679ABF0069004CB79ABFEA568754DF58A73D97993AE327F22F6EA434A49AA5D12110FB08F88A77C3EA795B49DFE542046BE6FF89B46146088D770CA8D4E221D73BEECB978974A7E3A2B29C0FE7002230356778F0AB6BBFC45279E08D93657B5D857EE9915A3343FDFAD5ED96C886E225EE77AABAB71",
-    "identity_type": "provisioned",
-    "key_seed": "B45EA904C8AD8040870C0C5996E7D6474109D43A1ED77CFD4EC5A0909ED8537D",
-    "metadata_key": "F27F824E7A676B87F008B914C697F731",
-    "nonce": "A9D31F3FC180A643BFD2393B",
-    "section_mic_hmac_key": "C57B68D19F0C0467792E61C6129CAEFC41C9BC017CFDBF3ECECFE053247C86B4",
-    "section_salt": "D5B533CD04983822338C7E4679ABF006"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "E76311EA2FA2BD5E23ADDD81EEB7035C",
-    "data_elements": [
-      {
-        "contents": "F0A613ACBCA74BE6A2B1EBFD40C52E0D66E5AB04",
-        "de_type": 294
-      }
-    ],
-    "encoded_section": "4C911000CC0BB7E46499CF0636B26F05408554E39004020E9465725DE7A884C5B175C90015562B2F9B106F9F1AEC608425109ECAC0513AD32D14354CE552859514849D19D6BB59CE2D0EF9A0F9",
-    "identity_type": "provisioned",
-    "key_seed": "CC9A75CC75BB1D230A75AE87B6AA6D14E08ECD864C90DFA2C144DA62AA2FDA4F",
-    "metadata_key": "D1FA24F308946E26486C02AB69CACDFF",
-    "nonce": "DDA1727B363F44B3A00AC548",
-    "section_mic_hmac_key": "1FFE447BB7A8331E0AB04BD9E920759CE433094711AAE085EED90F11AAB04DB9",
-    "section_salt": "CC0BB7E46499CF0636B26F05408554E3"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "E670DF92DE550C7D16A47FF544AEB0A6",
-    "data_elements": [
-      {
-        "contents": "5297CB087EF89FF30B412ED7FBFE9B",
-        "de_type": 536
-      },
-      {
-        "contents": "C6C348BA415CB959EE2B51B43DAF200076890AD49B044F5F847728",
-        "de_type": 555
-      }
-    ],
-    "encoded_section": "65911000FAF0B973AAED0ADA284F65A67CC775A6900191A4765553BDA369F32677FF10F57E0793B4FC72165819EB2E38975021A78E8367C4BC9A94E846F6A04ACCE516355A0A93938147C6908509D1599D7EA96A377B7ECB31F81BA98D8AA01CED9617C410D6",
-    "identity_type": "private",
-    "key_seed": "238046E9C80693E551BF4F3E7E563FDB8FC7EF319A4E3405D603DAA10BFA0AF2",
-    "metadata_key": "BB080335533CFA2002C3FB861236215D",
-    "nonce": "30A968C64C6D236C78A202D7",
-    "section_mic_hmac_key": "20309013BDC66CF400D5E221109AB10CF0E742555D8713BBB332648E3897E847",
-    "section_salt": "FAF0B973AAED0ADA284F65A67CC775A6"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "236A98706211AAF9BE9FF085882ACD8E",
-    "data_elements": [
-      {
-        "contents": "6D6AC5C8256B7859373EB86A68E7F9",
-        "de_type": 748
-      },
-      {
-        "contents": "ED06337DA3ACC11B7BD86A0D315DA7A4EF",
-        "de_type": 703
-      },
-      {
-        "contents": "1639315FF6AAAA4B",
-        "de_type": 855
-      },
-      {
-        "contents": "785FD18ABF9AEDED7D3D407144A7BCE2F34444",
-        "de_type": 738
-      }
-    ],
-    "encoded_section": "7C911000F1B2D3FF697FEA61F1D24313BA24DFB09004BA73F0D4A7915365382ABF284BBB970F4F4FD77A6B3F48CE433EDF5679495A60CC803BACE3039072420D0FE9C1CBB529280C72864F31A3523FE98BBD39AC48E3A47524E57BC2259AD362901721E8EB8FE1F563C6C3EFF3374A277553D9D41732D44A4CB97BFE1F",
-    "identity_type": "provisioned",
-    "key_seed": "2D8D09BB3E1DFAB0611039A1BAF74D5258343FA303332251F86EA7D1FCCBA814",
-    "metadata_key": "158E2E60C7F45BB9D6487B4BB3984DEE",
-    "nonce": "9592919D3E310674BFDC2E9E",
-    "section_mic_hmac_key": "69D008503467933362AFA9AAEB534D22839A8946A0A2AC717553CD50ECBD5D44",
-    "section_salt": "F1B2D3FF697FEA61F1D24313BA24DFB0"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "52A4E397E30CD9CC7494F1FB77E608AA",
-    "data_elements": [
-      {
-        "contents": "8C3E52853DF5586EBD",
-        "de_type": 413
-      }
-    ],
-    "encoded_section": "41911000BC844E7B944BEB5585FAAB4B71443FE990044489F0D699085E6ECDBC45D5A180550578468BF5FCAF01E7AAFC8612A47B5B8BB81A82E844D8EF05E4B223B5",
-    "identity_type": "provisioned",
-    "key_seed": "4975391685B7E23B969FD2EAF9ADAFFDA9E54345F6C978691019DFE171CF1AEE",
-    "metadata_key": "ABBCF6913AF3396D4698A3D3ED9C844C",
-    "nonce": "6DBC8A54F98C20F59D424BBD",
-    "section_mic_hmac_key": "7DA38D6F5976FA186530C3259FC16951302C45E1A182A5B856778AC15D261A63",
-    "section_salt": "BC844E7B944BEB5585FAAB4B71443FE9"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "81775F391E97501F10A06ACF2C1E1E8A",
-    "data_elements": [
-      {
-        "contents": "E0E9135120FB76345D7F8649D99DCF21082B",
-        "de_type": 209
-      },
-      {
-        "contents": "A8930F2B30AD1020C7719732557A115B73",
-        "de_type": 702
-      },
-      {
-        "contents": "8F573C102D81EAD9DC",
-        "de_type": 608
-      }
-    ],
-    "encoded_section": "6A9110005135798EFB48BFB42A2542E5EC84F8BE90024EDE7FBED7BE6DB4BF57CCAF6B79B42AD2AE82EF17209B1118B1B9BC30E3DC7EE47CC74BE402A40786226E6662CF3C582AECFEDB6BD9FDD87CD780D34B7CC7708714ABD30875A945E6592FC178421FE73B85293353",
-    "identity_type": "trusted",
-    "key_seed": "CEB5046E0E41EA2D087D6BB2584FC8F5AC4AE70C3F262AFA2FE83BEFC6A7F96C",
-    "metadata_key": "09F8FB1D817845D11CE0DD441FB6E6B5",
-    "nonce": "70D102E7E612845E49DC9A32",
-    "section_mic_hmac_key": "600AF01CC5117CB4AC58D49609CA3235EA0ADAC22B68D6FEA6973397744E7100",
-    "section_salt": "5135798EFB48BFB42A2542E5EC84F8BE"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "F3293976BEB4E1EA2904909A54A90D0B",
-    "data_elements": [
-      {
-        "contents": "AEA9748A963B21DDDC0B",
-        "de_type": 82
-      }
-    ],
-    "encoded_section": "41911000CCFD0A49D64FB8913DB3533CD842618F90020B1F2BE6DFF4647003A4CD20BEC2045A9BC3557018B6D8D1DDF5BAF68CBB0043BFE6F7F3A16914E72473574C",
-    "identity_type": "trusted",
-    "key_seed": "F7AB19164021C53FD3F406A4F4617A396FBB0B2083680D6A70F0FA93ACE0F337",
-    "metadata_key": "43CAC734B4E59E6410B789DCA22E05B6",
-    "nonce": "14B2E4D0A4BE88CD3AEEB9FF",
-    "section_mic_hmac_key": "6D816BF43B74326FE8A2A88B37CE383F8B46D0C126454936586F027EC60C489D",
-    "section_salt": "CCFD0A49D64FB8913DB3533CD842618F"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "375B33A2656E245BB1ECD330174673A4",
-    "data_elements": [
-      {
-        "contents": "54A8E89BD5EEA62D46C39CEE1D60ED4733976A",
-        "de_type": 596
-      },
-      {
-        "contents": "",
-        "de_type": 599
-      },
-      {
-        "contents": "05286DB66919D9F031C704E925CBF68E6B8E",
-        "de_type": 766
-      },
-      {
-        "contents": "95B7B05F119963240B933C337C6FA9B6",
-        "de_type": 336
-      }
-    ],
-    "encoded_section": "76911000AB7FA706A14892CDC9CED6E9C13C028D9002D26550BE730945C1CA0C220A658856FA14430A49D45BCEEDE10DD1CAFD274BE5FD828116B8E3E3D71A3F679486A30D832958AD1B2E1415BF7F111BF4F6CCF94634C904B46F5FB2482F8520F7EA10061C018B6C24891AFE186B8144EFB4981D1E79",
-    "identity_type": "trusted",
-    "key_seed": "D93D2A387F132BE81714405D487F9323099CD7089ED80FE06DBD5F19B9479AC4",
-    "metadata_key": "C249909E8C874421F56B5D5FD883F6D0",
-    "nonce": "3608D422560EBDF764ECC44F",
-    "section_mic_hmac_key": "ED742AAFE3D39273AC51EC4E94A7E5AD3EFE52D36E61F96ACD446F2A9C77618C",
-    "section_salt": "AB7FA706A14892CDC9CED6E9C13C028D"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "9911435E27099CC033ADA0B809A9BA61",
-    "data_elements": [],
-    "encoded_section": "35911000DB8B02B57082EB58CC198556EBED435E9004A72315469AD264409CF53387374629C4683B4C7D198CCFFA95ABA332D9EAD095",
-    "identity_type": "provisioned",
-    "key_seed": "DE3FF3C256080ACA31DCE848A0EF6DE573A584AC3FDF00011207692CBFC71AED",
-    "metadata_key": "A59DB751175865B4DB9CE9F5791D48E2",
-    "nonce": "22CEE3D3F35609F5B04F2681",
-    "section_mic_hmac_key": "88118CAA46862E8B57E83C23387C88F232BF22E5772D80E9EC15B75DA160F25F",
-    "section_salt": "DB8B02B57082EB58CC198556EBED435E"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "78237E429DEB7FF64A0CF0390C6B5AB5",
-    "data_elements": [],
-    "encoded_section": "3591100092F6C10DFBB105F88D53E46103584A369001CEBC52E442697CE7C0439351600F775089647E153AA0A20B8E0446CF188523A9",
-    "identity_type": "private",
-    "key_seed": "3343E7EAA3F48E39F01E5E8BDD5082E6DC4FA5A75E4FA570E717EDECAA9D26C2",
-    "metadata_key": "65D3B6A2568AE0425F06C2E1A5F75F61",
-    "nonce": "B44EB10C11D792A4474CD363",
-    "section_mic_hmac_key": "FA58129EF913C31E017D2583EF3C53A6921C4C05F1F46DFAA29644E1AC8D13F1",
-    "section_salt": "92F6C10DFBB105F88D53E46103584A36"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "718BB6207F09ABF1767C88964995A316",
-    "data_elements": [
-      {
-        "contents": "41B733343BE9A40195965F3BE53A9E8DA772F91D",
-        "de_type": 38
-      },
-      {
-        "contents": "CB88998C5B3ADC6834216F607769",
-        "de_type": 644
-      },
-      {
-        "contents": "E2974A19DE611A27FF0AF9D27042B8073563FD2119C828CE",
-        "de_type": 587
-      },
-      {
-        "contents": "4FE69B",
-        "de_type": 58
-      },
-      {
-        "contents": "16A152105C8A1CF879C2838EA6CF4CC9",
-        "de_type": 494
-      }
-    ],
-    "encoded_section": "8F91100054D79FCDA215B6D37188275A6D71B49C9002E7ED4C98D0689465F88315C5E1E4CB1C994AF90E6DB73F55D62E14F5F6102EAEC722DD4B819BA652CDA67523910FBF4AD135A678F170DF68642B9A3E651817C280D56CB333C492B7B7C6EEC2718D16DEE888255E9470E50548CFDD8EF3FA1BE96BE3FC9206AF1E806AB72D7CEB2556A84DED90BFB26C7473D6A3",
-    "identity_type": "trusted",
-    "key_seed": "87AF0A342940CCE0D8A7C250CEC581D677776EFE684FE7A461538D306F79595F",
-    "metadata_key": "ADC6D21008E6C6F9C6140DD2CA7A2D74",
-    "nonce": "DEA185B8030CB125ACCD1991",
-    "section_mic_hmac_key": "0B96048E575A8DDE941E0BFCC9AF72673B9E59D0EEE1D410EA3B5D7059D8CE43",
-    "section_salt": "54D79FCDA215B6D37188275A6D71B49C"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "AD321D407A23FC1F3DEA4AFBFCE666D8",
-    "data_elements": [
-      {
-        "contents": "DE6102C7908322A68E36094307406EFE",
-        "de_type": 130
-      },
-      {
-        "contents": "D811C6749B",
-        "de_type": 979
-      }
-    ],
-    "encoded_section": "50911000EEF15DD154F1CBB12B876AB7E4C28C519001BADA4DD563CC46E682BE16A078D5AE079DB587A78C9BD6BB37621F9F8EA5B7FB490D6857D3C3D3BCCABDF00E85438DB6227C002BFA7291D54E5FF9",
-    "identity_type": "private",
-    "key_seed": "9B702205F66C701BAE43D0EDDF8C3EAD92C80A3A8583C3397E7A3371758011EF",
-    "metadata_key": "FF0BEBFC2C42168A47B3474D3439772F",
-    "nonce": "D225BF85D21A4C34FE6B6AA8",
-    "section_mic_hmac_key": "9CE75F27AC9E99BEA41587BFFB701F9D59793C68127183BA3BA38D219DBFDD1F",
-    "section_salt": "EEF15DD154F1CBB12B876AB7E4C28C51"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "D62FF5F858D26914C62F3C93ABD608D0",
-    "data_elements": [
-      {
-        "contents": "72FFD6C3F4B1B90771A08D281085",
-        "de_type": 817
-      },
-      {
-        "contents": "A4F94F7BEFD75D29A53C2ACDC0A908C2F5AA0768",
-        "de_type": 618
-      },
-      {
-        "contents": "0FBBAC5329C023",
-        "de_type": 729
-      }
-    ],
-    "encoded_section": "6791100097FF92E30C6B6E08B416A150E2099C2C9004A34006E38EDAD68F7BBA8294E12E9F92DF7A91259AFC5B813BE7A4FD8601959912E5B2CFBACA99A2460EC2CC0F6DE999F47A7580B9F0CEA2E0F882836864E82CEC3DF6F890269AABF65852CB9CF026D85452",
-    "identity_type": "provisioned",
-    "key_seed": "393F3DAA73636DE22AF63B869FFC970C1DAE302372E51F35BAC421F3C354B666",
-    "metadata_key": "CA8F91CB3DA7A5A37E33F851D93EBEAF",
-    "nonce": "A521F5DAA92BF1C1654569ED",
-    "section_mic_hmac_key": "67AADDFA3C1E18B279906DB7987D29EDFEA643D678FA1AB4987A0A1D610005B0",
-    "section_salt": "97FF92E30C6B6E08B416A150E2099C2C"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "EF85681E26EF87C8D2A1A3EE56FEAD2A",
-    "data_elements": [
-      {
-        "contents": "C24BDBD7E3697F223F1621217A9C36DC9394EC33",
-        "de_type": 335
-      },
-      {
-        "contents": "C1",
-        "de_type": 14
-      },
-      {
-        "contents": "E4231F2308EB41",
-        "de_type": 902
-      }
-    ],
-    "encoded_section": "5891100037E9D4686030FBAC57D3FE42D92B096490014DBC20227A7388E74D0961E8674691D101607D944F8AAD12C950188603894DA02D5FA3698A54DC40598F44313F297CDCCBC1DC14CD32D267267747B0B125D91A89A557",
-    "identity_type": "private",
-    "key_seed": "4A289EAEC36BE20656071FE87F8A55155D8A893815038B9ABCBAD3E72DB0C8AC",
-    "metadata_key": "C0450DFCC6AB17837C56EEDBE57D62A8",
-    "nonce": "A9FA4AB4C0929CF2CDEDDF34",
-    "section_mic_hmac_key": "8D45B9467DF17FE0943F155851C70CCEA0555BD59453503FF6A861F9011EF853",
-    "section_salt": "37E9D4686030FBAC57D3FE42D92B0964"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "BAC1BE39B5BE69F29396EF5CC41402B9",
-    "data_elements": [
-      {
-        "contents": "AF9A279790C0EB79F07F700C1CED8CAF60B72F",
-        "de_type": 101
-      },
-      {
-        "contents": "14F153F824",
-        "de_type": 463
-      }
-    ],
-    "encoded_section": "529110008ED27B5A64F8584EE798DA971BB3890290023D54202AA267D540AD159401DD2CC11E1F387FC4F90C9BA19D810C095D415CDF08EEC637B9CB2DF1259229CB2F02F894D63404B428C9AA33BDA7507601",
-    "identity_type": "trusted",
-    "key_seed": "9AAF4E2D596A7B4CA21DF9312AFD43A63FA8FA727DD60273072C4C4C0D472E3C",
-    "metadata_key": "5F68164865C2DFB852C81375C3D3D5E6",
-    "nonce": "4F23D7E91CB8E22BD3712FC1",
-    "section_mic_hmac_key": "0BF5C29B16B353256F793BBB4F1C8C52597D3F1C2EFA9D61EA46CDFDE84797C7",
-    "section_salt": "8ED27B5A64F8584EE798DA971BB38902"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "68A9C01E867B79A4CF5C2B9B0A5AF91C",
-    "data_elements": [
-      {
-        "contents": "AE6F1048CD",
-        "de_type": 505
-      },
-      {
-        "contents": "092DCE72E9C606136151791CACA850F34040EB06480609",
-        "de_type": 524
-      }
-    ],
-    "encoded_section": "57911000E8C86E9B41DF4F3B937952F49BC6738B900232D03070C79D80DDE8C1E0999B4434DD06A23FCF22CCD7DCA29E5074665F99831137706D16C0200B8B4598365543AD4BD647A682DD8D226897C1EB8F43C443DBD6F8",
-    "identity_type": "trusted",
-    "key_seed": "580ACE8B6CDE7180BD4AD61F486AB6AF65F04174A9E10C982CA1BE480E27085B",
-    "metadata_key": "E850D26EF14BBB1E7B3D1A5F64A50353",
-    "nonce": "2DD94E374A847EE945DCDEDE",
-    "section_mic_hmac_key": "597E20CCB0FF2807C1B8217F21115FB753F67303B79DC679094D7105C5EE8A16",
-    "section_salt": "E8C86E9B41DF4F3B937952F49BC6738B"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "CC5210E1AA93721F6A77C03B0F1460F9",
-    "data_elements": [
-      {
-        "contents": "31B8D3BD3DAD8D7EA787EBBAAD32",
-        "de_type": 105
-      },
-      {
-        "contents": "52CD1D16C9C10292F325BD",
-        "de_type": 679
-      },
-      {
-        "contents": "9AA553097F55186B693EF9F811255D4F9E33012D",
-        "de_type": 70
-      }
-    ],
-    "encoded_section": "69911000B0B06E5BCBFB897F867D1A98860C88819001968D65E73BFDB4A9C8D0CBCFD94F0AFF86A172DBA041C9B4819B60AE15C84ABF10167323F72BCE73348C948BADCACA60B31C01DF54A03A41FFA1CB83B1724E34BA74D43D17956AEBD6F5E9411F6D801A30C545F5",
-    "identity_type": "private",
-    "key_seed": "BB1C67EC1220D0A1CD718868B6DFF75A45F535D79358AD7417E98F584A520850",
-    "metadata_key": "5E11BC73AFA36ABB0FA9D177F470772C",
-    "nonce": "10C1E9BEAA44D044A6A52DA8",
-    "section_mic_hmac_key": "8F47EFD6981006EC099088AA42864E13399300C42A22D67FE00540745BC434D7",
-    "section_salt": "B0B06E5BCBFB897F867D1A98860C8881"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "636CF984B555E396E1ABE06B45A0DC9A",
-    "data_elements": [
-      {
-        "contents": "26124A707B14872CE3CCA803962D1E8E",
-        "de_type": 356
-      },
-      {
-        "contents": "5BE121EE1535F68B384B2048",
-        "de_type": 191
-      }
-    ],
-    "encoded_section": "5791100091150893740502FC52626D5C1FE1D97B9004D692F649B521B08446966B43B3A0E2853FC66A18FD044B2D9FA619580671AF9A093BC9C29AC2285193C8377E231E4E378A0EB12B69F309BBED412881C06054211F79",
-    "identity_type": "provisioned",
-    "key_seed": "9F10722920D514FCD00582382A3D744B7513089C4ED4F0F762E62F079817EADA",
-    "metadata_key": "CED359A792C26F42583568633F362936",
-    "nonce": "AC3820DF4711726BD92A95E3",
-    "section_mic_hmac_key": "73CFEDBC2C7DCC199778704504D8406E69DEF3850E9E556E65264FA5C0F72049",
-    "section_salt": "91150893740502FC52626D5C1FE1D97B"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "BB72B0AF2C215797791F4813246EEAAA",
-    "data_elements": [
-      {
-        "contents": "B560FB4055782B29002780C1C79A92C3AB5C94BBFE1D30",
-        "de_type": 704
-      },
-      {
-        "contents": "D9D67201EFBBC989BC73CB82CE5A54BA30270BCBCD16C3D918C46C8FBA",
-        "de_type": 759
-      }
-    ],
-    "encoded_section": "6F911000C4FC89ADEB54417C2145D31AFE590E67900479850A6176A11C5005C1B816084BBBC8E1B96A5E84FA83927CFE3F85B13CA15C78C4E487AF94942D9527A4EE40FCE291F8A4076FF3285DBC70EB2AE12076384B309C9DB593EF97E5952F1A878E9357C5215EC4CA7382A07821CE",
-    "identity_type": "provisioned",
-    "key_seed": "4F5BCFCBCE781EBD9353546FEFDAB4123548E63980050C1FB2FEBE745EFDEFFC",
-    "metadata_key": "18CE930F2687B1C72063C2B147ACBD0C",
-    "nonce": "09C9C088DEE34D597B83255B",
-    "section_mic_hmac_key": "3BD3CBD58CE94F98266BE570B2897F0C0CDBC8900AFB76E0699315F0132C8D5E",
-    "section_salt": "C4FC89ADEB54417C2145D31AFE590E67"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "9BBC179545C4C8661833BFEFE25E4478",
-    "data_elements": [],
-    "encoded_section": "3591100018CA84AD571B443615D490F5A1AB791E900287C25FBD0A1AA47A74D7960394D449914CF5F364A1DBBFEC57B7447A38AD0152",
-    "identity_type": "trusted",
-    "key_seed": "0B5BD58A7DB4DAB27EAB2B71CE802751442437C426E163735196EBDD9EEC98A4",
-    "metadata_key": "21E42F417AA748179A50406477891087",
-    "nonce": "22A25ECB407B6DC73A8BCE1F",
-    "section_mic_hmac_key": "35857C6B5BFBA20A9012E81B452C95D5F62B7D429C9F250F6FC8C96EEFDAD338",
-    "section_salt": "18CA84AD571B443615D490F5A1AB791E"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "29912DF85FEE4E2802F3CC758240C118",
-    "data_elements": [
-      {
-        "contents": "20EC521C467D60C62519AFEFFAB2989DA34F609D7348CB0648",
-        "de_type": 881
-      },
-      {
-        "contents": "7CDFF605A49D2327C04C9523E972",
-        "de_type": 339
-      }
-    ],
-    "encoded_section": "6291100096874E513BB028EA8D3147662E898C929004510A31F7EEE22BBAA1A676F50D87B736D02CF8AD40A2FC01F73D61050AF86BDD26889F66B6C7811E98013B10549BA699C5B0FF6B97BC95CA461512A91C6CD748A847E8A06250284F89D8768282",
-    "identity_type": "provisioned",
-    "key_seed": "BDBB0B95B3F933B91F05CC9ECED4DD468E3174109DEB7A4F0783C40F39E20761",
-    "metadata_key": "A97FB828B07A9D327D5567152634D0D4",
-    "nonce": "F8BD43493933BD842C97F146",
-    "section_mic_hmac_key": "32031801363FBE1A50288A6A9B0756EAA7CC2D653CA0B2AAF345CC13884DD2CE",
-    "section_salt": "96874E513BB028EA8D3147662E898C92"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "5D161A5D965DE072EC72B7EAAC295469",
-    "data_elements": [
-      {
-        "contents": "2499FFB9C498F2",
-        "de_type": 884
-      },
-      {
-        "contents": "941AFF758DD7D3B66EF8321874A7834A7E7F51BB284122405EE0",
-        "de_type": 463
-      },
-      {
-        "contents": "74C7CF3DF00CA35B",
-        "de_type": 435
-      },
-      {
-        "contents": "E07BAF5D40CA3AE208E6BDAFF05B4F9E0112559EC495",
-        "de_type": 2
-      },
-      {
-        "contents": "58E1ADF13A6F2383CCDE989D6CEB02EE47F1F3",
-        "de_type": 396
-      }
-    ],
-    "encoded_section": "9591100066B9B5E27075D2EAFB9B4E2E4159CA659002025D80ECE442E6A3D7F5DF17BEE9E8AA64D2904E04D09898D04EEB323B64C9AC0DC4F9FA55ECD3B7D58DEB3D9FD72FE1D8A213CCD6DFE89D053B106AE0F7A41844294DDAFB6E22A35F6F5A1251CAE0829609A463D966DC5C2BAEBD166B166CEBF5E3471EE3EAE4DFB5A594C0C64ECDBB00D301299F9782000628F11738B95006",
-    "identity_type": "trusted",
-    "key_seed": "5AEE4779ADDC954FAABA8C967AF32BAF74DA2E3790FF77750F20A10C300C66E9",
-    "metadata_key": "5819227E20E94984AF636A055E65EDF1",
-    "nonce": "627AF9F6E80A568473F0D41B",
-    "section_mic_hmac_key": "97182D1562F3DAC1911CBE791388036D5826EFC5BD2C9B7729555BC39D6300B9",
-    "section_salt": "66B9B5E27075D2EAFB9B4E2E4159CA65"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "8EF15FC5E15B86747BD2390DFD8D8FF4",
-    "data_elements": [
-      {
-        "contents": "5F99290FC42275AC698492E2263094B80F692233DB0DF8644B",
-        "de_type": 845
-      }
-    ],
-    "encoded_section": "51911000474A64D6CC5E1B015636E3CAF848E92D90029797772EBCF190224F6B5193FC0573554FD8D1B7C2B8DFB0C2344080EC5DE79A14650738DB3E375AB1F2416A0E091F2DADD7710C6731F9D2F4BC1ACF",
-    "identity_type": "trusted",
-    "key_seed": "3807E53D0E4A6A3C131FE2D9A12F3A17C9A81E206DD2403424D079986548F36D",
-    "metadata_key": "649BF41A962338BDCAFB3D6EFB39F30C",
-    "nonce": "11EFABF3B773890FD705DC45",
-    "section_mic_hmac_key": "BC9DE8BEFBFBFCEED8132F0E74A7FAE68CCF1F3259B42670C074355E57771EC6",
-    "section_salt": "474A64D6CC5E1B015636E3CAF848E92D"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "D87966196010EEDAE937B516E77C110B",
-    "data_elements": [
-      {
-        "contents": "D72414FDE144",
-        "de_type": 693
-      },
-      {
-        "contents": "89B82594CDEB3253E8BE54402F4EB47427FA",
-        "de_type": 139
-      },
-      {
-        "contents": "59F5D560DCB71A1A9AB340270EFCCC791F60ED3E5C16C9",
-        "de_type": 361
-      },
-      {
-        "contents": "5E8317B7EF6B2304CE733AF9AA4303",
-        "de_type": 347
-      },
-      {
-        "contents": "6B46914ACC05AC520CD86D64D95EF8B0E3B52684AC19C7",
-        "de_type": 217
-      }
-    ],
-    "encoded_section": "99911000550E90767EA297708A890EB778F86F42900194407433CAFDBC096242CBB630D9EFF2428967372331DA563029EE56D7736DB1C6B0F4171C6AACC70B165A1D7079B7213A484AC56B751F106EA890EE1F44B1DD84B03340FF64DE0D86EDF6E3C266B6E29F973D7DA9EA80DA5504E23FA9DEFB13DF26D97D8AC03B102233E7A400716212664A1A66A04360810288591BBC53D82755D0B7F4",
-    "identity_type": "private",
-    "key_seed": "5E128361F047F517713A5E7A9FBDFCBB8A65431B0FD54CEA276C15AA38738D00",
-    "metadata_key": "6219FC039F504E84C27E0C30A7922D50",
-    "nonce": "219ED909CC34B1C57EB81FED",
-    "section_mic_hmac_key": "80144AD3F7356F4D118216C943ADE3CE6A4C1CA1AC36CCEFD81AF75222A0C277",
-    "section_salt": "550E90767EA297708A890EB778F86F42"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "ED1872743ABF1CE1C54B5924D8B523C9",
-    "data_elements": [
-      {
-        "contents": "1EFD8A5E7097C3EA66A30F3F6EC8",
-        "de_type": 8
-      },
-      {
-        "contents": "70AA1FBCAEAF957AF9C114",
-        "de_type": 741
-      },
-      {
-        "contents": "727C6DA4BC51406D03",
-        "de_type": 628
-      },
-      {
-        "contents": "C80BCC953AFBD14D003F9C18",
-        "de_type": 905
-      },
-      {
-        "contents": "B3EC6F94CA366939C934FA84052FD4",
-        "de_type": 357
-      }
-    ],
-    "encoded_section": "8091100002C4DA8D1B21BAE28B3F9A0EB5693E8F900493FCDCCF43EBF7EF19D4476026AE4F5241FE0162977FB70B8E376CEE57BC2A866306A3787F61E88D954EAF7060BCE7A3023C7683CD7EB4F9D15A14147EAF9FE39A0BF9863BCFFEF8F4F2E8AE52F06651E06E94F809CE32E06DC15434A3A3D7AF4E42BBF88EB53E9197D489",
-    "identity_type": "provisioned",
-    "key_seed": "3DBF4DD4A42D6FE76568BF430BC73BC5772762941753D48A76134021D17123C7",
-    "metadata_key": "B6B17BEBEA55CFA5B610047C599FDCB4",
-    "nonce": "9E2B3708BC702B007B2B49E8",
-    "section_mic_hmac_key": "F0E7F12DF72F3EF40DE46181DD19D3E673AEE02757529C53F3F6DF9F8AAEF663",
-    "section_salt": "02C4DA8D1B21BAE28B3F9A0EB5693E8F"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "74300AD75CB40E2D6CC26BF7C3EE29E6",
-    "data_elements": [
-      {
-        "contents": "506CEBFF222CD35C949137A3A47D6FAD9620",
-        "de_type": 439
-      }
-    ],
-    "encoded_section": "4A9110007816B4ED58791237D1053F88CD8B7B859001D1EBFF70B960E79E6DFA6A89359F991816C253BB5B09AA2E98748E101D8BB2D18D4043847E353F85C82A586B92D5E7F0DC03E6CD14",
-    "identity_type": "private",
-    "key_seed": "8CE98EB290087656B04E4996CBF4007E92E653CAE71AF06D479B96D45B83447B",
-    "metadata_key": "81A2696BD7D21903EE2D63D2C4C44E4C",
-    "nonce": "CDCBF3E9E0E3EA002A004AFE",
-    "section_mic_hmac_key": "48966839EED698298B8544D0FABBB3E8009D9D67CE88CEB6F29B1FBFAEFB7C47",
-    "section_salt": "7816B4ED58791237D1053F88CD8B7B85"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "256EC73F1FAF17723C9B5C6062749FF7",
-    "data_elements": [],
-    "encoded_section": "359110007897264056B8ED625680B2F5124C30A890045D4D46423E9067DF2DFD36F3718282724748E10C6E16DA2C039F9C30D9FF6054",
-    "identity_type": "provisioned",
-    "key_seed": "D458DDE6E939B3C03B8CA27BE7D624D70F3777FB31C8278152C3C0161269A228",
-    "metadata_key": "76BCF4EB8C12033E8BF0534B8A6AAF4A",
-    "nonce": "1451BFC0DA68DCB2E0FCB937",
-    "section_mic_hmac_key": "DDAF42B3C3AC34F3E8F2C882568213E333A91F0A049E79AF52E98669048CA81A",
-    "section_salt": "7897264056B8ED625680B2F5124C30A8"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "1599363CAC85444B80B2BD6F32B44889",
-    "data_elements": [],
-    "encoded_section": "35911000D8624D1EC90B225B457D22AF6D8B4F799004990761D38053D068BBE94F66F9968E14BB793A96B05BA0F4716C0A7814D331B0",
-    "identity_type": "provisioned",
-    "key_seed": "7CF0C36D0E587661503F8F70B8F3A06AEA7B6352E843387F353CA8355EB92ADB",
-    "metadata_key": "A54230D1BAD378D531DB91F517D11568",
-    "nonce": "D25B10F1899F9A9C56648C1E",
-    "section_mic_hmac_key": "C1A07E6C0ECA629D1ABB6CF5D6175F97E97637756561930B256BB6891012A830",
-    "section_salt": "D8624D1EC90B225B457D22AF6D8B4F79"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "3EB28DFBFF8CB955CC44F68B0085F70E",
-    "data_elements": [
-      {
-        "contents": "8511547C881A7F7036FA2009F904BF",
-        "de_type": 42
-      },
-      {
-        "contents": "7C4430D434907C017DACFD336A161E70C10815B6",
-        "de_type": 284
-      },
-      {
-        "contents": "15C1124EF76C2D9BB61C",
-        "de_type": 132
-      },
-      {
-        "contents": "3E3B0BFE93189B50",
-        "de_type": 694
-      }
-    ],
-    "encoded_section": "759110007F2CB662B15FBA6D7C18AB52889B266D9004CB724FEF84FE55FCB1A2B571C77EE17C930A5F6606F40EC38F4241A6B287A8B1EB443F7C013B34B3113D193F884B120F0AA415452FFAAA0B40B78DD80A09858F2068537D2AC8F8DF750509B9D52A4B9B39F10C2CFC42A9B90FC762945BC31CAD",
-    "identity_type": "provisioned",
-    "key_seed": "547C965E115345BADE7FA439398A1618870F0392671DEDE9944FCC693DBE32B4",
-    "metadata_key": "C55B065D5B03C633C7546FFE3043FB8B",
-    "nonce": "888A5C195BCC1327C7DA9E7A",
-    "section_mic_hmac_key": "F672CB3E86B8E87B727CB0814C7AE096C3E7C7EE2398D1581847C26951486BBA",
-    "section_salt": "7F2CB662B15FBA6D7C18AB52889B266D"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "A952EA928E55FAE45B612EFBB2E92897",
-    "data_elements": [
-      {
-        "contents": "A39AD60114B4E470CD6E5B1981F6D94FDF21E386A957B3",
-        "de_type": 11
-      },
-      {
-        "contents": "30309360211C7E94BC8B3692BCE38EE888ECF197C594C3D07D",
-        "de_type": 10
-      },
-      {
-        "contents": "66DF42C0A9CC3385053676451C23276C1CB169D74DDB1C46A6EDE2C89707",
-        "de_type": 610
-      },
-      {
-        "contents": "550E675F587E",
-        "de_type": 554
-      }
-    ],
-    "encoded_section": "939110008F8202EF37CD727B74948F6A5205650390045048FB340FCBE272C4D667BD6691E08084C56D24D168EE7C6B3AD228DF31ADD89B1627C7511ADF1E12882ACECB1C05B49539F332C01F0C2778F039A5727864A5FA46F9030820D06D961123CBDED94746017E6B2224D43BB304D590F12343258BDCF84F691600B58C18025956190BD91D359B1654D5862590962E89FC3541",
-    "identity_type": "provisioned",
-    "key_seed": "D75311C9FD54732B0D1D58E8C13D69582675936BC2D4B948E4DA8B8FD4E4EC19",
-    "metadata_key": "44A8ED043D7B7A04048C04871D04BF28",
-    "nonce": "39A11C1233C3887D0FE2FF95",
-    "section_mic_hmac_key": "0370E5CD2CA4944C5633BDB505F471ED4ABD6F9E0803BD57669BF1A096FBFAF4",
-    "section_salt": "8F8202EF37CD727B74948F6A52056503"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "375CC4495D0E6037B4A269A5A4EA302C",
-    "data_elements": [
-      {
-        "contents": "E51613F10B7423784CB6E3FF2E58",
-        "de_type": 683
-      },
-      {
-        "contents": "ECBD79DDFA8130DCDEF28AD2AF40C6A8DBB16B2972",
-        "de_type": 264
-      },
-      {
-        "contents": "9FD18CDBD2FC1FBAF9B317FF280956",
-        "de_type": 617
-      },
-      {
-        "contents": "8871908BB20E8F",
-        "de_type": 918
-      },
-      {
-        "contents": "DD8DA6F96A3751",
-        "de_type": 721
-      }
-    ],
-    "encoded_section": "84911000C7A3432633031353A946F0684CFCFEBD90012BD99B1A635B8CB01DDBBDB46AFE8742B053F93A65C75293797C523784EAE07488134AE32476E0F698D440C3A0A389DB48B6846E2D42A89584741285C09301225752A1A5C8152E010337BBA3ECB3C15D6893C2B2CEAB1FB452D50E5990EA1C9B007C08642769AF82551F3FFA44040B",
-    "identity_type": "private",
-    "key_seed": "D948351CA86C11F94DFA31FC2B86C7EBD32251E770E243B287D5C03630A7A4B4",
-    "metadata_key": "0F37FFFBB8B3BF85BE6B4A5D7AE60C37",
-    "nonce": "5A8953196DB0C6B086052AC9",
-    "section_mic_hmac_key": "6A6CE54F7E0FCB42716786C56294DB3FF4839464EB6D6DDE3037015BF42591CD",
-    "section_salt": "C7A3432633031353A946F0684CFCFEBD"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "2276BD761EED06699E8A4C1B005226C8",
-    "data_elements": [
-      {
-        "contents": "09D7C7411219259A530E4207",
-        "de_type": 202
-      },
-      {
-        "contents": "E6206D70CB69117E5B0CFC6484E5A9F6E6",
-        "de_type": 335
-      },
-      {
-        "contents": "124F723ABCB0F46AC2D9904E49F29C72F6A37A6F1C",
-        "de_type": 173
-      },
-      {
-        "contents": "F03BE2BA870E9DDDE0A20C37F7C3C513418FC8595CC2D9",
-        "de_type": 162
-      }
-    ],
-    "encoded_section": "8A9110006FD81C4F4977A61EA77802E4AE331B1B90044590EF69F794BECCA20B04E1A5794A8FE0BE6B353BAE140B8A37594A011AA936FCD209E03D522D1C147E321147E9B33735F2CA413B5C945091EC1AB92537B781F26714EAB879BF33E801BE58488C11306DD259B63B1F05FC146871978B17CACF8AEF88D021782A6DB7112D4C930FCA66626E69140B",
-    "identity_type": "provisioned",
-    "key_seed": "B3E1C7E5933E243B2457C3493F6C62D88640DF85591E1A370EBB5ED1A7E87DDE",
-    "metadata_key": "584C3B455D31A6EB73CA6286A6D1D677",
-    "nonce": "7C59A6FBC27521B60FDE3D85",
-    "section_mic_hmac_key": "4FC402F911776BD4BA7E1B934A76823AAEB91AEAE70F229149737AD2446C8721",
-    "section_salt": "6FD81C4F4977A61EA77802E4AE331B1B"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "ABD1E3B92ECCC94887008F9712C8B92E",
-    "data_elements": [
-      {
-        "contents": "53AA992BF504934429F0D55BCA57E7E4DD02BF1C51",
-        "de_type": 77
-      },
-      {
-        "contents": "37",
-        "de_type": 41
-      }
-    ],
-    "encoded_section": "4F911000117F8B155FCF545D9DDE2E2404BF08579001181FAA51DA6430295D27D32A8894FC80977F9FBB2260C5F335DF5E2985B1E9DF677F8DF40E3DD10180E31FD4A1E640AA81AD2D59ED29C73E0DD9",
-    "identity_type": "private",
-    "key_seed": "BAE413A1CB297D1B55956EF2E3D47463B15721D83113EB265CDE9742DE57D1E2",
-    "metadata_key": "E118EB2F2448E831DCB2F01211D4E299",
-    "nonce": "641B644EC4576653B4ECE6BE",
-    "section_mic_hmac_key": "7001ACCF2A6FE829E333351CF9500F0EC9F144AC2432E2D4E65BA205B5A2A083",
-    "section_salt": "117F8B155FCF545D9DDE2E2404BF0857"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "0B886C679F8368F71B69095633FD827A",
-    "data_elements": [
-      {
-        "contents": "C57A03111A582D033BA2F0BBF50F0698BC008461C715",
-        "de_type": 648
-      },
-      {
-        "contents": "0C95F385C694E2BC55C219B0086BA9F8B64C0633",
-        "de_type": 415
-      },
-      {
-        "contents": "C9826C977F5D51",
-        "de_type": 246
-      },
-      {
-        "contents": "B88067D984",
-        "de_type": 989
-      },
-      {
-        "contents": "98C5E507657F0B36CAF5D0EB02A5B30F1087",
-        "de_type": 663
-      }
-    ],
-    "encoded_section": "8C911000AD6BDCEEB60CC9801003D4F71FB8AF0F90049916A86D88CC6C404111A7D9075FFC0C699303B2BB1CDC7492495C139069B609E02C45F3F2F5D1F11C5CC45EAA610AE5F9A3085D638A8D774E060572A9E8E276FDC2B9EAFFAF23469097068D9F2AA77BCB5F548EF636B8BEF3C02495DB690858EA8A13C78501E64C54D05155EA488719C539D7B9060048",
-    "identity_type": "provisioned",
-    "key_seed": "B323DB96EBC1CBB190E305EBE290EF7E600D3D3E7D23688DC2C8C23B4EFC7A4B",
-    "metadata_key": "6436A7FA7D38F566B85179BFF2E4DA68",
-    "nonce": "43B0153AB9B6FFB0DA670B28",
-    "section_mic_hmac_key": "88BBCFCEB5CFAB8C69C5C978C8C21758ADE72E557C108CDF780367F6456C85B4",
-    "section_salt": "AD6BDCEEB60CC9801003D4F71FB8AF0F"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "E4E6282E18B2700912371FD299648904",
-    "data_elements": [
-      {
-        "contents": "6E",
-        "de_type": 875
-      },
-      {
-        "contents": "3E7F165B8725499C73A790",
-        "de_type": 547
-      }
-    ],
-    "encoded_section": "4791100001BF56D7253FE9F2388222BE2B414EB49004A2C3ACCC4EEFD9949E6470223F1085EEF12CA22DBF4E5A9DA978E44AF3E93F6142BE9270F59E188FEF58BBAC3BDCE8413351",
-    "identity_type": "provisioned",
-    "key_seed": "CFF5A7B58AD3CF1BF200E0874E1E404E0B8EC0BCE8812F1FFD4537E2330BF4F2",
-    "metadata_key": "C048921E6EE411AD4698E29A54BC6B4D",
-    "nonce": "8D23F89851021F5141478E33",
-    "section_mic_hmac_key": "1988C5772B660124148F05733A326F0F0C29DC301F2989294312F41C56292A59",
-    "section_salt": "01BF56D7253FE9F2388222BE2B414EB4"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "78DECA4E98143AB700746AACAE4CC14E",
-    "data_elements": [
-      {
-        "contents": "F42C1F5B9E2060A6D4CDA7A2963F44A524BFFD5F0F2D69F3322DCF4B93E3",
-        "de_type": 359
-      }
-    ],
-    "encoded_section": "56911000FE30CCFEF9FADE459409BCA8286268B79002DC0290C23BD3C7F0DCD8443EABD050919255A3755D0F5EBCBA7B540DA11DF0E844207B803710FBE088BE18847F5406B7711DBC583711B7930508B0B06F801CA9AA",
-    "identity_type": "trusted",
-    "key_seed": "F44873C8E1AC0DC8D63BC7CE805E543BA8A73CB83DF807B608246E14CFCCE5D7",
-    "metadata_key": "D0CB13BBC77450B18573A000E0E4B324",
-    "nonce": "D97378F4DF5B4D5249845D4E",
-    "section_mic_hmac_key": "11A63498ACF2C0D60AFEE3AD00117129582FFD6D50D3CA4A21CD3330BAF0B2C0",
-    "section_salt": "FE30CCFEF9FADE459409BCA8286268B7"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "99CA428E9B70F3A4F34CCE2EA3FFD1C6",
-    "data_elements": [
-      {
-        "contents": "95651D83B5B463A0B4D7C3BF97C1CDFD9A2853DD216D",
-        "de_type": 842
-      },
-      {
-        "contents": "B1E32F",
-        "de_type": 633
-      },
-      {
-        "contents": "B44550D17D11C579DDE453A78F8E68",
-        "de_type": 764
-      }
-    ],
-    "encoded_section": "6691100074678AA45A5827136A450437EE29F5E39004B94F1326A3CE7BDCA34AC253366FE37E6849758193586827933D9E096053DC06102448287A436069815904A0F3895C825ED3538CB827DF1B153E833F11DD926D4E5A45CD20EA0FE3FDB21A587C6C77638B",
-    "identity_type": "provisioned",
-    "key_seed": "71A3A2B3B223F84BE6E8595208384B34D0379E898137A3B5DC4F18C71502CACF",
-    "metadata_key": "8998FDB14463108B5A5F66FE289D13BF",
-    "nonce": "AFE96D0E4FF65C1734F9F99B",
-    "section_mic_hmac_key": "2222BA7F416FFEB9B14B07D5D2522EDDF0D2E82CB116D23699566520035BC6E7",
-    "section_salt": "74678AA45A5827136A450437EE29F5E3"
-  },
-  {
-    "adv_header_byte": "20",
-    "aes_key": "3C5965394479133B2B0CDA3474935C12",
-    "data_elements": [
-      {
-        "contents": "4C5DE6A43F855833418B23DE6E4C0AB9B8A6D1C581F15A683D",
-        "de_type": 831
-      },
-      {
-        "contents": "1AE2D41BF9064E056C22712044707CEA3EFAF26964D83E684D33CE",
-        "de_type": 355
-      }
-    ],
-    "encoded_section": "6F911000BE86E394E730BAE02E2A22E8B54CF1A4900408EB6891543847E08002B13233D680C89B22F57320B4890E87533A2B4DFC30021206330351F378D93A7CE5051655B1EC84F8F4F68DBB327EDD4E0DA4948323F1B931733AA4D28F06238FF3622178015BB5C709094AC23A3E448D",
-    "identity_type": "provisioned",
-    "key_seed": "CFBB3B4D111CDF95E4AA9642D5D6C1611B8522D6F76C7B1F23C09DCE58133FE2",
-    "metadata_key": "3BFA93FB827970E4CD401DD9903ABF19",
-    "nonce": "2CFBAEDFFF07C80511FE4B1B",
-    "section_mic_hmac_key": "16B7AD3BB2B10A5B5D64ACEB9733BEDC6D0FD351E5D12AB36FF5FB283DEDF3F4",
-    "section_salt": "BE86E394E730BAE02E2A22E8B54CF1A4"
-  }
-]
\ No newline at end of file
diff --git a/nearby/presence/np_adv/resources/test/mic-extended-salt-encrypted-test-vectors.json b/nearby/presence/np_adv/resources/test/mic-extended-salt-encrypted-test-vectors.json
new file mode 100644
index 0000000..52ff336
--- /dev/null
+++ b/nearby/presence/np_adv/resources/test/mic-extended-salt-encrypted-test-vectors.json
@@ -0,0 +1,2213 @@
+[
+  {
+    "adv_header_byte": "20",
+    "aes_key": "93FD082896AFAD5C02272FCAC7A4B19B",
+    "data_elements": [
+      {
+        "contents": "F111383178463B",
+        "de_type": 965
+      },
+      {
+        "contents": "77A7CA333048119F23A460ABA89494CF7075CB3CC00E",
+        "de_type": 148
+      },
+      {
+        "contents": "E7B0F2",
+        "de_type": 596
+      },
+      {
+        "contents": "7B25D72AF20B8B53495E79E004108C7651C5F9CF07BDCD784874",
+        "de_type": 969
+      },
+      {
+        "contents": "938EF51180242B59D35CAD0DA09ED9FAA4D809BC",
+        "de_type": 47
+      }
+    ],
+    "encoded_section": "02E628D03BE58B76606FD929A1D1F814D1163B0AE8EFD98D12D07B1F0D8D0203296CC45F4BB29B1B55859205A94440A19A7D13551244694A00388C0DF6B25405C6D2F6E15D8D1E4383D8D6A28BE510CB8941A7E44040B656C77670B9DE3612BD51E7FC7200C5CA9D4014BD784335CC4C377156D3E8B8750CDEAF086D82715EFCDB87F02C7F96D8EAF7E011169F2A",
+    "identity_token": "92220A2275B7CB8D5B8F7FDEE137C385",
+    "key_seed": "74BD3E08D69FBF2DC5431C5D2CC66BF83D602B54FA10334916F9457DD7F309DA",
+    "nonce": "C2B4EB521EA983D74D009C90",
+    "section_mic_hmac_key": "D1A28568BDFFFFB2D23AD8FAA174F72993AC0B5B23ED27CB27E7D99760AE368B",
+    "section_salt": "E628D03BE58B76606FD929A1D1F814D1"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "D371D308EB2FE0705ACBECFA55123A55",
+    "data_elements": [],
+    "encoded_section": "02074BA9EC8D17A3258489F865AA8B5AF679E71F22BA9938EDB211EA8163236EA810390E1A6CAE374F5446C039F9A031CE38",
+    "identity_token": "1F01AB90BD3A83FE37E74A301D415475",
+    "key_seed": "1104D617C7F480B1B5875CE6711FF91BC76FB208F8CD7D9F9A08AA9DB0FBC5A3",
+    "nonce": "4FEC96E42BE9C8CD180003A3",
+    "section_mic_hmac_key": "A5E871DC448A3B0EFD26DFEBA9EED9FC74EE02481C6704FE906D7A82E2BBCD29",
+    "section_salt": "074BA9EC8D17A3258489F865AA8B5AF6"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "97F371754AFE9C6F5438F467D077CEA8",
+    "data_elements": [],
+    "encoded_section": "02973AC46A20846531A220E935400EDDFEC74BA44660727C7B33874948C21A903010ED5DE380C975B9216B743277D73628A8",
+    "identity_token": "3ECB6505ECA3F0644780E4F848B8C914",
+    "key_seed": "777BA0008B38A5AF3F3CE15B1DB83B2612E312739447D525885DC0123FD347B5",
+    "nonce": "A848DAB9F824A480E1ACC319",
+    "section_mic_hmac_key": "FBAF9AF0C9E256671A99C74F3FDB9C17803ED334F5BB24C72FA056E05E3FFF12",
+    "section_salt": "973AC46A20846531A220E935400EDDFE"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "C401C5B15A57D0A26CEE901764560E0E",
+    "data_elements": [
+      {
+        "contents": "F83D3AAF1566156FAF5128917CAD10CE348BCF",
+        "de_type": 563
+      },
+      {
+        "contents": "FB04C7C54945BE1F575F",
+        "de_type": 945
+      }
+    ],
+    "encoded_section": "02ED1132C6CB2146F95306E0095791FB969C04AD11D18479F8A868273B5A3C3E1833592069C4077690D4A48D8F5F55C27FF3565B3D859F44E6D4DC439A38BC6063D55F20EA3494469131156AC1568BA18A8AE6322B",
+    "identity_token": "932D597EA1265715083C01111B233551",
+    "key_seed": "5B1F90B019BC982CA431E8CE303F506E192913E9E0D9E7AF75936F1A8336E29E",
+    "nonce": "CE5FAF170ABFEE4B7D446D23",
+    "section_mic_hmac_key": "A876F7A6153B0A4E0C00E367D2A62C2E69B8B172AACD2F88F5AEA1CE1992BB32",
+    "section_salt": "ED1132C6CB2146F95306E0095791FB96"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "9CBAD2E4548CBCCD00B6E0FB7FBF9473",
+    "data_elements": [
+      {
+        "contents": "7A306B5925D15D6B43B7",
+        "de_type": 149
+      },
+      {
+        "contents": "5912AE51A1D62035C323468AF0198079E1A7110FDBA42D",
+        "de_type": 664
+      },
+      {
+        "contents": "091D6CE6C774BDF76F52CF04688503B8496460F3E90D0880",
+        "de_type": 305
+      },
+      {
+        "contents": "248EC62B07E7904634AD309D8238B1514FCEF2D86601BB2ACA027F14",
+        "de_type": 507
+      }
+    ],
+    "encoded_section": "026A74D621344D87428CEE5060FFDB570C73D57CE4A2CFF3EB2C14839E619A1D967170CAFCA1BAF48A2E45501A6453450187A1D4854E4B238A56DB9E26F231288701D10E43D8468A1E661AB447C32E366315B6BC1073EE143D7C02458C56030516A3DB91E367C941689CAFD6FA9252FF37B88EE0ED4D99EB9313B7BE72F5931ED45525D49C1E068C96D133078E33B466EECEC2",
+    "identity_token": "19263E5F61578937D8D7EFC85325267B",
+    "key_seed": "8578CDF5097D9F1C253B4DDA8471A03AF7F26A01FAE138D2F3D350FBB752FF24",
+    "nonce": "0C6C9E6B32B490B080285EDB",
+    "section_mic_hmac_key": "953DBE0C875F66FFFA680AEA984DFF74C906D326B58B19A804F20BD104B236A3",
+    "section_salt": "6A74D621344D87428CEE5060FFDB570C"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "DCE5AF2A5A7AB0A6F2308EF445084D6A",
+    "data_elements": [
+      {
+        "contents": "44986ED23F155AC13CB849AD9C4FD3",
+        "de_type": 520
+      },
+      {
+        "contents": "9AE30C51E00E42BB",
+        "de_type": 294
+      },
+      {
+        "contents": "",
+        "de_type": 192
+      }
+    ],
+    "encoded_section": "02384716FD70BCC1D2B3572C678ACDCB1CCB3ED7DAD80CEB08CD5C6E324C5BF51830123D1A83775462CC0BD667255BD88D21D8A972AF982CFCCB54DE20E1F38F8A7F6E2E525B13AC0AA5282ECB34FD2AA8C2",
+    "identity_token": "1831C66E762DD247D3D0A3F7CD278B6D",
+    "key_seed": "2168D48C66B609FA91D4A98D35F0941AB227F1D05CB42C81E6C3773DBCCE8556",
+    "nonce": "6BE8AB33FEA8DFCAF84ECC84",
+    "section_mic_hmac_key": "A62AAB51DDFA839AF5FA651C807D7148269148D0926E1A86D108796B98706601",
+    "section_salt": "384716FD70BCC1D2B3572C678ACDCB1C"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "A220AB81C5F6D95C89F75244513A865C",
+    "data_elements": [
+      {
+        "contents": "6045FC019235",
+        "de_type": 587
+      },
+      {
+        "contents": "F46063F443E58DD9A62CF5935E009EB1F4DB4021072AA8662EA3667D",
+        "de_type": 720
+      },
+      {
+        "contents": "335D2813B9BB0B3D302B",
+        "de_type": 978
+      },
+      {
+        "contents": "CBAD44D6954386F14343090074EF8B4BF900F75B810B",
+        "de_type": 209
+      },
+      {
+        "contents": "A5",
+        "de_type": 818
+      }
+    ],
+    "encoded_section": "025676256DEEE42A0A9FB667E01DE178058CA5C182B845F41E66D5A78E96C550B1620460461E74C4B4FAA6277A598899798E8E07B73E6B99BBBD4F22767E7B9C751B4AE7FD09529BFC16BFF03E02662E3D2F6AAFB8429B233260311E995B59CB3757479A4F5B1F167E0E461BA882F9F1A95D8B7B5A65014DA76C3796BFAB028974E9F487",
+    "identity_token": "E98107D0B6209C2F99E458FF9DDA5EDD",
+    "key_seed": "67397E84E19D943457D649FC1C85177CA2283A285DEC02F77DAE3223822F10C6",
+    "nonce": "84C350AAA3DBEFE6C4AEE93D",
+    "section_mic_hmac_key": "CAC3BB4657D8FE9840CE0B2339AE0F7416D583748B89D950BDE6DECA04EC5C5C",
+    "section_salt": "5676256DEEE42A0A9FB667E01DE17805"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "8183EC3E35044B898637AC452F1344F8",
+    "data_elements": [],
+    "encoded_section": "026167137CA22AF2130EA98F1046B4063D4D2353C7595F0631AC75DC9B450BEFEA106A6C243686C795EE3D7B68717556E523",
+    "identity_token": "E0855E9D123AD57CD4BAA1619A33CB8F",
+    "key_seed": "1572B2064990B9BA7EA8FD487BE6580BED3AD5332CA54605D801D82C25813372",
+    "nonce": "1AFDA6FFD7B3CF5FC5C2844E",
+    "section_mic_hmac_key": "E6F8DF148FC91C004791D9CB5E6881CA536E14CB366F6B2D73C1C3995161A74D",
+    "section_salt": "6167137CA22AF2130EA98F1046B4063D"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "083CFF217DC4F7DFFA8B3989A602A800",
+    "data_elements": [
+      {
+        "contents": "F01E2E1B19F7F410819FD81F2B22F7065E3164E1A54ACFF05B73D011",
+        "de_type": 886
+      },
+      {
+        "contents": "87A24D8B5990862B291D9CDF44A7CF15",
+        "de_type": 363
+      },
+      {
+        "contents": "",
+        "de_type": 230
+      }
+    ],
+    "encoded_section": "026DC137DC284DA96BE876644C985CFB021DFDAB61844B115F94E05FA2D4EAD52345AA96C988FB3376D05D29BDA4D027AAFE41CF5E10625A61B12A000FCA96AD5D5BAE78AA3A8C186DCF5729744F572FF4370291D35642D5088995AD034E28CC61D05876729AB1",
+    "identity_token": "7310CA7BB155529509A89AC74042075E",
+    "key_seed": "5DC834BD1930C71E749AEEE8E3518EBC09C9B860B6F6372D58A1BD141E89B08F",
+    "nonce": "4410414C0013373CBE93D03E",
+    "section_mic_hmac_key": "B9B9B858E9859BA09D8A1F17B0E6AC0C5F6286C708C95552A464924B629C7C10",
+    "section_salt": "6DC137DC284DA96BE876644C985CFB02"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "B44C62C6931281637F75DE6F1D3F39B1",
+    "data_elements": [],
+    "encoded_section": "02048517CD42EC7AF6C9FB66E2F3078ACFA38819E6A4E0B9BD8859750BADB4232910C0BE5A04C215798BC885D7781C579B72",
+    "identity_token": "D9EB6B7007B30C0EF6CF01A7910D8B11",
+    "key_seed": "15F39B4B7DA56E28820DE711A002EFE525A5E32605234508F57E31A12C520938",
+    "nonce": "EEABEA7D9A551E134F3BB00E",
+    "section_mic_hmac_key": "0C9AFE67BD257E145EDC49C851378382C4899F18B61ED1A9D1488D63D9A7AE1B",
+    "section_salt": "048517CD42EC7AF6C9FB66E2F3078ACF"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "187638BAC5FEA85287840A933BF6BC8F",
+    "data_elements": [
+      {
+        "contents": "15CAF364A09A674E23B3724F87",
+        "de_type": 655
+      },
+      {
+        "contents": "B44AAA22887D6901DAB2B15D2821DA6CAF",
+        "de_type": 663
+      },
+      {
+        "contents": "3D75D65255A28AFA0FFAC86128AC",
+        "de_type": 568
+      },
+      {
+        "contents": "8F87B0032134C16C3A5C55E944216C6CBA1FCA9699",
+        "de_type": 919
+      }
+    ],
+    "encoded_section": "021131C64A1BDAC5CD00E80724986AB4FFD8B57A83029720FCBDD1CB945FA6A0E95D43C3FEC6573FC13757D4EDF3D85AD806A456778D52E013B1B418BE71E73CC08857EEF334BC2DBC1AE87C04D8D5FC35874B09964455EF5B444D0569F53D2EA3F442011BB799A6646E266EEDB93BB2295E58C17BCE62C63DF2A988773A94",
+    "identity_token": "33C178C3119530767A131BA20E39C282",
+    "key_seed": "B1D18E5E497268A55170AE018FE33B3085490E7FA03A04AF02A6853BAA9C700F",
+    "nonce": "19A21AE5D8370686F9D64523",
+    "section_mic_hmac_key": "F751860DCF60C03436AA4782865E8C89C33C1851352793CE41E3E08B5585866F",
+    "section_salt": "1131C64A1BDAC5CD00E80724986AB4FF"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "BA6A05D814D7478B1D442A0C6E5ACBD3",
+    "data_elements": [
+      {
+        "contents": "7E5AF9A1D0129006",
+        "de_type": 272
+      }
+    ],
+    "encoded_section": "0284E383F03BCFBA802956AC1D331D49148DBDAE82CC80EF8E854495252475C3491BC6A881642FDDA6C4B8403F32D4E3EBC65F5702BA53D740EA6A01C2",
+    "identity_token": "2AD4A3ED2DB4D967EAC7F080F4CF40C8",
+    "key_seed": "19F9E4A47586993C7A276A929D00A0FFC15FE2F4020CF6B05B04C4F4D0C6DD1A",
+    "nonce": "F807F49730187CB3ADE6199D",
+    "section_mic_hmac_key": "1576D69950F0F1E079F379F3ADB914775C4AD5AD22CD61561F8B9372225E8019",
+    "section_salt": "84E383F03BCFBA802956AC1D331D4914"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "EB5575D5CF3527ECA045F3979A56A309",
+    "data_elements": [
+      {
+        "contents": "35D7835CAC",
+        "de_type": 205
+      },
+      {
+        "contents": "7FE4C599A437353892D3C213AC6A1AC604F4F9AF",
+        "de_type": 582
+      }
+    ],
+    "encoded_section": "02ED47A961048AF2432C532A68C85D8774E661489D120680C88F13AAEEBB7FB3492F43D8628F8F4F0908A22FFA780DDD956F89A5050DBB98E4F52C25FF30349A76D9B9E39D7FEABA2E63CBAA742B683B65",
+    "identity_token": "82A145333F4DEE9F069AADF41C54F072",
+    "key_seed": "DE758EEEE4750E04730AA94B4F238B6CD535F551C2BA67446A254314D600ED28",
+    "nonce": "4241309102A36EEB9CECAD67",
+    "section_mic_hmac_key": "4C1043B09127BBD4807B84CB8095EB362229FD36D9392553B988807B8EA386D0",
+    "section_salt": "ED47A961048AF2432C532A68C85D8774"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "9BD48ADA5F488E055CC936AE39807325",
+    "data_elements": [
+      {
+        "contents": "342C2F6E6DC0F507E670A49386",
+        "de_type": 389
+      }
+    ],
+    "encoded_section": "0288E7C4BD0D3B41077F9D17F9CDFAD09AC4BB564892C8E1CAFC76393E1BCBFF0720893D1D78C7F60C4A4379DA5631EB6410BBF5B9D2E695C95895F1386B3533A72F",
+    "identity_token": "98AC76B171BBA2D6F74D2EB070CCB2A5",
+    "key_seed": "289A2476986D21D1D4461D43370BFB00BAA75DB8129366D1B287E1DF2346D686",
+    "nonce": "5AA5D7324173A3A8B25CFD2E",
+    "section_mic_hmac_key": "613B7BEC61CA3F4917327E9F57265497A2DA3FB4A8D1A2E64F450E70BF4C2A55",
+    "section_salt": "88E7C4BD0D3B41077F9D17F9CDFAD09A"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "D6964D2F633FDF7ED603E51BDDC2257E",
+    "data_elements": [
+      {
+        "contents": "AC844B564093FA92B8CA12B19613D2F77F4A656F8319",
+        "de_type": 600
+      }
+    ],
+    "encoded_section": "025588191AB27D12C53FBB0AC6514C35B6A213087FD19806FD6344B4BB336E49962992FB90F69A43E3D3281CC6E20822037F382FB9D98F25653F9FA6984E51DEBAEA3061605A4614434689",
+    "identity_token": "A0A4A57454A31A1B4CE36D757ECD18AA",
+    "key_seed": "AB60BD9D7B2DBE281E8A6EB916B2042923046CF68F36CDA67F27E117AD4A7CC2",
+    "nonce": "82E43E99AD73B19CFD430B95",
+    "section_mic_hmac_key": "8F4D7D6A549F07659A3FC656056C17916AD3A4ADAC5DA504388BFEF875990157",
+    "section_salt": "5588191AB27D12C53FBB0AC6514C35B6"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "4B9221D8B63D26A9711B130E1C0535EE",
+    "data_elements": [
+      {
+        "contents": "ED6EBC",
+        "de_type": 591
+      },
+      {
+        "contents": "1D8DDBD8DF0C950711662DA284BC23FA6C6FDF479636",
+        "de_type": 29
+      },
+      {
+        "contents": "7AD06A9E86A0A2DF",
+        "de_type": 596
+      }
+    ],
+    "encoded_section": "027EF3DDC32AD8B67DEB708CB96FACF7E87C2ED0A5F105F30E74CC01BA5995023B39908CD7C3A28D416AD43B60A822D3A6781CEA5EAD3A26EEE188FEDBCC4B86207D626E3A7C5177E7EBB46D720404463C45A0832B3B146BF9E842",
+    "identity_token": "46470C0BA32542A5ED33D66AE1481EE6",
+    "key_seed": "7397B457C7BACC338D5C6134346FAF2D3164EE0E53FB48C18EAB31257483E83C",
+    "nonce": "BF76260300391F69E456FE0F",
+    "section_mic_hmac_key": "7056AD67950509C88DFEECEBE92D739F5C702C566F1854EE785EAC1509C26815",
+    "section_salt": "7EF3DDC32AD8B67DEB708CB96FACF7E8"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "1E806EFC9EDD141577721BB5E61CA53F",
+    "data_elements": [
+      {
+        "contents": "57A7035EA380C98F071876E21F4CEAEA67E964A732568F5D10AF",
+        "de_type": 518
+      },
+      {
+        "contents": "913B76C6EA7BE3C1FAF1C2E1CD4F929F4F56565BCC1113674C17",
+        "de_type": 898
+      },
+      {
+        "contents": "",
+        "de_type": 415
+      },
+      {
+        "contents": "5D48E33377BFA3527720447FDC7F8DA97DFC44DC4A",
+        "de_type": 588
+      },
+      {
+        "contents": "A3",
+        "de_type": 908
+      }
+    ],
+    "encoded_section": "026567A1F3D63165D9D7A854C9642CE61B9DE8B6E101BE92EC9ED692E04BF86E866946345059A1F4F9C54A625AC3EC77B55C37C774CD11AF71385AFB813F8DFECDB95CA83929CB8309AE5206F125E22E25368DA9580EAAA50F61EAAF37FA6A61D44CA946D3BE28D9E298858E09DF3D50CEF793CA14D94A733C1FE663A8ED047D4718336108C1154EBCE52E",
+    "identity_token": "DE24F284089324DCB3AF76801C2F94B6",
+    "key_seed": "6555B34DD6A332E6F727381BCE1A2B9FBDEC1B5367367BBCFCB5071BCF8178DF",
+    "nonce": "F423CF36EF58853645712DF1",
+    "section_mic_hmac_key": "54C323CCC04E240721D7F77C1205F216D251C79E41FCB3695EAB28C6C5423C40",
+    "section_salt": "6567A1F3D63165D9D7A854C9642CE61B"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "77F61511E7F92A37D2A114DD4CC4889F",
+    "data_elements": [
+      {
+        "contents": "51F97F",
+        "de_type": 22
+      },
+      {
+        "contents": "EB58DF0C96A41AA69329CC97D11242F4A3850D89FA82376C83",
+        "de_type": 110
+      },
+      {
+        "contents": "BDFDCF83C918D94DDE25E6A13E0586CD83EFC76F5C43AE46CFFE",
+        "de_type": 674
+      },
+      {
+        "contents": "F2C0052877838299",
+        "de_type": 273
+      }
+    ],
+    "encoded_section": "0282028A9CDE5CFB8B7B85C55034433DD7A9C6DC3CEEC3F54854F91708D8DAD5B458C396ADD9D43DF02B09513E3E428A0B91BC838C3D10B5D755ACFF0BA217A77117A96C3E2D5849F2861E7DC257A3450D740424901564F7F25EA0E7E239E628C8E22381D9B0FA86896E0A562DDCB26E1E6C4EB2DB7D4559CEA1",
+    "identity_token": "15A7903360B4430A13965FC72FA2B9D2",
+    "key_seed": "E06BFE99B917D1C1B38AB13D3F6A2163AF91507531E86E5F6F32D7116D94AA03",
+    "nonce": "F1728A657AD70C28875CBC1E",
+    "section_mic_hmac_key": "3AEAC66D3852FEB86D7D1FCBEA0A9D6EA47EFE74BF6F2CB6E861BB1ABE8E3657",
+    "section_salt": "82028A9CDE5CFB8B7B85C55034433DD7"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "C606686684F622AEC70CAA4F201F2EF3",
+    "data_elements": [
+      {
+        "contents": "4FA0D0",
+        "de_type": 599
+      },
+      {
+        "contents": "B3E804B287672E412D53B1183CEC5D",
+        "de_type": 174
+      },
+      {
+        "contents": "526BD3460157EBE8D364D73C30419689F8E5EA4A47",
+        "de_type": 461
+      },
+      {
+        "contents": "C378C50F",
+        "de_type": 800
+      }
+    ],
+    "encoded_section": "0222C5A6BC119ACEB0EC9EDCA6D57639B767DE04C14B5F0B4DEE8B49E4DE3AC7CD47CDEFFB4ECFDF012DED1CB340D2E3D98D7DE9970BB9F48ADC6998B8A52B1CC674048CB3C58752BAFD16B73DC21D36973DF5F18BA6027AD6060AB0EB78862BA8D7BED26968C3629F",
+    "identity_token": "59BA27AA8A76A50F12A5D2BC8EDA7635",
+    "key_seed": "CC49C0BC358FFB2BF90A62F446709CA6A6FD1CE686EB7D4EB37FF620AB5866E1",
+    "nonce": "D222455382EF2556A27EF3B3",
+    "section_mic_hmac_key": "507CDE4A24AAAD3B43BE6AAAAE73CA5C5F59D011AD220F1272E7FF4D43747F22",
+    "section_salt": "22C5A6BC119ACEB0EC9EDCA6D57639B7"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "A33086631E13B0A0226E32EF4D4CCDBD",
+    "data_elements": [],
+    "encoded_section": "0228770D81B47DD0E4F188546B893534900F41411BF870D5E9458D457698D1F74710D4A0F4340600E0E9285E5E6F3351E3CA",
+    "identity_token": "92A3CA4B2F9F59AB64EFBBFE25121C7E",
+    "key_seed": "D6AB21137556AF94989CF92E53261B867163C16147313EC5275B8E1DD5073BE1",
+    "nonce": "59480DC604E5C6AA30BFEF23",
+    "section_mic_hmac_key": "9B12B9904F7ACF8E22828B994212CABE3FC8B85F6121D1E5D47951473A0379DD",
+    "section_salt": "28770D81B47DD0E4F188546B89353490"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "05099C0809FFCE0781D65DB0E2F6B265",
+    "data_elements": [
+      {
+        "contents": "3B",
+        "de_type": 789
+      },
+      {
+        "contents": "2626C80C748B72F0A32FC66788960604A3FAED3070AB9114B8E358106CBC",
+        "de_type": 671
+      },
+      {
+        "contents": "09F36841",
+        "de_type": 406
+      },
+      {
+        "contents": "",
+        "de_type": 145
+      },
+      {
+        "contents": "BCBA4F2899DE",
+        "de_type": 446
+      }
+    ],
+    "encoded_section": "0227A3A0FE2CD4B612A4DD0355C51E1C2D53FA09EAB78824316A73D15FC3238396482A461EEBF605D7B33293B3D196BF25B1667EA66BB514A7F7EF27648FB8048B94FA70B2D4EA36BCD86D913ACA072E6D89CF3F5490550820D362E1A8B395B539B466D74A9592AA8ECE",
+    "identity_token": "C76668A2A2FA840BC9C89353B8540B43",
+    "key_seed": "162E48714870CDB8C0BE54F698A9010FFBC9532C15C8C2DD130D579AB4AE4512",
+    "nonce": "08860371E5C35E5F8B076FB5",
+    "section_mic_hmac_key": "DF7611BF559EA9C2D8A0A5E7032D4813971952E0A8701C4F55652939CA1A47BF",
+    "section_salt": "27A3A0FE2CD4B612A4DD0355C51E1C2D"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "B5B046CFE2E170E4E29529FC53380945",
+    "data_elements": [],
+    "encoded_section": "0209539A273A8888A0158CB6D08FE4CC1C0839E0605B3EAC31AC2855629A66D6F2106532ECC714BF797E4A08D85E49AE31DC",
+    "identity_token": "A9BF53E9D87F9E63328C211778CEB74B",
+    "key_seed": "AEF3279A8EEC857AB241D4B6678BAC2CC4EAA9CABF2A4C229E9FE17C44D725E8",
+    "nonce": "025E4CF04B1E5B5A77BFCA51",
+    "section_mic_hmac_key": "967C2FBC5F186228F868E5BEA0482273B1686A3411687FE6123FA533BB4D7FC4",
+    "section_salt": "09539A273A8888A0158CB6D08FE4CC1C"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "5126EA9102AD6B5ED537A4D06C62A735",
+    "data_elements": [],
+    "encoded_section": "02FA35178CEF220907A4C1C1EF1AB507C7D294D3B20708B7D18524BF185DA511A510052AA4B0C8474901B9B7E8D7B64AC9E7",
+    "identity_token": "027C381EF363ED32EA4CECA28DAD7FA8",
+    "key_seed": "6C43637A9F73217C7EC4D4754A0B292230B96368E7D4A70FD3C42B3F42E11685",
+    "nonce": "3A6CD0CDD1DF482C7A7B44D7",
+    "section_mic_hmac_key": "E2F8A00B2F7AC93D7BA123A5855124052A4077FE7CC331DB260AB2D284A87E1A",
+    "section_salt": "FA35178CEF220907A4C1C1EF1AB507C7"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "6451E8C836D286C2C658118FF5E0F808",
+    "data_elements": [
+      {
+        "contents": "7E07F945516A13D0910FBBB4C05258A4B21405268DF1DD3DA8EF",
+        "de_type": 175
+      },
+      {
+        "contents": "86D01ACD89762B2F",
+        "de_type": 691
+      },
+      {
+        "contents": "A0151F3798A7E3E871",
+        "de_type": 81
+      }
+    ],
+    "encoded_section": "0278DBC03386503100B99EEDFDAA3A54AA59606C74808EA12DF14116C0AAF2551143D8F46AFA3CDFE853CDB017AAA6AFDACBD1B5FD0088A709C4B9F007DD53269BBF736DFF095E0C7AD6087B9B8947B5F6D22C5ABC75278BAF2ABDD8D326B3D07C96C370F1",
+    "identity_token": "5A1729E7B2B2193C67112CD8DEC59637",
+    "key_seed": "82158E1C98CAE04C9FF662087015A804F181B25565FC29BB0CCEB32AD29AF8A6",
+    "nonce": "1C4B30EFE7A7EE25837487DD",
+    "section_mic_hmac_key": "3A3FEBA1F8EC810C8ADA1EB14859C5CC3B1A391B0ABEF72BAB65DC1EF1AD7E43",
+    "section_salt": "78DBC03386503100B99EEDFDAA3A54AA"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "37CED522A58075CD5910D4AD5C755AFF",
+    "data_elements": [],
+    "encoded_section": "029134AAA791A8F2F363752DE40D98CAD079A38F6AA2E33B1F368213CA503B1EBF108276BDB43AEFB7AF93C1B94EBC08EAF1",
+    "identity_token": "542BC2A7B465A7069300EA500112834B",
+    "key_seed": "B6DE0DBCA9A2C1290749B479AEBFF54549CC733CA19439E66A8EC38A81AEEFA1",
+    "nonce": "D8FB3E5CBEECB405135DE22B",
+    "section_mic_hmac_key": "311EE9A96B22421A80A8813DBD29ED80FE7AC3212A64988033105047A72A90E6",
+    "section_salt": "9134AAA791A8F2F363752DE40D98CAD0"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "D03161023435EBFCAF2F2D0ADAE25EFC",
+    "data_elements": [
+      {
+        "contents": "28",
+        "de_type": 471
+      },
+      {
+        "contents": "6536FA2872CDAF623C96BCEFF2471CB4E036039A4BE890ACB57FB1EA68",
+        "de_type": 72
+      },
+      {
+        "contents": "3F9D7E766C0879AE49D791",
+        "de_type": 118
+      },
+      {
+        "contents": "1D4CDFC938C8BE1CEE8BC23DA0D65E51121C1CE238E37259EE389D12",
+        "de_type": 778
+      }
+    ],
+    "encoded_section": "02A17062B53D66D4533A53A453F2485BE62F35088593326E7742857102AD4D29E45FD71D6D2423810C4B94EE6C28BCB8D171D49564294B141921978D180CA20F1798397775BF0E1F4182C915E186E768837E8E7779611BF91FD12C2E9E8608BE33C4AA6E7664C477D7186E03151F5692C1D61E246BE67263333765EF7573769AAF",
+    "identity_token": "DBB90C1A48327EF7A00F4B43D68A1F81",
+    "key_seed": "92750637170C948A4C8F8DBEC0C99DFA3B3F62D666E78860A0E66090E9EEAF46",
+    "nonce": "90059268A0BAAB4B5C27102D",
+    "section_mic_hmac_key": "D894D0DBC6434DC2E305B9DBAE11164339617951C9503578183E2243A89A0A62",
+    "section_salt": "A17062B53D66D4533A53A453F2485BE6"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "4709FE87DD6401199C69B10B42568335",
+    "data_elements": [
+      {
+        "contents": "1C2BBEA2EB6A22D4",
+        "de_type": 213
+      },
+      {
+        "contents": "5734FB78B9DB664F434594B0DE",
+        "de_type": 745
+      }
+    ],
+    "encoded_section": "02C9525C2999AA4C2313D3A88E0DD65385C468B7D341E9C2DA03D43E31514EAE512BB2AD526CF2AE44A758B4DEF16471C615F34A79E7CB8E4294BD91EA791BDA401DD3F4A6BA80EF87187D037E",
+    "identity_token": "67B6209E52C3003631906D5B6F8F99E2",
+    "key_seed": "E8BBEFAA58A804851A6A5FEEF442A5B42E1790D624E171D82666F37D98F479AD",
+    "nonce": "13ACC6C2DEB7C80286605695",
+    "section_mic_hmac_key": "2C5777BA0FC851E2168A7F6A065542A454A00FDBC37796DD2C0F61B8F0F479DB",
+    "section_salt": "C9525C2999AA4C2313D3A88E0DD65385"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "50D0D6451249827DFEC86AD5CABA7541",
+    "data_elements": [
+      {
+        "contents": "A2365C5B",
+        "de_type": 232
+      }
+    ],
+    "encoded_section": "02CCC6C1F682DC2B26C2F0EBD88DF8CD10B46827F6998B5BCD033810162169C9E6177E21F144FBF887F2D62C9384E5C67E9C293E808D79710B",
+    "identity_token": "84A064CB500C36450216D83A3893987A",
+    "key_seed": "BD2E624D9CFE4D8BFFD5F64E71D91E7E6C355207AAC713DCD27E762E43F93778",
+    "nonce": "2CD2585EC8A6FFB46BDD67B7",
+    "section_mic_hmac_key": "CBA836A7141228C2F80DD3CBA933639E4901D26EA5D596DF73749070389E72FE",
+    "section_salt": "CCC6C1F682DC2B26C2F0EBD88DF8CD10"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "353C59AD9C7303309F3F25D3DA7D5623",
+    "data_elements": [
+      {
+        "contents": "8E8E775CF15104DA9C2F070D9778",
+        "de_type": 22
+      },
+      {
+        "contents": "C05C83A5",
+        "de_type": 492
+      }
+    ],
+    "encoded_section": "02E13EE1C144ACAAC46546A7FC73BC517AEC2793AF4C8289C1179DB702134B875F279099ADA5B9F9D20CF79506163A1BB5B7CB158AB8FEB2D5BED7A9DC2737819FB7B5DC2475404D58",
+    "identity_token": "8206FA4E58FBB7DB6CCDC6A8E17D215A",
+    "key_seed": "9BBCA7052AAB7FFE1E37DCE21853BDC901CA146141C567A77E042D1DB8E7677C",
+    "nonce": "60FE311C1DE98B1F0CB8F645",
+    "section_mic_hmac_key": "9AF2DB9C6C964A6B120625992CBFE9810428FB44F41EA85EE8C98FAF048B45C3",
+    "section_salt": "E13EE1C144ACAAC46546A7FC73BC517A"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "63DBF59EA2D3FAD77153DB5412A0468B",
+    "data_elements": [
+      {
+        "contents": "4657BDF649917C3279B8D5E6D8107DF5FE",
+        "de_type": 698
+      }
+    ],
+    "encoded_section": "025368A3DEB0ABAFA3684502DBF3F2D2F1A4919DA6DFA241CA937D946397B2FAD624E77A62AC2FC3FFEACDED692D3C146025FD5E9A540D93A6BB85443C5958087A248897A165",
+    "identity_token": "2F46468244F32581D94A0E134A269C27",
+    "key_seed": "8C34898BE3791991680388FDAE3F11539734BD838C00F4D721D886031B1CD8D1",
+    "nonce": "158DA837D312152F80C64662",
+    "section_mic_hmac_key": "62BCFEF7FABECF3EF0B822A4970BF3444DB880A02805943EB055FE7E3E5BBFF7",
+    "section_salt": "5368A3DEB0ABAFA3684502DBF3F2D2F1"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "6FF3996ADE933F9445D5D9B3BA72ECED",
+    "data_elements": [],
+    "encoded_section": "02DFD2A8ACFCB1979E11868B2EB2A148861E7D3C74802325EA20998C2CF3013BA51070A82DE70003EBD48675EFDA6C8A1040",
+    "identity_token": "573B7D3AF2FD3EDDDA487D8254537EAC",
+    "key_seed": "45B309037CCE98B8DADF3DF7818D3A3B842E292E9F8C54E82F10C4B91EE601DA",
+    "nonce": "9FFEC13ECBB99430B280A426",
+    "section_mic_hmac_key": "4C699D0E312750AA09EFCA9A4C8C4E7E032BC4D5398A6F72F4FB6AFD22D64110",
+    "section_salt": "DFD2A8ACFCB1979E11868B2EB2A14886"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "95D640F8FEEF0F476D173FB35C70E3A8",
+    "data_elements": [
+      {
+        "contents": "0E32AADB46",
+        "de_type": 405
+      },
+      {
+        "contents": "DD700C351B",
+        "de_type": 316
+      },
+      {
+        "contents": "C58F70E4A3F3EDBD5E8E2F0A",
+        "de_type": 651
+      }
+    ],
+    "encoded_section": "029855CCB82C1B5CB8DBD66839B011C07894F5F607E6B8C7FE9C8841A25BB06AD92F451F1DAD781A11061A62ADB2B7F8E951759290C76BCF3497C6323C597BFDB3DB01BDE012660251994A30CEB69F3E76",
+    "identity_token": "4D475A274742151475B341C48194D9F5",
+    "key_seed": "4DCF489FA193E73A86A4EB4C3268CEE5B950A1957C0CA006F3422A15A11DD205",
+    "nonce": "1BC76946373179415F596851",
+    "section_mic_hmac_key": "3381BD66395B21BCFD1C73E775C967784C3F3DE77F2FC1EBE5D2CF5EEACB91C3",
+    "section_salt": "9855CCB82C1B5CB8DBD66839B011C078"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "31B7CC1805DAD1ED55DFCBA3ABD6C01A",
+    "data_elements": [
+      {
+        "contents": "D5D8A14C3B06",
+        "de_type": 534
+      },
+      {
+        "contents": "4D974CBC01B6728564EB30C3C780500EC117E0B2667EC576E10D7DF5",
+        "de_type": 364
+      },
+      {
+        "contents": "79CC9D9954D0C77577859D94D862FA",
+        "de_type": 131
+      },
+      {
+        "contents": "F1A1CA7D01BCF394",
+        "de_type": 115
+      }
+    ],
+    "encoded_section": "0220E344A3BD5D875DA30DF57C04B32022F56AE8CF9A5F02F774BD2483829674CA544027FC5D3CDE71FD19D2AE4861CB74ADE4484C3F758870EB49C211D07B2A0B1DBCAB684203713EC5ADC830B6FB75C927813EFC51BAFED6ECDF8322B679FB9190DAD7AA7915BDE58E399FE58066EBAB338B8F1AE1",
+    "identity_token": "4AC470B5857B8F029D2F8E4B634765F2",
+    "key_seed": "41F01CFE759E12F979BB0A6B23DBF53D408761D4F667CAB71042C9789588CC44",
+    "nonce": "8BACC88ED412BB6E927D153B",
+    "section_mic_hmac_key": "E7B1D619DF9082D0A9328111D0D72FD515B276BA4954B697B942363DBD12120D",
+    "section_salt": "20E344A3BD5D875DA30DF57C04B32022"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "D75007BB49FEF17E0CF767B19412BA83",
+    "data_elements": [
+      {
+        "contents": "483FF7B534C3DC8427",
+        "de_type": 702
+      },
+      {
+        "contents": "",
+        "de_type": 310
+      },
+      {
+        "contents": "02826430E4AD154BAD4FC491CFFEC8",
+        "de_type": 638
+      }
+    ],
+    "encoded_section": "026B334E9E52C272A60F1CD9AB613752273F973DF5218C53E2B5FC7487309A978F31870C486D424DF9B79D0D68C6594ACC1E30F8F4D7F3FC7E559FDBEFF1B88A849FB996E466D2F725D8C76BFC705F34227E67",
+    "identity_token": "60040508990303A048FACF72038CE464",
+    "key_seed": "98DF428BC184564FB1DCA947106B8F89CBB668C74A9CB48C0721430BD2047A1A",
+    "nonce": "C4CC65DCE3FA0CC241D9AA35",
+    "section_mic_hmac_key": "49AB3D69719CCE05B5A246DCF61AAB29376178213347CA7F318F9661A21CC63E",
+    "section_salt": "6B334E9E52C272A60F1CD9AB61375227"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "FAA4F8C869F0F4469CFA8B98E0B84786",
+    "data_elements": [
+      {
+        "contents": "9C3CFD4796001FFA0F0B1A17C2DDD4971945CF1581",
+        "de_type": 900
+      }
+    ],
+    "encoded_section": "027EA134E36AA6249D20357948A07A39430106C70B56C2DDD6649EA769968F03AE283A99A8441AC04033B08878A90E7684242045AC3A191170C5ACBB55B2218679CF770A384C7FB67E06",
+    "identity_token": "7FCFA0BC8F7C8C85EF59ADB9D250F8F5",
+    "key_seed": "7032B5E67A8BEF82845ADF25B6152283D544435CDCABFCA0750BCB81A84934A8",
+    "nonce": "018137DF8171014B5AA4F794",
+    "section_mic_hmac_key": "F2571523E2DC1E97A4A1D95D74967F22268C47733385C3241FAB1653A1F61BCA",
+    "section_salt": "7EA134E36AA6249D20357948A07A3943"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "135CFF48972D3892BD5236733CBDE72F",
+    "data_elements": [
+      {
+        "contents": "13E83591B1805E6EF828EBAB8189A0F7F47E65F6EA9B8EDD",
+        "de_type": 325
+      },
+      {
+        "contents": "8582F902174439B061C45305",
+        "de_type": 514
+      },
+      {
+        "contents": "ADF4960FABA6559565B659F87C13DD9C0838CCF7881CDC",
+        "de_type": 133
+      },
+      {
+        "contents": "F4884DC85E0B24372426BC917E7824B9CCC6C682619428170A2EF989CF",
+        "de_type": 863
+      }
+    ],
+    "encoded_section": "029EE7BC1BBBE9DABEF0A56E7E11D1A11D432AFF55DAB52B42407E950ECD6FB8D174564E551C9E68F5C5F229808F63E587225B49D682D481A708DBDCF8DC53D55AEB7250AF51464C4C8EDF689933DD8414BAD3BFA8C633B7914A67A55D90598A8070EC256500F60B92F22B2E0A08749AD01AD7AAFEEC9C547993E7D8F5495E1D9B0CA185F41DAE0F48E4E953530CF8A8EF77A80CDC11",
+    "identity_token": "24DB26605841C2C1F2FF4EAFA8C9A3B6",
+    "key_seed": "B80CEE8A5289B0E88AEE0FD08789C7D762F4DFF8D0FC5D79CC9332F24F7AB889",
+    "nonce": "20979F413869AE8D0069CEA6",
+    "section_mic_hmac_key": "F57425B28C88DD3C938D7C7F9E2E2DEDE7883C21116B6C2AECD9659F508172EB",
+    "section_salt": "9EE7BC1BBBE9DABEF0A56E7E11D1A11D"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "DDC14ABAE3E57A2E436D2B154CA341E2",
+    "data_elements": [
+      {
+        "contents": "0100CA9F13A748A7769E5DD2239A",
+        "de_type": 465
+      }
+    ],
+    "encoded_section": "02AD0144308355660F11BC1522BB4040343B6EE18AFFEF9B71D3239005448AD9FA2193AA48A6A9D558FCC66E02BD74A9A83891166A550882FDB1654D80C853A5F40E40",
+    "identity_token": "70AE495464B5C777F3B7533287CABFA0",
+    "key_seed": "703D9283FAFFA27816DFE81AB6FC0C86F70F9EE2A59F9AE9FA675BB94F4AFAF2",
+    "nonce": "F29395253E56ED38E3AEE6C7",
+    "section_mic_hmac_key": "62107DA82458F0FF6C1F510024186527BEC70BEB8E9E2BC63618656F1F10E27E",
+    "section_salt": "AD0144308355660F11BC1522BB404034"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "744AC27B83E2766619EC0FA0A3B7145F",
+    "data_elements": [
+      {
+        "contents": "8809E7D36FF481A81F7CFD46325C0E4999977FF41E8D9E6D",
+        "de_type": 326
+      },
+      {
+        "contents": "662C4AAB4A9065207E",
+        "de_type": 794
+      },
+      {
+        "contents": "7B302C10AB20F7E646A1DF783ABFDD4A568C9C1C17E26F8A",
+        "de_type": 111
+      }
+    ],
+    "encoded_section": "02CB4B333C50936FA751BFD0BF7F69C50DFC0358422A96FC26DC3450BAF84BB28751B73C86769524A89061A0178DAF1B8BA70B97DA597B7B4536312CD3FE3302EEC5659948F0310B6A04EA623628523BA417256D89531912AB7E5F5DF54EF46DD764CF9B7E10303A4CD3CE36A96E5F46139A98",
+    "identity_token": "A8ED77B3381D625297D75804FE02F5F1",
+    "key_seed": "94DA586A9221F4376146845409E3CD6C6464BD5313D34E2E6AAC864BE7088C20",
+    "nonce": "54278CC24894F36FD3FA5AF6",
+    "section_mic_hmac_key": "FACFF42071ECFC827096DCC9464F0FD7EE42772DE4D3CB9E33AE208A206D8145",
+    "section_salt": "CB4B333C50936FA751BFD0BF7F69C50D"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "8382239DC9F8AD351C6691B8765751A8",
+    "data_elements": [
+      {
+        "contents": "731A9182CFCDCB4B5AFDC795BB7D773039",
+        "de_type": 672
+      },
+      {
+        "contents": "AB559ED81984D1E11D3D77C65B1B068E",
+        "de_type": 34
+      },
+      {
+        "contents": "EC20C11534",
+        "de_type": 182
+      }
+    ],
+    "encoded_section": "02574DD074E3030F3C440118AF057A6E81DE77D7C6A81CB708D46329C685DBF63A3E49CB49CE48208A0BC9BE897C615383859784266BEABD8739CFCEB412B7D160A078FC7DC9717CD450C0EC5CFE6FF9078EE887CF798B061A8DC8A0A4B81FA7",
+    "identity_token": "A23C1EC6DA331C8BFCC59AD45299A8AD",
+    "key_seed": "3FFFE6BC8E09AEC10917636F693B7B623B83B0F60CD0A53B6E671AFA06BA71D7",
+    "nonce": "603365E941059E1DBA63216A",
+    "section_mic_hmac_key": "0600965BADD5FE7A27707EC8DD96E50F8565A262FA2F006E9C34C32FAA442F21",
+    "section_salt": "574DD074E3030F3C440118AF057A6E81"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "0B146273164E3535D882394B7D8E8A10",
+    "data_elements": [
+      {
+        "contents": "A6",
+        "de_type": 435
+      },
+      {
+        "contents": "FCD83E31FC",
+        "de_type": 117
+      }
+    ],
+    "encoded_section": "02DFD3246C71A4A97E4B1BA9D4F7AC34AACDAA84C85C205A6730B0C34D1925BB3D1BED7A91D44420270CFAD5FF0D68D5A081A74B5E6A9F825DAA528DD0",
+    "identity_token": "F188D1E3D8D9C0272F29936D3AA2C4C0",
+    "key_seed": "0454EA82B20D1CB3895AA549F07794CE16A417EBEA2D45CC3D7CEE88840D1DEE",
+    "nonce": "4A37E74B7919321224347C0F",
+    "section_mic_hmac_key": "89FB1E3BB8673A6A39207460CC3EFB7BFEA89A274547F2C1DCD105AF454A48F7",
+    "section_salt": "DFD3246C71A4A97E4B1BA9D4F7AC34AA"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "A6C5055276D8481CEF2ECCCD19DC6D86",
+    "data_elements": [
+      {
+        "contents": "E8C34AE045BECA84AFDF145AB2C11534C16FB5",
+        "de_type": 260
+      },
+      {
+        "contents": "87E8D027D88BDD326D08D4B0",
+        "de_type": 547
+      },
+      {
+        "contents": "A74A12CFF895DE4FD969A2BFEE920D3A131220",
+        "de_type": 954
+      },
+      {
+        "contents": "C02CB796EE5010050633D340C8",
+        "de_type": 784
+      },
+      {
+        "contents": "32EF1931690553C3B4AEDD035D12FB776F93BF91",
+        "de_type": 742
+      }
+    ],
+    "encoded_section": "02A0318DC4C04450120D114911CB0F21DA6895C9D7D2B7AF43E57F6DF8A5B85F297250D4B38EE384132B5026A691A5A848C1284550348231A6A97CF8D47CA5F1F71AC81B984264A05F794327D9C50A9F72A5A102CD566D023DFF1C4E116C82B83BBC8A0884611E37C6CD8581360318DE18DC880E2B00048F9C4308A81975B0B73F0A255F229D267F19CEDE7329D8180F71978672",
+    "identity_token": "E0D703959CE67BF41B6976FD455FCE5B",
+    "key_seed": "CB83F57DBD5CE9CA5F898CB4F5556A090479FD88C20F7EE18D632FAF178323A4",
+    "nonce": "380124B5FF2BF739EE781770",
+    "section_mic_hmac_key": "B044296E5AD11AE978E19E56856D6829769C3F38E488A3D741E9AA632BB32FF7",
+    "section_salt": "A0318DC4C04450120D114911CB0F21DA"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "690CA5CE0AC60ADF00AE579BDB164319",
+    "data_elements": [
+      {
+        "contents": "284488",
+        "de_type": 778
+      },
+      {
+        "contents": "2F9112B4015F302DD3068CB5C06236ACB4ED1ED9908BFD8859485E",
+        "de_type": 824
+      }
+    ],
+    "encoded_section": "0230ECD327F2101FD57C67F868BFE5591A9EA0510A3A1918D42719B7D937FC25EB347643A382CB1EEF1BE0D45278B0EF49241FD4AA53333E9B20D7DF545644C794C0C313FC9C2D22968DADB4406A9E092999EBFB7F92",
+    "identity_token": "35AB8A4472A635C5EC6458F32686DBEB",
+    "key_seed": "99637210F99F390A997F576C706F9BC7C8581692E1285E29D1BCDE117CC0B428",
+    "nonce": "10CCB0A8DE6C3AFC5E747D1C",
+    "section_mic_hmac_key": "57447E9E35E7BF08280D4F92360E0141A766AA4CE818AAA45752563253DBD173",
+    "section_salt": "30ECD327F2101FD57C67F868BFE5591A"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "73D9602DDB93D7699C8C046E0B9E602C",
+    "data_elements": [],
+    "encoded_section": "02D74F68B471771AD2A4F8559305EC53902AEAF1CBF3D8C11992C5DE18AC41601310DD15D84C0CB36514110875BA8151D0EF",
+    "identity_token": "00EE8F5898673EFCC8D7D593ACA886C2",
+    "key_seed": "6BCC9CE0FCED7111026D0ABAC01E65F4149D37F617683A58960B8F4AB3DA64B9",
+    "nonce": "14B891CE7E628E756A830231",
+    "section_mic_hmac_key": "8A469C665BB030DFA05EBAEDEFCBA98BB35C9B9EBE41B1692B2D74ACB0973D58",
+    "section_salt": "D74F68B471771AD2A4F8559305EC5390"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "116268F2138D48B7377D1F9B993F112F",
+    "data_elements": [
+      {
+        "contents": "142941",
+        "de_type": 384
+      },
+      {
+        "contents": "4E6B16DA0CD13D6EC21E797862C3AFDD25BE",
+        "de_type": 254
+      },
+      {
+        "contents": "B77B7519377B061318D90278B71FBA82C604D2DB5C69",
+        "de_type": 570
+      }
+    ],
+    "encoded_section": "02BE0BB3EBC88AD859D7B875008C0931365F394AB17B39FB727A93193C4FEAC4E844DF2573378CFFA4B15B0D082B3D59EAB8373E20E17079FE768CA1A2479D57E002BC914D4BC807673ACD10F31FF5F6A39183669EC4B8F593E6E086C4456A3A872F3F401844",
+    "identity_token": "BEC77D2328CC57D22E0258715D0841E9",
+    "key_seed": "70FF80E5D4D65D6C8E4C68082D75ACD7B1EA9FD3C0F4AA154D4086D2D20D0A8D",
+    "nonce": "DC2D9C34D25C7E6D95B384F6",
+    "section_mic_hmac_key": "2FEE78E42D658EAE17656D3815534D07B399660A2030795ADEF7FFF532321509",
+    "section_salt": "BE0BB3EBC88AD859D7B875008C093136"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "52E5153F816929E66AB35A982F597400",
+    "data_elements": [
+      {
+        "contents": "DF09806C22CEE77C4C664EFEE3288301768D",
+        "de_type": 589
+      }
+    ],
+    "encoded_section": "0263497BB609ACDC19D570B16320321281AF7072F3BB15F889145ED8A9AE6E9F6A25CC28D879CFDE5740FA13C8E044D88BB6CA7A7DD16174A3ED805757529717CB260449D92B67",
+    "identity_token": "855F0220CB5E718D26321D4D7C7577C7",
+    "key_seed": "A1F0ABA90F81ED6943F95065A29589446498EC2F79520A56108539E3C30A19D4",
+    "nonce": "39A3380C10BB5CF8E9435BAA",
+    "section_mic_hmac_key": "6B654DBCC294323566E00D2A8C486A6F1ECB91FF949FA640B93D3B152F047230",
+    "section_salt": "63497BB609ACDC19D570B16320321281"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "B6E9D40607D8C39ECF7625E686CEF325",
+    "data_elements": [
+      {
+        "contents": "2B2948F09099",
+        "de_type": 544
+      },
+      {
+        "contents": "E551587B",
+        "de_type": 375
+      },
+      {
+        "contents": "4E6F0748C486",
+        "de_type": 37
+      }
+    ],
+    "encoded_section": "0205FA3C47404A35D4FD5BF0F547164C81B299006BA6CBEFBD4E0E4A450CFFA1382856C3C42CDA3A189D6FA37FFE8CA32EA8548FE61DEFAC42D1532D3865A41D6F71DB37B9C1DB88B1A3",
+    "identity_token": "72A30E0B2EF76A0265690E2564D197F3",
+    "key_seed": "13F03C6648844D00F21EA2CF23AC0B8284D36465438DE45C81A59775BA116FCE",
+    "nonce": "499227762DE0BC7B33BC7153",
+    "section_mic_hmac_key": "8714D596BE3472DF951E6861CA718DF4C391976F932EEEF3F8B48F78C2AF7C40",
+    "section_salt": "05FA3C47404A35D4FD5BF0F547164C81"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "2293B6380F70640ABA8871ED56932E20",
+    "data_elements": [
+      {
+        "contents": "0479AB07E83192319E104D967353472F1DC0F863CCCCE0BB99",
+        "de_type": 258
+      },
+      {
+        "contents": "A0AC8E8D1A916AA5AB9E021CF32A85288F238809CBAE59AE8A71",
+        "de_type": 672
+      },
+      {
+        "contents": "",
+        "de_type": 27
+      }
+    ],
+    "encoded_section": "0294E4D54F244E47A822FF413F6BC376B2B6162B1A181C00187B1ABDB43612153D4B8EA58CD0CFC7314AE91A06A4E35027FEB5EB8C9D93AB2872B2A3B561EDFD98F3A937890D95B5994729C4D04DB85258644B6E92E15E87C21CC5D567A005CF496490988A3AE18BAD1F233DE0",
+    "identity_token": "3AA3E8B81C9B7D2D22CFD1E3FD7EA5D5",
+    "key_seed": "DBC016C10B947661DE37B57C0C33FFBA01B89FD64B9417DC635D9D19C44511A3",
+    "nonce": "3F5472259721D69E0511C6F1",
+    "section_mic_hmac_key": "1097F2D8149337A3927ECD7E4E0534AE66E7CECBA50D8C8699E2747AAEEB82DB",
+    "section_salt": "94E4D54F244E47A822FF413F6BC376B2"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "BCAD6FBC54D804FB305B651664B5EEED",
+    "data_elements": [
+      {
+        "contents": "98758452",
+        "de_type": 78
+      },
+      {
+        "contents": "816C9AADF492",
+        "de_type": 123
+      },
+      {
+        "contents": "C5AC",
+        "de_type": 539
+      },
+      {
+        "contents": "98C2695950C4B5C2F3C69D91B019D5045198846A088508158C4835",
+        "de_type": 929
+      },
+      {
+        "contents": "22C4B157B2334471749AB6",
+        "de_type": 320
+      }
+    ],
+    "encoded_section": "028C8FFACE6BCDFE650833331F10BFDFB7B4E3B071CA7AC5CC1A3BAE79685825EA4FFA30EA2417529BFF840848F99C6A8243648D1F8244457FCA1F808E370B94D2F04BF78B7E51C23752D922268EB1443C0B08D3D2CEE883673ECE7AE5DD5EC5E99360901AFD815B2413BB071A62FF2960",
+    "identity_token": "A1D0AB03665B36F07643E7DEACE86D57",
+    "key_seed": "40A14BB11063D05593EB466ACA4C4DF438FFE2FDA5D874A4E5B3B4E79A6DC65B",
+    "nonce": "484B3BAE18B85C1027094E93",
+    "section_mic_hmac_key": "CE4B87F04EBF02FF12744FCAD9753239514C6E0856D6C2C6CA768315385A98FC",
+    "section_salt": "8C8FFACE6BCDFE650833331F10BFDFB7"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "B5CBEC978876F51A7BB1396D7A1A5796",
+    "data_elements": [
+      {
+        "contents": "E55EF6F4749B6ED7F1E3254ED7",
+        "de_type": 427
+      },
+      {
+        "contents": "6B1F8A",
+        "de_type": 586
+      }
+    ],
+    "encoded_section": "022FCDCA777563F84DDE7D907A02D77407964B19DF4F96C67E2F5E3078AD2D094326EC3BA4DD06CBF9CD27A0996D83942964893D079B8CA07E07B72D80482F25156C66BC22D60047",
+    "identity_token": "A1D64E8B5EF18E775F30C87C7F41937F",
+    "key_seed": "CF5EA21DD3F05F95A8A1658B4904E63F7ECBBDF860F1D27B88E4B418E914D87F",
+    "nonce": "C8C9C5FCF402ADFC23600F63",
+    "section_mic_hmac_key": "9768775B2D00E70743DB112CFA2E4DB37DB7D9D469E594D364EE254533F2714E",
+    "section_salt": "2FCDCA777563F84DDE7D907A02D77407"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "841D9C330BD88576BB9159CFD67F912E",
+    "data_elements": [
+      {
+        "contents": "815DBB833846588103164057A0156ADDC531E401D585BD5368B419",
+        "de_type": 864
+      },
+      {
+        "contents": "2FA2A37BC6594C3B4A29A2BA077AB8F324F1C743354C2A1B97",
+        "de_type": 71
+      },
+      {
+        "contents": "E91DCCA08179D3",
+        "de_type": 873
+      }
+    ],
+    "encoded_section": "02F49C8B85636DBD028678AA984B5A0F2643DBF77E8FC339E837757F1FE34403F353E16488648B5914A8EB8045BEC91AE34344DE4B90445DB2730AC1FADA1635D2DB87068BBA5ED44F6EEA57EAE2CA5B15BD5FDDBDC1C71C1AF39E58E2E2DDFD721C6E7B4CBBA4918FCC33688EC2119A6402854F69",
+    "identity_token": "27A1DED5C2E9533BF3F99698E1E525BC",
+    "key_seed": "6B7032582260EC0DA57AA5AD575BEFEC7D28696DC6035472F5745873DCB3B840",
+    "nonce": "6B048702497ECD3CADA72B3B",
+    "section_mic_hmac_key": "80747614953138AF6109362B19BD2C97FB7F8A224C908CA870258F1CFD5D6196",
+    "section_salt": "F49C8B85636DBD028678AA984B5A0F26"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "56CD7B921FCE0DC253137ACBEF4C9E7F",
+    "data_elements": [
+      {
+        "contents": "8E2BA03625D8EB749839CA099D48DE15C7887A93",
+        "de_type": 194
+      }
+    ],
+    "encoded_section": "02999AD00461B54D36BA7FC221F7F10D1239480EA7C91F1C200AC73639F608F5022706B4E495786CEDD18D582218A20C83534889C7026B633769D626E2BF60E33069B91D50A8CB1CEF",
+    "identity_token": "158D50035DE9F0289B63225B57629D58",
+    "key_seed": "CF8C5DDD57F9E7399E46F52E06906C39D6155AAB1BA940EDC3068F3642B0F3CA",
+    "nonce": "7E6FEF58E278A361FA3F77BA",
+    "section_mic_hmac_key": "561D62F4B1EC440C5A8C9511A0981C6A14F1A30EE5D156AC2460A25D9F2A095D",
+    "section_salt": "999AD00461B54D36BA7FC221F7F10D12"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "99305ED53E510A6A5620AF58B298A200",
+    "data_elements": [
+      {
+        "contents": "BAE0D38B",
+        "de_type": 354
+      },
+      {
+        "contents": "CEC61AB08C2D8EF32C869D2FE2",
+        "de_type": 67
+      },
+      {
+        "contents": "0EF4293C",
+        "de_type": 947
+      }
+    ],
+    "encoded_section": "02E368EE79CEE4FF33EA1BCF4950B8A8799DFA666B5562CA808D1919E6F24C5A3C2DB36770DB9C7514738035B233AB2025C66D50FA57008D76346564BD8D3BEDA68A676153B3638C47AE4ED7043A6F",
+    "identity_token": "9105B80384B6610035BC153B06A07D90",
+    "key_seed": "D72E99D75674D6C2F4E8EED8D64568DABC14FA7C55BF6F7E0B04302142474AF1",
+    "nonce": "61E591B344D506E266FEB0E2",
+    "section_mic_hmac_key": "501159272ABDB1D1BA4BE9311D77AC0559D63152DA5BD34A2820DA15A82AD037",
+    "section_salt": "E368EE79CEE4FF33EA1BCF4950B8A879"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "F9C9733A407F95BC056EA093672AAC62",
+    "data_elements": [
+      {
+        "contents": "39A6851F0D7562A8D67C87847D",
+        "de_type": 33
+      },
+      {
+        "contents": "C7",
+        "de_type": 159
+      },
+      {
+        "contents": "C94372A46C75",
+        "de_type": 471
+      },
+      {
+        "contents": "E212FDD45F7031CCF2F0",
+        "de_type": 609
+      },
+      {
+        "contents": "60A513C6D691D578B1B8E0D8678C4C93417CCC8772A98A744EC6C3",
+        "de_type": 305
+      }
+    ],
+    "encoded_section": "0239448EC52D1360B39AAF3D8D2ECBE21C8903D1847E324F6AA5575C8EE28D8E4157ADCE19A5988809B2C3396F2F55B8B1A6D7C14B509EAD06129426C91C94D7387DD790EF5EACA005EABAEAFF8D0E4E1A2286A3EBD393A0B0E2A0C73F900B32D82F28F3CF8441414116BC415BF3D652DC1F6ADCBCE89D5A5E",
+    "identity_token": "EB2E94C1013BBCE935289A687EE855F4",
+    "key_seed": "CF638C9E9ADEED53473692F67FB230EFCA033A90FB31FE01640296AD33198E24",
+    "nonce": "8A1421B5D302EABCB4E9AFDE",
+    "section_mic_hmac_key": "C0F1CC05E31EDC758059D4FA1C6A539059A63E86ED3A81EFE5B49ED6504BC816",
+    "section_salt": "39448EC52D1360B39AAF3D8D2ECBE21C"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "AA35088237F8B48B7B9783E54DCC7310",
+    "data_elements": [
+      {
+        "contents": "C54293E528BF5A6D1C5299",
+        "de_type": 119
+      },
+      {
+        "contents": "B8F16ECD",
+        "de_type": 266
+      },
+      {
+        "contents": "C025E0",
+        "de_type": 749
+      },
+      {
+        "contents": "B668775936872A95200E2A1A544742E4B5DA218E66",
+        "de_type": 581
+      }
+    ],
+    "encoded_section": "02605100404180967E0C35A27B303198AA0777DBC954ECAEC74857B598034CF02642E7BE5C315CFD1DCD1AB6164D1AC72EE44713D4332D0ACD1C0A588BB4DED8FBD841CC2D6EC37D6FF4DC1D9E3E1DBFFE520C351C868F4DACD799112FBE876903BD4DB4",
+    "identity_token": "681670B22B7549584CC281C7E9C7BDC1",
+    "key_seed": "5B8298AAA972242D14166249DAB29D642898C1EDBF4AEE35E5A28A523D79EF4A",
+    "nonce": "EAE83D7AF44006D4DB2FBEF7",
+    "section_mic_hmac_key": "D4810C45A1A468EFFC95E987192D01D0B2ECFD313E9852B1A3C914AEE1BAAB5D",
+    "section_salt": "605100404180967E0C35A27B303198AA"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "E6E881B42FC339A5ECA605018A8EFCE5",
+    "data_elements": [
+      {
+        "contents": "",
+        "de_type": 486
+      },
+      {
+        "contents": "05BE835BECBFF491794F",
+        "de_type": 824
+      }
+    ],
+    "encoded_section": "02BBDBD06DC3AB72F0082A6D0BC513951E56012BBCAAAC40D3F10B436E42DCDBD020E61B167FB0A77E527F3837E41D9AB666EDEF30B943B65984B33E7CCEBE0B4F79",
+    "identity_token": "21346A45B8489F23A9E63CB46B0AD4EE",
+    "key_seed": "D940EE0C157926DAB1DBF8020B86F06D152092B162CAF11C607B125A18FE58EC",
+    "nonce": "5A5DBDBC22E9F0485B0F0C4B",
+    "section_mic_hmac_key": "0BC02C5EB12F36E458141B6C48D553B9164D52F712BEDD2E0A7CB3FDB3C54862",
+    "section_salt": "BBDBD06DC3AB72F0082A6D0BC513951E"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "0E6F2D1663BA6A254DE665F436412DAB",
+    "data_elements": [
+      {
+        "contents": "4E4693617BA9052ECC",
+        "de_type": 692
+      },
+      {
+        "contents": "",
+        "de_type": 510
+      },
+      {
+        "contents": "6DC4A08E16F5789C7F4CC0625715EC35E03DEC75153C98F9F3",
+        "de_type": 501
+      },
+      {
+        "contents": "72EC503A57",
+        "de_type": 283
+      }
+    ],
+    "encoded_section": "02184D45A5DFF4F98BBA2B9BCCD0E66F1E6E4F2095ABB97491594F1E2615C6EEEA434ACD59264403E3B7C932D33D7AF49E223255148D5B9176DA76830D451DC226F6290B659EFB946BE7CB2F323D1AC1ED48187ACA1C7A7A1AF00E47D0A09AEA3D5B1FEED1",
+    "identity_token": "EF610B037D7AA8DB3C45146D1CF5510C",
+    "key_seed": "33D5F107E69D63BB05BD8CDDE5BCA9749BC619217585D756B67D84889942B812",
+    "nonce": "3600FCE45834326431FD54EA",
+    "section_mic_hmac_key": "36DC6B1B43D34B4A36A28DD14996986E4F3B14D346A24B1C7D5055202E1232C4",
+    "section_salt": "184D45A5DFF4F98BBA2B9BCCD0E66F1E"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "67C7EA4F6E8B95960DF747286E6C253D",
+    "data_elements": [
+      {
+        "contents": "DFC4A6D167092E0EC4386C1F377C8D7A982D",
+        "de_type": 444
+      }
+    ],
+    "encoded_section": "02AC62D0F2D343B4B67F2541B51FAB36F7DE79C99A6FBB63335DD4A5DB2D418C2B253164CEFA3BCB42A517E89BCD1FB1E5C6F04FA0C99CCB247F03D8A9575F2154D07D25D551BF",
+    "identity_token": "63E358995194244AD3281A35F844DEBF",
+    "key_seed": "AABF59ABBB17F47267FC66D84EADD558ED1AAFDEA87072FBAE1597E4F894CC2C",
+    "nonce": "438DCBDC58F3DC62CF4DD8C0",
+    "section_mic_hmac_key": "9C1F6AEEDBF0ED82872AEE17C94E67388D8DE56BF4E6D064B091F26314BC1C37",
+    "section_salt": "AC62D0F2D343B4B67F2541B51FAB36F7"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "CE4777B8675B646E84D6CBA4000FE4CD",
+    "data_elements": [
+      {
+        "contents": "F1C005DE37",
+        "de_type": 856
+      },
+      {
+        "contents": "5CD278AE4F49F14B64AE6FFA980EDA5A30A96D674AE21E35EF1ACF",
+        "de_type": 669
+      },
+      {
+        "contents": "2CF1F076D4D763145F",
+        "de_type": 339
+      },
+      {
+        "contents": "E8EF398A871164F4AAC2512A73C2BBECD0ED64",
+        "de_type": 116
+      }
+    ],
+    "encoded_section": "02777790C1F455A388A8A8908F2530F5823703F5594AF0E8BC06C2C683DB980BF2575C3006313D8F42FC9A6E4F133C88CAF4EE90C76199F59C6778C13865B9FA83AFD4C4E27F22E1857B682EE6401C973E67F6A04CB1FC1030861B77DABD0FD7018DBDD221F4CCC89BBA1423311439598E945890317E4DB25C",
+    "identity_token": "98BE3C3FDFF640C7902485986A54CB7B",
+    "key_seed": "8AC62E22A13FD1D0BC83021FBE72E253187FD032D48150CE2E363F0DCA427A01",
+    "nonce": "838BA90741E30DB71D749F04",
+    "section_mic_hmac_key": "CA069B89338B5A7AB5B6385DF46AD1EDBEE95EE58763002DC70201E132F0B98D",
+    "section_salt": "777790C1F455A388A8A8908F2530F582"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "73204F77542E61479FFBB70F15BE9D82",
+    "data_elements": [
+      {
+        "contents": "E731",
+        "de_type": 228
+      },
+      {
+        "contents": "795B467FE034E95A4441C2E6E67FBE2117483A61793D21748522A4882BF8",
+        "de_type": 994
+      },
+      {
+        "contents": "0532BB79831FFD7B67D9FC26C7C25759EBE3AB6B",
+        "de_type": 343
+      },
+      {
+        "contents": "A488A172EFF5843E44EA9C",
+        "de_type": 28
+      }
+    ],
+    "encoded_section": "024E89F9D070337876390A8DC90F532057A9F37C117727BDBBD0521775EAC63B345A3E53A0483357B963123F26B640EADA4F0EF043D4B25D831100557C0DF475FFC0A6E721D016053596B52CB0404090F67FAF7565661416996E3EB43882485EFA6EB866520D4CBB482F26EAEE25053B69059BF757603E9514DF131B",
+    "identity_token": "49A94C68702A530BA761CF9A02D0D447",
+    "key_seed": "03BA9921A9EC9DE1B4C3975F758B63E9D5110DA1ED0C3926D9B047804C36C525",
+    "nonce": "CA9A5E6F0D1FB66704D8835E",
+    "section_mic_hmac_key": "790DB507668CD2006E6C56455ACCFD851022A5FD00E5AD27DC3A7F011241EE47",
+    "section_salt": "4E89F9D070337876390A8DC90F532057"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "13A1E3A6D3075C5319C41E8467634AE0",
+    "data_elements": [
+      {
+        "contents": "F98092C5EEE605C57E4F559DE1A39F6E097E90A6DF5ADD0516B1D0AE7C5E",
+        "de_type": 586
+      },
+      {
+        "contents": "9894D7",
+        "de_type": 148
+      },
+      {
+        "contents": "2F910D12E20F96F45D7E8D75D378C34ADC",
+        "de_type": 473
+      },
+      {
+        "contents": "",
+        "de_type": 382
+      }
+    ],
+    "encoded_section": "0201E3D1E7546915A844FBAE96D804883DC7EE8DA279EF8B4A38498DBB4B05C1444E7A3076881D63895422DDC17BE94B3AB229D60FC56E3F7B356246DB710D92629B73ADD1F33F12518070CB4B6409C406DCCDE26A4AD9972FC8235F4912ED2538D0B9D6B8D736C9FA05BF569A9033C4",
+    "identity_token": "39D0F77560A9F411D420421C51345467",
+    "key_seed": "D752C5EC1FD70E7443A37506DB53F6D9E1D521779526D7458CAA5E0239027498",
+    "nonce": "14BBE83961C2E06DF151C3D5",
+    "section_mic_hmac_key": "290C81A58DFBE27E6FCB3DFE0BC632B9D929AC6A60527853230793FFFEE83969",
+    "section_salt": "01E3D1E7546915A844FBAE96D804883D"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "065DEA7DD770D48BE65DAF2A01922D92",
+    "data_elements": [
+      {
+        "contents": "1B690D",
+        "de_type": 183
+      }
+    ],
+    "encoded_section": "02006BF54BA34C5EEABE6733C40C6A4B9FEDB9B4127A2B99B33B12AA97FF6CB70D16AC7E7B3147A10EDB1F652024427E285A71D69A95BD2A",
+    "identity_token": "5CE752DDBBDE278BB19C9C3B3ED426AA",
+    "key_seed": "649A30B204D8F4A1B64EB975ADB6A2B83A7B6BC69728489535090564F3ABA77A",
+    "nonce": "454F3FBAA1278732E147A055",
+    "section_mic_hmac_key": "99262C1952E0E08CAC8768A100996C6CF6F1519EC57F18A0B1D73F5B2F6426FB",
+    "section_salt": "006BF54BA34C5EEABE6733C40C6A4B9F"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "C6F955057E1A4F1FFC1F2ABEA0B8FD33",
+    "data_elements": [],
+    "encoded_section": "027518E350DA3DC2A90BEBE9C1446ADC7DC841ADD922FC0E770BFF64583C4D9E7610A97E13326A236796EC40EC1C636A3B65",
+    "identity_token": "A161A4CC8259EB0F707BF4DF7B3DC27A",
+    "key_seed": "E73209D0C0D85198651F3B798FC53A0927DED2EEC6CE5B8EFB83E50BEF351209",
+    "nonce": "A5984AB79981B29B451A8BC8",
+    "section_mic_hmac_key": "A8AD4166470668BE0394BA7A2B9DD470F34C3BA85186BD1ECB8B00C4DD8C513C",
+    "section_salt": "7518E350DA3DC2A90BEBE9C1446ADC7D"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "5A3D5F583031D4031F313ECCF3E7CDD1",
+    "data_elements": [
+      {
+        "contents": "7CA2CD5E09B6164ED80BDFD96614AB0EAF",
+        "de_type": 643
+      }
+    ],
+    "encoded_section": "02C99D27CDFF106A9BF60401B30A12B78CC29E229B704A39A08B30A358EC76C0B124735CC7DA14B34B5D303B34BCF804C0FF5E6FB3A56426766182C9DB7FB908D4AA1DCB08C0",
+    "identity_token": "40CE8EF0C7FB9C8B822F760DACF25447",
+    "key_seed": "1928670C4B6F0DEBED5BAF7A97E950BBE94EC7D567A70CE967E4B2B212B5A7A9",
+    "nonce": "E8D11B32953AD61E43FF147A",
+    "section_mic_hmac_key": "4CFDEBF521FF7DF20B146DB5947E0939F80346BA991E4D06BCE88587B675052A",
+    "section_salt": "C99D27CDFF106A9BF60401B30A12B78C"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "1D8DA89F7A7D6CE5CB5AA18F77039AF1",
+    "data_elements": [
+      {
+        "contents": "632D457E2AFD3A",
+        "de_type": 945
+      },
+      {
+        "contents": "E4B2038436599A2A4888A76F09A99786DA51BC3F",
+        "de_type": 628
+      }
+    ],
+    "encoded_section": "02660B19D834927254C3CA4C44B4E58F9592C282EAEEFC114E0CAB05CF072BF7E5310DC0238DF9009FA972F31FDB78172E2D8751ABFB1B8D2C5A1472CB8AB52F915318FBEB98357115D69CB19FA2B381E5EE03",
+    "identity_token": "58CFED2666F58A35DB28BEFADE58C82A",
+    "key_seed": "4B54C23D5EE9BA26FAB0C9D819532B27104C3158DCF1204D2C90E493371465FF",
+    "nonce": "BC35F77BE3787AE9D562921E",
+    "section_mic_hmac_key": "832DD5525C889D87C58DFC886FC275FD53EF23602257DBAAADD8260445190895",
+    "section_salt": "660B19D834927254C3CA4C44B4E58F95"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "DEFCEB7C3979CDA6A93ABB83FC1B2D1A",
+    "data_elements": [
+      {
+        "contents": "2D7A34E6CA20A0E05E4639488D400B35EA638CE6E538678F10BA",
+        "de_type": 143
+      },
+      {
+        "contents": "E84E413DC7B2226F720540790093",
+        "de_type": 781
+      },
+      {
+        "contents": "FD05E7976720868B46D00A20A71E",
+        "de_type": 358
+      }
+    ],
+    "encoded_section": "022B54CE0520655913AD3C401F94530EE71BDFC769D515CB765DCA10AB115D32FD4F371FA81787527B9EE63C1D0B68DBFEC7C9651EEB12D6442CEC14CA7877CEE0261668C118A1A255A0994F5B8B7D86D5A6242B709350887160A90EF1DD0A3F0DBC402C8C114CD57EED600F0044C6E971",
+    "identity_token": "8C50C0ACF8A65AEDEB6963051EBB25EC",
+    "key_seed": "E78E6D29382220CEE2E4C81341D26B5FA63132186B82A0F0EC2A723B2BB184C1",
+    "nonce": "A3E85AD7D06053583B8A3734",
+    "section_mic_hmac_key": "E1CAD553106692395DDEB1B29597259001161AE17E76B7D1329891F98547002A",
+    "section_salt": "2B54CE0520655913AD3C401F94530EE7"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "4357834BD8534421FC328E433DC7B1B5",
+    "data_elements": [
+      {
+        "contents": "A0B4D6535ADDE0",
+        "de_type": 842
+      },
+      {
+        "contents": "82B652635D1D50ED2BA2B02EB2C724B314C1F528079AE302",
+        "de_type": 350
+      }
+    ],
+    "encoded_section": "02258C3D33CE4C862F9B69FAAD6C1BFE1F1F8D746B94BD1AB65354B648AC0D054A35385EF82E5626CEB99335CF2637088410D4B278EC77451E3C9AE2DB438AF702EA1327B74E5B341CEE8D2D1DF16A482EE18DE4F8511B",
+    "identity_token": "D53777A4358E0F2A2EC95AFA3D408870",
+    "key_seed": "A01B914623D5624DBBF6DBC8757BF955A979E32B187BC7F70E3EDBDB95C4716F",
+    "nonce": "B55F4A96033F23DBB8BD3F5C",
+    "section_mic_hmac_key": "13A8C92D1F018B4F892D459BBFFFBA84307867F39B300918B0D6518766347554",
+    "section_salt": "258C3D33CE4C862F9B69FAAD6C1BFE1F"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "326371E295F358C8F1237CC22F501666",
+    "data_elements": [
+      {
+        "contents": "6C378A24DE51EE42",
+        "de_type": 264
+      },
+      {
+        "contents": "248624F8FF242C90854382BB24",
+        "de_type": 248
+      },
+      {
+        "contents": "99135A2BC5D7D748EE152CE572FFC032207AA2AA605D",
+        "de_type": 514
+      },
+      {
+        "contents": "1EA3BE72AE386B6F42",
+        "de_type": 820
+      },
+      {
+        "contents": "94AC3E25F14015AC7C7AAC7E611A931FAF59A447F1D3AE",
+        "de_type": 708
+      }
+    ],
+    "encoded_section": "023B3CCC62107DBA3672E653B736AC923AA99D7B440C792BCA1D85378A240F0D766A47C965608B564999D496CDA5185BA9180928BCA9C43D9CDE40A6F8B5E52141EE95718009505ADE7B0D5285867B634ADEF94FC38951BD92B98C4005CE71ADFEA83040140F00688181B3763809B99366E69077DF00ECF3CE124D629BB0AA5F49A2378DCB05EAE7F9D64B38",
+    "identity_token": "53E15A1B5692E1BE92BD953C353F7B91",
+    "key_seed": "8DA28C3966FAA33B3C7302CD269126F56D184CFC10498ED7969A288AFB0DA616",
+    "nonce": "F4839E1496794C1F01572669",
+    "section_mic_hmac_key": "B65A6625E49603D87BB9CDF3D5B0D4C2DCDB4536F45A953A67FE6F101C1AE798",
+    "section_salt": "3B3CCC62107DBA3672E653B736AC923A"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "EDA860E0707ED96A1172A45CC0811384",
+    "data_elements": [
+      {
+        "contents": "29",
+        "de_type": 305
+      },
+      {
+        "contents": "B72753",
+        "de_type": 903
+      },
+      {
+        "contents": "6983195D8AA985C58DB6DA9ADABBB38DB3917430F36372",
+        "de_type": 540
+      },
+      {
+        "contents": "BF901CD8",
+        "de_type": 951
+      },
+      {
+        "contents": "A4",
+        "de_type": 859
+      }
+    ],
+    "encoded_section": "021026C610CCE881C1A6126022EFE069A66FDB75AAE177D28EAD4AEC586AE134F23F54F49C0E7D888DB5DD850D204030D83C303EC74ABEA7876345F687BBAC70AC0F1C47D6FF493166B00D002A6E7B89CDB7E10E921F081D8C674D10E2D2633E92",
+    "identity_token": "A3D9812AA67DE7AFAFB41B411582B2F0",
+    "key_seed": "4A23659FB7CFA0A0E73DE712ADD01F8FA0E7215FF8CFBD35771236B60A387011",
+    "nonce": "53E571E0DF89BF6C7216C7C2",
+    "section_mic_hmac_key": "5ACB14F2E5F318A1C9D769FFEF152293CF1A0A37B735153951DD236CA6DAD527",
+    "section_salt": "1026C610CCE881C1A6126022EFE069A6"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "6AED67F14438D7D911EDB8D722F488BD",
+    "data_elements": [],
+    "encoded_section": "02F4CA6005D5468E98560AC1A7EF01DEE90F3B25A4E8592F10463ED297782C65DF10AA8303AD3B3F647B0B35AB90E3751E3A",
+    "identity_token": "DB6F1DD46763D8721214EA515B47CFFA",
+    "key_seed": "F86B31559658B13CC4384D00DDA8CA7924F7F545DCB973E94AFA03D006B8E305",
+    "nonce": "A7702BAF126B2CBAE121878E",
+    "section_mic_hmac_key": "1AE36FC285306CC764C77EA289920D5428518672A3B9A5941C02DC4D2C836231",
+    "section_salt": "F4CA6005D5468E98560AC1A7EF01DEE9"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "6B926CEBE38D4132E4BE5532867BD395",
+    "data_elements": [
+      {
+        "contents": "67B6BC35D8B35057BFC81EE006076C575766A1",
+        "de_type": 148
+      },
+      {
+        "contents": "3D2F96ADD78DEE2E928A2A6D1452AC9B5C",
+        "de_type": 830
+      }
+    ],
+    "encoded_section": "02C4D4D2A92767AA88EC70C16EF70254C5028B41E18F6CFE231D87F6DF5CCACB1E3A2A223140474CA5A48FC80DEF2A64251E63A8645D1B338045BE4F1755E31916DC8169AF62BD094E015DF416DCB929C6FE9419C0E553CCB48BC8B8",
+    "identity_token": "9BD9767523D8349176EB79FAA07CC03F",
+    "key_seed": "9D09EA97DF4EEC2BB30BF025A9E6B81BD3EEB19870BBA43A4EEE452066BA7F03",
+    "nonce": "5B6638F881B5983B1A5E97C2",
+    "section_mic_hmac_key": "8F6F64C6F8626EA4F63A0B62B87E5A9F25A5ADE408E1E829E06B952BFC1FC419",
+    "section_salt": "C4D4D2A92767AA88EC70C16EF70254C5"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "BBCA7CCF407D135B2E4E30F2F2D5BECF",
+    "data_elements": [],
+    "encoded_section": "021AA76A19C77A439E19ADB69D70B0DB1C25F4EF40194EA5874083980925FE7A7710C4C42DA77C168CD699E7ACE28920571B",
+    "identity_token": "5350E2CA3B7927CADA9D6F2B02C28310",
+    "key_seed": "1B679F25EB8C387B01FFB315A1DB04D8981936963BFCAE20BF4F5524C1F762AE",
+    "nonce": "182CB56F4E9A946E490A066B",
+    "section_mic_hmac_key": "F7683F77117946A69929DBD6580EA3311C1DAF13C3755FDF052DCA96D0685970",
+    "section_salt": "1AA76A19C77A439E19ADB69D70B0DB1C"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "52C67744CF1B5AE97F091C915CFAA6A4",
+    "data_elements": [
+      {
+        "contents": "0A882289B49B",
+        "de_type": 860
+      },
+      {
+        "contents": "38DB2A68327F6FFCDDCB2DEAE4B4D7C1C68FBD52F18711A8952AE1CE",
+        "de_type": 586
+      },
+      {
+        "contents": "E89FE85C63F175B22F6D373707D9C51F7D062B7278DFEB7F2699041DC4",
+        "de_type": 403
+      },
+      {
+        "contents": "FF6658532AD2EECB814EB0A5BF41",
+        "de_type": 63
+      },
+      {
+        "contents": "FBAA7E6F8C867A50DA6EB4F406B7FEEFC2378AC3CF45F1DEE9742FC9",
+        "de_type": 910
+      }
+    ],
+    "encoded_section": "0240A2448B3046FFE23A82D40AE027B410210C3D699F8057821BCB3C7465565CA087A7250E582F9AA365F5FC4E35F1F1340926427605DF90EED725463E57D16453CA4D3AC95BF35434676E3AA1616176E89F53AC9224003690DFEB64DB256D9DEFEDDB93D9393DEFA3A5BC664C8A234118E5548A6495334FC717CBC4571D2152B6D28FE4EECDE4F3C4179C62CB02BDE28EC7A55C029E3030A30407028FE1A4CE0925F431A2B14437E6",
+    "identity_token": "44CFC684E6C7050263E2CAB4D744A814",
+    "key_seed": "9A30AAA2B9A12B59236DB28F6761EFC40CCDDE59A07518BDB19D45BA2B72BF26",
+    "nonce": "AD0E77BB4D951804E67A70B8",
+    "section_mic_hmac_key": "CEB035A541C1D12C0F02B861402E73D21C8F4C2EC5C4C8DEFFD1623EC7FCBFDD",
+    "section_salt": "40A2448B3046FFE23A82D40AE027B410"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "23F75DC56095B764A3367B0BAC2CAB3A",
+    "data_elements": [
+      {
+        "contents": "3ECF",
+        "de_type": 825
+      },
+      {
+        "contents": "A0A7F010524BB3B0CF95D62762830DD0192486B2C21E",
+        "de_type": 307
+      },
+      {
+        "contents": "E58DB864D613F4CF4B613B0F9103BC14841C7B00ACE1CDDE594E448F",
+        "de_type": 769
+      },
+      {
+        "contents": "84E31D1747DA456817",
+        "de_type": 98
+      }
+    ],
+    "encoded_section": "02E1422169AFD9B4F1C931B65EF8BFCB907E66B43EB676DCF362B31F339BB43B1A58B6D6E192491D7C136397078953ABB335D77668F6EC744FD67142A16C9FCAE8761E4F8388E8B0A2DA4037AB767EDFF9A9A30E209A8D6E2E23A371C4085780A50EDDCD91B895442DB896B38455B9221B3096C69684C14599AC",
+    "identity_token": "10D4BDF9C47D2AB3EC885FBD625C0246",
+    "key_seed": "1FE48B81802881434FD77BF3C21EC0E8858AF9164C27C74D0F4B362F0EB5C528",
+    "nonce": "833F029D2BD54533FC3E70BC",
+    "section_mic_hmac_key": "78F4C0AAA20F8DA2EFC963344E5926EE8A07494B48E778BC158B3997AB2B6285",
+    "section_salt": "E1422169AFD9B4F1C931B65EF8BFCB90"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "7617C16EFAEF5EDFB06625756362C2A0",
+    "data_elements": [
+      {
+        "contents": "E5D673BAFF29A4D3C1",
+        "de_type": 270
+      },
+      {
+        "contents": "6A57381D",
+        "de_type": 887
+      },
+      {
+        "contents": "0E556091078C6748C36BAF40BBB4E4BB",
+        "de_type": 205
+      },
+      {
+        "contents": "2055F5870F08F9CD3EFA17955C99F226404A",
+        "de_type": 383
+      },
+      {
+        "contents": "C8A4A9A3FA548102A1F96327A4287A70D500DE35862AEC8E1AC6F1DB84",
+        "de_type": 628
+      }
+    ],
+    "encoded_section": "0279C36005CB2893825C08AC9D00C707FABE695CE889681CF240A6D97597ED0E7E6B4B7EE58F722D9DEA51FF86226C332E4917FA9E0724B39DBC3C1C9425F4CAAF49019E646B21816BA6B92701E19118AA31321C1ACC9725D4B7AECE13F56161A978E8F646FBEF9A61498F657340BB21E8D96E8F1980F62FF69F92695C6164D59D271991E38B4367DED244F3C4",
+    "identity_token": "F65402F8A50A61A802B8D5C12F7B6139",
+    "key_seed": "91E49925060895395298594A49F168FC765017D07C6E7A111FCED5C43A0C3A51",
+    "nonce": "5E14CA25F6C86D3DAAAE3A82",
+    "section_mic_hmac_key": "F81460B1DA857EF01821651349218A0B6DA51DBD2338913621264D75C0EA0BCE",
+    "section_salt": "79C36005CB2893825C08AC9D00C707FA"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "7D5C69BCCFE498E5464BEDDEFA884421",
+    "data_elements": [
+      {
+        "contents": "445E8055",
+        "de_type": 714
+      },
+      {
+        "contents": "2098DE470E2583D9FC2E040859F58B2C45D3",
+        "de_type": 769
+      },
+      {
+        "contents": "72BC2F855131395E0C6C0E5814B02D406724A11179EC",
+        "de_type": 799
+      },
+      {
+        "contents": "7456300CB3DD5E",
+        "de_type": 241
+      }
+    ],
+    "encoded_section": "02FD4D5055F7192BE8BA25A2FA5385B5D524CDE482D669C0E529D5308E322AE8A14F3A9DDC777769014856F0A8E6A042F1C05FB685E2E14E6B63F18556036E4D68A37E9941D7BD4F583C469D5F37105A22BE25B994D2227206DFE0922276D6375D480C08DE38B82D8DE92B0ED8C7AA1A37",
+    "identity_token": "AE507BDB86B14FE8EB76147249EC5865",
+    "key_seed": "797EE27B2E559335E524718A4346DC8823367D14A7D7242B0645A103D1E73391",
+    "nonce": "13623B08CBFD928C92254CFF",
+    "section_mic_hmac_key": "409F3A5462F5B64E15C9264A981ECEDC489C5779AAF29CB5523714DBBB82A06E",
+    "section_salt": "FD4D5055F7192BE8BA25A2FA5385B5D5"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "F5F75410EF7DC53152BD2130C6327171",
+    "data_elements": [],
+    "encoded_section": "022252124D2BC9B6498FDFCCD0196CF4C56B993255169168AA337B00796E3C0DDA106B5F1F519ADC5898F7F5661895462E16",
+    "identity_token": "E18F1D6F8CEB5CF84C941AA47C728D66",
+    "key_seed": "4A01C7DC09BD3999C74B95A6B50FC0CFAFEE1E36C260026D0DD46F21C56D82EF",
+    "nonce": "497A0BDB69B6E6258A1941A3",
+    "section_mic_hmac_key": "B4D73916A49E25F62EC59C9E9B24A347EB1A5B95694681467B8FF6EC79A3EC27",
+    "section_salt": "2252124D2BC9B6498FDFCCD0196CF4C5"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "3A64FA90D74450D083302E2C059C9665",
+    "data_elements": [
+      {
+        "contents": "CFC759543DAD41C009FC402A",
+        "de_type": 988
+      },
+      {
+        "contents": "786F5232031993953C6A9A08C1976E8C30BFF38FA84531086FF19DFF",
+        "de_type": 846
+      }
+    ],
+    "encoded_section": "025F023F5D79C52E9DBAB9F0DA69237D642DA017E11913BBB351FAA7067EB3B06A3E8BFB15DE59E4CD1C3444935A180C26E58FCD08650970B216E1CEE4E8E96A4EFB59E03AB6409A5FBC07282CAF51181B8196D0D6DD95D424076EBA7F4C92AC",
+    "identity_token": "71E0154EC5B0D069EF2CBDBE4D4F64EC",
+    "key_seed": "076E48318DBCAE9AC52822CCB8C328E8E526278A881C3E777285843D900DC7C3",
+    "nonce": "B81706D97EDB8A2F21130E10",
+    "section_mic_hmac_key": "5F5F097D7C90D9DD993E554E3620ACB5E32EEED68D0A70C8D0A96B9E8FCFCE0C",
+    "section_salt": "5F023F5D79C52E9DBAB9F0DA69237D64"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "B443584BA0695A5DE100FC7E4471F096",
+    "data_elements": [
+      {
+        "contents": "8D07D7C161B1E3B9AD44036C9152",
+        "de_type": 568
+      },
+      {
+        "contents": "241997237B",
+        "de_type": 304
+      },
+      {
+        "contents": "EAE8BF35970B3FA758ECD25FAFB52DAAE97E90AE207A5AE1F65EAD0D49",
+        "de_type": 240
+      },
+      {
+        "contents": "C3BEE2960CA3C9BA732858B26600209CC17E277BB91578F7F992A7",
+        "de_type": 923
+      },
+      {
+        "contents": "FAC026BE6D3446FD6C7EA221B4",
+        "de_type": 282
+      }
+    ],
+    "encoded_section": "02AADF94195BB7269EEA6B86F55F8F625EBAB866B50BDE8C3963CEFD8AF4C6415F77869D51C5DF0D1BE02D052056B0A8EC667DF3E294C898501276BB22C0657468C4BA18A25F35C4C3EE4A233A77C5C3C77F23588264BDE91EE9D0B00735C5A5DFF02F97849972CE1FF1CC5DC22200D33DF6FD1957F9CFE6C42242B63563BEAA654E63F10611B4C43262949BA084C9ADD9DD34430D71A64D68",
+    "identity_token": "F73F1700F798A87559130AB945C74741",
+    "key_seed": "DAC1B91B10F16C4398EF7C8058ACCAEC2A9B30A0FB9AD66E121DD19CDAEB37A0",
+    "nonce": "2EC6381E4B347BFED3E91754",
+    "section_mic_hmac_key": "642792EDF30489843D0FE58973082D231FDC14BE72A4C007BBBBF85CBE8757A2",
+    "section_salt": "AADF94195BB7269EEA6B86F55F8F625E"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "83EC80F024DF21B65CFEFD1EF94740E2",
+    "data_elements": [
+      {
+        "contents": "27A69E731F",
+        "de_type": 287
+      },
+      {
+        "contents": "DB716058E34CF7B472543BA8",
+        "de_type": 576
+      },
+      {
+        "contents": "5672B7B1AA5E483968D00E77AB12",
+        "de_type": 124
+      },
+      {
+        "contents": "F46774",
+        "de_type": 705
+      }
+    ],
+    "encoded_section": "02D525060ACC8BA94C63CDDEC60BEDB5AD791F43370ED6BDE4CC9BAE70A03E4C9B3D33536D52A575D5C02DD67E91BED7C3DE179AFC1C035397388F48297764EA4E8C329557E6A71FE5A63379A8618777524C2AD19E0A9BF1F65F6BC54C076C",
+    "identity_token": "DCF0C0BC6CF0AED2BB98005F9BD314CC",
+    "key_seed": "40402744DE9F4976A2DC489058A6E3392FB0B9835DB8C7018E678B3406C4441D",
+    "nonce": "02DA50D948182D528619E7AB",
+    "section_mic_hmac_key": "035640B10657D96179E987095B1D01D9A98C6158A98726FDDFADFCA582268F9E",
+    "section_salt": "D525060ACC8BA94C63CDDEC60BEDB5AD"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "0418BBAF50BB908526F913583233E243",
+    "data_elements": [
+      {
+        "contents": "F45CB09C4F9BCC64CEA8729CB757EBB30B9F35DDA00494D9DB2CFF53ACAE",
+        "de_type": 909
+      },
+      {
+        "contents": "50F3A9F85CE86BB46570EC179649CACF30E68184C26F0EF029D88AF32869",
+        "de_type": 47
+      },
+      {
+        "contents": "036508751C50D9C40FF288AC9CC81B46D0CA17",
+        "de_type": 498
+      }
+    ],
+    "encoded_section": "02CCE7901E20E283CD0A9CC73E4EECE408A6D1C01EDC8574E8FF9F2D4E1F07CB7267AC537E5BC0F41CA06B85E63818B10F5B012DB59C0D127A1AF043750A04910CF3E8CFCD245190702461AF431A690891BA9B37146990A0A330674F9708D25B3730D5B23A4DBEDF46694CBF3D7AA4CE6C35C8CC833A22B82B74A3858DCBFC262C065EB5A5DBEB36B7",
+    "identity_token": "F8D68732B8CB236625F2F9FE6EC1DC4B",
+    "key_seed": "A2BA315C4719A6F2F908D6662DBBCB072B283D419F9A56B79B2B6233E96C21C7",
+    "nonce": "1B41FBADE98FA042E9FEC7C3",
+    "section_mic_hmac_key": "72F23D0F266F564D8D5A2545426A7CDDA3F7D20D43E372CB6EE46D85871537FA",
+    "section_salt": "CCE7901E20E283CD0A9CC73E4EECE408"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "E2EE6FE0CCA11951F4854DA7BDA99ED3",
+    "data_elements": [
+      {
+        "contents": "C40552987F158C0CFC56EBF3",
+        "de_type": 321
+      },
+      {
+        "contents": "",
+        "de_type": 503
+      }
+    ],
+    "encoded_section": "020E4D5C6ED93B014055B8951F8FD220A57D827187D544B46DCD6ED9432B424F4122E01AC41591CB4DF89F5D00410412A7647452BF4AE9865BC9F337CBC12706F1881985",
+    "identity_token": "F46324863C329E19C0522E7AFB0D9B82",
+    "key_seed": "43766BADDE1B80B6598CA446112A8FC98DD42EAB244B3DEBF08302D7BD4FD376",
+    "nonce": "FC43822BD63D014A2ABA6479",
+    "section_mic_hmac_key": "DF4154082EFCF4F670B87A36A31E095ED7B7AFBF8C324D9F8E0D29E1EEE8F7FE",
+    "section_salt": "0E4D5C6ED93B014055B8951F8FD220A5"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "77F690701DDFD7AAEE15B393CA8E4113",
+    "data_elements": [],
+    "encoded_section": "029E470E6DD0076A256D60553EC207DA37F38577FBF8CB639C6E8F84A4502D5CAD10B4F17F4720071345C051290A66EE1347",
+    "identity_token": "AEA2B76D803628E0426E64AB7477B395",
+    "key_seed": "13154557990A80FB24A70BCE09BBF62DAC2C95146B1163508D9CACEE89C75D96",
+    "nonce": "6B54C468B6CD60C9BCF05C06",
+    "section_mic_hmac_key": "7C56EF628CB49E0E3439DBE47034A4EE4D74CC431D919E7ED97F31B7C4680D00",
+    "section_salt": "9E470E6DD0076A256D60553EC207DA37"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "CF0F755BC0929BA3338B04E0126BC444",
+    "data_elements": [
+      {
+        "contents": "0AA5A94208D57B66ABE5B8341167C1544ED91B35C6",
+        "de_type": 695
+      },
+      {
+        "contents": "3E39B499E2B74671EC3A54CB",
+        "de_type": 920
+      }
+    ],
+    "encoded_section": "02ED8065C1778DDB635023F96FC9DB4B43C2FCFC10BD074146EED51E7F9A46C9C437F01690BF9BAB7C0A9964BFDAFF313CF0B46D6387B78922EC76F009B1C9D11C103F54DB4B6C2CC32CFCE97E2F3349B91AF70279128DDE2D",
+    "identity_token": "0A257DF8A7692B3249407E160C037F32",
+    "key_seed": "3F64813E8A4B3DC815206ADADF36DB6D66B9BAD623636491CE3A47A85A80ACE1",
+    "nonce": "E1AA1F6A438632C46B33AB73",
+    "section_mic_hmac_key": "A8A1D0BB14D04085F5340F1ED0AEE5C2201BD4EC668991370E804DA18EC94CB5",
+    "section_salt": "ED8065C1778DDB635023F96FC9DB4B43"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "08851035A5C0B4211E613496BAF5EB58",
+    "data_elements": [
+      {
+        "contents": "C0D408E8EF9DDB0E755EB45523D250D58B7F5B647721C03ED8",
+        "de_type": 21
+      },
+      {
+        "contents": "0BD5FAC43AEB6F8CCEB0583EE744B80A897ED7558D57DFD06BB81C",
+        "de_type": 323
+      }
+    ],
+    "encoded_section": "022C1DC1FC780C25AA2E4852BA8CDB2BCB44DBC4820A019D95184F42A70E419861498AF4A6CD8AC3D9E110C0021461F9020233FBF843404099F378366DB902EB106947263B984BF7D40116604F55C42DB12F25C686B250E139DC1C994CDF5E7C1CF308144E294155D20AD3",
+    "identity_token": "776B3A62CB93BA94651552C97A2E4267",
+    "key_seed": "9509B69A6A2BB6CCBB1A1E7436E77AFC2D00A2FABD2A1665A7BA6FD6E33354B3",
+    "nonce": "42DA14BD4FA470084787B4EB",
+    "section_mic_hmac_key": "603743F9A5815E17229E364C363DEAB2ED6341B86E6D6A4F2F723629272CD0DD",
+    "section_salt": "2C1DC1FC780C25AA2E4852BA8CDB2BCB"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "B60EBD62633BB487ED9F7D9E426A0D96",
+    "data_elements": [
+      {
+        "contents": "7A0AC4F78A416F5B0114535909A5307F",
+        "de_type": 902
+      },
+      {
+        "contents": "EF581947C68D9E50AA05",
+        "de_type": 46
+      },
+      {
+        "contents": "FD",
+        "de_type": 888
+      },
+      {
+        "contents": "FD",
+        "de_type": 315
+      },
+      {
+        "contents": "CEDFEE9059F500C97D25C94F",
+        "de_type": 7
+      }
+    ],
+    "encoded_section": "02DF453EF92630EB12DC02E3EDCA4A36A08BA4F34CBFA1B595CB3A6D01C58724D0455434E8D72455F125B81A186A41FA589EE9710ABD7F9F67E011912FD0DFB2337306AC59112298BA20EE5FE66227AB94034D13388BB4484B8A6587A31F0234024115F6D30CF9",
+    "identity_token": "A1C908312B87892BB9828075C672635F",
+    "key_seed": "6D32B114EF3DF0128A529FDF8B2E7EFBAFF451FAB30302DDD8BCDC811D0FF0AD",
+    "nonce": "720138995818A3EE421768BF",
+    "section_mic_hmac_key": "9243F2687949CC4D05AEB4D8FB77890569AD61591E37FF206959B6CF98342BEB",
+    "section_salt": "DF453EF92630EB12DC02E3EDCA4A36A0"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "DB1A823A46931F80A323BA830F003123",
+    "data_elements": [
+      {
+        "contents": "8100834C61C6C93430E5C7BD4B86866C074468",
+        "de_type": 0
+      },
+      {
+        "contents": "CE623A4184335F6C9D55A7A2FE02478336DDF05D8F4BFFAE",
+        "de_type": 238
+      }
+    ],
+    "encoded_section": "023AF28C09CAEC840269BF5DEEEB2AA95C60E64AF819E6A97B8ABE980FD7320593400018850FCA6227B727F1ECFEFC2B74B62F21E44B40B14500E9736646713FE6B65DBCEA9A39CA410D4F4E7C25FC753BF4E89064485C09E8B30D936CF3FC31E704",
+    "identity_token": "0758CA3E92715E8088FEDB4D069661F7",
+    "key_seed": "46F7E0CC1B0AFF6971DE8E4AF1BEDA76D5FBBBC1673D67D5E3592207241B03CC",
+    "nonce": "CD05581E1D32C9D6F251E1DA",
+    "section_mic_hmac_key": "7EAE309E12347554A36BE7F5416BC98458A6BBE1CE11812AF7E1E0170942DE7D",
+    "section_salt": "3AF28C09CAEC840269BF5DEEEB2AA95C"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "094545346D6DA2AB87E9F21C22EBEB5A",
+    "data_elements": [
+      {
+        "contents": "FBD5BF3B86FC4DF98299A04516E245CF69BD201E44111D9F58835F",
+        "de_type": 962
+      },
+      {
+        "contents": "7A00781BDED3EB83ACB0F347A49F14D039",
+        "de_type": 611
+      },
+      {
+        "contents": "42A41A496D6BE276833FFFFD2778E555A4ACC09190B60594CFAA1F",
+        "de_type": 992
+      },
+      {
+        "contents": "F86971896012BD",
+        "de_type": 517
+      }
+    ],
+    "encoded_section": "02F8F5300B7A01F96A0B73610D4A82F877EC68C96FE249E235C18FC5856E98E4646AADE7D3002AA4F39AF7F2113392EE3C6521AAB6EDABC8A5C5CD33275355C434BA00554158FE2207C510267BB6A50B721D2BF1DB8A832CEE8C1D09CBE920E9B9AE68686B7393211E5CD0943C97A76CA8A0FD82B18CE06A7ED92FB378BE450B6930A6F241B1D2696E80B598",
+    "identity_token": "6F95D140C0F87F34DD4CE7CB17CE8609",
+    "key_seed": "0CCB26C7DB958F8435D66E11D6A68875DA25E7D12A11EAA58386A2290A42027E",
+    "nonce": "6DD2B018A20FAA4E46703F7B",
+    "section_mic_hmac_key": "D5A0F725FE6E6D8BDC79C2C668196D3303D6465C92D478BC676F91C1A462E024",
+    "section_salt": "F8F5300B7A01F96A0B73610D4A82F877"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "621702EDC90A5A8954B64A8CF883DAC1",
+    "data_elements": [
+      {
+        "contents": "09",
+        "de_type": 482
+      },
+      {
+        "contents": "D5",
+        "de_type": 698
+      }
+    ],
+    "encoded_section": "02B14D7C2C925F3E24ACB1E4A5D4DA6F193FA87DDA8E2FEF20F01FFB5ABF3B8CFC18B5ABC3BB0FA0C46785729CFCB9192442D7DCF702EFC0304F",
+    "identity_token": "9E6F8A6063A6BDD60D5954F592A3B8F2",
+    "key_seed": "C62E96131C7C8B805CC9FE6FD634CB9BF84BD4984529B30576B3F3856CF04E63",
+    "nonce": "0FDA5313782FED9FF7E65C4C",
+    "section_mic_hmac_key": "89023EED69C4E5DA5ACD11E15AA540323F2EA47A19E4937C19A10DB89BF9A735",
+    "section_salt": "B14D7C2C925F3E24ACB1E4A5D4DA6F19"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "B5CE72C63EFA9DD095716472FAEE0784",
+    "data_elements": [
+      {
+        "contents": "DC06F9FBA5ED966657D9CFA8667DC006850CF89899DD",
+        "de_type": 989
+      },
+      {
+        "contents": "F68AA9DFC06B7EB7D9CBBF5EAFA968984A100C2B03C504A5E1D9C7",
+        "de_type": 706
+      },
+      {
+        "contents": "055D1DEDA05C238D7A31E5CCAE",
+        "de_type": 379
+      },
+      {
+        "contents": "5D1B64B0D53EDAE32FA989CCC38E1B2ACEAEE0930EDC",
+        "de_type": 221
+      }
+    ],
+    "encoded_section": "02E1630DAC1DF049E06A3020858C0857589FF7482D737059F5C999422EC552C91670AA375473BE99ADE7BEBCE8F54E3EA98B2C36D02BAF87EAACB81D0C461C824138DFBBE8BDF53FA7E19F53BEEE1EC2E228956F551335C7BE1FA38CA5933DE6AF767650F6B00C4CE3F7FEC0ECEAAB12811D79317CE035BE9E5E19522A1E329CB0D9B6FD0275F619D95F0BFDF8F071C4FB0A",
+    "identity_token": "57EABE3C09AF926FCAC347ABFAD9E33B",
+    "key_seed": "D3E7B4BD3F00BB656B34B68A9B57D3499B1690439F52E39BF55145069B69E012",
+    "nonce": "70AA23057C2F616829EB283B",
+    "section_mic_hmac_key": "6DF923F1C439D6DCB1D17267F1DCF9F77974CA0CDF3815525FC480F56FBD46C7",
+    "section_salt": "E1630DAC1DF049E06A3020858C085758"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "E257258A3F83CEC8750E338556A3806F",
+    "data_elements": [
+      {
+        "contents": "FC2AA7F38F27003ACE8033FABCE80A1A0A2855",
+        "de_type": 534
+      },
+      {
+        "contents": "93CFA0A1BF2C1517B833EE189ADBE984348FE43D91B738D1D51B1A52",
+        "de_type": 317
+      },
+      {
+        "contents": "4883BFDB2678",
+        "de_type": 899
+      },
+      {
+        "contents": "EF90D2A4545867C3",
+        "de_type": 654
+      },
+      {
+        "contents": "37C02A83E008FA26",
+        "de_type": 532
+      }
+    ],
+    "encoded_section": "0289F842808FD5738C338616A2560581A8A52F4A6E16A6AD73CAA41028724B501D64C5800CE30D76F8D3BAD6CD098CDCE3FEB9F4425CDC836605063FFC21BCDD041E0158EB7478416478361D4E2073A9E1C61EE770E179998E0211D0B225F1F59C2B9392F5843E3E4F99716B356E52081797EDA99115FC8A0012D55A0C3D119117D0D98F57AF",
+    "identity_token": "80FAE8E4817345A5115FE9556AA13FE2",
+    "key_seed": "C6044A22FEF3D9547667D2B2E2BDB95FE9E436B43F7137CB775B790BBF1C5914",
+    "nonce": "DCB6A5793686750BFF4725CA",
+    "section_mic_hmac_key": "B2A815D44780CBD0317F016087313B40970A52496DAA6B665EDEEBE1287BC499",
+    "section_salt": "89F842808FD5738C338616A2560581A8"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "C92370BF646709CC74E15195B1ABABD1",
+    "data_elements": [
+      {
+        "contents": "BCBFD4CA484D111FC650E199022CEDB4472F4D9F59C27551835889B9",
+        "de_type": 89
+      },
+      {
+        "contents": "479B03",
+        "de_type": 87
+      },
+      {
+        "contents": "7491B3382FB4A3",
+        "de_type": 857
+      },
+      {
+        "contents": "28006920EB7BE921EE08CA9336FF3707D9EF2A7D46FD3CB35978CFCD0C72",
+        "de_type": 714
+      }
+    ],
+    "encoded_section": "02E87E2FF9D4E8BED3A34DE8708A523395A9FDFF7E97F5E5222F1C4B3AA8AAF6845EA536945D4DFA8BFEDDC75C93B34610D1621CCE80013B78B90E0E007523EA372201FA31AFC18C88A789792AE6E37CC5F054D82642578AC1DAC9EC7A79931FAE1F296A85C216E879CB49D5438B4D9F7342F63576831F37CBA052BEF9674439",
+    "identity_token": "08ACA51C40D663F21A4CF2BFDAD1E502",
+    "key_seed": "D4B0FE67999329E23C3217DB7A469FC35280B5978CE80E532C08B59BCEE4EF00",
+    "nonce": "8FCF4745A019605307496CCF",
+    "section_mic_hmac_key": "E2CD200FD23CE0BCDB1CB503048CE71B4E7E3F150D5658F8234B6C6D28893749",
+    "section_salt": "E87E2FF9D4E8BED3A34DE8708A523395"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "5787FFD3CFFF8F69FE45E50872B49181",
+    "data_elements": [
+      {
+        "contents": "8401AAFF665AD9920B6B33B839A72F",
+        "de_type": 237
+      },
+      {
+        "contents": "8B88158B1F60546E56508E86",
+        "de_type": 969
+      }
+    ],
+    "encoded_section": "02E44ED613C98F0DC2AE5C4323042CD36D21B0A7C29E85FB08B05F62E9EA5FE3C231E9F3572CBD15F02D0A232F930664DE8A8D93CD63BF008526F7F60015C1BCB75F2BC2D12AC600CE2E0F87AA96C8E54BF99F",
+    "identity_token": "19D4435134937780CED35A279D095109",
+    "key_seed": "6C41D17E9C408D509BEC4EA6716F010BC72B801F9545BDF8F9DB9539AC6273C6",
+    "nonce": "189F567A1F62F04507652C14",
+    "section_mic_hmac_key": "D1B6432B61BB4272E325A6979882FE9BED562061A383C648C3294B19B707169C",
+    "section_salt": "E44ED613C98F0DC2AE5C4323042CD36D"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "5533BA11358651AC5301BCC7DFE8AC6E",
+    "data_elements": [
+      {
+        "contents": "B775D5A14D08158FF431E168F10515145B07",
+        "de_type": 798
+      },
+      {
+        "contents": "2FE686DF80A9EBFE2405D84EAB72016B882AB50A11CE9EE5ECC5",
+        "de_type": 430
+      },
+      {
+        "contents": "1276D64D40BBAE244D398A82396342",
+        "de_type": 482
+      },
+      {
+        "contents": "C8EAFF62E466C78817A3747427C22E68750507B744A6",
+        "de_type": 823
+      },
+      {
+        "contents": "C964A89C33D058E2D533",
+        "de_type": 773
+      }
+    ],
+    "encoded_section": "02A25AF6F97A49FD5AD7A58AC7C618CADCE311355595A5534B1F7F0BAADF291C8A7AB1895AF21700990CD60C3B451431E563A6D614CA7E04A84935698C0401CF79D74AA6DD6B65E23E6B0FDAA670D41B995EE23576190458B823A743CC915471252B601682AD93237C3B986070F377F4E1254953760337B10F55882E805E44447AE30E32DD4111B1BE46735F9409A8F57BFD89C9CBC8389EB5DC220B",
+    "identity_token": "F8378536329D2B2E962DE8AC894BA112",
+    "key_seed": "195197E02A7D9C2F04073A07987A7FBA10B4EAF66D69FAB54015A68960933535",
+    "nonce": "E3647946D8FC39BCBF6CB625",
+    "section_mic_hmac_key": "1E35AFE30548CE05848D3E362CD077E2DB826E10B217D21C3026CD2D91F1CAFC",
+    "section_salt": "A25AF6F97A49FD5AD7A58AC7C618CADC"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "E8EA3502A9010273177F4A0273E3B568",
+    "data_elements": [
+      {
+        "contents": "",
+        "de_type": 93
+      },
+      {
+        "contents": "AD7FA1E4C83E494CADC0E105",
+        "de_type": 591
+      },
+      {
+        "contents": "8085D6",
+        "de_type": 425
+      },
+      {
+        "contents": "267DB04BA141F8A0B9D16F98F815AA051AC5A2DD18AE2CBB5C",
+        "de_type": 719
+      },
+      {
+        "contents": "0C4AD34F70D189FB9B6B93A52E0804BE81CAA18CDD",
+        "de_type": 272
+      }
+    ],
+    "encoded_section": "025723B7208ADF96D9B8BC420F33486A6C8B36F43A0470FE4B458949F34055F1E75BAB75AA88F163CC5E6DCE56928779AB957A749284BD8B7B84527BF9AD52C4A08EF70D4CD2B76BAE77533DF9239474DCF758373A9D0EB08BD383A582626E70F39871439F29B764B9348F6B9984421D2E7F6A13C62B7164CC9219958E",
+    "identity_token": "655638465094190FE9A77463BFC9E126",
+    "key_seed": "AE9604009D3FD6E950A44CC767AE10C22A6744CCF2CD8E0C76E76385E6081DD5",
+    "nonce": "228A038D1F89336A39CD594D",
+    "section_mic_hmac_key": "ECB8CB36E3E179D9FD78006D46C85058F1A2BAB87014F58D792382331B7FC062",
+    "section_salt": "5723B7208ADF96D9B8BC420F33486A6C"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "489C9B6D1094A881909199D2F70129E9",
+    "data_elements": [
+      {
+        "contents": "A50C355E004E703AB3E11199F9F7042FB8B297A8EF63EA692AE83EFCFD10",
+        "de_type": 636
+      },
+      {
+        "contents": "58069F92B86136E4DAFADE70D940D3C997D95F94",
+        "de_type": 434
+      },
+      {
+        "contents": "FE0781315E780B0C45D4B4B1",
+        "de_type": 577
+      }
+    ],
+    "encoded_section": "0271B85F63AC3C61F03F39EF0E169D1D82FCF69C3E0CE5EE7830BAF677CAB9A146578963EA76613CC2A8B79EAD6AC940394D4BB937993A3C29BB3566FA8B23B9F6C035CDF9CA49FBAAF3B9D32E7D177EEE6D5E30D64DA436E0A6F3822B94C8F9DC84F77D0546A0728F6D4A0C0886DAE3C50D217601E697BB48",
+    "identity_token": "F795F354C5FEEBF98A35AA27BF4FB65D",
+    "key_seed": "9E41319F2ABE29937E2BC9AADB59DF37DDD64CC95470A1FA92FCD92284C49C07",
+    "nonce": "8D134D36CACDFA48AD2E1624",
+    "section_mic_hmac_key": "8F01AC5CEA8B33DC8E3A6917CACE845E3113D054A6F190CCA4F8B19B00789CCE",
+    "section_salt": "71B85F63AC3C61F03F39EF0E169D1D82"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "1AEFDBD92CF0BFDD813CED1AD930424B",
+    "data_elements": [
+      {
+        "contents": "89",
+        "de_type": 537
+      },
+      {
+        "contents": "B5838F8BFF3E1FF8CBE34A",
+        "de_type": 148
+      }
+    ],
+    "encoded_section": "02AD263B2C711BFC36EDFA522E9607CE18DA76E578E9130C9FEE33FEDA62BD179322CF68031C2EF90E092EDB3707CD7F86FA9CD806C56ABC93321E301D4986832966C748",
+    "identity_token": "03785872BE7F3F5A06BA8DE5A547A0B6",
+    "key_seed": "E3D6F78BC1A45CB75B06E63B81733A1B31DAF8AF1E4790B884599718BB790412",
+    "nonce": "67A8B67A65C9C4D46FCCDD11",
+    "section_mic_hmac_key": "6C89903E5DFF8B5D2CF1B2DE9EDDC78FAAC03B5EBCC77B9CF810A9F29E6AB15A",
+    "section_salt": "AD263B2C711BFC36EDFA522E9607CE18"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "11FEBEE577A641BE1BD6D7A95DA0DC61",
+    "data_elements": [],
+    "encoded_section": "029C0F1303B8D7E8FB226FC19EB2D987E443667F2221C7DAEF257221200FC24B6210F1D684DDAEE3DFA978FE9A0C807887B4",
+    "identity_token": "9BF79D0640C3F033616E88C085BA0E84",
+    "key_seed": "E71E5C9532BFF3AE23003A89003A6D78BCD3D11705FC47C56D55ACDF0C1E8625",
+    "nonce": "727CC259D161A356AEA07884",
+    "section_mic_hmac_key": "4D2C247CB47A94E69FB4D4BE132D0F7C3FFBC4756234ED5B1E6D3A8DB4A88F7A",
+    "section_salt": "9C0F1303B8D7E8FB226FC19EB2D987E4"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "72909F4BB264D9EE250C302AB93F9691",
+    "data_elements": [],
+    "encoded_section": "027BE514C8BD6C7BE897EA35578108DD35C1D8154719977EA32D96CA42E86AE207107F459409E5AAAA74A2DB0542D6062DEA",
+    "identity_token": "5846C49FD8AC98C1892ECFA87C40496D",
+    "key_seed": "8D6D511E1B70E96DCF325405832C77CC3565D1503AA3965894616C1F0C57E113",
+    "nonce": "1524A30566D6169A501F43C0",
+    "section_mic_hmac_key": "C743F000CE8B2ED5C3735E9CCEA37E634A0965E71CB8CE9DA599917FA6337C50",
+    "section_salt": "7BE514C8BD6C7BE897EA35578108DD35"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "9FCAF424921B3EAC6108BBAD5A73CB99",
+    "data_elements": [
+      {
+        "contents": "9E1B5533A9E53934781C72",
+        "de_type": 208
+      },
+      {
+        "contents": "952B14BF77506C",
+        "de_type": 549
+      },
+      {
+        "contents": "6E5D3C18ACD4B106A55B4A2637FA57DCD34D",
+        "de_type": 782
+      },
+      {
+        "contents": "9931CB53B91C9EF8F62E358284A44B00D8AC1A95AA73",
+        "de_type": 222
+      }
+    ],
+    "encoded_section": "02D14BDD76436FA2B760E9AF470F4321DDDA37CA2E790A42149020904EB4F7C88A560D3F833B34C52AFE8DF576F04DD03F9E961F946B06501E88B0D7EFCB951EB7D876BE437FE69385B7CF0F331CA1B3A2E3E95279F83E363542E7D5F757AE3091B5A288B485C50EF7BB755A39296357C1864AC797E35505",
+    "identity_token": "36D94D61F32E7B6B24F59C8295F1AF96",
+    "key_seed": "4BB6613384D29BA3C848795DD8C4A2C43DC9510A8EC03F6043E815AF783B7F38",
+    "nonce": "C59C14410DA46F23624443C5",
+    "section_mic_hmac_key": "DF3E0EA835945BC60C5F661AFE7A4E33A056508943126AC0D057BB51C453F71A",
+    "section_salt": "D14BDD76436FA2B760E9AF470F4321DD"
+  },
+  {
+    "adv_header_byte": "20",
+    "aes_key": "CE880F0EE78F5EEF297475B681DFC310",
+    "data_elements": [
+      {
+        "contents": "99F0EA",
+        "de_type": 289
+      },
+      {
+        "contents": "98F41B89B20F1FE7",
+        "de_type": 498
+      },
+      {
+        "contents": "467C",
+        "de_type": 91
+      }
+    ],
+    "encoded_section": "02F18F6DCDBD4B8E8DE00A1A433A282C7EF840CA1A3788B4E9D8C8A6B9D413047925F0E300086648E857BB9ADFF9F6F6391661A854C2FA5F1AA7CA204D3DD126F6294C1E788D05",
+    "identity_token": "04ED862F43B938ABBCF539EBC70A59C7",
+    "key_seed": "710AE30C71188D8EB7E644A28219FF52966F449DC5B6BF342CC62A387E9FD166",
+    "nonce": "742EB407037A9A1001C248D7",
+    "section_mic_hmac_key": "49363E312316016C76C23E7AA07FB33C1CE4DBD0DDF9390FCA2601294E8740F0",
+    "section_salt": "F18F6DCDBD4B8E8DE00A1A433A282C7E"
+  }
+]
\ No newline at end of file
diff --git a/nearby/presence/np_adv/src/credential/book.rs b/nearby/presence/np_adv/src/credential/book.rs
index 902d55a..ef6b269 100644
--- a/nearby/presence/np_adv/src/credential/book.rs
+++ b/nearby/presence/np_adv/src/credential/book.rs
@@ -16,27 +16,25 @@
 //! the credentials to try for either advertisement version.
 //! See [`CredentialBookBuilder`] for batteries-included implementations.
 
-use crate::credential::source::{
-    CredentialSource, DiscoveryCredentialSource, SliceCredentialSource,
-};
-use crate::credential::v0::{V0DiscoveryCryptoMaterial, V0};
-use crate::credential::v1::{
-    SignedSectionIdentityResolutionMaterial, SignedSectionVerificationMaterial,
-    UnsignedSectionIdentityResolutionMaterial, UnsignedSectionVerificationMaterial,
-    V1DiscoveryCryptoMaterial, V1,
-};
-#[cfg(feature = "alloc")]
-use crate::credential::ReferencedMatchedCredential;
 use crate::credential::{
-    DiscoveryCryptoMaterial, MatchableCredential, MatchedCredential, ProtocolVersion,
+    source::{CredentialSource, SliceCredentialSource},
+    v0::{V0DiscoveryCryptoMaterial, V0},
+    v1::{
+        MicExtendedSaltSectionIdentityResolutionMaterial,
+        MicExtendedSaltSectionVerificationMaterial, MicShortSaltSectionIdentityResolutionMaterial,
+        MicShortSaltSectionVerificationMaterial, SignedSectionIdentityResolutionMaterial,
+        SignedSectionVerificationMaterial, V1DiscoveryCryptoMaterial, V1,
+    },
+    DiscoveryMetadataCryptoMaterial, MatchableCredential, MatchedCredential, ProtocolVersion,
 };
-use core::borrow::Borrow;
-use core::marker::PhantomData;
+use core::{borrow::Borrow, marker::PhantomData};
 use crypto_provider::CryptoProvider;
 
-#[cfg(feature = "alloc")]
+#[cfg(any(feature = "alloc", test))]
 extern crate alloc;
-#[cfg(feature = "alloc")]
+#[cfg(any(feature = "alloc", test))]
+use crate::credential::ReferencedMatchedCredential;
+#[cfg(any(feature = "alloc", test))]
 use alloc::vec::Vec;
 
 /// A collection of credentials to try when attempting to deserialize
@@ -171,17 +169,15 @@
 /// refer to the "precalculated" crypto-material variants
 /// for each protocol version.
 pub(crate) mod precalculated_for_version {
-    use crate::credential::v0::{
-        PrecalculatedV0DiscoveryCryptoMaterial, V0DiscoveryCredential, V0,
+    use crate::credential::{
+        v0::{PrecalculatedV0DiscoveryCryptoMaterial, V0DiscoveryCredential, V0},
+        v1::{PrecalculatedV1DiscoveryCryptoMaterial, V1DiscoveryCredential, V1},
+        DiscoveryMetadataCryptoMaterial, ProtocolVersion,
     };
-    use crate::credential::v1::{
-        PrecalculatedV1DiscoveryCryptoMaterial, V1DiscoveryCredential, V1,
-    };
-    use crate::credential::{DiscoveryCryptoMaterial, ProtocolVersion};
     use crypto_provider::CryptoProvider;
 
     pub trait MappingTrait<V: ProtocolVersion> {
-        type Output: DiscoveryCryptoMaterial<V>;
+        type Output: DiscoveryMetadataCryptoMaterial<V>;
         /// Provides pre-calculated crypto-material for the given
         /// discovery credential.
         fn precalculate<C: CryptoProvider>(
@@ -222,13 +218,12 @@
 }
 
 /// Iterator type for [`CachedCredentialSource`].
-pub struct CachedCredentialSourceIterator<
-    'a,
-    V: ProtocolVersion + 'a,
-    S: DiscoveryCredentialSource<'a, V> + 'a,
-    const N: usize,
-> where
+pub struct CachedCredentialSourceIterator<'a, V, S, const N: usize>
+where
     precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+    V: ProtocolVersion + 'a,
+    S: CredentialSource<'a, V> + 'a,
+    S::Crypto: Borrow<V::DiscoveryCredential>,
 {
     /// The current index of the (enumerated) elements that we're iterating over.
     /// Always counts up at every iteration, and may lie outside of the range
@@ -244,10 +239,12 @@
     source_iterator: S::Iterator,
 }
 
-impl<'a, V: ProtocolVersion + 'a, S: DiscoveryCredentialSource<'a, V> + 'a, const N: usize> Iterator
-    for CachedCredentialSourceIterator<'a, V, S, N>
+impl<'a, V, S, const N: usize> Iterator for CachedCredentialSourceIterator<'a, V, S, N>
 where
     precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+    V: ProtocolVersion + 'a,
+    S: CredentialSource<'a, V> + 'a,
+    S::Crypto: Borrow<V::DiscoveryCredential>,
 {
     type Item = (PossiblyCachedDiscoveryCryptoMaterial<'a, V>, S::Matched);
     fn next(&mut self) -> Option<Self::Item> {
@@ -272,7 +269,7 @@
     }
 }
 
-/// A [`CredentialSource`] which augments the externally-provided [`DiscoveryCredentialSource`] with
+/// A [`CredentialSource`] which augments the externally-provided [`CredentialSource`] with
 /// a cache containing up to the specified number of pre-calculated credentials.
 pub struct CachedCredentialSource<V: ProtocolVersion, S, const N: usize>
 where
@@ -292,7 +289,8 @@
     original: &'a S,
 ) -> [Option<PrecalculatedCryptoForProtocolVersion<V>>; N]
 where
-    S: DiscoveryCredentialSource<'a, V> + 'a,
+    S: CredentialSource<'a, V> + 'a,
+    S::Crypto: Borrow<V::DiscoveryCredential>,
     precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
 {
     let mut cache = [0u8; N].map(|_| None);
@@ -307,10 +305,10 @@
 
 impl<'a, V: ProtocolVersion, S, const N: usize> CachedCredentialSource<V, S, N>
 where
-    S: DiscoveryCredentialSource<'a, V> + 'a,
+    S: CredentialSource<'a, V> + 'a,
     precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
 {
-    /// Constructs a [`CachedCredentialSource`] from the given [`DiscoveryCredentialSource`]
+    /// Constructs a [`CachedCredentialSource`] from the given [`CredentialSource`]
     /// and the given (initial) cache contents, as constructed via the
     /// [`init_cache_from_source`] helper function.
     pub(crate) fn new(
@@ -354,7 +352,7 @@
     }
 }
 
-impl<'a, V: ProtocolVersion> DiscoveryCryptoMaterial<V>
+impl<'a, V: ProtocolVersion> DiscoveryMetadataCryptoMaterial<V>
     for PossiblyCachedDiscoveryCryptoMaterial<'a, V>
 where
     precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
@@ -368,7 +366,7 @@
 }
 
 impl<'a> V0DiscoveryCryptoMaterial for PossiblyCachedDiscoveryCryptoMaterial<'a, V0> {
-    fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C> {
+    fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
         match &self.wrapped {
             PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => x.ldt_adv_cipher::<C>(),
             PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => x.ldt_adv_cipher::<C>(),
@@ -390,15 +388,28 @@
         }
     }
 
-    fn unsigned_identity_resolution_material<C: CryptoProvider>(
+    fn mic_short_salt_identity_resolution_material<C: CryptoProvider>(
         &self,
-    ) -> UnsignedSectionIdentityResolutionMaterial {
+    ) -> MicShortSaltSectionIdentityResolutionMaterial {
         match &self.wrapped {
             PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => {
-                x.unsigned_identity_resolution_material::<C>()
+                x.mic_short_salt_identity_resolution_material::<C>()
             }
             PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => {
-                x.unsigned_identity_resolution_material::<C>()
+                x.mic_short_salt_identity_resolution_material::<C>()
+            }
+        }
+    }
+
+    fn mic_extended_salt_identity_resolution_material<C: CryptoProvider>(
+        &self,
+    ) -> MicExtendedSaltSectionIdentityResolutionMaterial {
+        match &self.wrapped {
+            PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => {
+                x.mic_extended_salt_identity_resolution_material::<C>()
+            }
+            PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => {
+                x.mic_extended_salt_identity_resolution_material::<C>()
             }
         }
     }
@@ -414,15 +425,28 @@
         }
     }
 
-    fn unsigned_verification_material<C: CryptoProvider>(
+    fn mic_short_salt_verification_material<C: CryptoProvider>(
         &self,
-    ) -> UnsignedSectionVerificationMaterial {
+    ) -> MicShortSaltSectionVerificationMaterial {
         match &self.wrapped {
             PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => {
-                x.unsigned_verification_material::<C>()
+                x.mic_short_salt_verification_material::<C>()
             }
             PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => {
-                x.unsigned_verification_material::<C>()
+                x.mic_short_salt_verification_material::<C>()
+            }
+        }
+    }
+
+    fn mic_extended_salt_verification_material<C: CryptoProvider>(
+        &self,
+    ) -> MicExtendedSaltSectionVerificationMaterial {
+        match &self.wrapped {
+            PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => {
+                x.mic_extended_salt_verification_material::<C>()
+            }
+            PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => {
+                x.mic_extended_salt_verification_material::<C>()
             }
         }
     }
@@ -432,11 +456,12 @@
     for CachedCredentialSource<V, S, N>
 where
     Self: 'a,
-    S: DiscoveryCredentialSource<'a, V> + 'a,
+    S: CredentialSource<'a, V> + 'a,
+    S::Crypto: Borrow<V::DiscoveryCredential>,
     precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
-    PossiblyCachedDiscoveryCryptoMaterial<'a, V>: DiscoveryCryptoMaterial<V>,
+    PossiblyCachedDiscoveryCryptoMaterial<'a, V>: DiscoveryMetadataCryptoMaterial<V>,
 {
-    type Matched = <S as DiscoveryCredentialSource<'a, V>>::Matched;
+    type Matched = <S as CredentialSource<'a, V>>::Matched;
     type Crypto = PossiblyCachedDiscoveryCryptoMaterial<'a, V>;
     type Iterator = CachedCredentialSourceIterator<'a, V, S, N>;
 
@@ -454,7 +479,7 @@
 /// can store an arbitrary amount of pre-calculated crypto-materials.
 ///
 /// Requires `alloc` as a result of internally leveraging a `Vec`.
-#[cfg(feature = "alloc")]
+#[cfg(any(feature = "alloc", test))]
 pub struct PrecalculatedOwnedCredentialSource<V: ProtocolVersion, M: MatchedCredential>
 where
     precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
@@ -462,7 +487,7 @@
     credentials: Vec<(PrecalculatedCryptoForProtocolVersion<V>, M)>,
 }
 
-#[cfg(feature = "alloc")]
+#[cfg(any(feature = "alloc", test))]
 impl<'a, V: ProtocolVersion + 'a, M: MatchedCredential + 'a>
     PrecalculatedOwnedCredentialSource<V, M>
 where
@@ -486,7 +511,7 @@
     }
 }
 
-#[cfg(feature = "alloc")]
+#[cfg(any(feature = "alloc", test))]
 fn reference_crypto_and_match_data<C, M: MatchedCredential>(
     pair_ref: &(C, M),
 ) -> (&C, ReferencedMatchedCredential<M>) {
@@ -494,13 +519,13 @@
     (c, m.into())
 }
 
-#[cfg(feature = "alloc")]
+#[cfg(any(feature = "alloc", test))]
 impl<'a, V: ProtocolVersion, M: MatchedCredential> CredentialSource<'a, V>
     for PrecalculatedOwnedCredentialSource<V, M>
 where
     Self: 'a,
     precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
-    &'a PrecalculatedCryptoForProtocolVersion<V>: DiscoveryCryptoMaterial<V>,
+    &'a PrecalculatedCryptoForProtocolVersion<V>: DiscoveryMetadataCryptoMaterial<V>,
 {
     type Matched = ReferencedMatchedCredential<'a, M>;
     type Crypto = &'a PrecalculatedCryptoForProtocolVersion<V>;
@@ -531,7 +556,7 @@
         CachedSliceCredentialSource<'a, V1, M, N1>,
     >;
 
-#[cfg(feature = "alloc")]
+#[cfg(any(feature = "alloc", test))]
 /// A credential-book which owns all of its (non-matched) credential data,
 /// and maintains pre-calculated cryptographic information about all
 /// stored credentials for speedy advertisement deserialization.
diff --git a/nearby/presence/np_adv/src/credential/matched.rs b/nearby/presence/np_adv/src/credential/matched.rs
new file mode 100644
index 0000000..90265d4
--- /dev/null
+++ b/nearby/presence/np_adv/src/credential/matched.rs
@@ -0,0 +1,318 @@
+// 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.
+
+//! Contains matched credential structs and traits which contain information about the credential
+//! which is passed back to the caller upon a successful decrypt and credential match.
+
+#[cfg(any(test, feature = "alloc"))]
+use alloc::vec::Vec;
+#[cfg(any(test, feature = "alloc"))]
+use crypto_provider::CryptoProvider;
+
+#[cfg(any(test, feature = "alloc"))]
+use crate::credential::metadata::{decrypt_metadata_with_nonce, encrypt_metadata};
+use crate::credential::{v0::V0, v1::V1, ProtocolVersion};
+use core::{convert::Infallible, fmt::Debug};
+use ldt_np_adv::V0IdentityToken;
+
+/// The portion of a credential's data to be bundled with the advertisement content it was used to
+/// decrypt. At a minimum, this includes any encrypted identity-specific metadata.
+///
+/// As it is `Debug` and `Eq`, implementors should not hold any cryptographic secrets to avoid
+/// accidental logging, timing side channels on comparison, etc, or should use custom impls of
+/// those traits rather than deriving them.
+///
+/// Instances of `MatchedCredential` may be cloned whenever advertisement content is
+/// successfully associated with a credential (see [`WithMatchedCredential`]). As a
+/// result, it's recommended to use matched-credentials which reference
+/// some underlying match-data, but don't necessarily own it.
+/// See [`ReferencedMatchedCredential`] for the most common case of shared references.
+pub trait MatchedCredential: Debug + PartialEq + Eq + Clone {
+    /// The type returned for successful calls to [`Self::fetch_encrypted_metadata`].
+    type EncryptedMetadata: AsRef<[u8]>;
+
+    /// The type of errors for [`Self::fetch_encrypted_metadata`].
+    type EncryptedMetadataFetchError: Debug;
+
+    /// Attempts to obtain the (AES-GCM)-encrypted metadata bytes for the credential,
+    /// with possible failure based on the availability of the underlying data (i.e:
+    /// failing disk reads.)
+    ///
+    /// If your implementation does not maintain any encrypted metadata for each credential,
+    /// you may simply return an empty byte-array from this method.
+    ///
+    /// If your method for obtaining metadata cannot fail, use
+    /// the `core::convert::Infallible` type for the error type
+    /// [`Self::EncryptedMetadataFetchError`].
+    fn fetch_encrypted_metadata(
+        &self,
+    ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError>;
+}
+
+/// [`MatchedCredential`] wrapper around a shared reference to a [`MatchedCredential`].
+/// This is done instead of providing a blanket impl of [`MatchedCredential`] for
+/// reference types to allow for downstream crates to impl [`MatchedCredential`] on
+/// specific reference types.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct ReferencedMatchedCredential<'a, M: MatchedCredential> {
+    wrapped: &'a M,
+}
+
+impl<'a, M: MatchedCredential> From<&'a M> for ReferencedMatchedCredential<'a, M> {
+    fn from(wrapped: &'a M) -> Self {
+        Self { wrapped }
+    }
+}
+
+impl<'a, M: MatchedCredential> AsRef<M> for ReferencedMatchedCredential<'a, M> {
+    fn as_ref(&self) -> &M {
+        self.wrapped
+    }
+}
+
+impl<'a, M: MatchedCredential> MatchedCredential for ReferencedMatchedCredential<'a, M> {
+    type EncryptedMetadata = <M as MatchedCredential>::EncryptedMetadata;
+    type EncryptedMetadataFetchError = <M as MatchedCredential>::EncryptedMetadataFetchError;
+    fn fetch_encrypted_metadata(
+        &self,
+    ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError> {
+        self.wrapped.fetch_encrypted_metadata()
+    }
+}
+
+/// A simple implementation of [`MatchedCredential`] where all match-data
+/// is contained in the encrypted metadata byte-field.
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct MetadataMatchedCredential<A: AsRef<[u8]> + Clone + Debug + PartialEq + Eq> {
+    encrypted_metadata: A,
+}
+
+#[cfg(any(test, feature = "alloc"))]
+impl MetadataMatchedCredential<Vec<u8>> {
+    /// Builds a [`MetadataMatchedCredential`] whose contents are given
+    /// as plaintext to be encrypted using AES-GCM against the given
+    /// broadcast crypto-material.
+    pub fn encrypt_from_plaintext<V, C>(
+        hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+        identity_token: V::IdentityToken,
+        plaintext_metadata: &[u8],
+    ) -> Self
+    where
+        V: ProtocolVersion,
+        C: CryptoProvider,
+    {
+        // TODO move this to identity provider
+        let encrypted_metadata = encrypt_metadata::<C, V>(hkdf, identity_token, plaintext_metadata);
+        Self { encrypted_metadata }
+    }
+}
+
+impl<A: AsRef<[u8]> + Clone + Debug + PartialEq + Eq> MetadataMatchedCredential<A> {
+    /// Builds a new [`MetadataMatchedCredential`] with the given
+    /// encrypted metadata.
+    pub fn new(encrypted_metadata: A) -> Self {
+        Self { encrypted_metadata }
+    }
+}
+
+impl<A: AsRef<[u8]> + Clone + Debug + PartialEq + Eq> MatchedCredential
+    for MetadataMatchedCredential<A>
+{
+    type EncryptedMetadata = A;
+    type EncryptedMetadataFetchError = Infallible;
+    fn fetch_encrypted_metadata(&self) -> Result<Self::EncryptedMetadata, Infallible> {
+        Ok(self.encrypted_metadata.clone())
+    }
+}
+
+/// Trivial implementation of [`MatchedCredential`] which consists of no match-data.
+/// Suitable for usage scenarios where the decoded advertisement contents matter,
+/// but not necessarily which devices generated the contents.
+///
+/// Attempting to obtain the encrypted metadata from this type of credential
+/// will always yield an empty byte-array.
+#[derive(Default, Debug, PartialEq, Eq, Clone)]
+pub struct EmptyMatchedCredential;
+
+impl MatchedCredential for EmptyMatchedCredential {
+    type EncryptedMetadata = [u8; 0];
+    type EncryptedMetadataFetchError = Infallible;
+    fn fetch_encrypted_metadata(
+        &self,
+    ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError> {
+        Ok([0u8; 0])
+    }
+}
+
+#[cfg(any(test, feature = "devtools"))]
+/// A [`MatchedCredential`] which consists only of the `key_seed` in the crypto-material
+/// for the credential. Note that this is unique per-credential by construction,
+/// and so this provides natural match-data for credentials in settings where
+/// there may not be any other information available.
+///
+/// Since this matched-credential type contains cryptographic information mirroring
+/// a credential's crypto-material, this structure is not suitable for production
+/// usage outside of unit tests and dev-tools.
+///
+/// Additionally, note that the metadata on this particular kind of matched credential
+/// is deliberately made inaccessible. This is done because a key-seed representation
+/// is only suitable in very limited circumstances where no other meaningful
+/// identifying information is available, such as that which is contained in metadata.
+/// Attempting to obtain the encrypted metadata from this type of matched credential
+/// will always yield an empty byte-array.
+#[derive(Default, Debug, PartialEq, Eq, Clone)]
+pub struct KeySeedMatchedCredential {
+    key_seed: [u8; 32],
+}
+
+#[cfg(any(test, feature = "devtools"))]
+impl From<[u8; 32]> for KeySeedMatchedCredential {
+    fn from(key_seed: [u8; 32]) -> Self {
+        Self { key_seed }
+    }
+}
+#[cfg(any(test, feature = "devtools"))]
+impl From<KeySeedMatchedCredential> for [u8; 32] {
+    fn from(matched: KeySeedMatchedCredential) -> Self {
+        matched.key_seed
+    }
+}
+
+#[cfg(any(test, feature = "devtools"))]
+impl MatchedCredential for KeySeedMatchedCredential {
+    type EncryptedMetadata = [u8; 0];
+    type EncryptedMetadataFetchError = Infallible;
+    fn fetch_encrypted_metadata(
+        &self,
+    ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError> {
+        Ok([0u8; 0])
+    }
+}
+
+/// Common trait to deserialized, decrypted V0 advs and V1 sections which
+/// exposes relevant data about matched identities.
+pub trait HasIdentityMatch {
+    /// The protocol version for which this advertisement
+    /// content has an identity-match.
+    type Version: ProtocolVersion;
+
+    /// Gets the decrypted plaintext version-specific
+    /// metadata key for the associated identity.
+    fn identity_token(&self) -> <Self::Version as ProtocolVersion>::IdentityToken;
+}
+
+impl HasIdentityMatch for V0IdentityToken {
+    type Version = V0;
+    fn identity_token(&self) -> Self {
+        *self
+    }
+}
+
+impl HasIdentityMatch for crate::extended::V1IdentityToken {
+    type Version = V1;
+    fn identity_token(&self) -> Self {
+        *self
+    }
+}
+
+#[cfg(any(test, feature = "alloc"))]
+/// Type for errors from [`WithMatchedCredential#decrypt_metadata`]
+#[derive(Debug)]
+pub enum MatchedMetadataDecryptionError<M: MatchedCredential> {
+    /// Retrieving the encrypted metadata failed for one reason
+    /// or another, so we didn't get a chance to try decryption.
+    RetrievalFailed(<M as MatchedCredential>::EncryptedMetadataFetchError),
+    /// The encrypted metadata could be retrieved, but it did
+    /// not successfully decrypt against the matched identity.
+    /// This could be an indication of data corruption or
+    /// of malformed crypto on the sender-side.
+    DecryptionFailed,
+}
+
+/// Decrypted advertisement content with the [MatchedCredential] from the credential that decrypted
+/// it, along with any other information which is relevant to the identity-match.
+#[derive(Debug, PartialEq, Eq)]
+pub struct WithMatchedCredential<M: MatchedCredential, T: HasIdentityMatch> {
+    matched: M,
+    /// The 12-byte metadata nonce as derived from the key-seed HKDF
+    /// to be used for decrypting the encrypted metadata in the attached
+    /// matched-credential.
+    metadata_nonce: [u8; 12],
+    contents: T,
+}
+
+impl<'a, M: MatchedCredential + Clone, T: HasIdentityMatch>
+    WithMatchedCredential<ReferencedMatchedCredential<'a, M>, T>
+{
+    /// Clones the referenced match-data to update this container
+    /// so that the match-data is owned, rather than borrowed.
+    pub fn clone_match_data(self) -> WithMatchedCredential<M, T> {
+        let matched = self.matched.as_ref().clone();
+        let metadata_nonce = self.metadata_nonce;
+        let contents = self.contents;
+
+        WithMatchedCredential { matched, metadata_nonce, contents }
+    }
+}
+
+impl<M: MatchedCredential, T: HasIdentityMatch> WithMatchedCredential<M, T> {
+    pub(crate) fn new(matched: M, metadata_nonce: [u8; 12], contents: T) -> Self {
+        Self { matched, metadata_nonce, contents }
+    }
+    /// Applies the given function to the wrapped contents, yielding
+    /// a new instance with the same matched-credential.
+    pub fn map<R: HasIdentityMatch>(
+        self,
+        mapping: impl FnOnce(T) -> R,
+    ) -> WithMatchedCredential<M, R> {
+        let contents = mapping(self.contents);
+        let matched = self.matched;
+        let metadata_nonce = self.metadata_nonce;
+        WithMatchedCredential { matched, metadata_nonce, contents }
+    }
+    /// Credential data for the credential that decrypted the content.
+    pub fn matched_credential(&self) -> &M {
+        &self.matched
+    }
+    /// The decrypted advertisement content.
+    pub fn contents(&self) -> &T {
+        &self.contents
+    }
+
+    #[cfg(any(test, feature = "alloc"))]
+    fn decrypt_metadata_from_fetch<C: CryptoProvider>(
+        &self,
+        encrypted_metadata: &[u8],
+    ) -> Result<Vec<u8>, MatchedMetadataDecryptionError<M>> {
+        decrypt_metadata_with_nonce::<C, T::Version>(
+            self.metadata_nonce,
+            self.contents.identity_token(),
+            encrypted_metadata,
+        )
+        .map_err(|_| MatchedMetadataDecryptionError::DecryptionFailed)
+    }
+
+    #[cfg(any(test, feature = "alloc"))]
+    /// Attempts to decrypt the encrypted metadata
+    /// associated with the matched credential
+    /// based on the details of the identity-match.
+    pub fn decrypt_metadata<C: CryptoProvider>(
+        &self,
+    ) -> Result<Vec<u8>, MatchedMetadataDecryptionError<M>> {
+        self.matched
+            .fetch_encrypted_metadata()
+            .map_err(|e| MatchedMetadataDecryptionError::RetrievalFailed(e))
+            .and_then(|x| Self::decrypt_metadata_from_fetch::<C>(self, x.as_ref()))
+    }
+}
diff --git a/nearby/presence/np_adv/src/credential/metadata/mod.rs b/nearby/presence/np_adv/src/credential/metadata/mod.rs
new file mode 100644
index 0000000..4e3a930
--- /dev/null
+++ b/nearby/presence/np_adv/src/credential/metadata/mod.rs
@@ -0,0 +1,79 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! A parking ground for the semi-dormant metadata code.
+//!
+//! Eventually, when we have a clearer idea of how metadat encryption and identity providers
+//! interact, this is likely to change aggressively.
+
+use alloc::vec::Vec;
+use crypto_provider::{
+    aead::{Aead, AeadInit},
+    aes::{self},
+    CryptoProvider,
+};
+
+use crate::credential::{MetadataDecryptionError, ProtocolVersion};
+
+#[cfg(test)]
+mod tests;
+
+/// Encrypts the given plaintext metadata bytes to allow that metadata
+/// to be shared with receiving devices.
+pub fn encrypt_metadata<C: CryptoProvider, V: ProtocolVersion>(
+    hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+    identity_token: V::IdentityToken,
+    plaintext_metadata: &[u8],
+) -> Vec<u8> {
+    let aead = <<C as CryptoProvider>::Aes128Gcm as AeadInit<aes::Aes128Key>>::new(
+        &V::extract_metadata_key::<C>(identity_token),
+    );
+    // No additional authenticated data for encrypted metadata.
+    aead.encrypt(plaintext_metadata, &[], &V::metadata_nonce_from_key_seed(hkdf))
+        .expect("Metadata encryption should be infallible")
+}
+
+/// Decrypt the given metadata using the given hkdf and version-specific
+/// identity token. Returns [`MetadataDecryptionError`] in the case that
+/// the decryption operation failed.
+///
+/// See also [decrypt_metadata_with_nonce].
+pub fn decrypt_metadata<C: CryptoProvider, V: ProtocolVersion>(
+    hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+    identity_token: V::IdentityToken,
+    encrypted_metadata: &[u8],
+) -> Result<Vec<u8>, MetadataDecryptionError> {
+    decrypt_metadata_with_nonce::<C, V>(
+        V::metadata_nonce_from_key_seed(hkdf),
+        identity_token,
+        encrypted_metadata,
+    )
+}
+
+/// Decrypt the given metadata using the given hkdf and version-specific
+/// identity token. Returns [`MetadataDecryptionError`] in the case that
+/// the decryption operation failed.
+///
+/// See also [decrypt_metadata].
+pub fn decrypt_metadata_with_nonce<C: CryptoProvider, V: ProtocolVersion>(
+    nonce: <C::Aes128Gcm as Aead>::Nonce,
+    identity_token: V::IdentityToken,
+    encrypted_metadata: &[u8],
+) -> Result<Vec<u8>, MetadataDecryptionError> {
+    // No additional authenticated data for encrypted metadata.
+    let metadata_key = V::extract_metadata_key::<C>(identity_token);
+    <<C as CryptoProvider>::Aes128Gcm as AeadInit<aes::Aes128Key>>::new(&metadata_key)
+        .decrypt(encrypted_metadata, &[], &nonce)
+        .map_err(|_| MetadataDecryptionError)
+}
diff --git a/nearby/presence/np_adv/src/credential/metadata/tests.rs b/nearby/presence/np_adv/src/credential/metadata/tests.rs
new file mode 100644
index 0000000..0409ebf
--- /dev/null
+++ b/nearby/presence/np_adv/src/credential/metadata/tests.rs
@@ -0,0 +1,95 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use super::*;
+use crate::credential::v0::V0;
+use crate::credential::v1::V1;
+use crate::extended::V1IdentityToken;
+use alloc::vec;
+use crypto_provider_default::CryptoProviderImpl;
+use ldt_np_adv::V0IdentityToken;
+
+#[test]
+fn v0_metadata_decryption_works_same_metadata_key() {
+    let key_seed = [3u8; 32];
+    let identity_token = V0IdentityToken::from([5u8; 14]);
+
+    let metadata = vec![7u8; 42];
+
+    let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+    let encrypted_metadata =
+        encrypt_metadata::<CryptoProviderImpl, V0>(&hkdf, identity_token, &metadata);
+
+    let decryption_result =
+        decrypt_metadata::<CryptoProviderImpl, V0>(&hkdf, identity_token, &encrypted_metadata);
+    assert_eq!(decryption_result, Ok(metadata))
+}
+
+#[test]
+fn v1_metadata_decryption_works_same_metadata_key() {
+    let key_seed = [9u8; 32];
+    let identity_token = V1IdentityToken::from([2u8; 16]);
+
+    let metadata = vec![6u8; 51];
+
+    let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+    let encrypted_metadata =
+        encrypt_metadata::<CryptoProviderImpl, V1>(&hkdf, identity_token, &metadata);
+
+    let decryption_result =
+        decrypt_metadata::<CryptoProviderImpl, V1>(&hkdf, identity_token, &encrypted_metadata);
+    assert_eq!(decryption_result, Ok(metadata))
+}
+
+#[test]
+fn v0_metadata_decryption_fails_different_metadata_key() {
+    let key_seed = [3u8; 32];
+    let identity_token = V0IdentityToken::from([5u8; 14]);
+
+    let metadata = vec![7u8; 42];
+
+    let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+    let encrypted_metadata =
+        encrypt_metadata::<CryptoProviderImpl, V0>(&hkdf, identity_token, &metadata);
+
+    let decrypting_identity_token = V0IdentityToken::from([6u8; 14]);
+
+    let decryption_result = decrypt_metadata::<CryptoProviderImpl, V0>(
+        &hkdf,
+        decrypting_identity_token,
+        &encrypted_metadata,
+    );
+    assert_eq!(decryption_result, Err(MetadataDecryptionError))
+}
+
+#[test]
+fn v1_metadata_decryption_fails_different_metadata_key() {
+    let key_seed = [251u8; 32];
+    let identity_token = V1IdentityToken::from([127u8; 16]);
+
+    let metadata = vec![255u8; 42];
+
+    let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+    let encrypted_metadata =
+        encrypt_metadata::<CryptoProviderImpl, V1>(&hkdf, identity_token, &metadata);
+
+    let decrypting_identity_token = V1IdentityToken::from([249u8; 16]);
+
+    let decryption_result = decrypt_metadata::<CryptoProviderImpl, V1>(
+        &hkdf,
+        decrypting_identity_token,
+        &encrypted_metadata,
+    );
+    assert_eq!(decryption_result, Err(MetadataDecryptionError))
+}
diff --git a/nearby/presence/np_adv/src/credential/mod.rs b/nearby/presence/np_adv/src/credential/mod.rs
index 3cab51e..b61106d 100644
--- a/nearby/presence/np_adv/src/credential/mod.rs
+++ b/nearby/presence/np_adv/src/credential/mod.rs
@@ -18,25 +18,25 @@
 //! efficiency gains with implementations tailored to suit (e.g. caching a few hot credentials
 //! rather than reading from disk every time, etc).
 
-use crate::MetadataKey;
-
-use crate::credential::v0::{V0DiscoveryCredential, V0ProtocolVersion};
-
-use core::convert::Infallible;
+use crate::credential::matched::{MatchedCredential, ReferencedMatchedCredential};
 use core::fmt::Debug;
-use crypto_provider::{CryptoProvider, CryptoRng};
+use crypto_provider::{aead::Aead, aes, CryptoProvider, FromCryptoRng};
 
 pub mod book;
+pub mod matched;
 pub mod source;
-#[cfg(test)]
-pub mod tests;
 pub mod v0;
 pub mod v1;
 
+#[cfg(any(test, feature = "alloc"))]
+pub mod metadata;
+
+#[cfg(test)]
+mod tests;
+
 /// Information about a credential as supplied by the caller.
-#[derive(Clone)]
 pub struct MatchableCredential<V: ProtocolVersion, M: MatchedCredential> {
-    /// The discovery credential/cryptographic information associated
+    /// The cryptographic information associated
     /// with this particular credential which is used for discovering
     /// advertisements/advertisement sections generated via the
     /// paired sender credential.
@@ -47,11 +47,6 @@
 }
 
 impl<V: ProtocolVersion, M: MatchedCredential> MatchableCredential<V, M> {
-    /// De-structures this credential into the pairing of a discovery
-    /// credential and some matched credential data.
-    pub fn into_pair(self) -> (V::DiscoveryCredential, M) {
-        (self.discovery_credential, self.match_data)
-    }
     /// Views this credential as a (borrowed) discovery-credential
     /// combined with some matched credential data
     /// (which is copied - see documentation on [`MatchedCredential`])
@@ -60,176 +55,6 @@
     }
 }
 
-/// The portion of a credential's data to be bundled with the advertisement content it was used to
-/// decrypt. At a minimum, this includes any encrypted identity-specific metadata.
-///
-/// As it is `Debug` and `Eq`, implementors should not hold any cryptographic secrets to avoid
-/// accidental logging, timing side channels on comparison, etc, or should use custom impls of
-/// those traits rather than deriving them.
-///
-/// Instances of `MatchedCredential` may be cloned whenever advertisement content is
-/// successfully associated with a credential (see [`crate::WithMatchedCredential`]). As a
-/// result, it's recommended to use matched-credentials which reference
-/// some underlying match-data, but don't necessarily own it.
-/// See [`ReferencedMatchedCredential`] for the most common case of shared references.
-pub trait MatchedCredential: Debug + PartialEq + Eq + Clone {
-    /// The type returned for successful calls to [`Self::fetch_encrypted_metadata`].
-    type EncryptedMetadata: AsRef<[u8]>;
-
-    /// The type of errors for [`Self::fetch_encrypted_metadata`].
-    type EncryptedMetadataFetchError: Debug;
-
-    /// Attempts to obtain the (AES-GCM)-encrypted metadata bytes for the credential,
-    /// with possible failure based on the availability of the underlying data (i.e:
-    /// failing disk reads.)
-    ///
-    /// If your implementation does not maintain any encrypted metadata for each credential,
-    /// you may simply return an empty byte-array from this method.
-    ///
-    /// If your method for obtaining metadata cannot fail, use
-    /// the `core::convert::Infallible` type for the error type
-    /// [`Self::EncryptedMetadataFetchError`].
-    fn fetch_encrypted_metadata(
-        &self,
-    ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError>;
-}
-
-/// [`MatchedCredential`] wrapper around a shared reference to a [`MatchedCredential`].
-/// This is done instead of providing a blanket impl of [`MatchedCredential`] for
-/// reference types to allow for downstream crates to impl [`MatchedCredential`] on
-/// specific reference types.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct ReferencedMatchedCredential<'a, M: MatchedCredential> {
-    wrapped: &'a M,
-}
-
-impl<'a, M: MatchedCredential> From<&'a M> for ReferencedMatchedCredential<'a, M> {
-    fn from(wrapped: &'a M) -> Self {
-        Self { wrapped }
-    }
-}
-
-impl<'a, M: MatchedCredential> AsRef<M> for ReferencedMatchedCredential<'a, M> {
-    fn as_ref(&self) -> &M {
-        self.wrapped
-    }
-}
-
-impl<'a, M: MatchedCredential> MatchedCredential for ReferencedMatchedCredential<'a, M> {
-    type EncryptedMetadata = <M as MatchedCredential>::EncryptedMetadata;
-    type EncryptedMetadataFetchError = <M as MatchedCredential>::EncryptedMetadataFetchError;
-    fn fetch_encrypted_metadata(
-        &self,
-    ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError> {
-        self.wrapped.fetch_encrypted_metadata()
-    }
-}
-
-/// A simple implementation of [`MatchedCredential`] where all match-data
-/// is contained in the encrypted metadata byte-field.
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct MetadataMatchedCredential<A: AsRef<[u8]> + Clone + Debug + PartialEq + Eq> {
-    encrypted_metadata: A,
-}
-
-#[cfg(any(test, feature = "alloc"))]
-impl MetadataMatchedCredential<alloc::vec::Vec<u8>> {
-    /// Builds a [`MetadataMatchedCredential`] whose contents are given
-    /// as plaintext to be encrypted using AES-GCM against the given
-    /// broadcast crypto-material.
-    pub fn encrypt_from_plaintext<V, B, C>(broadcast_cm: &B, plaintext_metadata: &[u8]) -> Self
-    where
-        V: ProtocolVersion,
-        B: BroadcastCryptoMaterial<V>,
-        C: CryptoProvider,
-    {
-        let encrypted_metadata = broadcast_cm.encrypt_metadata::<C>(plaintext_metadata);
-        Self { encrypted_metadata }
-    }
-}
-
-impl<A: AsRef<[u8]> + Clone + Debug + PartialEq + Eq> MetadataMatchedCredential<A> {
-    /// Builds a new [`MetadataMatchedCredential`] with the given
-    /// encrypted metadata.
-    pub fn new(encrypted_metadata: A) -> Self {
-        Self { encrypted_metadata }
-    }
-}
-
-impl<A: AsRef<[u8]> + Clone + Debug + PartialEq + Eq> MatchedCredential
-    for MetadataMatchedCredential<A>
-{
-    type EncryptedMetadata = A;
-    type EncryptedMetadataFetchError = Infallible;
-    fn fetch_encrypted_metadata(&self) -> Result<Self::EncryptedMetadata, Infallible> {
-        Ok(self.encrypted_metadata.clone())
-    }
-}
-
-/// Trivial implementation of [`MatchedCredential`] which consists of no match-data.
-/// Suitable for usage scenarios where the decoded advertisement contents matter,
-/// but not necessarily which devices generated the contents.
-///
-/// Attempting to obtain the encrypted metadata from this type of credential
-/// will always yield an empty byte-array.
-#[derive(Default, Debug, PartialEq, Eq, Clone)]
-pub struct EmptyMatchedCredential;
-
-impl MatchedCredential for EmptyMatchedCredential {
-    type EncryptedMetadata = [u8; 0];
-    type EncryptedMetadataFetchError = Infallible;
-    fn fetch_encrypted_metadata(
-        &self,
-    ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError> {
-        Ok([0u8; 0])
-    }
-}
-
-#[cfg(any(test, feature = "devtools"))]
-/// A [`MatchedCredential`] which consists only of the `key_seed` in the crypto-material
-/// for the credential. Note that this is unique per-credential by construction,
-/// and so this provides natural match-data for credentials in settings where
-/// there may not be any other information available.
-///
-/// Since this matched-credential type contains cryptographic information mirroring
-/// a credential's crypto-material, this structure is not suitable for production
-/// usage outside of unit tests and dev-tools.
-///
-/// Additionally, note that the metadata on this particular kind of matched credential
-/// is deliberately made inaccessible. This is done because a key-seed representation
-/// is only suitable in very limited circumstances where no other meaningful
-/// identifying information is available, such as that which is contained in metadata.
-/// Attempting to obtain the encrypted metadata from this type of matched credential
-/// will always yield an empty byte-array.
-#[derive(Default, Debug, PartialEq, Eq, Clone)]
-pub struct KeySeedMatchedCredential {
-    key_seed: [u8; 32],
-}
-
-#[cfg(any(test, feature = "devtools"))]
-impl From<[u8; 32]> for KeySeedMatchedCredential {
-    fn from(key_seed: [u8; 32]) -> Self {
-        Self { key_seed }
-    }
-}
-#[cfg(any(test, feature = "devtools"))]
-impl From<KeySeedMatchedCredential> for [u8; 32] {
-    fn from(matched: KeySeedMatchedCredential) -> Self {
-        matched.key_seed
-    }
-}
-
-#[cfg(any(test, feature = "devtools"))]
-impl MatchedCredential for KeySeedMatchedCredential {
-    type EncryptedMetadata = [u8; 0];
-    type EncryptedMetadataFetchError = Infallible;
-    fn fetch_encrypted_metadata(
-        &self,
-    ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError> {
-        Ok([0u8; 0])
-    }
-}
-
 /// Error returned when metadata decryption fails.
 #[derive(Debug, Eq, PartialEq)]
 pub struct MetadataDecryptionError;
@@ -248,43 +73,24 @@
     /// is the minimal amount of cryptographic materials that we need
     /// in order to discover advertisements/sections which make
     /// use of the sender-paired version of the credential.
-    type DiscoveryCredential: DiscoveryCryptoMaterial<Self> + Clone;
+    type DiscoveryCredential: DiscoveryMetadataCryptoMaterial<Self> + Clone;
 
-    /// The native-length metadata key for this protocol version
-    /// [i.e: if V0, a 14-byte metadata key, or if V1, a 16-byte
-    /// metadata key.]
-    type MetadataKey: Clone + AsRef<[u8]>;
+    /// The native-length identity token for this protocol version
+    /// [i.e: if V0, a 14-byte identity token, or if V1, a 16-byte
+    /// identity token.]
+    type IdentityToken: Clone + AsRef<[u8]> + FromCryptoRng;
 
     /// Computes the metadata nonce for this version from the given key-seed.
-    fn metadata_nonce_from_key_seed<C: CryptoProvider>(key_seed: &[u8; 32]) -> [u8; 12];
+    fn metadata_nonce_from_key_seed<C: CryptoProvider>(
+        hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+    ) -> <C::Aes128Gcm as Aead>::Nonce;
 
-    /// Expands the passed metadata key (if needed) to a 16-byte metadata key
+    // TODO this should be done by the identity provider that owns the corresponding credential
+    /// Transforms the passed metadata key (if needed) to a 16-byte metadata key
     /// which may be used for metadata encryption/decryption
-    fn expand_metadata_key<C: CryptoProvider>(metadata_key: Self::MetadataKey) -> MetadataKey;
-
-    /// Generates a random metadata key using the given cryptographically-secure Rng
-    fn gen_random_metadata_key<R: CryptoRng>(rng: &mut R) -> Self::MetadataKey;
-
-    #[cfg(any(test, feature = "alloc"))]
-    /// Decrypt the given metadata using the given metadata nonce and version-specific
-    /// metadata key. Returns [`MetadataDecryptionError`] in the case that
-    /// the decryption operation failed.
-    fn decrypt_metadata<C: CryptoProvider>(
-        metadata_nonce: [u8; 12],
-        metadata_key: Self::MetadataKey,
-        encrypted_metadata: &[u8],
-    ) -> Result<alloc::vec::Vec<u8>, MetadataDecryptionError> {
-        use crypto_provider::{
-            aead::{Aead, AeadInit},
-            aes::Aes128Key,
-        };
-
-        let metadata_key = Self::expand_metadata_key::<C>(metadata_key);
-        let metadata_key = Aes128Key::from(metadata_key.0);
-        let aead = <<C as CryptoProvider>::Aes128Gcm as AeadInit<Aes128Key>>::new(&metadata_key);
-        // No additional authenticated data for encrypted metadata.
-        aead.decrypt(encrypted_metadata, &[], &metadata_nonce).map_err(|_| MetadataDecryptionError)
-    }
+    fn extract_metadata_key<C: CryptoProvider>(
+        identity_token: Self::IdentityToken,
+    ) -> aes::Aes128Key;
 }
 
 /// Trait for structures which provide cryptographic
@@ -292,101 +98,8 @@
 /// See [`crate::credential::v0::V0DiscoveryCryptoMaterial`]
 /// and [`crate::credential::v1::V1DiscoveryCryptoMaterial`]
 /// for V0 and V1 specializations.
-pub trait DiscoveryCryptoMaterial<V: ProtocolVersion> {
+pub trait DiscoveryMetadataCryptoMaterial<V: ProtocolVersion> {
     /// Constructs or copies the metadata nonce used for decryption of associated credential
     /// metadata for the identity represented via this crypto material.
     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12];
 }
-
-/// Cryptographic materials necessary for broadcasting encrypted
-/// advertisement contents with the given protocol version.
-pub trait BroadcastCryptoMaterial<V: ProtocolVersion> {
-    /// Yields a copy of the key seed to be used to derive other key materials used
-    /// in the encryption of broadcasted advertisement contents.
-    fn key_seed(&self) -> [u8; 32];
-
-    /// Yields a copy of the metadata-key (size dependent on protocol version)
-    /// to tag advertisement contents sent with this broadcast crypto-material.
-    fn metadata_key(&self) -> V::MetadataKey;
-
-    /// Yields the 16-byte expanded metadata key, suitable for metadata encryption.
-    fn expanded_metadata_key<C: CryptoProvider>(&self) -> MetadataKey {
-        V::expand_metadata_key::<C>(self.metadata_key())
-    }
-
-    /// Constructs the metadata nonce used for encryption of associated credential
-    /// metadata for the identity represented via this crypto material.
-    fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
-        V::metadata_nonce_from_key_seed::<C>(&self.key_seed())
-    }
-
-    /// Derives a V0 discovery credential from this V0 broadcast crypto-material
-    /// which may be used to discover v0 advertisements broadcasted with this credential.`
-    fn derive_v0_discovery_credential<C: CryptoProvider>(&self) -> V0DiscoveryCredential
-    where
-        V: V0ProtocolVersion,
-    {
-        let key_seed = self.key_seed();
-        let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
-        let metadata_key_hmac =
-            hkdf.legacy_metadata_key_hmac_key().calculate_hmac(self.metadata_key().as_ref());
-        V0DiscoveryCredential::new(key_seed, metadata_key_hmac)
-    }
-
-    #[cfg(any(test, feature = "alloc"))]
-    /// Encrypts the given plaintext metadata bytes to allow that metadata
-    /// to be shared with receiving devices.
-    fn encrypt_metadata<C: CryptoProvider>(
-        &self,
-        plaintext_metadata: &[u8],
-    ) -> alloc::vec::Vec<u8> {
-        use crypto_provider::{
-            aead::{Aead, AeadInit},
-            aes::Aes128Key,
-        };
-        let plaintext_metadata_key = self.expanded_metadata_key::<C>();
-        let plaintext_metadata_key = Aes128Key::from(plaintext_metadata_key.0);
-
-        let aead =
-            <<C as CryptoProvider>::Aes128Gcm as AeadInit<Aes128Key>>::new(&plaintext_metadata_key);
-        // No additional authenticated data for encrypted metadata.
-        aead.encrypt(plaintext_metadata, &[], &self.metadata_nonce::<C>())
-            .expect("Metadata encryption should be infallible")
-    }
-}
-
-/// Concrete implementation of [`BroadcastCryptoMaterial<V>`] for
-/// a particular protocol version which keeps the key seed
-/// and the metadata key contiguous in memory.
-///
-/// Broadcast crypto-material specified in this way will only
-/// be usable for (unsigned) advertisement content broadcasts
-/// in the given protocol version.
-///
-/// For more flexible expression of broadcast credentials,
-/// feel free to directly implement one or more of the
-/// [`BroadcastCryptoMaterial`] and/or
-/// [`crate::credential::v1::SignedBroadcastCryptoMaterial`]
-/// traits on your own struct, dependent on the details
-/// of your own broadcast credentials.
-pub struct SimpleBroadcastCryptoMaterial<V: ProtocolVersion> {
-    key_seed: [u8; 32],
-    metadata_key: V::MetadataKey,
-}
-
-impl<V: ProtocolVersion> SimpleBroadcastCryptoMaterial<V> {
-    /// Builds some simple broadcast crypto-materials out of
-    /// the provided key-seed and version-specific metadata-key.
-    pub fn new(key_seed: [u8; 32], metadata_key: V::MetadataKey) -> Self {
-        Self { key_seed, metadata_key }
-    }
-}
-
-impl<V: ProtocolVersion> BroadcastCryptoMaterial<V> for SimpleBroadcastCryptoMaterial<V> {
-    fn key_seed(&self) -> [u8; 32] {
-        self.key_seed
-    }
-    fn metadata_key(&self) -> V::MetadataKey {
-        self.metadata_key.clone()
-    }
-}
diff --git a/nearby/presence/np_adv/src/credential/source.rs b/nearby/presence/np_adv/src/credential/source.rs
index 709bbd6..f22ed97 100644
--- a/nearby/presence/np_adv/src/credential/source.rs
+++ b/nearby/presence/np_adv/src/credential/source.rs
@@ -16,59 +16,17 @@
 //! credentials for discovering advertisements/advertisement
 //! sections for a _particular_ protocol version.
 
-use crate::credential::{
-    DiscoveryCryptoMaterial, MatchableCredential, MatchedCredential, ProtocolVersion,
-    ReferencedMatchedCredential,
-};
-use core::borrow::Borrow;
-
-/// Specialized version of the [`CredentialSource`] trait for
-/// credential-sources which provide discovery credentials
-/// for a specific protocol version.
-///
-/// If you want ready-made structures which can provide
-/// credentials for both V0 and V1 protocol versions,
-/// see [`crate::credential::book::CredentialBook`]
-/// and [`crate::credential::book::CredentialBookBuilder`] instead.
-///
-/// It's preferred to use this kind of credential-source
-/// in client code, if possible, and then lift to a
-/// [`CredentialSource`] using [`AsCredentialSource`]
-/// instead of implementing [`CredentialSource`] directly,
-/// since it's better to trust this crate to handle
-/// the details of what's in [`DiscoveryCryptoMaterial`]s
-/// for specific protocol versions.
-pub trait DiscoveryCredentialSource<'a, V: ProtocolVersion>
-where
-    Self: 'a,
-{
-    /// The kind of data yielded to the caller upon a successful
-    /// identity-match.
-    type Matched: MatchedCredential;
-
-    /// The kind of crypto-material yielded from the wrapped
-    /// iterator, which allows borrowing a discovery credential.
-    type Crypto: DiscoveryCryptoMaterial<V> + Borrow<V::DiscoveryCredential>;
-
-    /// The iterator type produced which emits credentials.
-    /// This is a lending iterator which may borrow things from `self`.
-    type Iterator: Iterator<Item = (Self::Crypto, Self::Matched)>;
-
-    /// Iterate over the available credentials
-    fn iter(&'a self) -> Self::Iterator;
-}
+use crate::credential::matched::{MatchedCredential, ReferencedMatchedCredential};
+use crate::credential::{DiscoveryMetadataCryptoMaterial, MatchableCredential, ProtocolVersion};
 
 /// A source of credentials for a particular protocol version,
-/// utilizing any [`DiscoveryCryptoMaterial`] which is usable
+/// utilizing any [`DiscoveryMetadataCryptoMaterial`] which is usable
 /// for discovering advertisements in that protocol version.
 ///
 /// This trait is largely leveraged as a tool for building
 /// new kinds of [`crate::credential::book::CredentialBook`]s
 /// via the [`crate::credential::book::CredentialBookFromSources`]
-/// wrapper. It differs from the [`DiscoveryCredentialSource`]
-/// trait in that the crypto-materials do not have to be
-/// discovery credentials, and can instead be some pre-calculated
-/// crypto-materials.
+/// wrapper.
 ///
 /// See [`crate::credential::book::CachedCredentialSource`]
 /// for an example of this pattern.
@@ -82,7 +40,7 @@
 
     /// The kind of crypto-material yielded from the wrapped
     /// iterator.
-    type Crypto: DiscoveryCryptoMaterial<V>;
+    type Crypto: DiscoveryMetadataCryptoMaterial<V>;
 
     /// The iterator type produced which emits credentials.
     /// This is a lending iterator which may borrow things from `self`.
@@ -92,27 +50,7 @@
     fn iter(&'a self) -> Self::Iterator;
 }
 
-// Note: This is needed to get around coherence problems
-// with the [`CredentialSource`] trait's relationship
-// with [`DiscoveryCredentialSource`] if it were declared
-// as a sub-trait (i.e: conflicting impls)
-/// Wrapper which turns any [`DiscoveryCredentialSource`]
-/// into a [`CredentialSource`].
-pub struct AsCredentialSource<S>(pub S);
-
-impl<'a, V: ProtocolVersion, S: DiscoveryCredentialSource<'a, V>> CredentialSource<'a, V>
-    for AsCredentialSource<S>
-{
-    type Matched = <S as DiscoveryCredentialSource<'a, V>>::Matched;
-    type Crypto = <S as DiscoveryCredentialSource<'a, V>>::Crypto;
-    type Iterator = <S as DiscoveryCredentialSource<'a, V>>::Iterator;
-
-    fn iter(&'a self) -> Self::Iterator {
-        self.0.iter()
-    }
-}
-
-/// A simple [`DiscoveryCredentialSource`] which iterates over a provided slice of credentials
+/// A simple [`CredentialSource`] which iterates over a provided slice of credentials
 pub struct SliceCredentialSource<'c, V: ProtocolVersion, M: MatchedCredential> {
     credentials: &'c [MatchableCredential<V, M>],
 }
@@ -124,12 +62,12 @@
     }
 }
 
-impl<'a, 'b, V: ProtocolVersion, M: MatchedCredential> DiscoveryCredentialSource<'a, V>
+impl<'a, 'b, V: ProtocolVersion, M: MatchedCredential> CredentialSource<'a, V>
     for SliceCredentialSource<'b, V, M>
 where
     'b: 'a,
     Self: 'b,
-    &'a <V as ProtocolVersion>::DiscoveryCredential: DiscoveryCryptoMaterial<V>,
+    &'a <V as ProtocolVersion>::DiscoveryCredential: DiscoveryMetadataCryptoMaterial<V>,
 {
     type Matched = ReferencedMatchedCredential<'a, M>;
     type Crypto = &'a V::DiscoveryCredential;
diff --git a/nearby/presence/np_adv/src/credential/tests.rs b/nearby/presence/np_adv/src/credential/tests.rs
index 8b12bfc..7e92934 100644
--- a/nearby/presence/np_adv/src/credential/tests.rs
+++ b/nearby/presence/np_adv/src/credential/tests.rs
@@ -15,38 +15,38 @@
 
 extern crate alloc;
 
+use crate::credential::matched::{
+    EmptyMatchedCredential, KeySeedMatchedCredential, ReferencedMatchedCredential,
+};
+use crate::credential::v1::MicSectionVerificationMaterial;
 use crate::credential::{
     book::{
         init_cache_from_source, CachedCredentialSource, PossiblyCachedDiscoveryCryptoMaterialKind,
     },
     source::{CredentialSource, SliceCredentialSource},
     v0::{V0DiscoveryCredential, V0},
-    v1::{
-        SignedBroadcastCryptoMaterial, SimpleSignedBroadcastCryptoMaterial, V1DiscoveryCredential,
-        V1DiscoveryCryptoMaterial, V1,
-    },
-    BroadcastCryptoMaterial, EmptyMatchedCredential, KeySeedMatchedCredential, MatchableCredential,
-    MetadataDecryptionError, ProtocolVersion, ReferencedMatchedCredential,
-    SimpleBroadcastCryptoMaterial,
+    v1::{V1BroadcastCredential, V1DiscoveryCredential, V1DiscoveryCryptoMaterial, V1},
+    MatchableCredential,
 };
-use crate::legacy::ShortMetadataKey;
-use crate::MetadataKey;
-use alloc::{vec, vec::Vec};
+use crate::extended::{V1IdentityToken, V1_IDENTITY_TOKEN_LEN};
+use alloc::vec::Vec;
+use crypto_provider::{ed25519, CryptoProvider};
 use crypto_provider_default::CryptoProviderImpl;
 
+type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
+
 fn get_zeroed_v0_discovery_credential() -> V0DiscoveryCredential {
     V0DiscoveryCredential::new([0u8; 32], [0u8; 32])
 }
 
 fn get_constant_packed_v1_discovery_credential(value: u8) -> V1DiscoveryCredential {
-    let key_pair = np_ed25519::KeyPair::<CryptoProviderImpl>::generate();
-    SimpleSignedBroadcastCryptoMaterial::new(
+    V1BroadcastCredential::new(
         [value; 32],
-        MetadataKey([value; 16]),
+        V1IdentityToken::from([value; V1_IDENTITY_TOKEN_LEN]),
         // NOTE: This winds up being unused in these test cases
-        key_pair.private_key(),
+        ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
     )
-    .derive_v1_discovery_credential::<CryptoProviderImpl>()
+    .derive_discovery_credential::<CryptoProviderImpl>()
 }
 
 #[test]
@@ -71,9 +71,11 @@
         .iter()
         .map(|cred| {
             (
-                cred.discovery_credential
-                    .unsigned_verification_material::<CryptoProviderImpl>()
-                    .mic_hmac_key,
+                *cred
+                    .discovery_credential
+                    .mic_extended_salt_verification_material::<CryptoProviderImpl>()
+                    .mic_hmac_key()
+                    .as_bytes(),
                 ReferencedMatchedCredential::from(&cred.match_data),
             )
         })
@@ -82,7 +84,10 @@
         .iter()
         .map(|(crypto_material, match_data)| {
             (
-                crypto_material.unsigned_verification_material::<CryptoProviderImpl>().mic_hmac_key,
+                *crypto_material
+                    .mic_extended_salt_verification_material::<CryptoProviderImpl>()
+                    .mic_hmac_key()
+                    .as_bytes(),
                 match_data,
             )
         })
@@ -119,90 +124,13 @@
     }
 }
 
-#[test]
-fn v0_metadata_decryption_works_same_metadata_key() {
-    let key_seed = [3u8; 32];
-    let metadata_key = ShortMetadataKey([5u8; 14]);
+mod coverage_gaming {
+    use crate::credential::MetadataDecryptionError;
+    use alloc::format;
 
-    let metadata = vec![7u8; 42];
-
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, metadata_key);
-
-    let encrypted_metadata = broadcast_cm.encrypt_metadata::<CryptoProviderImpl>(&metadata);
-
-    let metadata_nonce = broadcast_cm.metadata_nonce::<CryptoProviderImpl>();
-
-    let decryption_result = V0::decrypt_metadata::<CryptoProviderImpl>(
-        metadata_nonce,
-        metadata_key,
-        &encrypted_metadata,
-    );
-    assert_eq!(decryption_result, Ok(metadata))
-}
-
-#[test]
-fn v1_metadata_decryption_works_same_metadata_key() {
-    let key_seed = [9u8; 32];
-    let metadata_key = MetadataKey([2u8; 16]);
-
-    let metadata = vec![6u8; 51];
-
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
-
-    let encrypted_metadata = broadcast_cm.encrypt_metadata::<CryptoProviderImpl>(&metadata);
-
-    let metadata_nonce = broadcast_cm.metadata_nonce::<CryptoProviderImpl>();
-
-    let decryption_result = V1::decrypt_metadata::<CryptoProviderImpl>(
-        metadata_nonce,
-        metadata_key,
-        &encrypted_metadata,
-    );
-    assert_eq!(decryption_result, Ok(metadata))
-}
-
-#[test]
-fn v0_metadata_decryption_fails_different_metadata_key() {
-    let key_seed = [3u8; 32];
-    let encrypting_metadata_key = ShortMetadataKey([5u8; 14]);
-
-    let metadata = vec![7u8; 42];
-
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, encrypting_metadata_key);
-
-    let encrypted_metadata = broadcast_cm.encrypt_metadata::<CryptoProviderImpl>(&metadata);
-
-    let metadata_nonce = broadcast_cm.metadata_nonce::<CryptoProviderImpl>();
-
-    let decrypting_metadata_key = ShortMetadataKey([6u8; 14]);
-
-    let decryption_result = V0::decrypt_metadata::<CryptoProviderImpl>(
-        metadata_nonce,
-        decrypting_metadata_key,
-        &encrypted_metadata,
-    );
-    assert_eq!(decryption_result, Err(MetadataDecryptionError))
-}
-
-#[test]
-fn v1_metadata_decryption_fails_different_metadata_key() {
-    let key_seed = [251u8; 32];
-    let encrypting_metadata_key = MetadataKey([127u8; 16]);
-
-    let metadata = vec![255u8; 42];
-
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, encrypting_metadata_key);
-
-    let encrypted_metadata = broadcast_cm.encrypt_metadata::<CryptoProviderImpl>(&metadata);
-
-    let metadata_nonce = broadcast_cm.metadata_nonce::<CryptoProviderImpl>();
-
-    let decrypting_metadata_key = MetadataKey([249u8; 16]);
-
-    let decryption_result = V1::decrypt_metadata::<CryptoProviderImpl>(
-        metadata_nonce,
-        decrypting_metadata_key,
-        &encrypted_metadata,
-    );
-    assert_eq!(decryption_result, Err(MetadataDecryptionError))
+    #[test]
+    fn metadata_decryption_error_debug() {
+        let err = MetadataDecryptionError;
+        let _ = format!("{:?}", err);
+    }
 }
diff --git a/nearby/presence/np_adv/src/credential/v0.rs b/nearby/presence/np_adv/src/credential/v0.rs
index c89909d..84ff768 100644
--- a/nearby/presence/np_adv/src/credential/v0.rs
+++ b/nearby/presence/np_adv/src/credential/v0.rs
@@ -13,39 +13,42 @@
 // limitations under the License.
 
 //! Cryptographic materials for v0 advertisement-format credentials.
-use crate::credential::{protocol_version_seal, DiscoveryCryptoMaterial, ProtocolVersion};
-use crate::legacy::ShortMetadataKey;
-use crate::MetadataKey;
-use crypto_provider::{CryptoProvider, CryptoRng};
+use crate::credential::{protocol_version_seal, DiscoveryMetadataCryptoMaterial, ProtocolVersion};
+use crypto_provider::{aead::Aead, aes, aes::ctr, CryptoProvider};
+use ldt_np_adv::V0IdentityToken;
+use np_hkdf::NpKeySeedHkdf;
 
 /// Cryptographic information about a particular V0 discovery credential
 /// necessary to match and decrypt encrypted V0 advertisements.
-#[derive(Clone)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub struct V0DiscoveryCredential {
-    key_seed: [u8; 32],
-    legacy_metadata_key_hmac: [u8; 32],
+    /// The 32-byte key-seed used for generating other key material.
+    pub key_seed: [u8; 32],
+
+    /// The (LDT-variant) HMAC of the identity token.
+    pub expected_identity_token_hmac: [u8; 32],
 }
 
 impl V0DiscoveryCredential {
     /// Construct an [V0DiscoveryCredential] from the provided identity data.
-    pub fn new(key_seed: [u8; 32], legacy_metadata_key_hmac: [u8; 32]) -> Self {
-        Self { key_seed, legacy_metadata_key_hmac }
+    pub fn new(key_seed: [u8; 32], expected_identity_token_hmac: [u8; 32]) -> Self {
+        Self { key_seed, expected_identity_token_hmac }
     }
 }
 
-impl DiscoveryCryptoMaterial<V0> for V0DiscoveryCredential {
+impl DiscoveryMetadataCryptoMaterial<V0> for V0DiscoveryCredential {
     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
-        V0::metadata_nonce_from_key_seed::<C>(&self.key_seed)
+        np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed).v0_metadata_nonce()
     }
 }
 
 impl V0DiscoveryCryptoMaterial for V0DiscoveryCredential {
-    fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C> {
-        let hkdf = np_hkdf::NpKeySeedHkdf::new(&self.key_seed);
+    fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
+        let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
         ldt_np_adv::build_np_adv_decrypter(
-            &hkdf.legacy_ldt_key(),
-            self.legacy_metadata_key_hmac,
-            hkdf.legacy_metadata_key_hmac_key(),
+            &hkdf.v0_ldt_key(),
+            self.expected_identity_token_hmac,
+            hkdf.v0_identity_token_hmac_key(),
         )
     }
 }
@@ -58,17 +61,17 @@
 
 impl ProtocolVersion for V0 {
     type DiscoveryCredential = V0DiscoveryCredential;
-    type MetadataKey = ShortMetadataKey;
+    type IdentityToken = V0IdentityToken;
 
-    fn metadata_nonce_from_key_seed<C: CryptoProvider>(key_seed: &[u8; 32]) -> [u8; 12] {
-        let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(key_seed);
-        hkdf.legacy_metadata_nonce()
+    fn metadata_nonce_from_key_seed<C: CryptoProvider>(
+        hkdf: &NpKeySeedHkdf<C>,
+    ) -> <C::Aes128Gcm as Aead>::Nonce {
+        hkdf.v0_metadata_nonce()
     }
-    fn expand_metadata_key<C: CryptoProvider>(metadata_key: ShortMetadataKey) -> MetadataKey {
-        metadata_key.expand::<C>()
-    }
-    fn gen_random_metadata_key<R: CryptoRng>(rng: &mut R) -> ShortMetadataKey {
-        ShortMetadataKey(rng.gen())
+
+    // TODO should be IdP specific
+    fn extract_metadata_key<C: CryptoProvider>(metadata_key: V0IdentityToken) -> aes::Aes128Key {
+        np_hkdf::v0_metadata_expanded_key::<C>(&metadata_key.bytes()).into()
     }
 }
 
@@ -83,18 +86,18 @@
 /// be stored in implementors of this trait.
 // Space-time tradeoffs:
 // - LDT keys (64b) take about 1.4us.
-pub trait V0DiscoveryCryptoMaterial: DiscoveryCryptoMaterial<V0> {
+pub trait V0DiscoveryCryptoMaterial: DiscoveryMetadataCryptoMaterial<V0> {
     /// Returns an LDT NP advertisement cipher built with the provided `Aes`
-    fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C>;
+    fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>;
 }
 
 /// [`V0DiscoveryCryptoMaterial`] that minimizes CPU time when providing key material at
 /// the expense of occupied memory.
 pub struct PrecalculatedV0DiscoveryCryptoMaterial {
     pub(crate) legacy_ldt_key: ldt::LdtKey<xts_aes::XtsAes128Key>,
-    pub(crate) legacy_metadata_key_hmac: [u8; 32],
-    pub(crate) legacy_metadata_key_hmac_key: [u8; 32],
-    pub(crate) metadata_nonce: [u8; 12],
+    pub(crate) legacy_identity_token_hmac: [u8; 32],
+    pub(crate) legacy_identity_token_hmac_key: [u8; 32],
+    pub(crate) metadata_nonce: ctr::AesCtrNonce,
 }
 
 impl PrecalculatedV0DiscoveryCryptoMaterial {
@@ -102,26 +105,26 @@
     pub(crate) fn new<C: CryptoProvider>(discovery_credential: &V0DiscoveryCredential) -> Self {
         let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&discovery_credential.key_seed);
         Self {
-            legacy_ldt_key: hkdf.legacy_ldt_key(),
-            legacy_metadata_key_hmac: discovery_credential.legacy_metadata_key_hmac,
-            legacy_metadata_key_hmac_key: *hkdf.legacy_metadata_key_hmac_key().as_bytes(),
-            metadata_nonce: hkdf.legacy_metadata_nonce(),
+            legacy_ldt_key: hkdf.v0_ldt_key(),
+            legacy_identity_token_hmac: discovery_credential.expected_identity_token_hmac,
+            legacy_identity_token_hmac_key: *hkdf.v0_identity_token_hmac_key().as_bytes(),
+            metadata_nonce: hkdf.v0_metadata_nonce(),
         }
     }
 }
 
-impl DiscoveryCryptoMaterial<V0> for PrecalculatedV0DiscoveryCryptoMaterial {
+impl DiscoveryMetadataCryptoMaterial<V0> for PrecalculatedV0DiscoveryCryptoMaterial {
     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
         self.metadata_nonce
     }
 }
 
 impl V0DiscoveryCryptoMaterial for PrecalculatedV0DiscoveryCryptoMaterial {
-    fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C> {
+    fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
         ldt_np_adv::build_np_adv_decrypter(
             &self.legacy_ldt_key,
-            self.legacy_metadata_key_hmac,
-            self.legacy_metadata_key_hmac_key.into(),
+            self.legacy_identity_token_hmac,
+            self.legacy_identity_token_hmac_key.into(),
         )
     }
 }
@@ -129,26 +132,65 @@
 // Implementations for reference types -- we don't provide a blanket impl for references
 // due to the potential to conflict with downstream crates' implementations.
 
-impl<'a> DiscoveryCryptoMaterial<V0> for &'a V0DiscoveryCredential {
+impl<'a> DiscoveryMetadataCryptoMaterial<V0> for &'a V0DiscoveryCredential {
     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
         (*self).metadata_nonce::<C>()
     }
 }
 
 impl<'a> V0DiscoveryCryptoMaterial for &'a V0DiscoveryCredential {
-    fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C> {
+    fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
         (*self).ldt_adv_cipher::<C>()
     }
 }
 
-impl<'a> DiscoveryCryptoMaterial<V0> for &'a PrecalculatedV0DiscoveryCryptoMaterial {
+impl<'a> DiscoveryMetadataCryptoMaterial<V0> for &'a PrecalculatedV0DiscoveryCryptoMaterial {
     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
         (*self).metadata_nonce::<C>()
     }
 }
 
 impl<'a> V0DiscoveryCryptoMaterial for &'a PrecalculatedV0DiscoveryCryptoMaterial {
-    fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C> {
+    fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
         (*self).ldt_adv_cipher::<C>()
     }
 }
+
+/// Crypto material for creating V1 advertisements.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct V0BroadcastCredential {
+    /// The 32-byte key-seed used for generating other key material.
+    pub key_seed: [u8; 32],
+
+    /// The 14-byte identity-token which identifies the sender.
+    pub identity_token: V0IdentityToken,
+}
+
+impl V0BroadcastCredential {
+    /// Builds some simple broadcast crypto-materials out of
+    /// the provided key-seed and version-specific metadata-key.
+    pub fn new(key_seed: [u8; 32], identity_token: V0IdentityToken) -> Self {
+        Self { key_seed, identity_token }
+    }
+
+    /// Key seed from which other keys are derived.
+    pub(crate) fn key_seed(&self) -> [u8; 32] {
+        self.key_seed
+    }
+
+    /// Identity token that will be incorporated into encrypted advertisements.
+    pub(crate) fn identity_token(&self) -> V0IdentityToken {
+        self.identity_token
+    }
+
+    /// Derive a discovery credential with the data necessary to discover advertisements produced
+    /// by this broadcast credential.
+    pub fn derive_discovery_credential<C: CryptoProvider>(&self) -> V0DiscoveryCredential {
+        let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
+
+        V0DiscoveryCredential::new(
+            self.key_seed,
+            hkdf.v0_identity_token_hmac_key().calculate_hmac::<C>(&self.identity_token.bytes()),
+        )
+    }
+}
diff --git a/nearby/presence/np_adv/src/credential/v1.rs b/nearby/presence/np_adv/src/credential/v1.rs
index c13487d..b6b9c71 100644
--- a/nearby/presence/np_adv/src/credential/v1.rs
+++ b/nearby/presence/np_adv/src/credential/v1.rs
@@ -14,92 +14,71 @@
 
 //! Cryptographic materials for v1 advertisement-format credentials.
 
-use crate::credential::{
-    protocol_version_seal, BroadcastCryptoMaterial, DiscoveryCryptoMaterial, ProtocolVersion,
-};
-use crate::MetadataKey;
-use crypto_provider::ed25519::InvalidPublicKeyBytes;
-use crypto_provider::{aes::Aes128Key, ed25519, ed25519::PublicKey, CryptoProvider, CryptoRng};
-use np_hkdf::UnsignedSectionKeys;
+use crate::credential::{protocol_version_seal, DiscoveryMetadataCryptoMaterial, ProtocolVersion};
+use crate::extended::V1IdentityToken;
+use crypto_provider::{aead::Aead, aes, ed25519, CryptoProvider};
+use np_hkdf::{DerivedSectionKeys, NpKeySeedHkdf};
 
 /// Cryptographic information about a particular V1 discovery credential
 /// necessary to match and decrypt encrypted V1 sections.
-#[derive(Clone)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub struct V1DiscoveryCredential {
-    key_seed: [u8; 32],
-    expected_unsigned_metadata_key_hmac: [u8; 32],
-    expected_signed_metadata_key_hmac: [u8; 32],
-    validated_pub_key: sealed::ValidatedPublicKey,
+    /// The 32-byte key-seed used for generating other key material.
+    pub key_seed: [u8; 32],
+
+    /// The MIC-short-salt variant of the HMAC of the identity token.
+    pub expected_mic_short_salt_identity_token_hmac: [u8; 32],
+
+    /// The MIC-extended-salt variant of the HMAC of the identity token.
+    pub expected_mic_extended_salt_identity_token_hmac: [u8; 32],
+
+    /// The signed-extended-salt variant of the HMAC of the identity token.
+    pub expected_signed_extended_salt_identity_token_hmac: [u8; 32],
+
+    /// The ed25519 public key used for verification of signed sections.
+    pub public_key: ed25519::PublicKey,
 }
 
-mod sealed {
-    use crypto_provider::ed25519::InvalidPublicKeyBytes;
-    use crypto_provider::{ed25519, CryptoProvider};
-
-    // Wrapper of raw public ed25519 key bytes indicating the bytes stored
-    // actually represent a valid "edwards y" format and that said compressed
-    // point is actually a point on the curve.
-    #[derive(Clone, Copy)]
-    pub(crate) struct ValidatedPublicKey(ed25519::RawPublicKey);
-
-    impl ValidatedPublicKey {
-        pub(crate) fn from_raw_bytes<C: CryptoProvider>(
-            bytes: ed25519::RawPublicKey,
-        ) -> Result<Self, InvalidPublicKeyBytes> {
-            np_ed25519::PublicKey::<C>::from_bytes(&bytes)
-                .map(|_| Self(bytes))
-                .map_err(|_| InvalidPublicKeyBytes)
-        }
-
-        pub(crate) fn to_public_key<C: CryptoProvider>(self) -> np_ed25519::PublicKey<C> {
-            np_ed25519::PublicKey::<C>::from_bytes(&self.0)
-                .expect("The public key bytes were validated on construction")
-        }
-    }
-}
-pub(crate) use sealed::ValidatedPublicKey;
-
 impl V1DiscoveryCredential {
     /// Construct a V1 discovery credential from the provided identity data.
-    pub fn new<C: CryptoProvider>(
+    pub fn new(
         key_seed: [u8; 32],
-        expected_unsigned_metadata_key_hmac: [u8; 32],
-        expected_signed_metadata_key_hmac: [u8; 32],
-        pub_key: ed25519::RawPublicKey,
-    ) -> Result<Self, InvalidPublicKeyBytes> {
-        ValidatedPublicKey::from_raw_bytes::<C>(pub_key)
-            .map(|validated_pub_key| Self {
-                key_seed,
-                expected_unsigned_metadata_key_hmac,
-                expected_signed_metadata_key_hmac,
-                validated_pub_key,
-            })
-            .map_err(|_| InvalidPublicKeyBytes)
+        expected_mic_short_salt_identity_token_hmac: [u8; 32],
+        expected_mic_extended_salt_identity_token_hmac: [u8; 32],
+        expected_signed_extended_salt_identity_token_hmac: [u8; 32],
+        public_key: ed25519::PublicKey,
+    ) -> Self {
+        Self {
+            key_seed,
+            expected_mic_short_salt_identity_token_hmac,
+            expected_mic_extended_salt_identity_token_hmac,
+            expected_signed_extended_salt_identity_token_hmac,
+            public_key,
+        }
     }
 
     /// Constructs pre-calculated crypto material from this discovery credential.
     pub(crate) fn to_precalculated<C: CryptoProvider>(
         &self,
     ) -> PrecalculatedV1DiscoveryCryptoMaterial {
-        let signed_identity_resolution_material = self.signed_identity_resolution_material::<C>();
-        let unsigned_identity_resolution_material =
-            self.unsigned_identity_resolution_material::<C>();
-        let signed_verification_material = self.signed_verification_material::<C>();
-        let unsigned_verification_material = self.unsigned_verification_material::<C>();
-        let metadata_nonce = self.metadata_nonce::<C>();
         PrecalculatedV1DiscoveryCryptoMaterial {
-            signed_identity_resolution_material,
-            unsigned_identity_resolution_material,
-            signed_verification_material,
-            unsigned_verification_material,
-            metadata_nonce,
+            signed_identity_resolution_material: self.signed_identity_resolution_material::<C>(),
+            mic_short_salt_identity_resolution_material: self
+                .mic_short_salt_identity_resolution_material::<C>(),
+            mic_extended_salt_identity_resolution_material: self
+                .mic_extended_salt_identity_resolution_material::<C>(),
+            signed_verification_material: self.signed_verification_material::<C>(),
+            mic_short_salt_verification_material: self.mic_short_salt_verification_material::<C>(),
+            mic_extended_salt_verification_material: self
+                .mic_extended_salt_verification_material::<C>(),
+            metadata_nonce: self.metadata_nonce::<C>(),
         }
     }
 }
 
-impl DiscoveryCryptoMaterial<V1> for V1DiscoveryCredential {
+impl DiscoveryMetadataCryptoMaterial<V1> for V1DiscoveryCredential {
     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
-        V1::metadata_nonce_from_key_seed::<C>(&self.key_seed)
+        np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed).v1_metadata_nonce()
     }
 }
 
@@ -108,32 +87,50 @@
         &self,
     ) -> SignedSectionIdentityResolutionMaterial {
         let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
-        SignedSectionIdentityResolutionMaterial::from_hkdf_and_expected_metadata_key_hmac(
+        SignedSectionIdentityResolutionMaterial::from_hkdf_and_expected_identity_token_hmac(
             &hkdf,
-            self.expected_signed_metadata_key_hmac,
+            self.expected_signed_extended_salt_identity_token_hmac,
         )
     }
 
-    fn unsigned_identity_resolution_material<C: CryptoProvider>(
+    fn mic_short_salt_identity_resolution_material<C: CryptoProvider>(
         &self,
-    ) -> UnsignedSectionIdentityResolutionMaterial {
+    ) -> MicShortSaltSectionIdentityResolutionMaterial {
         let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
-        UnsignedSectionIdentityResolutionMaterial::from_hkdf_and_expected_metadata_key_hmac(
+        MicShortSaltSectionIdentityResolutionMaterial::from_hkdf_and_expected_identity_token_hmac(
             &hkdf,
-            self.expected_unsigned_metadata_key_hmac,
+            self.expected_mic_short_salt_identity_token_hmac,
+        )
+    }
+
+    fn mic_extended_salt_identity_resolution_material<C: CryptoProvider>(
+        &self,
+    ) -> MicExtendedSaltSectionIdentityResolutionMaterial {
+        let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
+        MicExtendedSaltSectionIdentityResolutionMaterial::from_hkdf_and_expected_identity_token_hmac(
+            &hkdf,
+            self.expected_mic_extended_salt_identity_token_hmac,
         )
     }
 
     fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
-        SignedSectionVerificationMaterial { validated_public_key: self.validated_pub_key }
+        SignedSectionVerificationMaterial { public_key: self.public_key.clone() }
     }
 
-    fn unsigned_verification_material<C: CryptoProvider>(
+    fn mic_short_salt_verification_material<C: CryptoProvider>(
         &self,
-    ) -> UnsignedSectionVerificationMaterial {
+    ) -> MicShortSaltSectionVerificationMaterial {
         let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
-        let mic_hmac_key = *UnsignedSectionKeys::hmac_key(&hkdf).as_bytes();
-        UnsignedSectionVerificationMaterial { mic_hmac_key }
+        let mic_hmac_key = *hkdf.v1_mic_short_salt_keys().mic_hmac_key().as_bytes();
+        MicShortSaltSectionVerificationMaterial { mic_hmac_key }
+    }
+
+    fn mic_extended_salt_verification_material<C: CryptoProvider>(
+        &self,
+    ) -> MicExtendedSaltSectionVerificationMaterial {
+        let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
+        let mic_hmac_key = *hkdf.v1_mic_extended_salt_keys().mic_hmac_key().as_bytes();
+        MicExtendedSaltSectionVerificationMaterial { mic_hmac_key }
     }
 }
 
@@ -145,17 +142,18 @@
 
 impl ProtocolVersion for V1 {
     type DiscoveryCredential = V1DiscoveryCredential;
-    type MetadataKey = MetadataKey;
+    type IdentityToken = V1IdentityToken;
 
-    fn metadata_nonce_from_key_seed<C: CryptoProvider>(key_seed: &[u8; 32]) -> [u8; 12] {
-        let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(key_seed);
-        hkdf.extended_metadata_nonce()
+    fn metadata_nonce_from_key_seed<C: CryptoProvider>(
+        hkdf: &NpKeySeedHkdf<C>,
+    ) -> <C::Aes128Gcm as Aead>::Nonce {
+        hkdf.v1_metadata_nonce()
     }
-    fn expand_metadata_key<C: CryptoProvider>(metadata_key: MetadataKey) -> MetadataKey {
-        metadata_key
-    }
-    fn gen_random_metadata_key<R: CryptoRng>(rng: &mut R) -> MetadataKey {
-        MetadataKey(rng.gen())
+
+    fn extract_metadata_key<C: CryptoProvider>(
+        identity_token: Self::IdentityToken,
+    ) -> aes::Aes128Key {
+        identity_token.into_bytes().into()
     }
 }
 
@@ -173,12 +171,12 @@
 #[derive(Clone)]
 pub(crate) struct SectionIdentityResolutionMaterial {
     /// The AES key for decrypting section ciphertext
-    pub(crate) aes_key: Aes128Key,
-    /// The metadata key HMAC key for deriving and verifying the identity metadata
+    pub(crate) aes_key: aes::Aes128Key,
+    /// The identity token HMAC key for deriving and verifying the identity metadata
     /// key HMAC against the expected value.
-    pub(crate) metadata_key_hmac_key: [u8; 32],
-    /// The expected metadata key HMAC to check against for an identity match.
-    pub(crate) expected_metadata_key_hmac: [u8; 32],
+    pub(crate) identity_token_hmac_key: [u8; 32],
+    /// The expected identity token HMAC to check against for an identity match.
+    pub(crate) expected_identity_token_hmac: [u8; 32],
 }
 
 /// Cryptographic materials necessary for determining whether or not
@@ -206,50 +204,97 @@
     /// Constructs identity-resolution material for a signed section whose
     /// discovery credential leverages the provided HKDF and has the given
     /// expected metadata-key HMAC.
-    pub(crate) fn from_hkdf_and_expected_metadata_key_hmac<C: CryptoProvider>(
+    pub(crate) fn from_hkdf_and_expected_identity_token_hmac<C: CryptoProvider>(
         hkdf: &np_hkdf::NpKeySeedHkdf<C>,
-        expected_metadata_key_hmac: [u8; 32],
+        expected_identity_token_hmac: [u8; 32],
     ) -> Self {
         Self(SectionIdentityResolutionMaterial {
-            aes_key: hkdf.extended_signed_section_aes_key(),
-            metadata_key_hmac_key: *hkdf.extended_signed_metadata_key_hmac_key().as_bytes(),
-            expected_metadata_key_hmac,
+            aes_key: hkdf.v1_signature_keys().aes_key(),
+            identity_token_hmac_key: *hkdf.v1_signature_keys().identity_token_hmac_key().as_bytes(),
+            expected_identity_token_hmac,
         })
     }
 }
 
 /// Cryptographic materials necessary for determining whether or not
-/// a given V1 MIC advertisement section matches an identity.
+/// a given V1 MIC extended salt advertisement section matches an identity.
 #[derive(Clone)]
-pub struct UnsignedSectionIdentityResolutionMaterial(SectionIdentityResolutionMaterial);
+pub struct MicExtendedSaltSectionIdentityResolutionMaterial(SectionIdentityResolutionMaterial);
 
-impl UnsignedSectionIdentityResolutionMaterial {
-    #[cfg(test)]
-    pub(crate) fn from_raw(raw: SectionIdentityResolutionMaterial) -> Self {
-        Self(raw)
-    }
+impl MicExtendedSaltSectionIdentityResolutionMaterial {
     /// Extracts the underlying section-identity resolution material carried around
-    /// within this wrapper for resolution of unsigned sections.
+    /// within this wrapper for resolution of MIC extended salt sections.
     pub(crate) fn into_raw_resolution_material(self) -> SectionIdentityResolutionMaterial {
         self.0
     }
     /// Gets the underlying section-identity resolution material carried around
-    /// within this wrapper for resolution of unsigned sections.
+    /// within this wrapper for resolution of MIC extended salt sections.
     #[cfg(any(test, feature = "devtools"))]
     pub(crate) fn as_raw_resolution_material(&self) -> &SectionIdentityResolutionMaterial {
         &self.0
     }
-    /// Constructs identity-resolution material for an unsigned (MIC) section whose
+    #[cfg(test)]
+    pub(crate) fn as_mut_raw_resolution_material(
+        &mut self,
+    ) -> &mut SectionIdentityResolutionMaterial {
+        &mut self.0
+    }
+    /// Constructs identity-resolution material for an MIC extended salt section whose
     /// discovery credential leverages the provided HKDF and has the given
     /// expected metadata-key HMAC.
-    pub(crate) fn from_hkdf_and_expected_metadata_key_hmac<C: CryptoProvider>(
+    pub(crate) fn from_hkdf_and_expected_identity_token_hmac<C: CryptoProvider>(
         hkdf: &np_hkdf::NpKeySeedHkdf<C>,
-        expected_metadata_key_hmac: [u8; 32],
+        expected_identity_token_hmac: [u8; 32],
     ) -> Self {
         Self(SectionIdentityResolutionMaterial {
-            aes_key: UnsignedSectionKeys::aes_key(hkdf),
-            metadata_key_hmac_key: *hkdf.extended_unsigned_metadata_key_hmac_key().as_bytes(),
-            expected_metadata_key_hmac,
+            aes_key: hkdf.v1_mic_extended_salt_keys().aes_key(),
+            identity_token_hmac_key: *hkdf
+                .v1_mic_extended_salt_keys()
+                .identity_token_hmac_key()
+                .as_bytes(),
+            expected_identity_token_hmac,
+        })
+    }
+}
+
+/// Cryptographic materials necessary for determining whether
+/// a given V1 MIC short salt advertisement section matches an identity.
+#[derive(Clone)]
+pub struct MicShortSaltSectionIdentityResolutionMaterial(SectionIdentityResolutionMaterial);
+
+impl MicShortSaltSectionIdentityResolutionMaterial {
+    /// Extracts the underlying section-identity resolution material carried around
+    /// within this wrapper for resolution of MIC short salt sections.
+    pub(crate) fn into_raw_resolution_material(self) -> SectionIdentityResolutionMaterial {
+        self.0
+    }
+    /// Gets the underlying section-identity resolution material carried around
+    /// within this wrapper for resolution of MIC short salt sections.
+    #[cfg(any(test, feature = "devtools"))]
+    pub(crate) fn as_raw_resolution_material(&self) -> &SectionIdentityResolutionMaterial {
+        &self.0
+    }
+    #[cfg(test)]
+    pub(crate) fn as_mut_raw_resolution_material(
+        &mut self,
+    ) -> &mut SectionIdentityResolutionMaterial {
+        &mut self.0
+    }
+
+    /// Constructs identity-resolution material for a MIC short salt section whose
+    /// discovery credential leverages the provided HKDF and has the given
+    /// expected metadata-key HMAC.
+    pub(crate) fn from_hkdf_and_expected_identity_token_hmac<C: CryptoProvider>(
+        hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+        expected_identity_token_hmac: [u8; 32],
+    ) -> Self {
+        Self(SectionIdentityResolutionMaterial {
+            aes_key: hkdf.v1_mic_short_salt_keys().aes_key(),
+            identity_token_hmac_key: *hkdf
+                .v1_mic_short_salt_keys()
+                .identity_token_hmac_key()
+                .as_bytes(),
+            expected_identity_token_hmac,
         })
     }
 }
@@ -260,34 +305,50 @@
 pub struct SignedSectionVerificationMaterial {
     /// The np_ed25519 public key to be
     /// used for signature verification of signed sections.
-    pub(crate) validated_public_key: sealed::ValidatedPublicKey,
+    pub(crate) public_key: ed25519::PublicKey,
 }
 
 impl SignedSectionVerificationMaterial {
     /// Gets the np_ed25519 public key for the given identity,
     /// used for signature verification of signed sections.
-    pub(crate) fn signature_verification_public_key<C: CryptoProvider>(
-        &self,
-    ) -> np_ed25519::PublicKey<C> {
-        self.validated_public_key.to_public_key()
+    pub(crate) fn signature_verification_public_key(&self) -> ed25519::PublicKey {
+        self.public_key.clone()
     }
 }
 
-/// Crypto materials for V1 unsigned sections which are not employed in identity resolution,
-/// but may be necessary to fully decrypt an unsigned section.
+/// Crypto materials for V1 MIC short salt sections which are not employed in identity resolution,
+/// but may be necessary to fully decrypt a MIC short salt section.
 #[derive(Clone)]
-pub struct UnsignedSectionVerificationMaterial {
-    /// The MIC HMAC key for verifying the integrity of unsigned sections.
+pub struct MicShortSaltSectionVerificationMaterial {
+    /// The MIC HMAC key for verifying the integrity of MIC short salt sections.
     pub(crate) mic_hmac_key: [u8; 32],
 }
 
-impl UnsignedSectionVerificationMaterial {
-    /// Returns the MIC HMAC key for unsigned sections
-    pub(crate) fn mic_hmac_key<C: CryptoProvider>(&self) -> np_hkdf::NpHmacSha256Key<C> {
+impl MicSectionVerificationMaterial for MicShortSaltSectionVerificationMaterial {
+    fn mic_hmac_key(&self) -> np_hkdf::NpHmacSha256Key {
         self.mic_hmac_key.into()
     }
 }
 
+/// Crypto materials for V1 MIC extended salt sections which are not employed in identity
+/// resolution, but may be necessary to fully decrypt a MIC extended salt section.
+#[derive(Clone)]
+pub struct MicExtendedSaltSectionVerificationMaterial {
+    /// The MIC HMAC key for verifying the integrity of MIC extended salt sections.
+    pub(crate) mic_hmac_key: [u8; 32],
+}
+
+impl MicSectionVerificationMaterial for MicExtendedSaltSectionVerificationMaterial {
+    fn mic_hmac_key(&self) -> np_hkdf::NpHmacSha256Key {
+        self.mic_hmac_key.into()
+    }
+}
+
+pub(crate) trait MicSectionVerificationMaterial {
+    /// Returns the HMAC key for calculating the MIC of the sections
+    fn mic_hmac_key(&self) -> np_hkdf::NpHmacSha256Key;
+}
+
 // Space-time tradeoffs:
 // - Calculating an HKDF from the key seed costs about 2us on a gLinux laptop, and occupies 80b.
 // - Calculating an AES (16b) or HMAC (32b) key from the HKDF costs about 700ns.
@@ -299,39 +360,54 @@
 // is only used on the matching identity, not all identities.
 
 /// Cryptographic material for an individual NP credential used to decrypt and verify v1 sections.
-pub trait V1DiscoveryCryptoMaterial: DiscoveryCryptoMaterial<V1> {
+pub trait V1DiscoveryCryptoMaterial: DiscoveryMetadataCryptoMaterial<V1> {
     /// Constructs or copies the identity resolution material for signed sections
     fn signed_identity_resolution_material<C: CryptoProvider>(
         &self,
     ) -> SignedSectionIdentityResolutionMaterial;
 
-    /// Constructs or copies the identity resolution material for unsigned sections
-    fn unsigned_identity_resolution_material<C: CryptoProvider>(
+    /// Constructs or copies the identity resolution material for MIC short salt sections
+    fn mic_short_salt_identity_resolution_material<C: CryptoProvider>(
         &self,
-    ) -> UnsignedSectionIdentityResolutionMaterial;
+    ) -> MicShortSaltSectionIdentityResolutionMaterial;
+
+    /// Constructs or copies the identity resolution material for MIC extended salt sections
+    fn mic_extended_salt_identity_resolution_material<C: CryptoProvider>(
+        &self,
+    ) -> MicExtendedSaltSectionIdentityResolutionMaterial;
 
     /// Constructs or copies non-identity-resolution deserialization material for signed
     /// sections.
     fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial;
 
-    /// Constructs or copies non-identity-resolution deserialization material for unsigned
+    /// Constructs or copies non-identity-resolution deserialization material for MIC short salt
     /// sections.
-    fn unsigned_verification_material<C: CryptoProvider>(
+    fn mic_short_salt_verification_material<C: CryptoProvider>(
         &self,
-    ) -> UnsignedSectionVerificationMaterial;
+    ) -> MicShortSaltSectionVerificationMaterial;
+
+    /// Constructs or copies non-identity-resolution deserialization material for MIC extended salt
+    /// sections.
+    fn mic_extended_salt_verification_material<C: CryptoProvider>(
+        &self,
+    ) -> MicExtendedSaltSectionVerificationMaterial;
 }
 
-/// V1 [`DiscoveryCryptoMaterial`] that minimizes CPU time when providing key material at
+/// [`V1DiscoveryCryptoMaterial`] that minimizes CPU time when providing key material at
 /// the expense of occupied memory
 pub struct PrecalculatedV1DiscoveryCryptoMaterial {
     pub(crate) signed_identity_resolution_material: SignedSectionIdentityResolutionMaterial,
-    pub(crate) unsigned_identity_resolution_material: UnsignedSectionIdentityResolutionMaterial,
+    pub(crate) mic_short_salt_identity_resolution_material:
+        MicShortSaltSectionIdentityResolutionMaterial,
+    pub(crate) mic_extended_salt_identity_resolution_material:
+        MicExtendedSaltSectionIdentityResolutionMaterial,
     pub(crate) signed_verification_material: SignedSectionVerificationMaterial,
-    pub(crate) unsigned_verification_material: UnsignedSectionVerificationMaterial,
+    pub(crate) mic_short_salt_verification_material: MicShortSaltSectionVerificationMaterial,
+    pub(crate) mic_extended_salt_verification_material: MicExtendedSaltSectionVerificationMaterial,
     pub(crate) metadata_nonce: [u8; 12],
 }
 
-impl DiscoveryCryptoMaterial<V1> for PrecalculatedV1DiscoveryCryptoMaterial {
+impl DiscoveryMetadataCryptoMaterial<V1> for PrecalculatedV1DiscoveryCryptoMaterial {
     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
         self.metadata_nonce
     }
@@ -343,25 +419,39 @@
     ) -> SignedSectionIdentityResolutionMaterial {
         self.signed_identity_resolution_material.clone()
     }
-    fn unsigned_identity_resolution_material<C: CryptoProvider>(
+
+    fn mic_short_salt_identity_resolution_material<C: CryptoProvider>(
         &self,
-    ) -> UnsignedSectionIdentityResolutionMaterial {
-        self.unsigned_identity_resolution_material.clone()
+    ) -> MicShortSaltSectionIdentityResolutionMaterial {
+        self.mic_short_salt_identity_resolution_material.clone()
+    }
+
+    fn mic_extended_salt_identity_resolution_material<C: CryptoProvider>(
+        &self,
+    ) -> MicExtendedSaltSectionIdentityResolutionMaterial {
+        self.mic_extended_salt_identity_resolution_material.clone()
     }
     fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
         self.signed_verification_material.clone()
     }
-    fn unsigned_verification_material<C: CryptoProvider>(
+
+    fn mic_short_salt_verification_material<C: CryptoProvider>(
         &self,
-    ) -> UnsignedSectionVerificationMaterial {
-        self.unsigned_verification_material.clone()
+    ) -> MicShortSaltSectionVerificationMaterial {
+        self.mic_short_salt_verification_material.clone()
+    }
+
+    fn mic_extended_salt_verification_material<C: CryptoProvider>(
+        &self,
+    ) -> MicExtendedSaltSectionVerificationMaterial {
+        self.mic_extended_salt_verification_material.clone()
     }
 }
 
 // Implementations for reference types -- we don't provide a blanket impl for references
 // due to the potential to conflict with downstream crates' implementations.
 
-impl<'a> DiscoveryCryptoMaterial<V1> for &'a V1DiscoveryCredential {
+impl<'a> DiscoveryMetadataCryptoMaterial<V1> for &'a V1DiscoveryCredential {
     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
         (*self).metadata_nonce::<C>()
     }
@@ -373,22 +463,36 @@
     ) -> SignedSectionIdentityResolutionMaterial {
         (*self).signed_identity_resolution_material::<C>()
     }
-    fn unsigned_identity_resolution_material<C: CryptoProvider>(
+
+    fn mic_short_salt_identity_resolution_material<C: CryptoProvider>(
         &self,
-    ) -> UnsignedSectionIdentityResolutionMaterial {
-        (*self).unsigned_identity_resolution_material::<C>()
+    ) -> MicShortSaltSectionIdentityResolutionMaterial {
+        (*self).mic_short_salt_identity_resolution_material::<C>()
+    }
+
+    fn mic_extended_salt_identity_resolution_material<C: CryptoProvider>(
+        &self,
+    ) -> MicExtendedSaltSectionIdentityResolutionMaterial {
+        (*self).mic_extended_salt_identity_resolution_material::<C>()
     }
     fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
         (*self).signed_verification_material::<C>()
     }
-    fn unsigned_verification_material<C: CryptoProvider>(
+
+    fn mic_short_salt_verification_material<C: CryptoProvider>(
         &self,
-    ) -> UnsignedSectionVerificationMaterial {
-        (*self).unsigned_verification_material::<C>()
+    ) -> MicShortSaltSectionVerificationMaterial {
+        (*self).mic_short_salt_verification_material::<C>()
+    }
+
+    fn mic_extended_salt_verification_material<C: CryptoProvider>(
+        &self,
+    ) -> MicExtendedSaltSectionVerificationMaterial {
+        (*self).mic_extended_salt_verification_material::<C>()
     }
 }
 
-impl<'a> DiscoveryCryptoMaterial<V1> for &'a PrecalculatedV1DiscoveryCryptoMaterial {
+impl<'a> DiscoveryMetadataCryptoMaterial<V1> for &'a PrecalculatedV1DiscoveryCryptoMaterial {
     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
         (*self).metadata_nonce::<C>()
     }
@@ -400,84 +504,92 @@
     ) -> SignedSectionIdentityResolutionMaterial {
         (*self).signed_identity_resolution_material::<C>()
     }
-    fn unsigned_identity_resolution_material<C: CryptoProvider>(
+
+    fn mic_short_salt_identity_resolution_material<C: CryptoProvider>(
         &self,
-    ) -> UnsignedSectionIdentityResolutionMaterial {
-        (*self).unsigned_identity_resolution_material::<C>()
+    ) -> MicShortSaltSectionIdentityResolutionMaterial {
+        (*self).mic_short_salt_identity_resolution_material::<C>()
+    }
+
+    fn mic_extended_salt_identity_resolution_material<C: CryptoProvider>(
+        &self,
+    ) -> MicExtendedSaltSectionIdentityResolutionMaterial {
+        (*self).mic_extended_salt_identity_resolution_material::<C>()
     }
     fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
         (*self).signed_verification_material::<C>()
     }
-    fn unsigned_verification_material<C: CryptoProvider>(
+
+    fn mic_short_salt_verification_material<C: CryptoProvider>(
         &self,
-    ) -> UnsignedSectionVerificationMaterial {
-        (*self).unsigned_verification_material::<C>()
+    ) -> MicShortSaltSectionVerificationMaterial {
+        (*self).mic_short_salt_verification_material::<C>()
+    }
+
+    fn mic_extended_salt_verification_material<C: CryptoProvider>(
+        &self,
+    ) -> MicExtendedSaltSectionVerificationMaterial {
+        (*self).mic_extended_salt_verification_material::<C>()
     }
 }
 
-/// Extension of [`BroadcastCryptoMaterial`] for `V1` to add
-/// crypto-materials which are necessary to sign V1 sections.
-pub trait SignedBroadcastCryptoMaterial: BroadcastCryptoMaterial<V1> {
-    /// Gets the advertisement-signing private key for constructing
-    /// signature-verified V1 sections.
-    ///
-    /// The private key is returned in an opaque, crypto-provider-independent
-    /// form to provide a safeguard against leaking the bytes of the key.
-    fn signing_key(&self) -> ed25519::PrivateKey;
+/// Crypto material for creating V1 sections.
+#[derive(Clone)]
+pub struct V1BroadcastCredential {
+    /// The 32-byte key-seed used for generating other key material.
+    pub key_seed: [u8; 32],
 
-    /// Constructs the V1 discovery credential which may be used to discover
-    /// V1 advertisement sections broadcasted using this broadcast crypto-material
-    fn derive_v1_discovery_credential<C: CryptoProvider>(&self) -> V1DiscoveryCredential {
-        let key_seed = self.key_seed();
-        let metadata_key = self.metadata_key();
-        let pub_key = self.signing_key().derive_public_key::<C::Ed25519>();
-        let pub_key = pub_key.to_bytes();
+    /// The 16-byte identity-token which identifies the sender.
+    pub identity_token: V1IdentityToken,
 
-        let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
-        let unsigned = hkdf
-            .extended_unsigned_metadata_key_hmac_key()
-            .calculate_hmac(metadata_key.0.as_slice());
-        let signed =
-            hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(metadata_key.0.as_slice());
-        V1DiscoveryCredential::new::<C>(key_seed, unsigned, signed, pub_key).expect("The public key bytes are guaranteed to be valid since they come from the key_pair construction above")
-    }
+    /// The ed25519 private key to be used for signing section contents.
+    pub private_key: ed25519::PrivateKey,
 }
 
-/// Concrete implementation of a [`SignedBroadcastCryptoMaterial`] which keeps the key
-/// seed, the V1 metadata key, and the signing key contiguous in memory.
-///
-/// For more flexible expression of broadcast
-/// credentials, feel free to directly implement [`SignedBroadcastCryptoMaterial`]
-/// for your own broadcast-credential-storing data-type.
-pub struct SimpleSignedBroadcastCryptoMaterial {
-    key_seed: [u8; 32],
-    metadata_key: MetadataKey,
-    signing_key: ed25519::PrivateKey,
-}
-
-impl SimpleSignedBroadcastCryptoMaterial {
+impl V1BroadcastCredential {
     /// Builds some simple V1 signed broadcast crypto-materials out of
-    /// the provided key-seed, metadata-key, and signing key.
+    /// the provided key-seed, metadata-key, and ed25519 private key.
     pub fn new(
         key_seed: [u8; 32],
-        metadata_key: MetadataKey,
-        signing_key: ed25519::PrivateKey,
+        identity_token: V1IdentityToken,
+        private_key: ed25519::PrivateKey,
     ) -> Self {
-        Self { key_seed, metadata_key, signing_key }
+        Self { key_seed, identity_token, private_key }
     }
-}
 
-impl BroadcastCryptoMaterial<V1> for SimpleSignedBroadcastCryptoMaterial {
-    fn key_seed(&self) -> [u8; 32] {
+    /// Key seed from which other keys are derived.
+    pub(crate) fn key_seed(&self) -> [u8; 32] {
         self.key_seed
     }
-    fn metadata_key(&self) -> MetadataKey {
-        self.metadata_key
-    }
-}
 
-impl SignedBroadcastCryptoMaterial for SimpleSignedBroadcastCryptoMaterial {
-    fn signing_key(&self) -> ed25519::PrivateKey {
-        self.signing_key.clone()
+    /// Identity token that will be incorporated into encrypted advertisements.
+    pub(crate) fn identity_token(&self) -> V1IdentityToken {
+        self.identity_token
+    }
+
+    /// Derive a discovery credential with the data necessary to discover advertisements produced
+    /// by this broadcast credential.
+    pub fn derive_discovery_credential<C: CryptoProvider>(&self) -> V1DiscoveryCredential {
+        let key_seed = self.key_seed();
+        let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
+
+        V1DiscoveryCredential::new(
+            key_seed,
+            hkdf.v1_mic_short_salt_keys()
+                .identity_token_hmac_key()
+                .calculate_hmac::<C>(self.identity_token.as_slice()),
+            hkdf.v1_mic_extended_salt_keys()
+                .identity_token_hmac_key()
+                .calculate_hmac::<C>(self.identity_token.as_slice()),
+            hkdf.v1_signature_keys()
+                .identity_token_hmac_key()
+                .calculate_hmac::<C>(self.identity_token.as_slice()),
+            self.signing_key().derive_public_key::<C::Ed25519>(),
+        )
+    }
+
+    /// Key used for signature-protected sections
+    pub(crate) fn signing_key(&self) -> ed25519::PrivateKey {
+        self.private_key.clone()
     }
 }
diff --git a/nearby/presence/np_adv/src/de_type.rs b/nearby/presence/np_adv/src/de_type.rs
deleted file mode 100644
index 4966ed0..0000000
--- a/nearby/presence/np_adv/src/de_type.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! DE types that are shared across v0 and v1.
-
-// The same ids are used for v0/legacy and v1/extended
-const IDENTITY_DE_TYPES_BY_CODE: [Option<IdentityDataElementType>; 5] = [
-    // no need for more elements since identity types are at indices 1-4
-    None,                                       // 0b0000
-    Some(IdentityDataElementType::Private),     // 0b0001
-    Some(IdentityDataElementType::Trusted),     // 0b0010
-    Some(IdentityDataElementType::Public),      // 0b0011
-    Some(IdentityDataElementType::Provisioned), // 0b0100
-];
-
-/// DE types for special DEs that contain other DEs and consume the entire payload of the
-/// advertisement.
-///
-/// Shared between V0 and V1.
-///
-/// Must not overlap with [`PlainDataElementType`](crate::legacy::de_type::PlainDataElementType).
-#[derive(strum_macros::EnumIter, Debug, Clone, Copy, PartialEq, Eq)]
-#[allow(clippy::enum_variant_names)]
-pub(crate) enum IdentityDataElementType {
-    Private,
-    Trusted,
-    Public,
-    Provisioned,
-}
-
-impl IdentityDataElementType {
-    pub(crate) fn as_encrypted_identity_de_type(&self) -> Option<EncryptedIdentityDataElementType> {
-        match self {
-            IdentityDataElementType::Private => Some(EncryptedIdentityDataElementType::Private),
-            IdentityDataElementType::Trusted => Some(EncryptedIdentityDataElementType::Trusted),
-            IdentityDataElementType::Public => None,
-            IdentityDataElementType::Provisioned => {
-                Some(EncryptedIdentityDataElementType::Provisioned)
-            }
-        }
-    }
-
-    /// Type codes for identity DEs are shared between versions 0 and 1.
-    pub(crate) fn shared_type_code(&self) -> u8 {
-        match self {
-            IdentityDataElementType::Private => 0b0001,
-            IdentityDataElementType::Trusted => 0b0010,
-            IdentityDataElementType::Public => 0b0011,
-            IdentityDataElementType::Provisioned => 0b0100,
-        }
-    }
-
-    pub(crate) fn from_shared_type_code(code: u8) -> Option<Self> {
-        IDENTITY_DE_TYPES_BY_CODE.get(code as usize).and_then(|o| *o)
-    }
-}
-
-/// The identity DE types that support encryption.
-#[derive(strum_macros::EnumIter, Debug, Clone, Copy, PartialEq, Eq)]
-pub enum EncryptedIdentityDataElementType {
-    /// An identity that's shared with other devices where a user has signed in to their identity provider
-    Private,
-    /// An identity shared via some trust designation (e.g. starred contacts)
-    Trusted,
-    /// An identity established via a p2p link between two specific devices
-    Provisioned,
-}
-
-impl EncryptedIdentityDataElementType {
-    pub(crate) fn as_identity_data_element_type(&self) -> IdentityDataElementType {
-        match self {
-            EncryptedIdentityDataElementType::Private => IdentityDataElementType::Private,
-            EncryptedIdentityDataElementType::Trusted => IdentityDataElementType::Trusted,
-            EncryptedIdentityDataElementType::Provisioned => IdentityDataElementType::Provisioned,
-        }
-    }
-}
diff --git a/nearby/presence/np_adv/src/deser_v1_tests.rs b/nearby/presence/np_adv/src/deser_v1_tests.rs
deleted file mode 100644
index cdd9030..0000000
--- a/nearby/presence/np_adv/src/deser_v1_tests.rs
+++ /dev/null
@@ -1,562 +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.
-
-#![allow(clippy::unwrap_used)]
-
-use core::marker::PhantomData;
-use rand::{rngs::StdRng, seq::SliceRandom as _, Rng as _, SeedableRng as _};
-
-extern crate std;
-
-use crate::{
-    credential::{
-        book::{CredentialBook, CredentialBookBuilder},
-        v1::{
-            SignedBroadcastCryptoMaterial, SimpleSignedBroadcastCryptoMaterial,
-            V1DiscoveryCredential,
-        },
-        EmptyMatchedCredential, MatchableCredential, MatchedCredential,
-    },
-    de_type::EncryptedIdentityDataElementType,
-    deserialization_arena,
-    deserialization_arena::DeserializationArena,
-    deserialize_advertisement,
-    extended::{
-        data_elements::GenericDataElement,
-        deserialize::VerificationMode,
-        serialize::{
-            AdvBuilder, AdvertisementType, EncodedAdvertisement, MicEncryptedSectionEncoder,
-            PublicSectionEncoder, SectionBuilder, SectionEncoder, SignedEncryptedSectionEncoder,
-        },
-    },
-    AdvDeserializationError, AdvDeserializationErrorDetailsHazmat, HasIdentityMatch, MetadataKey,
-    PlaintextIdentityMode, Section, V1AdvertisementContents, V1DeserializedSection,
-};
-use crypto_provider::{CryptoProvider, CryptoRng};
-use std::{collections, prelude::rust_2021::*, vec};
-use strum::IntoEnumIterator as _;
-
-use crate::extended::NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT;
-use crypto_provider_default::CryptoProviderImpl;
-
-#[test]
-fn v1_plaintext() {
-    let mut rng = StdRng::from_entropy();
-    for _ in 0..100 {
-        let identities = (0..100)
-            .map(|_| TestIdentity::<CryptoProviderImpl>::random(&mut rng))
-            .collect::<Vec<_>>();
-
-        let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-        let section_configs: Vec<SectionConfig<CryptoProviderImpl>> =
-            fill_plaintext_adv(&mut rng, &mut adv_builder);
-        let adv = adv_builder.into_advertisement();
-        let creds = identities
-            .iter()
-            .map(|i| MatchableCredential {
-                discovery_credential: i.discovery_credential(),
-                match_data: EmptyMatchedCredential,
-            })
-            .collect::<Vec<_>>();
-
-        let arena = deserialization_arena!();
-        // check if the section is empty or there is more than one public section
-        let cred_book =
-            CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &creds);
-        if section_configs.is_empty() {
-            let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
-            assert_eq!(
-                v1_error,
-                AdvDeserializationError::ParseError {
-                    details_hazmat:
-                        AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
-                }
-            ); //assert a adv deserialization error
-        } else {
-            let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
-            assert_eq!(0, v1_contents.invalid_sections_count());
-            assert_eq!(section_configs.len(), v1_contents.sections.len());
-            for (section_config, section) in section_configs.iter().zip(v1_contents.sections.iter())
-            {
-                assert_section_equals(section_config, section);
-            }
-        }
-    }
-}
-
-#[test]
-fn v1_all_identities_resolvable_ciphertext() {
-    let mut rng = StdRng::from_entropy();
-    for _ in 0..100 {
-        let identities = (0..100)
-            .map(|_| TestIdentity::<CryptoProviderImpl>::random(&mut rng))
-            .collect::<Vec<_>>();
-
-        let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-        let section_configs = fill_encrypted_adv(&mut rng, &identities, &mut adv_builder);
-        let adv = adv_builder.into_advertisement();
-        let creds = identities
-            .iter()
-            .map(|i| MatchableCredential {
-                discovery_credential: i.discovery_credential(),
-                match_data: EmptyMatchedCredential,
-            })
-            .collect::<Vec<_>>();
-        let arena = deserialization_arena!();
-        // check if the section header would be 0 or if the section is empty
-        let cred_book =
-            CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &creds);
-        if section_configs.is_empty() {
-            let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
-            assert_eq!(
-                v1_error,
-                AdvDeserializationError::ParseError {
-                    details_hazmat:
-                        AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
-                }
-            ); //assert a adv deserialization error
-        } else {
-            let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
-            assert_eq!(0, v1_contents.invalid_sections_count());
-            assert_eq!(section_configs.len(), v1_contents.sections.len());
-            for (section_config, section) in section_configs.iter().zip(v1_contents.sections.iter())
-            {
-                assert_section_equals(section_config, section);
-            }
-        }
-    }
-}
-
-#[test]
-fn v1_only_non_matching_identities_available_ciphertext() {
-    let mut rng = StdRng::from_entropy();
-    for _ in 0..100 {
-        let identities = (0..100)
-            .map(|_| TestIdentity::<CryptoProviderImpl>::random(&mut rng))
-            .collect::<Vec<_>>();
-
-        let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-        let section_configs = fill_encrypted_adv(&mut rng, &identities, &mut adv_builder);
-        let adv = adv_builder.into_advertisement();
-        let creds = identities
-            .iter()
-            .filter(|i| {
-                // remove all identities used in sections
-                !section_configs
-                    .iter()
-                    .any(|sc| sc.identity.map(|sci| sci.key_seed == i.key_seed).unwrap_or(false))
-            })
-            .map(|i| MatchableCredential {
-                discovery_credential: i.discovery_credential(),
-                match_data: EmptyMatchedCredential,
-            })
-            .collect::<Vec<_>>();
-
-        let arena = deserialization_arena!();
-        // check if the section header would be 0
-        let cred_book =
-            CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &creds);
-        if section_configs.is_empty() {
-            let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
-            assert_eq!(
-                v1_error,
-                AdvDeserializationError::ParseError {
-                    details_hazmat:
-                        AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
-                }
-            ); //assert a adv deserialization error
-        } else {
-            let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
-            // all encrypted identity sections are invalid
-            let encrypted_section_count =
-                section_configs.iter().filter(|sc| sc.identity.is_some()).count();
-            assert_eq!(encrypted_section_count, v1_contents.invalid_sections_count());
-            assert_eq!(section_configs.len() - encrypted_section_count, v1_contents.sections.len());
-            for (section_config, section) in section_configs
-                .iter()
-                // skip encrypted sections
-                .filter(|sc| sc.identity.is_none())
-                .zip(v1_contents.sections.iter())
-            {
-                assert_section_equals(section_config, section);
-            }
-        }
-    }
-}
-
-#[test]
-fn v1_no_creds_available_ciphertext() {
-    let mut rng = StdRng::from_entropy();
-    for _ in 0..100 {
-        let identities = (0..100).map(|_| TestIdentity::random(&mut rng)).collect::<Vec<_>>();
-
-        let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-        let section_configs = fill_encrypted_adv::<StdRng, CryptoProviderImpl>(
-            &mut rng,
-            &identities,
-            &mut adv_builder,
-        );
-        let adv = adv_builder.into_advertisement();
-        let arena = deserialization_arena!();
-        // check if the section header would be 0
-        let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
-            0,
-            0,
-            CryptoProviderImpl,
-        >(&[], &[]);
-        if section_configs.is_empty() {
-            let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
-            assert_eq!(
-                v1_error,
-                AdvDeserializationError::ParseError {
-                    details_hazmat:
-                        AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
-                }
-            ); //assert a adv deserialization error
-        } else {
-            let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
-            // all encrypted identity sections are invalid
-            let encrypted_section_count =
-                section_configs.iter().filter(|sc| sc.identity.is_some()).count();
-            assert_eq!(encrypted_section_count, v1_contents.invalid_sections_count());
-            assert_eq!(section_configs.len() - encrypted_section_count, v1_contents.sections.len());
-
-            for (section_config, section) in section_configs
-                .iter()
-                // skip encrypted sections
-                .filter(|sc| sc.identity.is_none())
-                .zip(v1_contents.sections.iter())
-            {
-                assert_section_equals(section_config, section);
-            }
-        }
-    }
-}
-
-#[test]
-fn v1_only_some_matching_identities_available_ciphertext() {
-    let mut rng = StdRng::from_entropy();
-    for _ in 0..100 {
-        let identities = (0..100)
-            .map(|_| TestIdentity::<CryptoProviderImpl>::random(&mut rng))
-            .collect::<Vec<_>>();
-
-        let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-        let section_configs = fill_encrypted_adv(&mut rng, &identities, &mut adv_builder);
-        let adv = adv_builder.into_advertisement();
-        // identities used in sections, which may be used in multiple sections too
-        let identities_to_remove: collections::HashSet<_> = identities
-            .iter()
-            .filter(|i| {
-                let identity_used = section_configs
-                    .iter()
-                    .any(|sc| sc.identity.map(|sci| sci.key_seed == i.key_seed).unwrap_or(false));
-
-                // only remove half the identities that were used
-                identity_used && rng.gen()
-            })
-            .map(|i| i.key_seed)
-            .collect();
-
-        let creds = identities
-            .iter()
-            .filter(|i| !identities_to_remove.contains(&i.key_seed))
-            .map(|i| MatchableCredential {
-                discovery_credential: i.discovery_credential(),
-                match_data: EmptyMatchedCredential,
-            })
-            .collect::<Vec<_>>();
-
-        let arena = deserialization_arena!();
-
-        let cred_book =
-            CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &creds);
-
-        // check if the section header would be 0
-        if section_configs.is_empty() {
-            let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
-            assert_eq!(
-                v1_error,
-                AdvDeserializationError::ParseError {
-                    details_hazmat:
-                        AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
-                }
-            ); //assert a adv deserialization error
-        } else {
-            let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
-
-            let affected_sections: Vec<_> = section_configs
-                .iter()
-                .filter(|sc| {
-                    sc.identity
-                        .map(|sci| identities_to_remove.iter().any(|ks| &sci.key_seed == ks))
-                        .unwrap_or(false)
-                })
-                .collect();
-
-            assert_eq!(affected_sections.len(), v1_contents.invalid_sections_count());
-            assert_eq!(section_configs.len() - affected_sections.len(), v1_contents.sections.len());
-
-            for (section_config, section) in section_configs
-                .iter()
-                // skip sections w/ removed identities
-                .filter(|sc| {
-                    sc.identity.map(|i| !identities_to_remove.contains(&i.key_seed)).unwrap_or(true)
-                })
-                .zip(v1_contents.sections.iter())
-            {
-                assert_section_equals(section_config, section);
-            }
-        }
-    }
-}
-
-fn assert_section_equals<M: MatchedCredential, C: CryptoProvider>(
-    section_config: &SectionConfig<C>,
-    section: &V1DeserializedSection<M>,
-) {
-    match section_config.identity {
-        None => match section {
-            V1DeserializedSection::Plaintext(p) => {
-                assert!(section_config.verification_mode.is_none());
-
-                assert_eq!(section_config.plaintext_mode.unwrap(), p.identity());
-                assert_eq!(
-                    section_config.data_elements,
-                    p.iter_data_elements().map(|de| (&de.unwrap()).into()).collect::<Vec<_>>()
-                )
-            }
-            V1DeserializedSection::Decrypted(_) => panic!("no id, but decrypted section"),
-        },
-        Some(_) => match section {
-            V1DeserializedSection::Plaintext(_) => panic!("id, but plaintext section"),
-            V1DeserializedSection::Decrypted(wmc) => {
-                assert!(section_config.plaintext_mode.is_none());
-
-                assert_eq!(
-                    section_config.data_elements,
-                    wmc.contents()
-                        .iter_data_elements()
-                        .map(|de| (&de.unwrap()).into())
-                        .collect::<Vec<GenericDataElement>>()
-                );
-                assert_eq!(
-                    section_config.identity.unwrap().identity_type,
-                    wmc.contents().identity_type()
-                );
-                assert_eq!(
-                    section_config.identity.unwrap().extended_metadata_key,
-                    wmc.contents().metadata_key()
-                );
-                assert_eq!(
-                    section_config.verification_mode.unwrap(),
-                    wmc.contents().verification_mode()
-                );
-            }
-        },
-    }
-}
-
-fn deser_v1_error<'a, B, P>(
-    arena: DeserializationArena<'a>,
-    adv: &'a EncodedAdvertisement,
-    cred_book: &'a B,
-) -> AdvDeserializationError
-where
-    B: CredentialBook<'a>,
-    P: CryptoProvider,
-{
-    let v1_contents = match deserialize_advertisement::<_, P>(arena, adv.as_slice(), cred_book) {
-        Err(e) => e,
-        _ => panic!("Expecting an error!"),
-    };
-    v1_contents
-}
-
-fn deser_v1<'adv, B, P>(
-    arena: DeserializationArena<'adv>,
-    adv: &'adv EncodedAdvertisement,
-    cred_book: &'adv B,
-) -> V1AdvertisementContents<'adv, B::Matched>
-where
-    B: CredentialBook<'adv>,
-    P: CryptoProvider,
-{
-    deserialize_advertisement::<_, P>(arena, adv.as_slice(), cred_book)
-        .expect("Should be a valid advertisement")
-        .into_v1()
-        .expect("Should be V1")
-}
-
-/// Populate a random number of sections with randomly chosen identities and random DEs
-fn fill_plaintext_adv<'a, R: rand::Rng, C: CryptoProvider>(
-    mut rng: &mut R,
-    adv_builder: &mut AdvBuilder,
-) -> Vec<SectionConfig<'a, C>> {
-    let mut expected = Vec::new();
-    // build sections
-    for _ in 0..rng.gen_range(0..=NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT) {
-        let res = adv_builder.section_builder(PublicSectionEncoder::default()).map(|s| {
-            SectionConfig::new(
-                None,
-                Some(PlaintextIdentityMode::Public),
-                None,
-                add_des(s, &mut rng),
-            )
-        });
-        match res {
-            Ok(tuple) => expected.push(tuple),
-            Err(_) => {
-                // couldn't fit that section; maybe another smaller section will fit
-                continue;
-            }
-        }
-    }
-    expected
-}
-
-/// Populate a random number of sections with randomly chosen identities and random DEs
-fn fill_encrypted_adv<'a, R: rand::Rng, C: CryptoProvider>(
-    mut rng: &mut R,
-    identities: &'a [TestIdentity<C>],
-    adv_builder: &mut AdvBuilder,
-) -> Vec<SectionConfig<'a, C>> {
-    let mut expected = Vec::new();
-    let mut salt_rng = C::CryptoRng::new();
-    // build sections
-    for _ in 0..rng.gen_range(0..=6) {
-        let chosen_index = rng.gen_range(0..identities.len());
-        let identity = &identities[chosen_index];
-
-        let broadcast_cm = identity.broadcast_credential();
-
-        let res = if rng.gen_bool(0.5) {
-            adv_builder
-                .section_builder(MicEncryptedSectionEncoder::<C>::new_random_salt(
-                    &mut salt_rng,
-                    identity.identity_type,
-                    &broadcast_cm,
-                ))
-                .map(|s| {
-                    SectionConfig::new(
-                        Some(identity),
-                        None,
-                        Some(VerificationMode::Mic),
-                        add_des(s, &mut rng),
-                    )
-                })
-        } else {
-            adv_builder
-                .section_builder(SignedEncryptedSectionEncoder::<C>::new_random_salt(
-                    &mut salt_rng,
-                    identity.identity_type,
-                    &broadcast_cm,
-                ))
-                .map(|s| {
-                    SectionConfig::new(
-                        Some(identity),
-                        None,
-                        Some(VerificationMode::Signature),
-                        add_des(s, &mut rng),
-                    )
-                })
-        };
-        match res {
-            Ok(tuple) => expected.push(tuple),
-            Err(_) => {
-                // couldn't fit that section; maybe another smaller section will fit
-                continue;
-            }
-        }
-    }
-    expected
-}
-
-struct TestIdentity<C: CryptoProvider> {
-    identity_type: EncryptedIdentityDataElementType,
-    key_seed: [u8; 32],
-    extended_metadata_key: MetadataKey,
-    key_pair: np_ed25519::KeyPair<C>,
-    marker: PhantomData<C>,
-}
-impl<C: CryptoProvider> TestIdentity<C> {
-    /// Generate a new identity with random crypto material
-    fn random<R: rand::Rng + rand::CryptoRng>(rng: &mut R) -> Self {
-        Self {
-            identity_type: *EncryptedIdentityDataElementType::iter()
-                .collect::<Vec<_>>()
-                .choose(rng)
-                .unwrap(),
-            key_seed: rng.gen(),
-            extended_metadata_key: MetadataKey(rng.gen()),
-            key_pair: np_ed25519::KeyPair::<C>::generate(),
-            marker: PhantomData,
-        }
-    }
-    /// Returns a (simple, signed) broadcast credential using crypto material from this identity
-    fn broadcast_credential(&self) -> SimpleSignedBroadcastCryptoMaterial {
-        SimpleSignedBroadcastCryptoMaterial::new(
-            self.key_seed,
-            self.extended_metadata_key,
-            self.key_pair.private_key(),
-        )
-    }
-    /// Returns a discovery credential using crypto material from this identity
-    fn discovery_credential(&self) -> V1DiscoveryCredential {
-        self.broadcast_credential().derive_v1_discovery_credential::<C>()
-    }
-}
-/// Add several DEs with random types and contents
-fn add_des<I: SectionEncoder, R: rand::Rng>(
-    mut sb: SectionBuilder<&mut AdvBuilder, I>,
-    rng: &mut R,
-) -> Vec<GenericDataElement> {
-    let mut des = Vec::new();
-    for _ in 0..rng.gen_range(0..=2) {
-        // not worried about multi byte type encoding here, so just sticking with what can sometimes
-        // fit in the 1-byte header, and isn't an identity element
-        let de_type = rng.gen_range(10_u32..=20);
-        // covers lengths that fit or don't fit in 3 bits (1 byte header)
-        let de_len = rng.gen_range(0..=10);
-        let mut de_data = vec![0; de_len];
-        rng.fill(&mut de_data[..]);
-        let de = GenericDataElement::try_from(de_type.into(), &de_data).unwrap();
-        if sb.add_de(|_| de.clone()).is_err() {
-            // no more room in the section
-            break;
-        }
-        des.push(de);
-    }
-    sb.add_to_advertisement();
-    des
-}
-struct SectionConfig<'a, C: CryptoProvider> {
-    /// `Some` iff an encrypted identity should be used
-    identity: Option<&'a TestIdentity<C>>,
-    /// `Some` iff `identity` is `None`
-    plaintext_mode: Option<PlaintextIdentityMode>,
-    /// `Some` iff `identity` is `Some`
-    verification_mode: Option<VerificationMode>,
-    data_elements: Vec<GenericDataElement>,
-}
-impl<'a, C: CryptoProvider> SectionConfig<'a, C> {
-    pub fn new(
-        identity: Option<&'a TestIdentity<C>>,
-        plaintext_mode: Option<PlaintextIdentityMode>,
-        verification_mode: Option<VerificationMode>,
-        data_elements: Vec<GenericDataElement>,
-    ) -> Self {
-        Self { identity, plaintext_mode, verification_mode, data_elements }
-    }
-}
diff --git a/nearby/presence/np_adv/src/deserialization_arena.rs b/nearby/presence/np_adv/src/deserialization_arena.rs
index 9b58cbf..d0ab12e 100644
--- a/nearby/presence/np_adv/src/deserialization_arena.rs
+++ b/nearby/presence/np_adv/src/deserialization_arena.rs
@@ -15,7 +15,7 @@
 //! Types for creating arenas used in deserialization of np_adv. This implementation is purpose-made
 //! for deserializing in `np_adv` and is not intended for general use as an arena.
 
-use crate::extended::BLE_ADV_SVC_CONTENT_LEN;
+use crate::extended::BLE_5_ADV_SVC_MAX_CONTENT_LEN;
 
 /// Create a [`DeserializationArena`] suitable for use with deserializing an advertisement.
 #[macro_export]
@@ -64,13 +64,13 @@
 /// [`deserialization_arena!`][crate::deserialization_arena!] macro to create an instance.
 pub struct DeserializationArena<'a> {
     #[doc(hidden)] // Exposed for use by `deserialization_arena!` only.
-    pub buffer: &'a mut [u8; BLE_ADV_SVC_CONTENT_LEN],
+    pub buffer: &'a mut [u8; BLE_5_ADV_SVC_MAX_CONTENT_LEN],
 }
 
 impl<'a> DeserializationArena<'a> {
     #[doc(hidden)] // Exposed for use by `deserialization_arena!` only.
-    pub fn new_buffer() -> [u8; BLE_ADV_SVC_CONTENT_LEN] {
-        [0; BLE_ADV_SVC_CONTENT_LEN]
+    pub fn new_buffer() -> [u8; BLE_5_ADV_SVC_MAX_CONTENT_LEN] {
+        [0; BLE_5_ADV_SVC_MAX_CONTENT_LEN]
     }
 
     /// Convert this arena into an allocator that can start allocating.
@@ -87,11 +87,11 @@
 
 #[cfg(test)]
 mod test {
-    use crate::{deserialization_arena::ArenaOutOfSpace, extended::BLE_ADV_SVC_CONTENT_LEN};
+    use crate::{deserialization_arena::ArenaOutOfSpace, extended::BLE_5_ADV_SVC_MAX_CONTENT_LEN};
 
     #[test]
     fn test_creation() {
-        assert_eq!(BLE_ADV_SVC_CONTENT_LEN, deserialization_arena!().buffer.len());
+        assert_eq!(BLE_5_ADV_SVC_MAX_CONTENT_LEN, deserialization_arena!().buffer.len());
     }
 
     #[test]
@@ -99,7 +99,7 @@
         let arena = deserialization_arena!();
         let mut allocator = arena.into_allocator();
         assert_eq!(Ok(&mut [0_u8; 100][..]), allocator.allocate(100));
-        assert_eq!(BLE_ADV_SVC_CONTENT_LEN - 100, allocator.slice.len());
+        assert_eq!(BLE_5_ADV_SVC_MAX_CONTENT_LEN - 100, allocator.slice.len());
     }
 
     #[test]
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 4e6e481..2989966 100644
--- a/nearby/presence/np_adv/src/extended/data_elements/mod.rs
+++ b/nearby/presence/np_adv/src/extended/data_elements/mod.rs
@@ -17,13 +17,15 @@
 //! Commonly used DEs have dedicated types (e.g. [TxPowerDataElement], etc), but if another DE is
 //! needed, [GenericDataElement] will allow constructing any type of DE.
 
-use crate::extended::{
-    de_type::DeType,
-    deserialize::DataElement,
-    serialize::{DeHeader, SingleTypeDataElement, WriteDataElement},
-    DeLength, ENCRYPTION_INFO_DE_TYPE, MAX_DE_LEN,
+use crate::{
+    extended::{
+        de_type::DeType,
+        deserialize::data_element::DataElement,
+        serialize::{DeHeader, SingleTypeDataElement, WriteDataElement},
+        MAX_DE_LEN,
+    },
+    shared_data::*,
 };
-use crate::shared_data::*;
 use array_view::ArrayView;
 use sink::Sink;
 
@@ -138,54 +140,6 @@
     ActionsTooLong,
 }
 
-pub(crate) const SIGNATURE_ENCRYPTION_SCHEME: u8 = 0b00001000;
-pub(crate) const MIC_ENCRYPTION_SCHEME: u8 = 0b00000000;
-
-/// Determines whether a signature or mic encryption scheme is used
-pub(crate) struct EncryptionInfoDataElement {
-    /// First byte is bESSSSRRR where SSSS is the encryption scheme, rest are the salt
-    pub info: [u8; 17],
-}
-
-impl SingleTypeDataElement for EncryptionInfoDataElement {
-    const DE_TYPE: DeType = ENCRYPTION_INFO_DE_TYPE;
-}
-
-impl EncryptionInfoDataElement {
-    pub(crate) fn serialize(&self) -> [u8; 19] {
-        let mut buffer = [0_u8; 19];
-        buffer[0..2].copy_from_slice(self.de_header().serialize().as_slice());
-        buffer[2..19].copy_from_slice(&self.info);
-        buffer
-    }
-
-    fn de_header(&self) -> DeHeader {
-        DeHeader::new(
-            Self::DE_TYPE,
-            DeLength {
-                len: self.info.len().try_into().expect("encryption info is a valid length"),
-            },
-        )
-    }
-
-    /// Constructs the signature encryption scheme variant
-    pub fn signature(salt_bytes: &[u8; 16]) -> Self {
-        Self::new(SIGNATURE_ENCRYPTION_SCHEME, salt_bytes)
-    }
-
-    /// Constructs the mic encryption scheme variant
-    pub fn mic(salt_bytes: &[u8; 16]) -> Self {
-        Self::new(MIC_ENCRYPTION_SCHEME, salt_bytes)
-    }
-
-    fn new(scheme: u8, salt_bytes: &[u8; 16]) -> Self {
-        let mut sig_info = [0_u8; 17];
-        sig_info[0] = scheme;
-        sig_info[1..].copy_from_slice(salt_bytes);
-        Self { info: sig_info }
-    }
-}
-
 /// 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 6f085cd..018f92c 100644
--- a/nearby/presence/np_adv/src/extended/data_elements/tests.rs
+++ b/nearby/presence/np_adv/src/extended/data_elements/tests.rs
@@ -17,51 +17,52 @@
 extern crate std;
 
 use super::*;
-use crate::extended::serialize::AdvertisementType;
-use crate::{
-    extended::serialize::{section_tests::SectionBuilderExt, AdvBuilder, PublicSectionEncoder},
-    shared_data::TxPower,
-};
+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;
 
 #[test]
 fn serialize_tx_power_de() {
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-    let mut section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+    let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
 
     section_builder.add_de_res(|_| TxPower::try_from(3_i8).map(TxPowerDataElement::from)).unwrap();
 
     assert_eq!(
         &[
-            3,    // section header
-            0x03, // public identity
+            V1_ENCODING_UNENCRYPTED,
+            2,    // section len
             0x15, // len 1 type 0x05
             3
         ],
-        section_builder.into_section().as_slice()
+        section_builder.into_section::<CryptoProviderImpl>().as_slice()
     );
 }
 
 #[test]
 fn serialize_actions_de_empty() {
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-    let mut section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+    let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
 
     section_builder.add_de_res(|_| ActionsDataElement::try_from_actions(&[])).unwrap();
 
     assert_eq!(
         &[
-            2,    // section header
-            0x03, // public identity
-            0x06, // len 0 type 0x06
+            V1_ENCODING_UNENCRYPTED, //header
+            1,                       // section len
+            0x06,                    // len 0 type 0x06
         ],
-        section_builder.into_section().as_slice()
+        section_builder.into_section::<CryptoProviderImpl>().as_slice()
     );
 }
 
+#[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(PublicSectionEncoder::default()).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]))
@@ -69,19 +70,21 @@
 
     assert_eq!(
         &[
-            8,    // section header
-            0x03, // public identity
+            V1_ENCODING_UNENCRYPTED,
+            7, // section len
             0x66, // len 6 type 0x06
             1, 1, 2, 3, 5, 8 // fibonacci, of course
         ],
-        section_builder.into_section().as_slice()
+        section_builder.into_section::<CryptoProviderImpl>().as_slice()
     );
 }
 
+#[rustfmt::skip]
 #[test]
 fn serialize_context_sync_seq_num_de() {
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-    let mut section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+    let mut section_builder =
+        adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
 
     section_builder
         .add_de_res(|_| ContextSyncSeqNum::try_from(3).map(ContextSyncSeqNumDataElement::from))
@@ -89,59 +92,65 @@
 
     assert_eq!(
         &[
-            4,    // section header
-            0x03, // public identity
+            V1_ENCODING_UNENCRYPTED,
+            3, // section len
             0x81, 0x13, // len 1 type 0x13
             3,    // seq num
         ],
-        section_builder.into_section().as_slice()
+        section_builder.into_section::<CryptoProviderImpl>().as_slice()
     );
 }
 
+#[rustfmt::skip]
 #[test]
 fn serialize_connectivity_info_de_bluetooth() {
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-    let mut section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+    let mut section_builder =
+        adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
 
     section_builder.add_de(|_| ConnectivityInfoDataElement::bluetooth([1; 4], [2; 6])).unwrap();
 
     assert_eq!(
         &[
-            14,   // section header
-            0x03, // public identity
+            V1_ENCODING_UNENCRYPTED,
+            13, // section len
             0x8B, 0x11, // len 11 type 0x11
             1,    // connectivity type
             1, 1, 1, 1, // svc id
             2, 2, 2, 2, 2, 2 // mac
         ],
-        section_builder.into_section().as_slice()
+        section_builder.into_section::<CryptoProviderImpl>().as_slice()
     );
 }
 
+#[rustfmt::skip]
 #[test]
 fn serialize_connectivity_info_de_mdns() {
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-    let mut section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+    let mut section_builder =
+        adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
 
     section_builder.add_de(|_| ConnectivityInfoDataElement::mdns([1; 4], 2)).unwrap();
 
     assert_eq!(
         &[
-            9,    // section header
-            0x03, // public identity
+            V1_ENCODING_UNENCRYPTED,
+            8, // section len
             0x86, 0x11, // len 11 type 0x11
             2,    // connectivity type
             1, 1, 1, 1, // svc id
             2  // port
         ],
-        section_builder.into_section().as_slice()
+        section_builder.into_section::<CryptoProviderImpl>().as_slice()
     );
 }
 
+#[rustfmt::skip]
 #[test]
 fn serialize_connectivity_info_de_wifi_direct() {
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-    let mut section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+    let mut section_builder =
+        adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
 
     section_builder
         .add_de(|_| ConnectivityInfoDataElement::wifi_direct([1; 10], [2; 10], [3; 2], 4))
@@ -149,8 +158,8 @@
 
     assert_eq!(
         &[
-            27,   // section header
-            0x03, // public identity
+            V1_ENCODING_UNENCRYPTED,
+            26, // section len
             0x98, 0x11, // len 24 type 0x11
             3,    // connectivity type
             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // ssid
@@ -158,14 +167,16 @@
             3, 3, // freq
             4  // port
         ],
-        section_builder.into_section().as_slice()
+        section_builder.into_section::<CryptoProviderImpl>().as_slice()
     );
 }
 
+#[rustfmt::skip]
 #[test]
 fn serialize_connectivity_capabilities_de_wifi_direct() {
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-    let mut section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+    let mut section_builder =
+        adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
 
     section_builder
         .add_de(|_| ConnectivityCapabilityDataElement::wifi_direct([1; 3], [2; 3]))
@@ -173,13 +184,46 @@
 
     assert_eq!(
         &[
-            10,   // section header
-            0x03, // public identity
+            V1_ENCODING_UNENCRYPTED,
+            9, // section len
             0x87, 0x12, // len 7 type 0x12
             2,    // connectivity type
             1, 1, 1, // supported
             2, 2, 2, // connected
         ],
-        section_builder.into_section().as_slice()
+        section_builder.into_section::<CryptoProviderImpl>().as_slice()
     );
 }
+
+mod coverage_gaming {
+    use super::*;
+    use alloc::format;
+    #[test]
+    fn de_type_const_from() {
+        let _ = DeType::const_from(3);
+    }
+
+    #[test]
+    fn template() {}
+
+    #[test]
+    fn generic_de_error_derives() {
+        let err = GenericDataElementError::DataTooLong;
+        let _ = format!("{:?}", err);
+        assert_eq!(err, err);
+    }
+
+    #[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();
+        let _ = format!("{:?}", generic);
+    }
+}
diff --git a/nearby/presence/np_adv/src/extended/de_type.rs b/nearby/presence/np_adv/src/extended/de_type.rs
index 975df90..baefe6a 100644
--- a/nearby/presence/np_adv/src/extended/de_type.rs
+++ b/nearby/presence/np_adv/src/extended/de_type.rs
@@ -13,10 +13,9 @@
 // limitations under the License.
 
 //! V1 DE type types
-use crate::de_type::{EncryptedIdentityDataElementType, IdentityDataElementType};
 
 /// Data element types for extended advertisements
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 pub struct DeType {
     // 4 billion type codes should be enough for anybody
     code: u32,
@@ -52,98 +51,13 @@
     }
 }
 
-pub(crate) trait ExtendedDataElementType: Sized {
-    /// A type code for use in the DE header.
-    fn type_code(&self) -> DeType;
-    /// Returns the matching type for the code, else `None`
-    fn from_type_code(de_type: DeType) -> Option<Self>;
-}
-
-impl ExtendedDataElementType for IdentityDataElementType {
-    fn type_code(&self) -> DeType {
-        DeType::from(self.shared_type_code())
-    }
-
-    fn from_type_code(de_type: DeType) -> Option<Self> {
-        de_type.code.try_into().ok().and_then(Self::from_shared_type_code)
-    }
-}
-
-impl ExtendedDataElementType for EncryptedIdentityDataElementType {
-    fn type_code(&self) -> DeType {
-        DeType::from(self.as_identity_data_element_type().shared_type_code())
-    }
-
-    fn from_type_code(code: DeType) -> Option<Self> {
-        IdentityDataElementType::from_type_code(code)
-            .and_then(|idet| idet.as_encrypted_identity_de_type())
-    }
-}
-
 #[cfg(test)]
-mod tests {
-    extern crate std;
-
-    use super::*;
-    use std::{collections, fmt};
+mod test {
+    use crate::extended::de_type::DeType;
 
     #[test]
-    fn identity_type_codes_are_consistent() {
-        de_type_codes_are_consistent::<IdentityDataElementType>()
-    }
-
-    #[test]
-    fn encrypted_identity_type_codes_are_consistent() {
-        de_type_codes_are_consistent::<EncryptedIdentityDataElementType>()
-    }
-
-    #[test]
-    fn identity_type_codes_are_distinct() {
-        de_distinct_type_codes::<IdentityDataElementType>()
-    }
-
-    #[test]
-    fn encrypted_identity_type_codes_are_distinct() {
-        de_distinct_type_codes::<EncryptedIdentityDataElementType>()
-    }
-
-    #[test]
-    fn identity_no_accidentally_mapped_type_codes() {
-        de_no_accidentally_mapped_type_codes::<IdentityDataElementType>()
-    }
-
-    #[test]
-    fn encrypted_identity_no_accidentally_mapped_type_codes() {
-        de_no_accidentally_mapped_type_codes::<EncryptedIdentityDataElementType>()
-    }
-
-    fn de_type_codes_are_consistent<
-        D: PartialEq + fmt::Debug + ExtendedDataElementType + strum::IntoEnumIterator,
-    >() {
-        for det in D::iter() {
-            let actual = D::from_type_code(det.type_code());
-            assert_eq!(Some(det), actual)
-        }
-    }
-
-    fn de_distinct_type_codes<
-        D: PartialEq + fmt::Debug + ExtendedDataElementType + strum::IntoEnumIterator,
-    >() {
-        let codes = D::iter().map(|det| det.type_code()).collect::<collections::HashSet<_>>();
-        assert_eq!(codes.len(), D::iter().count());
-    }
-
-    fn de_no_accidentally_mapped_type_codes<
-        D: PartialEq + fmt::Debug + ExtendedDataElementType + strum::IntoEnumIterator,
-    >() {
-        let codes = D::iter().map(|det| det.type_code()).collect::<collections::HashSet<_>>();
-        // not going to try all 4 billion possibilities, but we can make an effort
-        for possible_code in 0_u32..100_000 {
-            if codes.contains(&possible_code.into()) {
-                continue;
-            }
-
-            assert_eq!(None, D::from_type_code(possible_code.into()));
-        }
+    fn u32_from_de_type() {
+        let de = DeType::from(8u32);
+        let _val: u32 = de.into();
     }
 }
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
new file mode 100644
index 0000000..e717fa1
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/deserialize/data_element/mod.rs
@@ -0,0 +1,260 @@
+// 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.
+
+//! Parsing logic for V1 data elements, header + contents
+
+use crate::extended::{de_requires_extended_bit, de_type::DeType, deserialize, DeLength};
+use array_view::ArrayView;
+use nom::{branch, bytes, combinator, error, number, sequence};
+use np_hkdf::v1_salt;
+
+#[cfg(test)]
+mod tests;
+
+/// A deserialized data element in a section.
+///
+/// The DE has been processed to the point of exposing a DE type and its contents as a `&[u8]`, but
+/// no DE-type-specific processing has been performed.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct DataElement<'adv> {
+    offset: v1_salt::DataElementOffset,
+    de_type: DeType,
+    contents: &'adv [u8],
+}
+
+impl<'adv> DataElement<'adv> {
+    pub(crate) fn new(
+        offset: v1_salt::DataElementOffset,
+        de_type: DeType,
+        contents: &'adv [u8],
+    ) -> Self {
+        Self { offset, de_type, contents }
+    }
+}
+
+impl DeHeader {
+    pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], DeHeader> {
+        // 1-byte header: 0b0LLLTTTT
+        let parse_single_byte_de_header =
+            combinator::map_opt::<&[u8], _, DeHeader, error::Error<&[u8]>, _, _>(
+                combinator::consumed(combinator::map_res(
+                    combinator::verify(number::complete::u8, |&b| !deserialize::hi_bit_set(b)),
+                    |b| {
+                        // L bits
+                        let len = (b >> 4) & 0x07;
+                        // T bits
+                        let de_type = ((b & 0x0F) as u32).into();
+
+                        len.try_into().map(|l| (l, de_type))
+                    },
+                )),
+                |(header_bytes, (len, de_type))| {
+                    ArrayView::try_from_slice(header_bytes).map(|header_bytes| DeHeader {
+                        header_bytes,
+                        contents_len: len,
+                        de_type,
+                    })
+                },
+            );
+
+        // multi-byte headers: 0b1LLLLLLL (0b1TTTTTTT)* 0b0TTTTTTT
+        // leading 1 in first byte = multibyte format
+        // leading 1 in subsequent bytes = there is at least 1 more type bytes
+        // leading 0 = this is the last header byte
+        // 127-bit length, effectively infinite type bit length
+
+        // It's conceivable to have non-canonical extended type sequences where 1 or more leading
+        // bytes don't have any bits set (other than the marker hi bit), thereby contributing nothing
+        // to the final value.
+        // To prevent that, we require that either there be only 1 type byte, or that the first of the
+        // multiple type bytes must have a value bit set. It's OK to have no value bits in subsequent
+        // type bytes.
+
+        let parse_ext_de_header = combinator::verify(
+            combinator::map_opt(
+                combinator::consumed(sequence::pair(
+                    // length byte w/ leading 1
+                    combinator::map_res(
+                        combinator::verify(number::complete::u8::<&[u8], _>, |&b| {
+                            deserialize::hi_bit_set(b)
+                        }),
+                        // snag the lower 7 bits
+                        |b| (b & 0x7F).try_into(),
+                    ),
+                    branch::alt((
+                        // 1 type byte case
+                        combinator::recognize(
+                            // 0-hi-bit type code byte
+                            combinator::verify(number::complete::u8, |&b| {
+                                !deserialize::hi_bit_set(b)
+                            }),
+                        ),
+                        // multiple type byte case: leading type byte must have at least 1 value bit
+                        combinator::recognize(sequence::tuple((
+                            // hi bit and at least 1 value bit, otherwise it would be non-canonical
+                            combinator::verify(number::complete::u8, |&b| {
+                                deserialize::hi_bit_set(b) && (b & 0x7F != 0)
+                            }),
+                            // 0-3 1-hi-bit type code bytes with any bit pattern. Max is 3 since two 7
+                            // bit type chunks are processed before and after this, for a total of 5,
+                            // and that's as many 7-bit chunks as are needed to support a 32-bit type.
+                            bytes::complete::take_while_m_n(0, 3, deserialize::hi_bit_set),
+                            // final 0-hi-bit type code byte
+                            combinator::verify(number::complete::u8, |&b| {
+                                !deserialize::hi_bit_set(b)
+                            }),
+                        ))),
+                    )),
+                )),
+                |(header_bytes, (len, type_bytes))| {
+                    // snag the low 7 bits of each type byte and accumulate
+                    type_bytes
+                        .iter()
+                        .try_fold(0_u64, |accum, b| {
+                            accum.checked_shl(7).map(|n| n + ((b & 0x7F) as u64))
+                        })
+                        .and_then(|type_code| u32::try_from(type_code).ok())
+                        .and_then(|type_code| {
+                            ArrayView::try_from_slice(header_bytes).map(|header_bytes| DeHeader {
+                                header_bytes,
+                                contents_len: len,
+                                de_type: type_code.into(),
+                            })
+                        })
+                },
+            ),
+            |header| {
+                // verify that the length and type code actually require use of the extended bit
+                de_requires_extended_bit(header.de_type.as_u32(), header.contents_len.len)
+            },
+        );
+
+        branch::alt((parse_single_byte_de_header, parse_ext_de_header))(input)
+    }
+}
+
+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
+/// processing.
+#[derive(Debug)]
+pub struct DataElementParsingIterator<'adv> {
+    input: &'adv [u8],
+    // The index of the data element this is currently at
+    offset: u8,
+}
+
+impl<'adv> DataElementParsingIterator<'adv> {
+    pub(crate) fn new(input: &'adv [u8]) -> Self {
+        Self { input, offset: 0 }
+    }
+}
+
+impl<'adv> Iterator for DataElementParsingIterator<'adv> {
+    type Item = Result<DataElement<'adv>, DataElementParseError>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match ProtoDataElement::parse(self.input) {
+            Ok((rem, pde)) => {
+                self.input = rem;
+                let current_offset = self.offset;
+                self.offset = if let Some(offset) = self.offset.checked_add(1) {
+                    offset
+                } else {
+                    return Some(Err(DataElementParseError::TooManyDataElements));
+                };
+                Some(Ok(pde.into_data_element(v1_salt::DataElementOffset::from(current_offset))))
+            }
+            Err(nom::Err::Failure(e)) => Some(Err(DataElementParseError::NomError(e.code))),
+            Err(nom::Err::Incomplete(_)) => {
+                panic!("Should always complete since we are parsing using the `nom::complete` APIs")
+            }
+            Err(nom::Err::Error(_)) => {
+                // nom `Error` is recoverable, it usually means we should move on the parsing the
+                // next section. There is nothing after data elements within a section, so we just
+                // check that there is no remaining data.
+                if !self.input.is_empty() {
+                    return Some(Err(DataElementParseError::UnexpectedDataAfterEnd));
+                }
+                None
+            }
+        }
+    }
+}
+
+/// The error that may arise while parsing data elements.
+#[derive(Debug, PartialEq, Eq)]
+pub enum DataElementParseError {
+    /// Unexpected data found after the end of the data elements portion. This means either the
+    /// parser was fed with additional data (it should only be given the bytes within a section,
+    /// not the whole advertisement), or the length field in the header of the data element is
+    /// malformed.
+    UnexpectedDataAfterEnd,
+    /// There are too many data elements in the advertisement. The maximum number supported by the
+    /// current parsing logic is 255.
+    TooManyDataElements,
+    /// A parse error is returned during nom.
+    NomError(error::ErrorKind),
+}
+
+/// 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.
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub(crate) struct DeHeader {
+    /// The original bytes of the header, at most 6 bytes long (1 byte len, 5 bytes type)
+    pub(crate) header_bytes: ArrayView<u8, 6>,
+    pub(crate) de_type: DeType,
+    pub(crate) contents_len: DeLength,
+}
+
+/// An intermediate stage in parsing a [DataElement] that lacks `offset`.
+#[derive(Debug, PartialEq, Eq)]
+pub struct ProtoDataElement<'d> {
+    header: DeHeader,
+    /// `len()` must equal `header.contents_len`
+    contents: &'d [u8],
+}
+
+impl<'d> ProtoDataElement<'d> {
+    pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], ProtoDataElement> {
+        let (remaining, header) = DeHeader::parse(input)?;
+        let len = header.contents_len;
+        combinator::map(bytes::complete::take(len.as_u8()), move |slice| {
+            let header_clone = header.clone();
+            ProtoDataElement { header: header_clone, contents: slice }
+        })(remaining)
+    }
+
+    fn into_data_element(self, offset: v1_salt::DataElementOffset) -> DataElement<'d> {
+        DataElement::new(offset, self.header.de_type, self.contents)
+    }
+}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/data_element/tests.rs b/nearby/presence/np_adv/src/extended/deserialize/data_element/tests.rs
new file mode 100644
index 0000000..473cf37
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/deserialize/data_element/tests.rs
@@ -0,0 +1,348 @@
+// 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 super::*;
+use alloc::{vec, vec::Vec};
+
+#[test]
+fn parse_de_single_byte_header_length_overrun() {
+    // length 7, type 0x03
+    let input = [0b0111_0011, 0x01, 0x02];
+    assert_eq!(
+        nom::Err::Error(error::Error {
+            // attempted to read DE contents
+            input: &input.as_slice()[1..],
+            code: error::ErrorKind::Eof,
+        }),
+        ProtoDataElement::parse(&input).unwrap_err()
+    );
+}
+
+#[test]
+fn parse_de_multi_byte_header_length_overrun() {
+    let input = [0b1000_0111, 0x1F, 0x01, 0x02];
+    assert_eq!(
+        nom::Err::Error(error::Error {
+            // attempted to read DE contents
+            input: &input.as_slice()[2..],
+            code: error::ErrorKind::Eof,
+        }),
+        ProtoDataElement::parse(&input).unwrap_err()
+    );
+}
+
+#[test]
+fn parse_de_with_1_byte_header() {
+    let data = [0x51, 0x01, 0x02, 0x03, 0x04, 0x05, 0xFF, 0xFF];
+    assert_eq!(
+        Ok((
+            &data[6..],
+            ProtoDataElement {
+                header: DeHeader {
+                    de_type: 1_u8.into(),
+                    header_bytes: ArrayView::try_from_slice(&[0x51]).unwrap(),
+                    contents_len: 5_u8.try_into().unwrap(),
+                },
+                contents: &data[1..6],
+            }
+        )),
+        ProtoDataElement::parse(&data)
+    );
+}
+
+#[test]
+fn parse_de_with_2_byte_header() {
+    let data = [0x85, 0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0xFF, 0xFF];
+    assert_eq!(
+        Ok((
+            &data[7..],
+            ProtoDataElement {
+                header: DeHeader {
+                    de_type: 16_u8.into(),
+                    header_bytes: ArrayView::try_from_slice(&[0x85, 0x10]).unwrap(),
+                    contents_len: 5_u8.try_into().unwrap(),
+                },
+                contents: &data[2..7],
+            }
+        )),
+        ProtoDataElement::parse(&data)
+    );
+}
+
+#[test]
+fn parse_de_with_3_byte_header() {
+    let data = [0x85, 0xC1, 0x41, 0x01, 0x02, 0x03, 0x04, 0x05, 0xFF, 0xFF];
+    assert_eq!(
+        Ok((
+            &data[8..],
+            ProtoDataElement {
+                header: DeHeader {
+                    header_bytes: ArrayView::try_from_slice(&[0x85, 0xC1, 0x41]).unwrap(),
+                    contents_len: 5_u8.try_into().unwrap(),
+                    de_type: 0b0000_0000_0000_0000_0010_0000_1100_0001_u32.into(),
+                },
+                contents: &data[3..8],
+            }
+        )),
+        ProtoDataElement::parse(&data)
+    );
+}
+
+#[test]
+fn parse_de_header_1_byte() {
+    let data = [0x51, 0xFF, 0xFF];
+    assert_eq!(
+        Ok((
+            &data[1..],
+            DeHeader {
+                de_type: 1_u8.into(),
+                contents_len: 5_u8.try_into().unwrap(),
+                header_bytes: ArrayView::try_from_slice(&[0x51]).unwrap(),
+            }
+        )),
+        DeHeader::parse(&data)
+    );
+}
+
+#[test]
+fn parse_de_header_2_bytes() {
+    let data = [0x88, 0x01];
+    assert_eq!(
+        Ok((
+            &data[2..],
+            DeHeader {
+                de_type: 1_u8.into(),
+                contents_len: 8_u8.try_into().unwrap(),
+                header_bytes: ArrayView::try_from_slice(&[0x88, 0x01]).unwrap(),
+            }
+        )),
+        DeHeader::parse(&data)
+    );
+}
+
+#[test]
+fn parse_de_header_3_bytes() {
+    let data = [0x83, 0xC1, 0x41, 0xFF, 0xFF];
+    assert_eq!(
+        Ok((
+            &data[3..],
+            DeHeader {
+                de_type: 0b0000_0000_0000_0000_0010_0000_1100_0001_u32.into(),
+                contents_len: 3_u8.try_into().unwrap(),
+                header_bytes: ArrayView::try_from_slice(&[0x83, 0xC1, 0x41]).unwrap(),
+            }
+        )),
+        DeHeader::parse(&data)
+    );
+}
+
+#[test]
+fn parse_de_header_4_bytes() {
+    let data = [0x83, 0xC1, 0xC1, 0x41, 0xFF, 0xFF];
+    assert_eq!(
+        Ok((
+            &data[4..],
+            DeHeader {
+                de_type: 0b0000_0000_0001_0000_0110_0000_1100_0001_u32.into(),
+                contents_len: 3_u8.try_into().unwrap(),
+                header_bytes: ArrayView::try_from_slice(&[0x83, 0xC1, 0xC1, 0x41]).unwrap(),
+            }
+        )),
+        DeHeader::parse(&data)
+    );
+}
+
+#[test]
+fn parse_de_header_max_length_extension() {
+    // 1 byte length + 5 bytes of type is the max possible length of an extended DE header.
+    // The first 3 bits of type will be discarded by the left shift, so if an extended DE header
+    // does make it to 5 bytes, we will only read the least significant 32 bits out of a possible 35.
+    // The contents of the most significant 3 bits of type code must be 0's, otherwise the adv will
+    // be discarded
+    let data = [0x80, 0x8F, 0xFF, 0xFF, 0xFF, 0x7F];
+    assert_eq!(
+        Ok((
+            &data[6..],
+            DeHeader {
+                de_type: u32::MAX.into(),
+                contents_len: 0_u8.try_into().unwrap(),
+                header_bytes: ArrayView::try_from_slice(&[0x80, 0x8F, 0xFF, 0xFF, 0xFF, 0x7F])
+                    .unwrap(),
+            }
+        )),
+        DeHeader::parse(&data)
+    );
+}
+
+// In a 6 byte extended de header, if any bits are non-zero in the most 3 significant bits of type,
+// the parsing will fail as this would be out of range of a valid u32 value.
+#[test]
+fn parse_de_6_byte_header_invalid_significant_bits() {
+    assert!(DeHeader::parse(&[0x80, 0x9F, 0xFF, 0xFF, 0xFF, 0x7F]).is_err());
+    assert!(DeHeader::parse(&[0x80, 0xCF, 0xFF, 0xFF, 0xFF, 0x7F]).is_err());
+    assert!(DeHeader::parse(&[0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F]).is_err());
+}
+
+#[test]
+fn parse_de_header_over_max_length_extension() {
+    // last header byte cannot contain a leading 1
+    let data = [0x80, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF];
+    assert_eq!(
+        nom::Err::Error(error::Error {
+            // failed parsing the last byte
+            input: &data.as_slice()[5..],
+            code: error::ErrorKind::Verify,
+        }),
+        DeHeader::parse(&data).unwrap_err()
+    );
+}
+
+// Test edge cases which can be represented in fewer bytes
+#[test]
+fn parse_de_header_invalid_multi_byte_type_code() {
+    // extended length and type codes which can be represented in a single byte
+    assert!(DeHeader::parse(&[0b1000_0000, 0b000_0000]).is_err());
+    assert!(DeHeader::parse(&[0b1000_0000, 0b000_0001]).is_err());
+    assert!(DeHeader::parse(&[0b1000_0111, 0b000_1111]).is_err());
+
+    // first byte of type doesn't have any bits in it so it contributes nothing
+    assert!(DeHeader::parse(&[0b1000_0111, 0b1000_0000, 0b0100_0000]).is_err());
+    // first 2 bytes of type are unnecessary
+    assert!(DeHeader::parse(&[0b1000_0001, 0b1000_0000, 0b1000_0000, 0b1100_0000]).is_err());
+
+    // extended bit set with no following byte
+    assert!(DeHeader::parse(&[0b1000_0001, 0b1000_0000]).is_err());
+
+    // needs one extra bit of length so must use extended
+    assert!(DeHeader::parse(&[0b100_1111, 0b000_1111]).is_ok());
+
+    // needs one extra bit of type so must use extended
+    assert!(DeHeader::parse(&[0b100_0111, 0b001_1111]).is_ok());
+
+    // valid to trail with all 0's for a larger type code
+    assert!(DeHeader::parse(&[0b100_1111, 0b1000_0001, 0b0000_0000]).is_ok());
+
+    // valid to trail with all 0's in middle of type code
+    assert!(DeHeader::parse(&[0b100_1111, 0b1000_0001, 0b1000_0000, 0b0000_0001]).is_ok());
+}
+
+#[test]
+fn de_iteration_exposes_correct_data() {
+    let mut de_data = vec![];
+    // de 1 byte header, type 5, len 5
+    de_data.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
+    // de 2 byte header, type 16, len 1
+    de_data.extend_from_slice(&[0x81, 0x10, 0x01]);
+
+    let iterator = DataElementParsingIterator::new(&de_data);
+    let des = iterator.collect::<Result<Vec<_>, _>>().unwrap();
+
+    assert_eq!(
+        vec![
+            DataElement::new(0.into(), 5_u32.into(), &[0x01, 0x02, 0x03, 0x04, 0x05]),
+            DataElement::new(1.into(), 16_u32.into(), &[0x01]),
+        ],
+        des
+    );
+}
+
+#[test]
+fn de_iteration_single_de() {
+    let mut de_data = vec![];
+    // de 2 byte header, type 16, len 1
+    de_data.extend_from_slice(&[0x81, 0x10, 0x01]);
+
+    let iterator = DataElementParsingIterator::new(&de_data);
+    let des = iterator.collect::<Result<Vec<_>, _>>().unwrap();
+
+    assert_eq!(vec![DataElement::new(0.into(), 16_u32.into(), &[0x01]),], des);
+}
+
+#[test]
+fn de_iteration_single_de_empty_contents() {
+    let mut de_data = vec![];
+    // de 1 byte header, type 1, len 0
+    de_data.extend_from_slice(&[0x01]);
+
+    let iterator = DataElementParsingIterator::new(&de_data);
+    let des = iterator.collect::<Result<Vec<_>, _>>().unwrap();
+
+    assert_eq!(vec![DataElement::new(0.into(), 1_u32.into(), &[])], des);
+}
+
+#[test]
+fn de_iteration_max_number_des() {
+    let mut de_data = vec![];
+    // de 1 byte header, type 1, len 0
+    // add this 255 times which is the max amount of
+    // supported DEs in a single section
+    for _ in 0..255 {
+        de_data.extend_from_slice(&[0x01]);
+    }
+
+    let iterator = DataElementParsingIterator::new(&de_data);
+    assert!(iterator.collect::<Result<Vec<_>, _>>().is_ok());
+}
+
+#[test]
+fn de_iteration_over_max_number_des() {
+    let mut de_data = vec![];
+    // de 1 byte header, type 1, len 0
+    // add this 256 times to exceed max number of des in a section
+    for _ in 0..256 {
+        de_data.extend_from_slice(&[0x01]);
+    }
+
+    let iterator = DataElementParsingIterator::new(&de_data);
+    assert_eq!(
+        iterator.collect::<Result<Vec<_>, _>>(),
+        Err(DataElementParseError::TooManyDataElements)
+    );
+}
+
+#[test]
+fn de_parse_error_invalid_length_header() {
+    let mut de_data = vec![];
+    // de 1 byte header, type 1, len 2, but only one byte left to process
+    de_data.extend_from_slice(&[0x21, 0x00]);
+
+    let iterator = DataElementParsingIterator::new(&de_data);
+    assert_eq!(
+        iterator.collect::<Result<Vec<_>, _>>(),
+        Err(DataElementParseError::UnexpectedDataAfterEnd)
+    );
+}
+
+mod coverage_gaming {
+    use super::*;
+    use alloc::format;
+
+    #[allow(clippy::clone_on_copy)]
+    #[test]
+    fn data_element_debug_and_clone() {
+        let de = DataElement::new(0.into(), 0_u32.into(), &[]);
+        let _ = format!("{:?}", de);
+        let _ = de.clone();
+        let iterator = DataElementParsingIterator::new(&[]);
+        let _ = format!("{:?}", iterator);
+        let _ = format!("{:?}", DataElementParseError::TooManyDataElements);
+        let (_, header) = DeHeader::parse(&[0x11, 0xFF]).unwrap();
+        let _ = format!("{:?}", header);
+        let _ = header.clone();
+        let (_, pde) = ProtoDataElement::parse(&[0x11, 0xFF]).unwrap();
+        let _ = format!("{:?}", pde);
+    }
+}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/dev_tools.rs b/nearby/presence/np_adv/src/extended/deserialize/dev_tools.rs
new file mode 100644
index 0000000..e77e083
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/deserialize/dev_tools.rs
@@ -0,0 +1,87 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use core::fmt::Debug;
+
+use crate::deserialization_arena::DeserializationArena;
+use array_view::ArrayView;
+use crypto_provider::CryptoProvider;
+
+use crate::extended::deserialize::section::intermediate::{
+    parse_sections, CiphertextSection, IntermediateSection,
+};
+use crate::extended::NP_ADV_MAX_SECTION_LEN;
+use crate::{credential::book::CredentialBook, header::V1AdvHeader};
+
+/// Error in decryption operations for `deser_decrypt_v1_section_bytes_for_dev_tools`.
+#[derive(Debug, Clone)]
+pub enum AdvDecryptionError {
+    /// Cannot decrypt because the input section is not encrypted.
+    InputNotEncrypted,
+    /// Error parsing the given section.
+    ParseError,
+    /// No suitable credential found to decrypt the given section.
+    NoMatchingCredentials,
+}
+
+/// The encryption scheme used for a V1 advertisement.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum V1EncryptionScheme {
+    /// Indicates MIC-based encryption and verification.
+    Mic,
+    /// Indicates signature-based encryption and verification.
+    Signature,
+}
+
+/// Decrypt, but do not further deserialize the v1 bytes, intended for developer tooling uses only.
+/// Production uses should use [crate::deserialize_advertisement] instead, which deserializes to a
+/// structured format and provides extra type safety.
+pub fn deser_decrypt_v1_section_bytes_for_dev_tools<'adv, 'cred, B, P>(
+    arena: DeserializationArena<'adv>,
+    cred_book: &'cred B,
+    header_byte: u8,
+    section_bytes: &'adv [u8],
+) -> Result<(ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, V1EncryptionScheme), AdvDecryptionError>
+where
+    B: CredentialBook<'cred>,
+    P: CryptoProvider,
+{
+    let header = V1AdvHeader::new(header_byte);
+    let int_sections =
+        parse_sections(header, section_bytes).map_err(|_| AdvDecryptionError::ParseError)?;
+    let cipher_section = match &int_sections[0] {
+        IntermediateSection::Plaintext(_) => Err(AdvDecryptionError::InputNotEncrypted)?,
+        IntermediateSection::Ciphertext(section) => section,
+    };
+
+    let mut allocator = arena.into_allocator();
+    for (crypto_material, _) in cred_book.v1_iter() {
+        if let Some(plaintext) = cipher_section
+            .try_resolve_identity_and_decrypt::<_, P>(&mut allocator, &crypto_material)
+        {
+            let pt = plaintext.expect(concat!(
+                "Should not run out of space because DeserializationArenaAllocator is big ",
+                "enough to hold a single advertisement, and we exit immediately upon ",
+                "successful decryption",
+            ));
+
+            let encryption_scheme = match cipher_section {
+                CiphertextSection::SignatureEncrypted(_) => V1EncryptionScheme::Signature,
+                CiphertextSection::MicEncrypted(_) => V1EncryptionScheme::Mic,
+            };
+            return Ok((pt, encryption_scheme));
+        }
+    }
+    Err(AdvDecryptionError::NoMatchingCredentials)
+}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mic_decrypt_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mic_decrypt_tests.rs
deleted file mode 100644
index b172db6..0000000
--- a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mic_decrypt_tests.rs
+++ /dev/null
@@ -1,471 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#![allow(clippy::unwrap_used)]
-
-extern crate std;
-
-use super::*;
-use crate::deserialization_arena;
-use crate::extended::data_elements::TxPowerDataElement;
-use crate::extended::deserialize::DataElementParseError;
-use crate::extended::serialize::{AdvertisementType, SingleTypeDataElement, WriteDataElement};
-use crate::shared_data::TxPower;
-use crate::{
-    credential::{v1::V1, SimpleBroadcastCryptoMaterial},
-    de_type::EncryptedIdentityDataElementType,
-    extended::{
-        deserialize::{
-            encrypted_section::{
-                EncryptedSectionContents, IdentityResolutionOrDeserializationError,
-                MicVerificationError,
-            },
-            parse_sections,
-            test_stubs::IntermediateSectionExt,
-            CiphertextSection, DataElement, RawV1Salt,
-        },
-        serialize::{AdvBuilder, CapacityLimitedVec, MicEncryptedSectionEncoder},
-    },
-    parse_adv_header, AdvHeader, Section,
-};
-use crypto_provider_default::CryptoProviderImpl;
-use np_hkdf::v1_salt::DataElementOffset;
-use sink::Sink;
-use std::{prelude::rust_2021::*, vec};
-
-#[test]
-fn deserialize_mic_encrypted_correct_keys() {
-    let metadata_key = MetadataKey([1; 16]);
-    let key_seed = [2; 32];
-    let raw_salt = RawV1Salt([3; 16]);
-    let section_salt = V1Salt::<CryptoProviderImpl>::from(raw_salt);
-    let identity_type = EncryptedIdentityDataElementType::Private;
-    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
-
-    let mut section_builder = adv_builder
-        .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new(
-            identity_type,
-            raw_salt,
-            &broadcast_cm,
-        ))
-        .unwrap();
-
-    let txpower_de = TxPowerDataElement::from(TxPower::try_from(5).unwrap());
-    section_builder.add_de(|_| txpower_de.clone()).unwrap();
-    section_builder.add_to_advertisement();
-
-    let adv = adv_builder.into_advertisement();
-
-    let (remaining, header) = parse_adv_header(adv.as_slice()).unwrap();
-
-    let adv_header = if let AdvHeader::V1(h) = header {
-        h
-    } else {
-        panic!("incorrect header");
-    };
-
-    let sections = parse_sections(adv_header, remaining).unwrap();
-    assert_eq!(1, sections.len());
-
-    let section = sections.into_iter().next().unwrap();
-    let enc_section = section.as_ciphertext().unwrap();
-
-    let contents = if let CiphertextSection::MicEncryptedIdentity(contents) = &enc_section {
-        contents
-    } else {
-        panic!("incorrect flavor");
-    };
-
-    let keypair = np_ed25519::KeyPair::<CryptoProviderImpl>::generate();
-
-    // deserializing to Section works
-    let discovery_credential =
-        SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, keypair.private_key())
-            .derive_v1_discovery_credential::<CryptoProviderImpl>();
-
-    let identity_resolution_material =
-        discovery_credential.unsigned_identity_resolution_material::<CryptoProviderImpl>();
-    let verification_material =
-        discovery_credential.unsigned_verification_material::<CryptoProviderImpl>();
-
-    let arena = deserialization_arena!();
-    let mut allocator = arena.into_allocator();
-    let section = contents
-        .try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
-            &mut allocator,
-            &identity_resolution_material,
-            &verification_material,
-        )
-        .unwrap();
-
-    assert_eq!(
-        DecryptedSection::new(
-            EncryptedIdentityDataElementType::Private,
-            VerificationMode::Mic,
-            metadata_key,
-            raw_salt,
-            SectionContents {
-                section_header: 19 // encryption info de
-                        + 2 // de header
-                        + 16 // metadata key
-                        + 2 // de contents
-                        + 16, // mic hmac tag
-                // battery DE
-                data_element_start_offset: 2,
-                de_region_excl_identity: &[txpower_de.de_header().serialize().as_slice(), &[5],]
-                    .concat(),
-            }
-        ),
-        section
-    );
-    let data_elements = section.collect_data_elements().unwrap();
-    assert_eq!(
-        data_elements,
-        &[DataElement { offset: 2.into(), de_type: 0x05_u8.into(), contents: &[5] }]
-    );
-
-    assert_eq!(
-        vec![(DataElementOffset::from(2_u8), TxPowerDataElement::DE_TYPE, vec![5_u8])],
-        data_elements
-            .into_iter()
-            .map(|de| (de.offset(), de.de_type(), de.contents().to_vec()))
-            .collect::<Vec<_>>()
-    );
-
-    let mut encryption_info_bytes = [0_u8; 19];
-    encryption_info_bytes[0..2].copy_from_slice(&[0x91, 0x10]);
-    encryption_info_bytes[2] = 0x00;
-    encryption_info_bytes[3..].copy_from_slice(section_salt.as_slice());
-
-    let ciphertext_end = adv.as_slice().len() - 16;
-    assert_eq!(
-        &MicEncryptedSection {
-            contents: EncryptedSectionContents {
-                section_header: 19 // encryption info de
-                    + 2 // de header
-                    + 16 // metadata key
-                    + 2 // de contents
-                    + 16, // mic hmac tag
-                adv_header,
-                encryption_info: EncryptionInfo { bytes: encryption_info_bytes },
-                identity: EncryptedIdentityMetadata {
-                    header_bytes: [0x90, 0x1],
-                    offset: 1.into(),
-                    identity_type: EncryptedIdentityDataElementType::Private,
-                },
-                all_ciphertext: &adv.as_slice()[1 + 1 + 19 + 2..ciphertext_end],
-            },
-            mic: SectionMic { mic: adv.as_slice()[ciphertext_end..].try_into().unwrap() }
-        },
-        contents
-    );
-
-    // plaintext is correct
-    {
-        let identity_resolution_contents =
-            contents.contents.compute_identity_resolution_contents::<CryptoProviderImpl>();
-        let identity_match = identity_resolution_contents
-            .try_match::<CryptoProviderImpl>(
-                &identity_resolution_material.into_raw_resolution_material(),
-            )
-            .unwrap();
-        let arena = deserialization_arena!();
-        let mut allocator = arena.into_allocator();
-        let decrypted = contents
-            .contents
-            .decrypt_ciphertext::<CryptoProviderImpl>(&mut allocator, identity_match)
-            .unwrap();
-
-        let mut expected = Vec::new();
-        // battery de
-        expected.extend_from_slice(txpower_de.clone().de_header().serialize().as_slice());
-        let _ = txpower_de.write_de_contents(&mut expected);
-
-        assert_eq!(metadata_key, decrypted.metadata_key_plaintext);
-        assert_eq!(&expected, decrypted.plaintext_contents);
-    }
-}
-
-#[test]
-fn deserialize_mic_encrypted_incorrect_aes_key_error() {
-    // bad aes key -> bad metadata key plaintext
-    do_bad_deserialize_params::<CryptoProviderImpl>(
-        IdentityResolutionOrDeserializationError::IdentityMatchingError,
-        Some([0xFF; 16].into()),
-        None,
-        None,
-        None,
-    );
-}
-
-#[test]
-fn deserialize_mic_encrypted_incorrect_metadata_key_hmac_key_error() {
-    // bad metadata key hmac key -> bad calculated metadata key mac
-    do_bad_deserialize_params::<CryptoProviderImpl>(
-        IdentityResolutionOrDeserializationError::IdentityMatchingError,
-        None,
-        Some([0xFF; 32].into()),
-        None,
-        None,
-    );
-}
-
-#[test]
-fn deserialize_mic_encrypted_incorrect_mic_hmac_key_error() {
-    // bad mic hmac key -> bad calculated mic
-    do_bad_deserialize_params::<CryptoProviderImpl>(
-        MicVerificationError::MicMismatch.into(),
-        None,
-        None,
-        Some([0xFF; 32].into()),
-        None,
-    );
-}
-
-#[test]
-fn deserialize_mic_encrypted_incorrect_expected_metadata_key_hmac_error() {
-    // bad expected metadata key mac
-    do_bad_deserialize_params::<CryptoProviderImpl>(
-        IdentityResolutionOrDeserializationError::IdentityMatchingError,
-        None,
-        None,
-        None,
-        Some([0xFF; 32]),
-    );
-}
-
-#[test]
-fn deserialize_mic_encrypted_incorrect_salt_error() {
-    // bad salt -> bad iv -> bad metadata key plaintext
-    do_bad_deserialize_tampered(
-        DeserializeError::IdentityResolutionOrDeserializationError(
-            IdentityResolutionOrDeserializationError::IdentityMatchingError,
-        ),
-        |_| {},
-        |adv| adv[23..39].copy_from_slice(&[0xFF; 16]),
-    );
-}
-
-#[test]
-fn deserialize_mic_encrypted_de_that_wont_parse() {
-    // add an extra byte to the section, leading it to try to parse a DE that doesn't exist
-    do_bad_deserialize_tampered(
-        DeserializeError::DataElementParseError(DataElementParseError::UnexpectedDataAfterEnd),
-        |sec| sec.try_push(0xFF).unwrap(),
-        |_| {},
-    );
-}
-
-#[test]
-fn deserialize_mic_encrypted_tampered_mic_error() {
-    // flip the a bit in the first MIC byte
-    do_bad_deserialize_tampered(
-        DeserializeError::IdentityResolutionOrDeserializationError(
-            MicVerificationError::MicMismatch.into(),
-        ),
-        |_| {},
-        |adv| {
-            let mic_start = adv.len() - 16;
-            adv[mic_start] ^= 0x01
-        },
-    );
-}
-
-#[test]
-fn deserialize_mic_encrypted_tampered_payload_error() {
-    // flip the last payload bit
-    do_bad_deserialize_tampered(
-        DeserializeError::IdentityResolutionOrDeserializationError(
-            MicVerificationError::MicMismatch.into(),
-        ),
-        |_| {},
-        |adv| *adv.last_mut().unwrap() ^= 0x01,
-    );
-}
-
-/// Attempt a decryption that will fail when using the provided parameters for decryption only.
-/// `None` means use the correct value for that parameter.
-fn do_bad_deserialize_params<C: CryptoProvider>(
-    error: IdentityResolutionOrDeserializationError<MicVerificationError>,
-    aes_key: Option<crypto_provider::aes::Aes128Key>,
-    metadata_key_hmac_key: Option<np_hkdf::NpHmacSha256Key<C>>,
-    mic_hmac_key: Option<np_hkdf::NpHmacSha256Key<C>>,
-    expected_metadata_key_hmac: Option<[u8; 32]>,
-) {
-    let metadata_key = MetadataKey([1; 16]);
-    let key_seed = [2; 32];
-    let section_salt: V1Salt<C> = [3; 16].into();
-    let identity_type = EncryptedIdentityDataElementType::Private;
-    let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::new(&key_seed);
-
-    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
-
-    let mut section_builder = adv_builder
-        .section_builder(MicEncryptedSectionEncoder::<C>::new(
-            identity_type,
-            section_salt.into(),
-            &broadcast_cm,
-        ))
-        .unwrap();
-
-    section_builder.add_de(|_| TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
-
-    section_builder.add_to_advertisement();
-
-    let adv = adv_builder.into_advertisement();
-
-    let (remaining, header) = parse_adv_header(adv.as_slice()).unwrap();
-
-    let v1_header = if let AdvHeader::V1(h) = header {
-        h
-    } else {
-        panic!("incorrect header");
-    };
-
-    let sections = parse_sections(v1_header, remaining).unwrap();
-    assert_eq!(1, sections.len());
-
-    let section = sections.into_iter().next().unwrap();
-    let enc_section = section.as_ciphertext().unwrap();
-    let contents = if let CiphertextSection::MicEncryptedIdentity(contents) = &enc_section {
-        contents
-    } else {
-        panic!("incorrect flavor");
-    };
-
-    let unsigned_identity_resolution_material = SectionIdentityResolutionMaterial {
-        aes_key: aes_key.unwrap_or_else(|| np_hkdf::UnsignedSectionKeys::aes_key(&key_seed_hkdf)),
-        metadata_key_hmac_key: *metadata_key_hmac_key
-            .unwrap_or_else(|| key_seed_hkdf.extended_unsigned_metadata_key_hmac_key())
-            .as_bytes(),
-        expected_metadata_key_hmac: expected_metadata_key_hmac.unwrap_or_else(|| {
-            key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key.0)
-        }),
-    };
-    let identity_resolution_material =
-        UnsignedSectionIdentityResolutionMaterial::from_raw(unsigned_identity_resolution_material);
-
-    let verification_material = UnsignedSectionVerificationMaterial {
-        mic_hmac_key: *mic_hmac_key
-            .unwrap_or_else(|| np_hkdf::UnsignedSectionKeys::hmac_key(&key_seed_hkdf))
-            .as_bytes(),
-    };
-
-    assert_eq!(
-        error,
-        contents
-            .try_resolve_identity_and_deserialize::<C>(
-                &mut deserialization_arena!().into_allocator(),
-                &identity_resolution_material,
-                &verification_material,
-            )
-            .unwrap_err()
-    );
-}
-
-#[derive(Debug, PartialEq)]
-enum DeserializeError {
-    IdentityResolutionOrDeserializationError(
-        IdentityResolutionOrDeserializationError<MicVerificationError>,
-    ),
-    DataElementParseError(DataElementParseError),
-}
-
-fn do_bad_deserialize_tampered(
-    expected_error: DeserializeError,
-    mangle_section: impl Fn(&mut CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>),
-    mangle_adv: impl Fn(&mut Vec<u8>),
-) {
-    let metadata_key = MetadataKey([1; 16]);
-    let key_seed = [2; 32];
-    let section_salt: V1Salt<CryptoProviderImpl> = [3; 16].into();
-    let identity_type = EncryptedIdentityDataElementType::Private;
-
-    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
-
-    let mut section_builder = adv_builder
-        .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new(
-            identity_type,
-            section_salt.into(),
-            &broadcast_cm,
-        ))
-        .unwrap();
-
-    section_builder.add_de(|_| TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
-
-    mangle_section(&mut section_builder.section);
-
-    section_builder.add_to_advertisement();
-
-    let adv = adv_builder.into_advertisement();
-    let mut adv_mut = adv.as_slice().to_vec();
-    mangle_adv(&mut adv_mut);
-
-    let (remaining, header) = parse_adv_header(&adv_mut).unwrap();
-
-    let v1_header = if let AdvHeader::V1(h) = header {
-        h
-    } else {
-        panic!("incorrect header");
-    };
-
-    let sections = parse_sections(v1_header, remaining).unwrap();
-    assert_eq!(1, sections.len());
-
-    let section = sections.into_iter().next().unwrap();
-    let enc_section = section.as_ciphertext().unwrap();
-    let contents = if let CiphertextSection::MicEncryptedIdentity(contents) = &enc_section {
-        contents
-    } else {
-        panic!("incorrect flavor");
-    };
-
-    // generate a random key pair since we need _some_ public key in our discovery
-    // credential, even if it winds up going unused
-    let key_pair = np_ed25519::KeyPair::<CryptoProviderImpl>::generate();
-
-    let discovery_credential =
-        SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key())
-            .derive_v1_discovery_credential::<CryptoProviderImpl>();
-
-    let identity_resolution_material =
-        discovery_credential.unsigned_identity_resolution_material::<CryptoProviderImpl>();
-    let verification_material =
-        discovery_credential.unsigned_verification_material::<CryptoProviderImpl>();
-
-    match contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
-        &mut deserialization_arena!().into_allocator(),
-        &identity_resolution_material,
-        &verification_material,
-    ) {
-        Ok(section) => {
-            assert_eq!(
-                expected_error,
-                DeserializeError::DataElementParseError(
-                    section.collect_data_elements().unwrap_err()
-                )
-            );
-        }
-        Err(e) => assert_eq!(
-            expected_error,
-            DeserializeError::IdentityResolutionOrDeserializationError(e),
-        ),
-    };
-}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs
index eb2fe01..fc38b1c 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs
@@ -14,20 +14,26 @@
 
 use crate::{
     credential::v1::*,
-    deserialization_arena::DeserializationArenaAllocator,
     extended::{
-        deserialize::{
-            DecryptedSection, EncryptedIdentityMetadata, EncryptionInfo, SectionContents,
-            SectionMic, VerificationMode,
-        },
+        deserialize::{DecryptedSection, SectionMic, VerificationMode},
         section_signature_payload::*,
-        METADATA_KEY_LEN, NP_ADV_MAX_SECTION_LEN,
+        V1IdentityToken, NP_ADV_MAX_SECTION_LEN, V1_IDENTITY_TOKEN_LEN,
     },
-    MetadataKey, V1Header, NP_SVC_UUID,
+    NP_SVC_UUID,
 };
 
+use crate::deserialization_arena::DeserializationArenaAllocator;
+
 #[cfg(any(feature = "devtools", test))]
 extern crate alloc;
+
+use crate::{
+    extended::{
+        deserialize::section::header::CiphertextExtendedIdentityToken,
+        salt::{MultiSalt, V1Salt},
+    },
+    header::V1AdvHeader,
+};
 #[cfg(any(feature = "devtools", test))]
 use alloc::vec::Vec;
 #[cfg(feature = "devtools")]
@@ -38,24 +44,28 @@
     hmac::Hmac,
     CryptoProvider,
 };
-use np_hkdf::v1_salt::V1Salt;
+use np_hkdf::v1_salt::ExtendedV1Salt;
+
+#[cfg(test)]
+use crate::extended::deserialize::encrypted_section::tests::IdentityResolutionOrDeserializationError;
 
 use super::ArenaOutOfSpace;
 
 #[cfg(test)]
-mod mic_decrypt_tests;
-#[cfg(test)]
-mod signature_decrypt_tests;
+mod tests;
 
 /// Represents the contents of an encrypted section
 /// which are directly employed in identity resolution.
 /// This does not incorporate any information about credentials.
+///
+/// Should be re-used for multiple identity resolution attempts, if applicable, to amortize the
+/// cost of calculating this data.
 #[derive(PartialEq, Eq, Debug)]
 pub(crate) struct SectionIdentityResolutionContents {
-    /// The ciphertext for the metadata key
-    pub(crate) metadata_key_ciphertext: [u8; METADATA_KEY_LEN],
-    /// The 12-byte cryptographic nonce which is derived from the encryption info
-    /// and the identity metadata for a particular section.
+    /// The ciphertext for the identity token
+    pub(crate) identity_token: CiphertextExtendedIdentityToken,
+    /// The 12-byte cryptographic nonce which is derived from the salt for a
+    /// particular section.
     pub(crate) nonce: AesCtrNonce,
 }
 
@@ -73,29 +83,34 @@
         &self,
         identity_resolution_material: &SectionIdentityResolutionMaterial,
     ) -> Option<IdentityMatch<C>> {
-        let mut decrypt_buf = self.metadata_key_ciphertext;
-        let aes_key = &identity_resolution_material.aes_key;
-        let mut cipher = C::AesCtr128::new(aes_key, NonceAndCounter::from_nonce(self.nonce));
+        let mut decrypt_buf = self.identity_token.0;
+        let mut cipher = C::AesCtr128::new(
+            &identity_resolution_material.aes_key,
+            NonceAndCounter::from_nonce(self.nonce),
+        );
         cipher.apply_keystream(&mut decrypt_buf[..]);
 
-        let metadata_key_hmac_key: np_hkdf::NpHmacSha256Key<C> =
-            identity_resolution_material.metadata_key_hmac_key.into();
-        let expected_metadata_key_hmac = identity_resolution_material.expected_metadata_key_hmac;
-        metadata_key_hmac_key.verify_hmac(&decrypt_buf[..], expected_metadata_key_hmac).ok().map(
-            move |_| IdentityMatch {
+        let identity_token_hmac_key: np_hkdf::NpHmacSha256Key =
+            identity_resolution_material.identity_token_hmac_key.into();
+        identity_token_hmac_key
+            .verify_hmac::<C>(
+                &decrypt_buf[..],
+                identity_resolution_material.expected_identity_token_hmac,
+            )
+            .ok()
+            .map(move |_| IdentityMatch {
                 cipher,
-                metadata_key_plaintext: MetadataKey(decrypt_buf),
+                identity_token: V1IdentityToken(decrypt_buf),
                 nonce: self.nonce,
-            },
-        )
+            })
     }
 }
 
 /// Carries data about an identity "match" for a particular section
 /// against some particular V1 identity-resolution crypto-materials.
 pub(crate) struct IdentityMatch<C: CryptoProvider> {
-    /// Decrypted metadata key ciphertext
-    metadata_key_plaintext: MetadataKey,
+    /// Decrypted identity token
+    identity_token: V1IdentityToken,
     /// The AES-Ctr nonce to be used in section decryption and verification
     nonce: AesCtrNonce,
     /// The state of the AES-Ctr cipher after successfully decrypting
@@ -106,22 +121,24 @@
 
 /// Maximum length of a section's contents, after the metadata-key.
 #[allow(unused)]
-const MAX_SECTION_CONTENTS_LEN: usize = NP_ADV_MAX_SECTION_LEN - METADATA_KEY_LEN;
+const MAX_SECTION_CONTENTS_LEN: usize = NP_ADV_MAX_SECTION_LEN - V1_IDENTITY_TOKEN_LEN;
 
 /// Bare, decrypted contents from an encrypted section,
 /// including the decrypted metadata key and the decrypted section ciphertext.
 /// At this point, verification of the plaintext contents has not yet been performed.
-pub(crate) struct RawDecryptedSection<'adv> {
-    pub(crate) metadata_key_plaintext: MetadataKey,
+pub(crate) struct RawDecryptedSection<'a> {
+    // Only used with feature = "devtools"
+    #[allow(unused)]
+    pub(crate) identity_token: V1IdentityToken,
     pub(crate) nonce: AesCtrNonce,
-    pub(crate) plaintext_contents: &'adv [u8],
+    pub(crate) plaintext_contents: &'a [u8],
 }
 
 #[cfg(feature = "devtools")]
-impl<'adv> RawDecryptedSection<'adv> {
+impl<'a> RawDecryptedSection<'a> {
     pub(crate) fn to_raw_bytes(&self) -> ArrayView<u8, NP_ADV_MAX_SECTION_LEN> {
         let mut result = Vec::new();
-        result.extend_from_slice(&self.metadata_key_plaintext.0);
+        result.extend_from_slice(self.identity_token.as_slice());
         result.extend_from_slice(self.plaintext_contents);
         ArrayView::try_from_slice(&result).expect("Won't panic because of the involved lengths")
     }
@@ -130,17 +147,21 @@
 /// Represents the contents of an encrypted section,
 /// independent of the encryption type.
 #[derive(PartialEq, Eq, Debug)]
-pub(crate) struct EncryptedSectionContents<'a> {
-    pub(crate) section_header: u8,
-    pub(crate) adv_header: V1Header,
-    pub(crate) encryption_info: EncryptionInfo,
-    pub(crate) identity: EncryptedIdentityMetadata,
-    /// All ciphertext (Contents of identity DE + all DEs)
-    /// Length must be in `[METADATA_KEY_LEN, NP_ADV_MAX_SECTION_LEN]`.
-    pub(crate) all_ciphertext: &'a [u8],
+pub(crate) struct EncryptedSectionContents<'adv, S> {
+    adv_header: V1AdvHeader,
+    format_bytes: &'adv [u8],
+    pub(crate) salt: S,
+    /// Ciphertext of identity token (part of section header)
+    identity_token: CiphertextExtendedIdentityToken,
+    /// The portion of the ciphertext that has been encrypted.
+    /// Length must be in `[0, NP_ADV_MAX_SECTION_LEN]`.
+    section_contents: &'adv [u8],
+    // The length byte exactly as it appears in the adv. This is the length of the encrypted
+    // contents plus any additional bytes of suffix
+    total_section_contents_len: u8,
 }
 
-impl<'a> EncryptedSectionContents<'a> {
+impl<'adv, S: V1Salt> EncryptedSectionContents<'adv, S> {
     /// Constructs a representation of the contents of an encrypted V1 section
     /// from the advertisement header, the section header, information about
     /// the encryption used for identity verification, identity metadata,
@@ -148,30 +169,29 @@
     ///
     /// # Panics
     /// If `all_ciphertext` is greater than `NP_ADV_MAX_SECTION_LEN` bytes,
-    /// or less than `METADATA_KEY_LEN` bytes.
+    /// or less than `IDENTITY_TOKEN_LEN` bytes.
     pub(crate) fn new(
-        adv_header: V1Header,
-        section_header: u8,
-        encryption_info: EncryptionInfo,
-        identity: EncryptedIdentityMetadata,
-        all_ciphertext: &'a [u8],
+        adv_header: V1AdvHeader,
+        format_bytes: &'adv [u8],
+        salt: S,
+        identity_token: CiphertextExtendedIdentityToken,
+        section_contents_len: u8,
+        section_contents: &'adv [u8],
     ) -> Self {
-        assert!(all_ciphertext.len() >= METADATA_KEY_LEN);
-        assert!(all_ciphertext.len() <= NP_ADV_MAX_SECTION_LEN);
-        Self { adv_header, section_header, encryption_info, identity, all_ciphertext }
+        assert!(section_contents.len() <= NP_ADV_MAX_SECTION_LEN - V1_IDENTITY_TOKEN_LEN);
+        Self {
+            adv_header,
+            format_bytes,
+            salt,
+            identity_token,
+            total_section_contents_len: section_contents_len,
+            section_contents,
+        }
     }
 
     /// Gets the salt for this encrypted section
-    pub(crate) fn salt<C: CryptoProvider>(&self) -> V1Salt<C> {
-        self.encryption_info.salt().into()
-    }
-
-    /// Constructs a cryptographic nonce for this encrypted section
-    /// based on the contained salt.
-    pub(crate) fn compute_nonce<C: CryptoProvider>(&self) -> AesCtrNonce {
-        self.salt::<C>()
-            .derive(Some(self.identity.offset))
-            .expect("AES-CTR nonce is a valid HKDF size")
+    pub(crate) fn salt(&self) -> MultiSalt {
+        self.salt.into()
     }
 
     /// Constructs some cryptographic contents for section identity-resolution
@@ -179,34 +199,30 @@
     pub(crate) fn compute_identity_resolution_contents<C: CryptoProvider>(
         &self,
     ) -> SectionIdentityResolutionContents {
-        let nonce = self.compute_nonce::<C>();
-        let metadata_key_ciphertext: [u8; METADATA_KEY_LEN] = self.all_ciphertext
-            [..METADATA_KEY_LEN]
-            .try_into()
-            .expect("slice will always fit into same size array");
-
-        SectionIdentityResolutionContents { nonce, metadata_key_ciphertext }
+        let nonce = self.salt.compute_nonce::<C>();
+        SectionIdentityResolutionContents { nonce, identity_token: self.identity_token }
     }
 
     /// Given an identity-match, decrypts the ciphertext in this encrypted section
     /// and returns the raw bytes of the decrypted plaintext.
     pub(crate) fn decrypt_ciphertext<C: CryptoProvider>(
         &self,
-        arena: &mut DeserializationArenaAllocator<'a>,
+        arena: &mut DeserializationArenaAllocator<'adv>,
         mut identity_match: IdentityMatch<C>,
-    ) -> Result<RawDecryptedSection<'a>, ArenaOutOfSpace> {
-        // Fill decrypt_buf with the ciphertext after the metadata key
+    ) -> Result<RawDecryptedSection<'adv>, ArenaOutOfSpace> {
+        // Fill decrypt_buf with the ciphertext after the section length
         let decrypt_buf =
-            arena.allocate(u8::try_from(self.all_ciphertext.len() - METADATA_KEY_LEN).expect(
-                "all_ciphertext.len() must be in [METADATA_KEY_LEN, NP_ADV_MAX_SECTION_LEN]",
-            ))?;
-        decrypt_buf.copy_from_slice(&self.all_ciphertext[METADATA_KEY_LEN..]);
+            arena
+                .allocate(u8::try_from(self.section_contents.len()).expect(
+                    "section_contents.len() must be in [0, NP_ADV_MAX_SECTION_CONTENTS_LEN - EXTENDED_IDENTITY_TOKEN_LEN]",
+                ))?;
+        decrypt_buf.copy_from_slice(self.section_contents);
 
         // Decrypt everything after the metadata key
         identity_match.cipher.apply_keystream(decrypt_buf);
 
         Ok(RawDecryptedSection {
-            metadata_key_plaintext: identity_match.metadata_key_plaintext,
+            identity_token: identity_match.identity_token,
             nonce: identity_match.nonce,
             plaintext_contents: decrypt_buf,
         })
@@ -216,21 +232,21 @@
     #[cfg(feature = "devtools")]
     pub(crate) fn try_resolve_identity_and_decrypt<P: CryptoProvider>(
         &self,
-        allocator: &mut DeserializationArenaAllocator<'a>,
+        allocator: &mut DeserializationArenaAllocator<'adv>,
         identity_resolution_material: &SectionIdentityResolutionMaterial,
     ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
-        let identity_resolution_contents = self.compute_identity_resolution_contents::<P>();
-        identity_resolution_contents.try_match(identity_resolution_material).map(|identity_match| {
-            let decrypted_section = self.decrypt_ciphertext::<P>(allocator, identity_match)?;
-            Ok(decrypted_section.to_raw_bytes())
-        })
+        self.compute_identity_resolution_contents::<P>()
+            .try_match(identity_resolution_material)
+            .map(|identity_match| {
+                Ok(self.decrypt_ciphertext::<P>(allocator, identity_match)?.to_raw_bytes())
+            })
     }
 }
 
 /// An encrypted section which is verified using a ed25519 signature
 #[derive(PartialEq, Eq, Debug)]
 pub(crate) struct SignatureEncryptedSection<'a> {
-    pub(crate) contents: EncryptedSectionContents<'a>,
+    pub(crate) contents: EncryptedSectionContents<'a, ExtendedV1Salt>,
 }
 
 impl<'a> SignatureEncryptedSection<'a> {
@@ -245,51 +261,41 @@
     where
         P: CryptoProvider,
     {
+        let identity_token = identity_match.identity_token;
         let raw_decrypted = self.contents.decrypt_ciphertext(arena, identity_match)?;
-        let metadata_key = raw_decrypted.metadata_key_plaintext;
         let nonce = raw_decrypted.nonce;
         let remaining = raw_decrypted.plaintext_contents;
 
-        if remaining.len() < crypto_provider::ed25519::SIGNATURE_LENGTH {
-            return Err(SignatureVerificationError::SignatureMissing.into());
-        }
+        let (plaintext_des, sig) = remaining
+            .split_last_chunk::<{ crypto_provider::ed25519::SIGNATURE_LENGTH }>()
+            .ok_or(SignatureVerificationError::SignatureMissing)?;
 
-        // should not panic due to above check
-        let (non_identity_des, sig) =
-            remaining.split_at(remaining.len() - crypto_provider::ed25519::SIGNATURE_LENGTH);
+        let expected_signature = crypto_provider::ed25519::Signature::from(*sig);
 
-        // All implementations only check for 64 bytes, and this will always result in a 64 byte signature.
-        let expected_signature =
-            np_ed25519::Signature::<P>::try_from(sig).expect("Signature is always 64 bytes.");
-
-        let section_signature_payload = SectionSignaturePayload::from_deserialized_parts(
-            self.contents.adv_header.header_byte,
-            self.contents.section_header,
-            &self.contents.encryption_info.bytes,
+        let section_signature_payload = SectionSignaturePayload::new(
+            self.contents.format_bytes,
+            self.contents.salt.bytes(),
             &nonce,
-            self.contents.identity.header_bytes,
-            metadata_key,
-            non_identity_des,
+            identity_token.as_slice(),
+            self.contents.total_section_contents_len,
+            plaintext_des,
         );
 
         let public_key = verification_material.signature_verification_public_key();
 
-        section_signature_payload.verify(&expected_signature, &public_key).map_err(|e| {
-            // Length of the payload should fit in the signature verification buffer.
-            debug_assert!(e != np_ed25519::SignatureVerificationError::PayloadTooBig);
-
-            SignatureVerificationError::SignatureMismatch
-        })?;
-
-        let salt = self.contents.salt::<P>();
+        section_signature_payload.verify::<P::Ed25519>(expected_signature, &public_key).map_err(
+            |e| {
+                // Length of the payload should fit in the signature verification buffer.
+                debug_assert!(e != np_ed25519::SignatureVerificationError::PayloadTooBig);
+                SignatureVerificationError::SignatureMismatch
+            },
+        )?;
 
         Ok(DecryptedSection::new(
-            self.contents.identity.identity_type,
             VerificationMode::Signature,
-            metadata_key,
-            salt.into(),
-            // de offset 2 because of leading encryption info and identity DEs
-            SectionContents::new(self.contents.section_header, non_identity_des, 2),
+            self.contents.salt(),
+            identity_token,
+            plaintext_des,
         ))
     }
 
@@ -307,6 +313,13 @@
     }
 
     /// Try deserializing into a [Section] given some raw signed crypto-material.
+    ///
+    /// A less-efficient, one-shot way of getting
+    /// [EncryptedSectionContents::compute_identity_resolution_contents] and then attempting
+    /// deserialization.
+    ///
+    /// Normally, id resolution contents would be calculated for a bunch of sections, and then have
+    /// many identities tried on them. This just works for one identity.
     #[cfg(test)]
     pub(crate) fn try_resolve_identity_and_deserialize<P: CryptoProvider>(
         &self,
@@ -317,9 +330,9 @@
         DecryptedSection,
         IdentityResolutionOrDeserializationError<SignatureVerificationError>,
     > {
-        let section_identity_resolution_contents =
-            self.contents.compute_identity_resolution_contents::<P>();
-        match section_identity_resolution_contents
+        match self
+            .contents
+            .compute_identity_resolution_contents::<P>()
             .try_match::<P>(identity_resolution_material.as_raw_resolution_material())
         {
             Some(identity_match) => self
@@ -330,36 +343,6 @@
     }
 }
 
-/// An error when attempting to resolve an identity and then
-/// attempt to deserialize an encrypted advertisement.
-///
-/// This should not be exposed publicly, since it's too
-/// detailed.
-#[cfg(test)]
-#[derive(Debug, PartialEq, Eq)]
-pub(crate) enum IdentityResolutionOrDeserializationError<V: VerificationError> {
-    /// Failed to match the encrypted adv to an identity
-    IdentityMatchingError,
-    /// Failed to deserialize the encrypted adv after matching the identity
-    DeserializationError(DeserializationError<V>),
-}
-
-#[cfg(test)]
-impl<V: VerificationError> From<DeserializationError<V>>
-    for IdentityResolutionOrDeserializationError<V>
-{
-    fn from(deserialization_error: DeserializationError<V>) -> Self {
-        Self::DeserializationError(deserialization_error)
-    }
-}
-
-#[cfg(test)]
-impl<V: VerificationError> From<V> for IdentityResolutionOrDeserializationError<V> {
-    fn from(verification_error: V) -> Self {
-        Self::DeserializationError(DeserializationError::VerificationError(verification_error))
-    }
-}
-
 /// An error when attempting to deserialize an encrypted advertisement,
 /// assuming that we already have an identity-match.
 ///
@@ -406,7 +389,7 @@
 /// An encrypted section whose contents are verified to match a message integrity code (MIC)
 #[derive(PartialEq, Eq, Debug)]
 pub(crate) struct MicEncryptedSection<'a> {
-    pub(crate) contents: EncryptedSectionContents<'a>,
+    pub(crate) contents: EncryptedSectionContents<'a, MultiSalt>,
     pub(crate) mic: SectionMic,
 }
 
@@ -418,48 +401,71 @@
         &self,
         allocator: &mut DeserializationArenaAllocator<'a>,
         identity_match: IdentityMatch<P>,
-        verification_material: &UnsignedSectionVerificationMaterial,
+        crypto_material: &impl V1DiscoveryCryptoMaterial,
     ) -> Result<DecryptedSection<'a>, DeserializationError<MicVerificationError>>
     where
         P: CryptoProvider,
     {
-        let raw_decrypted = self.contents.decrypt_ciphertext(allocator, identity_match)?;
-        let metadata_key = raw_decrypted.metadata_key_plaintext;
-        let nonce = raw_decrypted.nonce;
-        let remaining_des = raw_decrypted.plaintext_contents;
+        let hmac_key = match self.contents.salt {
+            MultiSalt::Short(_) => {
+                crypto_material.mic_short_salt_verification_material::<P>().mic_hmac_key()
+            }
+            MultiSalt::Extended(_) => {
+                crypto_material.mic_extended_salt_verification_material::<P>().mic_hmac_key()
+            }
+        };
 
+        let mut mic_hmac = hmac_key.build_hmac::<P>();
         // if mic is ok, the section was generated by someone holding at least the shared credential
-        let mut mic_hmac = verification_material.mic_hmac_key::<P>().build_hmac();
         mic_hmac.update(&NP_SVC_UUID);
-        mic_hmac.update(&[self.contents.adv_header.header_byte]);
-        mic_hmac.update(&[self.contents.section_header]);
-        mic_hmac.update(&self.contents.encryption_info.bytes);
-        mic_hmac.update(&nonce);
-        mic_hmac.update(&self.contents.identity.header_bytes);
-        mic_hmac.update(self.contents.all_ciphertext);
+        mic_hmac.update(&[self.contents.adv_header.contents()]);
+        // section format
+        mic_hmac.update(self.contents.format_bytes);
+        // salt bytes
+        mic_hmac.update(self.contents.salt.as_slice());
+        // nonce
+        mic_hmac.update(identity_match.nonce.as_slice());
+        // ciphertext identity token
+        mic_hmac.update(self.contents.identity_token.0.as_slice());
+        // section payload len
+        mic_hmac.update(&[self.contents.total_section_contents_len]);
+        // rest of encrypted contents
+        mic_hmac.update(self.contents.section_contents);
         mic_hmac
             // adv only contains first 16 bytes of HMAC
             .verify_truncated_left(&self.mic.mic)
             .map_err(|_e| MicVerificationError::MicMismatch)?;
 
-        let salt = self.contents.salt::<P>();
-
+        // plaintext identity token, already decrypted during identity match
+        let identity_token = identity_match.identity_token;
+        let raw_decrypted = self.contents.decrypt_ciphertext(allocator, identity_match)?;
         Ok(DecryptedSection::new(
-            self.contents.identity.identity_type,
             VerificationMode::Mic,
-            metadata_key,
-            salt.into(),
-            // offset 2 for encryption info and identity DEs
-            SectionContents::new(self.contents.section_header, remaining_des, 2),
+            self.contents.salt(),
+            identity_token,
+            raw_decrypted.plaintext_contents,
         ))
     }
 
     /// Try decrypting into some raw bytes given some raw unsigned crypto-material.
     #[cfg(feature = "devtools")]
-    pub(crate) fn try_resolve_identity_and_decrypt<P: CryptoProvider>(
+    pub(crate) fn try_resolve_short_salt_identity_and_decrypt<P: CryptoProvider>(
         &self,
         allocator: &mut DeserializationArenaAllocator<'a>,
-        identity_resolution_material: &UnsignedSectionIdentityResolutionMaterial,
+        identity_resolution_material: &MicShortSaltSectionIdentityResolutionMaterial,
+    ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
+        self.contents.try_resolve_identity_and_decrypt::<P>(
+            allocator,
+            identity_resolution_material.as_raw_resolution_material(),
+        )
+    }
+
+    /// Try decrypting into some raw bytes given some raw unsigned crypto-material.
+    #[cfg(feature = "devtools")]
+    pub(crate) fn try_resolve_extended_salt_identity_and_decrypt<P: CryptoProvider>(
+        &self,
+        allocator: &mut DeserializationArenaAllocator<'a>,
+        identity_resolution_material: &MicExtendedSaltSectionIdentityResolutionMaterial,
     ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
         self.contents.try_resolve_identity_and_decrypt::<P>(
             allocator,
@@ -472,20 +478,27 @@
     pub(crate) fn try_resolve_identity_and_deserialize<P: CryptoProvider>(
         &self,
         allocator: &mut DeserializationArenaAllocator<'a>,
-        identity_resolution_material: &UnsignedSectionIdentityResolutionMaterial,
-        verification_material: &UnsignedSectionVerificationMaterial,
+        crypto_material: &impl V1DiscoveryCryptoMaterial,
     ) -> Result<DecryptedSection, IdentityResolutionOrDeserializationError<MicVerificationError>>
     {
         let section_identity_resolution_contents =
             self.contents.compute_identity_resolution_contents::<P>();
-        match section_identity_resolution_contents
-            .try_match::<P>(identity_resolution_material.as_raw_resolution_material())
-        {
-            Some(identity_match) => self
-                .try_deserialize(allocator, identity_match, verification_material)
-                .map_err(|e| e.into()),
-            None => Err(IdentityResolutionOrDeserializationError::IdentityMatchingError),
+
+        let identity_match = match self.contents.salt {
+            MultiSalt::Short(_) => section_identity_resolution_contents.try_match::<P>(
+                crypto_material
+                    .mic_short_salt_identity_resolution_material::<P>()
+                    .as_raw_resolution_material(),
+            ),
+            MultiSalt::Extended(_) => section_identity_resolution_contents.try_match::<P>(
+                crypto_material
+                    .mic_extended_salt_identity_resolution_material::<P>()
+                    .as_raw_resolution_material(),
+            ),
         }
+        .ok_or(IdentityResolutionOrDeserializationError::IdentityMatchingError)?;
+
+        self.try_deserialize(allocator, identity_match, crypto_material).map_err(|e| e.into())
     }
 }
 
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/signature_decrypt_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/signature_decrypt_tests.rs
deleted file mode 100644
index 7801744..0000000
--- a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/signature_decrypt_tests.rs
+++ /dev/null
@@ -1,570 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#![allow(clippy::unwrap_used)]
-
-extern crate std;
-
-use crate::deserialization_arena;
-use crate::extended::data_elements::TxPowerDataElement;
-use crate::extended::deserialize::{
-    DataElementParseError, DecryptedSection, EncryptionInfo, RawV1Salt, SectionContents,
-};
-use crate::extended::serialize::{AdvertisementType, DeSalt};
-use crate::shared_data::TxPower;
-use crate::{
-    credential::v1::*,
-    de_type::EncryptedIdentityDataElementType,
-    extended::{
-        deserialize::{
-            encrypted_section::{
-                EncryptedSectionContents, IdentityResolutionOrDeserializationError,
-                SignatureEncryptedSection, SignatureVerificationError,
-            },
-            parse_sections,
-            test_stubs::IntermediateSectionExt,
-            CiphertextSection, DataElement, EncryptedIdentityMetadata, VerificationMode,
-        },
-        section_signature_payload::*,
-        serialize::{
-            AdvBuilder, CapacityLimitedVec, SignedEncryptedSectionEncoder, SingleTypeDataElement,
-            WriteDataElement,
-        },
-        NP_ADV_MAX_SECTION_LEN,
-    },
-    parse_adv_header, AdvHeader, MetadataKey, Section,
-};
-use crypto_provider::{aes::ctr::AesCtrNonce, CryptoProvider};
-use crypto_provider_default::CryptoProviderImpl;
-use np_hkdf::v1_salt;
-use np_hkdf::v1_salt::V1Salt;
-use sink::Sink;
-use std::{prelude::rust_2021::*, vec};
-
-type KeyPair = np_ed25519::KeyPair<CryptoProviderImpl>;
-
-#[test]
-fn deserialize_signature_encrypted_correct_keys() {
-    let metadata_key = MetadataKey([1; 16]);
-    let key_seed = [2; 32];
-    let raw_salt = RawV1Salt([3; 16]);
-    let section_salt = V1Salt::<CryptoProviderImpl>::from(raw_salt);
-    let identity_type = EncryptedIdentityDataElementType::Private;
-    let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-    let key_pair = KeyPair::generate();
-
-    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
-    let broadcast_cm =
-        SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
-
-    let mut section_builder = adv_builder
-        .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new(
-            identity_type,
-            raw_salt,
-            &broadcast_cm,
-        ))
-        .unwrap();
-
-    let txpower_de = TxPowerDataElement::from(TxPower::try_from(7).unwrap());
-    section_builder.add_de(|_| txpower_de.clone()).unwrap();
-
-    section_builder.add_to_advertisement();
-
-    let adv = adv_builder.into_advertisement();
-
-    let (remaining, header) = parse_adv_header(adv.as_slice()).unwrap();
-
-    let adv_header = if let AdvHeader::V1(h) = header {
-        h
-    } else {
-        panic!("incorrect header");
-    };
-
-    let sections = parse_sections(adv_header, remaining).unwrap();
-    assert_eq!(1, sections.len());
-
-    let section = sections.into_iter().next().unwrap();
-    let enc_section = section.as_ciphertext().unwrap();
-    let contents = if let CiphertextSection::SignatureEncryptedIdentity(contents) = &enc_section {
-        contents
-    } else {
-        panic!("incorrect flavor");
-    };
-
-    let mut encryption_info_bytes = [0_u8; 19];
-    encryption_info_bytes[0..2].copy_from_slice(&[0x91, 0x10]);
-    encryption_info_bytes[2] = 0x08;
-    encryption_info_bytes[3..].copy_from_slice(section_salt.as_slice());
-
-    let section_len = 19 + 2 + 16 + 2 + 64;
-    assert_eq!(
-        &SignatureEncryptedSection {
-            contents: EncryptedSectionContents {
-                section_header: section_len,
-                adv_header,
-                encryption_info: EncryptionInfo { bytes: encryption_info_bytes },
-                identity: EncryptedIdentityMetadata {
-                    header_bytes: [0x90, 0x1],
-                    offset: 1.into(),
-                    identity_type: EncryptedIdentityDataElementType::Private,
-                },
-                // adv header + salt + section header + encryption info + identity header
-                all_ciphertext: &adv.as_slice()[1 + 1 + 19 + 2..],
-            },
-        },
-        contents
-    );
-
-    // plaintext is correct
-    {
-        let crypto_material = V1DiscoveryCredential::new::<CryptoProviderImpl>(
-            key_seed,
-            key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key.0),
-            key_seed_hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key.0),
-            key_pair.public().to_bytes(),
-        )
-        .expect("public key bytes are valid since they are generated from a key pair");
-        let signed_identity_resolution_material =
-            crypto_material.signed_identity_resolution_material::<CryptoProviderImpl>();
-        let identity_resolution_contents =
-            contents.contents.compute_identity_resolution_contents::<CryptoProviderImpl>();
-        let identity_match = identity_resolution_contents
-            .try_match::<CryptoProviderImpl>(
-                &signed_identity_resolution_material.into_raw_resolution_material(),
-            )
-            .unwrap();
-
-        let arena = deserialization_arena!();
-        let mut allocator = arena.into_allocator();
-        let decrypted =
-            contents.contents.decrypt_ciphertext(&mut allocator, identity_match).unwrap();
-
-        let mut expected = Vec::new();
-        expected.extend_from_slice(txpower_de.de_header().serialize().as_slice());
-        let _ = txpower_de.write_de_contents(&mut expected);
-
-        let nonce: AesCtrNonce = section_salt.derive(Some(1.into())).unwrap();
-
-        let mut encryption_info = vec![0x91, 0x10, 0x08];
-        encryption_info.extend_from_slice(section_salt.as_slice());
-        let encryption_info: [u8; EncryptionInfo::TOTAL_DE_LEN] =
-            encryption_info.try_into().unwrap();
-
-        let sig_payload = SectionSignaturePayload::from_deserialized_parts(
-            0x20,
-            section_len,
-            &encryption_info,
-            &nonce,
-            [0x90, 0x1],
-            metadata_key,
-            &expected,
-        );
-
-        expected.extend_from_slice(&sig_payload.sign(&key_pair).to_bytes());
-        assert_eq!(metadata_key, decrypted.metadata_key_plaintext);
-        assert_eq!(nonce, decrypted.nonce);
-        assert_eq!(&expected, decrypted.plaintext_contents);
-    }
-
-    // deserialization to Section works
-    {
-        let crypto_material = V1DiscoveryCredential::new::<CryptoProviderImpl>(
-            key_seed,
-            key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key.0),
-            key_seed_hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key.0),
-            key_pair.public().to_bytes(),
-        )
-        .expect("public key bytes are valid since they are generated from a key pair");
-        let signed_identity_resolution_material =
-            crypto_material.signed_identity_resolution_material::<CryptoProviderImpl>();
-        let signed_verification_material =
-            crypto_material.signed_verification_material::<CryptoProviderImpl>();
-
-        let arena = deserialization_arena!();
-        let mut allocator = arena.into_allocator();
-        let section = contents
-            .try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
-                &mut allocator,
-                &signed_identity_resolution_material,
-                &signed_verification_material,
-            )
-            .unwrap();
-
-        assert_eq!(
-            DecryptedSection::new(
-                EncryptedIdentityDataElementType::Private,
-                VerificationMode::Signature,
-                metadata_key,
-                raw_salt,
-                SectionContents {
-                    section_header: 19 + 2 + 16 + 1 + 1 + 64,
-                    data_element_start_offset: 2,
-                    de_region_excl_identity:
-                        &[txpower_de.de_header().serialize().as_slice(), &[7],].concat(),
-                },
-            ),
-            section
-        );
-        let data_elements = section.collect_data_elements().unwrap();
-        assert_eq!(
-            data_elements,
-            &[DataElement { offset: 2.into(), de_type: 0x05_u8.into(), contents: &[7] }]
-        );
-
-        assert_eq!(
-            vec![(v1_salt::DataElementOffset::from(2_u8), TxPowerDataElement::DE_TYPE, vec![7u8])],
-            data_elements
-                .into_iter()
-                .map(|de| (de.offset(), de.de_type(), de.contents().to_vec()))
-                .collect::<Vec<_>>()
-        );
-    }
-}
-
-#[test]
-fn deserialize_signature_encrypted_incorrect_aes_key_error() {
-    // bad aes key -> bad metadata key plaintext
-    do_bad_deserialize_params::<CryptoProviderImpl>(
-        IdentityResolutionOrDeserializationError::IdentityMatchingError,
-        Some([0xFF; 16].into()),
-        None,
-        None,
-        None,
-    );
-}
-
-#[test]
-fn deserialize_signature_encrypted_incorrect_metadata_key_hmac_key_error() {
-    // bad metadata key hmac key -> bad calculated metadata key mac
-    do_bad_deserialize_params::<CryptoProviderImpl>(
-        IdentityResolutionOrDeserializationError::IdentityMatchingError,
-        None,
-        Some([0xFF; 32].into()),
-        None,
-        None,
-    );
-}
-
-#[test]
-fn deserialize_signature_encrypted_incorrect_expected_metadata_key_hmac_error() {
-    // bad expected metadata key mac
-    do_bad_deserialize_params::<CryptoProviderImpl>(
-        IdentityResolutionOrDeserializationError::IdentityMatchingError,
-        None,
-        None,
-        Some([0xFF; 32]),
-        None,
-    );
-}
-
-#[test]
-fn deserialize_signature_encrypted_incorrect_pub_key_error() {
-    // a random pub key will lead to signature mismatch
-    do_bad_deserialize_params::<CryptoProviderImpl>(
-        SignatureVerificationError::SignatureMismatch.into(),
-        None,
-        None,
-        None,
-        Some(KeyPair::generate().public()),
-    );
-}
-
-#[test]
-fn deserialize_signature_encrypted_incorrect_salt_error() {
-    // bad salt -> bad iv -> bad metadata key plaintext
-    do_bad_deserialize_tampered(
-        DeserializeError::IdentityResolutionOrDeserializationError(
-            IdentityResolutionOrDeserializationError::IdentityMatchingError,
-        ),
-        None,
-        |_| {},
-        |adv_mut| adv_mut[5..21].copy_from_slice(&[0xFF; 16]),
-    )
-}
-
-#[test]
-fn deserialize_signature_encrypted_tampered_signature_error() {
-    do_bad_deserialize_tampered(
-        DeserializeError::IdentityResolutionOrDeserializationError(
-            SignatureVerificationError::SignatureMismatch.into(),
-        ),
-        None,
-        |_| {},
-        // flip a bit in the middle of the signature
-        |adv_mut| {
-            let len = adv_mut.len();
-            adv_mut[len - 30] ^= 0x1
-        },
-    )
-}
-
-#[test]
-fn deserialize_signature_encrypted_tampered_ciphertext_error() {
-    do_bad_deserialize_tampered(
-        DeserializeError::IdentityResolutionOrDeserializationError(
-            SignatureVerificationError::SignatureMismatch.into(),
-        ),
-        None,
-        |_| {},
-        // flip a bit outside of the signature
-        |adv_mut| {
-            let len = adv_mut.len();
-            adv_mut[len - 1 - 64] ^= 0x1
-        },
-    )
-}
-
-#[test]
-fn deserialize_signature_encrypted_missing_signature_de_error() {
-    let section_len = 19 + 2 + 16 + 1 + 1;
-    do_bad_deserialize_tampered(
-        DeserializeError::IdentityResolutionOrDeserializationError(
-            SignatureVerificationError::SignatureMissing.into(),
-        ),
-        Some(section_len),
-        |_| {},
-        |adv_mut| {
-            // chop off signature DE
-            adv_mut.truncate(adv_mut.len() - 64);
-            // fix section length
-            adv_mut[1] = section_len;
-        },
-    )
-}
-
-#[test]
-fn deserialize_signature_encrypted_des_wont_parse() {
-    do_bad_deserialize_tampered(
-        DeserializeError::DataElementParseError(DataElementParseError::UnexpectedDataAfterEnd),
-        Some(19 + 2 + 16 + 1 + 1 + 64 + 1),
-        // add an impossible DE
-        |section| section.try_push(0xFF).unwrap(),
-        |_| {},
-    )
-}
-
-/// Attempt a deserialization that will fail when using the provided parameters for decryption only.
-/// `None` means use the correct value for that parameter.
-fn do_bad_deserialize_params<C: CryptoProvider>(
-    error: IdentityResolutionOrDeserializationError<SignatureVerificationError>,
-    aes_key: Option<crypto_provider::aes::Aes128Key>,
-    metadata_key_hmac_key: Option<np_hkdf::NpHmacSha256Key<C>>,
-    expected_metadata_key_hmac: Option<[u8; 32]>,
-    pub_key: Option<np_ed25519::PublicKey<C>>,
-) {
-    let metadata_key = MetadataKey([1; 16]);
-    let key_seed = [2; 32];
-    let section_salt: v1_salt::V1Salt<C> = [3; 16].into();
-    let identity_type = EncryptedIdentityDataElementType::Private;
-    let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::new(&key_seed);
-    let key_pair = np_ed25519::KeyPair::<C>::generate();
-
-    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
-    let broadcast_cm =
-        SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
-
-    let mut section_builder = adv_builder
-        .section_builder(SignedEncryptedSectionEncoder::new(
-            identity_type,
-            section_salt.into(),
-            &broadcast_cm,
-        ))
-        .unwrap();
-
-    section_builder
-        .add_de_res(|_: DeSalt<C>| TxPower::try_from(2).map(TxPowerDataElement::from))
-        .unwrap();
-
-    section_builder.add_to_advertisement();
-
-    let adv = adv_builder.into_advertisement();
-
-    let (remaining, header) = parse_adv_header(adv.as_slice()).unwrap();
-
-    let v1_header = if let AdvHeader::V1(h) = header {
-        h
-    } else {
-        panic!("incorrect header");
-    };
-
-    let sections = parse_sections(v1_header, remaining).unwrap();
-    assert_eq!(1, sections.len());
-
-    let section = sections.into_iter().next().unwrap();
-    let enc_section = section.as_ciphertext().unwrap();
-    let contents = if let CiphertextSection::SignatureEncryptedIdentity(contents) = &enc_section {
-        contents
-    } else {
-        panic!("incorrect flavor");
-    };
-
-    let signed_identity_resolution_material =
-        SignedSectionIdentityResolutionMaterial::from_raw(SectionIdentityResolutionMaterial {
-            aes_key: aes_key.unwrap_or_else(|| key_seed_hkdf.extended_signed_section_aes_key()),
-
-            metadata_key_hmac_key: *metadata_key_hmac_key
-                .unwrap_or_else(|| key_seed_hkdf.extended_signed_metadata_key_hmac_key())
-                .as_bytes(),
-            expected_metadata_key_hmac: expected_metadata_key_hmac.unwrap_or_else(|| {
-                key_seed_hkdf
-                    .extended_signed_metadata_key_hmac_key()
-                    .calculate_hmac(&metadata_key.0)
-            }),
-        });
-
-    let signed_verification_material = SignedSectionVerificationMaterial {
-        validated_public_key: ValidatedPublicKey::from_raw_bytes::<CryptoProviderImpl>(
-            pub_key.unwrap_or_else(|| key_pair.public()).to_bytes(),
-        )
-        .expect("public key should be valid bytes"),
-    };
-
-    assert_eq!(
-        error,
-        contents
-            .try_resolve_identity_and_deserialize::<C>(
-                &mut deserialization_arena!().into_allocator(),
-                &signed_identity_resolution_material,
-                &signed_verification_material,
-            )
-            .unwrap_err()
-    );
-}
-
-#[derive(Debug, PartialEq)]
-enum DeserializeError {
-    IdentityResolutionOrDeserializationError(
-        IdentityResolutionOrDeserializationError<SignatureVerificationError>,
-    ),
-    DataElementParseError(DataElementParseError),
-}
-
-/// Run a test that mangles the advertisement contents before attempting to deserialize.
-///
-/// Since the advertisement is ciphertext, only changes outside
-fn do_bad_deserialize_tampered(
-    expected_error: DeserializeError,
-    expected_section_len: Option<u8>,
-    mangle_section: impl Fn(&mut CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>),
-    mangle_adv_contents: impl Fn(&mut Vec<u8>),
-) {
-    let metadata_key = MetadataKey([1; 16]);
-    let key_seed = [2; 32];
-    let section_salt: v1_salt::V1Salt<CryptoProviderImpl> = [3; 16].into();
-    let identity_type = EncryptedIdentityDataElementType::Private;
-    let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-    let key_pair = KeyPair::generate();
-
-    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
-    let broadcast_cm =
-        SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
-
-    let mut section_builder = adv_builder
-        .section_builder(SignedEncryptedSectionEncoder::new(
-            identity_type,
-            section_salt.into(),
-            &broadcast_cm,
-        ))
-        .unwrap();
-
-    section_builder
-        .add_de_res(|_: DeSalt<CryptoProviderImpl>| {
-            TxPower::try_from(2).map(TxPowerDataElement::from)
-        })
-        .unwrap();
-
-    mangle_section(&mut section_builder.section);
-
-    section_builder.add_to_advertisement();
-
-    let adv = adv_builder.into_advertisement();
-    let mut adv_mut = adv.as_slice().to_vec();
-    mangle_adv_contents(&mut adv_mut);
-
-    let (remaining, header) = parse_adv_header(&adv_mut).unwrap();
-
-    let adv_header = if let AdvHeader::V1(h) = header {
-        h
-    } else {
-        panic!("incorrect header");
-    };
-
-    let sections = parse_sections(adv_header, remaining).unwrap();
-    assert_eq!(1, sections.len());
-
-    let section = sections.into_iter().next().unwrap();
-    let enc_section = section.as_ciphertext().unwrap();
-    let contents = if let CiphertextSection::SignatureEncryptedIdentity(contents) = &enc_section {
-        contents
-    } else {
-        panic!("incorrect flavor");
-    };
-
-    let mut encryption_info_bytes = [0_u8; 19];
-    encryption_info_bytes[0..2].copy_from_slice(&[0x91, 0x10]);
-    encryption_info_bytes[2] = 0x08;
-    encryption_info_bytes[3..].copy_from_slice(&adv_mut[5..21]);
-
-    let section_len = 19 + 2 + 16 + 2 + 64;
-    assert_eq!(
-        &SignatureEncryptedSection {
-            contents: EncryptedSectionContents {
-                section_header: expected_section_len.unwrap_or(section_len),
-                adv_header,
-                encryption_info: EncryptionInfo { bytes: encryption_info_bytes },
-                identity: EncryptedIdentityMetadata {
-                    header_bytes: [0x90, 0x1],
-                    offset: 1.into(),
-                    identity_type: EncryptedIdentityDataElementType::Private,
-                },
-                all_ciphertext: &adv_mut[1 + 1 + 19 + 2..],
-            },
-        },
-        contents
-    );
-
-    let crypto_material = V1DiscoveryCredential::new::<CryptoProviderImpl>(
-        key_seed,
-        key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key.0),
-        key_seed_hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key.0),
-        key_pair.public().to_bytes(),
-    )
-    .expect("public key bytes are valid since they are generated from a key pair");
-    let identity_resolution_material =
-        crypto_material.signed_identity_resolution_material::<CryptoProviderImpl>();
-    let verification_material =
-        crypto_material.signed_verification_material::<CryptoProviderImpl>();
-
-    match contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
-        &mut deserialization_arena!().into_allocator(),
-        &identity_resolution_material,
-        &verification_material,
-    ) {
-        Ok(section) => {
-            assert_eq!(
-                expected_error,
-                DeserializeError::DataElementParseError(
-                    section.collect_data_elements().unwrap_err()
-                ),
-            );
-        }
-        Err(e) => assert_eq!(
-            expected_error,
-            DeserializeError::IdentityResolutionOrDeserializationError(e),
-        ),
-    };
-}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests.rs
new file mode 100644
index 0000000..2afa41c
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests.rs
@@ -0,0 +1,88 @@
+// 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 super::*;
+use crate::extended::V1_IDENTITY_TOKEN_LEN;
+use np_hkdf::v1_salt::{ExtendedV1Salt, EXTENDED_SALT_LEN};
+
+#[cfg(test)]
+mod mic_decrypt_tests;
+
+#[cfg(test)]
+mod signature_decrypt_tests;
+
+#[cfg(test)]
+mod coverage_gaming;
+
+/// An error when attempting to resolve an identity and then
+/// attempt to deserialize an encrypted advertisement.
+///
+/// This should not be exposed publicly, since it's too
+/// detailed.
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) enum IdentityResolutionOrDeserializationError<V: VerificationError> {
+    /// Failed to match the encrypted adv to an identity
+    IdentityMatchingError,
+    /// Failed to deserialize the encrypted adv after matching the identity
+    DeserializationError(DeserializationError<V>),
+}
+
+impl<V: VerificationError> From<DeserializationError<V>>
+    for IdentityResolutionOrDeserializationError<V>
+{
+    fn from(deserialization_error: DeserializationError<V>) -> Self {
+        Self::DeserializationError(deserialization_error)
+    }
+}
+
+impl<V: VerificationError> From<V> for IdentityResolutionOrDeserializationError<V> {
+    fn from(verification_error: V) -> Self {
+        Self::DeserializationError(DeserializationError::VerificationError(verification_error))
+    }
+}
+
+pub(crate) fn first_section_contents(after_version_header: &[u8]) -> &[u8] {
+    &after_version_header[1 + EXTENDED_SALT_LEN + V1_IDENTITY_TOKEN_LEN + 1..]
+}
+
+pub(crate) fn first_section_identity_token(
+    salt: MultiSalt,
+    after_version_header: &[u8],
+) -> CiphertextExtendedIdentityToken {
+    // Next 16 bytes after 1 byte format and 16 byte salt
+    after_version_header[1 + salt.as_slice().len()..][..V1_IDENTITY_TOKEN_LEN]
+        .try_into()
+        .map(|arr: [u8; V1_IDENTITY_TOKEN_LEN]| arr.into())
+        .unwrap()
+}
+
+pub(crate) fn first_section_format(after_version_header: &[u8]) -> &[u8] {
+    // 1 byte of format comes at the beginning
+    &after_version_header[..1]
+}
+
+pub(crate) fn first_section_salt(after_version_header: &[u8]) -> ExtendedV1Salt {
+    // Next 16 bytes after 1 byte format
+    after_version_header[1..][..EXTENDED_SALT_LEN]
+        .try_into()
+        .map(|arr: [u8; EXTENDED_SALT_LEN]| arr.into())
+        .unwrap()
+}
+
+pub(crate) fn first_section_contents_len(after_version_header: &[u8]) -> u8 {
+    // section len is the first byte after format + salt + identity token
+    after_version_header[1 + EXTENDED_SALT_LEN + V1_IDENTITY_TOKEN_LEN..][0]
+}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/coverage_gaming.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/coverage_gaming.rs
new file mode 100644
index 0000000..0d8ba8f
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/coverage_gaming.rs
@@ -0,0 +1,60 @@
+// 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 super::super::*;
+use alloc::format;
+use crypto_provider_default::CryptoProviderImpl;
+
+#[test]
+fn section_identity_resolution_content_derives() {
+    let salt: ExtendedV1Salt = [0; 16].into();
+    let nonce = salt.compute_nonce::<CryptoProviderImpl>();
+    let token = CiphertextExtendedIdentityToken([0; 16]);
+    let section = SectionIdentityResolutionContents { identity_token: token, nonce };
+    assert_eq!(section, section);
+    let _ = format!("{:?}", section);
+}
+
+#[test]
+fn sig_encrypted_section_debug() {
+    let ss = SignatureEncryptedSection {
+        contents: EncryptedSectionContents::new(
+            V1AdvHeader::new(0),
+            &[0],
+            [0x00; 16].into(),
+            [0x00; V1_IDENTITY_TOKEN_LEN].into(),
+            1,
+            &[0x00; 1],
+        ),
+    };
+    let _ = format!("{:?}", ss);
+}
+
+#[test]
+fn error_enum_debug_derives() {
+    let mic_err = MicVerificationError::MicMismatch;
+    let _ = format!("{:?}", mic_err);
+
+    let sig_err = SignatureVerificationError::SignatureMissing;
+    let _ = format!("{:?}", sig_err);
+
+    let deser_err = DeserializationError::ArenaOutOfSpace::<MicVerificationError>;
+    let _ = format!("{:?}", deser_err);
+
+    let err =
+        IdentityResolutionOrDeserializationError::IdentityMatchingError::<MicVerificationError>;
+    let _ = format!("{:?}", err);
+}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/mic_decrypt_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/mic_decrypt_tests.rs
new file mode 100644
index 0000000..5d2f759
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/mic_decrypt_tests.rs
@@ -0,0 +1,642 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#![allow(clippy::unwrap_used)]
+
+extern crate std;
+
+use super::super::*;
+use crate::{
+    deserialization_arena,
+    extended::{
+        data_elements::TxPowerDataElement,
+        deserialize::{
+            encrypted_section::tests::first_section_identity_token,
+            section::intermediate::{
+                parse_sections, tests::IntermediateSectionExt, CiphertextSection,
+            },
+            DataElement, DataElementParseError, Section,
+        },
+        salt::{ShortV1Salt, SHORT_SALT_LEN},
+        serialize::{
+            AdvBuilder, AdvertisementType, CapacityLimitedVec, MicEncryptedSectionEncoder,
+            WriteDataElement,
+        },
+        V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN,
+        V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN,
+    },
+    shared_data::TxPower,
+    NpVersionHeader,
+};
+use crypto_provider::ed25519;
+use crypto_provider_default::CryptoProviderImpl;
+use np_hkdf::{v1_salt::EXTENDED_SALT_LEN, DerivedSectionKeys};
+use sink::Sink;
+
+type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
+
+#[test]
+fn deserialize_mic_encrypted_correct_keys_extended_salt() {
+    deserialize_mic_encrypted_correct_keys(ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into())
+}
+
+#[test]
+fn deserialize_mic_encrypted_correct_keys_short_salt() {
+    deserialize_mic_encrypted_correct_keys(ShortV1Salt::from([3; SHORT_SALT_LEN]).into())
+}
+
+fn deserialize_mic_encrypted_correct_keys(salt: MultiSalt) {
+    let identity_token = V1IdentityToken::from([1; 16]);
+    let key_seed = [2; 32];
+    let broadcast_cm = V1BroadcastCredential::new(
+        key_seed,
+        identity_token,
+        ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
+    );
+
+    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+    let mut section_builder = adv_builder
+        .section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
+            salt,
+            &broadcast_cm,
+        ))
+        .unwrap();
+
+    let txpower_de = TxPowerDataElement::from(TxPower::try_from(5).unwrap());
+    section_builder.add_de(|_| txpower_de.clone()).unwrap();
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
+    let adv = adv_builder.into_advertisement();
+
+    let (remaining, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
+
+    let adv_header = if let NpVersionHeader::V1(h) = header {
+        h
+    } else {
+        panic!("incorrect header");
+    };
+
+    let sections = parse_sections(adv_header, remaining).unwrap();
+    assert_eq!(1, sections.len());
+
+    let section = sections.into_iter().next().unwrap();
+    let enc_section = section.as_ciphertext().unwrap();
+
+    let contents = if let CiphertextSection::MicEncrypted(contents) = &enc_section {
+        contents
+    } else {
+        panic!("incorrect flavor");
+    };
+
+    let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
+    // deserializing to Section works
+    let discovery_credential = V1BroadcastCredential::new(key_seed, identity_token, private_key)
+        .derive_discovery_credential::<CryptoProviderImpl>();
+
+    let arena = deserialization_arena!();
+    let mut allocator = arena.into_allocator();
+    let section = contents
+        .try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
+            &mut allocator,
+            &discovery_credential,
+        )
+        .unwrap();
+
+    assert_eq!(
+        DecryptedSection::new(
+            VerificationMode::Mic,
+            salt,
+            identity_token,
+            &[txpower_de.de_header().serialize().as_slice(), &[5],].concat(),
+        ),
+        section
+    );
+    let data_elements = section.collect_data_elements().unwrap();
+    assert_eq!(data_elements, &[DataElement::new(0.into(), 0x05_u8.into(), &[5])]);
+
+    let (_header, contents_bytes) =
+        remaining.split_at(1 + 1 + salt.as_slice().len() + V1_IDENTITY_TOKEN_LEN);
+    assert_eq!(
+        &MicEncryptedSection {
+            contents: EncryptedSectionContents {
+                adv_header,
+                format_bytes: &[match salt {
+                    MultiSalt::Short(_) => {
+                        V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN
+                    }
+                    MultiSalt::Extended(_) => {
+                        V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN
+                    }
+                }],
+                salt,
+                identity_token: first_section_identity_token(salt, remaining),
+                section_contents: &contents_bytes
+                    [..contents_bytes.len() - SectionMic::CONTENTS_LEN],
+                total_section_contents_len: contents_bytes.len().try_into().unwrap(),
+            },
+            mic: SectionMic {
+                mic: contents_bytes[contents_bytes.len() - SectionMic::CONTENTS_LEN..]
+                    .try_into()
+                    .unwrap()
+            },
+        },
+        contents
+    );
+
+    // plaintext is correct
+    {
+        let identity_resolution_contents =
+            contents.contents.compute_identity_resolution_contents::<CryptoProviderImpl>();
+        let identity_match = identity_resolution_contents
+            .try_match::<CryptoProviderImpl>(&match salt {
+                MultiSalt::Short(_) => discovery_credential
+                    .mic_short_salt_identity_resolution_material::<CryptoProviderImpl>()
+                    .into_raw_resolution_material(),
+                MultiSalt::Extended(_) => discovery_credential
+                    .mic_extended_salt_identity_resolution_material::<CryptoProviderImpl>()
+                    .into_raw_resolution_material(),
+            })
+            .unwrap();
+        let arena = deserialization_arena!();
+        let mut allocator = arena.into_allocator();
+        let decrypted = contents
+            .contents
+            .decrypt_ciphertext::<CryptoProviderImpl>(&mut allocator, identity_match)
+            .unwrap();
+
+        let mut expected = Vec::new();
+        // battery de
+        expected.extend_from_slice(txpower_de.clone().de_header().serialize().as_slice());
+        let _ = txpower_de.write_de_contents(&mut expected);
+
+        assert_eq!(identity_token, decrypted.identity_token);
+        assert_eq!(&expected, decrypted.plaintext_contents);
+    }
+}
+
+#[test]
+fn deserialize_mic_encrypted_short_salt_incorrect_aes_key_error() {
+    // bad aes key -> bad metadata key plaintext
+    do_bad_deserialize_params::<CryptoProviderImpl>(
+        ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
+        IdentityResolutionOrDeserializationError::IdentityMatchingError,
+        |cm| {
+            cm.mic_short_salt_identity_resolution_material
+                .as_mut_raw_resolution_material()
+                .aes_key = [0xFF; 16].into();
+        },
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_short_salt_incorrect_identity_token_hmac_key_error() {
+    // bad metadata key hmac key -> bad calculated metadata key mac
+    do_bad_deserialize_params::<CryptoProviderImpl>(
+        ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
+        IdentityResolutionOrDeserializationError::IdentityMatchingError,
+        |cm| {
+            cm.mic_short_salt_identity_resolution_material
+                .as_mut_raw_resolution_material()
+                .identity_token_hmac_key = [0xFF; 32];
+        },
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_short_salt_incorrect_mic_hmac_key_error() {
+    // bad mic hmac key -> bad calculated mic
+    do_bad_deserialize_params::<CryptoProviderImpl>(
+        ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
+        MicVerificationError::MicMismatch.into(),
+        |cm| {
+            cm.mic_short_salt_verification_material.mic_hmac_key = [0xFF; 32];
+        },
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_short_salt_incorrect_expected_identity_token_hmac_error() {
+    // bad expected metadata key mac
+    do_bad_deserialize_params::<CryptoProviderImpl>(
+        ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
+        IdentityResolutionOrDeserializationError::IdentityMatchingError,
+        |cm| {
+            cm.mic_short_salt_identity_resolution_material
+                .as_mut_raw_resolution_material()
+                .expected_identity_token_hmac = [0xFF; 32];
+        },
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_extended_salt_incorrect_aes_key_error() {
+    // bad aes key -> bad metadata key plaintext
+    do_bad_deserialize_params::<CryptoProviderImpl>(
+        ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
+        IdentityResolutionOrDeserializationError::IdentityMatchingError,
+        |cm| {
+            cm.mic_extended_salt_identity_resolution_material
+                .as_mut_raw_resolution_material()
+                .aes_key = [0xFF; 16].into();
+        },
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_extended_salt_incorrect_identity_token_hmac_key_error() {
+    // bad metadata key hmac key -> bad calculated metadata key mac
+    do_bad_deserialize_params::<CryptoProviderImpl>(
+        ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
+        IdentityResolutionOrDeserializationError::IdentityMatchingError,
+        |cm| {
+            cm.mic_extended_salt_identity_resolution_material
+                .as_mut_raw_resolution_material()
+                .identity_token_hmac_key = [0xFF; 32];
+        },
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_extended_salt_incorrect_mic_hmac_key_error() {
+    // bad mic hmac key -> bad calculated mic
+    do_bad_deserialize_params::<CryptoProviderImpl>(
+        ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
+        MicVerificationError::MicMismatch.into(),
+        |cm| {
+            cm.mic_extended_salt_verification_material.mic_hmac_key = [0xFF; 32];
+        },
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_extended_salt_incorrect_expected_identity_token_hmac_error() {
+    // bad expected metadata key mac
+    do_bad_deserialize_params::<CryptoProviderImpl>(
+        ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
+        IdentityResolutionOrDeserializationError::IdentityMatchingError,
+        |cm| {
+            cm.mic_extended_salt_identity_resolution_material
+                .as_mut_raw_resolution_material()
+                .expected_identity_token_hmac = [0xFF; 32];
+        },
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_extended_salt_incorrect_salt_error() {
+    // bad salt -> bad iv -> bad metadata key plaintext
+    do_bad_deserialize_tampered(
+        ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
+        DeserializeError::IdentityResolutionOrDeserializationError(
+            IdentityResolutionOrDeserializationError::IdentityMatchingError,
+        ),
+        |_| {},
+        // replace the extended salt bytes
+        |adv| adv[2..18].fill(0xFF),
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_extended_salt_de_that_wont_parse() {
+    // add an extra byte to the section, leading it to try to parse a DE that doesn't exist
+    do_bad_deserialize_tampered(
+        ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
+        DeserializeError::DataElementParseError(DataElementParseError::UnexpectedDataAfterEnd),
+        |sec| sec.try_push(0xFF).unwrap(),
+        |_| {},
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_extended_salt_tampered_mic_error() {
+    // flip a bit in the first MIC byte
+    do_bad_deserialize_tampered(
+        ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
+        DeserializeError::IdentityResolutionOrDeserializationError(
+            MicVerificationError::MicMismatch.into(),
+        ),
+        |_| {},
+        |adv| {
+            let mic_start = adv.len() - 16;
+            adv[mic_start] ^= 0x01
+        },
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_extended_salt_tampered_payload_error() {
+    // flip the last payload bit
+    do_bad_deserialize_tampered(
+        ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
+        DeserializeError::IdentityResolutionOrDeserializationError(
+            MicVerificationError::MicMismatch.into(),
+        ),
+        |_| {},
+        |adv| {
+            let before_mic = adv.len() - 17;
+            adv[before_mic] ^= 0x01
+        },
+    );
+}
+#[test]
+fn deserialize_mic_encrypted_short_salt_incorrect_salt_error() {
+    // bad salt -> bad iv -> bad metadata key plaintext
+    do_bad_deserialize_tampered(
+        ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
+        DeserializeError::IdentityResolutionOrDeserializationError(
+            IdentityResolutionOrDeserializationError::IdentityMatchingError,
+        ),
+        |_| {},
+        // replace the extended salt bytes
+        |adv| adv[2..4].fill(0xFF),
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_short_salt_de_that_wont_parse() {
+    // add an extra byte to the section, leading it to try to parse a DE that doesn't exist
+    do_bad_deserialize_tampered(
+        ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
+        DeserializeError::DataElementParseError(DataElementParseError::UnexpectedDataAfterEnd),
+        |sec| sec.try_push(0xFF).unwrap(),
+        |_| {},
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_short_salt_tampered_mic_error() {
+    // flip a bit in the first MIC byte
+    do_bad_deserialize_tampered(
+        ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
+        DeserializeError::IdentityResolutionOrDeserializationError(
+            MicVerificationError::MicMismatch.into(),
+        ),
+        |_| {},
+        |adv| {
+            let mic_start = adv.len() - 16;
+            adv[mic_start] ^= 0x01
+        },
+    );
+}
+
+#[test]
+fn deserialize_mic_encrypted_short_salt_tampered_payload_error() {
+    // flip the last payload bit
+    do_bad_deserialize_tampered(
+        ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
+        DeserializeError::IdentityResolutionOrDeserializationError(
+            MicVerificationError::MicMismatch.into(),
+        ),
+        |_| {},
+        |adv| {
+            let before_mic = adv.len() - 17;
+            adv[before_mic] ^= 0x01
+        },
+    );
+}
+
+#[test]
+fn arena_out_of_space_on_mic_verify() {
+    let identity_token = V1IdentityToken::from([1; 16]);
+    let key_seed = [2; 32];
+    let section_salt = ExtendedV1Salt::from([3; 16]);
+    let broadcast_cm = V1BroadcastCredential::new(
+        key_seed,
+        identity_token,
+        ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
+    );
+
+    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+    let mut section_builder = adv_builder
+        .section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
+            section_salt,
+            &broadcast_cm,
+        ))
+        .unwrap();
+
+    let txpower_de = TxPowerDataElement::from(TxPower::try_from(5).unwrap());
+    section_builder.add_de(|_| txpower_de.clone()).unwrap();
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
+    let adv = adv_builder.into_advertisement();
+
+    let (remaining, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
+
+    let adv_header = if let NpVersionHeader::V1(h) = header {
+        h
+    } else {
+        panic!("incorrect header");
+    };
+
+    let sections = parse_sections(adv_header, remaining).unwrap();
+    assert_eq!(1, sections.len());
+
+    let section = sections.into_iter().next().unwrap();
+    let enc_section = section.as_ciphertext().unwrap();
+
+    let contents = if let CiphertextSection::MicEncrypted(contents) = &enc_section {
+        contents
+    } else {
+        panic!("incorrect flavor");
+    };
+
+    let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
+    // deserializing to Section works
+    let discovery_credential = V1BroadcastCredential::new(key_seed, identity_token, private_key)
+        .derive_discovery_credential::<CryptoProviderImpl>();
+
+    let arena = deserialization_arena!();
+    let mut allocator = arena.into_allocator();
+    let _ = allocator.allocate(250).unwrap();
+    let res = contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
+        &mut allocator,
+        &discovery_credential,
+    );
+    assert_eq!(
+        Err(IdentityResolutionOrDeserializationError::DeserializationError(
+            DeserializationError::ArenaOutOfSpace
+        )),
+        res
+    );
+}
+
+/// Attempt a decryption that will fail when using the provided parameters for decryption only.
+/// `None` means use the correct value for that parameter.
+fn do_bad_deserialize_params<C: CryptoProvider>(
+    salt: MultiSalt,
+    error: IdentityResolutionOrDeserializationError<MicVerificationError>,
+    mut mangle_crypto_material: impl FnMut(&mut PrecalculatedV1DiscoveryCryptoMaterial),
+) {
+    let identity_token = V1IdentityToken([1; 16]);
+    let key_seed = [2; 32];
+    let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
+
+    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+
+    let broadcast_cm = V1BroadcastCredential::new(
+        key_seed,
+        identity_token,
+        ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
+    );
+
+    let mut section_builder = adv_builder
+        .section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
+            salt,
+            &broadcast_cm,
+        ))
+        .unwrap();
+
+    section_builder.add_de(|_| TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
+
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
+
+    let adv = adv_builder.into_advertisement();
+
+    let (remaining, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
+
+    let v1_header = if let NpVersionHeader::V1(h) = header {
+        h
+    } else {
+        panic!("incorrect header");
+    };
+
+    let sections = parse_sections(v1_header, remaining).unwrap();
+    assert_eq!(1, sections.len());
+
+    let section = sections.into_iter().next().unwrap();
+    let enc_section = section.as_ciphertext().unwrap();
+    let contents = if let CiphertextSection::MicEncrypted(contents) = &enc_section {
+        contents
+    } else {
+        panic!("incorrect flavor");
+    };
+
+    // start with correct crypto material
+    let mut crypto_material = V1DiscoveryCredential::new(
+        key_seed,
+        key_seed_hkdf
+            .v1_mic_short_salt_keys()
+            .identity_token_hmac_key()
+            .calculate_hmac::<C>(&identity_token.0),
+        key_seed_hkdf
+            .v1_mic_extended_salt_keys()
+            .identity_token_hmac_key()
+            .calculate_hmac::<C>(&identity_token.0),
+        key_seed_hkdf
+            .v1_signature_keys()
+            .identity_token_hmac_key()
+            .calculate_hmac::<C>(&identity_token.0),
+        crypto_provider::ed25519::PrivateKey::generate::<C::Ed25519>()
+            .derive_public_key::<C::Ed25519>(),
+    )
+    .to_precalculated::<C>();
+
+    // then break it per the test case
+    mangle_crypto_material(&mut crypto_material);
+
+    assert_eq!(
+        error,
+        contents
+            .try_resolve_identity_and_deserialize::<C>(
+                &mut deserialization_arena!().into_allocator(),
+                &crypto_material
+            )
+            .unwrap_err()
+    );
+}
+
+#[derive(Debug, PartialEq)]
+enum DeserializeError {
+    IdentityResolutionOrDeserializationError(
+        IdentityResolutionOrDeserializationError<MicVerificationError>,
+    ),
+    DataElementParseError(DataElementParseError),
+}
+
+fn do_bad_deserialize_tampered(
+    salt: MultiSalt,
+    expected_error: DeserializeError,
+    mangle_section: impl Fn(&mut CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>),
+    mangle_adv: impl Fn(&mut Vec<u8>),
+) {
+    let metadata_key = V1IdentityToken([1; 16]);
+    let key_seed = [2; 32];
+
+    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+
+    let broadcast_cm = V1BroadcastCredential::new(
+        key_seed,
+        metadata_key,
+        ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
+    );
+
+    let mut section_builder = adv_builder
+        .section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
+            salt,
+            &broadcast_cm,
+        ))
+        .unwrap();
+
+    section_builder.add_de(|_| TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
+
+    mangle_section(&mut section_builder.section);
+
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
+
+    let adv = adv_builder.into_advertisement();
+    let mut adv_mut = adv.as_slice().to_vec();
+    mangle_adv(&mut adv_mut);
+
+    let (remaining, header) = NpVersionHeader::parse(&adv_mut).unwrap();
+
+    let v1_header = if let NpVersionHeader::V1(h) = header {
+        h
+    } else {
+        panic!("incorrect header");
+    };
+
+    let sections = parse_sections(v1_header, remaining).unwrap();
+    assert_eq!(1, sections.len());
+
+    let section = sections.into_iter().next().unwrap();
+    let enc_section = section.as_ciphertext().unwrap();
+    let contents = if let CiphertextSection::MicEncrypted(contents) = &enc_section {
+        contents
+    } else {
+        panic!("incorrect flavor");
+    };
+
+    // generate a random key pair since we need _some_ public key in our discovery
+    // credential, even if it winds up going unused
+    let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
+
+    let discovery_credential = V1BroadcastCredential::new(key_seed, metadata_key, private_key)
+        .derive_discovery_credential::<CryptoProviderImpl>();
+
+    match contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
+        &mut deserialization_arena!().into_allocator(),
+        &discovery_credential,
+    ) {
+        Ok(section) => {
+            assert_eq!(
+                expected_error,
+                DeserializeError::DataElementParseError(
+                    section.collect_data_elements().unwrap_err()
+                )
+            );
+        }
+        Err(e) => assert_eq!(
+            expected_error,
+            DeserializeError::IdentityResolutionOrDeserializationError(e),
+        ),
+    };
+}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/signature_decrypt_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/signature_decrypt_tests.rs
new file mode 100644
index 0000000..d843cb0
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/signature_decrypt_tests.rs
@@ -0,0 +1,589 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#![allow(clippy::unwrap_used)]
+
+extern crate std;
+
+use super::*;
+use crate::{
+    deserialization_arena,
+    extended::{
+        data_elements::TxPowerDataElement,
+        deserialize::{
+            encrypted_section,
+            section::intermediate::{
+                parse_sections, tests::IntermediateSectionExt, CiphertextSection,
+            },
+            DataElement, DataElementParseError, Section,
+        },
+        serialize::{
+            AdvBuilder, AdvertisementType, CapacityLimitedVec, SignedEncryptedSectionEncoder,
+            SingleTypeDataElement, WriteDataElement,
+        },
+        V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN,
+    },
+    shared_data::TxPower,
+    NpVersionHeader,
+};
+use crypto_provider::ed25519;
+use crypto_provider_default::CryptoProviderImpl;
+use np_hkdf::{v1_salt, DerivedSectionKeys};
+use sink::Sink;
+use std::{prelude::rust_2021::*, vec};
+
+type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
+
+#[test]
+fn deserialize_signature_encrypted_correct_keys_extended_salt() {
+    let identity_token = V1IdentityToken::from([1; 16]);
+    let key_seed = [2; 32];
+    let section_salt = ExtendedV1Salt::from([3; 16]);
+    let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+    let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
+
+    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+
+    let broadcast_cm = V1BroadcastCredential::new(key_seed, identity_token, private_key.clone());
+
+    let mut section_builder = adv_builder
+        .section_builder(SignedEncryptedSectionEncoder::new::<CryptoProviderImpl>(
+            section_salt,
+            &broadcast_cm,
+        ))
+        .unwrap();
+
+    let txpower_de = TxPowerDataElement::from(TxPower::try_from(7).unwrap());
+    section_builder.add_de(|_| txpower_de.clone()).unwrap();
+
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
+
+    let adv = adv_builder.into_advertisement();
+
+    let (after_version_header, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
+
+    let adv_header = if let NpVersionHeader::V1(h) = header {
+        h
+    } else {
+        panic!("incorrect header");
+    };
+
+    let sections = parse_sections(adv_header, after_version_header).unwrap();
+    assert_eq!(1, sections.len());
+
+    let section = sections.into_iter().next().unwrap();
+    let enc_section = section.as_ciphertext().unwrap();
+    let contents = if let CiphertextSection::SignatureEncrypted(contents) = &enc_section {
+        contents
+    } else {
+        panic!("incorrect flavor");
+    };
+
+    assert_eq!(
+        &SignatureEncryptedSection {
+            contents: EncryptedSectionContents {
+                adv_header,
+                format_bytes: &[V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN],
+                salt: section_salt,
+                identity_token: first_section_identity_token(
+                    section_salt.into(),
+                    after_version_header
+                ),
+                // adv header + section len + format + salt + identity token
+                section_contents: first_section_contents(after_version_header),
+                total_section_contents_len: contents.contents.total_section_contents_len
+            },
+        },
+        contents
+    );
+
+    let mut de_bytes = Vec::new();
+    de_bytes.extend_from_slice(txpower_de.de_header().serialize().as_slice());
+    let _ = txpower_de.write_de_contents(&mut de_bytes);
+    let de_bytes = de_bytes;
+
+    // plaintext is correct
+    {
+        let credential = V1DiscoveryCredential::new(
+            key_seed,
+            key_seed_hkdf
+                .v1_mic_short_salt_keys()
+                .identity_token_hmac_key()
+                .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
+            key_seed_hkdf
+                .v1_mic_extended_salt_keys()
+                .identity_token_hmac_key()
+                .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
+            key_seed_hkdf
+                .v1_signature_keys()
+                .identity_token_hmac_key()
+                .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
+            private_key.derive_public_key::<Ed25519ProviderImpl>(),
+        );
+        let signed_identity_resolution_material =
+            credential.signed_identity_resolution_material::<CryptoProviderImpl>();
+        let identity_resolution_contents =
+            contents.contents.compute_identity_resolution_contents::<CryptoProviderImpl>();
+        let identity_match = identity_resolution_contents
+            .try_match::<CryptoProviderImpl>(
+                &signed_identity_resolution_material.into_raw_resolution_material(),
+            )
+            .unwrap();
+
+        let arena = deserialization_arena!();
+        let mut allocator = arena.into_allocator();
+        let decrypted =
+            contents.contents.decrypt_ciphertext(&mut allocator, identity_match).unwrap();
+
+        let nonce: AesCtrNonce = section_salt.compute_nonce::<CryptoProviderImpl>();
+
+        let sig_payload = SectionSignaturePayload::new(
+            &[V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN],
+            section_salt.as_slice(),
+            &nonce,
+            identity_token.as_slice(),
+            (de_bytes.len() + crypto_provider::ed25519::SIGNATURE_LENGTH).try_into().unwrap(),
+            &de_bytes,
+        );
+
+        let mut de_and_sig = de_bytes.clone();
+        de_and_sig
+            .extend_from_slice(&sig_payload.sign::<Ed25519ProviderImpl>(&private_key).to_bytes());
+        assert_eq!(identity_token, decrypted.identity_token);
+        assert_eq!(nonce, decrypted.nonce);
+        assert_eq!(&de_and_sig, decrypted.plaintext_contents);
+    }
+
+    // deserialization to Section works
+    {
+        let credential = V1DiscoveryCredential::new(
+            key_seed,
+            key_seed_hkdf
+                .v1_mic_short_salt_keys()
+                .identity_token_hmac_key()
+                .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
+            key_seed_hkdf
+                .v1_mic_extended_salt_keys()
+                .identity_token_hmac_key()
+                .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
+            key_seed_hkdf
+                .v1_signature_keys()
+                .identity_token_hmac_key()
+                .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
+            private_key.derive_public_key::<Ed25519ProviderImpl>(),
+        );
+        let signed_identity_resolution_material =
+            credential.signed_identity_resolution_material::<CryptoProviderImpl>();
+        let signed_verification_material =
+            credential.signed_verification_material::<CryptoProviderImpl>();
+
+        let arena = deserialization_arena!();
+        let mut allocator = arena.into_allocator();
+        let section = contents
+            .try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
+                &mut allocator,
+                &signed_identity_resolution_material,
+                &signed_verification_material,
+            )
+            .unwrap();
+
+        assert_eq!(
+            DecryptedSection::new(
+                VerificationMode::Signature,
+                section_salt.into(),
+                identity_token,
+                &de_bytes,
+            ),
+            section
+        );
+        let data_elements = section.collect_data_elements().unwrap();
+        assert_eq!(data_elements, &[DataElement::new(0.into(), 0x05_u8.into(), &[7])]);
+
+        assert_eq!(
+            vec![(v1_salt::DataElementOffset::from(0_u8), TxPowerDataElement::DE_TYPE, vec![7u8])],
+            data_elements
+                .into_iter()
+                .map(|de| (de.offset(), de.de_type(), de.contents().to_vec()))
+                .collect::<Vec<_>>()
+        );
+    }
+}
+
+#[test]
+fn deserialize_signature_encrypted_incorrect_aes_key_error() {
+    // bad aes key -> bad metadata key plaintext
+    do_bad_deserialize_params::<CryptoProviderImpl>(
+        IdentityResolutionOrDeserializationError::IdentityMatchingError,
+        Some([0xFF; 16].into()),
+        None,
+        None,
+        None,
+    );
+}
+
+#[test]
+fn deserialize_signature_encrypted_incorrect_metadata_key_hmac_key_error() {
+    // bad metadata key hmac key -> bad calculated metadata key mac
+    do_bad_deserialize_params::<CryptoProviderImpl>(
+        IdentityResolutionOrDeserializationError::IdentityMatchingError,
+        None,
+        Some([0xFF; 32].into()),
+        None,
+        None,
+    );
+}
+
+#[test]
+fn deserialize_signature_encrypted_incorrect_expected_metadata_key_hmac_error() {
+    // bad expected metadata key mac
+    do_bad_deserialize_params::<CryptoProviderImpl>(
+        IdentityResolutionOrDeserializationError::IdentityMatchingError,
+        None,
+        None,
+        Some([0xFF; 32]),
+        None,
+    );
+}
+
+#[test]
+fn deserialize_signature_encrypted_incorrect_pub_key_error() {
+    // a random pub key will lead to signature mismatch
+    do_bad_deserialize_params::<CryptoProviderImpl>(
+        SignatureVerificationError::SignatureMismatch.into(),
+        None,
+        None,
+        None,
+        Some(
+            ed25519::PrivateKey::generate::<Ed25519ProviderImpl>()
+                .derive_public_key::<Ed25519ProviderImpl>(),
+        ),
+    );
+}
+
+#[test]
+fn deserialize_signature_encrypted_incorrect_salt_error() {
+    // bad salt -> bad iv -> bad metadata key plaintext
+    do_bad_deserialize_tampered(
+        DeserializeError::IdentityResolutionOrDeserializationError(
+            IdentityResolutionOrDeserializationError::IdentityMatchingError,
+        ),
+        None,
+        |_| {},
+        |adv_mut| {
+            adv_mut[1 + 1 + 1..][..EXTENDED_SALT_LEN].copy_from_slice(&[0xFF; EXTENDED_SALT_LEN])
+        },
+    )
+}
+
+#[test]
+fn deserialize_signature_encrypted_tampered_signature_error() {
+    do_bad_deserialize_tampered(
+        DeserializeError::IdentityResolutionOrDeserializationError(
+            SignatureVerificationError::SignatureMismatch.into(),
+        ),
+        None,
+        |_| {},
+        // flip a bit in the middle of the signature
+        |adv_mut| {
+            let len = adv_mut.len();
+            adv_mut[len - 30] ^= 0x1
+        },
+    )
+}
+
+#[test]
+fn deserialize_signature_encrypted_tampered_ciphertext_error() {
+    do_bad_deserialize_tampered(
+        DeserializeError::IdentityResolutionOrDeserializationError(
+            SignatureVerificationError::SignatureMismatch.into(),
+        ),
+        None,
+        |_| {},
+        // flip a bit outside of the signature
+        |adv_mut| {
+            let len = adv_mut.len();
+            adv_mut[len - 1 - 64] ^= 0x1
+        },
+    )
+}
+
+#[test]
+fn deserialize_signature_encrypted_missing_signature_error() {
+    let de_len = 2;
+    do_bad_deserialize_tampered(
+        DeserializeError::IdentityResolutionOrDeserializationError(
+            SignatureVerificationError::SignatureMissing.into(),
+        ),
+        Some(de_len as u8),
+        |_| {},
+        |adv_mut| {
+            // chop off signature
+            adv_mut.truncate(adv_mut.len() - 64);
+            // fix section length
+            adv_mut[1 + 1 + EXTENDED_SALT_LEN + V1_IDENTITY_TOKEN_LEN..][0] = de_len as u8;
+        },
+    )
+}
+
+#[test]
+fn deserialize_signature_encrypted_des_wont_parse() {
+    do_bad_deserialize_tampered(
+        DeserializeError::DataElementParseError(DataElementParseError::UnexpectedDataAfterEnd),
+        Some(2 + 1 + crypto_provider::ed25519::SIGNATURE_LENGTH as u8),
+        // add an impossible DE
+        |section| {
+            section.try_push(0xFF).unwrap();
+        },
+        |_| {},
+    )
+}
+
+#[test]
+fn arena_out_of_space_on_signature_verify() {
+    let salt: ExtendedV1Salt = [0; 16].into();
+    let nonce = salt.compute_nonce::<CryptoProviderImpl>();
+    let sig_enc_section = SignatureEncryptedSection {
+        contents: EncryptedSectionContents::new(
+            V1AdvHeader::new(0),
+            &[0],
+            [0; 16].into(),
+            [0x00; V1_IDENTITY_TOKEN_LEN].into(),
+            1,
+            &[0x00; 1],
+        ),
+    };
+
+    let arena = deserialization_arena!();
+    let mut allocator = arena.into_allocator();
+    let _ = allocator.allocate(250).unwrap();
+
+    let cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
+        &[0u8; 16].into(),
+        NonceAndCounter::from_nonce(nonce),
+    );
+    let im: encrypted_section::IdentityMatch<CryptoProviderImpl> =
+        IdentityMatch { cipher, identity_token: V1IdentityToken([0; 16]), nonce };
+
+    let signed_verification_material = SignedSectionVerificationMaterial {
+        public_key: ed25519::PublicKey::from_bytes::<Ed25519ProviderImpl>([0u8; 32])
+            .expect("public key should be valid bytes"),
+    };
+
+    let _ = sig_enc_section.try_deserialize(&mut allocator, im, &signed_verification_material);
+}
+
+/// Attempt a deserialization that will fail when using the provided parameters for decryption only.
+/// `None` means use the correct value for that parameter.
+fn do_bad_deserialize_params<C: CryptoProvider>(
+    error: IdentityResolutionOrDeserializationError<SignatureVerificationError>,
+    aes_key: Option<crypto_provider::aes::Aes128Key>,
+    metadata_key_hmac_key: Option<np_hkdf::NpHmacSha256Key>,
+    expected_metadata_key_hmac: Option<[u8; 32]>,
+    pub_key: Option<ed25519::PublicKey>,
+) {
+    let metadata_key = V1IdentityToken([1; 16]);
+    let key_seed = [2; 32];
+    let section_salt: v1_salt::ExtendedV1Salt = [3; 16].into();
+    let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
+    let private_key = ed25519::PrivateKey::generate::<C::Ed25519>();
+
+    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+
+    let broadcast_cm = V1BroadcastCredential::new(key_seed, metadata_key, private_key.clone());
+
+    let mut section_builder = adv_builder
+        .section_builder(SignedEncryptedSectionEncoder::new::<C>(section_salt, &broadcast_cm))
+        .unwrap();
+
+    section_builder.add_de_res(|_| TxPower::try_from(2).map(TxPowerDataElement::from)).unwrap();
+
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
+
+    let adv = adv_builder.into_advertisement();
+
+    let (remaining, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
+
+    let v1_header = if let NpVersionHeader::V1(h) = header {
+        h
+    } else {
+        panic!("incorrect header");
+    };
+
+    let sections = parse_sections(v1_header, remaining).unwrap();
+    assert_eq!(1, sections.len());
+
+    let section = sections.into_iter().next().unwrap();
+    let enc_section = section.as_ciphertext().unwrap();
+    let contents = if let CiphertextSection::SignatureEncrypted(contents) = &enc_section {
+        contents
+    } else {
+        panic!("incorrect flavor");
+    };
+
+    let signed_identity_resolution_material =
+        SignedSectionIdentityResolutionMaterial::from_raw(SectionIdentityResolutionMaterial {
+            aes_key: aes_key.unwrap_or_else(|| key_seed_hkdf.v1_signature_keys().aes_key()),
+
+            identity_token_hmac_key: *metadata_key_hmac_key
+                .unwrap_or_else(|| key_seed_hkdf.v1_signature_keys().identity_token_hmac_key())
+                .as_bytes(),
+            expected_identity_token_hmac: expected_metadata_key_hmac.unwrap_or_else(|| {
+                key_seed_hkdf
+                    .v1_signature_keys()
+                    .identity_token_hmac_key()
+                    .calculate_hmac::<CryptoProviderImpl>(&metadata_key.0)
+            }),
+        });
+
+    let signed_verification_material = SignedSectionVerificationMaterial {
+        public_key: pub_key
+            .unwrap_or_else(|| private_key.derive_public_key::<Ed25519ProviderImpl>()),
+    };
+
+    assert_eq!(
+        error,
+        contents
+            .try_resolve_identity_and_deserialize::<C>(
+                &mut deserialization_arena!().into_allocator(),
+                &signed_identity_resolution_material,
+                &signed_verification_material,
+            )
+            .unwrap_err()
+    );
+}
+
+#[derive(Debug, PartialEq)]
+enum DeserializeError {
+    IdentityResolutionOrDeserializationError(
+        IdentityResolutionOrDeserializationError<SignatureVerificationError>,
+    ),
+    DataElementParseError(DataElementParseError),
+}
+
+/// Run a test that mangles the advertisement contents before attempting to deserialize.
+///
+/// The section will have a TxPower DE added.
+fn do_bad_deserialize_tampered(
+    expected_error: DeserializeError,
+    expected_section_de_len: Option<u8>,
+    mangle_section: impl Fn(&mut CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>),
+    mangle_adv_contents: impl Fn(&mut Vec<u8>),
+) {
+    let identity_token = V1IdentityToken::from([1; V1_IDENTITY_TOKEN_LEN]);
+    let key_seed = [2; 32];
+    let section_salt: v1_salt::ExtendedV1Salt = [3; EXTENDED_SALT_LEN].into();
+    let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+    let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
+
+    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+
+    let broadcast_cm = V1BroadcastCredential::new(key_seed, identity_token, private_key.clone());
+
+    let mut section_builder = adv_builder
+        .section_builder(SignedEncryptedSectionEncoder::new::<CryptoProviderImpl>(
+            section_salt,
+            &broadcast_cm,
+        ))
+        .unwrap();
+
+    section_builder.add_de_res(|_| TxPower::try_from(2).map(TxPowerDataElement::from)).unwrap();
+
+    mangle_section(&mut section_builder.section);
+
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
+
+    let adv = adv_builder.into_advertisement();
+    let mut adv_mut = adv.as_slice().to_vec();
+    mangle_adv_contents(&mut adv_mut);
+
+    let (after_version_header, header) = NpVersionHeader::parse(&adv_mut).unwrap();
+
+    let adv_header = if let NpVersionHeader::V1(h) = header {
+        h
+    } else {
+        panic!("incorrect header");
+    };
+
+    let sections = parse_sections(adv_header, after_version_header).unwrap();
+    assert_eq!(1, sections.len());
+
+    let section = sections.into_iter().next().unwrap();
+    let enc_section = section.as_ciphertext().unwrap();
+    let contents = if let CiphertextSection::SignatureEncrypted(contents) = &enc_section {
+        contents
+    } else {
+        panic!("incorrect flavor");
+    };
+    if let Some(len) = expected_section_de_len {
+        assert_eq!(usize::from(len), contents.contents.section_contents.len());
+    }
+
+    let extracted_salt = first_section_salt(after_version_header);
+    assert_eq!(
+        &SignatureEncryptedSection {
+            contents: EncryptedSectionContents {
+                adv_header,
+                // extract data from adv buffer in case the caller tampered with things
+                salt: extracted_salt,
+                identity_token: first_section_identity_token(
+                    extracted_salt.into(),
+                    after_version_header
+                ),
+                section_contents: first_section_contents(after_version_header),
+                format_bytes: first_section_format(after_version_header),
+                total_section_contents_len: first_section_contents_len(after_version_header),
+            },
+        },
+        contents
+    );
+
+    let credential = V1DiscoveryCredential::new(
+        key_seed,
+        key_seed_hkdf
+            .v1_mic_short_salt_keys()
+            .identity_token_hmac_key()
+            .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
+        key_seed_hkdf
+            .v1_mic_extended_salt_keys()
+            .identity_token_hmac_key()
+            .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
+        key_seed_hkdf
+            .v1_signature_keys()
+            .identity_token_hmac_key()
+            .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
+        private_key.derive_public_key::<Ed25519ProviderImpl>(),
+    );
+    let identity_resolution_material =
+        credential.signed_identity_resolution_material::<CryptoProviderImpl>();
+    let verification_material = credential.signed_verification_material::<CryptoProviderImpl>();
+
+    match contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
+        &mut deserialization_arena!().into_allocator(),
+        &identity_resolution_material,
+        &verification_material,
+    ) {
+        Ok(section) => {
+            assert_eq!(
+                expected_error,
+                DeserializeError::DataElementParseError(
+                    section.collect_data_elements().unwrap_err()
+                ),
+            );
+        }
+        Err(e) => assert_eq!(
+            expected_error,
+            DeserializeError::IdentityResolutionOrDeserializationError(e),
+        ),
+    };
+}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/mod.rs
index a208040..c6c7ba4 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/mod.rs
@@ -13,265 +13,84 @@
 // limitations under the License.
 
 //! Deserialization for V1 advertisement contents
-
 #[cfg(any(test, feature = "alloc"))]
-extern crate alloc;
-
-#[cfg(any(test, feature = "alloc", feature = "devtools"))]
 use alloc::vec::Vec;
-use core::array::TryFromSliceError;
 
-use core::fmt::Debug;
-use nom::{branch, bytes, combinator, error, multi, number, sequence};
-use strum::IntoEnumIterator as _;
+use core::{array::TryFromSliceError, fmt::Debug};
 
-use array_view::ArrayView;
-use crypto_provider::CryptoProvider;
-use np_hkdf::v1_salt::{self, V1Salt};
-
-use crate::array_vec::ArrayVecOption;
-#[cfg(any(feature = "devtools", test))]
-use crate::credential::v1::V1DiscoveryCryptoMaterial;
-use crate::credential::v1::V1;
-use crate::deserialization_arena::ArenaOutOfSpace;
-#[cfg(any(feature = "devtools", test))]
-use crate::deserialization_arena::DeserializationArenaAllocator;
-#[cfg(test)]
-use crate::extended::deserialize::encrypted_section::IdentityResolutionOrDeserializationError;
-use crate::extended::{NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT, NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT};
 use crate::{
-    de_type::{EncryptedIdentityDataElementType, IdentityDataElementType},
-    extended::{
-        data_elements::{MIC_ENCRYPTION_SCHEME, SIGNATURE_ENCRYPTION_SCHEME},
-        de_type::{DeType, ExtendedDataElementType as _},
-        deserialize::encrypted_section::{
-            DeserializationError, EncryptedSectionContents, MicEncryptedSection,
-            MicVerificationError, SignatureEncryptedSection, SignatureVerificationError,
-        },
-        DeLength, ENCRYPTION_INFO_DE_TYPE, NP_ADV_MAX_SECTION_LEN,
+    array_vec::ArrayVecOption,
+    credential::{
+        book::CredentialBook,
+        matched::{HasIdentityMatch, MatchedCredential, WithMatchedCredential},
+        v1::{V1DiscoveryCryptoMaterial, V1},
     },
-    HasIdentityMatch, MetadataKey, PlaintextIdentityMode, V1Header,
+    deserialization_arena::{ArenaOutOfSpace, DeserializationArena, DeserializationArenaAllocator},
+    extended::{
+        deserialize::{
+            data_element::{DataElement, DataElementParseError, DataElementParsingIterator},
+            encrypted_section::{
+                DeserializationError, MicVerificationError, SectionIdentityResolutionContents,
+                SignatureVerificationError,
+            },
+            section::intermediate::{
+                parse_sections, CiphertextSection, IntermediateSection, PlaintextSection,
+            },
+        },
+        salt::MultiSalt,
+        V1IdentityToken, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
+    },
+    header::V1AdvHeader,
+    AdvDeserializationError, AdvDeserializationErrorDetailsHazmat,
 };
+use crypto_provider::CryptoProvider;
 
+#[cfg(test)]
+mod tests;
+
+pub mod data_element;
 pub(crate) mod encrypted_section;
+pub(crate) mod section;
 
-#[cfg(test)]
-mod parse_tests;
+/// Provides deserialization APIs which expose more of the internals, suitable for use only in
+/// dev tools.
+#[cfg(feature = "devtools")]
+pub mod dev_tools;
 
-#[cfg(test)]
-mod section_tests;
+/// Deserialize and decrypt the contents of a v1 adv after the version header
+pub(crate) fn deser_decrypt_v1<'adv, 'cred, B, P>(
+    arena: DeserializationArena<'adv>,
+    cred_book: &'cred B,
+    remaining: &'adv [u8],
+    header: V1AdvHeader,
+) -> Result<V1AdvertisementContents<'adv, B::Matched>, AdvDeserializationError>
+where
+    B: CredentialBook<'cred>,
+    P: CryptoProvider,
+{
+    let mut sections_in_processing =
+        SectionsInProcessing::<'_, B::Matched>::from_advertisement_contents::<P>(
+            header, remaining,
+        )?;
 
-#[cfg(test)]
-mod test_stubs;
+    let mut allocator = arena.into_allocator();
 
-/// Parse into [IntermediateSection]s, exposing the underlying parsing errors.
-/// Consumes all of `adv_body`.
-pub(crate) fn parse_sections(
-    adv_header: V1Header,
-    adv_body: &[u8],
-) -> Result<
-    ArrayVecOption<IntermediateSection, NP_V1_ADV_MAX_ENCRYPTED_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,
-            IntermediateSection::parser_public_section(),
-            ArrayVecOption::default,
-            |mut acc, item| {
-                acc.push(item);
-                acc
-            },
-        ),
-        // Encrypted advertisement
-        multi::fold_many_m_n(
-            1,
-            NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
-            IntermediateSection::parser_encrypted_with_header(adv_header),
-            ArrayVecOption::default,
-            |mut acc, item| {
-                acc.push(item);
-                acc
-            },
-        ),
-    )))(adv_body)
-    .map(|(_rem, sections)| sections)
-}
-
-/// A partially processed section that hasn't been decrypted (if applicable) yet.
-#[derive(PartialEq, Eq, Debug)]
-pub(crate) enum IntermediateSection<'a> {
-    /// A section that was not encrypted, e.g. a public identity or no-identity section.
-    Plaintext(PlaintextSection<'a>),
-    /// A section whose contents were encrypted, e.g. a private identity section.
-    Ciphertext(CiphertextSection<'a>),
-}
-
-impl<'a> IntermediateSection<'a> {
-    fn parser_public_section(
-    ) -> impl Fn(&'a [u8]) -> nom::IResult<&'a [u8], IntermediateSection<'a>> {
-        move |adv_body| {
-            let (remaining, section_contents_len) =
-                combinator::verify(number::complete::u8, |sec_len| {
-                    *sec_len as usize <= NP_ADV_MAX_SECTION_LEN && *sec_len as usize > 0
-                })(adv_body)?;
-
-            // Section structure possibilities:
-            // - Public Identity DE, all other DEs
-            let parse_public_identity = combinator::map(
-                // 1 starting offset because of public identity before it
-                sequence::tuple((PublicIdentity::parse, nom::combinator::rest)),
-                // move closure to copy section_len into scope
-                move |(_identity, rest)| {
-                    IntermediateSection::Plaintext(PlaintextSection::new(
-                        PlaintextIdentityMode::Public,
-                        SectionContents::new(
-                            /* section_header */ section_contents_len,
-                            rest,
-                            1,
-                        ),
-                    ))
-                },
-            );
-            combinator::map_parser(
-                bytes::complete::take(section_contents_len),
-                // Guarantee we consume all of the section (not all of the adv body)
-                // Note: `all_consuming` should never fail, since we used the `rest` parser above.
-                combinator::all_consuming(parse_public_identity),
-            )(remaining)
+    // Hot loop
+    // We assume that iterating credentials is more expensive than iterating sections
+    for (crypto_material, match_data) in cred_book.v1_iter() {
+        sections_in_processing
+            .try_decrypt_with_credential::<_, P>(&mut allocator, crypto_material, match_data)
+            .expect(concat!(
+                "Should not run out of space because DeserializationArenaAllocator is big ",
+                "enough to hold a single advertisement, and we exit immediately upon ",
+                "successful decryption",
+            ));
+        if sections_in_processing.resolved_all_identities() {
+            // No need to consider the other credentials
+            break;
         }
     }
-
-    fn parser_encrypted_with_header(
-        adv_header: V1Header,
-    ) -> impl Fn(&'a [u8]) -> nom::IResult<&[u8], IntermediateSection> {
-        move |adv_body| {
-            let (remaining, section_contents_len) =
-                combinator::verify(number::complete::u8, |sec_len| {
-                    *sec_len as usize <= NP_ADV_MAX_SECTION_LEN && *sec_len as usize > 0
-                })(adv_body)?;
-
-            // Section structure possibilities:
-            // - Encryption information, non-public Identity header, ciphertext
-            // - Encryption information, non-public Identity header, ciphertext, MIC
-
-            let parse_encrypted_identity = combinator::map(
-                sequence::tuple((
-                    EncryptionInfo::parse_signature,
-                    combinator::verify(
-                        combinator::consumed(sequence::tuple((
-                            EncryptedIdentityMetadata::parser_at_offset(
-                                v1_salt::DataElementOffset::from(1),
-                            ),
-                            combinator::rest,
-                        ))),
-                        // should be trivially true since section length was checked above,
-                        // but this is an invariant for EncryptedSection, so we double check
-                        |(identity_and_ciphertext, _tuple)| {
-                            (EncryptedIdentityMetadata::TOTAL_DE_LEN..=NP_ADV_MAX_SECTION_LEN)
-                                .contains(&identity_and_ciphertext.len())
-                        },
-                    ),
-                )),
-                move |(encryption_info, (identity_and_ciphertext, (identity, _des_ciphertext)))| {
-                    // skip identity de header -- rest of that de is ciphertext
-                    let to_skip = identity.header_bytes.len();
-                    IntermediateSection::Ciphertext(CiphertextSection::SignatureEncryptedIdentity(
-                        SignatureEncryptedSection {
-                            contents: EncryptedSectionContents::new(
-                                adv_header,
-                                section_contents_len,
-                                encryption_info,
-                                identity,
-                                &identity_and_ciphertext[to_skip..],
-                            ),
-                        },
-                    ))
-                },
-            );
-
-            let parse_mic_encrypted_identity = combinator::map(
-                sequence::tuple((
-                    EncryptionInfo::parse_mic,
-                    combinator::verify(
-                        combinator::consumed(sequence::tuple((
-                            EncryptedIdentityMetadata::parser_at_offset(
-                                v1_salt::DataElementOffset::from(1),
-                            ),
-                            combinator::rest,
-                        ))),
-                        // Should be trivially true since section length was checked above,
-                        // but this is an invariant for MicEncryptedSection, so we double check.
-                        // Also verify that there is enough space at the end to contain a valid-length MIC.
-                        |(identity_ciphertext_and_mic, _tuple)| {
-                            (EncryptedIdentityMetadata::TOTAL_DE_LEN + SectionMic::CONTENTS_LEN
-                                ..=NP_ADV_MAX_SECTION_LEN)
-                                .contains(&identity_ciphertext_and_mic.len())
-                        },
-                    ),
-                )),
-                move |(
-                    encryption_info,
-                    (identity_ciphertext_and_mic, (identity, _ciphertext_and_mic)),
-                )| {
-                    // should not panic since we have already ensured a valid length
-                    let (identity_and_ciphertext, mic) = identity_ciphertext_and_mic
-                        .split_at(identity_ciphertext_and_mic.len() - SectionMic::CONTENTS_LEN);
-                    // skip identity de header -- rest of that de is ciphertext
-                    let to_skip = identity.header_bytes.len();
-                    IntermediateSection::Ciphertext(CiphertextSection::MicEncryptedIdentity(
-                        MicEncryptedSection {
-                            contents: EncryptedSectionContents::new(
-                                adv_header,
-                                section_contents_len,
-                                encryption_info,
-                                identity,
-                                &identity_and_ciphertext[to_skip..],
-                            ),
-                            mic: mic.try_into().unwrap_or_else(|_| {
-                                panic!("{} is a valid length", SectionMic::CONTENTS_LEN)
-                            }),
-                        },
-                    ))
-                },
-            );
-
-            combinator::map_parser(
-                bytes::complete::take(section_contents_len),
-                // guarantee we consume all of the section (not all of the adv body)
-                combinator::all_consuming(branch::alt((
-                    parse_mic_encrypted_identity,
-                    parse_encrypted_identity,
-                ))),
-            )(remaining)
-        }
-    }
-}
-
-#[derive(PartialEq, Eq, Debug)]
-struct SectionContents<'adv> {
-    section_header: u8,
-    de_region_excl_identity: &'adv [u8],
-    data_element_start_offset: u8,
-}
-
-impl<'adv> SectionContents<'adv> {
-    fn new(
-        section_header: u8,
-        de_region_excl_identity: &'adv [u8],
-        data_element_start_offset: u8,
-    ) -> Self {
-        Self { section_header, de_region_excl_identity, data_element_start_offset }
-    }
-
-    fn iter_data_elements(&self) -> DataElementParsingIterator<'adv> {
-        DataElementParsingIterator {
-            input: self.de_region_excl_identity,
-            offset: self.data_element_start_offset,
-        }
-    }
+    Ok(sections_in_processing.finished_with_decryption_attempts())
 }
 
 /// A section deserialized from a V1 advertisement.
@@ -295,83 +114,24 @@
     }
 }
 
-/// A plaintext section deserialized from a V1 advertisement.
-#[derive(PartialEq, Eq, Debug)]
-pub struct PlaintextSection<'adv> {
-    identity: PlaintextIdentityMode,
-    contents: SectionContents<'adv>,
-}
-
-impl<'adv> PlaintextSection<'adv> {
-    fn new(identity: PlaintextIdentityMode, contents: SectionContents<'adv>) -> Self {
-        Self { identity, contents }
-    }
-
-    /// The identity mode for the section.
-    ///
-    /// Since plaintext sections do not use encryption, they cannot be matched to a single identity,
-    /// and only have a mode (no identity or public).
-    pub fn identity(&self) -> PlaintextIdentityMode {
-        self.identity
-    }
-}
-
-impl<'adv> Section<'adv, DataElementParseError> for PlaintextSection<'adv> {
-    type Iterator = DataElementParsingIterator<'adv>;
-
-    fn iter_data_elements(&self) -> Self::Iterator {
-        self.contents.iter_data_elements()
-    }
-}
-
-/// A byte buffer the size of a V1 salt.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub struct RawV1Salt(pub(crate) [u8; 16]);
-
-impl RawV1Salt {
-    /// Returns the raw salt bytes as a vec.
-    #[cfg(feature = "devtools")]
-    pub fn to_vec(&self) -> Vec<u8> {
-        self.0.to_vec()
-    }
-}
-
-impl<C: CryptoProvider> From<RawV1Salt> for V1Salt<C> {
-    fn from(raw_salt: RawV1Salt) -> Self {
-        raw_salt.0.into()
-    }
-}
-
-impl<C: CryptoProvider> From<V1Salt<C>> for RawV1Salt {
-    fn from(salt: V1Salt<C>) -> Self {
-        RawV1Salt(salt.into_array())
-    }
-}
-
 /// Fully-parsed and verified decrypted contents from an encrypted section.
 #[derive(Debug, PartialEq, Eq)]
 pub struct DecryptedSection<'adv> {
-    identity_type: EncryptedIdentityDataElementType,
     verification_mode: VerificationMode,
-    metadata_key: MetadataKey,
-    salt: RawV1Salt,
-    contents: SectionContents<'adv>,
+    identity_token: V1IdentityToken,
+    salt: MultiSalt,
+    /// Decrypted DE data, excluding any encoder suffix
+    plaintext: &'adv [u8],
 }
 
 impl<'adv> DecryptedSection<'adv> {
     fn new(
-        identity_type: EncryptedIdentityDataElementType,
         verification_mode: VerificationMode,
-        metadata_key: MetadataKey,
-        salt: RawV1Salt,
-        contents: SectionContents<'adv>,
+        salt: MultiSalt,
+        identity_token: V1IdentityToken,
+        plaintext: &'adv [u8],
     ) -> Self {
-        Self { identity_type, verification_mode, metadata_key, salt, contents }
-    }
-
-    /// The type of identity DE used in the section.
-    pub fn identity_type(&self) -> EncryptedIdentityDataElementType {
-        self.identity_type
+        Self { verification_mode, identity_token, salt, plaintext }
     }
 
     /// The verification mode used in the section.
@@ -379,16 +139,26 @@
         self.verification_mode
     }
 
-    /// The salt used for decryption of this advertisement.
-    pub fn salt(&self) -> RawV1Salt {
-        self.salt
+    /// The identity token extracted from the section
+    pub fn identity_token(&self) -> &V1IdentityToken {
+        &self.identity_token
+    }
+
+    /// The salt used for decryption of this section.
+    pub fn salt(&self) -> &MultiSalt {
+        &self.salt
+    }
+
+    #[cfg(test)]
+    pub(crate) fn plaintext(&self) -> &'adv [u8] {
+        self.plaintext
     }
 }
 
 impl<'adv> HasIdentityMatch for DecryptedSection<'adv> {
     type Version = V1;
-    fn metadata_key(&self) -> MetadataKey {
-        self.metadata_key
+    fn identity_token(&self) -> V1IdentityToken {
+        self.identity_token
     }
 }
 
@@ -396,88 +166,7 @@
     type Iterator = DataElementParsingIterator<'adv>;
 
     fn iter_data_elements(&self) -> Self::Iterator {
-        self.contents.iter_data_elements()
-    }
-}
-
-#[derive(PartialEq, Eq, Debug)]
-pub(crate) enum CiphertextSection<'a> {
-    SignatureEncryptedIdentity(SignatureEncryptedSection<'a>),
-    MicEncryptedIdentity(MicEncryptedSection<'a>),
-}
-
-impl<'a> CiphertextSection<'a> {
-    /// Tries to match this section's identity using the given crypto-material,
-    /// and if successful, tries to decrypt this section.
-    #[cfg(test)]
-    pub(crate) fn try_resolve_identity_and_deserialize<C, P>(
-        &'a self,
-        allocator: &mut DeserializationArenaAllocator<'a>,
-        crypto_material: &C,
-    ) -> Result<DecryptedSection<'a>, SectionDeserializeError>
-    where
-        C: V1DiscoveryCryptoMaterial,
-        P: CryptoProvider,
-    {
-        match self {
-            CiphertextSection::SignatureEncryptedIdentity(contents) => {
-                let identity_resolution_material =
-                    crypto_material.signed_identity_resolution_material::<P>();
-                let verification_material = crypto_material.signed_verification_material::<P>();
-
-                contents
-                    .try_resolve_identity_and_deserialize::<P>(
-                        allocator,
-                        &identity_resolution_material,
-                        &verification_material,
-                    )
-                    .map_err(|e| e.into())
-            }
-            CiphertextSection::MicEncryptedIdentity(contents) => {
-                let identity_resolution_material =
-                    crypto_material.unsigned_identity_resolution_material::<P>();
-                let verification_material = crypto_material.unsigned_verification_material::<P>();
-
-                contents
-                    .try_resolve_identity_and_deserialize::<P>(
-                        allocator,
-                        &identity_resolution_material,
-                        &verification_material,
-                    )
-                    .map_err(|e| e.into())
-            }
-        }
-    }
-
-    /// Try decrypting into some raw bytes given some raw unsigned crypto-material.
-    #[cfg(feature = "devtools")]
-    pub(crate) fn try_resolve_identity_and_decrypt<
-        C: V1DiscoveryCryptoMaterial,
-        P: CryptoProvider,
-    >(
-        &self,
-        allocator: &mut DeserializationArenaAllocator<'a>,
-        crypto_material: &C,
-    ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
-        match self {
-            CiphertextSection::SignatureEncryptedIdentity(x) => {
-                let identity_resolution_material =
-                    crypto_material.signed_identity_resolution_material::<P>();
-                x.try_resolve_identity_and_decrypt::<P>(allocator, &identity_resolution_material)
-            }
-            CiphertextSection::MicEncryptedIdentity(x) => {
-                let identity_resolution_material =
-                    crypto_material.unsigned_identity_resolution_material::<P>();
-                x.try_resolve_identity_and_decrypt::<P>(allocator, &identity_resolution_material)
-            }
-        }
-    }
-
-    pub(crate) fn contents(&self) -> &EncryptedSectionContents<'a> {
-        match self {
-            CiphertextSection::SignatureEncryptedIdentity(x) => &x.contents,
-            CiphertextSection::MicEncryptedIdentity(x) => &x.contents,
-        }
+        DataElementParsingIterator::new(self.plaintext)
     }
 }
 
@@ -493,34 +182,6 @@
     ArenaOutOfSpace,
 }
 
-#[cfg(test)]
-impl From<IdentityResolutionOrDeserializationError<MicVerificationError>>
-    for SectionDeserializeError
-{
-    fn from(error: IdentityResolutionOrDeserializationError<MicVerificationError>) -> Self {
-        match error {
-            IdentityResolutionOrDeserializationError::IdentityMatchingError => {
-                Self::IncorrectCredential
-            }
-            IdentityResolutionOrDeserializationError::DeserializationError(e) => e.into(),
-        }
-    }
-}
-
-#[cfg(test)]
-impl From<IdentityResolutionOrDeserializationError<SignatureVerificationError>>
-    for SectionDeserializeError
-{
-    fn from(error: IdentityResolutionOrDeserializationError<SignatureVerificationError>) -> Self {
-        match error {
-            IdentityResolutionOrDeserializationError::IdentityMatchingError => {
-                Self::IncorrectCredential
-            }
-            IdentityResolutionOrDeserializationError::DeserializationError(e) => e.into(),
-        }
-    }
-}
-
 impl From<DeserializationError<MicVerificationError>> for SectionDeserializeError {
     fn from(mic_deserialization_error: DeserializationError<MicVerificationError>) -> Self {
         match mic_deserialization_error {
@@ -548,191 +209,226 @@
     }
 }
 
-/// An iterator that parses the given data elements iteratively. In environments where memory is
-/// not severely constrained, it is usually safer to collect this into `Result<Vec<DataElement>>`
-/// so the validity of the whole advertisement can be checked before proceeding with further
-/// processing.
-#[derive(Debug)]
-pub struct DataElementParsingIterator<'adv> {
-    input: &'adv [u8],
-    // The index of the data element this is currently at
-    offset: u8,
+/// A ciphertext section which has not yet been
+/// resolved to an identity, but for which some
+/// `SectionIdentityResolutionContents` have been
+/// pre-computed for speedy identity-resolution.
+struct ResolvableCiphertextSection<'a> {
+    identity_resolution_contents: SectionIdentityResolutionContents,
+    ciphertext_section: CiphertextSection<'a>,
 }
 
-/// The error that may arise while parsing data elements.
-#[derive(Debug, PartialEq, Eq)]
-pub enum DataElementParseError {
-    /// Only one identity data element is allowed in an advertisement, but a duplicate is found
-    /// while parsing.
-    DuplicateIdentityDataElement,
-    /// Unexpected data found after the end of the data elements portion. This means either the
-    /// parser was fed with additional data (it should only be given the bytes within a section,
-    /// not the whole advertisement), or the length field in the header of the data element is
-    /// malformed.
-    UnexpectedDataAfterEnd,
-    /// There are too many data elements in the advertisement. The maximum number supported by the
-    /// current parsing logic is 255.
-    TooManyDataElements,
-    /// A parse error is returned during nom.
-    NomError(nom::error::ErrorKind),
+/// A collection of possibly-deserialized sections which are separated according
+/// to whether/not they're intermediate encrypted sections (of either type)
+/// or fully-deserialized, with a running count of the number of malformed sections.
+/// Each potentially-valid section is tagged with a 0-based index derived from the original
+/// section ordering as they appeared within the original advertisement to ensure
+/// that the fully-deserialized advertisement may be correctly reconstructed.
+struct SectionsInProcessing<'adv, M: MatchedCredential> {
+    deserialized_sections: ArrayVecOption<
+        (usize, V1DeserializedSection<'adv, M>),
+        { NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT },
+    >,
+    encrypted_sections: ArrayVecOption<
+        (usize, ResolvableCiphertextSection<'adv>),
+        { NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT },
+    >,
+    malformed_sections_count: usize,
 }
 
-impl<'adv> Iterator for DataElementParsingIterator<'adv> {
-    type Item = Result<DataElement<'adv>, DataElementParseError>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        match ProtoDataElement::parse(self.input) {
-            Ok((rem, pde)) => {
-                if !IdentityDataElementType::iter().all(|t| t.type_code() != pde.header.de_type) {
-                    return Some(Err(DataElementParseError::DuplicateIdentityDataElement));
+impl<'adv, M: MatchedCredential> SectionsInProcessing<'adv, M> {
+    /// Attempts to parse a V1 advertisement's contents after the version header
+    /// into a collection of not-yet-fully-deserialized sections which may
+    /// require credentials to be decrypted.
+    fn from_advertisement_contents<C: CryptoProvider>(
+        header: V1AdvHeader,
+        remaining: &'adv [u8],
+    ) -> Result<Self, AdvDeserializationError> {
+        let int_sections =
+            parse_sections(header, remaining).map_err(|_| AdvDeserializationError::ParseError {
+                details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError,
+            })?;
+        let mut deserialized_sections = ArrayVecOption::default();
+        let mut encrypted_sections = ArrayVecOption::default();
+        // keep track of ordering for later sorting during `self.finished_with_decryption_attempts()`.
+        for (idx, s) in int_sections.into_iter().enumerate() {
+            match s {
+                IntermediateSection::Plaintext(p) => {
+                    deserialized_sections.push((idx, V1DeserializedSection::Plaintext(p)))
                 }
-                self.input = rem;
-                let current_offset = self.offset;
-                self.offset = if let Some(offset) = self.offset.checked_add(1) {
-                    offset
-                } else {
-                    return Some(Err(DataElementParseError::TooManyDataElements));
-                };
-                Some(Ok(pde.into_data_element(v1_salt::DataElementOffset::from(current_offset))))
-            }
-            Err(nom::Err::Failure(e)) => Some(Err(DataElementParseError::NomError(e.code))),
-            Err(nom::Err::Incomplete(_)) => {
-                panic!("Should always complete since we are parsing using the `nom::complete` APIs")
-            }
-            Err(nom::Err::Error(_)) => {
-                // nom `Error` is recoverable, it usually means we should move on the parsing the
-                // next section. There is nothing after data elements within a section, so we just
-                // check that there is no remaining data.
-                if !self.input.is_empty() {
-                    return Some(Err(DataElementParseError::UnexpectedDataAfterEnd));
+                IntermediateSection::Ciphertext(ciphertext_section) => {
+                    let identity_resolution_contents =
+                        ciphertext_section.identity_resolution_contents::<C>();
+                    let resolvable_ciphertext_section = ResolvableCiphertextSection {
+                        identity_resolution_contents,
+                        ciphertext_section,
+                    };
+                    encrypted_sections.push((idx, resolvable_ciphertext_section));
                 }
-                None
             }
         }
+        Ok(Self { deserialized_sections, encrypted_sections, malformed_sections_count: 0 })
     }
-}
 
-/// 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.
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub(crate) struct DeHeader {
-    /// The original bytes of the header, at most 6 bytes long (1 byte len, 5 bytes type)
-    pub(crate) header_bytes: ArrayView<u8, 6>,
-    pub(crate) de_type: DeType,
-    pub(crate) contents_len: DeLength,
-}
+    /// Returns true iff we have resolved all sections to identities.
+    fn resolved_all_identities(&self) -> bool {
+        self.encrypted_sections.is_empty()
+    }
 
-impl DeHeader {
-    pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], DeHeader> {
-        // 1-byte header: 0b0LLLTTTT
-        let parse_single_byte_de_header =
-            combinator::map_opt::<&[u8], _, DeHeader, error::Error<&[u8]>, _, _>(
-                combinator::consumed(combinator::map_res(
-                    combinator::verify(number::complete::u8, |&b| !hi_bit_set(b)),
-                    |b| {
-                        // L bits
-                        let len = (b >> 4) & 0x07;
-                        // T bits
-                        let de_type = ((b & 0x0F) as u32).into();
-
-                        len.try_into().map(|l| (l, de_type))
-                    },
-                )),
-                |(header_bytes, (len, de_type))| {
-                    ArrayView::try_from_slice(header_bytes).map(|header_bytes| DeHeader {
-                        header_bytes,
-                        contents_len: len,
-                        de_type,
-                    })
+    /// Runs through all of the encrypted sections in processing, and attempts
+    /// to use the given credential to decrypt them. Suitable for situations
+    /// where iterating over credentials is relatively slow compared to
+    /// the cost of iterating over sections-in-memory.
+    fn try_decrypt_with_credential<C: V1DiscoveryCryptoMaterial, P: CryptoProvider>(
+        &mut self,
+        arena: &mut DeserializationArenaAllocator<'adv>,
+        crypto_material: C,
+        match_data: M,
+    ) -> Result<(), ArenaOutOfSpace> {
+        let mut i = 0;
+        while i < self.encrypted_sections.len() {
+            let (section_idx, section): &(usize, ResolvableCiphertextSection) =
+                &self.encrypted_sections[i];
+            // Fast-path: Check for an identity match, ignore if there's no identity match.
+            let identity_resolution_contents = &section.identity_resolution_contents;
+            let identity_resolution_material = match &section.ciphertext_section {
+                CiphertextSection::MicEncrypted(m) => match m.contents.salt {
+                    MultiSalt::Short(_) => crypto_material
+                        .mic_short_salt_identity_resolution_material::<P>()
+                        .into_raw_resolution_material(),
+                    MultiSalt::Extended(_) => crypto_material
+                        .mic_extended_salt_identity_resolution_material::<P>()
+                        .into_raw_resolution_material(),
                 },
-            );
 
-        // multi-byte headers: 0b1LLLLLLL (0b1TTTTTTT)* 0b0TTTTTTT
-        // leading 1 in first byte = multibyte format
-        // leading 1 in subsequent bytes = there is at least 1 more type bytes
-        // leading 0 = this is the last header byte
-        // 127-bit length, effectively infinite type bit length
+                CiphertextSection::SignatureEncrypted(_) => crypto_material
+                    .signed_identity_resolution_material::<P>()
+                    .into_raw_resolution_material(),
+            };
+            match identity_resolution_contents.try_match::<P>(&identity_resolution_material) {
+                None => {
+                    // Try again with another section
+                    i += 1;
+                    continue;
+                }
+                Some(identity_match) => {
+                    // The identity matched, so now we need to more closely scrutinize
+                    // the provided ciphertext. Try to decrypt and parse the section.
+                    let deserialization_result = match &section.ciphertext_section {
+                        CiphertextSection::SignatureEncrypted(c) => c
+                            .try_deserialize(
+                                arena,
+                                identity_match,
+                                &crypto_material.signed_verification_material::<P>(),
+                            )
+                            .map_err(SectionDeserializeError::from),
+                        CiphertextSection::MicEncrypted(c) => c
+                            .try_deserialize(arena, identity_match, &crypto_material)
+                            .map_err(SectionDeserializeError::from),
+                    };
+                    match deserialization_result {
+                        Ok(s) => {
+                            self.deserialized_sections.push((
+                                *section_idx,
+                                V1DeserializedSection::Decrypted(WithMatchedCredential::new(
+                                    match_data.clone(),
+                                    crypto_material.metadata_nonce::<P>(),
+                                    s,
+                                )),
+                            ));
+                        }
+                        Err(e) => match e {
+                            SectionDeserializeError::IncorrectCredential => {
+                                // keep it around to try with another credential
+                                i += 1;
+                                continue;
+                            }
+                            SectionDeserializeError::ParseError => {
+                                // the credential worked, but the section itself was bogus
+                                self.malformed_sections_count += 1;
+                            }
+                            SectionDeserializeError::ArenaOutOfSpace => {
+                                return Err(ArenaOutOfSpace);
+                            }
+                        },
+                    }
+                    // By default, if we have an identity match, assume that decrypting the section worked,
+                    // or that the section was somehow invalid.
+                    // We don't care about maintaining order, so use O(1) remove
+                    let _ = self.encrypted_sections.swap_remove(i);
+                    // don't advance i -- it now points to a new element
+                }
+            }
+        }
+        Ok(())
+    }
 
-        // It's conceivable to have non-canonical extended type sequences where 1 or more leading
-        // bytes don't have any bits set (other than the marker hi bit), thereby contributing nothing
-        // to the final value.
-        // To prevent that, we require that either there be only 1 type byte, or that the first of the
-        // multiple type bytes must have a value bit set. It's OK to have no value bits in subsequent
-        // type bytes.
+    /// Packages the current state of the deserialization process into a
+    /// `V1AdvertisementContents` representing a fully-deserialized V1 advertisement.
+    ///
+    /// This method should only be called after all sections were either successfully
+    /// decrypted or have had all relevant credentials checked against
+    /// them without obtaining a successful identity-match and/or subsequent
+    /// cryptographic verification of the section contents.
+    fn finished_with_decryption_attempts(mut self) -> V1AdvertisementContents<'adv, M> {
+        // Invalid sections = malformed sections + number of encrypted sections
+        // which we could not manage to decrypt with any of our credentials
+        let invalid_sections_count = self.malformed_sections_count + self.encrypted_sections.len();
 
-        let parse_ext_de_header = combinator::map_opt(
-            combinator::consumed(sequence::pair(
-                // length byte w/ leading 1
-                combinator::map_res(
-                    combinator::verify(number::complete::u8::<&[u8], _>, |&b| hi_bit_set(b)),
-                    // snag the lower 7 bits
-                    |b| (b & 0x7F).try_into(),
-                ),
-                branch::alt((
-                    // 1 type byte case
-                    combinator::recognize(
-                        // 0-hi-bit type code byte
-                        combinator::verify(number::complete::u8, |&b| !hi_bit_set(b)),
-                    ),
-                    // multiple type byte case: leading type byte must have at least 1 value bit
-                    combinator::recognize(sequence::tuple((
-                        // hi bit and at least 1 value bit, otherwise it would be non-canonical
-                        combinator::verify(number::complete::u8, |&b| {
-                            hi_bit_set(b) && (b & 0x7F != 0)
-                        }),
-                        // 0-3 1-hi-bit type code bytes with any bit pattern. Max is 3 since two 7
-                        // bit type chunks are processed before and after this, for a total of 5,
-                        // and that's as many 7-bit chunks as are needed to support a 32-bit type.
-                        bytes::complete::take_while_m_n(0, 3, hi_bit_set),
-                        // final 0-hi-bit type code byte
-                        combinator::verify(number::complete::u8, |&b| !hi_bit_set(b)),
-                    ))),
-                )),
-            )),
-            |(header_bytes, (len, type_bytes))| {
-                // snag the low 7 bits of each type byte and accumulate
-
-                type_bytes
-                    .iter()
-                    .try_fold(0_u32, |accum, b| {
-                        accum.checked_shl(7).map(|n| n + ((b & 0x7F) as u32))
-                    })
-                    .and_then(|type_code| {
-                        ArrayView::try_from_slice(header_bytes).map(|header_bytes| DeHeader {
-                            header_bytes,
-                            contents_len: len,
-                            de_type: type_code.into(),
-                        })
-                    })
-            },
-        );
-
-        branch::alt((parse_single_byte_de_header, parse_ext_de_header))(input)
+        // Put the deserialized sections back into the original ordering for
+        // the returned `V1AdvertisementContents`
+        // (Note: idx is unique, so unstable sort is ok)
+        self.deserialized_sections.sort_unstable_by_key(|(idx, _section)| *idx);
+        let ordered_sections = self.deserialized_sections.into_iter().map(|(_idx, s)| s).collect();
+        V1AdvertisementContents::new(ordered_sections, invalid_sections_count)
     }
 }
 
-/// An intermediate stage in parsing a [DataElement] that lacks `offset`.
+/// The contents of a deserialized and decrypted V1 advertisement.
 #[derive(Debug, PartialEq, Eq)]
-struct ProtoDataElement<'d> {
-    header: DeHeader,
-    /// `len()` must equal `header.contents_len`
-    contents: &'d [u8],
+pub struct V1AdvertisementContents<'adv, M: MatchedCredential> {
+    sections: ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT>,
+    invalid_sections: usize,
 }
 
-impl<'d> ProtoDataElement<'d> {
-    fn parse(input: &[u8]) -> nom::IResult<&[u8], ProtoDataElement> {
-        let (remaining, header) = DeHeader::parse(input)?;
-        let len = header.contents_len;
-        combinator::map(bytes::complete::take(len.as_u8()), move |slice| {
-            let header_clone = header.clone();
-            ProtoDataElement { header: header_clone, contents: slice }
-        })(remaining)
+impl<'adv, M: MatchedCredential> V1AdvertisementContents<'adv, M> {
+    fn new(
+        sections: ArrayVecOption<
+            V1DeserializedSection<'adv, M>,
+            NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
+        >,
+        invalid_sections: usize,
+    ) -> Self {
+        Self { sections, invalid_sections }
     }
 
-    fn into_data_element(self, offset: v1_salt::DataElementOffset) -> DataElement<'d> {
-        DataElement { offset, de_type: self.header.de_type, contents: self.contents }
+    /// Destructures this V1 advertisement into just the sections
+    /// which could be successfully deserialized and decrypted
+    pub fn into_sections(
+        self,
+    ) -> ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT> {
+        self.sections
     }
+
+    /// The sections that could be successfully deserialized and decrypted
+    pub fn sections(&self) -> impl ExactSizeIterator<Item = &V1DeserializedSection<M>> {
+        self.sections.iter()
+    }
+
+    /// The number of sections that could not be parsed or decrypted.
+    pub fn invalid_sections_count(&self) -> usize {
+        self.invalid_sections
+    }
+}
+
+/// Advertisement content that was either already plaintext or has been decrypted.
+#[derive(Debug, PartialEq, Eq)]
+pub enum V1DeserializedSection<'adv, M: MatchedCredential> {
+    /// Section that was plaintext in the original advertisement
+    Plaintext(PlaintextSection<'adv>),
+    /// Section that was ciphertext in the original advertisement, and has been decrypted
+    /// with the credential in the [MatchedCredential]
+    Decrypted(WithMatchedCredential<M, DecryptedSection<'adv>>),
 }
 
 /// The level of integrity protection in an encrypted section
@@ -750,125 +446,13 @@
     Signature,
 }
 
-/// The identity used to successfully decrypt and validate an encrypted section
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct EncryptedSectionIdentity {
-    identity_type: EncryptedIdentityDataElementType,
-    validation_mode: VerificationMode,
-    metadata_key: MetadataKey,
-}
-
-impl EncryptedSectionIdentity {
-    /// The type of identity DE used in the section
-    pub fn identity_type(&self) -> EncryptedIdentityDataElementType {
-        self.identity_type
-    }
-    /// The validation mode used when decrypting and verifying the section
-    pub fn verification_mode(&self) -> VerificationMode {
-        self.validation_mode
-    }
-    /// The decrypted metadata key from the section's identity DE
-    pub fn metadata_key(&self) -> MetadataKey {
-        self.metadata_key
-    }
-}
-
-/// A deserialized data element in a section.
-///
-/// The DE has been processed to the point of exposing a DE type and its contents as a `&[u8]`, but
-/// no DE-type-specific processing has been performed.
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub struct DataElement<'adv> {
-    offset: v1_salt::DataElementOffset,
-    de_type: DeType,
-    contents: &'adv [u8],
-}
-
-impl<'adv> DataElement<'adv> {
-    /// The offset of the DE in its containing Section.
-    ///
-    /// Used with the section salt to derive per-DE salt.
-    pub fn offset(&self) -> v1_salt::DataElementOffset {
-        self.offset
-    }
-    /// The type of the DE
-    pub fn de_type(&self) -> DeType {
-        self.de_type
-    }
-    /// The contents of the DE
-    pub fn contents(&self) -> &'adv [u8] {
-        self.contents
-    }
-}
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-pub(crate) struct EncryptionInfo {
-    pub bytes: [u8; 19],
-}
-
-impl EncryptionInfo {
-    // 2-byte header, 1-byte encryption scheme, 16-byte salt
-    pub(crate) const TOTAL_DE_LEN: usize = 19;
-    const CONTENTS_LEN: usize = 17;
-    const ENCRYPTION_INFO_SCHEME_MASK: u8 = 0b01111000;
-
-    fn parse_signature(input: &[u8]) -> nom::IResult<&[u8], EncryptionInfo> {
-        Self::parser_for_scheme(SIGNATURE_ENCRYPTION_SCHEME)(input)
-    }
-
-    fn parse_mic(input: &[u8]) -> nom::IResult<&[u8], EncryptionInfo> {
-        Self::parser_for_scheme(MIC_ENCRYPTION_SCHEME)(input)
-    }
-
-    fn parser_for_scheme(
-        expected_scheme: u8,
-    ) -> impl Fn(&[u8]) -> nom::IResult<&[u8], EncryptionInfo> {
-        move |input| {
-            combinator::map_res(
-                combinator::consumed(combinator::map_parser(
-                    combinator::map(
-                        combinator::verify(ProtoDataElement::parse, |de| {
-                            de.header.de_type == ENCRYPTION_INFO_DE_TYPE
-                                && de.contents.len() == Self::CONTENTS_LEN
-                        }),
-                        |de| de.contents,
-                    ),
-                    sequence::tuple((
-                        combinator::verify(number::complete::be_u8, |scheme: &u8| {
-                            expected_scheme == (Self::ENCRYPTION_INFO_SCHEME_MASK & scheme)
-                        }),
-                        bytes::complete::take(16_usize),
-                    )),
-                )),
-                |(bytes, _contents)| bytes.try_into(),
-            )(input)
-        }
-    }
-
-    fn salt(&self) -> RawV1Salt {
-        RawV1Salt(
-            self.bytes[Self::TOTAL_DE_LEN - 16..]
-                .try_into()
-                .expect("a 16 byte slice will always fit into a 16 byte array"),
-        )
-    }
-}
-
-impl TryFrom<&[u8]> for EncryptionInfo {
-    type Error = TryFromSliceError;
-
-    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
-        value.try_into().map(|fixed_bytes: [u8; 19]| Ok(Self { bytes: fixed_bytes }))?
-    }
-}
-
 #[derive(PartialEq, Eq, Debug)]
 pub(crate) struct SectionMic {
-    mic: [u8; 16],
+    mic: [u8; Self::CONTENTS_LEN],
 }
 
 impl SectionMic {
-    // 16-byte metadata key
+    /// 16-byte MIC
     pub(crate) const CONTENTS_LEN: usize = 16;
 }
 
@@ -887,59 +471,6 @@
     }
 }
 
-#[derive(PartialEq, Eq, Debug)]
-struct PublicIdentity;
-
-impl PublicIdentity {
-    fn parse(input: &[u8]) -> nom::IResult<&[u8], PublicIdentity> {
-        combinator::map(
-            combinator::verify(DeHeader::parse, |deh| {
-                deh.de_type == IdentityDataElementType::Public.type_code()
-                    && deh.contents_len.as_u8() == 0
-            }),
-            |_| PublicIdentity,
-        )(input)
-    }
-}
-
-pub(crate) const IDENTITY_HEADER_LEN: usize = 2;
-
-/// Parsed form of an encrypted identity DE before its contents are decrypted.
-/// Metadata key is stored in the enclosing section.
-#[derive(PartialEq, Eq, Debug)]
-pub(crate) struct EncryptedIdentityMetadata {
-    pub(crate) offset: v1_salt::DataElementOffset,
-    /// The original DE header from the advertisement.
-    /// Encrypted identity should always be a len=2 header.
-    pub(crate) header_bytes: [u8; IDENTITY_HEADER_LEN],
-    pub(crate) identity_type: EncryptedIdentityDataElementType,
-}
-
-impl EncryptedIdentityMetadata {
-    // 2-byte header, 16-byte metadata key
-    pub(crate) const TOTAL_DE_LEN: usize = 18;
-
-    /// Returns a parser function that parses an [`EncryptedIdentityMetadata`] using the provided DE `offset`.
-    fn parser_at_offset(
-        offset: v1_salt::DataElementOffset,
-    ) -> impl Fn(&[u8]) -> nom::IResult<&[u8], EncryptedIdentityMetadata> {
-        move |input| {
-            combinator::map_opt(ProtoDataElement::parse, |de| {
-                EncryptedIdentityDataElementType::from_type_code(de.header.de_type).and_then(
-                    |identity_type| {
-                        de.header.header_bytes.as_slice().try_into().ok().and_then(|header_bytes| {
-                            de.contents.try_into().ok().map(|_metadata_key_ciphertext: [u8; 16]| {
-                                // ensure the ciphertext is the right size, then discard
-                                EncryptedIdentityMetadata { header_bytes, offset, identity_type }
-                            })
-                        })
-                    },
-                )
-            })(input)
-        }
-    }
-}
-
 fn hi_bit_set(b: u8) -> bool {
     b & 0x80 > 0
 }
diff --git a/nearby/presence/np_adv/src/extended/deserialize/parse_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/parse_tests.rs
deleted file mode 100644
index 41eb054..0000000
--- a/nearby/presence/np_adv/src/extended/deserialize/parse_tests.rs
+++ /dev/null
@@ -1,703 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#![allow(clippy::unwrap_used)]
-
-extern crate std;
-use super::*;
-use crate::extended::deserialize::encrypted_section::{
-    MicEncryptedSection, SignatureEncryptedSection,
-};
-use crate::extended::deserialize::test_stubs::IntermediateSectionExt;
-use rand::rngs::StdRng;
-use rand::seq::SliceRandom;
-use rand::{Rng, SeedableRng};
-use std::vec;
-
-#[test]
-fn parse_adv_ext_public_identity() {
-    // 2 sections, 3 DEs each
-    let mut adv_body = vec![];
-    // section
-    adv_body.push(10);
-    // public identity
-    adv_body.push(0x03);
-    // 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 parsed_sections =
-        parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap().into_vec();
-    assert_eq!(
-        vec![IntermediateSection::from(PlaintextSection::new(
-            PlaintextIdentityMode::Public,
-            SectionContents::new(10, &adv_body[2..], 1)
-        ))],
-        parsed_sections,
-    );
-    let expected_des = [
-        // 1 byte header, len 5
-        DataElement {
-            offset: 1_u8.into(),
-            de_type: 5_u8.into(),
-            contents: &[0x01, 0x02, 0x03, 0x04, 0x05],
-        },
-        // 2 byte header, len 1
-        DataElement { offset: 2_u8.into(), de_type: 6_u8.into(), contents: &[0x01] },
-    ];
-
-    assert_eq!(
-        &expected_des[..],
-        &parsed_sections[0].as_plaintext().unwrap().collect_data_elements().unwrap()
-    );
-}
-
-#[test]
-fn parse_adv_ext_identity() {
-    // 3 sections
-    let mut adv_body = vec![];
-    // section - 48 bytes total
-    adv_body.push(19 + 18 + 10);
-    // encryption info -- 2 + 1 + 16x 0x11
-    adv_body.extend_from_slice(&[
-        0x91, 0x10, // header
-        0x08, // scheme (signature)
-        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-        0x11, // salt
-    ]);
-    // private identity -- 2 + 16x 0x33
-    adv_body.extend_from_slice(&[
-        0x90, 0x01, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
-        0x33, 0x33, 0x33,
-    ]);
-    // 10 bytes of 0xFF ciphertext
-    adv_body.extend_from_slice(&[0xFF; 10]);
-
-    // section - 49 bytes total
-    adv_body.push(19 + 18 + 11);
-    // encryption info -- 2 + 1 + 16x 0x11
-    adv_body.extend_from_slice(&[
-        0x91, 0x10, // header
-        0x08, // scheme (signature)
-        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-        0x11, // salt
-    ]);
-    // trusted identity -- 2 + 16x 0x55
-    adv_body.extend_from_slice(&[
-        0x90, 0x02, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
-        0x55, 0x55, 0x55,
-    ]);
-    // 11 bytes of 0xFF ciphertext
-    adv_body.extend_from_slice(&[0xFF; 11]);
-
-    // section - 50 bytes total
-    adv_body.push(19 + 18 + 12);
-    // encryption info -- 2 + 1 + 16x 0x11
-    adv_body.extend_from_slice(&[
-        0x91, 0x10, // header
-        0x08, // scheme (signature)
-        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-        0x11, // salt
-    ]);
-    // provisioned identity -- 2 + 16x 0x77
-    adv_body.extend_from_slice(&[
-        0x90, 0x04, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
-        0x77, 0x77, 0x77,
-    ]);
-    // 12 bytes of 0xFF ciphertext
-    adv_body.extend_from_slice(&[0xFF; 12]);
-
-    let adv_header = V1Header { header_byte: 0x20 };
-    let encryption_info = EncryptionInfo {
-        bytes: [
-            0x91, 0x10, // header bytes
-            0x08, // scheme (signature)
-            0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-            0x11, 0x11,
-        ],
-    };
-    let expected_sections = [
-        SignatureEncryptedSection {
-            contents: EncryptedSectionContents {
-                section_header: 47,
-                adv_header,
-                encryption_info: encryption_info.clone(),
-                identity: EncryptedIdentityMetadata {
-                    header_bytes: [0x90, 0x01],
-                    offset: 1_u8.into(),
-                    identity_type: EncryptedIdentityDataElementType::Private,
-                },
-                // skip section header + encryption info + identity header -> end of section
-                all_ciphertext: &adv_body[1 + 19 + 2..48],
-            },
-        },
-        SignatureEncryptedSection {
-            contents: EncryptedSectionContents {
-                section_header: 48,
-                adv_header,
-                encryption_info: encryption_info.clone(),
-                identity: EncryptedIdentityMetadata {
-                    header_bytes: [0x90, 0x02],
-                    offset: 1_u8.into(),
-                    identity_type: EncryptedIdentityDataElementType::Trusted,
-                },
-                all_ciphertext: &adv_body[48 + 1 + 19 + 2..97],
-            },
-        },
-        SignatureEncryptedSection {
-            contents: EncryptedSectionContents {
-                section_header: 49,
-                adv_header,
-                encryption_info,
-                identity: EncryptedIdentityMetadata {
-                    header_bytes: [0x90, 0x04],
-                    offset: 1_u8.into(),
-                    identity_type: EncryptedIdentityDataElementType::Provisioned,
-                },
-                all_ciphertext: &adv_body[97 + 1 + 19 + 2..],
-            },
-        },
-    ];
-    let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
-    assert_eq!(parsed_sections.into_vec(), expected_sections.map(IntermediateSection::from));
-}
-
-#[test]
-fn parse_adv_ext_mic_identity() {
-    // 3 sections
-    let mut adv_body = vec![];
-    // section - 64 bytes total
-    adv_body.push(19 + 18 + 10 + 16);
-    // encryption info -- 2 + 1 + 16x 0x11
-    adv_body.extend_from_slice(&[
-        0x91, 0x10, // header
-        0x00, // scheme (mic)
-        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-        0x11, // salt
-    ]);
-    // private identity -- 2 + 16x 0x55
-    adv_body.extend_from_slice(&[
-        0x90, 0x01, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
-        0x55, 0x55, 0x55,
-    ]);
-    // 10 bytes of 0xFF ciphertext
-    adv_body.extend_from_slice(&[0xFF; 10]);
-    // mic - 16x 0x33
-    adv_body.extend_from_slice(&[
-        0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
-        0x33,
-    ]);
-
-    // section - 65 bytes total
-    adv_body.push(19 + 18 + 11 + 16);
-    // encryption info -- 2 + 1 + 16x 0x11
-    adv_body.extend_from_slice(&[
-        0x91, 0x10, // header
-        0x00, // scheme (mic)
-        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-        0x11, // salt
-    ]);
-    // trusted identity -- 2 + 16x 0x77
-    adv_body.extend_from_slice(&[
-        0x90, 0x02, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
-        0x77, 0x77, 0x77,
-    ]);
-    // 11 bytes of 0xFF ciphertext
-    adv_body.extend_from_slice(&[0xFF; 11]);
-    // mic - 16x 0x66
-    adv_body.extend_from_slice(&[
-        0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
-        0x66,
-    ]);
-
-    // section - 66 bytes total
-    adv_body.push(19 + 18 + 12 + 16);
-    // encryption info -- 2 + 1 + 16x 0x11
-    adv_body.extend_from_slice(&[
-        0x91, 0x10, // header
-        0x00, // scheme (mic)
-        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-        0x11, // salt
-    ]);
-    // provisioned identity -- 2 + 16x 0xAA
-    adv_body.extend_from_slice(&[
-        0x90, 0x04, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
-        0xAA, 0xAA, 0xAA,
-    ]);
-    // 12 bytes of 0xFF ciphertext
-    adv_body.extend_from_slice(&[0xFF; 12]);
-    // mic - 16x 0x99
-    adv_body.extend_from_slice(&[
-        0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
-        0x99,
-    ]);
-
-    let adv_header = V1Header { header_byte: 0x20 };
-    let encryption_info = EncryptionInfo {
-        bytes: [
-            0x91, 0x10, // header bytes
-            0x00, // scheme (mic)
-            0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-            0x11, 0x11,
-        ],
-    };
-    let expected_sections = [
-        MicEncryptedSection {
-            contents: EncryptedSectionContents {
-                section_header: 63,
-                adv_header,
-                encryption_info: encryption_info.clone(),
-                identity: EncryptedIdentityMetadata {
-                    header_bytes: [0x90, 0x01],
-                    offset: 1_u8.into(),
-                    identity_type: EncryptedIdentityDataElementType::Private,
-                },
-                // skip section header +  encryption info + identity header -> end of ciphertext
-                all_ciphertext: &adv_body[1 + 19 + 2..64 - 16],
-            },
-            mic: SectionMic::from([0x33; 16]),
-        },
-        MicEncryptedSection {
-            contents: EncryptedSectionContents {
-                section_header: 64,
-                adv_header,
-                encryption_info: encryption_info.clone(),
-                identity: EncryptedIdentityMetadata {
-                    header_bytes: [0x90, 0x02],
-                    offset: 1_u8.into(),
-                    identity_type: EncryptedIdentityDataElementType::Trusted,
-                },
-                all_ciphertext: &adv_body[64 + 1 + 19 + 2..129 - 16],
-            },
-            mic: SectionMic::from([0x66; 16]),
-        },
-        MicEncryptedSection {
-            contents: EncryptedSectionContents {
-                section_header: 65,
-                adv_header,
-                encryption_info,
-                identity: EncryptedIdentityMetadata {
-                    header_bytes: [0x90, 0x04],
-                    offset: 1_u8.into(),
-                    identity_type: EncryptedIdentityDataElementType::Provisioned,
-                },
-                all_ciphertext: &adv_body[129 + 1 + 19 + 2..195 - 16],
-            },
-            mic: SectionMic::from([0x99; 16]),
-        },
-    ];
-    let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
-    assert_eq!(parsed_sections.into_vec(), &expected_sections.map(IntermediateSection::from));
-}
-
-#[test]
-fn parse_de_with_1_byte_header() {
-    let data = [0x51, 0x01, 0x02, 0x03, 0x04, 0x05, 0xFF, 0xFF];
-    assert_eq!(
-        Ok((
-            &data[6..],
-            ProtoDataElement {
-                header: DeHeader {
-                    de_type: 1_u8.into(),
-                    header_bytes: ArrayView::try_from_slice(&[0x51]).unwrap(),
-                    contents_len: 5_u8.try_into().unwrap()
-                },
-                contents: &data[1..6]
-            }
-        )),
-        ProtoDataElement::parse(&data)
-    );
-}
-
-#[test]
-fn parse_de_with_2_byte_header() {
-    let data = [0x85, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0xFF, 0xFF];
-    assert_eq!(
-        Ok((
-            &data[7..],
-            ProtoDataElement {
-                header: DeHeader {
-                    de_type: 1_u8.into(),
-                    header_bytes: ArrayView::try_from_slice(&[0x85, 0x01]).unwrap(),
-                    contents_len: 5_u8.try_into().unwrap()
-                },
-                contents: &data[2..7]
-            }
-        )),
-        ProtoDataElement::parse(&data)
-    );
-}
-
-#[test]
-fn parse_de_with_3_byte_header() {
-    let data = [0x85, 0xC1, 0x41, 0x01, 0x02, 0x03, 0x04, 0x05, 0xFF, 0xFF];
-    assert_eq!(
-        Ok((
-            &data[8..],
-            ProtoDataElement {
-                header: DeHeader {
-                    header_bytes: ArrayView::try_from_slice(&[0x85, 0xC1, 0x41]).unwrap(),
-                    contents_len: 5_u8.try_into().unwrap(),
-                    de_type: 0b0000_0000_0000_0000_0010_0000_1100_0001_u32.into(),
-                },
-                contents: &data[3..8]
-            }
-        )),
-        ProtoDataElement::parse(&data)
-    );
-}
-
-#[test]
-fn parse_de_header_1_byte() {
-    let data = [0x51, 0xFF, 0xFF];
-    assert_eq!(
-        Ok((
-            &data[1..],
-            DeHeader {
-                de_type: 1_u8.into(),
-                contents_len: 5_u8.try_into().unwrap(),
-                header_bytes: ArrayView::try_from_slice(&[0x51]).unwrap(),
-            }
-        )),
-        DeHeader::parse(&data)
-    );
-}
-
-#[test]
-fn parse_de_header_2_bytes() {
-    let data = [0x83, 0x01, 0xFF, 0xFF];
-    assert_eq!(
-        Ok((
-            &data[2..],
-            DeHeader {
-                de_type: 1_u8.into(),
-                contents_len: 3_u8.try_into().unwrap(),
-                header_bytes: ArrayView::try_from_slice(&[0x83, 0x01]).unwrap(),
-            }
-        )),
-        DeHeader::parse(&data)
-    );
-}
-
-#[test]
-fn parse_de_header_3_bytes() {
-    let data = [0x83, 0xC1, 0x41, 0xFF, 0xFF];
-    assert_eq!(
-        Ok((
-            &data[3..],
-            DeHeader {
-                de_type: 0b0000_0000_0000_0000_0010_0000_1100_0001_u32.into(),
-                contents_len: 3_u8.try_into().unwrap(),
-                header_bytes: ArrayView::try_from_slice(&[0x83, 0xC1, 0x41]).unwrap(),
-            }
-        )),
-        DeHeader::parse(&data)
-    );
-}
-
-#[test]
-fn parse_de_header_4_bytes() {
-    let data = [0x83, 0xC1, 0xC1, 0x41, 0xFF, 0xFF];
-    assert_eq!(
-        Ok((
-            &data[4..],
-            DeHeader {
-                de_type: 0b0000_0000_0001_0000_0110_0000_1100_0001_u32.into(),
-                contents_len: 3_u8.try_into().unwrap(),
-                header_bytes: ArrayView::try_from_slice(&[0x83, 0xC1, 0xC1, 0x41]).unwrap(),
-            }
-        )),
-        DeHeader::parse(&data)
-    );
-}
-
-#[test]
-fn public_identity_not_first_de_error() {
-    let mut adv_body = vec![];
-    // section
-    adv_body.push(3 + 1);
-    // misc other DE
-    adv_body.extend_from_slice(&[0x81, 0x70, 0xFF]);
-    // public identity after another DE
-    adv_body.push(0x03);
-
-    assert_eq!(
-        nom::Err::Error(error::Error { input: &adv_body[1..], code: error::ErrorKind::Verify }),
-        parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap_err()
-    );
-}
-
-#[test]
-fn invalid_public_section() {
-    let mut rng = StdRng::from_entropy();
-    for _ in 0..100 {
-        let mut adv_body = vec![];
-        // Add section length
-        let remove_section_len = rng.gen_bool(0.5);
-        // Add public identity
-        let add_public_identity = rng.gen_bool(0.5);
-        // Add DEs
-        let add_des = rng.gen_bool(0.5);
-        // Shuffle adv
-        let shuffle = rng.gen_bool(0.5);
-
-        adv_body.push(0);
-        if add_public_identity {
-            adv_body[0] += 1;
-            adv_body.push(0x03);
-        }
-        if add_des {
-            adv_body[0] += 1;
-            adv_body.extend_from_slice(&[0x81]);
-        }
-        if remove_section_len {
-            let _ = adv_body.remove(0);
-        }
-
-        let ordered_adv = adv_body.clone();
-
-        if shuffle {
-            adv_body.shuffle(&mut rng);
-        }
-        // A V1 public section is invalid if
-        // * section length is missing
-        // * the section is empty
-        // * the section identity is missing
-        // * the identity does not follow the section length
-        // * the section length is 0
-        if remove_section_len || !add_public_identity || (ordered_adv != adv_body) {
-            let _ = parse_sections(V1Header { header_byte: 0x20 }, &adv_body)
-                .expect_err("Expected to fail because of missing section length or identity");
-        }
-    }
-}
-
-// There can only be one identity DE
-#[test]
-fn public_identity_after_public_identity_error() {
-    let mut adv_body = vec![];
-    // section
-    adv_body.push(1 + 3 + 1);
-    // public identity after another DE
-    adv_body.push(0x03);
-    // misc other DE
-    adv_body.extend_from_slice(&[0x81, 0x70, 0xFF]);
-    // public identity after another DE
-    adv_body.push(0x03);
-
-    let sections = parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap();
-    assert_eq!(sections.len(), 1);
-    assert_eq!(
-        DataElementParseError::DuplicateIdentityDataElement,
-        sections[0].as_plaintext().unwrap().collect_data_elements().unwrap_err()
-    );
-}
-
-#[test]
-fn salt_public_identity_error() {
-    let mut adv_body = vec![];
-    // section
-    adv_body.push(3 + 1 + 3);
-    // salt - 1 + 2x 0x22 (invalid: must be first DE)
-    adv_body.extend_from_slice(&[0x20, 0x22, 0x22]);
-    // public identity
-    adv_body.push(0x03);
-    // misc other DE
-    adv_body.extend_from_slice(&[0x81, 0x70, 0xFF]);
-
-    assert_eq!(
-        nom::Err::Error(error::Error {
-            input: &adv_body[1..],
-            // Eof because all_consuming is used to ensure complete section is parsed
-            code: error::ErrorKind::Verify
-        }),
-        parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap_err()
-    );
-}
-
-#[test]
-fn salt_mic_public_identity_error() {
-    let mut adv_body = vec![];
-    // section
-    adv_body.push(3 + 18 + 1 + 3);
-    // salt - 1 + 2x 0x22 (invalid: must be first DE)
-    adv_body.extend_from_slice(&[0x20, 0x22, 0x22]);
-    // mic - 2 + 16x 0x33
-    adv_body.extend_from_slice(&[
-        0x90, 0x13, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
-        0x33, 0x33, 0x33,
-    ]);
-    // public identity
-    adv_body.push(0x03);
-    // misc other DE
-    adv_body.extend_from_slice(&[0x81, 0x70, 0xFF]);
-
-    assert_eq!(
-        nom::Err::Error(error::Error {
-            input: &adv_body[1..],
-            // Eof because all_consuming is used to ensure complete section is parsed
-            code: error::ErrorKind::Verify
-        }),
-        parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap_err()
-    );
-}
-
-#[test]
-fn parse_adv_no_identity() {
-    let adv_body = vec![0x55, 0x01, 0x02, 0x03, 0x04, 0x05];
-    assert_eq!(
-        nom::Err::Error(error::Error { input: &adv_body[1..], code: error::ErrorKind::Eof }),
-        parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap_err()
-    );
-}
-
-#[test]
-fn parse_empty_section() {
-    // empty section - should return an EOF error
-    let input = [];
-    assert_eq!(
-        nom::Err::Error(error::Error {
-            // attempted to read section contents
-            input: input.as_slice(),
-            code: error::ErrorKind::Eof
-        }),
-        IntermediateSection::parser_encrypted_with_header(V1Header { header_byte: 0x20 })(&input)
-            .unwrap_err()
-    );
-}
-
-#[test]
-fn parse_de_header_non_canonical_multi_byte() {
-    // length 1, type 1
-    // first byte of type doesn't have any bits in it so it contributes nothing
-    let input = [0b1000_0001, 0b1000_0000, 0b0000_0001];
-    assert_eq!(
-        nom::Err::Error(error::Error {
-            // attempted to read first type byte
-            input: &input.as_slice()[1..],
-            code: error::ErrorKind::Verify
-        }),
-        DeHeader::parse(&input).unwrap_err()
-    );
-}
-
-#[test]
-fn parse_section_length_zero() {
-    // Section length of 0 - should return a verification error
-    let input = [0x00];
-    assert_eq!(
-        nom::Err::Error(error::Error {
-            // attempted to read section contents
-            input: input.as_slice(),
-            code: error::ErrorKind::Verify
-        }),
-        IntermediateSection::parser_encrypted_with_header(V1Header { header_byte: 0x20 })(&input)
-            .unwrap_err()
-    );
-}
-
-#[test]
-fn parse_section_length_overrun() {
-    // section of length 0xF0 - legal but way longer than 3
-    let input = [0xF0, 0x01, 0x02, 0x03];
-    assert_eq!(
-        nom::Err::Error(error::Error {
-            // attempted to read section contents
-            input: &input.as_slice()[1..],
-            code: error::ErrorKind::Eof
-        }),
-        IntermediateSection::parser_encrypted_with_header(V1Header { header_byte: 0x20 })(&input)
-            .unwrap_err()
-    );
-}
-
-#[test]
-fn parse_de_single_byte_header_length_overrun() {
-    // length 7, type 0x03
-    let input = [0b0111_0011, 0x01, 0x02];
-    assert_eq!(
-        nom::Err::Error(error::Error {
-            // attempted to read DE contents
-            input: &input.as_slice()[1..],
-            code: error::ErrorKind::Eof
-        }),
-        ProtoDataElement::parse(&input).unwrap_err()
-    );
-}
-
-#[test]
-fn parse_de_multi_byte_header_length_overrun() {
-    // length 7, type 0x0F
-    let input = [0b1000_0111, 0x0F, 0x01, 0x02];
-    assert_eq!(
-        nom::Err::Error(error::Error {
-            // attempted to read DE contents
-            input: &input.as_slice()[2..],
-            code: error::ErrorKind::Eof
-        }),
-        ProtoDataElement::parse(&input).unwrap_err()
-    );
-}
-
-#[test]
-fn parse_adv_signature_encrypted_plaintext_mix() {
-    // 2 sections
-    let mut adv_body = vec![];
-
-    // section 1 - plaintext - 10 bytes
-    adv_body.push(10);
-    // public identity
-    adv_body.push(0x03);
-    // 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(10);
-    // public identity
-    adv_body.push(0x03);
-    // 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 = V1Header { header_byte: 0x20 };
-
-    assert_eq!(
-        nom::Err::Error(error::Error { input: &adv_body[11..], code: error::ErrorKind::Eof }),
-        parse_sections(adv_header, &adv_body).unwrap_err()
-    );
-}
-
-// for convenient .into() in expected test data
-
-impl<'a> From<SignatureEncryptedSection<'a>> for IntermediateSection<'a> {
-    fn from(s: SignatureEncryptedSection<'a>) -> Self {
-        IntermediateSection::Ciphertext(CiphertextSection::SignatureEncryptedIdentity(s))
-    }
-}
-
-impl<'a> From<MicEncryptedSection<'a>> for IntermediateSection<'a> {
-    fn from(s: MicEncryptedSection<'a>) -> Self {
-        IntermediateSection::Ciphertext(CiphertextSection::MicEncryptedIdentity(s))
-    }
-}
-
-impl<'a> From<PlaintextSection<'a>> for IntermediateSection<'a> {
-    fn from(s: PlaintextSection<'a>) -> Self {
-        IntermediateSection::Plaintext(s)
-    }
-}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/section/header/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/section/header/mod.rs
new file mode 100644
index 0000000..cb6a955
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/deserialize/section/header/mod.rs
@@ -0,0 +1,141 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! High-level, early-stage parsed structures for a section: the header, then everything else.
+
+use crate::extended::{
+    deserialize, salt::ShortV1Salt, 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,
+};
+use crate::helpers::parse_byte_array;
+use nom::{combinator, error, number, sequence};
+use np_hkdf::v1_salt::{ExtendedV1Salt, EXTENDED_SALT_LEN};
+
+#[cfg(test)]
+mod tests;
+
+#[derive(PartialEq, Eq, Debug)]
+pub(crate) enum SectionHeader {
+    Unencrypted,
+    Encrypted(EncryptedSectionHeader),
+}
+
+impl SectionHeader {
+    /// Returns the parsed header and the remaining length of the section contents
+    ///
+    /// This structure makes it easy to get a slice of the entire header with
+    /// [combinator::consumed] for later inclusion in signatures etc, but also
+    /// to [nom::bytes::complete::take] the rest of the section with a minimum of
+    /// error-prone length calculations.
+    pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], (&[u8], Self, u8)> {
+        // Consume first 1-2 bytes of format
+        // 0bERRRSSSS first header byte
+        let (input, (format_bytes, (first_header_byte, maybe_second_byte))) =
+            combinator::consumed(nom::branch::alt((
+                // Extended bit not set, verify all reserved bits are 0
+                combinator::map(
+                    combinator::verify(number::complete::u8, |b| {
+                        !deserialize::hi_bit_set(*b) && (*b & 0x70) == 0
+                    }),
+                    |b| (b, None),
+                ),
+                // Extended bit is set, take another byte and verify all reserved bits are 0
+                combinator::map(
+                    nom::sequence::pair(
+                        combinator::verify(number::complete::u8, |b| {
+                            deserialize::hi_bit_set(*b) && (*b & 0x70) == 0
+                        }),
+                        combinator::verify(number::complete::u8, |second_byte| *second_byte == 0u8),
+                    ),
+                    |(b1, b2)| (b1, Some(b2)),
+                ),
+            )))(input)?;
+
+        let encoding = first_header_byte & 0x0F;
+
+        let (input, section_header) = match (encoding, maybe_second_byte) {
+            (V1_ENCODING_UNENCRYPTED, None) => Ok((input, (SectionHeader::Unencrypted))),
+            (V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN, None) => combinator::map(
+                sequence::tuple((ShortV1Salt::parse, CiphertextExtendedIdentityToken::parse)),
+                |(salt, token)| {
+                    SectionHeader::Encrypted(EncryptedSectionHeader::MicShortSalt { salt, token })
+                },
+            )(input),
+            (V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN, None) => combinator::map(
+                sequence::tuple((parse_v1_extended_salt, CiphertextExtendedIdentityToken::parse)),
+                |(salt, token)| {
+                    SectionHeader::Encrypted(EncryptedSectionHeader::MicExtendedSalt {
+                        salt,
+                        token,
+                    })
+                },
+            )(input),
+            (V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN, None) => {
+                combinator::map(
+                    sequence::tuple((
+                        parse_v1_extended_salt,
+                        CiphertextExtendedIdentityToken::parse,
+                    )),
+                    |(salt, token)| {
+                        SectionHeader::Encrypted(EncryptedSectionHeader::SigExtendedSalt {
+                            salt,
+                            token,
+                        })
+                    },
+                )(input)
+            }
+            _ => Err(nom::Err::Error(error::Error::new(input, error::ErrorKind::Alt))),
+        }?;
+
+        //finally parse the section payload length, this is the same regardless of encoding scheme
+        let (input, section_len) = combinator::verify(number::complete::u8, |b| {
+            // length cannot be 0 for an unencrypted section as this is meaningless
+            !(encoding == V1_ENCODING_UNENCRYPTED && *b == 0u8)
+        })(input)?;
+
+        Ok((input, (format_bytes, section_header, section_len)))
+    }
+}
+
+fn parse_v1_extended_salt(input: &[u8]) -> nom::IResult<&[u8], ExtendedV1Salt> {
+    combinator::map(parse_byte_array::<{ EXTENDED_SALT_LEN }>, ExtendedV1Salt::from)(input)
+}
+
+#[allow(clippy::enum_variant_names)]
+#[derive(PartialEq, Eq, Debug)]
+pub(crate) enum EncryptedSectionHeader {
+    MicShortSalt { salt: ShortV1Salt, token: CiphertextExtendedIdentityToken },
+    MicExtendedSalt { salt: ExtendedV1Salt, token: CiphertextExtendedIdentityToken },
+    SigExtendedSalt { salt: ExtendedV1Salt, token: CiphertextExtendedIdentityToken },
+}
+
+/// 16-byte identity token, straight out of the section.
+///
+/// If identity resolution succeeds, decrypted to an [ExtendedIdentityToken](crate::extended::V1IdentityToken).
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub(crate) struct CiphertextExtendedIdentityToken(pub(crate) [u8; V1_IDENTITY_TOKEN_LEN]);
+
+impl CiphertextExtendedIdentityToken {
+    pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], Self> {
+        combinator::map(parse_byte_array::<V1_IDENTITY_TOKEN_LEN>, Self)(input)
+    }
+}
+
+impl From<[u8; V1_IDENTITY_TOKEN_LEN]> for CiphertextExtendedIdentityToken {
+    fn from(value: [u8; V1_IDENTITY_TOKEN_LEN]) -> Self {
+        Self(value)
+    }
+}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/section/header/tests.rs b/nearby/presence/np_adv/src/extended/deserialize/section/header/tests.rs
new file mode 100644
index 0000000..92cd5da
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/deserialize/section/header/tests.rs
@@ -0,0 +1,194 @@
+// 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.
+
+mod happy_path {
+    use super::super::*;
+    use alloc::vec;
+
+    #[test]
+    fn parse_extended_bit_not_set() {
+        assert_eq!(
+            SectionHeader::parse(&[0b0000_0000, 3]).expect("Parsing should succeed"),
+            ([].as_slice(), (&[0u8][..], SectionHeader::Unencrypted, 3u8))
+        );
+    }
+
+    #[test]
+    fn parse_with_extended_bit_set() {
+        let _ = SectionHeader::parse(&[0b1000_0000, 0b0000_0000, 4])
+            .expect_err("Extended bits should not be parsed at this time");
+    }
+
+    #[test]
+    fn parse_section_header_mic_with_short_salt() {
+        let mut section_header = vec![];
+        section_header.push(0b0000_0001u8); //format
+        section_header.extend_from_slice(&[0xFF; 2]); // short salt
+        section_header.extend_from_slice(&[0xCC; 16]); // identity token
+        section_header.push(100); // payload length
+
+        assert_eq!(
+            SectionHeader::parse(section_header.as_slice()).expect("Parsing should succeed"),
+            (
+                [].as_slice(),
+                (
+                    &[0b0000_0001u8][..],
+                    SectionHeader::Encrypted(EncryptedSectionHeader::MicShortSalt {
+                        salt: [0xFF; 2].into(),
+                        token: [0xCC; 16].into(),
+                    }),
+                    100
+                )
+            )
+        );
+    }
+
+    #[test]
+    fn parse_section_header_multi_byte_mic_with_short_salt() {
+        let mut section_header = vec![];
+        section_header.extend_from_slice(&[0b1000_0001u8, 0b0000_0000]); //format
+        section_header.extend_from_slice(&[0xFF; 2]); // short salt
+        section_header.extend_from_slice(&[0xCC; 16]); // identity token
+        section_header.push(100); // payload length
+        let _ = SectionHeader::parse(section_header.as_slice())
+            .expect_err("Extended bit parsing should fail at this time");
+    }
+
+    #[test]
+    fn parse_section_header_mic_with_extended_salt() {
+        let mut section_header = vec![];
+        section_header.push(0b0000_0010u8); //format
+        section_header.extend_from_slice(&[0xFF; 16]); // short salt
+        section_header.extend_from_slice(&[0xCC; 16]); // identity token
+        section_header.push(100); // payload length
+
+        assert_eq!(
+            SectionHeader::parse(section_header.as_slice()).expect("Parsing should succeed"),
+            (
+                [].as_slice(),
+                (
+                    &[0b0000_0010u8][..],
+                    SectionHeader::Encrypted(EncryptedSectionHeader::MicExtendedSalt {
+                        salt: [0xFF; 16].into(),
+                        token: [0xCC; 16].into(),
+                    }),
+                    100
+                )
+            )
+        );
+    }
+
+    #[test]
+    fn parse_section_header_sig_with_extended_salt() {
+        let mut section_header = vec![];
+        section_header.push(0b0000_0011u8); //format
+        section_header.extend_from_slice(&[0xFF; 16]); // short salt
+        section_header.extend_from_slice(&[0xCC; 16]); // identity token
+        section_header.push(100); // payload length
+
+        assert_eq!(
+            SectionHeader::parse(section_header.as_slice()).expect("Parsing should succeed"),
+            (
+                [].as_slice(),
+                (
+                    &[0b0000_0011u8][..],
+                    SectionHeader::Encrypted(EncryptedSectionHeader::SigExtendedSalt {
+                        salt: [0xFF; 16].into(),
+                        token: [0xCC; 16].into(),
+                    }),
+                    100
+                )
+            )
+        );
+    }
+}
+
+mod error_condition {
+    use super::super::*;
+    use alloc::vec;
+
+    #[test]
+    fn parse_single_byte_invalid_reserve() {
+        let _ = SectionHeader::parse(&[0b0001_0000, 3])
+            .expect_err("Invalid reserve bits should fail to parse");
+        let _ = SectionHeader::parse(&[0b0011_0000, 3])
+            .expect_err("Invalid reserve bits should fail to parse");
+        let _ = SectionHeader::parse(&[0b0111_0000, 3])
+            .expect_err("Invalid reserve bits should fail to parse");
+    }
+
+    #[test]
+    fn parse_multi_byte_invalid_reserve_bits() {
+        let _ = SectionHeader::parse(&[0b1000_0000, 0b1000_0000, 4])
+            .expect_err("Invalid reserve bits should fail to parse");
+        let _ = SectionHeader::parse(&[0b1000_0000, 0b1111_1111, 4])
+            .expect_err("Invalid reserve bits should fail to parse");
+        let _ = SectionHeader::parse(&[0b1000_0000, 0b0000_0001, 4])
+            .expect_err("Invalid reserve bits should fail to parse");
+        let _ = SectionHeader::parse(&[0b1000_0000, 0b0001_0000, 4])
+            .expect_err("Invalid reserve bits should fail to parse");
+        let _ = SectionHeader::parse(&[0b1001_0000, 0b0000_0000, 4])
+            .expect_err("Invalid reserve bits should fail to parse");
+        let _ = SectionHeader::parse(&[0b1100_0000, 0b0000_0000, 4])
+            .expect_err("Invalid reserve bits should fail to parse");
+        let _ = SectionHeader::parse(&[0b1100_0000, 0b0000_0001, 4])
+            .expect_err("Invalid reserve bits should fail to parse");
+    }
+
+    #[test]
+    fn parse_section_header_unencrypted_encoding_zero_length_payload() {
+        let _ = SectionHeader::parse(&[0b0000_0000, 0])
+            .expect_err("0 is an invalid section length for unencrypted sections");
+    }
+
+    #[test]
+    fn parse_section_header_mic_with_short_salt_too_short() {
+        let _ = SectionHeader::parse(&[0b0000_0001])
+            .expect_err("Not enough bytes present to parse a mic with short salt");
+    }
+
+    #[test]
+    fn parse_section_header_mic_with_short_salt_missing_length() {
+        let mut section_header = vec![];
+        section_header.push(0b0000_0001u8); //format
+        section_header.extend_from_slice(&[0xFF; 2]); // short salt
+        section_header.extend_from_slice(&[0xCC; 16]); // identity token
+
+        let _ = SectionHeader::parse(section_header.as_slice())
+            .expect_err("Section payload length byte is missing");
+    }
+}
+
+mod coverage_gaming {
+    use crate::extended::deserialize::section::header::{
+        CiphertextExtendedIdentityToken, EncryptedSectionHeader,
+    };
+    use alloc::format;
+
+    #[test]
+    fn ct_identity_token_clone() {
+        let token = CiphertextExtendedIdentityToken([0; 16]);
+        #[allow(clippy::clone_on_copy)]
+        let _ = token.clone();
+    }
+
+    #[test]
+    fn encrypted_section_header_debug() {
+        let header = EncryptedSectionHeader::MicShortSalt {
+            salt: [0; 2].into(),
+            token: CiphertextExtendedIdentityToken([0; 16]),
+        };
+        let _ = format!("{:?}", header);
+    }
+}
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
new file mode 100644
index 0000000..f998299
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/mod.rs
@@ -0,0 +1,289 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Covers the first half of section parsing before decryption, if relevant, is
+//! attempted.
+
+use crate::{
+    array_vec::ArrayVecOption,
+    extended::{
+        deserialize::{
+            encrypted_section::{
+                EncryptedSectionContents, MicEncryptedSection, SectionIdentityResolutionContents,
+                SignatureEncryptedSection,
+            },
+            section::header::{
+                CiphertextExtendedIdentityToken, EncryptedSectionHeader, SectionHeader,
+            },
+            DataElementParsingIterator, Section, SectionMic,
+        },
+        salt::MultiSalt,
+        NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT, NP_V1_ADV_MAX_PUBLIC_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,
+};
+#[cfg(feature = "devtools")]
+use crate::{deserialization_arena::ArenaOutOfSpace, extended::NP_ADV_MAX_SECTION_LEN};
+#[cfg(feature = "devtools")]
+use array_view::ArrayView;
+
+#[cfg(test)]
+pub(crate) mod tests;
+
+/// Parse into [IntermediateSection]s, exposing the underlying parsing errors.
+/// Consumes all of `adv_body`.
+pub(crate) fn parse_sections(
+    adv_header: V1AdvHeader,
+    adv_body: &[u8],
+) -> Result<
+    ArrayVecOption<IntermediateSection, NP_V1_ADV_MAX_ENCRYPTED_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,
+            IntermediateSection::parser_unencrypted_section,
+            ArrayVecOption::default,
+            |mut acc, item| {
+                acc.push(item);
+                acc
+            },
+        ),
+        // Encrypted advertisement
+        multi::fold_many_m_n(
+            1,
+            NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
+            IntermediateSection::parser_encrypted_with_header(adv_header),
+            ArrayVecOption::default,
+            |mut acc, item| {
+                acc.push(item);
+                acc
+            },
+        ),
+    )))(adv_body)
+    .map(|(_rem, sections)| sections)
+}
+
+/// A partially processed section that hasn't been decrypted (if applicable) yet.
+#[derive(PartialEq, Eq, Debug)]
+pub(crate) enum IntermediateSection<'a> {
+    /// A section that was not encrypted, e.g. a public identity or no-identity section.
+    Plaintext(PlaintextSection<'a>),
+    /// A section whose contents were encrypted, e.g. a private identity section.
+    Ciphertext(CiphertextSection<'a>),
+}
+
+impl<'a> IntermediateSection<'a> {
+    fn parser_unencrypted_section(
+        np_adv_body: &'a [u8],
+    ) -> nom::IResult<&'a [u8], IntermediateSection<'a>> {
+        combinator::map_opt(SectionContents::parse, |sc| match sc.header {
+            SectionHeader::Unencrypted => {
+                Some(IntermediateSection::Plaintext(PlaintextSection::new(sc.contents)))
+            }
+            SectionHeader::Encrypted(_) => None,
+        })(np_adv_body)
+    }
+
+    pub(crate) fn parser_encrypted_with_header(
+        adv_header: V1AdvHeader,
+    ) -> impl Fn(&'a [u8]) -> nom::IResult<&[u8], IntermediateSection> {
+        move |adv_body| {
+            fn split_at_mic(contents: &[u8]) -> Option<(&[u8], SectionMic)> {
+                contents.len().checked_sub(SectionMic::CONTENTS_LEN).map(|len_before_mic| {
+                    let (before_mic, mic) = contents.split_at(len_before_mic);
+                    let mic = SectionMic::try_from(mic).expect("MIC length checked above");
+
+                    (before_mic, mic)
+                })
+            }
+
+            fn build_mic_section<'a>(
+                adv_header: V1AdvHeader,
+                format_bytes: &'a [u8],
+                salt: MultiSalt,
+                token: CiphertextExtendedIdentityToken,
+                contents_len: u8,
+                contents: &'a [u8],
+            ) -> Option<IntermediateSection<'a>> {
+                split_at_mic(contents).map(|(before_mic, mic)| {
+                    IntermediateSection::Ciphertext(CiphertextSection::MicEncrypted(
+                        MicEncryptedSection {
+                            contents: EncryptedSectionContents::new(
+                                adv_header,
+                                format_bytes,
+                                salt,
+                                token,
+                                contents_len,
+                                before_mic,
+                            ),
+                            mic,
+                        },
+                    ))
+                })
+            }
+
+            combinator::map_opt(
+                combinator::map_opt(SectionContents::parse, |sc| match sc.header {
+                    SectionHeader::Unencrypted => None,
+                    SectionHeader::Encrypted(e) => {
+                        Some((sc.format_bytes, sc.contents, sc.contents_len, e))
+                    }
+                }),
+                move |(format_bytes, contents, contents_len, header)| match header {
+                    EncryptedSectionHeader::MicShortSalt { salt, token } => build_mic_section(
+                        adv_header,
+                        format_bytes,
+                        salt.into(),
+                        token,
+                        contents_len,
+                        contents,
+                    ),
+                    EncryptedSectionHeader::MicExtendedSalt { salt, token } => build_mic_section(
+                        adv_header,
+                        format_bytes,
+                        salt.into(),
+                        token,
+                        contents_len,
+                        contents,
+                    ),
+                    EncryptedSectionHeader::SigExtendedSalt { salt, token } => {
+                        Some(IntermediateSection::Ciphertext(
+                            CiphertextSection::SignatureEncrypted(SignatureEncryptedSection {
+                                contents: EncryptedSectionContents::new(
+                                    adv_header,
+                                    format_bytes,
+                                    salt,
+                                    token,
+                                    contents_len,
+                                    contents,
+                                ),
+                            }),
+                        ))
+                    }
+                },
+            )(adv_body)
+        }
+    }
+}
+
+/// Components of a section after header decode, but before decryption or DE parsing.
+///
+/// This is just the first stage of parsing sections, followed by [IntermediateSection].
+#[derive(PartialEq, Eq, Debug)]
+pub(crate) struct SectionContents<'adv> {
+    /// 1-2 bytes of the format saved for later use in verification
+    pub(crate) format_bytes: &'adv [u8],
+    /// Section header contents which includes salt + identity token
+    pub(crate) header: SectionHeader,
+    /// Contents of the section after the header.
+    /// No validation is performed on the contents.
+    pub(crate) contents: &'adv [u8],
+    /// The length of the contents stored as an u8.
+    pub(crate) contents_len: u8,
+}
+
+impl<'adv> SectionContents<'adv> {
+    pub(crate) fn parse(input: &'adv [u8]) -> nom::IResult<&'adv [u8], Self> {
+        let (input, (format_bytes, header, contents_len)) = SectionHeader::parse(input)?;
+        let (input, contents) = bytes::complete::take(contents_len)(input)?;
+
+        Ok((input, Self { format_bytes, header, contents, contents_len }))
+    }
+}
+
+/// A plaintext section deserialized from a V1 advertisement.
+#[derive(PartialEq, Eq, Debug)]
+pub struct PlaintextSection<'adv> {
+    contents: &'adv [u8],
+}
+
+impl<'adv> PlaintextSection<'adv> {
+    pub(crate) fn new(contents: &'adv [u8]) -> Self {
+        Self { contents }
+    }
+}
+
+impl<'adv> Section<'adv, DataElementParseError> for PlaintextSection<'adv> {
+    type Iterator = DataElementParsingIterator<'adv>;
+
+    fn iter_data_elements(&self) -> Self::Iterator {
+        DataElementParsingIterator::new(self.contents)
+    }
+}
+
+#[derive(PartialEq, Eq, Debug)]
+pub(crate) enum CiphertextSection<'a> {
+    SignatureEncrypted(SignatureEncryptedSection<'a>),
+    MicEncrypted(MicEncryptedSection<'a>),
+}
+
+impl<'a> CiphertextSection<'a> {
+    /// Try decrypting into some raw bytes given some raw unsigned crypto-material.
+    #[cfg(feature = "devtools")]
+    pub(crate) fn try_resolve_identity_and_decrypt<
+        C: V1DiscoveryCryptoMaterial,
+        P: CryptoProvider,
+    >(
+        &self,
+        allocator: &mut DeserializationArenaAllocator<'a>,
+        crypto_material: &C,
+    ) -> Option<Result<ArrayView<u8, { NP_ADV_MAX_SECTION_LEN }>, ArenaOutOfSpace>> {
+        match self {
+            CiphertextSection::SignatureEncrypted(x) => {
+                let identity_resolution_material =
+                    crypto_material.signed_identity_resolution_material::<P>();
+                x.try_resolve_identity_and_decrypt::<P>(allocator, &identity_resolution_material)
+            }
+            CiphertextSection::MicEncrypted(x) => match x.contents.salt {
+                MultiSalt::Short(_) => x.try_resolve_short_salt_identity_and_decrypt::<P>(
+                    allocator,
+                    &crypto_material.mic_short_salt_identity_resolution_material::<P>(),
+                ),
+                MultiSalt::Extended(_) => x.try_resolve_extended_salt_identity_and_decrypt::<P>(
+                    allocator,
+                    &crypto_material.mic_extended_salt_identity_resolution_material::<P>(),
+                ),
+            },
+        }
+    }
+
+    /// Return the data needed to resolve identities.
+    ///
+    /// In the typical case of trying many identities across a few sections,
+    /// these should be calculated once for all relevant sections, then re-used
+    /// for all identity match attempts.
+    pub(crate) fn identity_resolution_contents<C: CryptoProvider>(
+        &self,
+    ) -> SectionIdentityResolutionContents {
+        match self {
+            CiphertextSection::SignatureEncrypted(x) => {
+                x.contents.compute_identity_resolution_contents::<C>()
+            }
+            CiphertextSection::MicEncrypted(x) => {
+                x.contents.compute_identity_resolution_contents::<C>()
+            }
+        }
+    }
+}
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
new file mode 100644
index 0000000..84ce763
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/tests.rs
@@ -0,0 +1,725 @@
+// 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)]
+
+//! Tests for intermediate sections representation and logic - this is the initial stage of parsing
+//! before any ciphertext is actually decrypted.
+
+extern crate std;
+use std::{prelude::rust_2021::*, vec};
+
+use super::*;
+use crate::{
+    extended::{
+        deserialize::{
+            data_element::DataElement,
+            encrypted_section::{
+                EncryptedSectionContents, MicEncryptedSection, SignatureEncryptedSection,
+            },
+            SectionMic,
+        },
+        salt::{ShortV1Salt, SHORT_SALT_LEN},
+        serialize::{AdvBuilder, AdvertisementType},
+        NP_V1_ADV_MAX_PUBLIC_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,
+    },
+    header::V1AdvHeader,
+    NpVersionHeader,
+};
+use crypto_provider_default::CryptoProviderImpl;
+use nom::error;
+use np_hkdf::v1_salt::EXTENDED_SALT_LEN;
+use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng as _};
+
+/// Section header length after the length byte for ext salt + token
+const EXT_SALT_SECTION_HEADER_LEN: usize = 1 + EXTENDED_SALT_LEN + V1_IDENTITY_TOKEN_LEN + 1;
+
+/// Section header length after the length byte for ext salt + token
+const SHORT_SALT_SECTION_HEADER_LEN: usize = 1 + SHORT_SALT_LEN + V1_IDENTITY_TOKEN_LEN + 1;
+
+mod happy_path {
+    use super::*;
+    use crate::{
+        extended::{data_elements::TxPowerDataElement, serialize::UnencryptedSectionEncoder},
+        shared_data::TxPower,
+    };
+    use np_hkdf::v1_salt::ExtendedV1Salt;
+
+    #[test]
+    fn parse_adv_ext_public_identity() {
+        let mut adv_body = vec![];
+        // public identity
+        adv_body.push(V1_ENCODING_UNENCRYPTED);
+        // section len
+        adv_body.push(6 + 3);
+        // de 1 byte header, type 5, len 5
+        adv_body.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
+        // de 2 byte header, type 22, len 1
+        adv_body.extend_from_slice(&[0x81, 0x16, 0x01]);
+
+        let parsed_sections = parse_sections(V1AdvHeader::new(0x20), &adv_body).unwrap().into_vec();
+        assert_eq!(
+            vec![IntermediateSection::from(PlaintextSection::new(&adv_body[2..]))],
+            parsed_sections,
+        );
+        let expected_des = [
+            // 1 byte header, len 5
+            DataElement::new(0_u8.into(), 5_u8.into(), &[0x01, 0x02, 0x03, 0x04, 0x05]),
+            // 2 byte header, len 1
+            DataElement::new(1_u8.into(), 22_u8.into(), &[0x01]),
+        ];
+
+        assert_eq!(
+            &expected_des[..],
+            &parsed_sections[0].as_plaintext().unwrap().collect_data_elements().unwrap()
+        );
+    }
+
+    #[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 {
+            let mut section_builder =
+                adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
+            section_builder
+                .add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(7).unwrap()))
+                .unwrap();
+            section_builder.add_to_advertisement::<CryptoProviderImpl>();
+        }
+
+        let adv = adv_builder.into_advertisement();
+        let (remaining, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
+
+        let v1_header = if let NpVersionHeader::V1(h) = header {
+            h
+        } else {
+            panic!("incorrect header");
+        };
+        let sections = parse_sections(v1_header, remaining).unwrap();
+        assert_eq!(NP_V1_ADV_MAX_PUBLIC_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 {
+            let _ = add_mic_short_salt_section_to_adv(&mut adv_body);
+        }
+        let adv_header = V1AdvHeader::new(0x20);
+        assert!(parse_sections(adv_header, &adv_body).is_ok());
+    }
+
+    #[test]
+    fn max_number_encrypted_sections_sig() {
+        let mut adv_body = vec![];
+        for _ in 0..NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT {
+            let _ = add_sig_encrpyted_section(&mut adv_body, 5, &[0x55; EXTENDED_SALT_LEN]);
+        }
+        let adv_header = V1AdvHeader::new(0x20);
+        assert!(parse_sections(adv_header, &adv_body).is_ok());
+    }
+
+    #[test]
+    fn both_mic_and_sig_sections() {
+        let mut adv_body = vec![];
+        let short_salt = ShortV1Salt::from([0x11; SHORT_SALT_LEN]);
+        let token_bytes = [0x55; V1_IDENTITY_TOKEN_LEN];
+        let extended_salt = ExtendedV1Salt::from([0x55; EXTENDED_SALT_LEN]);
+        let total_section_1_len = add_mic_short_salt_section_to_adv(&mut adv_body);
+        let _ = add_sig_encrpyted_section(&mut adv_body, 5, extended_salt.bytes());
+
+        let adv_header = V1AdvHeader::new(0x20);
+        let section1 = &adv_body[0..total_section_1_len];
+        let section2 = &adv_body[total_section_1_len..];
+        let expected_sections = [
+            IntermediateSection::from(MicEncryptedSection {
+                contents: EncryptedSectionContents::new(
+                    adv_header,
+                    &section1[..1],
+                    short_salt.into(),
+                    token_bytes.into(),
+                    (10 + SectionMic::CONTENTS_LEN).try_into().unwrap(),
+                    &[0xFF; 10],
+                ),
+                mic: SectionMic::from([0x33; SectionMic::CONTENTS_LEN]),
+            }),
+            IntermediateSection::from(SignatureEncryptedSection {
+                contents: EncryptedSectionContents::new(
+                    adv_header,
+                    &section2[..1],
+                    extended_salt,
+                    [0x33; V1_IDENTITY_TOKEN_LEN].into(),
+                    5,
+                    &[0xFF; 5],
+                ),
+            }),
+        ];
+        let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
+        assert_eq!(parsed_sections.into_vec(), expected_sections);
+    }
+
+    #[test]
+    fn parse_adv_sig_encrypted_sections() {
+        // 3 sections
+        let mut adv_body = vec![];
+        let salt_bytes = [0x11; EXTENDED_SALT_LEN];
+
+        let section_1_len = add_sig_encrpyted_section(&mut adv_body, 10, &salt_bytes);
+        let section_2_len = add_sig_encrpyted_section(&mut adv_body, 11, &salt_bytes);
+        let _ = add_sig_encrpyted_section(&mut adv_body, 12, &salt_bytes);
+
+        let adv_header = V1AdvHeader::new(0x20);
+        let section1 = &adv_body[..section_1_len];
+        let section2 = &adv_body[section_1_len..][..section_2_len];
+        let section3 = &adv_body[(section_1_len + section_2_len)..];
+        let expected_sections = [
+            SignatureEncryptedSection {
+                contents: EncryptedSectionContents::new(
+                    adv_header,
+                    &section1[..1],
+                    salt_bytes.into(),
+                    [0x33; V1_IDENTITY_TOKEN_LEN].into(),
+                    10,
+                    &[0xFF; 10],
+                ),
+            },
+            SignatureEncryptedSection {
+                contents: EncryptedSectionContents::new(
+                    adv_header,
+                    &section2[..1],
+                    salt_bytes.into(),
+                    [0x33; V1_IDENTITY_TOKEN_LEN].into(),
+                    11,
+                    &[0xFF; 11],
+                ),
+            },
+            SignatureEncryptedSection {
+                contents: EncryptedSectionContents::new(
+                    adv_header,
+                    &section3[..1],
+                    salt_bytes.into(),
+                    [0x33; V1_IDENTITY_TOKEN_LEN].into(),
+                    12,
+                    &[0xFF; 12],
+                ),
+            },
+        ];
+        let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
+        assert_eq!(parsed_sections.into_vec(), expected_sections.map(IntermediateSection::from));
+    }
+
+    #[test]
+    fn parse_adv_ext_salt_mic_sections() {
+        // 3 sections
+        let mut adv_body = vec![];
+        // section
+        adv_body.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+        let salt = ExtendedV1Salt::from([0x11; EXTENDED_SALT_LEN]);
+        adv_body.extend_from_slice(salt.bytes().as_slice());
+        let token_bytes_1 = [0x55; V1_IDENTITY_TOKEN_LEN];
+        adv_body.extend_from_slice(&token_bytes_1);
+        let section_1_len = EXT_SALT_SECTION_HEADER_LEN as u8 + 10 + SectionMic::CONTENTS_LEN as u8;
+        adv_body.push(10 + SectionMic::CONTENTS_LEN as u8);
+        // 10 bytes of 0xFF ciphertext
+        adv_body.extend_from_slice(&[0xFF; 10]);
+        // mic - 16x 0x33
+        adv_body.extend_from_slice(&[0x33; SectionMic::CONTENTS_LEN]);
+
+        // section
+        adv_body.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+        adv_body.extend_from_slice(salt.bytes().as_slice());
+        let token_bytes_2 = [0x77; V1_IDENTITY_TOKEN_LEN];
+        adv_body.extend_from_slice(&token_bytes_2);
+        let section_2_len = EXT_SALT_SECTION_HEADER_LEN as u8 + 11 + SectionMic::CONTENTS_LEN as u8;
+        adv_body.push(11 + SectionMic::CONTENTS_LEN as u8);
+        // 11 bytes of 0xFF ciphertext
+        adv_body.extend_from_slice(&[0xFF; 11]);
+        // mic - 16x 0x66
+        adv_body.extend_from_slice(&[0x66; SectionMic::CONTENTS_LEN]);
+
+        // section
+        adv_body.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+        adv_body.extend_from_slice(salt.bytes().as_slice());
+        let token_bytes_3 = [0xAA; V1_IDENTITY_TOKEN_LEN];
+        adv_body.extend_from_slice(&token_bytes_3);
+        let _ = EXT_SALT_SECTION_HEADER_LEN as u8 + 12 + SectionMic::CONTENTS_LEN as u8;
+        adv_body.push(12 + SectionMic::CONTENTS_LEN as u8);
+        // 12 bytes of 0xFF ciphertext
+        adv_body.extend_from_slice(&[0xFF; 12]);
+        // mic - 16x 0x99
+        adv_body.extend_from_slice(&[0x99; SectionMic::CONTENTS_LEN]);
+
+        let adv_header = V1AdvHeader::new(0x20);
+        let section1 = &adv_body[..section_1_len as usize];
+        let section2 = &adv_body[section_1_len as usize..][..section_2_len as usize];
+        let section3 = &adv_body[section_1_len as usize + section_2_len as usize..];
+        let expected_sections = [
+            MicEncryptedSection {
+                contents: EncryptedSectionContents::new(
+                    adv_header,
+                    &section1[..1],
+                    salt.into(),
+                    token_bytes_1.into(),
+                    (10 + SectionMic::CONTENTS_LEN).try_into().unwrap(),
+                    &[0xFF; 10],
+                ),
+                mic: SectionMic::from([0x33; SectionMic::CONTENTS_LEN]),
+            },
+            MicEncryptedSection {
+                contents: EncryptedSectionContents::new(
+                    adv_header,
+                    &section2[..1],
+                    salt.into(),
+                    token_bytes_2.into(),
+                    (11 + SectionMic::CONTENTS_LEN).try_into().unwrap(),
+                    &[0xFF; 11],
+                ),
+                mic: SectionMic::from([0x66; SectionMic::CONTENTS_LEN]),
+            },
+            MicEncryptedSection {
+                contents: EncryptedSectionContents::new(
+                    adv_header,
+                    &section3[..1],
+                    salt.into(),
+                    token_bytes_3.into(),
+                    (12 + SectionMic::CONTENTS_LEN).try_into().unwrap(),
+                    &[0xFF; 12],
+                ),
+                mic: SectionMic::from([0x99; SectionMic::CONTENTS_LEN]),
+            },
+        ];
+        let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
+        assert_eq!(parsed_sections.into_vec(), &expected_sections.map(IntermediateSection::from));
+    }
+
+    #[test]
+    fn parse_adv_short_salt_mic() {
+        // 3 sections
+        let short_salt = ShortV1Salt::from([0x11; SHORT_SALT_LEN]);
+        let token_bytes = [0x55; V1_IDENTITY_TOKEN_LEN];
+        let mut adv_body = vec![];
+        let section_1_len = add_mic_short_salt_section_to_adv(&mut adv_body);
+        let _ = add_mic_short_salt_section_to_adv(&mut adv_body);
+        let _ = add_mic_short_salt_section_to_adv(&mut adv_body);
+
+        let adv_header = V1AdvHeader::new(0x20);
+        let section1 = &adv_body[..(1 + section_1_len)];
+        let mut expected_sections = [None, None, None];
+        for section in &mut expected_sections {
+            *section = Some(MicEncryptedSection {
+                contents: EncryptedSectionContents::new(
+                    adv_header,
+                    &section1[..1],
+                    short_salt.into(),
+                    token_bytes.into(),
+                    (10 + SectionMic::CONTENTS_LEN).try_into().unwrap(),
+                    &[0xFF; 10],
+                ),
+                mic: SectionMic::from([0x33; SectionMic::CONTENTS_LEN]),
+            })
+        }
+        let expected: Vec<IntermediateSection> =
+            expected_sections.into_iter().map(|x| IntermediateSection::from(x.unwrap())).collect();
+        let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
+        assert_eq!(parsed_sections.into_vec(), expected);
+    }
+}
+
+mod error_condition {
+    use super::*;
+    #[test]
+    fn parse_adv_too_many_unencrypted() {
+        // 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]);
+
+        let adv_header = V1AdvHeader::new(0x20);
+
+        assert_eq!(
+            nom::Err::Error(error::Error { input: &adv_body[11..], code: error::ErrorKind::Eof }),
+            parse_sections(adv_header, &adv_body).unwrap_err()
+        );
+    }
+
+    #[test]
+    fn parse_adv_too_many_encrypted() {
+        // 3 sections
+        let mut adv_body = vec![];
+        for _ in 0..NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT + 1 {
+            let _ = add_mic_short_salt_section_to_adv(&mut adv_body);
+        }
+        let adv_header = V1AdvHeader::new(0x20);
+        let _ = parse_sections(adv_header, &adv_body).expect_err("Expected an error");
+    }
+
+    #[test]
+    fn do_deserialize_zero_section_header() {
+        let mut adv: tinyvec::ArrayVec<[u8; 254]> = tinyvec::ArrayVec::new();
+        adv.push(0x20); // V1 Advertisement
+        adv.push(0x00); // Section header of 0
+        let (remaining, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
+        let v1_header = if let NpVersionHeader::V1(h) = header {
+            h
+        } else {
+            panic!("incorrect header");
+        };
+        let _ = parse_sections(v1_header, remaining).expect_err("Expected an error");
+    }
+
+    #[test]
+    fn invalid_public_section() {
+        let mut rng = StdRng::from_entropy();
+        for _ in 0..100 {
+            let mut adv_body = vec![];
+            // Add section length
+            let remove_section_len = rng.gen_bool(0.5);
+            // Add public identity
+            let add_public_identity = rng.gen_bool(0.5);
+            // Add DEs
+            let add_des = rng.gen_bool(0.5);
+            // Shuffle adv
+            let shuffle = rng.gen_bool(0.5);
+
+            adv_body.push(0);
+            if add_public_identity {
+                adv_body[0] += 1;
+                adv_body.push(0x03);
+            }
+            if add_des {
+                adv_body[0] += 1;
+                adv_body.extend_from_slice(&[0x81]);
+            }
+            if remove_section_len {
+                let _ = adv_body.remove(0);
+            }
+
+            let ordered_adv = adv_body.clone();
+
+            if shuffle {
+                adv_body.shuffle(&mut rng);
+            }
+            // A V1 public section is invalid if
+            // * section length is missing
+            // * the section is empty
+            // * the section identity is missing
+            // * the identity does not follow the section length
+            // * the section length is 0
+            if remove_section_len || !add_public_identity || (ordered_adv != adv_body) {
+                let _ = parse_sections(V1AdvHeader::new(0x20), &adv_body)
+                    .expect_err("Expected to fail because of missing section length or identity");
+            }
+        }
+    }
+
+    #[test]
+    fn parse_empty_section_as_encrypted() {
+        // empty section - should return an EOF error
+        let input = [];
+        assert_eq!(
+            nom::Err::Error(error::Error {
+                // attempted to read section contents
+                input: input.as_slice(),
+                code: error::ErrorKind::Eof,
+            }),
+            IntermediateSection::parser_encrypted_with_header(V1AdvHeader::new(0x20))(&input)
+                .unwrap_err()
+        );
+    }
+
+    #[test]
+    fn parse_unencrypted_as_encrypted() {
+        let mut input = vec![];
+        // public identity
+        input.push(V1_ENCODING_UNENCRYPTED);
+        // section len
+        input.push(6 + 3);
+        // de 1 byte header, type 5, len 5
+        input.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
+        // de 2 byte header, type 22, len 1
+        input.extend_from_slice(&[0x81, 0x16, 0x01]);
+        assert_eq!(
+            nom::Err::Error(error::Error {
+                // attempted to read section contents
+                input: input.as_slice(),
+                code: error::ErrorKind::MapOpt,
+            }),
+            IntermediateSection::parser_encrypted_with_header(V1AdvHeader::new(0x20))(&input)
+                .unwrap_err()
+        );
+    }
+
+    #[test]
+    fn parse_section_length_overrun() {
+        // section of length 0xF0 - legal but way longer than 2
+        let input = [V1_ENCODING_UNENCRYPTED, 0xF0, 0x10, 0x11];
+        assert_eq!(
+            nom::Err::Error(error::Error {
+                // attempted to read section contents after parsing header
+                input: &input.as_slice()[2..],
+                code: error::ErrorKind::Eof,
+            }),
+            SectionContents::parse(&input).unwrap_err()
+        );
+    }
+
+    #[test]
+    fn do_deserialize_empty_section() {
+        let adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+        let adv = adv_builder.into_advertisement();
+        let (remaining, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
+        let v1_header = if let NpVersionHeader::V1(h) = header {
+            h
+        } else {
+            panic!("incorrect header");
+        };
+        let _ = parse_sections(v1_header, remaining).expect_err("Expected an error");
+    }
+
+    #[test]
+    fn parse_adv_sig_encrypted_section_with_short_salt() {
+        // 3 sections
+        let mut adv_body = vec![];
+        let salt_bytes = [0x11; SHORT_SALT_LEN];
+        let section_len = EXT_SALT_SECTION_HEADER_LEN as u8 + 10;
+        adv_body.push(section_len);
+        adv_body.push(V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN);
+        adv_body.extend_from_slice(&salt_bytes);
+        adv_body.extend_from_slice(&[0x33; V1_IDENTITY_TOKEN_LEN]);
+        adv_body.extend_from_slice(&[0x22; 10]);
+        let adv_header = V1AdvHeader::new(0x20);
+        let parsed_sections = parse_sections(adv_header, &adv_body);
+        assert!(parsed_sections.is_err());
+    }
+
+    // specify extended salt in the header but adv actual contains short salt
+    #[test]
+    fn parse_adv_mic_encrypted_wrong_salt_for_extended_header() {
+        // 3 sections
+        let mut adv = vec![];
+        let section_len = SHORT_SALT_SECTION_HEADER_LEN as u8 + 10 + SectionMic::CONTENTS_LEN as u8;
+        adv.push(section_len);
+        adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+        let salt = ShortV1Salt::from([0x11; SHORT_SALT_LEN]);
+        adv.extend_from_slice(salt.bytes().as_slice());
+        let token_bytes_1 = [0x55; V1_IDENTITY_TOKEN_LEN];
+        adv.extend_from_slice(&token_bytes_1);
+        // 10 bytes of 0xFF ciphertext
+        adv.extend_from_slice(&[0xFF; 10]);
+        // mic - 16x 0x33
+        adv.extend_from_slice(&[0x33; SectionMic::CONTENTS_LEN]);
+
+        let adv_header = V1AdvHeader::new(0x20);
+        let parsed_sections = parse_sections(adv_header, &adv);
+        assert!(parsed_sections.is_err());
+    }
+
+    // specify extended salt in the header but adv actual contains short salt
+    #[test]
+    fn encrypted_then_unencrpyted_fails_to_parse() {
+        // 3 sections
+        let mut adv = vec![];
+        let section_len = SHORT_SALT_SECTION_HEADER_LEN as u8 + 10 + SectionMic::CONTENTS_LEN as u8;
+        adv.push(section_len);
+        adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN);
+        let salt = ShortV1Salt::from([0x11; SHORT_SALT_LEN]);
+        adv.extend_from_slice(salt.bytes().as_slice());
+        let token_bytes_1 = [0x55; V1_IDENTITY_TOKEN_LEN];
+        adv.extend_from_slice(&token_bytes_1);
+        // 10 bytes of 0xFF ciphertext
+        adv.extend_from_slice(&[0xFF; 10]);
+        // mic - 16x 0x33
+        adv.extend_from_slice(&[0x33; SectionMic::CONTENTS_LEN]);
+
+        // Now add public section
+        // section len
+        adv.push(1 + 6 + 3);
+        // public identity
+        adv.push(V1_ENCODING_UNENCRYPTED);
+        // de 1 byte header, type 5, len 5
+        adv.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
+        // de 2 byte header, type 6, len 1
+        adv.extend_from_slice(&[0x81, 0x06, 0x01]);
+
+        let adv_header = V1AdvHeader::new(0x20);
+        let parsed_sections = parse_sections(adv_header, &adv);
+        assert!(parsed_sections.is_err());
+    }
+
+    // specify extended salt in the header but adv actual contains short salt
+    #[test]
+    fn unencrypted_then_encrpyted_fails_to_parse() {
+        let mut adv = vec![];
+
+        // Public section
+        // section len
+        adv.push(1 + 6 + 3);
+        // public identity
+        adv.push(V1_ENCODING_UNENCRYPTED);
+        // de 1 byte header, type 5, len 5
+        adv.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
+        // de 2 byte header, type 6, len 1
+        adv.extend_from_slice(&[0x81, 0x06, 0x01]);
+
+        // mic encrypted section
+        let section_len = SHORT_SALT_SECTION_HEADER_LEN as u8 + 10 + SectionMic::CONTENTS_LEN as u8;
+        adv.push(section_len);
+        adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN);
+        let salt = ShortV1Salt::from([0x11; SHORT_SALT_LEN]);
+        adv.extend_from_slice(salt.bytes().as_slice());
+        let token_bytes_1 = [0x55; V1_IDENTITY_TOKEN_LEN];
+        adv.extend_from_slice(&token_bytes_1);
+        // 10 bytes of 0xFF ciphertext
+        adv.extend_from_slice(&[0xFF; 10]);
+        // mic - 16x 0x33
+        adv.extend_from_slice(&[0x33; SectionMic::CONTENTS_LEN]);
+
+        let adv_header = V1AdvHeader::new(0x20);
+        let parsed_sections = parse_sections(adv_header, &adv);
+        assert!(parsed_sections.is_err());
+    }
+}
+
+mod coverage_gaming {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn section_contents() {
+        let sc = SectionContents {
+            format_bytes: &[],
+            header: SectionHeader::Unencrypted,
+            contents: &[],
+            contents_len: 0,
+        };
+        let _ = format!("{:?}", sc);
+        assert_eq!(sc, sc);
+    }
+
+    #[test]
+    fn intermediate_section() {
+        let is = IntermediateSection::Plaintext(PlaintextSection::new(&[]));
+        let _ = format!("{:?}", is);
+    }
+    #[test]
+    fn ciphertext_section() {
+        let ms = MicEncryptedSection {
+            contents: EncryptedSectionContents::new(
+                V1AdvHeader::new(1),
+                &[],
+                ShortV1Salt::from([0x00; 2]).into(),
+                CiphertextExtendedIdentityToken([0x00; 16]),
+                0,
+                &[],
+            ),
+            mic: [0x00; 16].into(),
+        };
+        let cs = CiphertextSection::MicEncrypted(ms);
+        let _ = format!("{:?}", cs);
+    }
+}
+
+pub(crate) trait IntermediateSectionExt<'adv> {
+    /// Returns `Some` if `self` is `Plaintext`
+    fn as_plaintext(&self) -> Option<&PlaintextSection<'adv>>;
+    /// Returns `Some` if `self` is `Ciphertext`
+    fn as_ciphertext(&self) -> Option<&CiphertextSection<'adv>>;
+}
+
+impl<'adv> IntermediateSectionExt<'adv> for IntermediateSection<'adv> {
+    fn as_plaintext(&self) -> Option<&PlaintextSection<'adv>> {
+        match self {
+            IntermediateSection::Plaintext(s) => Some(s),
+            IntermediateSection::Ciphertext(_) => None,
+        }
+    }
+
+    fn as_ciphertext(&self) -> Option<&CiphertextSection<'adv>> {
+        match self {
+            IntermediateSection::Plaintext(_) => None,
+            IntermediateSection::Ciphertext(s) => Some(s),
+        }
+    }
+}
+
+// returns the number of bytes appended to adv
+fn add_mic_short_salt_section_to_adv(adv: &mut Vec<u8>) -> usize {
+    // section
+    adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN);
+    let salt = ShortV1Salt::from([0x11; SHORT_SALT_LEN]);
+    adv.extend_from_slice(salt.bytes().as_slice());
+    let token_bytes_1 = [0x55; V1_IDENTITY_TOKEN_LEN];
+    adv.extend_from_slice(&token_bytes_1);
+    let section_len = 10 + SectionMic::CONTENTS_LEN as u8;
+    adv.push(section_len);
+    // 10 bytes of 0xFF ciphertext
+    adv.extend_from_slice(&[0xFF; 10]);
+    // mic - 16x 0x33
+    adv.extend_from_slice(&[0x33; SectionMic::CONTENTS_LEN]);
+    1 + SHORT_SALT_LEN + V1_IDENTITY_TOKEN_LEN + 1 + 10 + SectionMic::CONTENTS_LEN
+}
+
+// returns the total number of bytes appended to adv
+fn add_sig_encrpyted_section(
+    adv_body: &mut Vec<u8>,
+    len: u8,
+    salt_bytes: &[u8; EXTENDED_SALT_LEN],
+) -> usize {
+    adv_body.push(V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN);
+    adv_body.extend_from_slice(salt_bytes);
+    adv_body.extend_from_slice(&[0x33; V1_IDENTITY_TOKEN_LEN]);
+    adv_body.push(len);
+    // len bytes of 0xFF ciphertext -- in a real adv this would include the
+    // signature, but for the purposes of this parser, it's all just ciphertext
+    for _ in 0..len {
+        adv_body.push(0xFF);
+    }
+    1 + EXTENDED_SALT_LEN + V1_IDENTITY_TOKEN_LEN + 1 + len as usize
+}
+
+// for convenient .into() in expected test data
+impl<'a> From<SignatureEncryptedSection<'a>> for IntermediateSection<'a> {
+    fn from(s: SignatureEncryptedSection<'a>) -> Self {
+        IntermediateSection::Ciphertext(CiphertextSection::SignatureEncrypted(s))
+    }
+}
+
+impl<'a> From<MicEncryptedSection<'a>> for IntermediateSection<'a> {
+    fn from(s: MicEncryptedSection<'a>) -> Self {
+        IntermediateSection::Ciphertext(CiphertextSection::MicEncrypted(s))
+    }
+}
+
+impl<'a> From<PlaintextSection<'a>> for IntermediateSection<'a> {
+    fn from(s: PlaintextSection<'a>) -> Self {
+        IntermediateSection::Plaintext(s)
+    }
+}
diff --git a/nearby/util/pourover_macro_core/src/lib.rs b/nearby/presence/np_adv/src/extended/deserialize/section/mod.rs
similarity index 78%
copy from nearby/util/pourover_macro_core/src/lib.rs
copy to nearby/presence/np_adv/src/extended/deserialize/section/mod.rs
index ae06345..1020e8d 100644
--- a/nearby/util/pourover_macro_core/src/lib.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/section/mod.rs
@@ -1,10 +1,10 @@
-// Copyright 2024 Google LLC
+// Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
 //
-//     http://www.apache.org/licenses/LICENSE-2.0
+//      http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
 // distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,5 +12,5 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-mod jni_method;
-pub use jni_method::jni_method;
+pub(crate) mod header;
+pub(crate) mod intermediate;
diff --git a/nearby/presence/np_adv/src/extended/deserialize/section_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/section_tests.rs
deleted file mode 100644
index 5fa2dbe..0000000
--- a/nearby/presence/np_adv/src/extended/deserialize/section_tests.rs
+++ /dev/null
@@ -1,622 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#![allow(clippy::unwrap_used)]
-
-extern crate std;
-
-use super::*;
-use crate::deserialization_arena;
-use crate::deserialization_arena::DeserializationArena;
-use crate::extended::serialize::AdvertisementType;
-use crate::extended::NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT;
-use crate::{
-    credential::{
-        source::{DiscoveryCredentialSource, SliceCredentialSource},
-        v1::{SignedBroadcastCryptoMaterial, SimpleSignedBroadcastCryptoMaterial, V1},
-        DiscoveryCryptoMaterial, EmptyMatchedCredential, MatchableCredential,
-        MetadataMatchedCredential, SimpleBroadcastCryptoMaterial,
-    },
-    extended::{
-        data_elements::GenericDataElement,
-        deserialize::{test_stubs::IntermediateSectionExt, DataElement},
-        serialize::{
-            self, AdvBuilder, MicEncryptedSectionEncoder, PublicSectionEncoder, SectionBuilder,
-            SignedEncryptedSectionEncoder, WriteDataElement,
-        },
-        MAX_DE_LEN,
-    },
-    parse_adv_header, AdvHeader, WithMatchedCredential,
-};
-use core::borrow::Borrow;
-use core::convert::Into;
-use crypto_provider::{CryptoProvider, CryptoRng};
-use crypto_provider_default::CryptoProviderImpl;
-use rand::{seq::SliceRandom as _, Rng as _, SeedableRng as _};
-use std::prelude::rust_2021::*;
-use std::vec;
-
-type KeyPair = np_ed25519::KeyPair<CryptoProviderImpl>;
-
-#[test]
-fn deserialize_public_identity_section() {
-    do_deserialize_section_unencrypted::<PublicSectionEncoder>(
-        PublicSectionEncoder::default(),
-        PlaintextIdentityMode::Public,
-        1,
-    );
-}
-
-// due to lifetime issues, this is somewhat challenging to share with the 90% identical signature
-// test, but if someone feels like putting in the tinkering to make it happen, please do
-#[test]
-fn deserialize_mic_encrypted_rand_identities_finds_correct_one() {
-    let mut rng = rand::rngs::StdRng::from_entropy();
-    let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
-    for _ in 0..100 {
-        let identities = (0..100).map(|_| (rng.gen(), KeyPair::generate())).collect::<Vec<_>>();
-
-        let chosen_index = rng.gen_range(0..identities.len());
-        let (chosen_key_seed, _chosen_key_pair) = &identities[chosen_index];
-
-        // share a metadata key to emphasize that we're _only_ using the identity to
-        // differentiate
-        let metadata_key: [u8; 16] = rng.gen();
-        let metadata_key = MetadataKey(metadata_key);
-
-        let creds = identities
-            .iter()
-            .map(|(key_seed, key_pair)| {
-                SimpleSignedBroadcastCryptoMaterial::new(
-                    *key_seed,
-                    metadata_key,
-                    key_pair.private_key(),
-                )
-            })
-            .enumerate()
-            .map(|(index, broadcast_cm)| {
-                let match_data = MetadataMatchedCredential::<Vec<u8>>::encrypt_from_plaintext::<
-                    _,
-                    _,
-                    CryptoProviderImpl,
-                >(&broadcast_cm, &[index as u8]);
-
-                let discovery_credential =
-                    broadcast_cm.derive_v1_discovery_credential::<CryptoProviderImpl>();
-
-                MatchableCredential { discovery_credential, match_data }
-            })
-            .collect::<Vec<_>>();
-
-        let cred_source = SliceCredentialSource::new(&creds);
-
-        let identity_type =
-            *EncryptedIdentityDataElementType::iter().collect::<Vec<_>>().choose(&mut rng).unwrap();
-
-        let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
-        let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(*chosen_key_seed, metadata_key);
-
-        let mut section_builder = adv_builder
-            .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
-                &mut crypto_rng,
-                identity_type,
-                &broadcast_cm,
-            ))
-            .unwrap();
-
-        let mut expected_de_data = vec![];
-        let (expected_des, orig_des) =
-            fill_section_random_des(&mut rng, &mut expected_de_data, &mut section_builder, 2);
-
-        section_builder.add_to_advertisement();
-
-        let adv = adv_builder.into_advertisement();
-
-        let (remaining, header) = parse_adv_header(adv.as_slice()).unwrap();
-
-        let v1_header = if let AdvHeader::V1(h) = header {
-            h
-        } else {
-            panic!("incorrect header");
-        };
-
-        let sections = parse_sections(v1_header, remaining).unwrap();
-        assert_eq!(1, sections.len());
-
-        let arena = deserialization_arena!();
-
-        let section = sections[0].as_ciphertext().unwrap();
-        let matched_section =
-            try_deserialize_all_creds::<_, CryptoProviderImpl>(arena, section, &cred_source)
-                .unwrap()
-                .unwrap();
-
-        // Verify that the decrypted metadata contains the chosen index
-        let decrypted_metadata = matched_section.decrypt_metadata::<CryptoProviderImpl>().unwrap();
-        assert_eq!([chosen_index as u8].as_slice(), &decrypted_metadata);
-
-        // Verify that the section contents passed through unaltered
-        let section = matched_section.contents();
-        assert_eq!(section.identity_type(), identity_type);
-        assert_eq!(section.verification_mode(), VerificationMode::Mic);
-        assert_eq!(section.metadata_key(), metadata_key);
-        assert_eq!(
-            section.contents.section_header,
-            (19 + 2 + 16 + total_de_len(&orig_des) + 16) as u8
-        );
-        let data_elements = section.collect_data_elements().unwrap();
-        assert_eq!(data_elements, expected_des);
-        assert_eq!(
-            data_elements
-                .iter()
-                .map(|de| GenericDataElement::try_from(de.de_type(), de.contents()).unwrap())
-                .collect::<Vec<_>>(),
-            orig_des
-        );
-    }
-}
-
-#[test]
-fn deserialize_signature_encrypted_rand_identities_finds_correct_one() {
-    let mut rng = rand::rngs::StdRng::from_entropy();
-    let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
-    for _ in 0..100 {
-        let identities = (0..100).map(|_| (rng.gen(), KeyPair::generate())).collect::<Vec<_>>();
-
-        let chosen_index = rng.gen_range(0..identities.len());
-        let (chosen_key_seed, chosen_key_pair) = &identities[chosen_index];
-
-        // share a metadata key to emphasize that we're _only_ using the identity to
-        // differentiate
-        let metadata_key: [u8; 16] = rng.gen();
-        let metadata_key = MetadataKey(metadata_key);
-
-        let creds = identities
-            .iter()
-            .map(|(key_seed, key_pair)| {
-                SimpleSignedBroadcastCryptoMaterial::new(
-                    *key_seed,
-                    metadata_key,
-                    key_pair.private_key(),
-                )
-            })
-            .enumerate()
-            .map(|(index, broadcast_cm)| {
-                let match_data = MetadataMatchedCredential::<Vec<u8>>::encrypt_from_plaintext::<
-                    _,
-                    _,
-                    CryptoProviderImpl,
-                >(&broadcast_cm, &[index as u8]);
-
-                let discovery_credential =
-                    broadcast_cm.derive_v1_discovery_credential::<CryptoProviderImpl>();
-                MatchableCredential { discovery_credential, match_data }
-            })
-            .collect::<Vec<_>>();
-
-        let cred_source = SliceCredentialSource::new(&creds);
-
-        let identity_type =
-            *EncryptedIdentityDataElementType::iter().collect::<Vec<_>>().choose(&mut rng).unwrap();
-
-        let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
-        let broadcast_cm = SimpleSignedBroadcastCryptoMaterial::new(
-            *chosen_key_seed,
-            metadata_key,
-            chosen_key_pair.private_key(),
-        );
-
-        let mut section_builder = adv_builder
-            .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
-                &mut crypto_rng,
-                identity_type,
-                &broadcast_cm,
-            ))
-            .unwrap();
-
-        let mut expected_de_data = vec![];
-        let (expected_des, orig_des) =
-            fill_section_random_des(&mut rng, &mut expected_de_data, &mut section_builder, 2);
-
-        section_builder.add_to_advertisement();
-
-        let adv = adv_builder.into_advertisement();
-
-        let (remaining, header) = parse_adv_header(adv.as_slice()).unwrap();
-
-        let v1_header = if let AdvHeader::V1(h) = header {
-            h
-        } else {
-            panic!("incorrect header");
-        };
-
-        let arena = deserialization_arena!();
-
-        let sections = parse_sections(v1_header, remaining).unwrap();
-        assert_eq!(1, sections.len());
-
-        let section = sections[0].as_ciphertext().unwrap();
-        let matched_section =
-            try_deserialize_all_creds::<_, CryptoProviderImpl>(arena, section, &cred_source)
-                .unwrap()
-                .unwrap();
-
-        // Verify that the decrypted metadata contains the chosen index
-        let decrypted_metadata = matched_section.decrypt_metadata::<CryptoProviderImpl>().unwrap();
-        assert_eq!([chosen_index as u8].as_slice(), &decrypted_metadata);
-
-        // Verify that the section contents passed through unaltered
-        let section = matched_section.contents();
-        assert_eq!(section.identity_type(), identity_type);
-        assert_eq!(section.verification_mode(), VerificationMode::Signature);
-        assert_eq!(section.metadata_key(), metadata_key);
-        assert_eq!(
-            section.contents.section_header,
-            (19 + 2 + 16 + 64 + total_de_len(&orig_des)) as u8
-        );
-        let data_elements = section.collect_data_elements().unwrap();
-        assert_eq!(data_elements, expected_des);
-        assert_eq!(
-            data_elements
-                .iter()
-                .map(|de| GenericDataElement::try_from(de.de_type(), de.contents()).unwrap())
-                .collect::<Vec<_>>(),
-            orig_des
-        );
-    }
-}
-
-#[test]
-fn deserialize_encrypted_no_matching_identities_finds_nothing() {
-    let mut rng = rand::rngs::StdRng::from_entropy();
-    let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
-    for _ in 0..100 {
-        let signed = rng.gen();
-        let mut identities = (0..100).map(|_| (rng.gen(), KeyPair::generate())).collect::<Vec<_>>();
-
-        let chosen_index = rng.gen_range(0..identities.len());
-        // remove so they won't be found later
-        let (chosen_key_seed, chosen_key_pair) = identities.remove(chosen_index);
-
-        // share a metadata key to emphasize that we're _only_ using the identity to
-        // differentiate
-        let metadata_key: [u8; 16] = rng.gen();
-        let metadata_key = MetadataKey(metadata_key);
-
-        let credentials = identities
-            .iter()
-            .map(|(key_seed, key_pair)| {
-                SimpleSignedBroadcastCryptoMaterial::new(
-                    *key_seed,
-                    metadata_key,
-                    key_pair.private_key(),
-                )
-                .derive_v1_discovery_credential::<CryptoProviderImpl>()
-            })
-            .map(|discovery_credential| MatchableCredential {
-                discovery_credential,
-                match_data: EmptyMatchedCredential,
-            })
-            .collect::<Vec<_>>();
-
-        let cred_source = SliceCredentialSource::new(&credentials);
-
-        let identity_type =
-            *EncryptedIdentityDataElementType::iter().collect::<Vec<_>>().choose(&mut rng).unwrap();
-
-        let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
-        let broadcast_cm = SimpleSignedBroadcastCryptoMaterial::new(
-            chosen_key_seed,
-            metadata_key,
-            chosen_key_pair.private_key(),
-        );
-
-        // awkward split because SectionEncoder isn't object-safe, so we can't just have a
-        // Box<dyn SectionEncoder> and use that in one code path
-        if signed {
-            let identity = SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
-                &mut crypto_rng,
-                identity_type,
-                &broadcast_cm,
-            );
-            let mut section_builder = adv_builder.section_builder(identity).unwrap();
-            let mut expected_de_data = vec![];
-            let _ =
-                fill_section_random_des(&mut rng, &mut expected_de_data, &mut section_builder, 2);
-            section_builder.add_to_advertisement();
-        } else {
-            let identity = MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
-                &mut crypto_rng,
-                identity_type,
-                &broadcast_cm,
-            );
-            let mut section_builder = adv_builder.section_builder(identity).unwrap();
-            let mut expected_de_data = vec![];
-            let _ =
-                fill_section_random_des(&mut rng, &mut expected_de_data, &mut section_builder, 2);
-            section_builder.add_to_advertisement();
-        };
-
-        let adv = adv_builder.into_advertisement();
-        let (remaining, header) = parse_adv_header(adv.as_slice()).unwrap();
-        let v1_header = if let AdvHeader::V1(h) = header {
-            h
-        } else {
-            panic!("incorrect header");
-        };
-
-        let sections = parse_sections(v1_header, remaining).unwrap();
-        assert_eq!(1, sections.len());
-
-        assert!(try_deserialize_all_creds::<_, CryptoProviderImpl>(
-            deserialization_arena!(),
-            sections[0].as_ciphertext().unwrap(),
-            &cred_source,
-        )
-        .unwrap()
-        .is_none());
-    }
-}
-
-#[test]
-fn section_des_expose_correct_data() {
-    // 2 sections, 3 DEs each
-    let mut de_data = vec![];
-    // de 1 byte header, type 5, len 5
-    de_data.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
-    // de 2 byte header, type 16, len 1
-    de_data.extend_from_slice(&[0x81, 0x10, 0x01]);
-
-    let section = SectionContents {
-        section_header: 99,
-        de_region_excl_identity: &de_data,
-        data_element_start_offset: 2,
-    };
-
-    // extract out the parts of the DE we care about
-    let des = section.iter_data_elements().collect::<Result<Vec<_>, _>>().unwrap();
-    assert_eq!(
-        vec![
-            DataElement {
-                offset: 2.into(),
-                de_type: 5_u32.into(),
-                contents: &[0x01, 0x02, 0x03, 0x04, 0x05]
-            },
-            DataElement { offset: 3.into(), de_type: 16_u32.into(), contents: &[0x01] },
-        ],
-        des
-    );
-}
-
-#[test]
-fn do_deserialize_zero_section_header() {
-    let mut adv: tinyvec::ArrayVec<[u8; 254]> = tinyvec::ArrayVec::new();
-    adv.push(0x20); // V1 Advertisement
-    adv.push(0x00); // Section header of 0
-    let (remaining, header) = parse_adv_header(adv.as_slice()).unwrap();
-    let v1_header = if let AdvHeader::V1(h) = header {
-        h
-    } else {
-        panic!("incorrect header");
-    };
-    let _ = parse_sections(v1_header, remaining).expect_err("Expected an error");
-}
-
-#[test]
-fn do_deserialize_empty_section() {
-    let adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-    let adv = adv_builder.into_advertisement();
-    let (remaining, header) = parse_adv_header(adv.as_slice()).unwrap();
-    let v1_header = if let AdvHeader::V1(h) = header {
-        h
-    } else {
-        panic!("incorrect header");
-    };
-    let _ = parse_sections(v1_header, remaining).expect_err("Expected an error");
-}
-
-#[test]
-fn do_deserialize_max_number_of_public_sections() {
-    let adv_builder = build_dummy_advertisement_sections(NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT);
-    let adv = adv_builder.into_advertisement();
-    let (remaining, header) = parse_adv_header(adv.as_slice()).unwrap();
-
-    let v1_header = if let AdvHeader::V1(h) = header {
-        h
-    } else {
-        panic!("incorrect header");
-    };
-    let sections = parse_sections(v1_header, remaining).unwrap();
-    assert_eq!(NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT, sections.len());
-}
-
-#[test]
-fn try_deserialize_over_max_number_of_public_sections() {
-    let adv_builder = build_dummy_advertisement_sections(NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT);
-    let mut adv = adv_builder.into_advertisement().as_slice().to_vec();
-
-    // Push an extra section
-    adv.extend_from_slice(
-        [
-            0x01, // Section header
-            0x03, // Public identity
-        ]
-        .as_slice(),
-    );
-
-    let (remaining, header) = parse_adv_header(&adv).unwrap();
-
-    let v1_header = if let AdvHeader::V1(h) = header {
-        h
-    } else {
-        panic!("incorrect header");
-    };
-    let _ = parse_sections(v1_header, remaining)
-        .expect_err("Expected an error because number of sections is over limit");
-}
-
-pub(crate) fn random_de<R: rand::Rng>(rng: &mut R) -> GenericDataElement {
-    let mut array = [0_u8; MAX_DE_LEN];
-    rng.fill(&mut array[..]);
-    let data: ArrayView<u8, MAX_DE_LEN> =
-        ArrayView::try_from_array(array, rng.gen_range(0..=MAX_DE_LEN)).unwrap();
-    // skip the first few DEs that Google uses
-    GenericDataElement::try_from(rng.gen_range(20_u32..1000).into(), data.as_slice()).unwrap()
-}
-
-fn do_deserialize_section_unencrypted<I: serialize::SectionEncoder>(
-    identity: I,
-    expected_identity: PlaintextIdentityMode,
-    de_offset: usize,
-) {
-    let mut rng = rand::rngs::StdRng::from_entropy();
-
-    let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-    let mut section_builder = adv_builder.section_builder(identity).unwrap();
-
-    let mut expected_de_data = vec![];
-    let (expected_des, orig_des) =
-        fill_section_random_des(&mut rng, &mut expected_de_data, &mut section_builder, de_offset);
-
-    section_builder.add_to_advertisement();
-
-    let adv = adv_builder.into_advertisement();
-
-    let (remaining, header) = parse_adv_header(adv.as_slice()).unwrap();
-
-    let v1_header = if let AdvHeader::V1(h) = header {
-        h
-    } else {
-        panic!("incorrect header");
-    };
-
-    let sections = parse_sections(v1_header, remaining).unwrap();
-    assert_eq!(1, sections.len());
-    let section = sections[0].as_plaintext().unwrap();
-
-    assert_eq!(section.identity(), expected_identity);
-    let data_elements = section.collect_data_elements().unwrap();
-    assert_eq!(data_elements, expected_des);
-    assert_eq!(
-        data_elements
-            .iter()
-            .map(|de| GenericDataElement::try_from(de.de_type(), de.contents()).unwrap())
-            .collect::<Vec<_>>(),
-        orig_des
-    );
-}
-
-fn fill_section_random_des<'adv, R: rand::Rng, I: serialize::SectionEncoder>(
-    mut rng: &mut R,
-    sink: &'adv mut Vec<u8>,
-    section_builder: &mut SectionBuilder<&mut AdvBuilder, I>,
-    de_offset: usize,
-) -> (Vec<DataElement<'adv>>, Vec<GenericDataElement>) {
-    let mut expected_des = vec![];
-    let mut orig_des = vec![];
-    let mut de_ranges = vec![];
-
-    for _ in 0..rng.gen_range(1..10) {
-        let de = random_de(&mut rng);
-
-        let de_clone = de.clone();
-        if section_builder.add_de(|_| de_clone).is_err() {
-            break;
-        }
-
-        let orig_len = sink.len();
-        de.write_de_contents(sink).unwrap();
-        let contents_len = sink.len() - orig_len;
-        de_ranges.push(orig_len..orig_len + contents_len);
-        orig_des.push(de);
-    }
-
-    for (index, (de, range)) in orig_des.iter().zip(de_ranges).enumerate() {
-        expected_des.push(DataElement {
-            offset: u8::try_from(index + de_offset).unwrap().into(),
-            de_type: de.de_header().de_type,
-            contents: &sink[range],
-        });
-    }
-    (expected_des, orig_des)
-}
-
-fn total_de_len(des: &[GenericDataElement]) -> usize {
-    des.iter()
-        .map(|de| {
-            let mut buf = vec![];
-            let _ = de.write_de_contents(&mut buf);
-            de.de_header().serialize().len() + buf.len()
-        })
-        .sum()
-}
-
-type TryDeserOutput<'adv, M> = Option<WithMatchedCredential<M, DecryptedSection<'adv>>>;
-
-/// Returns:
-/// - `Ok(Some)` if a matching credential was found
-/// - `Ok(None)` if no matching credential was found, or if `cred_source` provides no credentials
-/// - `Err` if an error occurred.
-fn try_deserialize_all_creds<'a, S, P>(
-    arena: DeserializationArena<'a>,
-    section: &'a CiphertextSection,
-    cred_source: &'a S,
-) -> Result<TryDeserOutput<'a, S::Matched>, BatchSectionDeserializeError>
-where
-    S: DiscoveryCredentialSource<'a, V1>,
-    P: CryptoProvider,
-{
-    let mut allocator = arena.into_allocator();
-    for (crypto_material, match_data) in cred_source.iter() {
-        match section
-            .try_resolve_identity_and_deserialize::<_, P>(&mut allocator, crypto_material.borrow())
-        {
-            Ok(s) => {
-                let metadata_nonce = crypto_material.metadata_nonce::<P>();
-                return Ok(Some(WithMatchedCredential::new(match_data, metadata_nonce, s)));
-            }
-            Err(e) => match e {
-                SectionDeserializeError::IncorrectCredential => continue,
-                SectionDeserializeError::ParseError => {
-                    return Err(BatchSectionDeserializeError::ParseError)
-                }
-                SectionDeserializeError::ArenaOutOfSpace => {
-                    return Err(BatchSectionDeserializeError::ArenaOutOfSpace)
-                }
-            },
-        }
-    }
-
-    Ok(None)
-}
-
-fn build_dummy_advertisement_sections(number_of_sections: usize) -> AdvBuilder {
-    let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-    for _ in 0..number_of_sections {
-        let section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
-        section_builder.add_to_advertisement();
-    }
-    adv_builder
-}
-
-#[derive(Debug, PartialEq, Eq)]
-enum BatchSectionDeserializeError {
-    /// Advertisement data is malformed
-    ParseError,
-    /// The given arena is not large enough to hold the decrypted data
-    ArenaOutOfSpace,
-}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/test_stubs.rs b/nearby/presence/np_adv/src/extended/deserialize/test_stubs.rs
deleted file mode 100644
index 68b6f5d..0000000
--- a/nearby/presence/np_adv/src/extended/deserialize/test_stubs.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-extern crate std;
-
-use std::prelude::rust_2021::*;
-
-use crate::{
-    extended::deserialize::{CiphertextSection, PlaintextSection},
-    IntermediateSection,
-};
-
-pub(crate) trait IntermediateSectionExt<'adv> {
-    /// Returns `Some` if `self` is `Plaintext`
-    fn as_plaintext(&self) -> Option<&PlaintextSection<'adv>>;
-    /// Returns `Some` if `self` is `Ciphertext`
-    fn as_ciphertext(&self) -> Option<&CiphertextSection<'adv>>;
-}
-
-impl<'adv> IntermediateSectionExt<'adv> for IntermediateSection<'adv> {
-    fn as_plaintext(&self) -> Option<&PlaintextSection<'adv>> {
-        match self {
-            IntermediateSection::Plaintext(s) => Some(s),
-            IntermediateSection::Ciphertext(_) => None,
-        }
-    }
-
-    fn as_ciphertext(&self) -> Option<&CiphertextSection<'adv>> {
-        match self {
-            IntermediateSection::Plaintext(_) => None,
-            IntermediateSection::Ciphertext(s) => Some(s),
-        }
-    }
-}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/tests.rs b/nearby/presence/np_adv/src/extended/deserialize/tests.rs
new file mode 100644
index 0000000..3164c2b
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/deserialize/tests.rs
@@ -0,0 +1,145 @@
+// 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.
+
+//! Unit tests for top level credential iteration and adv deserialization
+
+#![allow(clippy::unwrap_used)]
+
+use crate::{
+    credential::book::CredentialBook,
+    deserialization_arena,
+    deserialization_arena::ArenaOutOfSpace,
+    extended::serialize::{AddSectionError, AdvBuilder, AdvertisementType},
+    header::NpVersionHeader,
+    tests::deser_v1_tests::{
+        add_mic_rand_salt_to_adv, add_sig_rand_salt_to_adv, SectionConfig, TestIdentity,
+    },
+};
+use crypto_provider_default::CryptoProviderImpl;
+use rand::{prelude::StdRng, seq::IteratorRandom, SeedableRng};
+
+#[test]
+fn v1_arena_out_of_space_error_sig() {
+    v1_arena_out_of_space_error_encrypted_adv(
+        // Need to use many DE's to be sure we go over the arena limit
+        add_sig_rand_salt_to_adv::<StdRng, CryptoProviderImpl, 5>,
+    )
+}
+
+#[test]
+fn v1_arena_out_of_space_error_mic() {
+    v1_arena_out_of_space_error_encrypted_adv(
+        // Need to use many DE's to be sure we go over the arena limit
+        add_mic_rand_salt_to_adv::<StdRng, CryptoProviderImpl, 5>,
+    )
+}
+
+fn v1_arena_out_of_space_error_encrypted_adv(
+    add_to_adv: impl for<'a> Fn(
+        &mut StdRng,
+        &'a TestIdentity,
+        &mut AdvBuilder,
+    ) -> Result<SectionConfig<'a>, AddSectionError>,
+) {
+    let mut rng = StdRng::from_entropy();
+    let mut builder = AdvBuilder::new(AdvertisementType::Encrypted);
+    let identities =
+        crate::tests::deser_v1_tests::TestIdentities::generate::<1, _, CryptoProviderImpl>(
+            &mut rng,
+        );
+    let _ = add_to_adv(&mut rng, &identities.0[0], &mut builder);
+    let adv = builder.into_advertisement().as_slice().to_vec();
+    let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
+
+    let (remaining, header) = NpVersionHeader::parse(&adv).unwrap();
+
+    let h =
+        if let NpVersionHeader::V1(v1_header) = header { Some(v1_header) } else { None }.unwrap();
+
+    let mut sections_in_processing =
+        crate::extended::deserialize::SectionsInProcessing::<'_, _>::from_advertisement_contents::<
+            CryptoProviderImpl,
+        >(h, remaining)
+        .unwrap();
+
+    // fill up allocator so we will run out of space
+    let arena = deserialization_arena!();
+    let mut allocator = arena.into_allocator();
+    let _ = allocator.allocate(250).unwrap();
+
+    let (crypto_material, match_data) = cred_book.v1_iter().choose(&mut rng).unwrap();
+
+    let res = sections_in_processing.try_decrypt_with_credential::<_, CryptoProviderImpl>(
+        &mut allocator,
+        crypto_material,
+        match_data,
+    );
+    assert_eq!(Err(ArenaOutOfSpace), res);
+}
+
+mod coverage_gaming {
+    use crate::{
+        array_vec::ArrayVecOption,
+        credential::matched::EmptyMatchedCredential,
+        extended::{
+            deserialize::{
+                section::intermediate::PlaintextSection, DecryptedSection, SectionDeserializeError,
+                V1AdvertisementContents, V1DeserializedSection, VerificationMode,
+            },
+            salt::MultiSalt,
+        },
+    };
+    use alloc::format;
+
+    #[test]
+    fn decrypted_section_derives() {
+        let d = DecryptedSection::new(
+            VerificationMode::Mic,
+            MultiSalt::Extended([0u8; 16].into()),
+            [0u8; 16].into(),
+            &[],
+        );
+        let _ = format!("{:?}", d);
+    }
+
+    #[test]
+    fn section_deserialize_error_derives() {
+        let e = SectionDeserializeError::IncorrectCredential;
+        assert_eq!(e, e);
+        let _ = format!("{:?}", e);
+    }
+
+    #[test]
+    fn adv_contents_derives() {
+        let c: V1AdvertisementContents<'_, EmptyMatchedCredential> =
+            V1AdvertisementContents::new(ArrayVecOption::default(), 0);
+        let _ = format!("{:?}", c);
+        assert_eq!(c, c);
+    }
+
+    #[test]
+    fn deserialized_section_derives() {
+        let d: V1DeserializedSection<'_, EmptyMatchedCredential> =
+            V1DeserializedSection::Plaintext(PlaintextSection::new(&[]));
+        assert_eq!(d, d);
+        let _ = format!("{:?}", d);
+    }
+
+    #[test]
+    fn verification_mode_derives() {
+        let m = VerificationMode::Signature;
+        #[allow(clippy::clone_on_copy)]
+        let _ = format!("{:?}", m.clone());
+    }
+}
diff --git a/nearby/presence/np_adv/src/extended/mod.rs b/nearby/presence/np_adv/src/extended/mod.rs
index 68b16c3..4a13ea5 100644
--- a/nearby/presence/np_adv/src/extended/mod.rs
+++ b/nearby/presence/np_adv/src/extended/mod.rs
@@ -13,18 +13,20 @@
 // limitations under the License.
 
 //! V1 advertisement support.
-use crate::extended::de_type::DeType;
 use crate::DeLengthOutOfRange;
 use array_view::ArrayView;
+use crypto_provider::CryptoRng;
 
 pub mod data_elements;
 pub mod de_type;
 pub mod deserialize;
+pub mod salt;
 pub mod section_signature_payload;
 pub mod serialize;
 
+// TODO make this easy to use w/ configurable arena size
 /// Maximum size of an NP advertisement, including the adv header
-pub const BLE_ADV_SVC_CONTENT_LEN: usize = 254
+pub const BLE_5_ADV_SVC_MAX_CONTENT_LEN: usize = 254
     // length and type bytes for svc data TLV
     - 1 - 1
     // NP UUID
@@ -36,16 +38,81 @@
 /// Maximum number of public sections in an advertisement
 pub const NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT: usize = 1;
 
-/// Maximum size of a NP section, including its header byte
-pub const NP_ADV_MAX_SECTION_LEN: usize = BLE_ADV_SVC_CONTENT_LEN
-    // adv header byte
-    - 1;
+/// 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;
+
+// TODO should this be 255 (or 256, if we +1 the length)?
+/// Maximum hypothetical size of a NP section's contents, excluding its header
+/// byte. This is longer than can fit in a BLE 5 extended adv, but other media
+/// could fit it, like mDNS.
+const NP_ADV_MAX_SECTION_CONTENTS_LEN: usize = 255;
+
+/// Size of a V1 identity token
+pub const V1_IDENTITY_TOKEN_LEN: usize = 16;
+
+// 4-bit encoding ids
+/// Encoding ID for unencrypted sections with no salt
+pub const V1_ENCODING_UNENCRYPTED: u8 = 0x00;
+/// Encoding ID for encrypted sections with a MIC and a short salt
+pub const V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN: u8 = 0x01;
+/// Encoding ID for encrypted sections with a MIC and an extended salt
+pub const V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN: u8 = 0x02;
+/// Encoding ID for encrypted sections with a signature and an extended salt
+pub const V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN: u8 = 0x03;
+
+// The maximum de length that fits into a non-extended de header
+const MAX_NON_EXTENDED_LEN: u8 = 7;
+// The maximum type code that fits into a non-extended de header
+const MAX_NON_EXTENDED_TYPE_CODE: u32 = 15;
+
+fn de_requires_extended_bit(type_code: u32, de_len: u8) -> bool {
+    de_len > MAX_NON_EXTENDED_LEN || type_code > MAX_NON_EXTENDED_TYPE_CODE
+}
+
+/// 16-byte plaintext identity token.
+///
+/// Identity tokens are present in encrypted form in a section's header.
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
+pub struct V1IdentityToken([u8; V1_IDENTITY_TOKEN_LEN]);
+
+impl From<[u8; V1_IDENTITY_TOKEN_LEN]> for V1IdentityToken {
+    fn from(value: [u8; V1_IDENTITY_TOKEN_LEN]) -> Self {
+        Self(value)
+    }
+}
+
+impl V1IdentityToken {
+    /// Returns a reference to the inner byte array
+    pub fn bytes(&self) -> &[u8; V1_IDENTITY_TOKEN_LEN] {
+        &self.0
+    }
+
+    /// Returns the inner byte array
+    pub const fn into_bytes(self) -> [u8; V1_IDENTITY_TOKEN_LEN] {
+        self.0
+    }
+
+    /// Returns the token bytes as a slice
+    pub fn as_slice(&self) -> &[u8] {
+        &self.0
+    }
+}
+
+impl AsRef<[u8]> for V1IdentityToken {
+    fn as_ref(&self) -> &[u8] {
+        &self.0
+    }
+}
+
+impl crypto_provider::FromCryptoRng for V1IdentityToken {
+    fn new_random<R: CryptoRng>(rng: &mut R) -> Self {
+        Self(rng.gen())
+    }
+}
 
 /// Max V1 DE length (7 bit length field).
 pub(crate) const MAX_DE_LEN: usize = 127;
 
-const METADATA_KEY_LEN: usize = 16;
-
 /// Length of a DE's content -- must be in `[0, 127]`
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
 pub struct DeLength {
@@ -82,7 +149,7 @@
 }
 
 /// Convert a tinyvec into an equivalent ArrayView
-fn to_array_view<T, const N: usize>(vec: tinyvec::ArrayVec<[T; N]>) -> ArrayView<T, N>
+pub(crate) fn to_array_view<T, const N: usize>(vec: tinyvec::ArrayVec<[T; N]>) -> ArrayView<T, N>
 where
     [T; N]: tinyvec::Array,
 {
@@ -90,4 +157,15 @@
     ArrayView::try_from_array(vec.into_inner(), len).expect("len is from original vec")
 }
 
-pub(crate) const ENCRYPTION_INFO_DE_TYPE: DeType = DeType::const_from(0x10);
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use rand::{distributions, Rng};
+
+    // support randomly generating tokens just for tests
+    impl distributions::Distribution<V1IdentityToken> for distributions::Standard {
+        fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> V1IdentityToken {
+            V1IdentityToken::from(rng.gen::<[u8; V1_IDENTITY_TOKEN_LEN]>())
+        }
+    }
+}
diff --git a/nearby/presence/np_adv/src/extended/salt.rs b/nearby/presence/np_adv/src/extended/salt.rs
new file mode 100644
index 0000000..b35275f
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/salt.rs
@@ -0,0 +1,113 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Various representations of salts for extended advertisements.
+
+use nom::combinator;
+
+use crypto_provider::{aes::ctr::AesCtrNonce, CryptoProvider, CryptoRng, FromCryptoRng};
+use np_hkdf::v1_salt::ExtendedV1Salt;
+
+use crate::helpers::parse_byte_array;
+
+/// Common behavior for V1 section salts.
+pub trait V1Salt: Copy + Into<MultiSalt> {
+    /// Derive the nonce used for section encryption.
+    ///
+    /// Both kinds of salts can compute the nonce needed for de/encrypting a
+    /// section, but only extended salts can have data derived from them.
+    fn compute_nonce<C: CryptoProvider>(&self) -> AesCtrNonce;
+}
+
+impl V1Salt for ExtendedV1Salt {
+    fn compute_nonce<C: CryptoProvider>(&self) -> AesCtrNonce {
+        self.derive::<12, C>(None).expect("AES-CTR nonce is a valid HKDF size")
+    }
+}
+
+pub(crate) const SHORT_SALT_LEN: usize = 2;
+
+/// A byte buffer the size of a V1 short salt
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct ShortV1Salt([u8; SHORT_SALT_LEN]);
+
+impl From<[u8; SHORT_SALT_LEN]> for ShortV1Salt {
+    fn from(value: [u8; SHORT_SALT_LEN]) -> Self {
+        Self(value)
+    }
+}
+
+impl ShortV1Salt {
+    pub(crate) fn bytes(&self) -> &[u8; SHORT_SALT_LEN] {
+        &self.0
+    }
+
+    pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], Self> {
+        combinator::map(parse_byte_array::<SHORT_SALT_LEN>, Self)(input)
+    }
+}
+
+impl V1Salt for ShortV1Salt {
+    fn compute_nonce<C: CryptoProvider>(&self) -> AesCtrNonce {
+        np_hkdf::extended_mic_section_short_salt_nonce::<C>(self.0)
+    }
+}
+
+impl FromCryptoRng for ShortV1Salt {
+    fn new_random<R: CryptoRng>(rng: &mut R) -> Self {
+        rng.gen::<[u8; SHORT_SALT_LEN]>().into()
+    }
+}
+
+/// Either a short or extended salt.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum MultiSalt {
+    /// A 2-byte salt
+    Short(ShortV1Salt),
+    /// A 16-byte salt
+    Extended(ExtendedV1Salt),
+}
+
+impl MultiSalt {
+    /// Salt bytes as a slice, for when variable-size access is sensible
+    pub fn as_slice(&self) -> &[u8] {
+        match self {
+            MultiSalt::Short(s) => s.bytes().as_slice(),
+            MultiSalt::Extended(s) => s.bytes().as_slice(),
+        }
+    }
+}
+
+impl From<ExtendedV1Salt> for MultiSalt {
+    fn from(value: ExtendedV1Salt) -> Self {
+        Self::Extended(value)
+    }
+}
+
+impl From<ShortV1Salt> for MultiSalt {
+    fn from(value: ShortV1Salt) -> Self {
+        Self::Short(value)
+    }
+}
+
+impl V1Salt for MultiSalt {
+    /// Both kinds of salts can compute the nonce needed for decrypting an
+    /// advertisement, but only extended salts can have data derived from them.
+    fn compute_nonce<C: CryptoProvider>(&self) -> AesCtrNonce {
+        match self {
+            Self::Short(s) => V1Salt::compute_nonce::<C>(s),
+            Self::Extended(s) => s.compute_nonce::<C>(),
+        }
+    }
+}
diff --git a/nearby/presence/np_adv/src/extended/section_signature_payload.rs b/nearby/presence/np_adv/src/extended/section_signature_payload.rs
index d8ec93c..3cdaead 100644
--- a/nearby/presence/np_adv/src/extended/section_signature_payload.rs
+++ b/nearby/presence/np_adv/src/extended/section_signature_payload.rs
@@ -16,40 +16,31 @@
 //! after the included context bytes, and utilities for
 //! performing signatures and signature verification.
 
-use crate::extended::deserialize::EncryptionInfo;
-use crate::MetadataKey;
-use crate::NP_SVC_UUID;
-use crypto_provider::{aes::ctr::AesCtrNonce, CryptoProvider};
+use crate::header::VERSION_HEADER_V1;
+use crypto_provider::{
+    aes::ctr::AesCtrNonce,
+    ed25519::{Ed25519Provider, PrivateKey, PublicKey, Signature},
+};
 use sink::{Sink, SinkWriter};
 
+use crate::NP_SVC_UUID;
+
 /// A struct representing the necessary contents
 /// of an v1 advertisement section np_ed25519 signature payload which
 /// come after the context prefix (shared among all advs).
 pub(crate) struct SectionSignaturePayload<'a> {
-    /// Advertisement header byte
-    adv_header_byte: u8,
-    /// Header byte for the v1 section being signed
-    section_header: u8,
-    /// Reference to the complete contents of the [`EncryptionInfo`] DE.
-    encryption_info: &'a [u8; EncryptionInfo::TOTAL_DE_LEN],
-    /// Reference to the derived salt (IV) for the section
-    nonce_ref: &'a AesCtrNonce,
-    /// Reference to all remaining information after the derived salt, but
-    /// not including the signature itself [which gets tacked onto the end].
-    after_iv_info: AfterIVInfo<'a>,
-}
-
-/// Representation of the plaintext information in an advertisement
-/// signature payload which comes after the derived salt
-enum AfterIVInfo<'a> {
-    /// Reference to a raw byte array containing all information
-    /// to be included in the signature payload after the derived salt,
-    /// and before the signature itself.
-    Raw(&'a [u8]),
-    /// Plaintext identity DE header followed by the metadata key,
-    /// then the rest of the section plaintext (including
-    /// the plaintext identity DE payload).
-    IdentityHeaderMetadataKeyAndRemainder([u8; 2], MetadataKey, &'a [u8]),
+    /// first 1-2 bytes of format
+    format_bytes: &'a [u8],
+    /// Salt bytes
+    salt_bytes: &'a [u8],
+    /// Nonce for en/decrypting the section
+    nonce: &'a AesCtrNonce,
+    /// plaintext identity token
+    plaintext_identity_token: &'a [u8],
+    /// the len of the rest of the section contents stored as an u8
+    section_payload_len: u8,
+    /// plaintext identity token
+    plaintext_data_elements: &'a [u8],
 }
 
 const ADV_SIGNATURE_CONTEXT: np_ed25519::SignatureContext = {
@@ -60,66 +51,48 @@
 };
 
 impl<'a> SectionSignaturePayload<'a> {
-    /// Construct a section signature payload using parts typically found during
-    /// deserialization of advertisements.
-    pub(crate) fn from_deserialized_parts(
-        adv_header_byte: u8,
-        section_header: u8,
-        encryption_info: &'a [u8; EncryptionInfo::TOTAL_DE_LEN],
-        nonce_ref: &'a AesCtrNonce,
-        identity_header: [u8; 2],
-        plaintext_metadata_key: MetadataKey,
-        raw_plaintext_remainder: &'a [u8],
+    /// Construct a section signature payload with separate section len and
+    /// remaining contents of the section header.
+    ///
+    /// The section header should be in its on-the-wire form.
+    pub(crate) fn new(
+        format_bytes: &'a [u8],
+        salt_bytes: &'a [u8],
+        nonce: &'a AesCtrNonce,
+        plaintext_identity_token: &'a [u8],
+        section_payload_len: u8,
+        plaintext_data_elements: &'a [u8],
     ) -> Self {
         Self {
-            adv_header_byte,
-            section_header,
-            encryption_info,
-            nonce_ref,
-            after_iv_info: AfterIVInfo::IdentityHeaderMetadataKeyAndRemainder(
-                identity_header,
-                plaintext_metadata_key,
-                raw_plaintext_remainder,
-            ),
-        }
-    }
-
-    /// Construct a section signature payload using parts typically found during
-    /// serialization of advertisements.
-    pub(crate) fn from_serialized_parts(
-        adv_header_byte: u8,
-        section_header: u8,
-        encryption_info: &'a [u8; EncryptionInfo::TOTAL_DE_LEN],
-        nonce_ref: &'a AesCtrNonce,
-        raw_after_iv_info: &'a [u8],
-    ) -> Self {
-        Self {
-            adv_header_byte,
-            section_header,
-            encryption_info,
-            nonce_ref,
-            after_iv_info: AfterIVInfo::Raw(raw_after_iv_info),
+            format_bytes,
+            salt_bytes,
+            nonce,
+            plaintext_identity_token,
+            section_payload_len,
+            plaintext_data_elements,
         }
     }
 
     /// Generates a signature for this section signing payload using
     /// the given Ed25519 key-pair.
-    pub(crate) fn sign<C: CryptoProvider>(
-        self,
-        key_pair: &np_ed25519::KeyPair<C>,
-    ) -> np_ed25519::Signature<C> {
-        key_pair
-            .sign_with_context(&ADV_SIGNATURE_CONTEXT, self)
+    pub(crate) fn sign<E: Ed25519Provider>(self, private_key: &PrivateKey) -> Signature {
+        np_ed25519::sign_with_context::<E, _>(private_key, &ADV_SIGNATURE_CONTEXT, self)
             .expect("section signature payloads should fit in signature buffer")
     }
+
     /// Verifies a signature for this section signing payload using
     /// the given Ed25519 public key.
-    pub(crate) fn verify<C: CryptoProvider>(
+    pub(crate) fn verify<E: Ed25519Provider>(
         self,
-        signature: &np_ed25519::Signature<C>,
-        public_key: &np_ed25519::PublicKey<C>,
+        signature: Signature,
+        public_key: &PublicKey,
     ) -> Result<(), np_ed25519::SignatureVerificationError> {
-        public_key.verify_signature_with_context(&ADV_SIGNATURE_CONTEXT, self, signature)
+        np_ed25519::verify_signature_with_context::<E, _>(
+            public_key,
+            &ADV_SIGNATURE_CONTEXT,
+            self,
+            signature,
+        )
     }
 }
 
@@ -128,23 +101,12 @@
 
     fn write_payload<S: Sink<u8> + ?Sized>(self, sink: &mut S) -> Option<()> {
         sink.try_extend_from_slice(&NP_SVC_UUID)?;
-        sink.try_push(self.adv_header_byte)?;
-        sink.try_push(self.section_header)?;
-        sink.try_extend_from_slice(self.encryption_info)?;
-        sink.try_extend_from_slice(self.nonce_ref)?;
-
-        // identity DE and the rest of the DEs except for the suffix
-        match self.after_iv_info {
-            AfterIVInfo::Raw(s) => sink.try_extend_from_slice(s),
-            AfterIVInfo::IdentityHeaderMetadataKeyAndRemainder(
-                identity_header,
-                metadata_key,
-                remainder,
-            ) => {
-                sink.try_extend_from_slice(&identity_header)?;
-                sink.try_extend_from_slice(&metadata_key.0)?;
-                sink.try_extend_from_slice(remainder)
-            }
-        }
+        sink.try_push(VERSION_HEADER_V1)?;
+        sink.try_extend_from_slice(self.format_bytes)?;
+        sink.try_extend_from_slice(self.salt_bytes)?;
+        sink.try_extend_from_slice(self.nonce)?;
+        sink.try_extend_from_slice(self.plaintext_identity_token)?;
+        sink.try_push(self.section_payload_len)?;
+        sink.try_extend_from_slice(self.plaintext_data_elements)
     }
 }
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 ee44c75..f094992 100644
--- a/nearby/presence/np_adv/src/extended/serialize/adv_tests.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/adv_tests.rs
@@ -16,92 +16,107 @@
 
 extern crate std;
 use super::*;
+use crate::extended::serialize::section::header::SectionHeader;
 use crate::extended::serialize::section_tests::{fill_section_builder, DummyDataElement};
-use np_hkdf::v1_salt::DataElementOffset;
+use crate::extended::V1_ENCODING_UNENCRYPTED;
+use crypto_provider_default::CryptoProviderImpl;
 use std::{prelude::rust_2021::*, vec};
 
 #[test]
-fn adv_encode_no_salt() {
+fn adv_encode_unencrypted() {
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
 
     let mut public_identity_section_builder =
-        adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+        adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
     public_identity_section_builder
         .add_de(|_| DummyDataElement { de_type: 30_u32.into(), data: vec![] })
         .unwrap();
 
-    public_identity_section_builder.add_to_advertisement();
+    public_identity_section_builder.add_to_advertisement::<CryptoProviderImpl>();
 
     assert_eq!(
         &[
-            0x20, // adv header
-            0x3,  // section header
-            0x3,  // public identity
-            0x80, 30, // de header
+            0x20, // NP version header
+            V1_ENCODING_UNENCRYPTED,
+            0x02, // section len
+            0x80, // de header, 0 length
+            30,
         ],
         adv_builder.into_advertisement().as_slice()
     )
 }
 
 #[test]
-fn adding_any_allowed_section_length_always_works_for_single_section() {
-    // up to section len - 1 to leave room for section header
-    for section_contents_len in 0..NP_ADV_MAX_SECTION_LEN - 1 {
+fn adding_any_ble5_allowed_section_length_always_works_for_single_section() {
+    // up to section len - 2 to leave room for NP version header, section length,
+    // and header
+    for section_contents_len in 0..=BLE_5_ADV_SVC_MAX_CONTENT_LEN - 3 {
         let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-        let mut section_builder =
-            adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
-        fill_section_builder(section_contents_len, &mut section_builder);
+        let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
+        fill_section_builder(section_contents_len, &mut section_builder).unwrap();
 
-        section_builder.add_to_advertisement();
+        section_builder.add_to_advertisement::<CryptoProviderImpl>();
 
         let adv = adv_builder.into_advertisement();
         assert_eq!(
-            section_contents_len + 1 + 1 + 1, // adv and section headers and identity
+            section_contents_len + 1 + 1 + 1, // NP version header, section length, section header
             adv.as_slice().len(),
             "adv: {:?}\nsection contents len: {}",
             adv.as_slice(),
             section_contents_len
         );
     }
+
+    // one longer won't fit, though
+    let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+    let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
+    assert_eq!(
+        AddDataElementError::InsufficientSectionSpace,
+        fill_section_builder(BLE_5_ADV_SVC_MAX_CONTENT_LEN - 2, &mut section_builder).unwrap_err()
+    );
 }
 
 #[test]
-fn building_capacity_0_section_works() {
+fn building_capacity_0_ble5_section_works() {
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
 
-    let mut section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+    let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
 
-    // leave room for section header and the public identity
-    fill_section_builder(NP_ADV_MAX_SECTION_LEN - 2, &mut section_builder);
+    // leave room for NP version header, section length and header
+    fill_section_builder(BLE_5_ADV_SVC_MAX_CONTENT_LEN - 3, &mut section_builder).unwrap();
 
-    assert_eq!(NP_ADV_MAX_SECTION_LEN, section_builder.section.capacity);
-    assert_eq!(NP_ADV_MAX_SECTION_LEN, section_builder.section.len());
+    // this section can fill everything except the NP version header
+    assert_eq!(BLE_5_ADV_SVC_MAX_CONTENT_LEN - 1, section_builder.section.capacity);
+    assert_eq!(BLE_5_ADV_SVC_MAX_CONTENT_LEN - 1, section_builder.section.len());
 
-    section_builder.add_to_advertisement();
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
 
-    assert_eq!(BLE_ADV_SVC_CONTENT_LEN, adv_builder.into_advertisement().as_slice().len());
+    assert_eq!(BLE_5_ADV_SVC_MAX_CONTENT_LEN, adv_builder.into_advertisement().as_slice().len());
 }
 
+// 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 PREFIX_LEN: usize = 200;
     const SUFFIX_LEN: usize = 0;
-    const INITIAL_DE_OFFSET: DataElementOffset = DataElementOffset::ZERO;
     const ADVERTISEMENT_TYPE: AdvertisementType = AdvertisementType::Plaintext;
+    type DerivedSalt = ();
 
-    fn postprocess(
+    fn header(&self) -> SectionHeader {
+        unimplemented!("Should never be hit")
+    }
+    fn postprocess<C: CryptoProvider>(
         &mut self,
-        _adv_header_byte: u8,
-        _section_header: u8,
-        _section_contents: &mut [u8],
+        _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")
     }
 
-    type DerivedSalt = ();
     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/de_header_tests.rs b/nearby/presence/np_adv/src/extended/serialize/de_header_tests.rs
index cb6c9ef..515ae97 100644
--- a/nearby/presence/np_adv/src/extended/serialize/de_header_tests.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/de_header_tests.rs
@@ -17,7 +17,6 @@
 use super::*;
 use crate::extended::deserialize;
 use core::cmp;
-use rand_ext::rand;
 use rand_ext::rand::{distributions, Rng as _};
 
 #[test]
@@ -69,10 +68,14 @@
 
             assert_eq!(header_len as usize, buf.len());
 
-            let (_, deser) = deserialize::DeHeader::parse(buf.as_slice()).unwrap();
+            let (_, deser) = deserialize::data_element::DeHeader::parse(buf.as_slice()).unwrap();
 
             assert_eq!(
-                deserialize::DeHeader { de_type, contents_len: len, header_bytes: buf },
+                deserialize::data_element::DeHeader {
+                    de_type,
+                    contents_len: len,
+                    header_bytes: buf
+                },
                 deser
             )
         }
@@ -89,10 +92,10 @@
         let header_len = expected_header_len(hdr);
 
         assert_eq!(header_len as usize, buf.len());
-        let (_, deser) = deserialize::DeHeader::parse(buf.as_slice()).unwrap();
+        let (_, deser) = deserialize::data_element::DeHeader::parse(buf.as_slice()).unwrap();
 
         assert_eq!(
-            deserialize::DeHeader {
+            deserialize::data_element::DeHeader {
                 de_type: hdr.de_type,
                 contents_len: hdr.len,
                 header_bytes: buf
diff --git a/nearby/presence/np_adv/src/extended/serialize/mod.rs b/nearby/presence/np_adv/src/extended/serialize/mod.rs
index 363862f..d94fee2 100644
--- a/nearby/presence/np_adv/src/extended/serialize/mod.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/mod.rs
@@ -19,15 +19,15 @@
 //! Serialize some DEs without an adv salt:
 //!
 //! ```
+//! use crypto_provider_default::CryptoProviderImpl;
 //! use np_adv::{
-//!     extended::{data_elements::*, serialize::*, de_type::DeType },
-//!     PublicIdentity
+//!     extended::{data_elements::*, serialize::*, de_type::DeType, V1_ENCODING_UNENCRYPTED},
+//!     shared_data::TxPower
 //! };
-//! use np_adv::shared_data::TxPower;
 //!
 //! // no section identities or DEs need salt in this example
 //! let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-//! let mut section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+//! let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
 //!
 //! section_builder.add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
 //!
@@ -36,13 +36,13 @@
 //!     GenericDataElement::try_from( DeType::from(1000_u32), &[10, 11, 12, 13])
 //! ).unwrap();
 //!
-//! section_builder.add_to_advertisement();
+//! section_builder.add_to_advertisement::<CryptoProviderImpl>();
 //!
 //! assert_eq!(
 //!     &[
-//!         0x20, // adv header
-//!         10, // section header
-//!         0x03, // public identity
+//!         0x20, // version header
+//!         V1_ENCODING_UNENCRYPTED, //section format
+//!         0x09, // section length
 //!         0x15, 3, // tx power
 //!         0x84, 0x87, 0x68, 10, 11, 12, 13, // other DE
 //!     ],
@@ -54,34 +54,32 @@
 //!
 //! ```
 //! use np_adv::{
-//!     credential::{SimpleBroadcastCryptoMaterial, v1::V1},
-//!     de_type::EncryptedIdentityDataElementType,
-//!     extended::{data_elements::*, serialize::*, de_type::DeType },
-//!     MetadataKey,
+//!     credential::{ v1::{V1, V1BroadcastCredential}},
+//!     extended::{data_elements::*, serialize::*, de_type::DeType, V1IdentityToken },
 //! };
 //! use rand::{Rng as _, SeedableRng as _};
-//! use crypto_provider::{CryptoProvider, CryptoRng};
+//! use crypto_provider::{CryptoProvider, CryptoRng, ed25519};
 //! use crypto_provider_default::CryptoProviderImpl;
 //! use np_adv::shared_data::TxPower;
 //!
 //! let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
 //!
-//! // these would come from the credential//!
+//! // these would come from the credential
+//!
 //! let mut rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
-//! let metadata_key: [u8; 16] = rng.gen();
-//! let metadata_key = MetadataKey(metadata_key);
+//! let identity_token = rng.gen();
 //! let key_seed: [u8; 32] = rng.gen();
 //! // use your preferred crypto impl
 //! let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
 //!
-//! let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(
+//! let broadcast_cm = V1BroadcastCredential::new(
 //!     key_seed,
-//!     metadata_key,
+//!     identity_token,
+//!     ed25519::PrivateKey::generate::<<CryptoProviderImpl as CryptoProvider>::Ed25519>(),
 //! );
 //!
-//! let mut section_builder = adv_builder.section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
+//! let mut section_builder = adv_builder.section_builder(MicEncryptedSectionEncoder::<_>::new_random_salt::<CryptoProviderImpl>(
 //!     &mut rng,
-//!     EncryptedIdentityDataElementType::Private,
 //!     &broadcast_cm,
 //! )).unwrap();
 //!
@@ -91,10 +89,10 @@
 //! section_builder.add_de_res(|salt|
 //!     GenericDataElement::try_from(
 //!         DeType::from(1000_u32),
-//!         &do_fancy_crypto(salt.derive::<16>().expect("16 is a valid HKDF length")))
+//!         &do_fancy_crypto(salt.derive::<16, CryptoProviderImpl>().expect("16 is a valid HKDF length")))
 //! ).unwrap();
 //!
-//! section_builder.add_to_advertisement();
+//! section_builder.add_to_advertisement::<CryptoProviderImpl>();
 //!
 //! // can't assert much about this since most of it is random
 //! assert_eq!(
@@ -111,37 +109,33 @@
 //!         .try_into().expect("array sizes match")
 //! }
 //! ```
-use crate::extended::deserialize::RawV1Salt;
-use crate::extended::{NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT, NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT};
-use crate::{
-    credential::{
-        v1::{SignedBroadcastCryptoMaterial, V1},
-        BroadcastCryptoMaterial,
-    },
-    de_type::{EncryptedIdentityDataElementType, IdentityDataElementType},
-    extended::{
-        data_elements::EncryptionInfoDataElement,
-        de_type::{DeType, ExtendedDataElementType},
-        deserialize::{EncryptedIdentityMetadata, EncryptionInfo, SectionMic},
-        section_signature_payload::*,
-        to_array_view, DeLength, BLE_ADV_SVC_CONTENT_LEN, NP_ADV_MAX_SECTION_LEN,
-    },
-    DeLengthOutOfRange, MetadataKey, NP_SVC_UUID,
-};
-use array_view::ArrayView;
 use core::fmt::{self, Display};
-use crypto_provider::{
-    aes::{
-        ctr::{AesCtr, AesCtrNonce, NonceAndCounter},
-        Aes128Key,
-    },
-    hmac::Hmac,
-    CryptoProvider, CryptoRng,
-};
-use np_hkdf::v1_salt;
-use np_hkdf::v1_salt::{DataElementOffset, V1Salt};
+
+use array_view::ArrayView;
+use crypto_provider::CryptoProvider;
+use np_hkdf::v1_salt::{DataElementOffset, ExtendedV1Salt};
 use sink::Sink;
 
+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,
+};
+
+mod section;
+
+use crate::header::VERSION_HEADER_V1;
+pub use section::{
+    encoder::{
+        MicEncryptedSectionEncoder, SectionEncoder, SignedEncryptedSectionEncoder,
+        UnencryptedSectionEncoder,
+    },
+    AddDataElementError, SectionBuilder,
+};
+
+#[cfg(test)]
+use crate::header::V1AdvHeader;
+
 #[cfg(test)]
 pub(crate) mod adv_tests;
 #[cfg(test)]
@@ -154,8 +148,9 @@
 /// Builder for V1 advertisements.
 #[derive(Debug)]
 pub struct AdvBuilder {
+    // TODO make this configurable, and test making sections whose length is not restricted by BLE limitations
     /// Contains the adv header byte
-    adv: tinyvec::ArrayVec<[u8; BLE_ADV_SVC_CONTENT_LEN]>,
+    adv: tinyvec::ArrayVec<[u8; BLE_5_ADV_SVC_MAX_CONTENT_LEN]>,
     /// To track the number of sections that are in the advertisement
     section_count: usize,
     /// Advertisement type: Public or Encrypted
@@ -172,46 +167,10 @@
     /// Build an [AdvBuilder].
     pub fn new(advertisement_type: AdvertisementType) -> Self {
         let mut adv = tinyvec::ArrayVec::new();
-        // version 1, 0bVVVRRRRR
-        adv.push(0b00100000);
+        adv.push(VERSION_HEADER_V1);
         Self { adv, section_count: 0, advertisement_type }
     }
 
-    fn prepare_section_builder_buffer_and_de_offset<SE: SectionEncoder>(
-        &self,
-    ) -> Result<(CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>, DataElementOffset), AddSectionError>
-    {
-        // section header and identity prefix
-        let prefix_len = 1 + SE::PREFIX_LEN;
-        let minimum_section_len = prefix_len + SE::SUFFIX_LEN;
-        // the max overall len available to the section
-        let available_len = self.adv.capacity() - self.adv.len();
-
-        if available_len < minimum_section_len {
-            return Err(AddSectionError::InsufficientAdvSpace);
-        }
-
-        if self.section_count >= self.advertisement_type.max_sections() {
-            return Err(AddSectionError::MaxSectionCountExceeded);
-        }
-
-        if self.advertisement_type != SE::ADVERTISEMENT_TYPE {
-            return Err(AddSectionError::IncompatibleSectionType);
-        }
-
-        let mut section: tinyvec::ArrayVec<[u8; 249]> = tinyvec::ArrayVec::new();
-        // placeholder for section header and identity prefix
-        section.resize(prefix_len, 0);
-
-        let section = CapacityLimitedVec {
-            vec: section,
-            // won't underflow: checked above
-            capacity: available_len - SE::SUFFIX_LEN,
-        };
-        let next_de_offset = SE::INITIAL_DE_OFFSET;
-        Ok((section, next_de_offset))
-    }
-
     /// Create a section builder whose contents may be added to this advertisement.
     ///
     /// The builder will not accept more DEs than can fit given the space already used in the
@@ -223,10 +182,8 @@
         &mut self,
         section_encoder: SE,
     ) -> Result<SectionBuilder<&mut AdvBuilder, SE>, AddSectionError> {
-        let (section, next_de_offset) =
-            self.prepare_section_builder_buffer_and_de_offset::<SE>()?;
-
-        Ok(SectionBuilder { section, section_encoder, adv_builder: self, next_de_offset })
+        let (header_len, contents) = self.prepare_section_builder_buffer(&section_encoder)?;
+        Ok(SectionBuilder::new(header_len, contents, section_encoder, self))
     }
 
     /// Create a section builder which actually takes ownership of this advertisement builder.
@@ -240,9 +197,9 @@
         self,
         section_encoder: SE,
     ) -> Result<SectionBuilder<AdvBuilder, SE>, (AdvBuilder, AddSectionError)> {
-        match self.prepare_section_builder_buffer_and_de_offset::<SE>() {
-            Ok((section, next_de_offset)) => {
-                Ok(SectionBuilder { section, section_encoder, adv_builder: self, next_de_offset })
+        match self.prepare_section_builder_buffer::<SE>(&section_encoder) {
+            Ok((header_len, section)) => {
+                Ok(SectionBuilder::new(header_len, section, section_encoder, self))
             }
             Err(err) => Err((self, err)),
         }
@@ -259,6 +216,37 @@
         self.section_count
     }
 
+    /// Returns the length of the header (excluding the leading length byte),
+    /// and a buffer already populated with a placeholder section length byte and the rest
+    /// of the header.
+    fn prepare_section_builder_buffer<SE: SectionEncoder>(
+        &self,
+        section_encoder: &SE,
+    ) -> Result<(usize, CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>), AddSectionError> {
+        if self.section_count >= self.advertisement_type.max_sections() {
+            return Err(AddSectionError::MaxSectionCountExceeded);
+        }
+        if self.advertisement_type != SE::ADVERTISEMENT_TYPE {
+            return Err(AddSectionError::IncompatibleSectionType);
+        }
+
+        // The header contains all the header bytes except for the final length byte.
+        let header = section_encoder.header();
+        let header_slice = header.as_slice();
+
+        // the max overall len available to the section
+        let available_len = self.adv.capacity() - self.adv.len();
+
+        let mut prefix = available_len
+            .checked_sub(SE::SUFFIX_LEN)
+            .and_then(CapacityLimitedVec::new)
+            .ok_or(AddSectionError::InsufficientAdvSpace)?;
+        prefix.try_extend_from_slice(header_slice).ok_or(AddSectionError::InsufficientAdvSpace)?;
+        // Placeholder for section length, which we do not know yet
+        prefix.try_push(0).ok_or(AddSectionError::InsufficientAdvSpace)?;
+        Ok((header_slice.len(), prefix))
+    }
+
     /// Add the section, which must have come from a SectionBuilder generated from this, into this
     /// advertisement.
     fn add_section(&mut self, section: EncodedSection) {
@@ -268,8 +256,9 @@
         self.section_count += 1;
     }
 
-    fn header_byte(&self) -> u8 {
-        self.adv[0]
+    #[cfg(test)]
+    fn adv_header(&self) -> V1AdvHeader {
+        V1AdvHeader::new(self.adv[0])
     }
 }
 
@@ -288,7 +277,7 @@
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             AddSectionError::InsufficientAdvSpace => {
-                write!(f, "The advertisement (max {BLE_ADV_SVC_CONTENT_LEN} bytes) doesn't have enough remaining space to hold the section")
+                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")
@@ -303,7 +292,7 @@
 /// An encoded NP V1 advertisement, starting with the NP advertisement header byte.
 #[derive(Debug, PartialEq, Eq)]
 pub struct EncodedAdvertisement {
-    adv: ArrayView<u8, BLE_ADV_SVC_CONTENT_LEN>,
+    adv: ArrayView<u8, BLE_5_ADV_SVC_MAX_CONTENT_LEN>,
 }
 
 impl EncodedAdvertisement {
@@ -313,158 +302,11 @@
     }
     /// Converts this encoded advertisement into
     /// a raw byte-array.
-    pub fn into_array_view(self) -> ArrayView<u8, BLE_ADV_SVC_CONTENT_LEN> {
+    pub fn into_array_view(self) -> ArrayView<u8, BLE_5_ADV_SVC_MAX_CONTENT_LEN> {
         self.adv
     }
 }
 
-/// The encoded form of an advertisement section
-type EncodedSection = ArrayView<u8, NP_ADV_MAX_SECTION_LEN>;
-
-/// Accumulates data elements and encodes them into a section.
-#[derive(Debug)]
-pub struct SectionBuilder<R: AsMut<AdvBuilder>, SE: SectionEncoder> {
-    /// Contains the section header, the identity-specified overhead, and any DEs added
-    pub(crate) section: CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>,
-    section_encoder: SE,
-    /// mut ref-able to enforce only one active section builder at a time
-    adv_builder: R,
-    next_de_offset: DataElementOffset,
-}
-
-impl<'a, SE: SectionEncoder> SectionBuilder<&'a mut AdvBuilder, SE> {
-    /// Add this builder to the advertisement that created it.
-    pub fn add_to_advertisement(self) {
-        let _ = self.add_to_advertisement_internal();
-    }
-}
-
-impl<SE: SectionEncoder> SectionBuilder<AdvBuilder, SE> {
-    /// Gets the 0-based index of the section currently under construction
-    /// in the context of the containing advertisement.
-    pub fn section_index(&self) -> usize {
-        self.adv_builder.section_count()
-    }
-    /// Add this builder to the advertisement that created it,
-    /// and returns the containing advertisement back to the caller.
-    pub fn add_to_advertisement(self) -> AdvBuilder {
-        self.add_to_advertisement_internal()
-    }
-}
-
-impl<R: AsMut<AdvBuilder>, SE: SectionEncoder> SectionBuilder<R, SE> {
-    /// Add this builder to the advertisement that created it.
-    /// Returns the mut-refable to the advertisement builder
-    /// which the contents of this section builder were added to.
-    fn add_to_advertisement_internal(mut self) -> R {
-        let adv_builder = self.adv_builder.as_mut();
-        let adv_builder_header_byte = adv_builder.header_byte();
-        adv_builder.add_section(Self::build_section(
-            adv_builder_header_byte,
-            self.section.into_inner(),
-            self.section_encoder,
-        ));
-        self.adv_builder
-    }
-
-    /// Gets the derived salt which will be employed for the next DE offset.
-    ///
-    /// Suitable for scenarios (like FFI) where a closure would be inappropriate
-    /// for DE construction, and interaction with the client is preferred.
-    pub fn next_de_salt(&self) -> SE::DerivedSalt {
-        self.section_encoder.de_salt(self.next_de_offset)
-    }
-
-    /// Add a data element to the section with a closure that returns a `Result`.
-    ///
-    /// The provided `build_de` closure will be invoked with the derived salt for this DE.
-    pub fn add_de_res<W: WriteDataElement, E, F: FnOnce(SE::DerivedSalt) -> Result<W, E>>(
-        &mut self,
-        build_de: F,
-    ) -> Result<(), AddDataElementError<E>> {
-        let writer = build_de(self.next_de_salt()).map_err(AddDataElementError::BuildDeError)?;
-
-        let orig_len = self.section.len();
-        // since we own the writer, and it's immutable, no race risk writing header w/ len then
-        // the contents as long as it's not simply an incorrect impl
-        let de_header = writer.de_header();
-        let content_len = self
-            .section
-            .try_extend_from_slice(de_header.serialize().as_slice())
-            .ok_or(AddDataElementError::InsufficientSectionSpace)
-            .and_then(|_| {
-                let after_header_len = self.section.len();
-                writer
-                    .write_de_contents(&mut self.section)
-                    .ok_or(AddDataElementError::InsufficientSectionSpace)
-                    .map(|_| self.section.len() - after_header_len)
-            })
-            .map_err(|e| {
-                // if anything went wrong, truncate any partial writes (e.g. just the header)
-                self.section.truncate(orig_len);
-                e
-            })?;
-
-        if content_len != usize::from(de_header.len.as_u8()) {
-            panic!(
-                "Buggy WriteDataElement impl: header len {}, actual written len {}",
-                de_header.len.as_u8(),
-                content_len
-            );
-        }
-
-        self.next_de_offset = self.next_de_offset.incremented();
-
-        Ok(())
-    }
-
-    /// Add a data element to the section with a closure that returns the data element directly.
-    ///
-    /// The provided `build_de` closure will be invoked with the derived salt for this DE.
-    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)))
-    }
-
-    /// Convert a section builder's contents into an encoded section.
-    ///
-    /// Implemented without self to avoid partial-move issues.
-    fn build_section(
-        adv_builder_header_byte: u8,
-        mut section_contents: tinyvec::ArrayVec<[u8; NP_ADV_MAX_SECTION_LEN]>,
-        mut section_encoder: SE,
-    ) -> EncodedSection {
-        // there is space because the capacity for DEs was restricted to allow it
-        section_contents.resize(section_contents.len() + SE::SUFFIX_LEN, 0);
-
-        section_contents[0] = section_contents
-            .len()
-            .try_into()
-            .ok()
-            .and_then(|len: u8| len.checked_sub(1))
-            .expect("section length is always <=255 and non-negative");
-
-        section_encoder.postprocess(
-            adv_builder_header_byte,
-            section_contents[0],
-            &mut section_contents[1..],
-        );
-
-        to_array_view(section_contents)
-    }
-}
-
-/// Errors for adding a DE to a section
-#[derive(Debug, PartialEq, Eq)]
-pub enum AddDataElementError<E> {
-    /// An error occurred when invoking the DE builder closure.
-    BuildDeError(E),
-    /// Too much data to fit into the section
-    InsufficientSectionSpace,
-}
-
 /// The advertisement type, which dictates what sections can exist
 #[derive(Debug, PartialEq, Eq)]
 pub enum AdvertisementType {
@@ -483,288 +325,18 @@
     }
 }
 
-/// Everything needed to properly encode a section
-pub trait SectionEncoder {
-    /// How much space needs to be reserved for this identity's prefix bytes after the section
-    /// header and before other DEs
-    const PREFIX_LEN: usize;
-
-    /// How much space needs to be reserved after the DEs
-    const SUFFIX_LEN: usize;
-
-    /// The DE offset to use for any DEs added to the section
-    const INITIAL_DE_OFFSET: DataElementOffset;
-
-    /// The advertisement type that can support this section
-    const ADVERTISEMENT_TYPE: AdvertisementType;
-
-    /// Postprocess the contents of the section (the data after the section header byte), which will
-    /// start with [Self::PREFIX_LEN] bytes set aside for the identity's use, and similarly end with
-    /// [Self::SUFFIX_LEN] bytes, with DEs (if any) in the middle.
-    fn postprocess(&mut self, adv_header_byte: u8, section_header: u8, section_contents: &mut [u8]);
-
-    /// The type of derived salt produced for a DE sharing a section with this identity.
-    type DerivedSalt;
-
-    /// Produce a `Self::Output` salt for a DE.
-    fn de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt;
-}
-
-/// Public section for plaintext data elements
-#[derive(Default, Debug)]
-pub struct PublicSectionEncoder {}
-impl SectionEncoder for PublicSectionEncoder {
-    /// 1 byte of public identity DE header
-    const PREFIX_LEN: usize = 1;
-    const SUFFIX_LEN: usize = 0;
-    /// Room for the public DE
-    const INITIAL_DE_OFFSET: DataElementOffset = DataElementOffset::ZERO.incremented();
-    const ADVERTISEMENT_TYPE: AdvertisementType = AdvertisementType::Plaintext;
-    fn postprocess(
-        &mut self,
-        _adv_header_byte: u8,
-        _section_header: u8,
-        section_contents: &mut [u8],
-    ) {
-        section_contents[0..1].copy_from_slice(
-            DeHeader { len: DeLength::ZERO, de_type: IdentityDataElementType::Public.type_code() }
-                .serialize()
-                .as_slice(),
-        )
-    }
-    type DerivedSalt = ();
-    fn de_salt(&self, _de_offset: DataElementOffset) -> Self::DerivedSalt {}
-}
-
-/// Encrypts the data elements and protects integrity with an np_ed25519 signature
-/// using key material derived from an NP identity.
-pub struct SignedEncryptedSectionEncoder<C: CryptoProvider> {
-    identity_type: EncryptedIdentityDataElementType,
-    salt: RawV1Salt,
-    metadata_key: MetadataKey,
-    key_pair: np_ed25519::KeyPair<C>,
-    aes_key: Aes128Key,
-}
-
-impl<C: CryptoProvider> SignedEncryptedSectionEncoder<C> {
-    /// Build a [SignedEncryptedSectionEncoder] from an identity type,
-    /// some broadcast crypto-material, and with a random salt.
-    pub fn new_random_salt<B: SignedBroadcastCryptoMaterial>(
-        rng: &mut C::CryptoRng,
-        identity_type: EncryptedIdentityDataElementType,
-        crypto_material: &B,
-    ) -> Self {
-        let salt = RawV1Salt(rng.gen::<[u8; 16]>());
-        Self::new(identity_type, salt, crypto_material)
-    }
-
-    /// Build a [SignedEncryptedSectionEncoder] from an identity type,
-    /// a provided salt, and some broadcast crypto-material.
-    pub(crate) fn new<B: SignedBroadcastCryptoMaterial>(
-        identity_type: EncryptedIdentityDataElementType,
-        salt: RawV1Salt,
-        crypto_material: &B,
-    ) -> Self {
-        let metadata_key = crypto_material.metadata_key();
-        let key_seed = crypto_material.key_seed();
-        let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
-        let private_key = crypto_material.signing_key();
-        let key_pair = np_ed25519::KeyPair::<C>::from_private_key(&private_key);
-        let aes_key = key_seed_hkdf.extended_signed_section_aes_key();
-        Self { identity_type, salt, metadata_key, key_pair, aes_key }
-    }
-}
-
-impl<C: CryptoProvider> SectionEncoder for SignedEncryptedSectionEncoder<C> {
-    const PREFIX_LEN: usize =
-        EncryptionInfo::TOTAL_DE_LEN + EncryptedIdentityMetadata::TOTAL_DE_LEN;
-    /// Ed25519 signature
-    const SUFFIX_LEN: usize = crypto_provider::ed25519::SIGNATURE_LENGTH;
-    /// Room for the encryption info and identity DEs
-    const INITIAL_DE_OFFSET: DataElementOffset =
-        DataElementOffset::ZERO.incremented().incremented();
-    const ADVERTISEMENT_TYPE: AdvertisementType = AdvertisementType::Encrypted;
-
-    fn postprocess(
-        &mut self,
-        adv_header_byte: u8,
-        section_header: u8,
-        section_contents: &mut [u8],
-    ) {
-        let encryption_info_bytes = EncryptionInfoDataElement::signature(&self.salt.0).serialize();
-        section_contents[0..19].copy_from_slice(&encryption_info_bytes);
-
-        let identity_header = identity_de_header(self.identity_type, self.metadata_key);
-        section_contents[19..21].copy_from_slice(identity_header.serialize().as_slice());
-        section_contents[21..37].copy_from_slice(&self.metadata_key.0);
-
-        let nonce: AesCtrNonce = self
-            .de_salt(v1_salt::DataElementOffset::from(1))
-            .derive()
-            .expect("AES-CTR nonce is a valid HKDF length");
-
-        let (before_sig, sig) =
-            section_contents.split_at_mut(section_contents.len() - Self::SUFFIX_LEN);
-        let (encryption_info, after_encryption_info) =
-            before_sig.split_at(EncryptionInfo::TOTAL_DE_LEN);
-
-        let encryption_info: &[u8; EncryptionInfo::TOTAL_DE_LEN] =
-            encryption_info.try_into().expect("encryption info should always be the correct size");
-
-        // we need to sign the 16-byte IV, which doesn't have to actually fit in the adv, but we
-        // don't need a bigger buffer here since we won't be including the 66 bytes for the sig +
-        // header.
-        // If the stack usage ever becomes a problem, we can investigate pre hashing for the
-        // signature.
-        let nonce_ref = &nonce;
-        let section_signature_payload = SectionSignaturePayload::from_serialized_parts(
-            adv_header_byte,
-            section_header,
-            encryption_info,
-            nonce_ref,
-            after_encryption_info,
-        );
-
-        let signature = section_signature_payload.sign(&self.key_pair);
-
-        sig[0..64].copy_from_slice(&signature.to_bytes());
-
-        let mut cipher = C::AesCtr128::new(&self.aes_key, NonceAndCounter::from_nonce(nonce));
-
-        // encrypt just the part that should be ciphertext: identity DE contents and subsequent DEs
-        cipher.apply_keystream(&mut section_contents[21..]);
-    }
-
-    type DerivedSalt = DeSalt<C>;
-
-    fn de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt {
-        DeSalt { salt: V1Salt::from(self.salt.0), de_offset }
-    }
-}
-
-/// Encrypts the data elements and protects integrity with a MIC using key material derived from
-/// an NP identity.
-pub struct MicEncryptedSectionEncoder<C: CryptoProvider> {
-    identity_type: EncryptedIdentityDataElementType,
-    salt: RawV1Salt,
-    metadata_key: MetadataKey,
-    aes_key: Aes128Key,
-    mic_hmac_key: np_hkdf::NpHmacSha256Key<C>,
-}
-
-impl<C: CryptoProvider> MicEncryptedSectionEncoder<C> {
-    /// Build a [MicEncryptedSectionEncoder] from the provided identity
-    /// info with a random salt.
-    pub fn new_random_salt<B: BroadcastCryptoMaterial<V1>>(
-        rng: &mut C::CryptoRng,
-        identity_type: EncryptedIdentityDataElementType,
-        crypto_material: &B,
-    ) -> Self {
-        let salt = RawV1Salt(rng.gen::<[u8; 16]>());
-        Self::new(identity_type, salt, crypto_material)
-    }
-
-    /// Build a [MicEncryptedSectionEncoder] from the provided identity info.
-    pub(crate) fn new<B: BroadcastCryptoMaterial<V1>>(
-        identity_type: EncryptedIdentityDataElementType,
-        salt: RawV1Salt,
-        crypto_material: &B,
-    ) -> Self {
-        let metadata_key = crypto_material.metadata_key();
-        let key_seed = crypto_material.key_seed();
-        let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
-        let aes_key = np_hkdf::UnsignedSectionKeys::aes_key(&key_seed_hkdf);
-        let mic_hmac_key = np_hkdf::UnsignedSectionKeys::hmac_key(&key_seed_hkdf);
-
-        Self { identity_type, salt, metadata_key, aes_key, mic_hmac_key }
-    }
-
-    /// Build a [MicEncrypedSectionEncoder] from the provided identity info.
-    /// Exposed outside of this crate for testing purposes only, since this
-    /// does not handle the generation of random salts.
-    #[cfg(any(test, feature = "testing"))]
-    pub fn new_for_testing<B: BroadcastCryptoMaterial<V1>>(
-        identity_type: EncryptedIdentityDataElementType,
-        salt: RawV1Salt,
-        crypto_material: &B,
-    ) -> Self {
-        Self::new(identity_type, salt, crypto_material)
-    }
-}
-
-impl<C: CryptoProvider> SectionEncoder for MicEncryptedSectionEncoder<C> {
-    const PREFIX_LEN: usize =
-        EncryptionInfo::TOTAL_DE_LEN + EncryptedIdentityMetadata::TOTAL_DE_LEN;
-    /// Length of mic
-    const SUFFIX_LEN: usize = SectionMic::CONTENTS_LEN;
-    /// Room for the mic, encryption info, and identity DEs
-    const INITIAL_DE_OFFSET: DataElementOffset =
-        DataElementOffset::ZERO.incremented().incremented();
-
-    const ADVERTISEMENT_TYPE: AdvertisementType = AdvertisementType::Encrypted;
-
-    fn postprocess(
-        &mut self,
-        adv_header_byte: u8,
-        section_header: u8,
-        section_contents: &mut [u8],
-    ) {
-        // prefix byte layout:
-        // 0-18: Encryption Info DE (header + scheme + salt)
-        // 19-20: Identity DE header
-        // 21-36: Identity DE contents (metadata key)
-        // Encryption Info DE
-        let encryption_info_bytes = EncryptionInfoDataElement::mic(&self.salt.0).serialize();
-        section_contents[0..19].copy_from_slice(&encryption_info_bytes);
-        // Identity DE
-        let identity_header = identity_de_header(self.identity_type, self.metadata_key);
-        section_contents[19..21].copy_from_slice(identity_header.serialize().as_slice());
-        section_contents[21..37].copy_from_slice(&self.metadata_key.0);
-        // DE offset for identity is 1: Encryption Info DE, Identity DE, then other DEs
-        let nonce: AesCtrNonce = self
-            .de_salt(v1_salt::DataElementOffset::from(1))
-            .derive()
-            .expect("AES-CTR nonce is a valid HKDF length");
-        let mut cipher = C::AesCtr128::new(&self.aes_key, NonceAndCounter::from_nonce(nonce));
-        let ciphertext_end = section_contents.len() - SectionMic::CONTENTS_LEN;
-        // encrypt just the part that should be ciphertext: identity DE contents and subsequent DEs
-        cipher.apply_keystream(&mut section_contents[21..ciphertext_end]);
-        // calculate MAC per the spec
-        let mut section_hmac = self.mic_hmac_key.build_hmac();
-        // svc uuid
-        section_hmac.update(NP_SVC_UUID.as_slice());
-        // adv header
-        section_hmac.update(&[adv_header_byte]);
-        // section header
-        section_hmac.update(&[section_header]);
-        // encryption info
-        section_hmac.update(&encryption_info_bytes);
-        // derived salt
-        section_hmac.update(&nonce);
-        // identity header + ciphertext
-        section_hmac.update(&section_contents[19..ciphertext_end]);
-        let mic: [u8; 32] = section_hmac.finalize();
-        // write truncated MIC
-        section_contents[ciphertext_end..].copy_from_slice(&mic[..SectionMic::CONTENTS_LEN]);
-    }
-    type DerivedSalt = DeSalt<C>;
-    fn de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt {
-        DeSalt { salt: V1Salt::from(self.salt.0), de_offset }
-    }
-}
-
 /// Derived salt for an individual data element.
-pub struct DeSalt<C: CryptoProvider> {
-    salt: V1Salt<C>,
+pub struct DeSalt {
+    salt: ExtendedV1Salt,
     de_offset: DataElementOffset,
 }
 
-impl<C: CryptoProvider> DeSalt<C> {
+impl DeSalt {
     /// Derive salt of the requested length.
     ///
     /// The length must be a valid HKDF-SHA256 length.
-    pub fn derive<const N: usize>(&self) -> Option<[u8; N]> {
-        self.salt.derive(Some(self.de_offset))
+    pub fn derive<const N: usize, C: CryptoProvider>(&self) -> Option<[u8; N]> {
+        self.salt.derive::<N, C>(Some(self.de_offset))
     }
 }
 
@@ -823,7 +395,7 @@
         let de_type = self.de_type.as_u32();
         let hi_bit = 0x80_u8;
         let len = self.len.len;
-        if len < 8 && de_type < 16 {
+        if !de_requires_extended_bit(de_type, len) {
             buffer[0] = len << 4 | de_type as u8;
             ArrayView::try_from_array(buffer, 1).expect("1 is a valid length")
         } else {
@@ -867,22 +439,6 @@
     }
 }
 
-fn identity_de_header(
-    id_type: EncryptedIdentityDataElementType,
-    metadata_key: MetadataKey,
-) -> DeHeader {
-    DeHeader {
-        de_type: id_type.type_code(),
-        len: metadata_key
-            .0
-            .len()
-            .try_into()
-            .map_err(|_e| DeLengthOutOfRange)
-            .and_then(|len: u8| len.try_into())
-            .expect("metadata key is a valid DE length"),
-    }
-}
-
 /// A wrapper around a fixed-size tinyvec that can have its capacity further constrained to handle
 /// dynamic size limits.
 #[derive(Debug)]
@@ -893,7 +449,7 @@
     <[T; N] as tinyvec::Array>::Item: fmt::Debug + Clone,
 {
     /// constraint on the occupied space in `vec`.
-    /// Invariant: `vec.len() <= constraint` and `vec.capacity() >= capacity`.
+    /// Invariant: `vec.len() <= capacity` and `vec.capacity() >= capacity`.
     capacity: usize,
     vec: tinyvec::ArrayVec<[T; N]>,
 }
@@ -904,6 +460,15 @@
     [T; N]: tinyvec::Array + fmt::Debug,
     <[T; N] as tinyvec::Array>::Item: fmt::Debug + Clone,
 {
+    /// Returns `None` if `capacity > N`
+    pub(crate) fn new(capacity: usize) -> Option<Self> {
+        if capacity <= N {
+            Some(Self { capacity, vec: tinyvec::ArrayVec::new() })
+        } else {
+            None
+        }
+    }
+
     pub(crate) fn len(&self) -> usize {
         self.vec.len()
     }
@@ -916,7 +481,7 @@
         self.vec.truncate(len);
     }
 
-    fn into_inner(self) -> tinyvec::ArrayVec<[T; N]> {
+    pub(crate) fn into_inner(self) -> tinyvec::ArrayVec<[T; N]> {
         self.vec
     }
 }
@@ -946,24 +511,3 @@
         }
     }
 }
-
-impl<T, const N: usize> AsRef<[<[T; N] as tinyvec::Array>::Item]> for CapacityLimitedVec<T, N>
-where
-    T: fmt::Debug + Clone,
-    [T; N]: tinyvec::Array + fmt::Debug,
-    <[T; N] as tinyvec::Array>::Item: fmt::Debug + Clone,
-{
-    fn as_ref(&self) -> &[<[T; N] as tinyvec::Array>::Item] {
-        self.vec.as_slice()
-    }
-}
-impl<T, const N: usize> AsMut<[<[T; N] as tinyvec::Array>::Item]> for CapacityLimitedVec<T, N>
-where
-    T: fmt::Debug + Clone,
-    [T; N]: tinyvec::Array + fmt::Debug,
-    <[T; N] as tinyvec::Array>::Item: fmt::Debug + Clone,
-{
-    fn as_mut(&mut self) -> &mut [<[T; N] as tinyvec::Array>::Item] {
-        self.vec.as_mut_slice()
-    }
-}
diff --git a/nearby/presence/np_adv/src/extended/serialize/section/encoder.rs b/nearby/presence/np_adv/src/extended/serialize/section/encoder.rs
new file mode 100644
index 0000000..85f55bc
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/serialize/section/encoder.rs
@@ -0,0 +1,429 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::credential::v1::V1BroadcastCredential;
+use crate::extended::salt::{MultiSalt, ShortV1Salt, V1Salt};
+use crate::header::VERSION_HEADER_V1;
+use crate::{
+    extended::{
+        deserialize::SectionMic,
+        section_signature_payload::SectionSignaturePayload,
+        serialize::{section::header::SectionHeader, AdvertisementType, DeSalt},
+        V1IdentityToken, V1_IDENTITY_TOKEN_LEN,
+    },
+    NP_SVC_UUID,
+};
+use crypto_provider::{
+    aes,
+    aes::ctr::{AesCtr as _, AesCtrNonce, NonceAndCounter},
+    ed25519,
+    hmac::Hmac,
+    CryptoProvider, CryptoRng as _,
+};
+use np_hkdf::v1_salt::EXTENDED_SALT_LEN;
+use np_hkdf::{
+    v1_salt::{DataElementOffset, ExtendedV1Salt},
+    DerivedSectionKeys,
+};
+
+/// Everything needed to properly encode a section
+pub trait SectionEncoder {
+    /// How much space needs to be reserved after the DEs
+    const SUFFIX_LEN: usize;
+
+    /// The advertisement type that can support this section
+    const ADVERTISEMENT_TYPE: AdvertisementType;
+
+    /// The type of derived salt produced for a DE sharing a section with this identity.
+    type DerivedSalt;
+
+    /// Header to write at the start of the section contents
+    fn header(&self) -> SectionHeader;
+
+    /// Postprocess the contents of the section (the data after the section header byte), which will
+    /// start with the contents of [Self::header()], and similarly end with
+    /// [Self::SUFFIX_LEN] bytes, with DEs (if any) in the middle.
+    ///
+    /// `section_header_without_length` is the bytes of the section header up until but not
+    /// including the length byte
+    /// `section_len` is the length of the contents that come after the length byte which includes
+    /// the de contents + the suffix. This is the length of `remaining_content_bytes` stored as an u8
+    /// `remaining_content_bytes` are the bytes of the rest of the contents that come after section length
+    fn postprocess<C: CryptoProvider>(
+        &mut self,
+        section_header_without_length: &mut [u8],
+        section_len: u8,
+        remaining_content_bytes: &mut [u8],
+    );
+
+    /// Produce a `Self::Output` salt for a DE.
+    fn de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt;
+}
+
+/// Encoder for plaintext data elements
+#[derive(Debug)]
+pub struct UnencryptedSectionEncoder;
+
+impl UnencryptedSectionEncoder {}
+
+impl SectionEncoder for UnencryptedSectionEncoder {
+    const SUFFIX_LEN: usize = 0;
+    const ADVERTISEMENT_TYPE: AdvertisementType = AdvertisementType::Plaintext;
+
+    type DerivedSalt = ();
+
+    fn header(&self) -> SectionHeader {
+        SectionHeader::unencrypted()
+    }
+    fn postprocess<C: CryptoProvider>(
+        &mut self,
+        _section_header_without_length: &mut [u8],
+        _section_len: u8,
+        _remaining_content_bytes: &mut [u8],
+    ) {
+        // no op
+    }
+
+    fn de_salt(&self, _de_offset: DataElementOffset) -> Self::DerivedSalt {}
+}
+
+/// Encrypts the data elements and protects integrity with an np_ed25519 signature
+/// using key material derived from an NP identity.
+pub struct SignedEncryptedSectionEncoder {
+    pub(crate) salt: ExtendedV1Salt,
+    identity_token: V1IdentityToken,
+    private_key: ed25519::PrivateKey,
+    aes_key: aes::Aes128Key,
+}
+
+impl SignedEncryptedSectionEncoder {
+    /// Build a [SignedEncryptedSectionEncoder] from an identity type,
+    /// some broadcast crypto-material, and with a random salt.
+    pub fn new_random_salt<C: CryptoProvider>(
+        rng: &mut C::CryptoRng,
+        crypto_material: &V1BroadcastCredential,
+    ) -> Self {
+        let salt: ExtendedV1Salt = rng.gen::<[u8; 16]>().into();
+        Self::new::<C>(salt, crypto_material)
+    }
+
+    /// Build a [SignedEncryptedSectionEncoder] from an identity type,
+    /// a provided salt, and some broadcast crypto-material.
+    pub(crate) fn new<C: CryptoProvider>(
+        salt: ExtendedV1Salt,
+        crypto_material: &V1BroadcastCredential,
+    ) -> Self {
+        let identity_token = crypto_material.identity_token();
+        let key_seed = crypto_material.key_seed();
+        let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
+        let private_key = crypto_material.signing_key();
+        let aes_key = key_seed_hkdf.v1_signature_keys().aes_key();
+        Self { salt, identity_token, private_key, aes_key }
+    }
+}
+
+impl SectionEncoder for SignedEncryptedSectionEncoder {
+    /// Ed25519 signature
+    const SUFFIX_LEN: usize = crypto_provider::ed25519::SIGNATURE_LENGTH;
+    const ADVERTISEMENT_TYPE: AdvertisementType = AdvertisementType::Encrypted;
+
+    type DerivedSalt = DeSalt;
+
+    fn header(&self) -> SectionHeader {
+        SectionHeader::encrypted_signature_extended_salt(&self.salt, &self.identity_token)
+    }
+
+    fn postprocess<C: CryptoProvider>(
+        &mut self,
+        section_header_without_length: &mut [u8],
+        section_len: u8,
+        remaining_contents: &mut [u8],
+    ) {
+        let nonce: AesCtrNonce = self.salt.compute_nonce::<C>();
+        let mut cipher = C::AesCtr128::new(&self.aes_key, NonceAndCounter::from_nonce(nonce));
+
+        let start_of_identity_token = section_header_without_length.len() - V1_IDENTITY_TOKEN_LEN;
+        let (format_and_salt_bytes, identity_token) =
+            section_header_without_length.split_at_mut(start_of_identity_token);
+        debug_assert!(identity_token.len() == V1_IDENTITY_TOKEN_LEN);
+
+        let start_of_salt = format_and_salt_bytes.len() - EXTENDED_SALT_LEN;
+        let (format_bytes, salt_bytes) = format_and_salt_bytes.split_at_mut(start_of_salt);
+        debug_assert!(salt_bytes.len() == EXTENDED_SALT_LEN);
+        debug_assert!((1..=2).contains(&format_bytes.len()));
+
+        let start_of_signature = remaining_contents.len() - Self::SUFFIX_LEN;
+        let (plaintext_data_elements, sig) = remaining_contents.split_at_mut(start_of_signature);
+        debug_assert!(sig.len() == Self::SUFFIX_LEN);
+
+        let section_signature_payload = SectionSignaturePayload::new(
+            format_bytes,
+            salt_bytes,
+            &nonce,
+            identity_token,
+            section_len,
+            plaintext_data_elements,
+        );
+
+        sig.copy_from_slice(
+            &section_signature_payload.sign::<C::Ed25519>(&self.private_key).to_bytes(),
+        );
+
+        cipher.apply_keystream(identity_token);
+        cipher.apply_keystream(plaintext_data_elements);
+        cipher.apply_keystream(sig);
+    }
+
+    fn de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt {
+        DeSalt { salt: self.salt, de_offset }
+    }
+}
+
+/// Encrypts the data elements and protects integrity with a MIC using key material derived from
+/// an NP identity.
+pub struct MicEncryptedSectionEncoder<S> {
+    pub(crate) salt: S,
+    identity_token: V1IdentityToken,
+    aes_key: aes::Aes128Key,
+    mic_hmac_key: np_hkdf::NpHmacSha256Key,
+}
+
+impl<S: MicSectionEncoderSalt> MicEncryptedSectionEncoder<S> {
+    /// Build a [MicEncryptedSectionEncoder] from the provided identity info.
+    pub(crate) fn new<C: CryptoProvider>(
+        salt: S,
+        broadcast_credential: &V1BroadcastCredential,
+    ) -> Self {
+        let identity_token = broadcast_credential.identity_token();
+        let key_seed = broadcast_credential.key_seed();
+        let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
+        let aes_key = salt.derive_aes_key(&key_seed_hkdf);
+        let mic_hmac_key = salt.derive_mic_hmac_key(&key_seed_hkdf);
+
+        Self { salt, identity_token, aes_key, mic_hmac_key }
+    }
+
+    /// Build a [MicEncryptedSectionEncoder] from the provided identity info.
+    /// Exposed outside of this crate for testing purposes only, since this
+    /// does not handle the generation of random salts.
+    #[cfg(any(test, feature = "testing", feature = "devtools"))]
+    pub fn new_with_salt<C: CryptoProvider>(
+        salt: S,
+        broadcast_credential: &V1BroadcastCredential,
+    ) -> Self {
+        Self::new::<C>(salt, broadcast_credential)
+    }
+}
+
+impl MicEncryptedSectionEncoder<ExtendedV1Salt> {
+    /// Build a [MicEncryptedSectionEncoder] from the provided identity
+    /// info with a random extended salt.
+    pub fn new_random_salt<C: CryptoProvider>(
+        rng: &mut C::CryptoRng,
+        broadcast_credential: &V1BroadcastCredential,
+    ) -> Self {
+        Self::new::<C>(rng.gen(), broadcast_credential)
+    }
+}
+
+impl MicEncryptedSectionEncoder<MultiSalt> {
+    /// Build a [MicEncryptedSectionEncoder] from the provided identity
+    /// info with a random extended salt wrapped in [MultiSalt].
+    ///
+    /// Prefer [Self::new_random_salt] unless there is a need for the type
+    /// parameter to be [MultiSalt].
+    pub fn new_wrapped_salt<C: CryptoProvider>(
+        rng: &mut C::CryptoRng,
+        broadcast_credential: &V1BroadcastCredential,
+    ) -> Self {
+        Self::new::<C>(rng.gen::<ExtendedV1Salt>().into(), broadcast_credential)
+    }
+}
+
+impl<S: MicSectionEncoderSalt> SectionEncoder for MicEncryptedSectionEncoder<S> {
+    /// Length of mic
+    const SUFFIX_LEN: usize = SectionMic::CONTENTS_LEN;
+
+    const ADVERTISEMENT_TYPE: AdvertisementType = AdvertisementType::Encrypted;
+
+    type DerivedSalt = S::DerivedSalt;
+
+    fn header(&self) -> SectionHeader {
+        self.salt.header(&self.identity_token)
+    }
+
+    fn postprocess<C: CryptoProvider>(
+        &mut self,
+        section_header_without_length: &mut [u8],
+        section_len: u8,
+        remaining_contents: &mut [u8],
+    ) {
+        let nonce: AesCtrNonce = self.salt.compute_nonce::<C>();
+        let mut cipher = C::AesCtr128::new(&self.aes_key, NonceAndCounter::from_nonce(nonce));
+
+        let start_of_identity_token = section_header_without_length.len() - V1_IDENTITY_TOKEN_LEN;
+        let (format_and_salt_bytes, identity_token) =
+            section_header_without_length.split_at_mut(start_of_identity_token);
+        debug_assert!(identity_token.len() == V1_IDENTITY_TOKEN_LEN);
+        // format can be 1-2 bytes
+        debug_assert!((1 + self.salt.into().as_slice().len()
+            ..=2 + self.salt.into().as_slice().len())
+            .contains(&format_and_salt_bytes.len()));
+
+        let start_of_mic = remaining_contents.len() - Self::SUFFIX_LEN;
+        let (data_element_contents, mic) = remaining_contents.split_at_mut(start_of_mic);
+        debug_assert!(mic.len() == Self::SUFFIX_LEN);
+
+        // First encrypt the identity token
+        cipher.apply_keystream(identity_token);
+
+        // Now encrypt the rest of the ciphertext bytes that come after the section length byte
+        cipher.apply_keystream(data_element_contents);
+
+        // calculate MAC per the spec
+        let mut section_hmac = self.mic_hmac_key.build_hmac::<C>();
+        // svc uuid
+        section_hmac.update(NP_SVC_UUID.as_slice());
+        // adv header
+        section_hmac.update(&[VERSION_HEADER_V1]);
+        // section format and salt
+        section_hmac.update(format_and_salt_bytes);
+        // nonce
+        section_hmac.update(&nonce);
+        // encrypted identity token
+        section_hmac.update(identity_token);
+        // section len
+        section_hmac.update(&[section_len]);
+        // rest of the ciphertext
+        section_hmac.update(data_element_contents);
+
+        // write truncated MIC
+        mic.copy_from_slice(&section_hmac.finalize()[..SectionMic::CONTENTS_LEN]);
+    }
+
+    fn de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt {
+        self.salt.derive_de_salt(de_offset)
+    }
+}
+
+/// Behavior for salts used with MIC sections.
+pub trait MicSectionEncoderSalt: V1Salt {
+    /// The type of derived data produced, or `()` if no data can be derived.
+    type DerivedSalt;
+
+    /// Build the appropriate header for the type of salt used
+    fn header(&self, identity_token: &V1IdentityToken) -> SectionHeader;
+
+    /// Derive a DE salt at the specified offset.
+    fn derive_de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt;
+
+    /// Derive the AES key  suitable for this salt type
+    fn derive_aes_key<C: CryptoProvider>(&self, hkdf: &np_hkdf::NpKeySeedHkdf<C>)
+        -> aes::Aes128Key;
+
+    /// Derive the MIC HMAC key suitable for this salt type
+    fn derive_mic_hmac_key<C: CryptoProvider>(
+        &self,
+        hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+    ) -> np_hkdf::NpHmacSha256Key;
+}
+
+impl MicSectionEncoderSalt for ExtendedV1Salt {
+    type DerivedSalt = DeSalt;
+
+    fn header(&self, identity_token: &V1IdentityToken) -> SectionHeader {
+        SectionHeader::encrypted_mic_extended_salt(self, identity_token)
+    }
+
+    fn derive_de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt {
+        DeSalt { salt: *self, de_offset }
+    }
+
+    fn derive_aes_key<C: CryptoProvider>(
+        &self,
+        hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+    ) -> aes::Aes128Key {
+        hkdf.v1_mic_extended_salt_keys().aes_key()
+    }
+
+    fn derive_mic_hmac_key<C: CryptoProvider>(
+        &self,
+        hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+    ) -> np_hkdf::NpHmacSha256Key {
+        hkdf.v1_mic_extended_salt_keys().mic_hmac_key()
+    }
+}
+
+// TODO is this impl used?
+impl MicSectionEncoderSalt for ShortV1Salt {
+    type DerivedSalt = ();
+
+    fn header(&self, identity_token: &V1IdentityToken) -> SectionHeader {
+        SectionHeader::encrypted_mic_short_salt(*self, identity_token)
+    }
+
+    fn derive_de_salt(&self, _de_offset: DataElementOffset) -> Self::DerivedSalt {}
+
+    fn derive_aes_key<C: CryptoProvider>(
+        &self,
+        hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+    ) -> aes::Aes128Key {
+        hkdf.v1_mic_short_salt_keys().aes_key()
+    }
+
+    fn derive_mic_hmac_key<C: CryptoProvider>(
+        &self,
+        hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+    ) -> np_hkdf::NpHmacSha256Key {
+        hkdf.v1_mic_short_salt_keys().mic_hmac_key()
+    }
+}
+
+impl MicSectionEncoderSalt for MultiSalt {
+    type DerivedSalt = Option<DeSalt>;
+
+    fn header(&self, identity_token: &V1IdentityToken) -> SectionHeader {
+        match self {
+            MultiSalt::Short(s) => SectionHeader::encrypted_mic_short_salt(*s, identity_token),
+            MultiSalt::Extended(s) => SectionHeader::encrypted_mic_extended_salt(s, identity_token),
+        }
+    }
+
+    fn derive_de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt {
+        match self {
+            MultiSalt::Short(_) => None,
+            MultiSalt::Extended(s) => Some(DeSalt { salt: *s, de_offset }),
+        }
+    }
+
+    fn derive_aes_key<C: CryptoProvider>(
+        &self,
+        hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+    ) -> aes::Aes128Key {
+        match self {
+            MultiSalt::Short(_) => hkdf.v1_mic_short_salt_keys().aes_key(),
+            MultiSalt::Extended(_) => hkdf.v1_mic_extended_salt_keys().aes_key(),
+        }
+    }
+
+    fn derive_mic_hmac_key<C: CryptoProvider>(
+        &self,
+        hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+    ) -> np_hkdf::NpHmacSha256Key {
+        match self {
+            MultiSalt::Short(_) => hkdf.v1_mic_short_salt_keys().mic_hmac_key(),
+            MultiSalt::Extended(_) => hkdf.v1_mic_extended_salt_keys().mic_hmac_key(),
+        }
+    }
+}
diff --git a/nearby/presence/np_adv/src/extended/serialize/section/header.rs b/nearby/presence/np_adv/src/extended/serialize/section/header.rs
new file mode 100644
index 0000000..5e8b2e4
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/serialize/section/header.rs
@@ -0,0 +1,164 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::extended::salt::ShortV1Salt;
+use crate::extended::{
+    V1IdentityToken, 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,
+};
+use np_hkdf::v1_salt::ExtendedV1Salt;
+
+/// Format || salt || token
+pub(crate) const SECTION_HEADER_MAX_LEN: usize = 2 + 16 + 16;
+
+/// Assembles the header bytes for a section after the section length, before postprocessing by
+/// a [SectionEncoder](crate::extended::serialize::section::SectionEncoder).
+///
+/// Does not include the overall NP header byte that defines the adv version.
+pub struct SectionHeader {
+    /// The section header
+    /// Invariant: always has at least 1 byte
+    header_bytes: tinyvec::ArrayVec<[u8; SECTION_HEADER_MAX_LEN]>,
+}
+
+impl SectionHeader {
+    pub(crate) fn unencrypted() -> Self {
+        let mut header_bytes = tinyvec::ArrayVec::new();
+        header_bytes.push(V1_ENCODING_UNENCRYPTED);
+        Self { header_bytes }
+    }
+
+    pub(crate) fn encrypted_mic_short_salt(salt: ShortV1Salt, token: &V1IdentityToken) -> Self {
+        let mut header_bytes = tinyvec::ArrayVec::new();
+        header_bytes.push(V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN);
+        header_bytes.extend_from_slice(salt.bytes().as_slice());
+        header_bytes.extend_from_slice(token.bytes().as_slice());
+        Self { header_bytes }
+    }
+
+    pub(crate) fn encrypted_mic_extended_salt(
+        salt: &ExtendedV1Salt,
+        token: &V1IdentityToken,
+    ) -> Self {
+        let mut header_bytes = tinyvec::ArrayVec::new();
+        header_bytes.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+        header_bytes.extend_from_slice(salt.bytes().as_slice());
+        header_bytes.extend_from_slice(token.bytes().as_slice());
+        Self { header_bytes }
+    }
+
+    pub(crate) fn encrypted_signature_extended_salt(
+        salt: &ExtendedV1Salt,
+        token: &V1IdentityToken,
+    ) -> Self {
+        let mut header_bytes = tinyvec::ArrayVec::new();
+        header_bytes.push(V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN);
+        header_bytes.extend_from_slice(salt.bytes().as_slice());
+        header_bytes.extend_from_slice(token.bytes().as_slice());
+        Self { header_bytes }
+    }
+
+    pub(crate) fn as_slice(&self) -> &[u8] {
+        self.header_bytes.as_slice()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::extended::V1_IDENTITY_TOKEN_LEN;
+    use ldt_np_adv::V0_SALT_LEN;
+    use np_hkdf::v1_salt::EXTENDED_SALT_LEN;
+
+    const SHORT_SALT_BYTES: [u8; V0_SALT_LEN] = [0x10, 0x11];
+    const EXTENDED_SALT_BYTES: [u8; EXTENDED_SALT_LEN] = [
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E,
+        0x1F,
+    ];
+    const TOKEN_BYTES: [u8; V1_IDENTITY_TOKEN_LEN] = [
+        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E,
+        0x2F,
+    ];
+
+    #[test]
+    fn unencrypted_slice() {
+        assert_eq!(&[V1_ENCODING_UNENCRYPTED], SectionHeader::unencrypted().as_slice());
+    }
+
+    #[rustfmt::skip]
+    #[test]
+    fn encrypted_mic_short_salt_slice() {
+        assert_eq!(
+            &[
+                // format
+                0x01,
+                // salt
+                0x10, 0x11,
+                // token
+                0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,
+                0x2C, 0x2D, 0x2E, 0x2F,
+            ],
+            SectionHeader::encrypted_mic_short_salt(
+                SHORT_SALT_BYTES.into(),
+                &TOKEN_BYTES.into()
+            )
+                .as_slice()
+        );
+    }
+
+    #[rustfmt::skip]
+    #[test]
+    fn encrypted_mic_extended_salt_slice() {
+        assert_eq!(
+            &[
+                // format
+                0x02,
+                // salt
+                0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,
+                0x1C, 0x1D, 0x1E, 0x1F,
+                // token
+                0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,
+                0x2C, 0x2D, 0x2E, 0x2F,
+            ],
+            SectionHeader::encrypted_mic_extended_salt(
+                &EXTENDED_SALT_BYTES.into(),
+                &TOKEN_BYTES.into()
+            )
+                .as_slice()
+        );
+    }
+
+    #[rustfmt::skip]
+    #[test]
+    fn encrypted_sig_extended_salt_slice() {
+        assert_eq!(
+            &[
+                // format
+                0x03,
+                // salt
+                0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,
+                0x1C, 0x1D, 0x1E, 0x1F,
+                // token
+                0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,
+                0x2C, 0x2D, 0x2E, 0x2F,
+            ],
+            SectionHeader::encrypted_signature_extended_salt(
+                &EXTENDED_SALT_BYTES.into(),
+                &TOKEN_BYTES.into()
+            )
+                .as_slice()
+        );
+    }
+}
diff --git a/nearby/presence/np_adv/src/extended/serialize/section/mod.rs b/nearby/presence/np_adv/src/extended/serialize/section/mod.rs
new file mode 100644
index 0000000..8fb76ab
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/serialize/section/mod.rs
@@ -0,0 +1,204 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use array_view::ArrayView;
+use crypto_provider::CryptoProvider;
+use np_hkdf::v1_salt::DataElementOffset;
+use sink::Sink as _;
+
+use crate::extended::{
+    serialize::{
+        section::encoder::SectionEncoder, AdvBuilder, CapacityLimitedVec, WriteDataElement,
+    },
+    to_array_view, NP_ADV_MAX_SECTION_LEN,
+};
+
+pub(crate) mod encoder;
+pub(crate) mod header;
+
+/// Accumulates data elements and encodes them into a section.
+#[derive(Debug)]
+pub struct SectionBuilder<R: AsMut<AdvBuilder>, SE: SectionEncoder> {
+    /// The length of the header produced by `section_encoder`
+    pub(crate) header_len: usize,
+    /// Contains the section header, the identity-specified overhead, and any DEs added
+    pub(crate) section: CapacityLimitedVec<u8, { NP_ADV_MAX_SECTION_LEN }>,
+    pub(crate) section_encoder: SE,
+    /// mut ref-able to enforce only one active section builder at a time
+    pub(crate) adv_builder: R,
+    next_de_offset: DataElementOffset,
+}
+
+impl<'a, SE: SectionEncoder> SectionBuilder<&'a mut AdvBuilder, SE> {
+    /// Add this builder to the advertisement that created it.
+    pub fn add_to_advertisement<C: CryptoProvider>(self) {
+        let _ = self.add_to_advertisement_internal::<C>();
+    }
+}
+
+impl<SE: SectionEncoder> SectionBuilder<AdvBuilder, SE> {
+    /// Gets the 0-based index of the section currently under construction
+    /// in the context of the containing advertisement.
+    pub fn section_index(&self) -> usize {
+        self.adv_builder.section_count()
+    }
+    /// Add this builder to the advertisement that created it,
+    /// and returns the containing advertisement back to the caller.
+    pub fn add_to_advertisement<C: CryptoProvider>(self) -> AdvBuilder {
+        self.add_to_advertisement_internal::<C>()
+    }
+}
+
+impl<R: AsMut<AdvBuilder>, SE: SectionEncoder> SectionBuilder<R, SE> {
+    pub(crate) fn new(
+        header_len: usize,
+        section: CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>,
+        section_encoder: SE,
+        adv_builder: R,
+    ) -> Self {
+        Self {
+            header_len,
+            section,
+            section_encoder,
+            adv_builder,
+            next_de_offset: DataElementOffset::ZERO,
+        }
+    }
+
+    /// Add this builder to the advertisement that created it.
+    /// Returns the mut-refable to the advertisement builder
+    /// which the contents of this section builder were added to.
+    //TODO: make this fallible, if the section being added is invalid, right now it is possible to
+    // create invalid adv's that don't parse
+    fn add_to_advertisement_internal<C: CryptoProvider>(mut self) -> R {
+        let adv_builder = self.adv_builder.as_mut();
+        adv_builder.add_section(Self::build_section::<C>(
+            self.header_len,
+            self.section.into_inner(),
+            self.section_encoder,
+        ));
+        self.adv_builder
+    }
+
+    /// Gets the derived salt which will be employed for the next DE offset.
+    ///
+    /// Suitable for scenarios (like FFI) where a closure would be inappropriate
+    /// for DE construction, and interaction with the client is preferred.
+    pub fn next_de_salt(&self) -> SE::DerivedSalt {
+        self.section_encoder.de_salt(self.next_de_offset)
+    }
+
+    /// Add a data element to the section with a closure that returns a `Result`.
+    ///
+    /// The provided `build_de` closure will be invoked with the derived salt for this DE.
+    pub fn add_de_res<W: WriteDataElement, E, F: FnOnce(SE::DerivedSalt) -> Result<W, E>>(
+        &mut self,
+        build_de: F,
+    ) -> Result<(), AddDataElementError<E>> {
+        let writer = build_de(self.next_de_salt()).map_err(AddDataElementError::BuildDeError)?;
+
+        let orig_len = self.section.len();
+        // since we own the writer, and it's immutable, no race risk writing header w/ len then
+        // the contents as long as it's not simply an incorrect impl
+        let de_header = writer.de_header();
+        let content_len = self
+            .section
+            .try_extend_from_slice(de_header.serialize().as_slice())
+            .ok_or(AddDataElementError::InsufficientSectionSpace)
+            .and_then(|_| {
+                let after_header_len = self.section.len();
+                writer
+                    .write_de_contents(&mut self.section)
+                    .ok_or(AddDataElementError::InsufficientSectionSpace)
+                    .map(|_| self.section.len() - after_header_len)
+            })
+            .map_err(|e| {
+                // if anything went wrong, truncate any partial writes (e.g. just the header)
+                self.section.truncate(orig_len);
+                e
+            })?;
+
+        if content_len != usize::from(de_header.len.as_u8()) {
+            // TODO eliminate this possibility by keeping a 127-byte buffer
+            // to write DEs into, then calculating the written length, so the
+            // DE impl doesn't have to do it
+            panic!(
+                "Buggy WriteDataElement impl: header len {}, actual written len {}",
+                de_header.len.as_u8(),
+                content_len
+            );
+        }
+
+        self.next_de_offset = self.next_de_offset.incremented();
+
+        Ok(())
+    }
+
+    /// Add a data element to the section with a closure that returns the data element directly.
+    ///
+    /// The provided `build_de` closure will be invoked with the derived salt for this DE.
+    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)))
+    }
+
+    /// Convert a section builder's contents into an encoded section.
+    ///
+    /// `section_contents` must have size > 0.
+    ///
+    /// `header_len` is the length of the prefix of `section_contents` that has been populated
+    /// with the data returned from [SectionEncoder::header()] which does NOT include the length byte.
+    ///
+    /// Implemented without self to avoid partial-move issues.
+    pub(crate) fn build_section<C: CryptoProvider>(
+        header_len: usize,
+        mut section_contents: tinyvec::ArrayVec<[u8; NP_ADV_MAX_SECTION_LEN]>,
+        mut section_encoder: SE,
+    ) -> EncodedSection {
+        // there is space because the capacity for DEs was restricted to allow it
+        section_contents.resize(section_contents.len() + SE::SUFFIX_LEN, 0);
+
+        let (format_and_salt_and_identity_token, rest_of_contents) =
+            section_contents.split_at_mut(header_len);
+
+        let (section_length_byte, rest_of_contents) = rest_of_contents.split_at_mut(1);
+
+        let section_len = rest_of_contents.len().try_into().expect(
+            "section length will always fit into a u8 and has been validated by the section builder",
+        );
+        // set the section length byte
+        section_length_byte[0] = section_len;
+        section_encoder.postprocess::<C>(
+            format_and_salt_and_identity_token,
+            section_len,
+            rest_of_contents,
+        );
+
+        to_array_view(section_contents)
+    }
+}
+
+/// Errors for adding a DE to a section
+#[derive(Debug, PartialEq, Eq)]
+pub enum AddDataElementError<E> {
+    /// An error occurred when invoking the DE builder closure.
+    BuildDeError(E),
+    /// Too much data to fit into the section
+    InsufficientSectionSpace,
+}
+
+/// 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 2961fec..e1e90b7 100644
--- a/nearby/presence/np_adv/src/extended/serialize/section_tests.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/section_tests.rs
@@ -17,28 +17,46 @@
 extern crate std;
 
 use super::*;
-use crate::credential::{
-    v1::{SimpleSignedBroadcastCryptoMaterial, V1},
-    SimpleBroadcastCryptoMaterial,
+use crate::extended::{
+    V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN,
+    V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN, V1_ENCODING_UNENCRYPTED,
 };
-use crate::extended::data_elements::GenericDataElement;
-use crate::extended::serialize::AddSectionError::MaxSectionCountExceeded;
-use crypto_provider::aes::ctr::AES_CTR_NONCE_LEN;
+use crate::{
+    credential::v1::V1BroadcastCredential,
+    extended::{
+        data_elements::{GenericDataElement, GenericDataElementError},
+        deserialize::SectionMic,
+        salt::V1Salt,
+        section_signature_payload::SectionSignaturePayload,
+        serialize::AddSectionError::MaxSectionCountExceeded,
+        V1IdentityToken, V1_IDENTITY_TOKEN_LEN,
+    },
+    NP_SVC_UUID,
+};
+use crypto_provider::ed25519;
+use crypto_provider::ed25519::SIGNATURE_LENGTH;
+use crypto_provider::{
+    aes::ctr::{AesCtr, NonceAndCounter},
+    hmac::Hmac,
+    CryptoRng,
+};
 use crypto_provider_default::CryptoProviderImpl;
-use np_hkdf::v1_salt::V1Salt;
-use rand::rngs::StdRng;
-use rand::{prelude::SliceRandom as _, Rng as _, SeedableRng as _};
+use np_hkdf::v1_salt::EXTENDED_SALT_LEN;
+use np_hkdf::DerivedSectionKeys;
+use rand::{rngs::StdRng, Rng as _, SeedableRng as _};
 use std::{prelude::rust_2021::*, vec};
-use strum::IntoEnumIterator as _;
 
-type KeyPair = np_ed25519::KeyPair<CryptoProviderImpl>;
+type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
 
 #[test]
-fn public_identity_section_empty() {
+fn unencrypted_section_empty() {
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-    let section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+    let section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
 
-    assert_eq!(&[1_u8, 0x03], section_builder.into_section().as_slice());
+    assert_eq!(
+        &[V1_ENCODING_UNENCRYPTED, 0_u8],
+        section_builder.into_section::<CryptoProviderImpl>().as_slice()
+    );
 }
 
 #[test]
@@ -64,99 +82,81 @@
             })
             .collect::<Vec<_>>();
 
-        let metadata_key = rng.gen();
-        let metadata_key = MetadataKey(metadata_key);
+        let identity_token: V1IdentityToken = crypto_rng.gen();
         let key_seed = rng.gen();
-        let identity_type =
-            *EncryptedIdentityDataElementType::iter().collect::<Vec<_>>().choose(&mut rng).unwrap();
         let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
 
         let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
 
-        let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+        let broadcast_cred = V1BroadcastCredential::new(
+            key_seed,
+            identity_token,
+            ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
+        );
 
-        let mut section_builder = adv_builder
-            .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
-                &mut crypto_rng,
-                identity_type,
-                &broadcast_cm,
-            ))
-            .unwrap();
-        let section_salt: V1Salt<CryptoProviderImpl> = section_builder.section_encoder.salt.into();
+        let mut section_builder =
+            adv_builder
+                .section_builder(MicEncryptedSectionEncoder::<_>::new_random_salt::<
+                    CryptoProviderImpl,
+                >(&mut crypto_rng, &broadcast_cred))
+                .unwrap();
+        let section_salt = section_builder.section_encoder.salt;
 
         for de in extra_des.iter() {
             section_builder.add_de(|_| de).unwrap();
         }
 
-        // length 53: 19 for encryption info, 18 for identity, 16 for MIC
-        let section_length = 53
-            + extra_des
-                .iter()
-                .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_u8())
-                .sum::<u8>();
+        let section_length = mic_section_len(&extra_des);
 
-        let encryption_info = [
-            &[
-                0x91, 0x10, // header
-                0x00, // scheme (mic)
-            ],
-            section_salt.as_slice(),
-        ]
-        .concat();
+        let mut hmac = key_seed_hkdf
+            .v1_mic_extended_salt_keys()
+            .mic_hmac_key()
+            .build_hmac::<CryptoProviderImpl>();
+        let nonce = section_salt.compute_nonce::<CryptoProviderImpl>();
 
-        let identity_de_header =
-            DeHeader { len: 16_u8.try_into().unwrap(), de_type: identity_type.type_code() };
+        let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
+            &key_seed_hkdf.v1_mic_extended_salt_keys().aes_key(),
+            NonceAndCounter::from_nonce(nonce),
+        );
 
-        let mut hmac = np_hkdf::UnsignedSectionKeys::hmac_key(&key_seed_hkdf).build_hmac();
+        // encrypt identity token and de contents
+        let mut plaintext_identity_token = identity_token.as_slice().to_vec();
+        cipher.apply_keystream(&mut plaintext_identity_token);
+        let ct_identity_token = plaintext_identity_token;
+
+        let mut de_contents = Vec::new();
+        for de in extra_des {
+            de_contents.extend_from_slice(de.de_header().serialize().as_slice());
+            let _ = de.write_de_contents(&mut de_contents);
+        }
+        cipher.apply_keystream(&mut de_contents);
+
         // just to be sure, we'll construct our test hmac all in one update() call
         let mut hmac_input = vec![];
         hmac_input.extend_from_slice(NP_SVC_UUID.as_slice());
-        hmac_input.push(0b00100000); // v1
-
-        // section header
-        hmac_input.push(section_length);
-        hmac_input.extend_from_slice(&encryption_info);
-        let nonce = section_salt.derive::<{ AES_CTR_NONCE_LEN }>(Some(1.into())).unwrap();
+        hmac_input.push(VERSION_HEADER_V1);
+        hmac_input.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+        hmac_input.extend_from_slice(section_salt.as_slice());
         hmac_input.extend_from_slice(nonce.as_slice());
-
-        hmac_input.extend_from_slice(identity_de_header.serialize().as_slice());
-
-        let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
-            &np_hkdf::UnsignedSectionKeys::aes_key(&key_seed_hkdf),
-            NonceAndCounter::from_nonce(nonce),
-        );
-        let mut plaintext = metadata_key.0.as_slice().to_vec();
-
-        for de in extra_des {
-            plaintext.extend_from_slice(de.de_header().serialize().as_slice());
-            let _ = de.write_de_contents(&mut plaintext);
-        }
-
-        cipher.apply_keystream(&mut plaintext);
-        let ciphertext = plaintext;
-
-        hmac_input.extend_from_slice(&ciphertext);
-
+        hmac_input.extend_from_slice(&ct_identity_token);
+        hmac_input.push(section_length);
+        hmac_input.extend_from_slice(&de_contents);
         hmac.update(&hmac_input);
         let mic = hmac.finalize();
 
         let mut expected = vec![];
+        expected.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+        expected.extend_from_slice(section_salt.as_slice());
+        expected.extend_from_slice(&ct_identity_token);
         expected.push(section_length);
-        expected.extend_from_slice(&encryption_info);
-        expected.extend_from_slice(identity_de_header.serialize().as_slice());
-        expected.extend_from_slice(&ciphertext);
+        expected.extend_from_slice(&de_contents);
         expected.extend_from_slice(&mic[..16]);
 
-        assert_eq!(&expected, section_builder.into_section().as_slice());
+        assert_eq!(&expected, section_builder.into_section::<CryptoProviderImpl>().as_slice());
     }
 }
 
 #[test]
-fn signature_encrypted_identity_section_empty() {
-    do_signature_encrypted_identity_fixed_key_material_test::<DummyDataElement>(&[]);
-}
-
-#[test]
 fn signature_encrypted_identity_section_random_des() {
     let mut rng = StdRng::from_entropy();
     let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
@@ -164,29 +164,23 @@
     for _ in 0..1_000 {
         let num_des = rng.gen_range(1..=5);
 
-        let metadata_key = MetadataKey(rng.gen());
+        let identity_token = V1IdentityToken(rng.gen());
         let key_seed = rng.gen();
-        let identity_type =
-            *EncryptedIdentityDataElementType::iter().collect::<Vec<_>>().choose(&mut rng).unwrap();
         let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
 
         let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-        let key_pair = KeyPair::generate();
+        let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
 
-        let broadcast_cm = SimpleSignedBroadcastCryptoMaterial::new(
-            key_seed,
-            metadata_key,
-            key_pair.private_key(),
-        );
+        let broadcast_cred =
+            V1BroadcastCredential::new(key_seed, identity_token, private_key.clone());
 
         let mut section_builder = adv_builder
-            .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
+            .section_builder(SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
                 &mut crypto_rng,
-                identity_type,
-                &broadcast_cm,
+                &broadcast_cred,
             ))
             .unwrap();
-        let section_salt: V1Salt<CryptoProviderImpl> = section_builder.section_encoder.salt.into();
+        let section_salt = section_builder.section_encoder.salt;
 
         let extra_des = (0..num_des)
             .map(|_| {
@@ -200,32 +194,8 @@
             .filter_map(|de| section_builder.add_de(|_| de.clone()).ok().map(|_| de))
             .collect::<Vec<_>>();
 
-        // 19 for encryption info, 18 for identity, 64 for signature
-        let section_length = 19
-            + 18
-            + 64
-            + extra_des
-                .iter()
-                .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_u8())
-                .sum::<u8>();
-
-        let encryption_info = [
-            &[
-                0x91, 0x10, // header
-                0x08, // scheme (signature)
-            ],
-            section_salt.as_slice(),
-        ]
-        .concat();
-        let encryption_info: [u8; EncryptionInfo::TOTAL_DE_LEN] =
-            encryption_info.try_into().unwrap();
-
-        let identity_de_header =
-            DeHeader { len: 16_u8.try_into().unwrap(), de_type: identity_type.type_code() };
-        let identity_de_header: [u8; 2] =
-            identity_de_header.serialize().as_slice().try_into().unwrap();
-
-        let nonce = section_salt.derive::<{ AES_CTR_NONCE_LEN }>(Some(1.into())).unwrap();
+        let section_length = sig_section_len(&extra_des);
+        let nonce = section_salt.compute_nonce::<CryptoProviderImpl>();
 
         let mut section_body = Vec::new();
         for de in extra_des {
@@ -233,34 +203,41 @@
             let _ = de.write_de_contents(&mut section_body);
         }
 
-        let sig_payload = SectionSignaturePayload::from_deserialized_parts(
-            0x20,
-            section_length,
-            &encryption_info,
+        let mut section_header = vec![];
+        section_header
+            .extend_from_slice(&[V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN]);
+        section_header.extend_from_slice(section_salt.as_slice());
+        section_header.extend_from_slice(identity_token.as_slice());
+
+        let sig_payload = SectionSignaturePayload::new(
+            &[V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN],
+            section_salt.as_slice(),
             &nonce,
-            identity_de_header,
-            metadata_key,
+            identity_token.as_slice(),
+            section_length,
             &section_body,
         );
 
-        let mut plaintext = metadata_key.0.as_slice().to_vec();
-        plaintext.extend_from_slice(section_body.as_slice());
+        let mut plaintext = section_body.as_slice().to_vec();
+        plaintext
+            .extend_from_slice(&sig_payload.sign::<Ed25519ProviderImpl>(&private_key).to_bytes());
 
-        plaintext.extend_from_slice(&sig_payload.sign(&key_pair).to_bytes());
-
-        <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
-            &key_seed_hkdf.extended_signed_section_aes_key(),
+        let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
+            &key_seed_hkdf.v1_signature_keys().aes_key(),
             NonceAndCounter::from_nonce(nonce),
-        )
-        .apply_keystream(&mut plaintext);
+        );
+        let start_of_identity_token = section_header.len() - V1_IDENTITY_TOKEN_LEN;
+        cipher.apply_keystream(&mut section_header[start_of_identity_token..]);
+
+        cipher.apply_keystream(&mut plaintext);
         let ciphertext = plaintext;
 
-        let mut expected = vec![section_length];
-        expected.extend_from_slice(&encryption_info);
-        expected.extend_from_slice(&identity_de_header);
+        let mut expected = vec![];
+        expected.extend_from_slice(&section_header);
+        expected.push(section_length);
         expected.extend_from_slice(&ciphertext);
 
-        assert_eq!(&expected, section_builder.into_section().as_slice());
+        assert_eq!(&expected, section_builder.into_section::<CryptoProviderImpl>().as_slice());
     }
 }
 
@@ -272,23 +249,26 @@
 
     let key_seed = [22; 32];
     let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-    let metadata_key = MetadataKey([33; 16]);
+    let identity_token = V1IdentityToken([33; 16]);
 
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+    let broadcast_cred = V1BroadcastCredential::new(
+        key_seed,
+        identity_token,
+        ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
+    );
 
     let mut section_builder = adv_builder
-        .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
+        .section_builder(MicEncryptedSectionEncoder::<_>::new_random_salt::<CryptoProviderImpl>(
             &mut crypto_rng,
-            EncryptedIdentityDataElementType::Trusted,
-            &broadcast_cm,
+            &broadcast_cred,
         ))
         .unwrap();
-    let salt: V1Salt<CryptoProviderImpl> = section_builder.section_encoder.salt.into();
+    let salt = section_builder.section_encoder.salt;
 
     section_builder
         .add_de(|de_salt| DummyDataElement {
             de_type: 100_u32.into(),
-            data: de_salt.derive::<100>().unwrap().to_vec(),
+            data: de_salt.derive::<100, CryptoProviderImpl>().unwrap().to_vec(),
         })
         .unwrap();
 
@@ -298,7 +278,7 @@
         section_builder
             .add_de(|de_salt| DummyDataElement {
                 de_type: 101_u32.into(),
-                data: de_salt.derive::<100>().unwrap().to_vec(),
+                data: de_salt.derive::<100, CryptoProviderImpl>().unwrap().to_vec(),
             })
             .unwrap_err()
     );
@@ -306,35 +286,38 @@
     section_builder
         .add_de(|de_salt| DummyDataElement {
             de_type: 102_u32.into(),
-            data: de_salt.derive::<10>().unwrap().to_vec(),
+            data: de_salt.derive::<10, CryptoProviderImpl>().unwrap().to_vec(),
         })
         .unwrap();
 
-    section_builder.add_to_advertisement();
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
 
     let mut expected = vec![];
-    // metadata key
-    expected.extend_from_slice(&metadata_key.0);
+    // identity token
+    expected.extend_from_slice(&identity_token.0);
+    // section len
+    expected.push(2 + 100 + 2 + 10u8 + u8::try_from(SectionMic::CONTENTS_LEN).unwrap());
     // de header
     expected.extend_from_slice(&[0x80 + 100, 100]);
-    // section 0 de 2
-    expected.extend_from_slice(&salt.derive::<100>(Some(2.into())).unwrap());
+    // section 0 de 0
+    expected.extend_from_slice(&salt.derive::<100, CryptoProviderImpl>(Some(0.into())).unwrap());
     // de header
     expected.extend_from_slice(&[0x80 + 10, 102]);
-    // section 0 de 3
-    expected.extend_from_slice(&salt.derive::<10>(Some(3.into())).unwrap());
+    // section 0 de 1
+    expected.extend_from_slice(&salt.derive::<10, CryptoProviderImpl>(Some(1.into())).unwrap());
 
     let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
-        &np_hkdf::UnsignedSectionKeys::aes_key(&key_seed_hkdf),
-        NonceAndCounter::from_nonce(salt.derive(Some(1.into())).unwrap()),
+        &key_seed_hkdf.v1_mic_extended_salt_keys().aes_key(),
+        NonceAndCounter::from_nonce(salt.compute_nonce::<CryptoProviderImpl>()),
     );
 
-    cipher.apply_keystream(&mut expected);
+    cipher.apply_keystream(&mut expected[..V1_IDENTITY_TOKEN_LEN]);
+    cipher.apply_keystream(&mut expected[V1_IDENTITY_TOKEN_LEN + 1..]);
 
     let adv_bytes = adv_builder.into_advertisement();
     // ignoring the MIC, etc, since that's tested elsewhere
-    let ciphertext_end = adv_bytes.as_slice().len() - 16;
-    assert_eq!(&expected, &adv_bytes.as_slice()[1 + 1 + 19 + 2..ciphertext_end])
+    let ciphertext_end = adv_bytes.as_slice().len() - SectionMic::CONTENTS_LEN;
+    assert_eq!(&expected, &adv_bytes.as_slice()[1 + 1 + EXTENDED_SALT_LEN..ciphertext_end])
 }
 
 #[test]
@@ -344,9 +327,10 @@
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
 
     // fill up space to produce desired capacity
-    let mut section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
-    // leave space for adv header, 1 section headers, 1 section identity and fill almost full
-    fill_section_builder(BLE_ADV_SVC_CONTENT_LEN - 1 - 1 - 1 - 1, &mut section_builder);
+    let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
+    // leave space for adv header, 1 section len, 1 section header and 1 extra byte
+    fill_section_builder(BLE_5_ADV_SVC_MAX_CONTENT_LEN - 1 - 1 - 1 - 1, &mut section_builder)
+        .unwrap();
     assert_eq!(1, section_builder.section.capacity() - section_builder.section.len(), "capacity: ");
 
     // can't add a 2 byte DE
@@ -368,23 +352,26 @@
 
     let key_seed = [22; 32];
     let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-    let metadata_key = MetadataKey([33; 16]);
+    let identity_token = V1IdentityToken([33; 16]);
 
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+    let broadcast_cred = V1BroadcastCredential::new(
+        key_seed,
+        identity_token,
+        ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
+    );
 
     let mut section_builder = adv_builder
-        .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
+        .section_builder(MicEncryptedSectionEncoder::<_>::new_random_salt::<CryptoProviderImpl>(
             &mut crypto_rng,
-            EncryptedIdentityDataElementType::Trusted,
-            &broadcast_cm,
+            &broadcast_cred,
         ))
         .unwrap();
-    let salt: V1Salt<CryptoProviderImpl> = section_builder.section_encoder.salt.into();
+    let salt = section_builder.section_encoder.salt;
 
     section_builder
         .add_de(|de_salt| DummyDataElement {
             de_type: 100_u32.into(),
-            data: de_salt.derive::<100>().unwrap().to_vec(),
+            data: de_salt.derive::<100, CryptoProviderImpl>().unwrap().to_vec(),
         })
         .unwrap();
 
@@ -396,35 +383,38 @@
     section_builder
         .add_de(|de_salt| DummyDataElement {
             de_type: 103_u32.into(),
-            data: de_salt.derive::<10>().unwrap().to_vec(),
+            data: de_salt.derive::<10, CryptoProviderImpl>().unwrap().to_vec(),
         })
         .unwrap();
 
-    section_builder.add_to_advertisement();
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
 
     let mut expected = vec![];
-    // metadata key
-    expected.extend_from_slice(&metadata_key.0);
+    // identity_token
+    expected.extend_from_slice(identity_token.as_slice());
+    //section len
+    expected.push(2 + 100 + 2 + 10 + u8::try_from(SectionMic::CONTENTS_LEN).unwrap());
     // de header
     expected.extend_from_slice(&[0x80 + 100, 100]);
-    // section 0 de 2
-    expected.extend_from_slice(&salt.derive::<100>(Some(2.into())).unwrap());
+    // section 0 de 0
+    expected.extend_from_slice(&salt.derive::<100, CryptoProviderImpl>(Some(0.into())).unwrap());
     // de header
     expected.extend_from_slice(&[0x80 + 10, 103]);
-    // section 0 de 3
-    expected.extend_from_slice(&salt.derive::<10>(Some(3.into())).unwrap());
+    // section 0 de 1
+    expected.extend_from_slice(&salt.derive::<10, CryptoProviderImpl>(Some(1.into())).unwrap());
 
     let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
-        &np_hkdf::UnsignedSectionKeys::aes_key(&key_seed_hkdf),
-        NonceAndCounter::from_nonce(salt.derive(Some(1.into())).unwrap()),
+        &key_seed_hkdf.v1_mic_extended_salt_keys().aes_key(),
+        NonceAndCounter::from_nonce(salt.compute_nonce::<CryptoProviderImpl>()),
     );
 
-    cipher.apply_keystream(&mut expected);
+    cipher.apply_keystream(&mut expected[..V1_IDENTITY_TOKEN_LEN]);
+    cipher.apply_keystream(&mut expected[V1_IDENTITY_TOKEN_LEN + 1..]);
 
     let adv_bytes = adv_builder.into_advertisement();
     // ignoring the MIC, etc, since that's tested elsewhere
-    let ciphertext_end = adv_bytes.as_slice().len() - 16;
-    assert_eq!(&expected, &adv_bytes.as_slice()[1 + 1 + 19 + 2..ciphertext_end])
+    let ciphertext_end = adv_bytes.as_slice().len() - SectionMic::CONTENTS_LEN;
+    assert_eq!(&expected, &adv_bytes.as_slice()[1 + 1 + EXTENDED_SALT_LEN..ciphertext_end])
 }
 
 #[test]
@@ -435,57 +425,63 @@
 
     let key_seed = [22; 32];
     let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-    let metadata_key = MetadataKey([33; 16]);
+    let identity_token = V1IdentityToken([33; 16]);
 
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+    let broadcast_cred = V1BroadcastCredential::new(
+        key_seed,
+        identity_token,
+        ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
+    );
 
     let mut section_builder = adv_builder
-        .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
+        .section_builder(MicEncryptedSectionEncoder::<_>::new_random_salt::<CryptoProviderImpl>(
             &mut crypto_rng,
-            EncryptedIdentityDataElementType::Trusted,
-            &broadcast_cm,
+            &broadcast_cred,
         ))
         .unwrap();
-    let salt: V1Salt<CryptoProviderImpl> = section_builder.section_encoder.salt.into();
+    let salt = section_builder.section_encoder.salt;
 
     section_builder
         .add_de(|de_salt| DummyDataElement {
             de_type: 64_u32.into(),
-            data: de_salt.derive::<16>().unwrap().to_vec(),
+            data: de_salt.derive::<16, CryptoProviderImpl>().unwrap().to_vec(),
         })
         .unwrap();
     section_builder
         .add_de(|de_salt| DummyDataElement {
             de_type: 65_u32.into(),
-            data: de_salt.derive::<16>().unwrap().to_vec(),
+            data: de_salt.derive::<16, CryptoProviderImpl>().unwrap().to_vec(),
         })
         .unwrap();
 
-    section_builder.add_to_advertisement();
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
 
     let mut expected = vec![];
-    // metadata key
-    expected.extend_from_slice(&metadata_key.0);
+    // identity_token
+    expected.extend_from_slice(identity_token.as_slice());
+    // section len, 2 des of 18 bytes each + section mic length
+    expected.push((18 * 2u8) + u8::try_from(SectionMic::CONTENTS_LEN).unwrap());
     // de header
     expected.extend_from_slice(&[0x90, 0x40]);
-    // section 0 de 2
-    expected.extend_from_slice(&salt.derive::<16>(Some(2.into())).unwrap());
+    // section 0 de 0
+    expected.extend_from_slice(&salt.derive::<16, CryptoProviderImpl>(Some(0.into())).unwrap());
     // de header
     expected.extend_from_slice(&[0x90, 0x41]);
-    // section 0 de 3
-    expected.extend_from_slice(&salt.derive::<16>(Some(3.into())).unwrap());
+    // section 0 de 1
+    expected.extend_from_slice(&salt.derive::<16, CryptoProviderImpl>(Some(1.into())).unwrap());
 
     let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
-        &np_hkdf::UnsignedSectionKeys::aes_key(&key_seed_hkdf),
-        NonceAndCounter::from_nonce(salt.derive(Some(1.into())).unwrap()),
+        &key_seed_hkdf.v1_mic_extended_salt_keys().aes_key(),
+        NonceAndCounter::from_nonce(salt.derive::<12, CryptoProviderImpl>(None).unwrap()),
     );
 
-    cipher.apply_keystream(&mut expected);
+    cipher.apply_keystream(&mut expected[..V1_IDENTITY_TOKEN_LEN]);
+    cipher.apply_keystream(&mut expected[V1_IDENTITY_TOKEN_LEN + 1..]);
 
     let adv_bytes = adv_builder.into_advertisement();
     // ignoring the MIC, etc, since that's tested elsewhere
     let ciphertext_end = adv_bytes.as_slice().len() - 16;
-    assert_eq!(&expected, &adv_bytes.as_slice()[1 + 1 + 19 + 2..ciphertext_end])
+    assert_eq!(&expected, &adv_bytes.as_slice()[1 + 1 + 16..ciphertext_end])
 }
 
 #[test]
@@ -496,60 +492,62 @@
 
     let key_seed = [22; 32];
     let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-    let metadata_key = MetadataKey([33; 16]);
-    let key_pair = KeyPair::generate();
+    let identity_token = V1IdentityToken([33; 16]);
+    let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
 
-    let broadcast_cm =
-        SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
+    let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token, private_key.clone());
 
     let mut section_builder = adv_builder
-        .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
+        .section_builder(SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
             &mut crypto_rng,
-            EncryptedIdentityDataElementType::Trusted,
-            &broadcast_cm,
+            &broadcast_cred,
         ))
         .unwrap();
-    let salt: V1Salt<CryptoProviderImpl> = section_builder.section_encoder.salt.into();
+    let salt = section_builder.section_encoder.salt;
 
     section_builder
         .add_de(|de_salt| DummyDataElement {
             de_type: 64_u32.into(),
-            data: de_salt.derive::<16>().unwrap().to_vec(),
+            data: de_salt.derive::<16, CryptoProviderImpl>().unwrap().to_vec(),
         })
         .unwrap();
     section_builder
         .add_de(|de_salt| DummyDataElement {
             de_type: 65_u32.into(),
-            data: de_salt.derive::<16>().unwrap().to_vec(),
+            data: de_salt.derive::<16, CryptoProviderImpl>().unwrap().to_vec(),
         })
         .unwrap();
 
-    section_builder.add_to_advertisement();
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
 
     let mut expected = vec![];
-    // metadata key
-    expected.extend_from_slice(&metadata_key.0);
+    // identity token
+    expected.extend_from_slice(&identity_token.0);
+    //len
+    expected.push((18 * 2) + u8::try_from(SIGNATURE_LENGTH).unwrap());
     // de header
     expected.extend_from_slice(&[0x90, 0x40]);
-    // section 0 de 2
-    expected.extend_from_slice(&salt.derive::<16>(Some(2.into())).unwrap());
+    // section 0 de 0
+    expected.extend_from_slice(&salt.derive::<16, CryptoProviderImpl>(Some(0.into())).unwrap());
     // de header
     expected.extend_from_slice(&[0x90, 0x41]);
-    // section 0 de 3
-    expected.extend_from_slice(&salt.derive::<16>(Some(3.into())).unwrap());
+    // section 0 de 1
+    expected.extend_from_slice(&salt.derive::<16, CryptoProviderImpl>(Some(1.into())).unwrap());
 
-    <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
-        &key_seed_hkdf.extended_signed_section_aes_key(),
-        NonceAndCounter::from_nonce(salt.derive(Some(1.into())).unwrap()),
-    )
-    .apply_keystream(&mut expected);
+    let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
+        &key_seed_hkdf.v1_signature_keys().aes_key(),
+        NonceAndCounter::from_nonce(salt.derive::<12, CryptoProviderImpl>(None).unwrap()),
+    );
+
+    cipher.apply_keystream(&mut expected[..V1_IDENTITY_TOKEN_LEN]);
+    cipher.apply_keystream(&mut expected[V1_IDENTITY_TOKEN_LEN + 1..]);
 
     let adv_bytes = adv_builder.into_advertisement();
     // ignoring the signature since that's tested elsewhere
     assert_eq!(
         &expected,
-        // adv header + salt + section header + encryption info + identity header
-        &adv_bytes.as_slice()[1 + 1 + 19 + 2..adv_bytes.as_slice().len() - 64]
+        // skip adv header + section header + salt
+        &adv_bytes.as_slice()[1 + 1 + 16..adv_bytes.as_slice().len() - 64]
     )
 }
 
@@ -560,17 +558,15 @@
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
 
     let key_seed = [22; 32];
-    let metadata_key = MetadataKey([33; 16]);
-    let key_pair = KeyPair::generate();
+    let metadata_key = V1IdentityToken([33; 16]);
+    let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
 
-    let broadcast_cm =
-        SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
+    let broadcast_cred = V1BroadcastCredential::new(key_seed, metadata_key, private_key.clone());
 
     let mut section_builder = adv_builder
-        .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
+        .section_builder(SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
             &mut crypto_rng,
-            EncryptedIdentityDataElementType::Trusted,
-            &broadcast_cm,
+            &broadcast_cred,
         ))
         .unwrap();
 
@@ -588,7 +584,7 @@
         section_builder
             .add_de(|_| DummyDataElement {
                 de_type: 100_u32.into(),
-                data: vec![0; max_total_de_len - 100 - 1]
+                data: vec![0; max_total_de_len - 100 - 1],
             })
             .unwrap_err()
     );
@@ -599,7 +595,7 @@
         section_builder
             .add_de(|_| DummyDataElement {
                 de_type: 100_u32.into(),
-                data: vec![0; max_total_de_len - 100 - 2]
+                data: vec![0; max_total_de_len - 100 - 2],
             })
             .unwrap_err()
     );
@@ -609,154 +605,142 @@
 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 {
-        let mut section_builder =
-            adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+        let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
         section_builder
             .add_de(|_| DummyDataElement { de_type: 100_u32.into(), data: vec![0; 98] })
             .unwrap();
-        section_builder.add_to_advertisement();
+        section_builder.add_to_advertisement::<CryptoProviderImpl>();
     }
     assert_eq!(
-        adv_builder.section_builder(PublicSectionEncoder::default()).unwrap_err(),
+        adv_builder.section_builder(UnencryptedSectionEncoder).unwrap_err(),
         MaxSectionCountExceeded
     );
 }
 
 fn do_mic_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(extra_des: &[W]) {
-    let metadata_key = MetadataKey([1; 16]);
+    let identity_token = V1IdentityToken([1; 16]);
     let key_seed = [2; 32];
     let adv_header_byte = 0b00100000;
-    let raw_salt = [3; 16];
-    let section_salt: V1Salt<CryptoProviderImpl> = raw_salt.into();
-    let identity_type = EncryptedIdentityDataElementType::Private;
+    let section_salt: ExtendedV1Salt = [3; 16].into();
     let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
 
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+    let broadcast_cred = V1BroadcastCredential::new(
+        key_seed,
+        identity_token,
+        ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
+    );
 
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
     let mut section_builder = adv_builder
-        .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new(
-            identity_type,
-            RawV1Salt(raw_salt),
-            &broadcast_cm,
+        .section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
+            section_salt,
+            &broadcast_cred,
         ))
         .unwrap();
-
     for de in extra_des {
         section_builder.add_de(|_| de).unwrap();
     }
 
-    // length 53: 19 for encryption info, 18 for identity, 16 for MIC
-    let section_length = 53
-        + extra_des
-            .iter()
-            .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_u8())
-            .sum::<u8>();
+    // now construct expected bytes
+    // mic length + length of des
+    let section_length = mic_section_len(extra_des);
 
-    let encryption_info = [
-        &[
-            0x91, 0x10, // header
-            0x00, // scheme (mic)
-        ],
-        section_salt.as_slice(),
-    ]
-    .concat();
+    let mut hmac =
+        key_seed_hkdf.v1_mic_extended_salt_keys().mic_hmac_key().build_hmac::<CryptoProviderImpl>();
+    let nonce = section_salt.compute_nonce::<CryptoProviderImpl>();
 
-    let identity_de_header =
-        DeHeader { len: 16_u8.try_into().unwrap(), de_type: identity_type.type_code() };
+    let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
+        &key_seed_hkdf.v1_mic_extended_salt_keys().aes_key(),
+        NonceAndCounter::from_nonce(nonce),
+    );
 
-    let mut hmac = np_hkdf::UnsignedSectionKeys::hmac_key(&key_seed_hkdf).build_hmac();
+    // encrypt identity token and de contents
+    let mut plaintext_identity_token = identity_token.as_slice().to_vec();
+    cipher.apply_keystream(&mut plaintext_identity_token);
+    let ct_identity_token = plaintext_identity_token;
+
+    let mut de_contents = Vec::new();
+    for de in extra_des {
+        de_contents.extend_from_slice(de.de_header().serialize().as_slice());
+        let _ = de.write_de_contents(&mut de_contents);
+    }
+    cipher.apply_keystream(&mut de_contents);
+
     // just to be sure, we'll construct our test hmac all in one update() call
     let mut hmac_input = vec![];
     hmac_input.extend_from_slice(NP_SVC_UUID.as_slice());
     hmac_input.push(adv_header_byte);
-    // section header
-    hmac_input.push(section_length);
-    hmac_input.extend_from_slice(&encryption_info);
-    let nonce = section_salt.derive::<{ AES_CTR_NONCE_LEN }>(Some(1.into())).unwrap();
+    hmac_input.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+    hmac_input.extend_from_slice(section_salt.as_slice());
     hmac_input.extend_from_slice(nonce.as_slice());
-
-    hmac_input.extend_from_slice(identity_de_header.serialize().as_slice());
-
-    let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
-        &np_hkdf::UnsignedSectionKeys::aes_key(&key_seed_hkdf),
-        NonceAndCounter::from_nonce(nonce),
-    );
-    let mut plaintext = metadata_key.0.as_slice().to_vec();
-
-    for de in extra_des {
-        plaintext.extend_from_slice(de.de_header().serialize().as_slice());
-        let _ = de.write_de_contents(&mut plaintext);
-    }
-
-    cipher.apply_keystream(&mut plaintext);
-    let ciphertext = plaintext;
-
-    hmac_input.extend_from_slice(&ciphertext);
-
+    hmac_input.extend_from_slice(&ct_identity_token);
+    hmac_input.push(section_length);
+    hmac_input.extend_from_slice(&de_contents);
     hmac.update(&hmac_input);
     let mic = hmac.finalize();
 
     let mut expected = vec![];
+    expected.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+    expected.extend_from_slice(section_salt.as_slice());
+    expected.extend_from_slice(&ct_identity_token);
     expected.push(section_length);
-    expected.extend_from_slice(&encryption_info);
-    expected.extend_from_slice(identity_de_header.serialize().as_slice());
-    expected.extend_from_slice(&ciphertext);
+    expected.extend_from_slice(&de_contents);
     expected.extend_from_slice(&mic[..16]);
 
-    assert_eq!(&expected, section_builder.into_section().as_slice());
+    assert_eq!(&expected, section_builder.into_section::<CryptoProviderImpl>().as_slice());
 }
 
-fn do_signature_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(extra_des: &[W]) {
-    let metadata_key = MetadataKey([1; 16]);
-    let key_seed = [2; 32];
-    let adv_header_byte = 0b00100000;
-    let raw_salt = [3; 16];
-    let section_salt: V1Salt<CryptoProviderImpl> = raw_salt.into();
-    let identity_type = EncryptedIdentityDataElementType::Private;
-    let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-    let key_pair = KeyPair::generate();
-
-    let broadcast_cm =
-        SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
-
-    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
-    let mut section_builder = adv_builder
-        .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new(
-            identity_type,
-            RawV1Salt(raw_salt),
-            &broadcast_cm,
-        ))
-        .unwrap();
-
-    for de in extra_des {
-        section_builder.add_de(|_| de).unwrap();
-    }
-
-    // 19 for encryption info, 18 for identity, 64 for sig
-    let section_length = 19
-        + 18
-        + 64
+/// Returns the length of a mic section containing `extra_des`
+fn mic_section_len<W: WriteDataElement>(extra_des: &[W]) -> u8 {
+    u8::try_from(SectionMic::CONTENTS_LEN).unwrap()
         + extra_des
             .iter()
             .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_u8())
-            .sum::<u8>();
+            .sum::<u8>()
+}
 
-    let encryption_info = [
-        &[
-            0x91, 0x10, // header
-            0x08, // scheme (signature)
-        ],
-        section_salt.as_slice(),
-    ]
-    .concat();
-    let encryption_info: [u8; EncryptionInfo::TOTAL_DE_LEN] = encryption_info.try_into().unwrap();
+/// Returns the length of a signed section containing `extra_des`
+fn sig_section_len<W: WriteDataElement>(extra_des: &[W]) -> u8 {
+    u8::try_from(crypto_provider::ed25519::SIGNATURE_LENGTH).unwrap()
+        + extra_des
+            .iter()
+            .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_u8())
+            .sum::<u8>()
+}
 
-    let identity_de_header =
-        DeHeader { len: 16_u8.try_into().unwrap(), de_type: identity_type.type_code() };
-    let identity_de_header: [u8; 2] = identity_de_header.serialize().as_slice().try_into().unwrap();
+#[test]
+fn signature_encrypted_identity_section_empty() {
+    do_signature_encrypted_identity_fixed_key_material_test::<DummyDataElement>(&[]);
+}
+
+fn do_signature_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(extra_des: &[W]) {
+    // input fixed credential data used to encode the adv
+    let identity_token = V1IdentityToken::from([1; 16]);
+    let key_seed = [2; 32];
+    let section_salt: ExtendedV1Salt = [3; 16].into();
+    let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+    let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
+    let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token, private_key.clone());
+
+    // Build an adv given the provided DEs in extra_des
+    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+    let mut section_builder = adv_builder
+        .section_builder(SignedEncryptedSectionEncoder::new::<CryptoProviderImpl>(
+            section_salt,
+            &broadcast_cred,
+        ))
+        .unwrap();
+    for de in extra_des {
+        section_builder.add_de(|_| de).unwrap();
+    }
+    let section = section_builder.into_section::<CryptoProviderImpl>();
+
+    // now manually construct the expected output
+    let format = [V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN];
+    let salt_bytes = section_salt.as_slice();
+    let identity_token_bytes = identity_token.as_slice();
+    // len of de contents + signature
+    let section_length = sig_section_len(extra_des);
 
     let mut section_body = Vec::new();
     for de in extra_des {
@@ -764,49 +748,55 @@
         let _ = de.write_de_contents(&mut section_body);
     }
 
-    let nonce = section_salt.derive(Some(1.into())).unwrap();
+    let nonce = section_salt.compute_nonce::<CryptoProviderImpl>();
 
-    let sig_payload = SectionSignaturePayload::from_deserialized_parts(
-        adv_header_byte,
-        section_length,
-        &encryption_info,
+    let sig_payload = SectionSignaturePayload::new(
+        format.as_slice(),
+        salt_bytes,
         &nonce,
-        identity_de_header,
-        metadata_key,
+        identity_token.as_slice(),
+        section_length,
         &section_body,
     );
+    let sig = sig_payload.sign::<Ed25519ProviderImpl>(&private_key).to_bytes();
 
-    let mut plaintext = metadata_key.0.as_slice().to_vec();
-    plaintext.extend_from_slice(section_body.as_slice());
-    plaintext.extend_from_slice(&sig_payload.sign(&key_pair).to_bytes());
-
-    <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
-        &key_seed_hkdf.extended_signed_section_aes_key(),
+    let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
+        &key_seed_hkdf.v1_signature_keys().aes_key(),
         NonceAndCounter::from_nonce(nonce),
-    )
-    .apply_keystream(&mut plaintext);
-    let ciphertext = plaintext;
+    );
 
-    let mut expected = vec![section_length];
+    let mut expected = vec![];
+    expected.extend_from_slice(&format);
+    expected.extend_from_slice(salt_bytes);
 
-    expected.extend_from_slice(&encryption_info);
-    expected.extend_from_slice(&identity_de_header);
-    expected.extend_from_slice(&ciphertext);
+    let mut ct_identity_token = Vec::new();
+    ct_identity_token.extend_from_slice(identity_token_bytes);
+    cipher.apply_keystream(&mut ct_identity_token);
 
-    assert_eq!(&expected, section_builder.into_section().as_slice());
+    expected.extend_from_slice(&ct_identity_token);
+    expected.push(section_length);
+
+    let mut remaining_ct = Vec::new();
+    remaining_ct.extend_from_slice(&section_body);
+    remaining_ct.extend_from_slice(&sig);
+    cipher.apply_keystream(&mut remaining_ct);
+
+    expected.extend_from_slice(&remaining_ct);
+
+    assert_eq!(&expected, section.as_slice());
 }
 
-/// Write `section_contents_len` bytes of DE and header into `section_builder`
+/// Write `section_contents_len` bytes of DE into `section_builder`
 pub(crate) fn fill_section_builder<I: SectionEncoder>(
     section_contents_len: usize,
     section_builder: &mut SectionBuilder<&mut AdvBuilder, I>,
-) {
+) -> Result<(), AddDataElementError<GenericDataElementError>> {
+    let original_len = section_builder.section.len();
     // DEs can only go up to 127, so we'll need multiple for long sections
     for _ in 0..(section_contents_len / 100) {
         let de_contents = vec![0x33; 98];
         section_builder
-            .add_de_res(|_| GenericDataElement::try_from(100_u32.into(), &de_contents))
-            .unwrap();
+            .add_de_res(|_| GenericDataElement::try_from(100_u32.into(), &de_contents))?;
     }
 
     let remainder_len = section_contents_len % 100;
@@ -816,25 +806,24 @@
         }
         1 => {
             // 1 byte header
-            section_builder
-                .add_de_res(|_| GenericDataElement::try_from(3_u32.into(), &[]))
-                .unwrap();
+            section_builder.add_de_res(|_| GenericDataElement::try_from(3_u32.into(), &[]))?;
         }
         2 => {
             // 2 byte header
-            section_builder
-                .add_de_res(|_| GenericDataElement::try_from(100_u32.into(), &[]))
-                .unwrap();
+            section_builder.add_de_res(|_| GenericDataElement::try_from(100_u32.into(), &[]))?;
         }
         _ => {
             // 2 byte header + contents as needed
-            // leave room for section and DE headers
+            // leave room for section length, section header, and DE headers
             let de_contents = vec![0x44; remainder_len - 2];
             section_builder
-                .add_de_res(|_| GenericDataElement::try_from(100_u32.into(), &de_contents))
-                .unwrap();
+                .add_de_res(|_| GenericDataElement::try_from(100_u32.into(), &de_contents))?;
         }
     }
+
+    assert_eq!(section_contents_len, section_builder.section.len() - original_len);
+
+    Ok(())
 }
 
 #[derive(Clone)]
@@ -854,17 +843,12 @@
 }
 
 pub(crate) trait SectionBuilderExt {
-    fn into_section(self) -> EncodedSection;
+    fn into_section<C: CryptoProvider>(self) -> EncodedSection;
 }
 
 impl<R: AsMut<AdvBuilder>, I: SectionEncoder> SectionBuilderExt for SectionBuilder<R, I> {
     /// Convenience method for tests
-    fn into_section(mut self) -> EncodedSection {
-        let adv_builder_header_byte = self.adv_builder.as_mut().header_byte();
-        Self::build_section(
-            adv_builder_header_byte,
-            self.section.into_inner(),
-            self.section_encoder,
-        )
+    fn into_section<C: CryptoProvider>(self) -> EncodedSection {
+        Self::build_section::<C>(self.header_len, self.section.into_inner(), self.section_encoder)
     }
 }
diff --git a/nearby/presence/np_adv/src/extended/serialize/test_vectors.rs b/nearby/presence/np_adv/src/extended/serialize/test_vectors.rs
index 0f5b22a..829123c 100644
--- a/nearby/presence/np_adv/src/extended/serialize/test_vectors.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/test_vectors.rs
@@ -16,31 +16,31 @@
 
 extern crate std;
 
-use crate::extended::deserialize::RawV1Salt;
+use crate::credential::v1::V1BroadcastCredential;
+use crate::extended::de_type::DeType;
+use crate::extended::salt::V1Salt;
 use crate::extended::serialize::AdvertisementType;
-use crate::{
-    credential::{v1::V1, SimpleBroadcastCryptoMaterial},
-    de_type::EncryptedIdentityDataElementType,
-    extended::serialize::{
-        section_tests::{DummyDataElement, SectionBuilderExt},
-        AdvBuilder, MicEncryptedSectionEncoder,
-    },
-    MetadataKey,
+use crate::extended::serialize::{
+    section_tests::{DummyDataElement, SectionBuilderExt},
+    AdvBuilder, MicEncryptedSectionEncoder,
 };
+use crate::extended::V1IdentityToken;
+use alloc::format;
 use anyhow::anyhow;
-use crypto_provider::{aes::ctr::AES_CTR_NONCE_LEN, aes::AesKey};
+use crypto_provider::{aes::ctr::AES_CTR_NONCE_LEN, aes::AesKey, ed25519, CryptoProvider};
 use crypto_provider_default::CryptoProviderImpl;
-use np_hkdf::v1_salt;
-use rand_ext::rand::{prelude::SliceRandom as _, Rng as _, SeedableRng as _};
+use np_hkdf::{v1_salt, DerivedSectionKeys};
 use serde_json::json;
 use std::{fs, io::Read as _, prelude::rust_2021::*, println};
-use strum::IntoEnumIterator as _;
 use test_helper::extract_key_array;
+use test_vector_hkdf::TestVectorHkdf;
+
+type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
 
 #[test]
 fn mic_encrypted_test_vectors() -> Result<(), anyhow::Error> {
     let full_path = test_helper::get_data_file(
-        "presence/np_adv/resources/test/mic-encrypted-test-vectors.json",
+        "presence/np_adv/resources/test/mic-extended-salt-encrypted-test-vectors.json",
     );
     let mut file = fs::File::open(full_path)?;
     let mut data = String::new();
@@ -54,12 +54,11 @@
     for tc in test_cases {
         {
             let key_seed = extract_key_array::<32>(&tc, "key_seed");
-            let metadata_key = MetadataKey(extract_key_array::<16>(&tc, "metadata_key"));
+            let identity_token =
+                V1IdentityToken::from(extract_key_array::<16>(&tc, "identity_token"));
             let adv_header_byte = extract_key_array::<1>(&tc, "adv_header_byte")[0];
-            let section_salt = v1_salt::V1Salt::<CryptoProviderImpl>::from(
-                extract_key_array::<16>(&tc, "section_salt"),
-            );
-            let identity_type = tc["identity_type"].as_str().map(identity_type_from_label).unwrap();
+            let section_salt =
+                v1_salt::ExtendedV1Salt::from(extract_key_array::<16>(&tc, "section_salt"));
             let data_elements = tc["data_elements"]
                 .as_array()
                 .unwrap()
@@ -74,28 +73,31 @@
 
             assert_eq!(
                 extract_key_array::<16>(&tc, "aes_key").as_slice(),
-                np_hkdf::UnsignedSectionKeys::aes_key(&hkdf).as_slice()
+                hkdf.v1_mic_extended_salt_keys().aes_key().as_slice()
             );
             assert_eq!(
                 extract_key_array::<32>(&tc, "section_mic_hmac_key").as_slice(),
-                np_hkdf::UnsignedSectionKeys::hmac_key(&hkdf).as_bytes()
+                hkdf.v1_mic_extended_salt_keys().mic_hmac_key().as_bytes()
             );
             assert_eq!(
                 extract_key_array::<{ AES_CTR_NONCE_LEN }>(&tc, "nonce").as_slice(),
-                section_salt.derive::<{ AES_CTR_NONCE_LEN }>(Some(1.into())).unwrap()
+                section_salt.compute_nonce::<CryptoProviderImpl>()
             );
 
-            let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+            let broadcast_cred = V1BroadcastCredential::new(
+                key_seed,
+                identity_token,
+                ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
+            );
 
             // make an adv builder in the configuration we need
             let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-            assert_eq!(adv_header_byte, adv_builder.header_byte());
+            assert_eq!(adv_header_byte, adv_builder.adv_header().contents());
 
             let mut section_builder = adv_builder
-                .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new(
-                    identity_type,
-                    section_salt.into(),
-                    &broadcast_cm,
+                .section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
+                    section_salt,
+                    &broadcast_cred,
                 ))
                 .unwrap();
 
@@ -105,7 +107,7 @@
 
             assert_eq!(
                 hex::decode(tc["encoded_section"].as_str().unwrap()).unwrap(),
-                section_builder.into_section().as_slice()
+                section_builder.into_section::<CryptoProviderImpl>().as_slice()
             );
         }
     }
@@ -116,42 +118,57 @@
 #[ignore]
 #[test]
 fn gen_mic_encrypted_test_vectors() {
-    let mut rng = rand::rngs::StdRng::from_entropy();
-
     let mut array = Vec::<serde_json::Value>::new();
 
-    for _ in 0..100 {
-        let num_des = rng.gen_range(0..=5);
+    for i in 0_u32..100 {
+        let test_vector_seed_hkdf = TestVectorHkdf::<CryptoProviderImpl>::new(
+            "NP V1 MIC test vectors HpakGBH8I+zbcEdxEanIT1r3bkgmL+mWI/kMrPiCzPw",
+            &i.to_be_bytes(),
+        );
 
-        let extra_des = (0..num_des)
-            .map(|_| {
-                let de_len = rng.gen_range(0..=30);
-                DummyDataElement {
-                    de_type: rng.gen_range(0_u32..=1_000).into(),
-                    data: rand_ext::random_vec_rc(&mut rng, de_len),
-                }
-            })
-            .collect::<Vec<_>>();
+        let num_des = test_vector_seed_hkdf.derive_range_element("num des", 0_u64..=5);
 
-        let metadata_key = MetadataKey(rng.gen());
-        let key_seed = rng.gen();
+        let extra_des =
+            (0..num_des)
+                .map(|de_index| {
+                    let de_len = test_vector_seed_hkdf
+                        .derive_range_element(&format!("de len {}", de_index), 0..=30);
+                    let data = test_vector_seed_hkdf
+                        .derive_vec(&format!("de data {}", de_index), de_len.try_into().unwrap());
+                    DummyDataElement {
+                        de_type: DeType::from(
+                            u32::try_from(test_vector_seed_hkdf.derive_range_element(
+                                &format!("de type {}", de_index),
+                                0_u64..=1_000,
+                            ))
+                            .unwrap(),
+                        ),
+                        data,
+                    }
+                })
+                .collect::<Vec<_>>();
+
+        let identity_token =
+            V1IdentityToken::from(test_vector_seed_hkdf.derive_array("identity token"));
+        let key_seed = test_vector_seed_hkdf.derive_array("key seed");
         let adv_header_byte = 0b00100000;
-        let identity_type =
-            *EncryptedIdentityDataElementType::iter().collect::<Vec<_>>().choose(&mut rng).unwrap();
 
         let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
 
-        let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+        let broadcast_cred = V1BroadcastCredential::new(
+            key_seed,
+            identity_token,
+            ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
+        );
 
         let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
 
-        let salt = rng.gen::<[u8; 16]>();
-        let section_salt = v1_salt::V1Salt::<CryptoProviderImpl>::from(salt);
+        let section_salt =
+            v1_salt::ExtendedV1Salt::from(test_vector_seed_hkdf.derive_array::<16>("salt"));
         let mut section_builder = adv_builder
-            .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new(
-                identity_type,
-                RawV1Salt(salt),
-                &broadcast_cm,
+            .section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
+                section_salt,
+                &broadcast_cred,
             ))
             .unwrap();
 
@@ -159,42 +176,24 @@
             section_builder.add_de(|_| de).unwrap();
         }
 
-        let nonce = section_salt.derive::<{ AES_CTR_NONCE_LEN }>(Some(1.into())).unwrap();
-
-        array
-            .push(json!({
-                "key_seed": hex::encode_upper(key_seed),
-                "metadata_key": hex::encode_upper(metadata_key.0),
-                "adv_header_byte": hex::encode_upper([adv_header_byte]),
-                "section_salt": hex::encode_upper(section_salt.as_slice()),
-                "identity_type": identity_type_label(identity_type),
-                "data_elements": extra_des.iter().map(|de| json!({
-                    "de_type": de.de_type.as_u32(),
-                    "contents": hex::encode_upper(&de.data)
-                })).collect::<Vec<_>>(),
-                "aes_key": hex::encode_upper(np_hkdf::UnsignedSectionKeys::aes_key(&key_seed_hkdf).as_slice()),
-                "nonce": hex::encode_upper(nonce),
-                "section_mic_hmac_key": hex::encode_upper(np_hkdf::UnsignedSectionKeys::hmac_key(&key_seed_hkdf).as_bytes()),
-                "encoded_section": hex::encode_upper(section_builder.into_section().as_slice())
-            }));
+        let nonce = section_salt.compute_nonce::<CryptoProviderImpl>();
+        let keys = key_seed_hkdf.v1_mic_extended_salt_keys();
+        array.push(json!({
+            "key_seed": hex::encode_upper(key_seed),
+            "identity_token": hex::encode_upper(identity_token.as_slice()),
+            "adv_header_byte": hex::encode_upper([adv_header_byte]),
+            "section_salt": hex::encode_upper(section_salt.as_slice()),
+            "data_elements": extra_des.iter().map(|de| json!({
+                "de_type": de.de_type.as_u32(),
+                "contents": hex::encode_upper(&de.data)
+            })).collect::<Vec<_>>(),
+            "aes_key": hex::encode_upper(keys.aes_key().as_slice()),
+            "nonce": hex::encode_upper(nonce),
+            "section_mic_hmac_key": hex::encode_upper(keys.mic_hmac_key().as_bytes()),
+            "encoded_section": hex::encode_upper(section_builder.into_section::<CryptoProviderImpl>().as_slice())
+        }));
     }
 
     println!("{}", serde_json::ser::to_string_pretty(&array).unwrap());
-}
-
-fn identity_type_label(t: EncryptedIdentityDataElementType) -> &'static str {
-    match t {
-        EncryptedIdentityDataElementType::Private => "private",
-        EncryptedIdentityDataElementType::Trusted => "trusted",
-        EncryptedIdentityDataElementType::Provisioned => "provisioned",
-    }
-}
-
-fn identity_type_from_label(label: &str) -> EncryptedIdentityDataElementType {
-    match label {
-        "private" => EncryptedIdentityDataElementType::Private,
-        "trusted" => EncryptedIdentityDataElementType::Trusted,
-        "provisioned" => EncryptedIdentityDataElementType::Provisioned,
-        _ => panic!("unknown label: {}", label),
-    }
+    panic!("Don't leave this test enabled. Meanwhile, enjoy the text output above.");
 }
diff --git a/nearby/presence/np_adv/src/filter/mod.rs b/nearby/presence/np_adv/src/filter/mod.rs
index a2b2d9c..e2b37e7 100644
--- a/nearby/presence/np_adv/src/filter/mod.rs
+++ b/nearby/presence/np_adv/src/filter/mod.rs
@@ -16,21 +16,18 @@
 //! a valid Nearby Presence advertisement and if so which was its corresponding identity
 //! it matched with. Used as a first pass option to quickly check if a buffer should
 //! further processed.
-use crate::credential::MatchedCredential;
+use crate::credential::matched::MatchedCredential;
+use crate::header::{NpVersionHeader, V0Encoding, V1AdvHeader};
 use crate::legacy::data_elements::DataElementDeserializeError;
+use crate::legacy::deserialize::intermediate::{IntermediateAdvContents, LdtAdvContents};
 use crate::legacy::deserialize::DecryptedAdvContents;
 use crate::{
     credential::{book::CredentialBook, v0::V0DiscoveryCryptoMaterial},
-    legacy,
     legacy::{
-        actions,
-        actions::ActionsDataElement,
-        deserialize::{
-            DecryptError, EncryptedAdvContents, IntermediateAdvContents, PlainDataElement,
-        },
+        data_elements::actions::{self, ActionsDataElement},
+        deserialize::{DecryptError, DeserializedDataElement},
         PacketFlavor,
     },
-    parse_adv_header, AdvHeader, V1Header,
 };
 use array_view::ArrayView;
 use core::fmt::Debug;
@@ -91,7 +88,7 @@
 }
 
 /// The total number of unique boolean action types
-const NUM_ACTIONS: usize = 7;
+const NUM_ACTIONS: usize = 6;
 
 /// Specify which specific actions bits to filter on, will filter on if any of the specified
 /// actions are matched
@@ -117,17 +114,17 @@
         B: CredentialBook<'a>,
         P: CryptoProvider,
     {
-        parse_adv_header(adv)
+        NpVersionHeader::parse(adv)
             .map(|(remaining, header)| match header {
-                AdvHeader::V0 => {
+                NpVersionHeader::V0(encoding) => {
                     let filter = match self {
                         FilterOptions::V0FilterOptions(filter) => filter,
                         FilterOptions::Either(filter, _) => filter,
                         _ => return Err(NoMatch),
                     };
-                    filter.match_v0_adv::<B, P>(cred_book, remaining)
+                    filter.match_v0_adv::<B, P>(encoding, cred_book, remaining)
                 }
-                AdvHeader::V1(header) => {
+                NpVersionHeader::V1(header) => {
                     let filter = match self {
                         FilterOptions::V1FilterOptions(filter) => filter,
                         FilterOptions::Either(_, filter) => filter,
@@ -145,6 +142,7 @@
     /// match the filter criteria
     fn match_v0_adv<'a, B, P>(
         &self,
+        encoding: V0Encoding,
         cred_book: &'a B,
         remaining: &[u8],
     ) -> Result<FilterResult<B::Matched>, NoMatch>
@@ -153,16 +151,16 @@
         P: CryptoProvider,
     {
         let contents =
-            legacy::deserialize::deserialize_adv_contents::<P>(remaining).map_err(|_| NoMatch)?;
+            IntermediateAdvContents::deserialize::<P>(encoding, remaining).map_err(|_| NoMatch)?;
         match contents {
-            IntermediateAdvContents::Plaintext(p) => match self.identity {
+            IntermediateAdvContents::Unencrypted(p) => match self.identity {
                 IdentityFilterType::Public | IdentityFilterType::Any => self
                     .data_elements
                     .match_v0_legible_adv(|| p.data_elements())
                     .map(|()| FilterResult::Public),
                 _ => Err(NoMatch),
             },
-            IntermediateAdvContents::Ciphertext(c) => match self.identity {
+            IntermediateAdvContents::Ldt(c) => match self.identity {
                 IdentityFilterType::Private | IdentityFilterType::Any => {
                     let (legible_adv, m) = try_decrypt_and_match::<B, P>(cred_book.v0_iter(), &c)?;
                     self.data_elements
@@ -177,7 +175,7 @@
 
 fn try_decrypt_and_match<'cred, B, P>(
     v0_creds: B::V0Iterator,
-    adv: &EncryptedAdvContents,
+    adv: &LdtAdvContents,
 ) -> Result<(DecryptedAdvContents, B::Matched), NoMatch>
 where
     B: CredentialBook<'cred>,
@@ -189,9 +187,6 @@
             Ok(c) => return Ok((c, m)),
             Err(e) => match e {
                 DecryptError::DecryptOrVerifyError => continue,
-                DecryptError::DeserializeError(_) => {
-                    return Err(NoMatch);
-                }
             },
         }
     }
@@ -203,12 +198,14 @@
     fn match_v0_legible_adv<F, I>(&self, data_elements: impl Fn() -> I) -> Result<(), NoMatch>
     where
         F: PacketFlavor,
-        I: Iterator<Item = Result<PlainDataElement<F>, DataElementDeserializeError>>,
+        I: Iterator<Item = Result<DeserializedDataElement<F>, DataElementDeserializeError>>,
     {
         match &self.contains_tx_power {
             None => Ok(()),
             Some(c) => {
-                if c == &data_elements().any(|de| matches!(de, Ok(PlainDataElement::TxPower(_)))) {
+                if c == &data_elements()
+                    .any(|de| matches!(de, Ok(DeserializedDataElement::TxPower(_))))
+                {
                     Ok(())
                 } else {
                     Err(NoMatch)
@@ -224,7 +221,7 @@
                 }
                 // find if an actions DE exists, if so match on the provided action filter
                 let actions = data_elements().find_map(|de| match de {
-                    Ok(PlainDataElement::Actions(actions)) => Some(actions),
+                    Ok(DeserializedDataElement::Actions(actions)) => Some(actions),
                     _ => None,
                 });
                 if let Some(actions) = actions {
@@ -262,11 +259,7 @@
         actions: &ActionsDataElement<F>,
     ) -> Result<(), NoMatch> {
         for action in self.actions.as_slice().iter() {
-            if actions
-                .action
-                .has_action(&action.expect("This will always contain Some"))
-                .unwrap_or(false)
-            {
+            if actions.action.has_action(action.expect("This will always contain Some")) {
                 return Ok(());
             }
         }
@@ -285,7 +278,7 @@
         &self,
         _cred_book: &'a B,
         _remaining: &[u8],
-        _header: V1Header,
+        _header: V1AdvHeader,
     ) -> Result<FilterResult<B::Matched>, NoMatch>
     where
         B: CredentialBook<'a>,
diff --git a/nearby/presence/np_adv/src/filter/tests/actions_filter_tests.rs b/nearby/presence/np_adv/src/filter/tests/actions_filter_tests.rs
index ffdeeb9..c395124 100644
--- a/nearby/presence/np_adv/src/filter/tests/actions_filter_tests.rs
+++ b/nearby/presence/np_adv/src/filter/tests/actions_filter_tests.rs
@@ -15,8 +15,10 @@
 #![allow(clippy::unwrap_used)]
 
 use super::super::*;
-use crate::legacy::actions::{ActionBits, InstantTethering, NearbyShare};
+use crate::legacy::data_elements::actions::{ActionBits, InstantTethering, NearbyShare};
 use crate::legacy::{Ciphertext, Plaintext};
+use alloc::vec::Vec;
+use strum::IntoEnumIterator;
 
 #[test]
 fn new_v0_actions_invalid_length() {
@@ -28,7 +30,7 @@
 
 #[test]
 fn new_v0_actions() {
-    let actions = [actions::ActionType::ActiveUnlock; 7];
+    let actions = [actions::ActionType::ActiveUnlock; 5];
     let result = V0ActionsFilter::new_from_slice(&actions);
     assert!(result.is_ok());
 }
@@ -55,16 +57,8 @@
     // default is all 0 bits
     let action_bits = ActionBits::<Plaintext>::default();
 
-    let filter = V0ActionsFilter::new_from_slice(&[
-        actions::ActionType::ActiveUnlock,
-        actions::ActionType::NearbyShare,
-        actions::ActionType::InstantTethering,
-        actions::ActionType::PhoneHub,
-        actions::ActionType::Finder,
-        actions::ActionType::FastPairSass,
-        actions::ActionType::PresenceManager,
-    ])
-    .expect("7 is a valid length");
+    let filter = V0ActionsFilter::new_from_slice(&actions::ActionType::iter().collect::<Vec<_>>())
+        .expect("5 is a valid length");
 
     assert_eq!(filter.match_v0_actions(&action_bits.into()), Err(NoMatch))
 }
@@ -75,16 +69,8 @@
     let mut action_bits = ActionBits::<Plaintext>::default();
     action_bits.set_action(NearbyShare::from(true));
 
-    let filter = V0ActionsFilter::new_from_slice(&[
-        actions::ActionType::ActiveUnlock,
-        actions::ActionType::NearbyShare,
-        actions::ActionType::InstantTethering,
-        actions::ActionType::PhoneHub,
-        actions::ActionType::Finder,
-        actions::ActionType::FastPairSass,
-        actions::ActionType::PresenceManager,
-    ])
-    .expect("7 is a valid length");
+    let filter = V0ActionsFilter::new_from_slice(&actions::ActionType::iter().collect::<Vec<_>>())
+        .expect("5 is a valid length");
 
     assert_eq!(filter.match_v0_actions(&action_bits.into()), Ok(()))
 }
@@ -96,14 +82,12 @@
     action_bits.set_action(NearbyShare::from(true));
 
     let filter = V0ActionsFilter::new_from_slice(&[
+        actions::ActionType::CallTransfer,
         actions::ActionType::ActiveUnlock,
         actions::ActionType::InstantTethering,
         actions::ActionType::PhoneHub,
-        actions::ActionType::Finder,
-        actions::ActionType::FastPairSass,
-        actions::ActionType::PresenceManager,
     ])
-    .expect("6 is a valid length");
+    .expect("4 is a valid length");
 
     assert_eq!(filter.match_v0_actions(&action_bits.into()), Err(NoMatch))
 }
@@ -136,3 +120,8 @@
 
     assert_eq!(filter.match_v0_actions(&action_bits.into()), Ok(()))
 }
+
+#[test]
+fn num_actions_is_correct() {
+    assert_eq!(actions::ActionType::iter().count(), NUM_ACTIONS);
+}
diff --git a/nearby/presence/np_adv/src/filter/tests/data_elements_filter_tests.rs b/nearby/presence/np_adv/src/filter/tests/data_elements_filter_tests.rs
index 4a893a2..0b83831 100644
--- a/nearby/presence/np_adv/src/filter/tests/data_elements_filter_tests.rs
+++ b/nearby/presence/np_adv/src/filter/tests/data_elements_filter_tests.rs
@@ -13,10 +13,16 @@
 // limitations under the License.
 
 use super::super::*;
-use crate::legacy::actions::{ActionBits, ActiveUnlock};
-use crate::legacy::data_elements::TxPowerDataElement;
-use crate::legacy::{Ciphertext, Plaintext};
-use crate::shared_data::TxPower;
+use crate::{
+    legacy::{
+        data_elements::{
+            actions::{ActionBits, ActiveUnlock},
+            tx_power::TxPowerDataElement,
+        },
+        Ciphertext, Plaintext,
+    },
+    shared_data::TxPower,
+};
 
 #[test]
 fn match_contains_tx_power() {
@@ -24,7 +30,7 @@
 
     let tx_power = TxPower::try_from(5).expect("within range");
     let result = filter.match_v0_legible_adv(|| {
-        [Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone())))]
+        [Ok(DeserializedDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power)))]
             .into_iter()
     });
     assert_eq!(result, Ok(()))
@@ -44,7 +50,7 @@
     let filter = V0DataElementsFilter { contains_tx_power: None, actions_filter: Some(filter) };
     let tx_power = TxPower::try_from(5).expect("within range");
     let result = filter.match_v0_legible_adv(|| {
-        [Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone())))]
+        [Ok(DeserializedDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power)))]
             .into_iter()
     });
     assert_eq!(result, Err(NoMatch))
@@ -62,8 +68,8 @@
 
     let result = filter.match_v0_legible_adv(|| {
         [
-            Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone()))),
-            Ok(PlainDataElement::Actions(action_bits.into())),
+            Ok(DeserializedDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power))),
+            Ok(DeserializedDataElement::Actions(action_bits.into())),
         ]
         .into_iter()
     });
@@ -83,8 +89,8 @@
 
     let result = filter.match_v0_legible_adv(|| {
         [
-            Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone()))),
-            Ok(PlainDataElement::Actions(action_bits.into())),
+            Ok(DeserializedDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power))),
+            Ok(DeserializedDataElement::Actions(action_bits.into())),
         ]
         .into_iter()
     });
@@ -100,7 +106,7 @@
     let tx_power = TxPower::try_from(5).expect("within range");
 
     let result = filter.match_v0_legible_adv(|| {
-        [Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone())))]
+        [Ok(DeserializedDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power)))]
             .into_iter()
     });
     assert_eq!(result, Err(NoMatch))
diff --git a/nearby/presence/np_adv/src/filter/tests/mod.rs b/nearby/presence/np_adv/src/filter/tests/mod.rs
index 29d3e7c..42ab177 100644
--- a/nearby/presence/np_adv/src/filter/tests/mod.rs
+++ b/nearby/presence/np_adv/src/filter/tests/mod.rs
@@ -13,11 +13,13 @@
 // limitations under the License.
 
 use crate::credential::book::CredentialBookBuilder;
-use crate::credential::KeySeedMatchedCredential;
+use crate::credential::matched::KeySeedMatchedCredential;
+use crate::extended::V1_ENCODING_UNENCRYPTED;
 use crate::filter::IdentityFilterType::Any;
 use crate::filter::{
     FilterOptions, FilterResult, NoMatch, V0DataElementsFilter, V0Filter, V1Filter,
 };
+use crate::header::{VERSION_HEADER_V0_UNENCRYPTED, VERSION_HEADER_V1};
 use crypto_provider_default::CryptoProviderImpl;
 
 mod actions_filter_tests;
@@ -42,9 +44,9 @@
     let result = filter.match_advertisement::<_, CryptoProviderImpl>(
         &empty_cred_book,
         &[
-            0x0,  // adv header
-            0x03, // public DE
-            0x16, 0x00, // actions
+            VERSION_HEADER_V0_UNENCRYPTED,
+            0x16,
+            0x00, // actions
         ],
     );
 
@@ -69,9 +71,9 @@
     let result = filter.match_advertisement::<_, CryptoProviderImpl>(
         &empty_cred_book,
         &[
-            0x0,  // adv header
-            0x03, // public DE
-            0x16, 0x00, // actions
+            VERSION_HEADER_V0_UNENCRYPTED,
+            0x16,
+            0x00, // actions
         ],
     );
 
@@ -96,10 +98,11 @@
     let result = filter.match_advertisement::<_, CryptoProviderImpl>(
         &empty_cred_book,
         &[
-            0x20, // V1 Advertisement header
+            VERSION_HEADER_V1,
             0x03, // Section Header
-            0x03, // Public Identity DE header
-            0x15, 0x03, // Length 1 Tx Power DE with value 3
+            V1_ENCODING_UNENCRYPTED,
+            0x15,
+            0x03, // Length 1 Tx Power DE with value 3
         ],
     );
 
diff --git a/nearby/presence/np_adv/src/filter/tests/v0_filter_tests.rs b/nearby/presence/np_adv/src/filter/tests/v0_filter_tests.rs
index 971a39e..9c60e2f 100644
--- a/nearby/presence/np_adv/src/filter/tests/v0_filter_tests.rs
+++ b/nearby/presence/np_adv/src/filter/tests/v0_filter_tests.rs
@@ -12,27 +12,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#![allow(clippy::unwrap_used)]
+
 use super::super::*;
 use crate::credential::book::CredentialBookBuilder;
+use crate::credential::matched::{KeySeedMatchedCredential, ReferencedMatchedCredential};
 use crate::credential::v0::{V0DiscoveryCredential, V0};
-use crate::credential::{
-    KeySeedMatchedCredential, MatchableCredential, ReferencedMatchedCredential,
-};
+use crate::credential::{v0::V0BroadcastCredential, MatchableCredential};
 use crate::filter::IdentityFilterType::{Any, Private, Public};
+use crate::legacy::data_elements::tx_power::TxPowerDataElement;
+use crate::legacy::serialize::{AdvBuilder, LdtEncoder};
+use crate::shared_data::TxPower;
+use alloc::vec::Vec;
 use crypto_provider_default::CryptoProviderImpl;
-use ldt_np_adv::NP_LEGACY_METADATA_KEY_LEN;
+use ldt_np_adv::V0_IDENTITY_TOKEN_LEN;
 
-const METADATA_KEY: [u8; NP_LEGACY_METADATA_KEY_LEN] = [0x33; NP_LEGACY_METADATA_KEY_LEN];
+const IDENTITY_TOKEN: [u8; V0_IDENTITY_TOKEN_LEN] = [0x33; V0_IDENTITY_TOKEN_LEN];
 const KEY_SEED: [u8; 32] = [0x11_u8; 32];
-const PRIVATE_IDENTITY_V0_ADV_CONTENTS: [u8; 19] = [
-    0x21, // private DE
-    0x22, 0x22, // salt
-    // ciphertext
-    0x85, 0xBF, 0xA8, 0x83, 0x58, 0x7C, 0x50, 0xCF, 0x98, 0x38, 0xA7, 0x8A, 0xC0, 0x1C, 0x96, 0xF9,
-];
 
-const PUBLIC_IDENTITY_V0_ADV_CONTENTS: [u8; 3] = [
-    0x03, // public DE
+const PUBLIC_IDENTITY_V0_ADV_CONTENTS: [u8; 2] = [
     0x16, 0x00, // actions
 ];
 
@@ -49,8 +47,11 @@
         CryptoProviderImpl,
     >(&[], &[]);
 
-    let result =
-        filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PUBLIC_IDENTITY_V0_ADV_CONTENTS);
+    let result = filter.match_v0_adv::<_, CryptoProviderImpl>(
+        V0Encoding::Unencrypted,
+        &cred_book,
+        &PUBLIC_IDENTITY_V0_ADV_CONTENTS,
+    );
 
     assert_eq!(result, Ok(FilterResult::Public));
 }
@@ -68,8 +69,11 @@
         CryptoProviderImpl,
     >(&[], &[]);
 
-    let result =
-        filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PUBLIC_IDENTITY_V0_ADV_CONTENTS);
+    let result = filter.match_v0_adv::<_, CryptoProviderImpl>(
+        V0Encoding::Ldt,
+        &cred_book,
+        &PUBLIC_IDENTITY_V0_ADV_CONTENTS,
+    );
 
     assert_eq!(result, Err(NoMatch));
 }
@@ -87,8 +91,11 @@
         CryptoProviderImpl,
     >(&[], &[]);
 
-    let result =
-        filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PUBLIC_IDENTITY_V0_ADV_CONTENTS);
+    let result = filter.match_v0_adv::<_, CryptoProviderImpl>(
+        V0Encoding::Unencrypted,
+        &cred_book,
+        &PUBLIC_IDENTITY_V0_ADV_CONTENTS,
+    );
 
     assert_eq!(result, Ok(FilterResult::Public));
 }
@@ -106,8 +113,11 @@
         CryptoProviderImpl,
     >(&[], &[]);
 
-    let result =
-        filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PRIVATE_IDENTITY_V0_ADV_CONTENTS);
+    let result = filter.match_v0_adv::<_, CryptoProviderImpl>(
+        V0Encoding::Ldt,
+        &cred_book,
+        v0_adv_contents().as_slice(),
+    );
 
     assert_eq!(result, Err(NoMatch));
 }
@@ -121,11 +131,12 @@
 
     let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&KEY_SEED);
     let metadata_key_hmac: [u8; 32] =
-        hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&METADATA_KEY);
-    let discovery_credential = V0DiscoveryCredential::new(KEY_SEED, metadata_key_hmac);
+        hkdf.v0_identity_token_hmac_key().calculate_hmac::<CryptoProviderImpl>(&IDENTITY_TOKEN);
     let match_data: KeySeedMatchedCredential = KEY_SEED.into();
-    let v0_creds: [MatchableCredential<V0, KeySeedMatchedCredential>; 1] =
-        [MatchableCredential { discovery_credential, match_data: match_data.clone() }];
+    let v0_creds: [MatchableCredential<V0, KeySeedMatchedCredential>; 1] = [MatchableCredential {
+        discovery_credential: V0DiscoveryCredential::new(KEY_SEED, metadata_key_hmac),
+        match_data: match_data.clone(),
+    }];
 
     let cred_book = CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
         0,
@@ -133,8 +144,11 @@
         CryptoProviderImpl,
     >(&v0_creds, &[]);
 
-    let result =
-        filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PRIVATE_IDENTITY_V0_ADV_CONTENTS);
+    let result = filter.match_v0_adv::<_, CryptoProviderImpl>(
+        V0Encoding::Ldt,
+        &cred_book,
+        v0_adv_contents().as_slice(),
+    );
 
     assert_eq!(result, Ok(FilterResult::Private(ReferencedMatchedCredential::from(&match_data))));
 }
@@ -148,11 +162,12 @@
 
     let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&KEY_SEED);
     let metadata_key_hmac: [u8; 32] =
-        hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&METADATA_KEY);
-    let discovery_credential = V0DiscoveryCredential::new(KEY_SEED, metadata_key_hmac);
+        hkdf.v0_identity_token_hmac_key().calculate_hmac::<CryptoProviderImpl>(&IDENTITY_TOKEN);
     let match_data: KeySeedMatchedCredential = KEY_SEED.into();
-    let v0_creds: [MatchableCredential<V0, KeySeedMatchedCredential>; 1] =
-        [MatchableCredential { discovery_credential, match_data: match_data.clone() }];
+    let v0_creds: [MatchableCredential<V0, KeySeedMatchedCredential>; 1] = [MatchableCredential {
+        discovery_credential: V0DiscoveryCredential::new(KEY_SEED, metadata_key_hmac),
+        match_data: match_data.clone(),
+    }];
 
     let cred_book = CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
         0,
@@ -160,8 +175,11 @@
         CryptoProviderImpl,
     >(&v0_creds, &[]);
 
-    let result =
-        filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PRIVATE_IDENTITY_V0_ADV_CONTENTS);
+    let result = filter.match_v0_adv::<_, CryptoProviderImpl>(
+        V0Encoding::Ldt,
+        &cred_book,
+        v0_adv_contents().as_slice(),
+    );
 
     assert_eq!(result, Ok(FilterResult::Private(ReferencedMatchedCredential::from(&match_data))));
 }
@@ -180,10 +198,11 @@
 
     let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
     let metadata_key_hmac: [u8; 32] =
-        hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&METADATA_KEY);
-    let discovery_credential = V0DiscoveryCredential::new(key_seed, metadata_key_hmac);
-    let v0_creds: [MatchableCredential<V0, KeySeedMatchedCredential>; 1] =
-        [MatchableCredential { discovery_credential, match_data: KEY_SEED.into() }];
+        hkdf.v0_identity_token_hmac_key().calculate_hmac::<CryptoProviderImpl>(&IDENTITY_TOKEN);
+    let v0_creds: [MatchableCredential<V0, KeySeedMatchedCredential>; 1] = [MatchableCredential {
+        discovery_credential: V0DiscoveryCredential::new(key_seed, metadata_key_hmac),
+        match_data: KEY_SEED.into(),
+    }];
 
     let cred_book = CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
         0,
@@ -191,8 +210,11 @@
         CryptoProviderImpl,
     >(&v0_creds, &[]);
 
-    let result =
-        filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PRIVATE_IDENTITY_V0_ADV_CONTENTS);
+    let result = filter.match_v0_adv::<_, CryptoProviderImpl>(
+        V0Encoding::Ldt,
+        &cred_book,
+        v0_adv_contents().as_slice(),
+    );
 
     assert_eq!(result, Err(NoMatch));
 }
@@ -204,9 +226,10 @@
         data_elements: V0DataElementsFilter { contains_tx_power: None, actions_filter: None },
     };
 
-    let discovery_credential = V0DiscoveryCredential::new(KEY_SEED, [0u8; 32]);
-    let v0_creds: [MatchableCredential<V0, KeySeedMatchedCredential>; 1] =
-        [MatchableCredential { discovery_credential, match_data: KEY_SEED.into() }];
+    let v0_creds: [MatchableCredential<V0, KeySeedMatchedCredential>; 1] = [MatchableCredential {
+        discovery_credential: V0DiscoveryCredential::new(KEY_SEED, [0u8; 32]),
+        match_data: KEY_SEED.into(),
+    }];
 
     let cred_book = CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
         0,
@@ -214,8 +237,21 @@
         CryptoProviderImpl,
     >(&v0_creds, &[]);
 
-    let result =
-        filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PRIVATE_IDENTITY_V0_ADV_CONTENTS);
+    let result = filter.match_v0_adv::<_, CryptoProviderImpl>(
+        V0Encoding::Ldt,
+        &cred_book,
+        v0_adv_contents().as_slice(),
+    );
 
     assert_eq!(result, Err(NoMatch));
 }
+
+/// Returns the contents of an advertisement after the version header.
+fn v0_adv_contents() -> Vec<u8> {
+    let broadcast_cm = V0BroadcastCredential::new(KEY_SEED, IDENTITY_TOKEN.into());
+    let mut builder =
+        AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new([0x22; 2].into(), &broadcast_cm));
+
+    builder.add_data_element(TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
+    builder.into_advertisement().unwrap().as_slice()[1..].to_vec()
+}
diff --git a/nearby/presence/np_adv/src/header.rs b/nearby/presence/np_adv/src/header.rs
new file mode 100644
index 0000000..dc0b96f
--- /dev/null
+++ b/nearby/presence/np_adv/src/header.rs
@@ -0,0 +1,165 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! NP version header (the first byte) support.
+//!
+//! The version header byte is 3 bits of version followed by 5 reserved bits.
+//!
+//! For V0 (`0b000`), the first 3 of the reserved bits identify the encoding
+//! scheme used, and the last 2 are still reserved.
+
+use nom::{combinator, number};
+
+// 3-bit versions (high 3 bits in version header)
+const PROTOCOL_VERSION_LEGACY: u8 = 0;
+const PROTOCOL_VERSION_EXTENDED: u8 = 1;
+
+// 3-bit encoding ids (3 bits after version, leaving 2 reserved bits)
+const V0_ENCODING_SCHEME_ID_UNENCRYPTED: u8 = 0;
+const V0_ENCODING_SCHEME_ID_LDT: u8 = 1;
+
+/// Version header byte for V1
+pub const VERSION_HEADER_V1: u8 = PROTOCOL_VERSION_EXTENDED << 5;
+
+/// Version header byte for V0 with the unencrypted encoding
+pub const VERSION_HEADER_V0_UNENCRYPTED: u8 =
+    (PROTOCOL_VERSION_LEGACY << 5) | (V0_ENCODING_SCHEME_ID_UNENCRYPTED << 2);
+
+/// Version header byte for V0 with the LDT encoding
+pub const VERSION_HEADER_V0_LDT: u8 =
+    (PROTOCOL_VERSION_LEGACY << 5) | (V0_ENCODING_SCHEME_ID_LDT << 2);
+
+/// The first byte in the NP svc data. It defines which version of the protocol
+/// is used for the rest of the svc data.
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub(crate) enum NpVersionHeader {
+    V0(V0Encoding),
+    V1(V1AdvHeader),
+}
+
+impl NpVersionHeader {
+    /// Parse a NP advertisement header.
+    ///
+    /// This can be used on all versions of advertisements since it's the header that determines the
+    /// version.
+    ///
+    /// Returns a `nom::IResult` with the parsed header and the remaining bytes of the advertisement.
+    pub(crate) fn parse(adv: &[u8]) -> nom::IResult<&[u8], Self> {
+        combinator::map_opt(number::complete::u8, |version_header| match version_header {
+            VERSION_HEADER_V0_UNENCRYPTED => Some(NpVersionHeader::V0(V0Encoding::Unencrypted)),
+            VERSION_HEADER_V0_LDT => Some(NpVersionHeader::V0(V0Encoding::Ldt)),
+            VERSION_HEADER_V1 => Some(NpVersionHeader::V1(V1AdvHeader::new(version_header))),
+            _ => None,
+        })(adv)
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub(crate) enum V0Encoding {
+    Unencrypted,
+    Ldt,
+}
+
+/// A parsed NP Version Header that indicates the V1 protocol is in use.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct V1AdvHeader {
+    header_byte: u8,
+}
+
+impl V1AdvHeader {
+    pub(crate) fn new(header_byte: u8) -> Self {
+        Self { header_byte }
+    }
+
+    /// The version header byte
+    pub(crate) fn contents(&self) -> u8 {
+        self.header_byte
+    }
+}
+
+#[cfg(test)]
+#[allow(clippy::unwrap_used)]
+mod tests {
+    use super::*;
+
+    extern crate std;
+
+    use nom::error;
+
+    #[test]
+    fn parse_header_v0_unencrypted() {
+        let (_, header) = NpVersionHeader::parse(&[0x00]).unwrap();
+        assert_eq!(NpVersionHeader::V0(V0Encoding::Unencrypted), header);
+    }
+
+    #[test]
+    fn parse_header_v0_ldt() {
+        let (_, header) = NpVersionHeader::parse(&[0x04]).unwrap();
+        assert_eq!(NpVersionHeader::V0(V0Encoding::Ldt), header);
+    }
+
+    #[test]
+    fn parse_header_v0_nonzero_invalid_encoding() {
+        let input = &[0x08];
+        assert_eq!(
+            nom::Err::Error(error::Error {
+                input: input.as_slice(),
+                code: error::ErrorKind::MapOpt,
+            }),
+            NpVersionHeader::parse(input).unwrap_err()
+        );
+    }
+
+    #[test]
+    fn parse_header_v0_nonzero_reserved() {
+        let input = &[0x01];
+        assert_eq!(
+            nom::Err::Error(error::Error {
+                input: input.as_slice(),
+                code: error::ErrorKind::MapOpt,
+            }),
+            NpVersionHeader::parse(input).unwrap_err()
+        );
+    }
+
+    #[test]
+    fn parse_header_v1_nonzero_reserved() {
+        let input = &[0x30];
+        assert_eq!(
+            nom::Err::Error(error::Error {
+                input: input.as_slice(),
+                code: error::ErrorKind::MapOpt,
+            }),
+            NpVersionHeader::parse(input).unwrap_err()
+        );
+    }
+
+    #[test]
+    fn parse_header_bad_version() {
+        let input = &[0x80];
+        assert_eq!(
+            nom::Err::Error(error::Error {
+                input: input.as_slice(),
+                code: error::ErrorKind::MapOpt,
+            }),
+            NpVersionHeader::parse(input).unwrap_err()
+        );
+    }
+
+    #[test]
+    fn parse_header_v1() {
+        let (_, header) = NpVersionHeader::parse(&[0x20]).unwrap();
+        assert_eq!(NpVersionHeader::V1(V1AdvHeader::new(0x20)), header);
+    }
+}
diff --git a/nearby/presence/np_adv/src/header_parse_tests.rs b/nearby/presence/np_adv/src/header_parse_tests.rs
deleted file mode 100644
index f6b5533..0000000
--- a/nearby/presence/np_adv/src/header_parse_tests.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#![allow(clippy::unwrap_used)]
-
-use super::*;
-
-extern crate std;
-
-use nom::error;
-
-#[test]
-fn parse_header_v0() {
-    let (_, header) = parse_adv_header(&[0x00]).unwrap();
-    assert_eq!(AdvHeader::V0, header);
-}
-
-#[test]
-fn parse_header_v0_nonzero_reserved() {
-    let input = &[0x01];
-    assert_eq!(
-        nom::Err::Error(error::Error { input: input.as_slice(), code: error::ErrorKind::Verify }),
-        parse_adv_header(input).unwrap_err()
-    );
-}
-
-#[test]
-fn parse_header_v1_nonzero_reserved() {
-    let input = &[0x30];
-    assert_eq!(
-        nom::Err::Error(error::Error { input: input.as_slice(), code: error::ErrorKind::Verify }),
-        parse_adv_header(input).unwrap_err()
-    );
-}
-
-#[test]
-fn parse_header_bad_version() {
-    let input = &[0x80];
-    assert_eq!(
-        nom::Err::Error(error::Error { input: input.as_slice(), code: error::ErrorKind::Verify }),
-        parse_adv_header(input).unwrap_err()
-    );
-}
-
-#[test]
-fn parse_header_v1() {
-    let (_, header) = parse_adv_header(&[0x20]).unwrap();
-    assert_eq!(AdvHeader::V1(V1Header { header_byte: 0x20 }), header);
-}
diff --git a/nearby/presence/np_adv/src/helpers.rs b/nearby/presence/np_adv/src/helpers.rs
new file mode 100644
index 0000000..f76fb9a
--- /dev/null
+++ b/nearby/presence/np_adv/src/helpers.rs
@@ -0,0 +1,39 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use nom::{bytes, combinator};
+
+/// Nom parser for a `[u8; N]`.
+pub(crate) fn parse_byte_array<const N: usize>(input: &[u8]) -> nom::IResult<&[u8], [u8; N]> {
+    combinator::map_res(bytes::complete::take(N), |slice: &[u8]| slice.try_into())(input)
+}
+
+#[allow(clippy::unwrap_used)]
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn parse_empty_array() {
+        assert_eq!(([1_u8, 2, 3].as_slice(), []), parse_byte_array::<0>(&[1, 2, 3]).unwrap())
+    }
+
+    #[test]
+    fn parse_nonempty_array() {
+        assert_eq!(
+            ([4_u8, 5, 6].as_slice(), [1, 2, 3]),
+            parse_byte_array::<3>(&[1, 2, 3, 4, 5, 6]).unwrap()
+        )
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/actions/mod.rs b/nearby/presence/np_adv/src/legacy/actions/mod.rs
deleted file mode 100644
index e0f4514..0000000
--- a/nearby/presence/np_adv/src/legacy/actions/mod.rs
+++ /dev/null
@@ -1,443 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! The "Actions" data element and associated types.
-//!
-//! This DE is somewhat more complex than other DEs. Whether or not it supports a particular flavor
-//! depends on the actions set, so it has to be treated as two separate types based on which
-//! flavor type parameter is used.
-use crate::{
-    legacy::{
-        data_elements::{DataElement, DataElementDeserializeError},
-        de_type::{DataElementType, PlainDataElementType},
-        serialize::{DataElementBundle, ToDataElementBundle},
-        Ciphertext, PacketFlavor, PacketFlavorEnum, Plaintext,
-    },
-    shared_data::ContextSyncSeqNum,
-};
-#[cfg(feature = "devtools")]
-use core::ops::Range;
-use core::{marker, num, ops};
-use nom::{bytes, combinator, error};
-use strum::IntoEnumIterator as _;
-
-mod macros;
-#[cfg(test)]
-pub(crate) mod tests;
-
-/// Actions DE.
-/// Only as many DE payload bytes will be present as needed to represent all set bits that are encoded,
-/// with a lower bound of 1 byte in the special case of no set action bits, and an upper bound
-/// of 3 bytes occupied by the DE payload.
-#[derive(Debug, PartialEq, Eq)]
-pub struct ActionsDataElement<F: PacketFlavor> {
-    /// The action bits
-    pub action: ActionBits<F>,
-}
-
-pub(crate) const ACTIONS_MAX_LEN: usize = 3;
-
-impl<F> ActionsDataElement<F>
-where
-    F: PacketFlavor,
-    ActionsDataElement<F>: DataElement,
-{
-    pub(crate) const ACTIONS_LEN: ops::RangeInclusive<usize> = (1..=ACTIONS_MAX_LEN);
-
-    /// Generic deserialize, not meant to be called directly -- use [DataElement] impls instead.
-    fn deserialize(de_contents: &[u8]) -> Result<Self, DataElementDeserializeError> {
-        combinator::all_consuming::<&[u8], _, error::Error<&[u8]>, _>(combinator::map(
-            bytes::complete::take_while_m_n(0, ACTIONS_MAX_LEN, |_| true),
-            |bytes: &[u8]| {
-                let mut action_bytes = [0_u8; 4];
-                action_bytes[..bytes.len()].copy_from_slice(bytes);
-                u32::from_be_bytes(action_bytes)
-            },
-        ))(de_contents)
-        .map_err(|_| DataElementDeserializeError::DeserializeError {
-            de_type: Self::DE_TYPE_VARIANT,
-        })
-        .map(|(_remaining, actions)| actions)
-        .and_then(|action_bits_num| {
-            let action = ActionBits::try_from(action_bits_num).map_err(|e| {
-                DataElementDeserializeError::FlavorNotSupported {
-                    de_type: Self::DE_TYPE_VARIANT,
-                    flavor: e.flavor,
-                }
-            })?;
-            Ok(Self { action })
-        })
-    }
-}
-
-impl<F: PacketFlavor> From<ActionBits<F>> for ActionsDataElement<F> {
-    fn from(action: ActionBits<F>) -> Self {
-        Self { action }
-    }
-}
-
-impl<F: PacketFlavor> ToDataElementBundle<F> for ActionsDataElement<F> {
-    fn to_de_bundle(&self) -> DataElementBundle<F> {
-        let action_byte_len = self.action.bytes_used();
-        let slice = &self.action.bits.to_be_bytes()[..action_byte_len];
-
-        DataElementBundle::try_from(PlainDataElementType::Actions, slice)
-            .expect("Length < max DE size")
-    }
-}
-
-impl DataElement for ActionsDataElement<Plaintext> {
-    const DE_TYPE_VARIANT: DataElementType = DataElementType::Actions;
-
-    fn supports_flavor(flavor: PacketFlavorEnum) -> bool {
-        match flavor {
-            PacketFlavorEnum::Plaintext => true,
-            PacketFlavorEnum::Ciphertext => false,
-        }
-    }
-
-    fn deserialize<F: PacketFlavor>(
-        de_contents: &[u8],
-    ) -> Result<Self, DataElementDeserializeError> {
-        match F::ENUM_VARIANT {
-            PacketFlavorEnum::Plaintext => ActionsDataElement::deserialize(de_contents),
-            PacketFlavorEnum::Ciphertext => Err(DataElementDeserializeError::FlavorNotSupported {
-                de_type: DataElementType::Actions,
-                flavor: F::ENUM_VARIANT,
-            }),
-        }
-    }
-}
-
-impl DataElement for ActionsDataElement<Ciphertext> {
-    const DE_TYPE_VARIANT: DataElementType = DataElementType::Actions;
-
-    fn supports_flavor(flavor: PacketFlavorEnum) -> bool {
-        match flavor {
-            PacketFlavorEnum::Plaintext => false,
-            PacketFlavorEnum::Ciphertext => true,
-        }
-    }
-
-    fn deserialize<F: PacketFlavor>(
-        de_contents: &[u8],
-    ) -> Result<Self, DataElementDeserializeError> {
-        match F::ENUM_VARIANT {
-            PacketFlavorEnum::Plaintext => Err(DataElementDeserializeError::FlavorNotSupported {
-                de_type: DataElementType::Actions,
-                flavor: F::ENUM_VARIANT,
-            }),
-            PacketFlavorEnum::Ciphertext => ActionsDataElement::deserialize(de_contents),
-        }
-    }
-}
-
-/// Container for the 24 bits defined for "actions" (feature flags and the like).
-/// This internally stores a u32, but only the 24 highest bits of this
-/// field will actually ever be populated.
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub struct ActionBits<F: PacketFlavor> {
-    bits: u32,
-    // marker for element type
-    flavor: marker::PhantomData<F>,
-}
-
-impl<F: PacketFlavor> ActionBits<F> {
-    /// Returns the actions bits as a u32. The upper limit of an actions field is 3 bytes,
-    /// so the last bytes of this u32 will always be 0
-    pub fn as_u32(self) -> u32 {
-        self.bits
-    }
-
-    /// Return whether a boolean action type is set in this data element, or `None` if the given
-    /// action type does not represent a boolean.
-    pub fn has_action(&self, action_type: &ActionType) -> Option<bool> {
-        (action_type.bits_len() == 1).then_some(self.bits_for_type(action_type) != 0)
-    }
-
-    /// Return the context sync sequence number.
-    pub fn context_sync_seq_num(&self) -> ContextSyncSeqNum {
-        ContextSyncSeqNum::try_from(self.bits_for_type(&ActionType::ContextSyncSeqNum) as u8)
-            .expect("Masking with ActionType::ContextSyncSeqNum should always be in range")
-    }
-}
-
-impl<F: PacketFlavor> Default for ActionBits<F> {
-    fn default() -> Self {
-        ActionBits {
-            bits: 0, // no bits set
-            flavor: marker::PhantomData,
-        }
-    }
-}
-
-/// At least one action doesn't support the required flavor
-#[derive(PartialEq, Eq, Debug)]
-pub struct FlavorNotSupported {
-    flavor: PacketFlavorEnum,
-}
-
-lazy_static::lazy_static! {
-    /// All bits for plaintext action types: 1 where a plaintext action could have a bit, 0 elsewhere.
-    static ref ALL_PLAINTEXT_ELEMENT_BITS: u32 = ActionType::iter()
-        .filter(|t| t.supports_flavor(PacketFlavorEnum::Plaintext))
-        .map(|t| t.all_bits())
-        .fold(0_u32, |accum, bits| accum | bits);
-}
-
-lazy_static::lazy_static! {
-    /// All bits for ciphertext action types: 1 where a ciphertext action could have a bit, 0 elsewhere.
-    static ref ALL_CIPHERTEXT_ELEMENT_BITS: u32 = ActionType::iter()
-        .filter(|t| t.supports_flavor(PacketFlavorEnum::Ciphertext))
-        .map(|t| t.all_bits())
-        .fold(0_u32, |accum, bits| accum | bits);
-}
-
-impl<F: PacketFlavor> ActionBits<F> {
-    /// Tries to create ActionBits from a u32, returning error in the event a specific bit is set for
-    /// an unsupported flavor
-    pub fn try_from(value: u32) -> Result<Self, FlavorNotSupported> {
-        let ok_bits: u32 = match F::ENUM_VARIANT {
-            PacketFlavorEnum::Plaintext => *ALL_PLAINTEXT_ELEMENT_BITS,
-            PacketFlavorEnum::Ciphertext => *ALL_CIPHERTEXT_ELEMENT_BITS,
-        };
-
-        // no bits set beyond what's allowed for this flavor
-        if value | ok_bits == ok_bits {
-            Ok(Self { bits: value, flavor: marker::PhantomData })
-        } else {
-            Err(FlavorNotSupported { flavor: F::ENUM_VARIANT })
-        }
-    }
-
-    /// Set the bits for the provided element.
-    /// Bits outside the range set by the action will be unaffected.
-    pub fn set_action<E: ToActionElement<F>>(&mut self, to_element: E) {
-        let element = to_element.to_action_element();
-        let len = element.len.get();
-
-        // validate that the element is not horribly broken
-        debug_assert!(len + element.index <= 32);
-        // must not have bits set past the high `len` bits
-        debug_assert_eq!(0, element.bits >> (8 - len));
-
-        // 0-extend to u32
-        let byte_extended = element.bits as u32;
-        // Shift so that the high bit is at the desired index.
-        // Won't overflow since length > 0.
-        let bits_in_position = byte_extended << (32 - len - element.index);
-
-        // We want to effectively clear out the bits already in place, so we don't want to just |=.
-        // Instead, we construct a u32 with all 1s above and below the relevant bits and &=, so that
-        // if the new bits are 0, the stored bits will be cleared.
-
-        // avoid overflow when index = 0 -- need zero 1 bits to the left in that case
-        let left_1s = u32::MAX.checked_shl(32 - element.index).unwrap_or(0);
-        // avoid underflow when index + len = 32 -- zero 1 bits to the right
-        let right_1s = u32::MAX.checked_shr(element.index + len).unwrap_or(0);
-        let mask = left_1s | right_1s;
-        let bits_for_other_actions = self.bits & mask;
-        self.bits = bits_for_other_actions | bits_in_position;
-    }
-
-    /// How many bytes (1-3) are needed to represent the set bits, starting from the most
-    /// significant bit. The lower bound of 1 is because the unique special case of
-    /// an actions field of all zeroes is required by the spec to occupy exactly one byte.
-    pub(crate) fn bytes_used(&self) -> usize {
-        let bits_used = 32 - self.bits.trailing_zeros();
-        let raw_count = (bits_used as usize + 7) / 8;
-        if raw_count == 0 {
-            1 // Uncommon case - should only be hit for all-zero action bits
-        } else {
-            raw_count
-        }
-    }
-
-    /// Return the bits for a given action type as the low bits in the returned u32.
-    ///
-    /// For example, when extracting the bits `B` from `0bXXXXXXXXXXBBBBBBXXXXXXXXXXXXXXXX`, the
-    /// return value will be `0b00000000000000000000000000BBBBBB`.
-    pub fn bits_for_type(&self, action_type: &ActionType) -> u32 {
-        self.bits << action_type.high_bit_index() >> (32 - action_type.bits_len())
-    }
-}
-
-/// The encoded form of an individual action element.
-#[derive(Debug, Clone, Copy)]
-pub struct ActionElementBits<F: PacketFlavor> {
-    /// Offset from the high bit in `ActionBits.bits`, which would be bit 0 of byte 0 of the big-endian
-    /// representation.
-    /// Must leave enough room for `len` bits in a u32; that is, `index + len <= 32`.
-    index: u32,
-    /// Number of bits used.
-    /// `len + index <= 32` must be true.
-    len: num::NonZeroU32,
-    /// Returns the bits to set as the lower `len` bits of the byte.
-    bits: u8,
-    /// Marker for whether it can be used in plaintext or encrypted data elements.
-    flavor: marker::PhantomData<F>,
-}
-
-/// Core trait for an individual action
-pub trait ActionElement {
-    /// The offset from the high bit in the eventual bit sequence of all actions.
-    /// See [ActionElementBits.index].
-    ///
-    /// Each implementation must have a non-overlapping sequence of bits defined by
-    /// `HIGH_BIT_INDEX` and `BITS_LEN` w.r.t every other implementation.
-    const HIGH_BIT_INDEX: u32;
-    /// The number of high bits in a `u8` that should be used when assembling the complete
-    /// action bit vector.
-    ///
-    /// Must be >0.
-    const BITS_LEN: u32;
-    /// Forces implementations to have a matching enum variant so the enum can be kept up to date.
-    const ACTION_TYPE: ActionType;
-
-    /// Returns whether or not this action supports the provided `flavor`.
-    ///
-    /// Must match the implementations of [ToActionElement].
-    fn supports_flavor(flavor: PacketFlavorEnum) -> bool;
-
-    /// Returns true if the bits for this element are all zero, or if the flavor is supported.
-    fn action_is_supported_or_not_set(bits: u32, flavor: PacketFlavorEnum) -> bool {
-        let shifted = bits << Self::HIGH_BIT_INDEX;
-        let masked = shifted & (!(u32::MAX >> Self::BITS_LEN));
-
-        (masked == 0) || Self::supports_flavor(flavor)
-    }
-}
-
-/// An analog of `Into` tailored to converting structs modeling specific action elements into the
-/// representation needed by [ActionBits].
-pub trait ToActionElement<F: PacketFlavor>: ActionElement {
-    /// Convert the high-level representation of an element into the literal bits needed.
-    fn to_action_element(&self) -> ActionElementBits<F> {
-        ActionElementBits {
-            index: Self::HIGH_BIT_INDEX,
-            len: Self::BITS_LEN.try_into().expect("all elements must have nonzero len"),
-            bits: self.bits(),
-            flavor: marker::PhantomData,
-        }
-    }
-
-    /// Returns the bits that should be set starting at `Self::INDEX`.
-    ///
-    /// Must not have more than the low `len()` bits set.
-    fn bits(&self) -> u8;
-}
-
-/// Provides a way to iterate over all action types.
-#[derive(Clone, Copy, strum_macros::EnumIter, PartialEq, Eq, Hash, Debug)]
-#[allow(missing_docs)]
-pub enum ActionType {
-    ContextSyncSeqNum,
-    ActiveUnlock,
-    NearbyShare,
-    InstantTethering,
-    PhoneHub,
-    Finder,
-    FastPairSass,
-    PresenceManager,
-}
-
-impl ActionType {
-    /// A u32 with all possible bits for this action type set
-    const fn all_bits(&self) -> u32 {
-        (u32::MAX << (32_u32 - self.bits_len())) >> self.high_bit_index()
-    }
-
-    /// Get the range of the bits occupied used by this bit index. For example, if the action type
-    /// uses the 5th and 6th bits, the returned range will be (5..7).
-    /// (0 is the index of the most significant bit).
-    #[cfg(feature = "devtools")]
-    pub const fn bits_range_for_devtools(&self) -> Range<u32> {
-        let high_bit_index = self.high_bit_index();
-        high_bit_index..high_bit_index + self.bits_len()
-    }
-
-    const fn high_bit_index(&self) -> u32 {
-        match self {
-            ActionType::ContextSyncSeqNum => ContextSyncSeqNum::HIGH_BIT_INDEX,
-            ActionType::ActiveUnlock => ActiveUnlock::HIGH_BIT_INDEX,
-            ActionType::NearbyShare => NearbyShare::HIGH_BIT_INDEX,
-            ActionType::InstantTethering => InstantTethering::HIGH_BIT_INDEX,
-            ActionType::PhoneHub => PhoneHub::HIGH_BIT_INDEX,
-            ActionType::Finder => Finder::HIGH_BIT_INDEX,
-            ActionType::FastPairSass => FastPairSass::HIGH_BIT_INDEX,
-            ActionType::PresenceManager => PresenceManager::HIGH_BIT_INDEX,
-        }
-    }
-
-    const fn bits_len(&self) -> u32 {
-        match self {
-            ActionType::ContextSyncSeqNum => ContextSyncSeqNum::BITS_LEN,
-            ActionType::ActiveUnlock => ActiveUnlock::BITS_LEN,
-            ActionType::NearbyShare => NearbyShare::BITS_LEN,
-            ActionType::InstantTethering => InstantTethering::BITS_LEN,
-            ActionType::PhoneHub => PhoneHub::BITS_LEN,
-            ActionType::Finder => Finder::BITS_LEN,
-            ActionType::FastPairSass => FastPairSass::BITS_LEN,
-            ActionType::PresenceManager => PresenceManager::BITS_LEN,
-        }
-    }
-
-    pub(crate) fn supports_flavor(&self, flavor: PacketFlavorEnum) -> bool {
-        match self {
-            ActionType::ContextSyncSeqNum => ContextSyncSeqNum::supports_flavor(flavor),
-            ActionType::ActiveUnlock => ActiveUnlock::supports_flavor(flavor),
-            ActionType::NearbyShare => NearbyShare::supports_flavor(flavor),
-            ActionType::InstantTethering => InstantTethering::supports_flavor(flavor),
-            ActionType::PhoneHub => PhoneHub::supports_flavor(flavor),
-            ActionType::Finder => Finder::supports_flavor(flavor),
-            ActionType::FastPairSass => FastPairSass::supports_flavor(flavor),
-            ActionType::PresenceManager => PresenceManager::supports_flavor(flavor),
-        }
-    }
-}
-
-impl ActionElement for ContextSyncSeqNum {
-    const HIGH_BIT_INDEX: u32 = 0;
-    const BITS_LEN: u32 = 4;
-    const ACTION_TYPE: ActionType = ActionType::ContextSyncSeqNum;
-
-    fn supports_flavor(flavor: PacketFlavorEnum) -> bool {
-        match flavor {
-            PacketFlavorEnum::Plaintext => true,
-            PacketFlavorEnum::Ciphertext => true,
-        }
-    }
-}
-
-impl ToActionElement<Plaintext> for ContextSyncSeqNum {
-    fn bits(&self) -> u8 {
-        self.as_u8()
-    }
-}
-
-impl ToActionElement<Ciphertext> for ContextSyncSeqNum {
-    fn bits(&self) -> u8 {
-        self.as_u8()
-    }
-}
-
-// enabling an element for public adv requires privacy approval due to fingerprinting risk
-
-macros::boolean_element!(ActiveUnlock, 8, ciphertext_only);
-macros::boolean_element!(NearbyShare, 9, plaintext_and_ciphertext);
-macros::boolean_element!(InstantTethering, 10, ciphertext_only);
-macros::boolean_element!(PhoneHub, 11, ciphertext_only);
-macros::boolean_element!(PresenceManager, 12, ciphertext_only);
-macros::boolean_element!(Finder, 13, plaintext_and_ciphertext);
-macros::boolean_element!(FastPairSass, 14, plaintext_and_ciphertext);
diff --git a/nearby/presence/np_adv/src/legacy/actions/tests.rs b/nearby/presence/np_adv/src/legacy/actions/tests.rs
deleted file mode 100644
index 6e68211..0000000
--- a/nearby/presence/np_adv/src/legacy/actions/tests.rs
+++ /dev/null
@@ -1,368 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#![allow(clippy::unwrap_used)]
-
-extern crate std;
-
-use crate::legacy::{
-    actions::*, serialize::ToDataElementBundle, Ciphertext, PacketFlavorEnum, Plaintext,
-};
-use std::collections;
-
-#[test]
-fn set_context_sync_works() {
-    let mut actions = ActionBits::<Plaintext>::default();
-
-    actions.set_action(ContextSyncSeqNum::try_from(15).unwrap());
-    assert_eq_hex(0xF0000000, actions.bits);
-
-    assert_eq!(actions.bits_for_type(&ActionType::ContextSyncSeqNum), 15);
-}
-
-#[test]
-fn set_context_sync_doesnt_clobber_neighboring_bit() {
-    let mut actions = ActionBits::<Plaintext>::default();
-
-    // set bit just below
-    actions.bits |= 0x8000000;
-
-    actions.set_action(ContextSyncSeqNum::try_from(15).unwrap());
-    assert_eq_hex(0xF8000000, actions.bits);
-
-    assert_eq!(actions.bits_for_type(&ActionType::ContextSyncSeqNum), 15);
-}
-
-#[test]
-fn unset_context_sync_works() {
-    let mut actions = ActionBits::<Plaintext> {
-        // all 1s
-        bits: u32::MAX,
-        ..Default::default()
-    };
-
-    actions.set_action(ContextSyncSeqNum::try_from(0).unwrap());
-    assert_eq_hex(0x0FFFFFFF, actions.bits);
-
-    assert_eq!(actions.bits_for_type(&ActionType::ContextSyncSeqNum), 0);
-}
-
-#[test]
-fn set_ns_works() {
-    let mut actions = ActionBits::<Plaintext>::default();
-
-    actions.set_action(NearbyShare::from(true));
-    assert_eq_hex(0x00400000, actions.bits);
-
-    assert_eq!(actions.bits_for_type(&ActionType::NearbyShare), 1);
-    assert_eq!(actions.bits_for_type(&ActionType::ActiveUnlock), 0);
-    assert_eq!(actions.bits_for_type(&ActionType::InstantTethering), 0);
-}
-
-#[test]
-fn set_ns_doesnt_clobber_others() {
-    let mut actions = ActionBits::<Plaintext>::default();
-
-    // set neighboring bits
-    actions.bits |= 0x00120000;
-
-    actions.set_action(NearbyShare::from(true));
-    assert_eq_hex(0x00520000, actions.bits);
-
-    assert_eq!(actions.bits_for_type(&ActionType::NearbyShare), 1);
-    assert_eq!(actions.bits_for_type(&ActionType::PhoneHub), 1);
-    assert_eq!(actions.bits_for_type(&ActionType::FastPairSass), 1);
-}
-
-#[test]
-fn unset_ns_works() {
-    let mut actions = ActionBits::<Plaintext> {
-        // all 1s
-        bits: u32::MAX,
-        ..Default::default()
-    };
-
-    actions.set_action(NearbyShare::from(false));
-    assert_eq_hex(0xFFBFFFFF, actions.bits);
-}
-
-#[test]
-fn set_last_bit_works() {
-    let mut actions = ActionBits::<Plaintext>::default();
-
-    actions.set_action(LastBit::from(true));
-    assert_eq_hex(0x0100, actions.bits);
-}
-
-#[test]
-fn set_last_bit_doesnt_clobber_others() {
-    let mut actions = ActionBits::<Plaintext>::default();
-
-    // set neighboring bits
-    actions.bits |= 0x200;
-
-    actions.set_action(LastBit::from(true));
-    assert_eq_hex(0x300, actions.bits);
-}
-
-#[test]
-fn unset_last_bit_works() {
-    let mut actions = ActionBits::<Plaintext> {
-        // all 1s
-        bits: u32::MAX,
-        ..Default::default()
-    };
-
-    actions.set_action(LastBit::from(false));
-    assert_eq_hex(0xFFFFFEFF, actions.bits);
-}
-
-#[test]
-fn bytes_used_works() {
-    let mut actions = ActionBits::<Plaintext>::default();
-
-    // Special-case: All-zeroes should lead to a single byte being used.
-    assert_eq!(1, actions.bytes_used());
-
-    actions.set_action(ContextSyncSeqNum::try_from(3).unwrap());
-    assert_eq!(1, actions.bytes_used());
-
-    actions.set_action(NearbyShare::from(true));
-    assert_eq!(2, actions.bytes_used());
-
-    actions.set_action(LastBit::from(true));
-    assert_eq!(3, actions.bytes_used());
-
-    actions.set_action(LastBit::from(false));
-    assert_eq!(2, actions.bytes_used());
-}
-
-#[test]
-fn write_de_empty_actions() {
-    // The special case of no action bits set should still occupy one byte [of all zeroes].
-    let de = ActionsDataElement::<Plaintext>::from(ActionBits::default()).to_de_bundle();
-
-    assert_eq!(&[0x00], de.contents_as_slice());
-}
-
-#[test]
-fn write_de_one_action_byte() {
-    let mut action = ActionBits::default();
-    action.set_action(ContextSyncSeqNum::try_from(7).unwrap());
-    let de = ActionsDataElement::<Plaintext>::from(action).to_de_bundle();
-
-    assert_eq!(&[0x70], de.contents_as_slice());
-}
-
-#[test]
-fn write_de_three_action_bytes() {
-    let mut action = ActionBits::default();
-    action.set_action(LastBit::from(true));
-    let de = ActionsDataElement::<Plaintext>::from(action).to_de_bundle();
-
-    assert_eq!(&[0, 0, 1], de.contents_as_slice());
-}
-
-#[test]
-fn write_de_all_plaintext_actions() {
-    let mut action = all_plaintext_actions();
-    action.set_action(LastBit::from(true));
-    let de = ActionsDataElement::<Plaintext>::from(action).to_de_bundle();
-
-    // byte 0: context sync
-    // byte 1: nearby share, finder, fp sass
-    // byte 2: last bit
-    assert_eq!(&[0x90, 0x46, 0x01], de.contents_as_slice());
-}
-
-#[test]
-fn write_de_all_encrypted_actions() {
-    let mut action = all_ciphertext_actions();
-    action.set_action(LastBit::from(true));
-    let de = ActionsDataElement::<Ciphertext>::from(action).to_de_bundle();
-
-    // byte 1: context sync num = 9, 4 unused bits
-    // byte 2: active unlock, nearby share, instant tethering, phone hub,
-    // presence manager, last 3 bits unused
-    // byte 3: last bit
-    assert_eq!(&[0x90, 0xF8, 0x01], de.contents_as_slice());
-}
-
-#[test]
-fn action_element_nonzero_len() {
-    for t in ActionType::iter() {
-        assert!(t.bits_len() > 0);
-    }
-}
-
-#[test]
-fn action_element_bits_dont_overlap() {
-    let type_to_bits =
-        ActionType::iter().map(|t| (t, t.all_bits())).collect::<collections::HashMap<_, _>>();
-
-    for t in ActionType::iter() {
-        let bits = type_to_bits.get(&t).unwrap();
-
-        for (_, other_bits) in type_to_bits.iter().filter(|(other_type, _)| t != **other_type) {
-            assert_eq!(0, bits & other_bits, "type {t:?}");
-        }
-    }
-}
-
-#[test]
-fn action_type_all_bits() {
-    assert_eq!(0xF0000000, ActionType::ContextSyncSeqNum.all_bits());
-    assert_eq!(0x00800000, ActionType::ActiveUnlock.all_bits());
-    assert_eq!(0x00020000, ActionType::FastPairSass.all_bits());
-}
-
-#[test]
-fn action_type_all_bits_in_per_type_masks() {
-    for t in ActionType::iter().filter(|t| t.supports_flavor(PacketFlavorEnum::Plaintext)) {
-        assert_eq!(t.all_bits(), t.all_bits() & *ALL_PLAINTEXT_ELEMENT_BITS);
-    }
-    for t in ActionType::iter().filter(|t| t.supports_flavor(PacketFlavorEnum::Ciphertext)) {
-        assert_eq!(t.all_bits(), t.all_bits() & *ALL_CIPHERTEXT_ELEMENT_BITS);
-    }
-}
-
-#[test]
-fn action_bits_try_from_flavor_mismatch_plaintext() {
-    assert_eq!(
-        FlavorNotSupported { flavor: PacketFlavorEnum::Plaintext },
-        ActionBits::<Plaintext>::try_from(ActionType::PresenceManager.all_bits()).unwrap_err()
-    );
-    assert_eq!(
-        0xF0000000,
-        ActionBits::<Plaintext>::try_from(ActionType::ContextSyncSeqNum.all_bits()).unwrap().bits
-    );
-}
-
-#[test]
-fn actions_de_deser_plaintext_with_ciphertext_action() {
-    assert_eq!(
-        DataElementDeserializeError::FlavorNotSupported {
-            de_type: DataElementType::Actions,
-            flavor: PacketFlavorEnum::Plaintext
-        },
-        <ActionsDataElement<Plaintext> as DataElement>::deserialize::<Plaintext>(&[
-            // active unlock bit set
-            0x00, 0x80, 0x00,
-        ])
-        .unwrap_err()
-    );
-}
-
-#[test]
-fn actions_de_deser_ciphertext_with_plaintext_action() {
-    assert_eq!(
-        DataElementDeserializeError::FlavorNotSupported {
-            de_type: DataElementType::Actions,
-            flavor: PacketFlavorEnum::Ciphertext
-        },
-        <ActionsDataElement<Ciphertext> as DataElement>::deserialize::<Ciphertext>(&[
-            // Finder bit set
-            0x00, 0x00, 0x80,
-        ])
-        .unwrap_err()
-    );
-}
-
-#[test]
-fn context_sync_seq_num_works() {
-    let mut action_bits = ActionBits::<Plaintext>::default();
-    action_bits.set_action(ContextSyncSeqNum::try_from(15).unwrap());
-    let action_de = ActionsDataElement::from(action_bits);
-    assert_eq!(15, action_de.action.context_sync_seq_num().as_u8());
-}
-
-#[test]
-fn context_sync_seq_num_default_zero() {
-    let action_bits = ActionBits::<Plaintext>::default();
-    let action_de = ActionsDataElement::from(action_bits);
-    assert_eq!(0, action_de.action.context_sync_seq_num().as_u8());
-}
-
-#[test]
-fn has_action_plaintext_works() {
-    let mut action_bits = ActionBits::<Plaintext>::default();
-    action_bits.set_action(ContextSyncSeqNum::try_from(15).unwrap());
-    action_bits.set_action(NearbyShare::from(true));
-    let action_de = ActionsDataElement::from(action_bits);
-    assert_eq!(action_de.action.has_action(&ActionType::NearbyShare), Some(true));
-    assert_eq!(action_de.action.has_action(&ActionType::ActiveUnlock), Some(false));
-    assert_eq!(action_de.action.has_action(&ActionType::PhoneHub), Some(false));
-}
-
-#[test]
-fn has_action_encrypted_works() {
-    let mut action_bits = ActionBits::<Ciphertext>::default();
-    action_bits.set_action(ContextSyncSeqNum::try_from(15).unwrap());
-    action_bits.set_action(NearbyShare::from(true));
-    action_bits.set_action(ActiveUnlock::from(true));
-    let action_de = ActionsDataElement::from(action_bits);
-    assert_eq!(action_de.action.has_action(&ActionType::NearbyShare), Some(true));
-    assert_eq!(action_de.action.has_action(&ActionType::ActiveUnlock), Some(true));
-    assert_eq!(action_de.action.has_action(&ActionType::PhoneHub), Some(false));
-    assert_eq!(action_de.action.has_action(&ActionType::ContextSyncSeqNum), None);
-}
-
-// hypothetical action using the last bit
-#[derive(Debug)]
-struct LastBit {
-    enabled: bool,
-}
-impl From<bool> for LastBit {
-    fn from(value: bool) -> Self {
-        LastBit { enabled: value }
-    }
-}
-impl ActionElement for LastBit {
-    const HIGH_BIT_INDEX: u32 = 23;
-    const BITS_LEN: u32 = 1;
-
-    // don't want to add a variant for this test only type
-    const ACTION_TYPE: ActionType = ActionType::ActiveUnlock;
-
-    fn supports_flavor(_flavor: PacketFlavorEnum) -> bool {
-        true
-    }
-}
-
-macros::boolean_element_to_plaintext_element!(LastBit);
-macros::boolean_element_to_encrypted_element!(LastBit);
-
-fn assert_eq_hex(expected: u32, actual: u32) {
-    assert_eq!(expected, actual, "{expected:#010X} != {actual:#010X}");
-}
-
-pub(crate) fn all_plaintext_actions() -> ActionBits<Plaintext> {
-    let mut action = ActionBits::default();
-    action.set_action(ContextSyncSeqNum::try_from(9).unwrap());
-    action.set_action(NearbyShare::from(true));
-    action.set_action(Finder::from(true));
-    action.set_action(FastPairSass::from(true));
-    action
-}
-
-pub(crate) fn all_ciphertext_actions() -> ActionBits<Ciphertext> {
-    let mut action = ActionBits::default();
-    action.set_action(ContextSyncSeqNum::try_from(9).unwrap());
-    action.set_action(ActiveUnlock::from(true));
-    action.set_action(NearbyShare::from(true));
-    action.set_action(InstantTethering::from(true));
-    action.set_action(PhoneHub::from(true));
-    action.set_action(PresenceManager::from(true));
-    action
-}
diff --git a/nearby/presence/np_adv/src/legacy/data_elements.rs b/nearby/presence/np_adv/src/legacy/data_elements.rs
deleted file mode 100644
index c97cb25..0000000
--- a/nearby/presence/np_adv/src/legacy/data_elements.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! V0 data elements and core trait impls.
-use nom::error::{ErrorKind, FromExternalError};
-
-use crate::legacy::{
-    de_type::{DataElementType, PlainDataElementType},
-    serialize::{DataElementBundle, ToDataElementBundle},
-    PacketFlavor, PacketFlavorEnum,
-};
-use crate::shared_data::*;
-
-/// Core behavior for data elements.
-///
-/// See also [ToDataElementBundle] for flavor-specific, infallible serialization.
-pub trait DataElement: Sized {
-    /// The corresponding DataElementType variant.
-    const DE_TYPE_VARIANT: DataElementType;
-
-    /// Return true if the DE supports serialization and deserialization for the provided flavor.
-    fn supports_flavor(flavor: PacketFlavorEnum) -> bool;
-
-    /// Returns `Err` if the flavor is not supported or if parsing fails.
-    ///
-    /// `<F>` is the flavor of the packet being deserialized.
-    fn deserialize<F: PacketFlavor>(
-        de_contents: &[u8],
-    ) -> Result<Self, DataElementDeserializeError>;
-}
-
-/// Errors possible when deserializing a DE
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum DataElementDeserializeError {
-    /// The data element doesn't support the [PacketFlavor] of the advertisement packet.
-    FlavorNotSupported {
-        /// The DE type attempting to be deserialized
-        de_type: DataElementType,
-        /// The flavor that was not supported
-        flavor: PacketFlavorEnum,
-    },
-    /// The data element couldn't be deserialized from the supplied data.
-    DeserializeError {
-        /// The DE type attempting to be deserialized
-        de_type: DataElementType,
-    },
-    /// Only one identity data element is allowed in an advertisement, but a duplicate is found
-    /// while parsing.
-    DuplicateIdentityDataElement,
-    /// There is unexpected data remaining in the incoming payload.
-    UnexpectedDataRemaining,
-    /// Parsing error returned from Nom.
-    NomError(nom::error::ErrorKind),
-}
-
-impl FromExternalError<&[u8], DataElementDeserializeError> for DataElementDeserializeError {
-    fn from_external_error(
-        _input: &[u8],
-        _kind: ErrorKind,
-        e: DataElementDeserializeError,
-    ) -> Self {
-        e
-    }
-}
-
-impl nom::error::ParseError<&[u8]> for DataElementDeserializeError {
-    /// Creates an error from the input position and an [ErrorKind]
-    fn from_error_kind(_input: &[u8], kind: ErrorKind) -> Self {
-        Self::NomError(kind)
-    }
-
-    /// Combines an existing error with a new one created from the input
-    /// position and an [ErrorKind]. This is useful when backtracking
-    /// through a parse tree, accumulating error context on the way
-    fn append(_input: &[u8], kind: ErrorKind, _other: Self) -> Self {
-        Self::NomError(kind)
-    }
-}
-
-/// Data element holding a [TxPower].
-#[derive(Debug, PartialEq, Eq)]
-pub struct TxPowerDataElement {
-    /// The tx power value
-    pub tx_power: TxPower,
-}
-
-impl TxPowerDataElement {
-    /// Gets the underlying Tx Power value
-    pub fn tx_power_value(&self) -> i8 {
-        self.tx_power.as_i8()
-    }
-}
-
-impl From<TxPower> for TxPowerDataElement {
-    fn from(tx_power: TxPower) -> Self {
-        Self { tx_power }
-    }
-}
-
-impl<F: PacketFlavor> ToDataElementBundle<F> for TxPowerDataElement {
-    fn to_de_bundle(&self) -> DataElementBundle<F> {
-        let tx_power = self.tx_power.as_i8();
-        DataElementBundle::try_from(
-            PlainDataElementType::TxPower,
-            tx_power.to_be_bytes().as_slice(),
-        )
-        .expect("Length < max DE size")
-    }
-}
-
-impl DataElement for TxPowerDataElement {
-    const DE_TYPE_VARIANT: DataElementType = DataElementType::TxPower;
-
-    fn supports_flavor(flavor: PacketFlavorEnum) -> bool {
-        match flavor {
-            PacketFlavorEnum::Plaintext => true,
-            PacketFlavorEnum::Ciphertext => true,
-        }
-    }
-    fn deserialize<F: PacketFlavor>(
-        de_contents: &[u8],
-    ) -> Result<Self, DataElementDeserializeError> {
-        de_contents
-            .try_into()
-            .ok()
-            .and_then(|arr: [u8; 1]| TxPower::try_from(i8::from_be_bytes(arr)).ok())
-            .map(|tx_power| Self { tx_power })
-            .ok_or(DataElementDeserializeError::DeserializeError {
-                de_type: DataElementType::TxPower,
-            })
-    }
-}
diff --git a/nearby/presence/np_adv/src/legacy/actions/macros.rs b/nearby/presence/np_adv/src/legacy/data_elements/actions/macros.rs
similarity index 64%
rename from nearby/presence/np_adv/src/legacy/actions/macros.rs
rename to nearby/presence/np_adv/src/legacy/data_elements/actions/macros.rs
index db241d6..3d1ab48 100644
--- a/nearby/presence/np_adv/src/legacy/actions/macros.rs
+++ b/nearby/presence/np_adv/src/legacy/data_elements/actions/macros.rs
@@ -38,9 +38,8 @@
 macro_rules! boolean_element_action_element_impl_shared {
     ($type_name:ident, $index:expr) => {
         const HIGH_BIT_INDEX: u32 = $index;
-        const BITS_LEN: u32 = 1;
-        const ACTION_TYPE: $crate::legacy::actions::ActionType =
-            $crate::legacy::actions::ActionType::$type_name;
+        const ACTION_TYPE: $crate::legacy::data_elements::actions::ActionType =
+            $crate::legacy::data_elements::actions::ActionType::$type_name;
     };
 }
 
@@ -49,11 +48,11 @@
 /// impls.
 macro_rules! boolean_element {
     ($type_name:ident, $index:expr, ciphertext_only) => {
-        $crate::legacy::actions::macros::boolean_element_struct!($type_name);
-        $crate::legacy::actions::macros::boolean_element_struct_from_bool!($type_name);
+        $crate::legacy::data_elements::actions::macros::boolean_element_struct!($type_name);
+        $crate::legacy::data_elements::actions::macros::boolean_element_struct_from_bool!($type_name);
 
-        impl $crate::legacy::actions::ActionElement for $type_name {
-            $crate::legacy::actions::macros::boolean_element_action_element_impl_shared!(
+        impl $crate::legacy::data_elements::actions::ActionElement for $type_name {
+            $crate::legacy::data_elements::actions::macros::boolean_element_action_element_impl_shared!(
                 $type_name, $index
             );
 
@@ -63,16 +62,20 @@
                     $crate::legacy::PacketFlavorEnum::Ciphertext => true,
                 }
             }
+
+            fn bits(&self) -> u8 {
+                self.enabled as u8
+            }
         }
 
-        $crate::legacy::actions::macros::boolean_element_to_encrypted_element!($type_name);
+        $crate::legacy::data_elements::actions::macros::boolean_element_to_encrypted_element!($type_name);
     };
     ($type_name:ident, $index:expr, plaintext_and_ciphertext) => {
-        $crate::legacy::actions::macros::boolean_element_struct!($type_name);
-        $crate::legacy::actions::macros::boolean_element_struct_from_bool!($type_name);
+        $crate::legacy::data_elements::actions::macros::boolean_element_struct!($type_name);
+        $crate::legacy::data_elements::actions::macros::boolean_element_struct_from_bool!($type_name);
 
-        impl $crate::legacy::actions::ActionElement for $type_name {
-            $crate::legacy::actions::macros::boolean_element_action_element_impl_shared!(
+        impl $crate::legacy::data_elements::actions::ActionElement for $type_name {
+            $crate::legacy::data_elements::actions::macros::boolean_element_action_element_impl_shared!(
                 $type_name, $index
             );
 
@@ -82,31 +85,33 @@
                     $crate::legacy::PacketFlavorEnum::Ciphertext => true,
                 }
             }
+
+            fn bits(&self) -> u8 {
+                self.enabled as u8
+            }
         }
 
-        $crate::legacy::actions::macros::boolean_element_to_plaintext_element!($type_name);
-        $crate::legacy::actions::macros::boolean_element_to_encrypted_element!($type_name);
+        $crate::legacy::data_elements::actions::macros::boolean_element_to_plaintext_element!($type_name);
+        $crate::legacy::data_elements::actions::macros::boolean_element_to_encrypted_element!($type_name);
     };
 }
 
-/// Create a [`ToActionElement<Encrypted>`](super::ToActionElement) impl with the given index and length 1.
+/// Create a [`ToActionElement<Encrypted>`](super::ActionElementFlavor) impl with the given index and length 1.
 macro_rules! boolean_element_to_encrypted_element {
     ( $type_name:ident) => {
-        impl $crate::legacy::actions::ToActionElement<$crate::legacy::Ciphertext> for $type_name {
-            fn bits(&self) -> u8 {
-                self.enabled as u8
-            }
+        impl $crate::legacy::data_elements::actions::ActionElementFlavor<$crate::legacy::Ciphertext>
+            for $type_name
+        {
         }
     };
 }
 
-/// Create a [`ToActionElement<Plaintext>`](super::ToActionElement) impl with the given index and length 1.
+/// Create a [`ToActionElement<Plaintext>`](super::ActionElementFlavor) impl with the given index and length 1.
 macro_rules! boolean_element_to_plaintext_element {
     ( $type_name:ident) => {
-        impl $crate::legacy::actions::ToActionElement<$crate::legacy::Plaintext> for $type_name {
-            fn bits(&self) -> u8 {
-                self.enabled as u8
-            }
+        impl $crate::legacy::data_elements::actions::ActionElementFlavor<$crate::legacy::Plaintext>
+            for $type_name
+        {
         }
     };
 }
diff --git a/nearby/presence/np_adv/src/legacy/data_elements/actions/mod.rs b/nearby/presence/np_adv/src/legacy/data_elements/actions/mod.rs
new file mode 100644
index 0000000..36ac35d
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/data_elements/actions/mod.rs
@@ -0,0 +1,351 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! The "Actions" data element and associated types.
+//!
+//! This DE is somewhat more complex than other DEs. Whether or not it supports a particular flavor
+//! depends on the actions set, so it has to be treated as two separate types based on which
+//! flavor type parameter is used.
+use crate::legacy::data_elements::{DirectMapPredicate, DirectMapper, LengthMapper};
+use crate::{
+    legacy::{
+        data_elements::{
+            de_type::{DeActualLength, DeEncodedLength, DeTypeCode},
+            DataElementDeserializeError, DataElementSerializationBuffer, DataElementSerializeError,
+            DeserializeDataElement, SerializeDataElement,
+        },
+        PacketFlavor, PacketFlavorEnum,
+    },
+    private::Sealed,
+};
+
+#[cfg(feature = "devtools")]
+use core::ops::Range;
+use core::{marker, ops};
+use nom::{bytes, combinator, error};
+use sink::Sink;
+use strum::IntoEnumIterator as _;
+
+mod macros;
+#[cfg(test)]
+pub(crate) mod tests;
+
+/// Actions DE.
+/// Only as many DE payload bytes will be present as needed to represent all set bits that are encoded,
+/// with a lower bound of 1 byte in the special case of no set action bits, and an upper bound
+/// of 3 bytes occupied by the DE payload.
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct ActionsDataElement<F: PacketFlavor> {
+    /// The action bits
+    pub action: ActionBits<F>,
+}
+
+/// Max length of an actions DE contents
+pub(crate) const ACTIONS_MAX_LEN: usize = 3;
+/// Range of valid actual lengths
+pub(crate) const ACTIONS_VALID_ACTUAL_LEN: ops::RangeInclusive<usize> = 1..=ACTIONS_MAX_LEN;
+
+impl<F> ActionsDataElement<F>
+where
+    F: PacketFlavor,
+{
+    /// Generic deserialize, not meant to be called directly -- use [DeserializeDataElement] impls instead.
+    #[allow(clippy::assertions_on_constants)]
+    fn deserialize(de_contents: &[u8]) -> Result<Self, DataElementDeserializeError> {
+        combinator::all_consuming::<&[u8], _, error::Error<&[u8]>, _>(combinator::map(
+            bytes::complete::take_while_m_n(0, ACTIONS_MAX_LEN, |_| true),
+            |bytes: &[u8]| {
+                // pack bits into u32 for convenient access
+                debug_assert!(4 >= ACTIONS_MAX_LEN, "Actions must fit in u32");
+                let mut action_bytes = [0_u8; 4];
+                action_bytes[..bytes.len()].copy_from_slice(bytes);
+                u32::from_be_bytes(action_bytes)
+            },
+        ))(de_contents)
+        .map_err(|_| DataElementDeserializeError::DeserializeError { de_type: Self::DE_TYPE_CODE })
+        .map(|(_remaining, actions)| actions)
+        .and_then(|action_bits_num| {
+            let action = ActionBits::try_from(action_bits_num).map_err(|e| {
+                DataElementDeserializeError::FlavorNotSupported {
+                    de_type: Self::DE_TYPE_CODE,
+                    flavor: e.flavor,
+                }
+            })?;
+            Ok(Self { action })
+        })
+    }
+}
+
+impl<F: PacketFlavor> From<ActionBits<F>> for ActionsDataElement<F> {
+    fn from(action: ActionBits<F>) -> Self {
+        Self { action }
+    }
+}
+
+impl<F: PacketFlavor> Sealed for ActionsDataElement<F> {}
+
+impl<F: PacketFlavor> SerializeDataElement<F> for ActionsDataElement<F> {
+    fn de_type_code(&self) -> DeTypeCode {
+        ActionsDataElement::<F>::DE_TYPE_CODE
+    }
+
+    fn map_actual_len_to_encoded_len(&self, actual_len: DeActualLength) -> DeEncodedLength {
+        <Self as DeserializeDataElement>::LengthMapper::map_actual_len_to_encoded_len(actual_len)
+    }
+
+    fn serialize_contents(
+        &self,
+        sink: &mut DataElementSerializationBuffer,
+    ) -> Result<(), DataElementSerializeError> {
+        let used = self.action.bytes_used();
+        sink.try_extend_from_slice(&self.action.bits.to_be_bytes()[..used])
+            .ok_or(DataElementSerializeError::InsufficientSpace)
+    }
+}
+
+impl<E: PacketFlavor> DeserializeDataElement for ActionsDataElement<E> {
+    const DE_TYPE_CODE: DeTypeCode = match DeTypeCode::try_from(0b0110) {
+        Ok(t) => t,
+        Err(_) => unreachable!(),
+    };
+
+    type LengthMapper = DirectMapper<ActionsLengthPredicate>;
+
+    fn deserialize<F: PacketFlavor>(
+        de_contents: &[u8],
+    ) -> Result<Self, DataElementDeserializeError> {
+        if E::ENUM_VARIANT == F::ENUM_VARIANT {
+            ActionsDataElement::deserialize(de_contents)
+        } else {
+            Err(DataElementDeserializeError::FlavorNotSupported {
+                de_type: Self::DE_TYPE_CODE,
+                flavor: F::ENUM_VARIANT,
+            })
+        }
+    }
+}
+
+pub(in crate::legacy) struct ActionsLengthPredicate;
+
+impl DirectMapPredicate for ActionsLengthPredicate {
+    fn is_valid(len: usize) -> bool {
+        ACTIONS_VALID_ACTUAL_LEN.contains(&len)
+    }
+}
+
+/// Container for the 24 bits defined for "actions" (feature flags and the like).
+/// This internally stores a u32, but only the 24 highest bits of this
+/// field will actually ever be populated.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct ActionBits<F: PacketFlavor> {
+    bits: u32,
+    // marker for element type
+    flavor: marker::PhantomData<F>,
+}
+
+impl<F: PacketFlavor> ActionBits<F> {
+    /// Returns the actions bits as a u32. The upper limit of an actions field is 3 bytes,
+    /// so the last bytes of this u32 will always be 0
+    pub fn as_u32(self) -> u32 {
+        self.bits
+    }
+
+    /// Return whether a boolean action type is set in this data element, or `None` if the given
+    /// action type does not represent a boolean.
+    pub fn has_action(&self, action_type: ActionType) -> bool {
+        self.bits_for_type(action_type) != 0
+    }
+}
+
+impl<F: PacketFlavor> Default for ActionBits<F> {
+    fn default() -> Self {
+        ActionBits {
+            bits: 0, // no bits set
+            flavor: marker::PhantomData,
+        }
+    }
+}
+
+/// At least one action doesn't support the required flavor
+#[derive(PartialEq, Eq, Debug)]
+pub struct FlavorNotSupported {
+    flavor: PacketFlavorEnum,
+}
+
+lazy_static::lazy_static! {
+    /// All bits for plaintext action types: 1 where a plaintext action could have a bit, 0 elsewhere.
+    static ref ALL_PLAINTEXT_ELEMENT_BITS: u32 = ActionType::iter()
+        .filter(|t| t.supports_flavor(PacketFlavorEnum::Plaintext))
+        .map(|t| t.all_bits())
+        .fold(0_u32, |accum, bits| accum | bits);
+}
+
+lazy_static::lazy_static! {
+    /// All bits for ciphertext action types: 1 where a ciphertext action could have a bit, 0 elsewhere.
+    static ref ALL_CIPHERTEXT_ELEMENT_BITS: u32 = ActionType::iter()
+        .filter(|t| t.supports_flavor(PacketFlavorEnum::Ciphertext))
+        .map(|t| t.all_bits())
+        .fold(0_u32, |accum, bits| accum | bits);
+}
+
+impl<F: PacketFlavor> ActionBits<F> {
+    /// Tries to create ActionBits from a u32, returning error in the event a specific bit is set for
+    /// an unsupported flavor
+    pub fn try_from(value: u32) -> Result<Self, FlavorNotSupported> {
+        let ok_bits: u32 = match F::ENUM_VARIANT {
+            PacketFlavorEnum::Plaintext => *ALL_PLAINTEXT_ELEMENT_BITS,
+            PacketFlavorEnum::Ciphertext => *ALL_CIPHERTEXT_ELEMENT_BITS,
+        };
+
+        // no bits set beyond what's allowed for this flavor
+        if value | ok_bits == ok_bits {
+            Ok(Self { bits: value, flavor: marker::PhantomData })
+        } else {
+            Err(FlavorNotSupported { flavor: F::ENUM_VARIANT })
+        }
+    }
+
+    /// Set the bits for the provided element.
+    /// Bits outside the range set by the action will be unaffected.
+    pub fn set_action<E: ActionElementFlavor<F>>(&mut self, action_element: E) {
+        let bits = action_element.bits();
+
+        // validate that the element is not horribly broken
+        debug_assert!(E::HIGH_BIT_INDEX < 32);
+        // must not have bits set past the low `len` bits
+        debug_assert_eq!(0, bits >> 1);
+
+        // 0-extend to u32
+        let byte_extended = bits as u32;
+        // Shift so that the high bit is at the desired index.
+        // Won't overflow since length > 0.
+        let bits_in_position = byte_extended << (31 - E::HIGH_BIT_INDEX);
+
+        // We want to effectively clear out the bits already in place, so we don't want to just |=.
+        // Instead, we construct a u32 with all 1s above and below the relevant bits and &=, so that
+        // if the new bits are 0, the stored bits will be cleared.
+
+        // avoid overflow when index = 0 -- need zero 1 bits to the left in that case
+        let left_1s = u32::MAX.checked_shl(32 - E::HIGH_BIT_INDEX).unwrap_or(0);
+        // avoid underflow when index + len = 32 -- zero 1 bits to the right
+        let right_1s = u32::MAX.checked_shr(E::HIGH_BIT_INDEX + 1).unwrap_or(0);
+        let mask = left_1s | right_1s;
+        let bits_for_other_actions = self.bits & mask;
+        self.bits = bits_for_other_actions | bits_in_position;
+    }
+
+    /// How many bytes (1-3) are needed to represent the set bits, starting from the most
+    /// significant bit. The lower bound of 1 is because the unique special case of
+    /// an actions field of all zeroes is required by the spec to occupy exactly one byte.
+    fn bytes_used(&self) -> usize {
+        let bits_used = 32 - self.bits.trailing_zeros();
+        let raw_count = (bits_used as usize + 7) / 8;
+        if raw_count == 0 {
+            1 // Uncommon case - should only be hit for all-zero action bits
+        } else {
+            raw_count
+        }
+    }
+
+    /// Return the bits for a given action type as the low bits in the returned u32.
+    ///
+    /// For example, when extracting the bits `B` from `0bXXXXXXXXXXBBBBBBXXXXXXXXXXXXXXXX`, the
+    /// return value will be `0b00000000000000000000000000BBBBBB`.
+    pub fn bits_for_type(&self, action_type: ActionType) -> u32 {
+        self.bits << action_type.high_bit_index() >> (31)
+    }
+}
+
+/// Core trait for an individual action
+pub trait ActionElement {
+    /// The assigned offset for this type from the high bit in the eventual bit sequence of all
+    /// actions.
+    ///
+    /// Each implementation must have a non-conflicting index defined by
+    /// [Self::HIGH_BIT_INDEX]
+    const HIGH_BIT_INDEX: u32;
+
+    /// Forces implementations to have a matching enum variant so the enum can be kept up to date.
+    const ACTION_TYPE: ActionType;
+
+    /// Returns whether this action supports the provided `flavor`.
+    ///
+    /// Must match the implementations of [ActionElementFlavor].
+    fn supports_flavor(flavor: PacketFlavorEnum) -> bool;
+
+    /// Returns the low bit that should be included in the final bit vector
+    /// starting at [Self::HIGH_BIT_INDEX].
+    fn bits(&self) -> u8;
+}
+
+/// Marker trait indicating support for a particular [PacketFlavor].
+pub trait ActionElementFlavor<F: PacketFlavor>: ActionElement {}
+
+/// Provides a way to iterate over all action types.
+#[derive(Clone, Copy, strum_macros::EnumIter, PartialEq, Eq, Hash, Debug)]
+#[allow(missing_docs)]
+pub enum ActionType {
+    CrossDevSdk,
+    CallTransfer,
+    ActiveUnlock,
+    NearbyShare,
+    InstantTethering,
+    PhoneHub,
+}
+
+impl ActionType {
+    /// A u32 with all possible bits for this action type set
+    const fn all_bits(&self) -> u32 {
+        (u32::MAX << (31_u32)) >> self.high_bit_index()
+    }
+
+    /// Get the range of the bits occupied used by this bit index. For example, if the action type
+    /// uses the 5th and 6th bits, the returned range will be (5..7).
+    /// (0 is the index of the most significant bit).
+    #[cfg(feature = "devtools")]
+    pub const fn bits_range_for_devtools(&self) -> Range<u32> {
+        let high_bit_index = self.high_bit_index();
+        high_bit_index..high_bit_index + 1
+    }
+
+    const fn high_bit_index(&self) -> u32 {
+        match self {
+            ActionType::CrossDevSdk => CrossDevSdk::HIGH_BIT_INDEX,
+            ActionType::CallTransfer => CallTransfer::HIGH_BIT_INDEX,
+            ActionType::ActiveUnlock => ActiveUnlock::HIGH_BIT_INDEX,
+            ActionType::NearbyShare => NearbyShare::HIGH_BIT_INDEX,
+            ActionType::InstantTethering => InstantTethering::HIGH_BIT_INDEX,
+            ActionType::PhoneHub => PhoneHub::HIGH_BIT_INDEX,
+        }
+    }
+
+    pub(crate) fn supports_flavor(&self, flavor: PacketFlavorEnum) -> bool {
+        match self {
+            ActionType::CrossDevSdk => CrossDevSdk::supports_flavor(flavor),
+            ActionType::CallTransfer => CallTransfer::supports_flavor(flavor),
+            ActionType::ActiveUnlock => ActiveUnlock::supports_flavor(flavor),
+            ActionType::NearbyShare => NearbyShare::supports_flavor(flavor),
+            ActionType::InstantTethering => InstantTethering::supports_flavor(flavor),
+            ActionType::PhoneHub => PhoneHub::supports_flavor(flavor),
+        }
+    }
+}
+
+// enabling an element for public adv requires privacy approval due to fingerprinting risk
+macros::boolean_element!(CrossDevSdk, 1, plaintext_and_ciphertext);
+macros::boolean_element!(CallTransfer, 4, ciphertext_only);
+macros::boolean_element!(ActiveUnlock, 8, ciphertext_only);
+macros::boolean_element!(NearbyShare, 9, plaintext_and_ciphertext);
+macros::boolean_element!(InstantTethering, 10, ciphertext_only);
+macros::boolean_element!(PhoneHub, 11, ciphertext_only);
diff --git a/nearby/presence/np_adv/src/legacy/data_elements/actions/tests.rs b/nearby/presence/np_adv/src/legacy/data_elements/actions/tests.rs
new file mode 100644
index 0000000..b96f94a
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/data_elements/actions/tests.rs
@@ -0,0 +1,690 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#![allow(clippy::unwrap_used)]
+
+extern crate std;
+
+use crate::{
+    legacy::{
+        data_elements::{
+            actions::{self, *},
+            tests::macros::de_roundtrip_test,
+        },
+        serialize::encode_de_header,
+        serialize::tests::serialize,
+        Ciphertext, Plaintext,
+    },
+    DeLengthOutOfRange,
+};
+use rand::seq::SliceRandom;
+use rand::Rng;
+use std::collections;
+use std::panic;
+use std::prelude::rust_2021::*;
+
+#[test]
+fn setting_action_only_changes_that_actions_bits() {
+    fn do_test<F: PacketFlavor>(
+        set_ones: impl Fn(ActionType, &mut ActionBits<F>),
+        set_zeros: impl Fn(ActionType, &mut ActionBits<F>),
+    ) {
+        for t in supported_action_types(F::ENUM_VARIANT) {
+            let other_types = supported_action_types(F::ENUM_VARIANT)
+                .into_iter()
+                .filter(|t2| *t2 != t)
+                .collect::<Vec<_>>();
+
+            let mut actions = ActionBits::<F>::default();
+            set_ones(t, &mut actions);
+
+            // only the correct bits are set internally
+            assert_eq!(t.all_bits(), actions.as_u32());
+            // we can extract those bits
+            assert_eq!(t.all_bits() >> (31 - t.high_bit_index()), actions.bits_for_type(t));
+            // consider context sync (None) to be "set" for our purposes
+            assert!(actions.has_action(t));
+            // other types aren't set
+            for &t2 in &other_types {
+                assert_eq!(0, actions.bits_for_type(t2))
+            }
+
+            // now check that unsetting works
+            actions.bits = u32::MAX;
+            set_zeros(t, &mut actions);
+
+            assert_eq!(!t.all_bits(), actions.as_u32());
+            assert_eq!(0, actions.bits_for_type(t));
+            assert!(!actions.has_action(t));
+            // other types are set
+            for &t2 in &other_types {
+                assert_eq!(t2.all_bits() >> (31 - t2.high_bit_index()), actions.bits_for_type(t2));
+            }
+        }
+    }
+
+    do_test(
+        |t, bits| set_plaintext_action(t, true, bits),
+        |t, bits| set_plaintext_action(t, false, bits),
+    );
+    do_test(
+        |t, bits| set_ciphertexttext_action(t, true, bits),
+        |t, bits| set_ciphertexttext_action(t, false, bits),
+    );
+}
+
+#[test]
+fn random_combos_of_actions_have_correct_bits_set() {
+    fn do_test<F: PacketFlavor>(set_ones: impl Fn(ActionType, &mut ActionBits<F>)) {
+        let all_types = supported_action_types(F::ENUM_VARIANT);
+        let mut rng = rand::thread_rng();
+
+        for _ in 0..1000 {
+            let len = rng.gen_range(0..=all_types.len());
+            let selected = all_types.choose_multiple(&mut rng, len).copied().collect::<Vec<_>>();
+            let not_selected =
+                all_types.iter().filter(|t| !selected.contains(t)).copied().collect::<Vec<_>>();
+
+            let mut actions = ActionBits::<F>::default();
+            for &t in &selected {
+                set_ones(t, &mut actions);
+            }
+
+            for &t in &selected {
+                assert_ne!(0, actions.bits_for_type(t));
+            }
+            for &t in &not_selected {
+                assert_eq!(0, actions.bits_for_type(t));
+            }
+
+            assert_eq!(selected.iter().fold(0, |accum, t| accum | t.all_bits()), actions.bits);
+        }
+    }
+
+    do_test::<Plaintext>(|t, bits| set_plaintext_action(t, true, bits));
+    do_test::<Ciphertext>(|t, bits| set_ciphertexttext_action(t, true, bits));
+}
+
+#[test]
+fn set_last_bit_works() {
+    let mut actions = ActionBits::<Plaintext>::default();
+
+    actions.set_action(LastBit::from(true));
+    assert_eq_hex(0x0100, actions.bits);
+}
+
+#[test]
+fn set_last_bit_doesnt_clobber_others() {
+    let mut actions = ActionBits::<Plaintext>::default();
+
+    // set neighboring bits
+    actions.bits |= 0x200;
+
+    actions.set_action(LastBit::from(true));
+    assert_eq_hex(0x300, actions.bits);
+}
+
+#[test]
+fn unset_last_bit_works() {
+    let mut actions = ActionBits::<Plaintext> {
+        // all 1s
+        bits: u32::MAX,
+        ..Default::default()
+    };
+
+    actions.set_action(LastBit::from(false));
+    assert_eq_hex(0xFFFFFEFF, actions.bits);
+}
+
+#[test]
+fn bytes_used_works() {
+    let mut actions = ActionBits::<Plaintext>::default();
+
+    // Special-case: All-zeroes should lead to a single byte being used.
+    assert_eq!(1, actions.bytes_used());
+
+    actions.set_action(NearbyShare::from(true));
+    assert_eq!(2, actions.bytes_used());
+
+    actions.set_action(LastBit::from(true));
+    assert_eq!(3, actions.bytes_used());
+
+    actions.set_action(LastBit::from(false));
+    assert_eq!(2, actions.bytes_used());
+}
+
+#[test]
+fn write_de_empty_actions() {
+    // The special case of no action bits set should still occupy one byte [of all zeroes].
+
+    assert_eq!(
+        &[actions_de_header_byte(1), 0x00],
+        serialize(&ActionsDataElement::<Plaintext>::from(ActionBits::default())).as_slice()
+    );
+}
+
+#[test]
+fn write_de_one_action_byte() {
+    let mut action = ActionBits::default();
+    action.set_action(FirstBit::from(true));
+
+    assert_eq!(
+        &[actions_de_header_byte(1), 0b1000_0000],
+        serialize(&ActionsDataElement::<Plaintext>::from(action)).as_slice()
+    );
+}
+
+#[test]
+fn write_de_three_action_bytes() {
+    let mut action = ActionBits::default();
+    action.set_action(LastBit::from(true));
+
+    assert_eq!(
+        &[actions_de_header_byte(3), 0, 0, 1],
+        serialize(&ActionsDataElement::<Plaintext>::from(action)).as_slice()
+    );
+}
+
+#[test]
+fn write_de_all_plaintext_actions() {
+    let mut action = all_plaintext_actions_set();
+    action.set_action(LastBit::from(true));
+
+    // byte 0: cross dev sdk = 1
+    // byte 1: nearby share
+    // byte 2: last bit
+    assert_eq!(
+        &[actions_de_header_byte(3), 0x40, 0x40, 0x01],
+        serialize(&ActionsDataElement::<Plaintext>::from(action)).as_slice()
+    );
+}
+
+#[test]
+fn write_de_all_encrypted_actions() {
+    let mut action = all_ciphertext_actions_set();
+    action.set_action(LastBit::from(true));
+
+    // byte 1: cross dev sdk = 1, call transfer = 4
+    // byte 2: active unlock, nearby share, instant tethering, phone hub,
+    // byte 3: last bit
+    assert_eq!(
+        &[actions_de_header_byte(3), 0x48, 0xF0, 0x01],
+        serialize(&ActionsDataElement::<Ciphertext>::from(action)).as_slice()
+    );
+}
+
+#[test]
+fn roundtrip_de_random_action_combos() {
+    fn do_test<F>(set_ones: impl Fn(ActionType, &mut ActionBits<F>))
+    where
+        F: PacketFlavor,
+        ActionsDataElement<F>: DeserializeDataElement,
+    {
+        let all_types = supported_action_types(F::ENUM_VARIANT);
+        let mut rng = rand::thread_rng();
+
+        for _ in 0..1000 {
+            let len = rng.gen_range(0..=all_types.len());
+            let selected = all_types.choose_multiple(&mut rng, len).copied().collect::<Vec<_>>();
+
+            let mut actions = ActionBits::<F>::default();
+            for &t in &selected {
+                set_ones(t, &mut actions);
+            }
+
+            let de = ActionsDataElement::<F>::from(actions);
+            let serialized = serialize(&de);
+            // skip header
+            let contents = &serialized.as_slice()[1..];
+            let deserialized = ActionsDataElement::<F>::deserialize(contents).unwrap();
+
+            assert_eq!(de.action, deserialized.action);
+        }
+    }
+
+    do_test::<Plaintext>(|t, bits| set_plaintext_action(t, true, bits));
+    do_test::<Ciphertext>(|t, bits| set_ciphertexttext_action(t, true, bits));
+}
+
+#[test]
+fn action_element_bits_dont_overlap() {
+    let type_to_bits =
+        ActionType::iter().map(|t| (t, t.all_bits())).collect::<collections::HashMap<_, _>>();
+
+    for t in ActionType::iter() {
+        let bits = type_to_bits.get(&t).unwrap();
+
+        for (_, other_bits) in type_to_bits.iter().filter(|(other_type, _)| t != **other_type) {
+            assert_eq!(0, bits & other_bits, "type {t:?}");
+        }
+    }
+}
+
+#[test]
+fn action_type_all_bits_masks() {
+    assert_eq!(0x08000000, ActionType::CallTransfer.all_bits());
+    assert_eq!(0x00800000, ActionType::ActiveUnlock.all_bits());
+    assert_eq!(0x00400000, ActionType::NearbyShare.all_bits());
+    assert_eq!(0x00200000, ActionType::InstantTethering.all_bits());
+    assert_eq!(0x00100000, ActionType::PhoneHub.all_bits());
+}
+
+#[test]
+fn action_type_all_bits_in_per_type_masks() {
+    for t in supported_action_types(PacketFlavorEnum::Plaintext) {
+        assert_eq!(t.all_bits(), t.all_bits() & *ALL_PLAINTEXT_ELEMENT_BITS);
+    }
+    for t in supported_action_types(PacketFlavorEnum::Ciphertext) {
+        assert_eq!(t.all_bits(), t.all_bits() & *ALL_CIPHERTEXT_ELEMENT_BITS);
+    }
+}
+
+#[test]
+fn action_bits_try_from_flavor_mismatch_plaintext() {
+    assert_eq!(
+        FlavorNotSupported { flavor: PacketFlavorEnum::Plaintext },
+        ActionBits::<Plaintext>::try_from(ActionType::CallTransfer.all_bits()).unwrap_err()
+    );
+}
+
+#[test]
+fn actions_de_deser_plaintext_with_ciphertext_action() {
+    assert_eq!(
+        DataElementDeserializeError::FlavorNotSupported {
+            de_type: ActionsDataElement::<Plaintext>::DE_TYPE_CODE,
+            flavor: PacketFlavorEnum::Plaintext,
+        },
+        <ActionsDataElement<Plaintext> as DeserializeDataElement>::deserialize::<Plaintext>(&[
+            // active unlock bit set
+            0x00, 0x80, 0x00,
+        ])
+        .unwrap_err()
+    );
+}
+
+#[test]
+fn actions_de_deser_ciphertext_with_plaintext_action() {
+    assert_eq!(
+        DataElementDeserializeError::FlavorNotSupported {
+            de_type: ActionsDataElement::<Plaintext>::DE_TYPE_CODE,
+            flavor: PacketFlavorEnum::Ciphertext,
+        },
+        <ActionsDataElement<Ciphertext> as DeserializeDataElement>::deserialize::<Ciphertext>(&[
+            // Finder bit set
+            0x00, 0x00, 0x80,
+        ])
+        .unwrap_err()
+    );
+}
+
+#[test]
+fn actions_de_deser_plaintext_with_ciphertext_error() {
+    assert_eq!(
+        DataElementDeserializeError::FlavorNotSupported {
+            de_type: ActionsDataElement::<Plaintext>::DE_TYPE_CODE,
+            flavor: PacketFlavorEnum::Plaintext,
+        },
+        <ActionsDataElement<Ciphertext> as DeserializeDataElement>::deserialize::<Plaintext>(&[
+            0x00
+        ])
+        .unwrap_err()
+    );
+}
+
+#[test]
+fn actions_de_deser_ciphertext_with_plaintext_error() {
+    assert_eq!(
+        DataElementDeserializeError::FlavorNotSupported {
+            de_type: ActionsDataElement::<Plaintext>::DE_TYPE_CODE,
+            flavor: PacketFlavorEnum::Ciphertext,
+        },
+        <ActionsDataElement<Plaintext> as DeserializeDataElement>::deserialize::<Ciphertext>(&[
+            0x00
+        ])
+        .unwrap_err()
+    );
+}
+
+#[test]
+fn deserialize_content_too_long_error() {
+    assert_eq!(
+        DataElementDeserializeError::DeserializeError {
+            de_type: ActionsDataElement::<Plaintext>::DE_TYPE_CODE
+        },
+        <ActionsDataElement<Plaintext> as DeserializeDataElement>::deserialize::<Plaintext>(
+            &[0x00; 10]
+        )
+        .unwrap_err()
+    );
+}
+
+#[test]
+fn actions_min_len_unencrypted() {
+    let actions = ActionBits::<Plaintext>::default();
+
+    let (_de, ser) = de_roundtrip_test!(
+        ActionsDataElement<Plaintext>,
+        Actions,
+        Actions,
+        Plaintext,
+        serialize(&actions::ActionsDataElement::from(actions))
+    );
+
+    assert_eq!(
+        &[
+            encode_de_header(
+                ActionsDataElement::<Plaintext>::DE_TYPE_CODE,
+                DeEncodedLength::from(1),
+            ),
+            0
+        ],
+        ser.as_slice()
+    );
+}
+
+#[test]
+fn actions_min_len_ldt() {
+    let actions = ActionBits::<Ciphertext>::default();
+
+    let (_de, ser) = de_roundtrip_test!(
+        ActionsDataElement<Ciphertext>,
+        Actions,
+        Actions,
+        Ciphertext,
+        serialize(&actions::ActionsDataElement::from(actions))
+    );
+
+    // header and 1 DE contents byte
+    assert_eq!(2, ser.as_slice().len());
+}
+
+#[test]
+fn actions_de_contents_normal_actions_roundtrip_unencrypted() {
+    let actions = all_plaintext_actions_set();
+
+    let _ = de_roundtrip_test!(
+        ActionsDataElement<Plaintext>,
+        Actions,
+        Actions,
+        Plaintext,
+        serialize(&actions::ActionsDataElement::from(actions))
+    );
+}
+
+#[test]
+fn actions_de_contents_normal_actions_roundtrip_ldt() {
+    let actions = all_ciphertext_actions_set();
+
+    let _ = de_roundtrip_test!(
+        ActionsDataElement<Ciphertext>,
+        Actions,
+        Actions,
+        Ciphertext,
+        serialize(&actions::ActionsDataElement::from(actions))
+    );
+}
+
+#[test]
+fn has_action_plaintext_works() {
+    let mut action_bits = ActionBits::<Plaintext>::default();
+    action_bits.set_action(NearbyShare::from(true));
+    let action_de = ActionsDataElement::from(action_bits);
+    assert!(action_de.action.has_action(ActionType::NearbyShare));
+    assert!(!action_de.action.has_action(ActionType::ActiveUnlock));
+    assert!(!action_de.action.has_action(ActionType::PhoneHub));
+}
+
+#[test]
+fn has_action_encrypted_works() {
+    let mut action_bits = ActionBits::<Ciphertext>::default();
+    action_bits.set_action(NearbyShare::from(true));
+    action_bits.set_action(ActiveUnlock::from(true));
+    let action_de = ActionsDataElement::from(action_bits);
+    assert!(action_de.action.has_action(ActionType::NearbyShare));
+    assert!(action_de.action.has_action(ActionType::ActiveUnlock));
+    assert!(!action_de.action.has_action(ActionType::PhoneHub));
+}
+
+#[test]
+fn actual_length_must_be_in_range() {
+    let de = ActionsDataElement::<Plaintext>::from(ActionBits::default());
+
+    for l in [0, ACTIONS_MAX_LEN + 1] {
+        let actual = DeActualLength::try_from(l).unwrap();
+        let _ = panic::catch_unwind(|| de.map_actual_len_to_encoded_len(actual)).unwrap_err();
+    }
+
+    for l in ACTIONS_VALID_ACTUAL_LEN {
+        assert_eq!(
+            l,
+            de.map_actual_len_to_encoded_len(DeActualLength::try_from(l).unwrap()).as_usize()
+        )
+    }
+}
+
+#[test]
+fn encoded_length_must_be_in_range() {
+    for l in [0, ACTIONS_MAX_LEN + 1] {
+        assert_eq!(
+            DeLengthOutOfRange,
+            <ActionsDataElement<Plaintext> as DeserializeDataElement>::LengthMapper::map_encoded_len_to_actual_len(
+                DeEncodedLength::try_from(l as u8).unwrap()
+            )
+                .unwrap_err()
+        )
+    }
+
+    for l in ACTIONS_VALID_ACTUAL_LEN {
+        assert_eq!(
+            l,
+            <ActionsDataElement<Plaintext> as DeserializeDataElement>::LengthMapper::map_encoded_len_to_actual_len(
+                DeEncodedLength::try_from(l as u8).unwrap()
+            )
+                .unwrap()
+                .as_usize()
+        );
+    }
+}
+
+mod coverage_gaming {
+    use crate::legacy::data_elements::actions::*;
+    use crate::legacy::Plaintext;
+    use alloc::format;
+
+    #[test]
+    fn actions_de_debug() {
+        let actions = ActionsDataElement::<Plaintext>::from(ActionBits::default());
+        let _ = format!("{:?}", actions);
+    }
+
+    #[test]
+    fn flavor_not_supported_debug() {
+        let _ = format!("{:?}", FlavorNotSupported { flavor: PacketFlavorEnum::Plaintext });
+    }
+
+    #[test]
+    fn action_type_clone_debug() {
+        let _ = format!("{:?}", ActionType::CallTransfer.clone());
+    }
+
+    #[test]
+    fn actions_debug() {
+        let _ = format!("{:?}", CallTransfer::from(true));
+        let _ = format!("{:?}", ActiveUnlock::from(true));
+        let _ = format!("{:?}", NearbyShare::from(true));
+        let _ = format!("{:?}", InstantTethering::from(true));
+        let _ = format!("{:?}", PhoneHub::from(true));
+    }
+}
+
+// Test only action which uses the first bit
+#[derive(Debug)]
+pub(crate) struct FirstBit {
+    enabled: bool,
+}
+
+impl From<bool> for FirstBit {
+    fn from(value: bool) -> Self {
+        FirstBit { enabled: value }
+    }
+}
+
+impl ActionElement for FirstBit {
+    const HIGH_BIT_INDEX: u32 = 0;
+    // don't want to add a variant for this test only type
+    const ACTION_TYPE: ActionType = ActionType::ActiveUnlock;
+
+    fn supports_flavor(_flavor: PacketFlavorEnum) -> bool {
+        true
+    }
+
+    fn bits(&self) -> u8 {
+        self.enabled as u8
+    }
+}
+
+macros::boolean_element_to_plaintext_element!(FirstBit);
+macros::boolean_element_to_encrypted_element!(FirstBit);
+
+// hypothetical action using the last bit
+#[derive(Debug)]
+pub(crate) struct LastBit {
+    enabled: bool,
+}
+
+impl From<bool> for LastBit {
+    fn from(value: bool) -> Self {
+        LastBit { enabled: value }
+    }
+}
+
+impl ActionElement for LastBit {
+    const HIGH_BIT_INDEX: u32 = 23;
+    // don't want to add a variant for this test only type
+    const ACTION_TYPE: ActionType = ActionType::ActiveUnlock;
+
+    fn supports_flavor(_flavor: PacketFlavorEnum) -> bool {
+        true
+    }
+
+    fn bits(&self) -> u8 {
+        self.enabled as u8
+    }
+}
+
+macros::boolean_element_to_plaintext_element!(LastBit);
+macros::boolean_element_to_encrypted_element!(LastBit);
+
+// An action that only supports plaintext, to allow testing that error case
+pub(in crate::legacy) struct PlaintextOnly {
+    enabled: bool,
+}
+
+impl From<bool> for PlaintextOnly {
+    fn from(value: bool) -> Self {
+        Self { enabled: value }
+    }
+}
+
+impl ActionElement for PlaintextOnly {
+    const HIGH_BIT_INDEX: u32 = 22;
+
+    const ACTION_TYPE: ActionType = ActionType::ActiveUnlock;
+
+    fn supports_flavor(flavor: PacketFlavorEnum) -> bool {
+        match flavor {
+            PacketFlavorEnum::Plaintext => true,
+            PacketFlavorEnum::Ciphertext => false,
+        }
+    }
+
+    fn bits(&self) -> u8 {
+        self.enabled as u8
+    }
+}
+
+macros::boolean_element_to_plaintext_element!(PlaintextOnly);
+// sneakily allow serializing it, but deserializing will fail due to supports_flavor above
+macros::boolean_element_to_encrypted_element!(PlaintextOnly);
+
+fn assert_eq_hex(expected: u32, actual: u32) {
+    assert_eq!(expected, actual, "{expected:#010X} != {actual:#010X}");
+}
+
+pub(crate) fn all_plaintext_actions_set() -> ActionBits<Plaintext> {
+    let mut action = ActionBits::default();
+    action.set_action(CrossDevSdk::from(true));
+    action.set_action(NearbyShare::from(true));
+
+    assert!(supported_action_types(PacketFlavorEnum::Plaintext)
+        .into_iter()
+        .all(|t| t.all_bits() & action.bits != 0));
+
+    action
+}
+
+pub(crate) fn all_ciphertext_actions_set() -> ActionBits<Ciphertext> {
+    let mut action = ActionBits::default();
+    action.set_action(CrossDevSdk::from(true));
+    action.set_action(CallTransfer::from(true));
+    action.set_action(ActiveUnlock::from(true));
+    action.set_action(NearbyShare::from(true));
+    action.set_action(InstantTethering::from(true));
+    action.set_action(PhoneHub::from(true));
+
+    assert!(supported_action_types(PacketFlavorEnum::Ciphertext)
+        .into_iter()
+        .all(|t| t.all_bits() & action.bits != 0));
+
+    action
+}
+
+fn supported_action_types(flavor: PacketFlavorEnum) -> Vec<ActionType> {
+    ActionType::iter().filter(|t| t.supports_flavor(flavor)).collect()
+}
+
+/// Encode a DE header byte with the provided type and actual len, transforming into an encoded
+/// len appropriately.
+fn actions_de_header_byte(actual_len: u8) -> u8 {
+    encode_de_header(
+        ActionsDataElement::<Plaintext>::DE_TYPE_CODE,
+        DeEncodedLength::try_from(actual_len).unwrap(),
+    )
+}
+
+pub(crate) fn set_plaintext_action(t: ActionType, value: bool, bits: &mut ActionBits<Plaintext>) {
+    match t {
+        ActionType::CrossDevSdk => bits.set_action(CrossDevSdk::from(value)),
+        ActionType::NearbyShare => bits.set_action(NearbyShare::from(value)),
+        ActionType::CallTransfer
+        | ActionType::PhoneHub
+        | ActionType::ActiveUnlock
+        | ActionType::InstantTethering => panic!(),
+    }
+}
+
+pub(crate) fn set_ciphertexttext_action(
+    t: ActionType,
+    value: bool,
+    bits: &mut ActionBits<Ciphertext>,
+) {
+    match t {
+        ActionType::CrossDevSdk => bits.set_action(CrossDevSdk::from(value)),
+        ActionType::CallTransfer => bits.set_action(CallTransfer::from(value)),
+        ActionType::ActiveUnlock => bits.set_action(ActiveUnlock::from(value)),
+        ActionType::NearbyShare => bits.set_action(NearbyShare::from(value)),
+        ActionType::InstantTethering => bits.set_action(InstantTethering::from(value)),
+        ActionType::PhoneHub => bits.set_action(PhoneHub::from(value)),
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/data_elements/de_type/mod.rs b/nearby/presence/np_adv/src/legacy/data_elements/de_type/mod.rs
new file mode 100644
index 0000000..e5dd153
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/data_elements/de_type/mod.rs
@@ -0,0 +1,127 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! V0 data element types.
+//!
+//! In V0, there are only 16 DE types total, and parsing unknown types is not possible, so we can
+//! represent all known DE types in enums without needing to handle the "unknown type" case.
+
+use crate::{legacy::NP_MAX_DE_CONTENT_LEN, DeLengthOutOfRange};
+use strum_macros::EnumIter;
+
+#[cfg(test)]
+mod tests;
+
+/// A V0 DE type in `[0, 15]`.
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
+pub struct DeTypeCode {
+    /// The code used in a V0 adv header
+    code: u8,
+}
+
+impl DeTypeCode {
+    /// Returns a u8 in `[0, 15`].
+    pub(crate) fn as_u8(&self) -> u8 {
+        self.code
+    }
+
+    pub(crate) const fn try_from(value: u8) -> Result<Self, DeTypeCodeOutOfRange> {
+        if value < 16 {
+            Ok(Self { code: value })
+        } else {
+            Err(DeTypeCodeOutOfRange)
+        }
+    }
+}
+
+/// The DE type code is out of range for v0 DE types.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub(crate) struct DeTypeCodeOutOfRange;
+
+/// The actual length of a DE's contents, not the encoded representation.
+///
+/// See [DeEncodedLength] for the encoded length.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct DeActualLength {
+    /// Invariant: <= [NP_MAX_DE_CONTENT_LEN].
+    len: u8,
+}
+
+impl DeActualLength {
+    pub(crate) fn try_from(value: usize) -> Result<Self, DeLengthOutOfRange> {
+        if value <= NP_MAX_DE_CONTENT_LEN {
+            Ok(Self { len: value as u8 })
+        } else {
+            Err(DeLengthOutOfRange)
+        }
+    }
+
+    pub(crate) fn as_u8(&self) -> u8 {
+        self.len
+    }
+
+    pub(crate) fn as_usize(&self) -> usize {
+        self.len.into()
+    }
+}
+
+/// Maximum encoded length value
+pub(crate) const MAX_DE_ENCODED_LEN: u8 = 15;
+
+/// The encoded length of a DE, not the actual length of the DE contents.
+///
+/// See [DeActualLength] for the length of the contents.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct DeEncodedLength {
+    /// Invariant: `len <= 0x0F` (15, aka 4 bits)
+    len: u8,
+}
+
+impl DeEncodedLength {
+    pub(crate) fn try_from(value: u8) -> Result<Self, DeLengthOutOfRange> {
+        if value <= MAX_DE_ENCODED_LEN {
+            Ok(Self { len: value })
+        } else {
+            Err(DeLengthOutOfRange)
+        }
+    }
+
+    /// Test-only helper that panics for less unwrapping in tests.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `value` is invalid.
+    #[cfg(test)]
+    pub(in crate::legacy) fn from(value: u8) -> Self {
+        Self::try_from(value).expect("Invalid len")
+    }
+
+    /// Returns a u8 in `[0, 15]`
+    pub(crate) fn as_u8(&self) -> u8 {
+        self.len
+    }
+
+    /// Returns a usize in `[0, 15]`
+    pub(crate) fn as_usize(&self) -> usize {
+        self.len as usize
+    }
+}
+
+/// Corresponds to the normal data element types.
+#[derive(EnumIter, Debug, Clone, Copy, PartialEq, Eq)]
+#[allow(missing_docs)]
+pub(in crate::legacy) enum DataElementType {
+    TxPower,
+    Actions,
+}
diff --git a/nearby/presence/np_adv/src/legacy/data_elements/de_type/tests.rs b/nearby/presence/np_adv/src/legacy/data_elements/de_type/tests.rs
new file mode 100644
index 0000000..72c8482
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/data_elements/de_type/tests.rs
@@ -0,0 +1,153 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#![allow(clippy::unwrap_used)]
+
+extern crate std;
+
+use super::*;
+use crate::legacy::data_elements::{DeserializeDataElement, LengthMapper};
+use crate::legacy::serialize::tests::helpers::{LongDataElement, ShortDataElement};
+use crate::legacy::{Ciphertext, PacketFlavor};
+use crate::{
+    legacy::{
+        data_elements::{
+            actions::{ActionBits, ActionsDataElement},
+            tx_power::TxPowerDataElement,
+            SerializeDataElement,
+        },
+        Plaintext,
+    },
+    shared_data::TxPower,
+};
+use alloc::vec;
+use core::panic::{RefUnwindSafe, UnwindSafe};
+use std::{collections, panic};
+
+#[test]
+fn de_type_code_in_range_ok() {
+    assert_eq!(3, DeTypeCode::try_from(3).unwrap().code);
+}
+
+#[test]
+fn de_type_code_out_of_range_err() {
+    assert_eq!(DeTypeCodeOutOfRange, DeTypeCode::try_from(30).unwrap_err());
+}
+
+#[test]
+fn de_actual_length_in_range_ok() {
+    assert_eq!(3, DeActualLength::try_from(3).unwrap().len);
+}
+
+#[test]
+fn de_actual_length_out_of_range_err() {
+    assert_eq!(
+        DeLengthOutOfRange,
+        DeActualLength::try_from(NP_MAX_DE_CONTENT_LEN + 1).unwrap_err()
+    );
+}
+
+#[test]
+fn de_encoded_length_in_range_ok() {
+    assert_eq!(3, DeEncodedLength::from(3).len);
+}
+
+#[test]
+fn de_encoded_length_out_of_range_err() {
+    assert_eq!(DeLengthOutOfRange, DeEncodedLength::try_from(MAX_DE_ENCODED_LEN + 1).unwrap_err());
+}
+
+#[test]
+fn de_length_actual_encoded_round_trip() {
+    fn do_de_length_test<
+        F: PacketFlavor,
+        D: DeserializeDataElement + SerializeDataElement<F> + UnwindSafe + RefUnwindSafe,
+    >(
+        de: D,
+    ) {
+        // for all possible lengths, calculate actual -> encoded and the inverse
+        let actual_to_encoded = (0_u8..=255)
+            .filter_map(|num| DeActualLength::try_from(usize::from(num)).ok())
+            .filter_map(|actual: DeActualLength| {
+                panic::catch_unwind(|| de.map_actual_len_to_encoded_len(actual))
+                    .ok()
+                    .map(|encoded| (actual, encoded))
+            })
+            .collect::<collections::HashMap<_, _>>();
+
+        let encoded_to_actual = (0_u8..=255)
+            .filter_map(|num| DeEncodedLength::try_from(num).ok())
+            .filter_map(|encoded: DeEncodedLength| {
+                D::LengthMapper::map_encoded_len_to_actual_len(encoded)
+                    .ok()
+                    .map(|actual| (encoded, actual))
+            })
+            .collect::<collections::HashMap<_, _>>();
+
+        // ensure the two maps are inverses of each other
+        assert_eq!(
+            actual_to_encoded,
+            encoded_to_actual.into_iter().map(|(encoded, actual)| (actual, encoded)).collect(),
+        );
+    }
+
+    do_de_length_test::<Plaintext, _>(TxPowerDataElement::from(TxPower::try_from(1).unwrap()));
+    do_de_length_test(ActionsDataElement::<Plaintext>::from(ActionBits::default()));
+    do_de_length_test(ActionsDataElement::<Ciphertext>::from(ActionBits::default()));
+
+    // might as well make sure our test DEs behave as well
+    do_de_length_test::<Plaintext, _>(ShortDataElement::new(vec![]));
+    do_de_length_test::<Plaintext, _>(LongDataElement::new(vec![]));
+}
+
+mod coverage_gaming {
+    use crate::legacy::data_elements::de_type::{
+        DataElementType, DeActualLength, DeEncodedLength, DeTypeCode, DeTypeCodeOutOfRange,
+    };
+
+    extern crate std;
+
+    use std::{collections, format};
+
+    #[test]
+    fn de_type_code_debug_hash_clone() {
+        let code = DeTypeCode::try_from(3).unwrap();
+        let _ = format!("{:?}", code.clone());
+        let _ = collections::HashSet::from([code]);
+    }
+
+    #[test]
+    fn de_type_code_out_of_range() {
+        // debug, clone
+        let _ = format!("{:?}", DeTypeCodeOutOfRange.clone());
+    }
+
+    #[test]
+    fn de_actual_length() {
+        // debug, clone
+        let _ = format!("{:?}", DeActualLength::try_from(3).unwrap().clone());
+    }
+
+    #[test]
+    fn de_encoded_length() {
+        // debug, clone
+        let _ = format!("{:?}", DeEncodedLength::from(3).clone());
+    }
+
+    #[test]
+    fn de_type() {
+        // debug, clone
+        let _ = format!("{:?}", DataElementType::TxPower.clone());
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/data_elements/mod.rs b/nearby/presence/np_adv/src/legacy/data_elements/mod.rs
new file mode 100644
index 0000000..5dd0587
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/data_elements/mod.rs
@@ -0,0 +1,266 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! V0 data elements and core trait impls.
+
+use crate::{
+    extended::serialize::CapacityLimitedVec,
+    legacy::{
+        data_elements::de_type::{DeActualLength, DeEncodedLength, DeTypeCode},
+        PacketFlavor, PacketFlavorEnum, NP_MAX_ADV_CONTENT_LEN,
+    },
+    private::Sealed,
+    DeLengthOutOfRange,
+};
+use core::marker;
+use nom::error::{self};
+use sink::Sink;
+
+pub mod actions;
+pub mod de_type;
+pub mod tx_power;
+
+#[cfg(test)]
+pub(in crate::legacy) mod tests;
+
+/// Deserialization for a specific data element type.
+///
+/// See also [SerializeDataElement] for flavor-specific, infallible serialization.
+pub(in crate::legacy) trait DeserializeDataElement: Sized + Sealed {
+    const DE_TYPE_CODE: DeTypeCode;
+
+    /// Define how length mapping is done for this DE type
+    type LengthMapper: LengthMapper;
+
+    /// Deserialize `Self` from the provided DE contents (not including the
+    /// DE header).
+    ///
+    /// Returns `Err` if the flavor is not supported or if parsing fails.
+    ///
+    /// `<F>` is the flavor of the packet being deserialized.
+    fn deserialize<F: PacketFlavor>(
+        de_contents: &[u8],
+    ) -> Result<Self, DataElementDeserializeError>;
+}
+
+/// Errors possible when deserializing a DE
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum DataElementDeserializeError {
+    /// The data element doesn't support the [PacketFlavor] of the advertisement packet.
+    FlavorNotSupported {
+        /// The DE type attempting to be deserialized
+        de_type: DeTypeCode,
+        /// The flavor that was not supported
+        flavor: PacketFlavorEnum,
+    },
+    /// The data element couldn't be deserialized from the supplied data.
+    DeserializeError {
+        /// The DE type attempting to be deserialized
+        de_type: DeTypeCode,
+    },
+    /// Invalid DE type
+    InvalidDeType {
+        /// The unknown type
+        de_type: DeTypeCode,
+    },
+    /// Invalid DE length
+    InvalidDeLength {
+        /// The DE type attempting to be deserialized
+        de_type: DeTypeCode,
+        /// The invalid length
+        len: DeEncodedLength,
+    },
+    /// Other parse error, e.g. the adv is truncated
+    InvalidStructure,
+}
+
+impl error::FromExternalError<&[u8], DataElementDeserializeError> for DataElementDeserializeError {
+    fn from_external_error(
+        _input: &[u8],
+        _kind: error::ErrorKind,
+        e: DataElementDeserializeError,
+    ) -> Self {
+        e
+    }
+}
+
+impl error::ParseError<&[u8]> for DataElementDeserializeError {
+    /// Creates an error from the input position and an [error::ErrorKind]
+    fn from_error_kind(_input: &[u8], _kind: error::ErrorKind) -> Self {
+        Self::InvalidStructure
+    }
+
+    /// Combines an existing error with a new one created from the input
+    /// position and an [error::ErrorKind]. This is useful when backtracking
+    /// through a parse tree, accumulating error context on the way
+    fn append(_input: &[u8], _kind: error::ErrorKind, _other: Self) -> Self {
+        Self::InvalidStructure
+    }
+}
+
+/// Serialization of a DE for a particular flavor.
+///
+/// The flavor is a type parameter on the trait, rather than on the method,
+/// so that a DE can indicate its flavor support by implementing this trait
+/// only with the relevant flavors. Deserialization, on the other hand, can
+/// express "flavor not supported" for invalid input.
+pub trait SerializeDataElement<F: PacketFlavor>: Sealed {
+    /// Returns the DE type code this DE uses in the header.
+    fn de_type_code(&self) -> DeTypeCode;
+
+    /// Convert the actual DE content length to the encoded length included in the header.
+    ///
+    /// This has a `&self` receiver only so that it can be object-safe; it
+    /// should not be relevant in the calculation.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the actual length is invalid for this DE type, or if the
+    /// encoded length cannot fit in [DeEncodedLength], either of which means
+    /// that the serialization impl is broken.
+    fn map_actual_len_to_encoded_len(&self, actual_len: DeActualLength) -> DeEncodedLength;
+
+    /// Serialize the data element's data (not including the header) into the sink.
+    fn serialize_contents(
+        &self,
+        sink: &mut DataElementSerializationBuffer,
+    ) -> Result<(), DataElementSerializeError>;
+}
+
+/// Errors possible when serializing a DE
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum DataElementSerializeError {
+    /// Not enough available space
+    InsufficientSpace,
+}
+
+/// Buffer to serialize a DE, including the header, into.
+pub struct DataElementSerializationBuffer {
+    vec: CapacityLimitedVec<u8, NP_MAX_ADV_CONTENT_LEN>,
+}
+
+impl DataElementSerializationBuffer {
+    /// Returns `None` if `capacity` exceeds [NP_MAX_ADV_CONTENT_LEN].
+    pub(crate) fn new(capacity: usize) -> Option<Self> {
+        CapacityLimitedVec::new(capacity).map(|vec| Self { vec })
+    }
+
+    pub(crate) fn len(&self) -> usize {
+        self.vec.len()
+    }
+
+    pub(crate) fn into_inner(self) -> CapacityLimitedVec<u8, NP_MAX_ADV_CONTENT_LEN> {
+        self.vec
+    }
+}
+
+impl Sink<u8> for DataElementSerializationBuffer {
+    fn try_extend_from_slice(&mut self, items: &[u8]) -> Option<()> {
+        Sink::try_extend_from_slice(&mut self.vec, items)
+    }
+
+    fn try_push(&mut self, item: u8) -> Option<()> {
+        Sink::try_push(&mut self.vec, item)
+    }
+}
+
+/// Trait object reference to a `ToDataElementBundle<I>` with lifetime `'a`.
+/// Implements [SerializeDataElement] by deferring to the wrapped trait object.
+pub struct DynamicSerializeDataElement<'a, I: PacketFlavor> {
+    wrapped: &'a dyn SerializeDataElement<I>,
+}
+
+impl<'a, I: PacketFlavor> From<&'a dyn SerializeDataElement<I>>
+    for DynamicSerializeDataElement<'a, I>
+{
+    fn from(wrapped: &'a dyn SerializeDataElement<I>) -> Self {
+        DynamicSerializeDataElement { wrapped }
+    }
+}
+
+impl<'a, F: PacketFlavor> Sealed for DynamicSerializeDataElement<'a, F> {}
+
+impl<'a, F: PacketFlavor> SerializeDataElement<F> for DynamicSerializeDataElement<'a, F> {
+    fn de_type_code(&self) -> DeTypeCode {
+        self.wrapped.de_type_code()
+    }
+
+    fn map_actual_len_to_encoded_len(&self, actual_len: DeActualLength) -> DeEncodedLength {
+        self.wrapped.map_actual_len_to_encoded_len(actual_len)
+    }
+
+    fn serialize_contents(
+        &self,
+        sink: &mut DataElementSerializationBuffer,
+    ) -> Result<(), DataElementSerializeError> {
+        self.wrapped.serialize_contents(sink)
+    }
+}
+
+/// Maps encoded to actual lengths and vice versa.
+///
+/// Each v0 DE type has their own length mapping rules.
+pub(in crate::legacy) trait LengthMapper {
+    /// Convert the encoded DE content length in the header to the actual length to consume from
+    /// the advertisement, or an error if the encoded length is invalid for the DE type.
+    fn map_encoded_len_to_actual_len(
+        encoded_len: DeEncodedLength,
+    ) -> Result<DeActualLength, DeLengthOutOfRange>;
+
+    /// Convert the actual DE content length to the encoded length included in the header.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the actual length is invalid for this DE type, or if the
+    /// encoded length cannot fit in [DeEncodedLength], either of which means
+    /// that the serialization impl is broken.
+    fn map_actual_len_to_encoded_len(actual_len: DeActualLength) -> DeEncodedLength;
+}
+
+/// A length predicate used with [DirectMapper].
+pub(in crate::legacy) trait DirectMapPredicate {
+    /// Return `true` iff the len is valid as a DE encoded len _and_ actual len.
+    fn is_valid(len: usize) -> bool;
+}
+
+/// A [LengthMapper] that maps the input number directly to the output number without any scaling or
+/// shifting.
+///
+/// Iff `predicate` evaluates to true, the input number will be transformed into the output type,
+/// for both directions.
+pub(in crate::legacy) struct DirectMapper<P: DirectMapPredicate> {
+    _marker: marker::PhantomData<P>,
+}
+
+impl<P: DirectMapPredicate> LengthMapper for DirectMapper<P> {
+    fn map_encoded_len_to_actual_len(
+        encoded_len: DeEncodedLength,
+    ) -> Result<DeActualLength, DeLengthOutOfRange> {
+        let enc = encoded_len.as_usize();
+        if P::is_valid(enc) {
+            DeActualLength::try_from(enc)
+        } else {
+            Err(DeLengthOutOfRange)
+        }
+    }
+
+    fn map_actual_len_to_encoded_len(actual_len: DeActualLength) -> DeEncodedLength {
+        assert!(
+            P::is_valid(actual_len.as_usize()),
+            "Broken DE implementation produced invalid length"
+        );
+        DeEncodedLength::try_from(actual_len.as_u8())
+            .expect("Actual length has already been validated")
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/data_elements/tests.rs b/nearby/presence/np_adv/src/legacy/data_elements/tests.rs
new file mode 100644
index 0000000..d3f3061
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/data_elements/tests.rs
@@ -0,0 +1,304 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#![allow(clippy::unwrap_used)]
+
+use alloc::boxed::Box;
+
+use crate::header::VERSION_HEADER_V0_UNENCRYPTED;
+use crate::legacy::data_elements::actions::{ActionBits, ActionsDataElement};
+use crate::legacy::serialize::{encode_de_header, AdvBuilder, UnencryptedEncoder};
+use crate::legacy::Plaintext;
+
+use super::*;
+
+#[test]
+fn dynamic_de_works() {
+    let mut builder = AdvBuilder::new(UnencryptedEncoder);
+
+    let actions: Box<dyn SerializeDataElement<Plaintext>> =
+        Box::new(ActionsDataElement::<Plaintext>::from(ActionBits::default()));
+    builder.add_data_element(DynamicSerializeDataElement::from(actions.as_ref())).unwrap();
+
+    assert_eq!(
+        &[
+            VERSION_HEADER_V0_UNENCRYPTED,
+            encode_de_header(
+                ActionsDataElement::<Plaintext>::DE_TYPE_CODE,
+                DeEncodedLength::from(1),
+            ),
+            0
+        ],
+        builder.into_advertisement().unwrap().as_slice()
+    );
+}
+
+pub(in crate::legacy) mod macros {
+    use alloc::vec::Vec;
+
+    use crate::legacy::data_elements::de_type::DataElementType;
+    use crate::legacy::deserialize::{DeIterator, DeserializedDataElement};
+    use crate::legacy::serialize::SerializedDataElement;
+    use crate::legacy::PacketFlavor;
+
+    /// Test method body that creates an array, deserializes it into a DE, serializes it,
+    /// and asserts that the same bytes are produced.
+    ///
+    /// Evaluates to (the deserialized DE, the serialized form of the DE).
+    macro_rules! de_roundtrip_test {
+        ($de_type:ty, $type_variant:ident, $de_variant:ident, $flavor:ty,  $bytes:expr) => {{
+            let parsed_de_enum =
+                crate::legacy::data_elements::tests::macros::construct_and_parse_de::<
+                    $flavor,
+                >(crate::legacy::data_elements::de_type::DataElementType::$type_variant, &$bytes);
+            if let crate::legacy::deserialize::DeserializedDataElement::$de_variant(de) =
+                parsed_de_enum
+            {
+                // skip DE header byte
+                let expected = <$de_type as crate::legacy::data_elements::DeserializeDataElement>
+                                    ::deserialize::<$flavor>(&$bytes.as_slice()[1..]).unwrap();
+                assert_eq!(expected, de);
+
+                let serialized = crate::legacy::serialize::tests::serialize::<$flavor, _>(&de);
+                assert_eq!($bytes.as_slice(), serialized.as_slice());
+
+                (de, serialized)
+            } else {
+                panic!("Unexpected variant: {:?}", parsed_de_enum);
+            }
+        }};
+    }
+
+    pub(in crate::legacy) use de_roundtrip_test;
+
+    /// Construct the serialized DE and parse it
+    pub(in crate::legacy) fn construct_and_parse_de<F>(
+        de_type: DataElementType,
+        contents: &SerializedDataElement,
+    ) -> DeserializedDataElement<F>
+    where
+        F: PacketFlavor,
+    {
+        let mut plain_des =
+            DeIterator::new(contents.as_slice()).collect::<Result<Vec<_>, _>>().unwrap();
+        assert_eq!(1, plain_des.len());
+        let de = plain_des.swap_remove(0);
+        assert_eq!(
+            de_type,
+            match de {
+                DeserializedDataElement::Actions(_) => DataElementType::Actions,
+                DeserializedDataElement::TxPower(_) => DataElementType::TxPower,
+            }
+        );
+        de
+    }
+}
+
+mod coverage_gaming {
+    use alloc::format;
+
+    use nom::error;
+    use nom::error::ParseError;
+
+    use crate::legacy::data_elements::de_type::DeTypeCode;
+    use crate::legacy::data_elements::{DataElementDeserializeError, DataElementSerializeError};
+
+    #[test]
+    fn data_element_serialize_error_debug_eq_clone() {
+        let _ = format!("{:?}", DataElementSerializeError::InsufficientSpace.clone());
+        assert_eq!(
+            DataElementSerializeError::InsufficientSpace,
+            DataElementSerializeError::InsufficientSpace
+        );
+    }
+
+    #[test]
+    fn data_element_deserialize_error_debug_clone() {
+        let _ = format!("{:?}", DataElementDeserializeError::InvalidStructure.clone());
+    }
+
+    #[test]
+    fn data_element_deserialize_error_append() {
+        assert_eq!(
+            DataElementDeserializeError::InvalidStructure,
+            DataElementDeserializeError::append(
+                &[0_u8],
+                error::ErrorKind::CrLf,
+                DataElementDeserializeError::InvalidDeType {
+                    de_type: DeTypeCode::try_from(10).unwrap()
+                },
+            )
+        );
+    }
+}
+
+pub(in crate::legacy) mod test_des {
+    use alloc::vec;
+
+    use rand::distributions;
+    use strum_macros::EnumIter;
+
+    use crate::legacy::data_elements::de_type::{
+        DeActualLength, DeEncodedLength, DeTypeCode, MAX_DE_ENCODED_LEN,
+    };
+    use crate::legacy::data_elements::{
+        DataElementDeserializeError, DataElementSerializationBuffer, DataElementSerializeError,
+        DeserializeDataElement, LengthMapper, SerializeDataElement,
+    };
+    use crate::legacy::deserialize::{DataElementDeserializer, LengthError, RawDataElement};
+    use crate::legacy::serialize::tests::helpers::{LongDataElement, ShortDataElement};
+    use crate::legacy::{PacketFlavor, NP_MAX_DE_CONTENT_LEN};
+    use crate::private::Sealed;
+
+    /// A [DataElementDeserializer] that can deserialize the test stubs [ShortDataElement] and
+    /// [LongDataElement].
+    pub(in crate::legacy) struct TestDeDeserializer;
+
+    impl DataElementDeserializer for TestDeDeserializer {
+        type DeTypeDisambiguator = TestDataElementType;
+        type Deserialized<F: PacketFlavor> = TestDataElement;
+
+        fn map_encoded_len_to_actual_len(
+            de_type: DeTypeCode,
+            encoded_len: DeEncodedLength,
+        ) -> Result<(Self::DeTypeDisambiguator, DeActualLength), LengthError> {
+            match de_type {
+                ShortDataElement::DE_TYPE_CODE => {
+                    <ShortDataElement as DeserializeDataElement>::LengthMapper::map_encoded_len_to_actual_len(encoded_len)
+                        .map(|l| (TestDataElementType::Short, l))
+                        .map_err(|e| e.into())
+                }
+                LongDataElement::DE_TYPE_CODE => {
+                    <LongDataElement as DeserializeDataElement>::LengthMapper::map_encoded_len_to_actual_len(encoded_len)
+                        .map(|l| (TestDataElementType::Long, l))
+                        .map_err(|e| e.into())
+                }
+                _ => Err(LengthError::InvalidType),
+            }
+        }
+
+        fn deserialize_de<F: PacketFlavor>(
+            raw_de: RawDataElement<Self>,
+        ) -> Result<Self::Deserialized<F>, DataElementDeserializeError> {
+            match raw_de.de_type {
+                TestDataElementType::Short => {
+                    ShortDataElement::deserialize::<F>(raw_de.contents).map(TestDataElement::Short)
+                }
+                TestDataElementType::Long => {
+                    LongDataElement::deserialize::<F>(raw_de.contents).map(TestDataElement::Long)
+                }
+            }
+        }
+    }
+
+    #[derive(EnumIter, Debug, Clone, Copy)]
+    pub(in crate::legacy) enum TestDataElementType {
+        Short,
+        Long,
+    }
+
+    #[derive(Debug, PartialEq, Eq, Clone)]
+    pub(in crate::legacy) enum TestDataElement {
+        Short(ShortDataElement),
+        Long(LongDataElement),
+    }
+
+    impl From<ShortDataElement> for TestDataElement {
+        fn from(value: ShortDataElement) -> Self {
+            Self::Short(value)
+        }
+    }
+
+    impl From<LongDataElement> for TestDataElement {
+        fn from(value: LongDataElement) -> Self {
+            Self::Long(value)
+        }
+    }
+
+    impl Sealed for TestDataElement {}
+
+    // Not representative of how the main [DeserializedDataElement] would be used, but handy
+    // in tests to be able to directly serialize the deserialized representation
+    impl<F: PacketFlavor> SerializeDataElement<F> for TestDataElement {
+        fn de_type_code(&self) -> DeTypeCode {
+            match self {
+                TestDataElement::Short(s) => {
+                    <ShortDataElement as SerializeDataElement<F>>::de_type_code(s)
+                }
+                TestDataElement::Long(l) => {
+                    <LongDataElement as SerializeDataElement<F>>::de_type_code(l)
+                }
+            }
+        }
+
+        fn map_actual_len_to_encoded_len(&self, actual_len: DeActualLength) -> DeEncodedLength {
+            match self {
+                TestDataElement::Short(s) => {
+                    <ShortDataElement as SerializeDataElement<F>>::map_actual_len_to_encoded_len(
+                        s, actual_len,
+                    )
+                }
+                TestDataElement::Long(l) => {
+                    <LongDataElement as SerializeDataElement<F>>::map_actual_len_to_encoded_len(
+                        l, actual_len,
+                    )
+                }
+            }
+        }
+
+        fn serialize_contents(
+            &self,
+            sink: &mut DataElementSerializationBuffer,
+        ) -> Result<(), DataElementSerializeError> {
+            match self {
+                TestDataElement::Short(s) => {
+                    <ShortDataElement as SerializeDataElement<F>>::serialize_contents(s, sink)
+                }
+                TestDataElement::Long(l) => {
+                    <LongDataElement as SerializeDataElement<F>>::serialize_contents(l, sink)
+                }
+            }
+        }
+    }
+
+    impl distributions::Distribution<ShortDataElement> for distributions::Standard {
+        fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> ShortDataElement {
+            let len = rng.gen_range(0_usize..MAX_DE_ENCODED_LEN.into());
+            let mut data = vec![0; len];
+            rng.fill(&mut data[..]);
+            ShortDataElement::new(data)
+        }
+    }
+
+    impl distributions::Distribution<LongDataElement> for distributions::Standard {
+        fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> LongDataElement {
+            let len = rng.gen_range(LongDataElement::OFFSET..NP_MAX_DE_CONTENT_LEN);
+            let mut data = vec![0; len];
+            rng.fill(&mut data[..]);
+            LongDataElement::new(data)
+        }
+    }
+
+    /// Generate a random instance of the requested de type, or `None` if that type does not support
+    /// plaintext.
+    pub(crate) fn random_test_de<R>(de_type: TestDataElementType, rng: &mut R) -> TestDataElement
+    where
+        R: rand::Rng,
+    {
+        match de_type {
+            TestDataElementType::Short => TestDataElement::Short(rng.gen()),
+            TestDataElementType::Long => TestDataElement::Long(rng.gen()),
+        }
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/data_elements/tx_power.rs b/nearby/presence/np_adv/src/legacy/data_elements/tx_power.rs
new file mode 100644
index 0000000..e8c787e
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/data_elements/tx_power.rs
@@ -0,0 +1,189 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Data element for TX Power.
+
+use crate::legacy::data_elements::de_type::{DeActualLength, DeEncodedLength, DeTypeCode};
+use crate::legacy::data_elements::{
+    DataElementDeserializeError, DataElementSerializationBuffer, DataElementSerializeError,
+    DeserializeDataElement, DirectMapPredicate, DirectMapper, LengthMapper, SerializeDataElement,
+};
+use crate::legacy::PacketFlavor;
+use crate::private::Sealed;
+use crate::shared_data::TxPower;
+use sink::Sink;
+
+/// Data element holding a [TxPower].
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct TxPowerDataElement {
+    /// The tx power value
+    pub tx_power: TxPower,
+}
+
+impl TxPowerDataElement {
+    /// Gets the underlying Tx Power value
+    pub fn tx_power_value(&self) -> i8 {
+        self.tx_power.as_i8()
+    }
+}
+
+impl From<TxPower> for TxPowerDataElement {
+    fn from(tx_power: TxPower) -> Self {
+        Self { tx_power }
+    }
+}
+
+impl Sealed for TxPowerDataElement {}
+
+impl<F: PacketFlavor> SerializeDataElement<F> for TxPowerDataElement {
+    fn de_type_code(&self) -> DeTypeCode {
+        TxPowerDataElement::DE_TYPE_CODE
+    }
+
+    fn map_actual_len_to_encoded_len(&self, actual_len: DeActualLength) -> DeEncodedLength {
+        <Self as DeserializeDataElement>::LengthMapper::map_actual_len_to_encoded_len(actual_len)
+    }
+
+    fn serialize_contents(
+        &self,
+        sink: &mut DataElementSerializationBuffer,
+    ) -> Result<(), DataElementSerializeError> {
+        sink.try_extend_from_slice(self.tx_power.as_i8().to_be_bytes().as_slice())
+            .ok_or(DataElementSerializeError::InsufficientSpace)
+    }
+}
+
+impl DeserializeDataElement for TxPowerDataElement {
+    const DE_TYPE_CODE: DeTypeCode = match DeTypeCode::try_from(0b0101) {
+        Ok(t) => t,
+        Err(_) => unreachable!(),
+    };
+
+    type LengthMapper = DirectMapper<TxPowerLengthPredicate>;
+
+    fn deserialize<F: PacketFlavor>(
+        de_contents: &[u8],
+    ) -> Result<Self, DataElementDeserializeError> {
+        de_contents
+            .try_into()
+            .ok()
+            .and_then(|arr: [u8; 1]| TxPower::try_from(i8::from_be_bytes(arr)).ok())
+            .map(|tx_power| Self { tx_power })
+            .ok_or(DataElementDeserializeError::DeserializeError { de_type: Self::DE_TYPE_CODE })
+    }
+}
+
+pub(in crate::legacy) struct TxPowerLengthPredicate;
+
+impl DirectMapPredicate for TxPowerLengthPredicate {
+    fn is_valid(len: usize) -> bool {
+        len == 1
+    }
+}
+
+#[allow(clippy::unwrap_used)]
+#[cfg(test)]
+mod tests {
+    use crate::legacy::data_elements::de_type::{DeActualLength, DeEncodedLength};
+    use crate::legacy::data_elements::tests::macros::de_roundtrip_test;
+    use crate::legacy::data_elements::tx_power::TxPowerDataElement;
+    use crate::legacy::data_elements::{DeserializeDataElement, LengthMapper};
+    use crate::legacy::serialize::tests::serialize;
+    use crate::legacy::{Ciphertext, Plaintext};
+    use crate::{shared_data, DeLengthOutOfRange};
+    use std::panic;
+
+    extern crate std;
+
+    #[test]
+    fn actual_length_must_be_1() {
+        for l in [0, 2] {
+            let actual = DeActualLength::try_from(l).unwrap();
+            let _ = panic::catch_unwind(|| {
+                <TxPowerDataElement as DeserializeDataElement>::LengthMapper::map_actual_len_to_encoded_len(actual)
+            })
+                .unwrap_err();
+        }
+
+        assert_eq!(
+            1,
+            <TxPowerDataElement as DeserializeDataElement>::LengthMapper::map_actual_len_to_encoded_len(
+                DeActualLength::try_from(1).unwrap(),
+            )
+                .as_u8()
+        )
+    }
+
+    #[test]
+    fn encoded_length_must_be_1() {
+        for l in [0, 2] {
+            assert_eq!(
+                DeLengthOutOfRange,
+                <TxPowerDataElement as DeserializeDataElement>::LengthMapper::map_encoded_len_to_actual_len(
+                    DeEncodedLength::try_from(l).unwrap()
+                )
+                    .unwrap_err()
+            )
+        }
+
+        assert_eq!(
+            1,
+            <TxPowerDataElement as DeserializeDataElement>::LengthMapper::map_encoded_len_to_actual_len(
+                DeEncodedLength::from(1)
+            )
+                .unwrap()
+                .as_u8()
+        );
+    }
+
+    #[test]
+    fn tx_power_de_contents_roundtrip_unencrypted() {
+        let tx = shared_data::TxPower::try_from(-10).unwrap();
+        let _ = de_roundtrip_test!(
+            TxPowerDataElement,
+            TxPower,
+            TxPower,
+            Plaintext,
+            serialize::<Plaintext, _>(&TxPowerDataElement::from(tx))
+        );
+    }
+
+    #[test]
+    fn tx_power_de_contents_roundtrip_ldt() {
+        let tx = shared_data::TxPower::try_from(-10).unwrap();
+
+        let _ = de_roundtrip_test!(
+            TxPowerDataElement,
+            TxPower,
+            TxPower,
+            Ciphertext,
+            serialize::<Ciphertext, _>(&TxPowerDataElement::from(tx))
+        );
+    }
+
+    mod coverage_gaming {
+        use crate::legacy::data_elements::tx_power::TxPowerDataElement;
+        use crate::shared_data::TxPower;
+        use alloc::format;
+
+        #[test]
+        fn tx_power_de() {
+            let de = TxPowerDataElement::from(TxPower::try_from(3).unwrap());
+            // debug
+            let _ = format!("{:?}", de);
+            // trivial accessor
+            assert_eq!(3, de.tx_power_value());
+        }
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/de_type/mod.rs b/nearby/presence/np_adv/src/legacy/de_type/mod.rs
deleted file mode 100644
index 304347d..0000000
--- a/nearby/presence/np_adv/src/legacy/de_type/mod.rs
+++ /dev/null
@@ -1,356 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! V0 data element types.
-//!
-//! In V0, there are only 16 DE types total, and parsing unknown types is not possible, so we can
-//! represent all known DE types in enums without needing to handle the "unknown type" case.
-
-use crate::{
-    de_type::IdentityDataElementType,
-    legacy::{
-        actions::ActionsDataElement,
-        data_elements::{DataElement as _, TxPowerDataElement},
-        Ciphertext, PacketFlavorEnum, Plaintext, NP_MAX_DE_CONTENT_LEN,
-    },
-    DeLengthOutOfRange,
-};
-use core::ops;
-use ldt_np_adv::NP_LEGACY_METADATA_KEY_LEN;
-use strum_macros::EnumIter;
-
-#[cfg(test)]
-mod tests;
-
-/// A V0 DE type in `[0, 15]`.
-#[derive(Debug, PartialEq, Eq, Hash)]
-pub(crate) struct DeTypeCode {
-    /// The code used in a V0 adv header
-    code: u8,
-}
-
-impl DeTypeCode {
-    /// Returns a u8 in `[0, 15`].
-    pub(crate) fn as_u8(&self) -> u8 {
-        self.code
-    }
-}
-
-/// The DE type code is out of range for v0 DE types.
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub(crate) struct DeTypeCodeOutOfRange;
-
-impl TryFrom<u8> for DeTypeCode {
-    type Error = DeTypeCodeOutOfRange;
-
-    fn try_from(value: u8) -> Result<Self, Self::Error> {
-        if value < 16 {
-            Ok(Self { code: value })
-        } else {
-            Err(DeTypeCodeOutOfRange)
-        }
-    }
-}
-
-/// The actual length of a DE, not the encoded representation.
-///
-/// See [DeEncodedLength] for the encoded length.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(crate) struct DeActualLength {
-    /// Invariant: <= `NP_MAX_DE_CONTENT_LEN`.
-    len: u8,
-}
-
-impl DeActualLength {
-    pub(crate) const ZERO: DeActualLength = DeActualLength { len: 0 };
-
-    pub(crate) fn as_usize(&self) -> usize {
-        self.len as usize
-    }
-}
-
-impl TryFrom<u8> for DeActualLength {
-    type Error = DeLengthOutOfRange;
-
-    fn try_from(value: u8) -> Result<Self, Self::Error> {
-        if (value as usize) <= NP_MAX_DE_CONTENT_LEN {
-            Ok(Self { len: value })
-        } else {
-            Err(DeLengthOutOfRange)
-        }
-    }
-}
-
-impl TryFrom<usize> for DeActualLength {
-    type Error = DeLengthOutOfRange;
-
-    fn try_from(value: usize) -> Result<Self, Self::Error> {
-        if value <= NP_MAX_DE_CONTENT_LEN {
-            Ok(Self { len: value as u8 })
-        } else {
-            Err(DeLengthOutOfRange)
-        }
-    }
-}
-
-/// The encoded length of a DE, not the actual length of the DE contents.
-///
-/// See [DeActualLength] for the length of the contents.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(crate) struct DeEncodedLength {
-    /// Invariant: `len <= 0x0F` (15, aka 4 bits)
-    len: u8,
-}
-
-impl DeEncodedLength {
-    /// Returns a u8 in `[0, 15]`
-    pub(crate) fn as_u8(&self) -> u8 {
-        self.len
-    }
-
-    /// Returns a usize in `[0, 15]`
-    pub(crate) fn as_usize(&self) -> usize {
-        self.len as usize
-    }
-}
-
-impl TryFrom<u8> for DeEncodedLength {
-    type Error = DeLengthOutOfRange;
-
-    fn try_from(value: u8) -> Result<Self, Self::Error> {
-        if value < 16 {
-            Ok(Self { len: value })
-        } else {
-            Err(DeLengthOutOfRange)
-        }
-    }
-}
-
-/// DE types for normal DEs (not an identity).
-///
-/// May be contained in identity DEs (see [IdentityDataElementType]). Must not overlap with
-/// [IdentityDataElementType].
-#[derive(EnumIter, Debug, Clone, Copy, PartialEq, Eq)]
-pub(crate) enum PlainDataElementType {
-    TxPower,
-    Actions,
-}
-
-impl PlainDataElementType {
-    pub(crate) fn as_generic_de_type(&self) -> DataElementType {
-        match self {
-            PlainDataElementType::TxPower => DataElementType::TxPower,
-            PlainDataElementType::Actions => DataElementType::Actions,
-        }
-    }
-
-    pub(crate) fn supports_flavor(&self, flavor: PacketFlavorEnum) -> bool {
-        match self {
-            PlainDataElementType::Actions => {
-                // Actions is effectively two different DEs based on which actions it can
-                // contain, so we have to check them separately. The Plaintext one obviously
-                // supports plaintext, and vice versa, so we could just say `true` here, but we
-                // spell it out to be consistent with the other DE types.
-                match flavor {
-                    PacketFlavorEnum::Plaintext => {
-                        ActionsDataElement::<Plaintext>::supports_flavor(flavor)
-                    }
-                    PacketFlavorEnum::Ciphertext => {
-                        ActionsDataElement::<Ciphertext>::supports_flavor(flavor)
-                    }
-                }
-            }
-            PlainDataElementType::TxPower => TxPowerDataElement::supports_flavor(flavor),
-        }
-    }
-}
-
-/// Corresponds to the different implementations of [DataElement].
-///
-/// It's intended for use cases that don't care if a DE is a plain or identity DE. Every variant
-/// corresponds to either a [PlainDataElementType] or a [IdentityDataElementType].
-#[derive(EnumIter, Debug, Clone, Copy, PartialEq, Eq)]
-#[allow(missing_docs)]
-#[doc(hidden)]
-pub enum DataElementType {
-    PrivateIdentity,
-    TrustedIdentity,
-    PublicIdentity,
-    ProvisionedIdentity,
-    TxPower,
-    Actions,
-}
-
-const DE_TYPES_BY_CODE: [Option<DataElementType>; 16] = [
-    None,                                       // 0b0000
-    Some(DataElementType::PrivateIdentity),     // 0b0001
-    Some(DataElementType::TrustedIdentity),     // 0b0010
-    Some(DataElementType::PublicIdentity),      // 0b0011
-    Some(DataElementType::ProvisionedIdentity), // 0b0100
-    Some(DataElementType::TxPower),             // 0b0101
-    Some(DataElementType::Actions),             // 0b0110
-    None,                                       // 0b0111
-    None,                                       // 0b1000
-    None,                                       // 0b1001
-    None,                                       // 0b1010
-    None,                                       // 0b1011
-    None,                                       // 0b1100
-    None,                                       // 0b1101
-    None,                                       // 0b1110
-    None,                                       // 0b1111
-];
-
-impl DataElementType {
-    /// Salt + key + at least 2 bytes of DE to make LDT ciphertext min length
-    const VALID_ENCRYPTED_IDENTITY_DE_ACTUAL_LEN: ops::RangeInclusive<usize> =
-        (2 + NP_LEGACY_METADATA_KEY_LEN + 2..=NP_MAX_DE_CONTENT_LEN);
-    const VALID_ENCRYPTED_IDENTITY_DE_HEADER_LEN: ops::RangeInclusive<usize> = (2..=6);
-
-    /// If there is a corresponding [ContainerDataElementType], returns it, otherwise None.
-    pub(crate) fn try_as_identity_de_type(&self) -> Option<IdentityDataElementType> {
-        match self {
-            DataElementType::PrivateIdentity => Some(IdentityDataElementType::Private),
-            DataElementType::TrustedIdentity => Some(IdentityDataElementType::Trusted),
-            DataElementType::PublicIdentity => Some(IdentityDataElementType::Public),
-            DataElementType::ProvisionedIdentity => Some(IdentityDataElementType::Provisioned),
-            DataElementType::TxPower | DataElementType::Actions => None,
-        }
-    }
-
-    /// If there is a corresponding [PlainDataElementType], returns it, otherwise None.
-    pub(crate) fn try_as_plain_de_type(&self) -> Option<PlainDataElementType> {
-        match self {
-            DataElementType::TxPower => Some(PlainDataElementType::TxPower),
-            DataElementType::Actions => Some(PlainDataElementType::Actions),
-            DataElementType::PrivateIdentity
-            | DataElementType::TrustedIdentity
-            | DataElementType::PublicIdentity
-            | DataElementType::ProvisionedIdentity => None,
-        }
-    }
-
-    /// Returns the matching type for the code, else `None`
-    pub(crate) fn from_type_code(de_type: DeTypeCode) -> Option<Self> {
-        DE_TYPES_BY_CODE.get(de_type.as_u8() as usize).and_then(|o| *o)
-    }
-
-    /// A type code in `[0,15]` for use in the high bits of the DE header byte.
-    pub(crate) fn type_code(&self) -> DeTypeCode {
-        match self {
-            DataElementType::PrivateIdentity => IdentityDataElementType::Private.shared_type_code(),
-            DataElementType::TrustedIdentity => IdentityDataElementType::Trusted.shared_type_code(),
-            DataElementType::PublicIdentity => IdentityDataElementType::Public.shared_type_code(),
-            DataElementType::ProvisionedIdentity => {
-                IdentityDataElementType::Provisioned.shared_type_code()
-            }
-            DataElementType::TxPower => 0b0101,
-            DataElementType::Actions => 0b0110,
-        }
-        .try_into()
-        .expect("hardcoded type codes are valid")
-    }
-
-    /// Convert the actual DE length to the encoded length included in the header.
-    ///
-    /// Returns `Err` if the actual length is invalid for the type, or the corresponding encoded length is out of range.
-    pub(crate) fn encoded_len_for_actual_len(
-        &self,
-        actual_len: DeActualLength,
-    ) -> Result<DeEncodedLength, DeLengthOutOfRange> {
-        match self {
-            // TODO 0-length provisioned
-            DataElementType::PrivateIdentity
-            | DataElementType::TrustedIdentity
-            | DataElementType::ProvisionedIdentity => {
-                if !Self::VALID_ENCRYPTED_IDENTITY_DE_ACTUAL_LEN.contains(&actual_len.as_usize()) {
-                    Err(DeLengthOutOfRange)
-                } else {
-                    actual_len
-                        .len
-                        .checked_sub(16)
-                        .ok_or(DeLengthOutOfRange)
-                        .and_then(|n| n.try_into())
-                }
-            }
-            DataElementType::PublicIdentity => {
-                if actual_len.len != 0 {
-                    Err(DeLengthOutOfRange)
-                } else {
-                    actual_len.len.try_into()
-                }
-            }
-            DataElementType::TxPower => {
-                if actual_len.len != 1 {
-                    Err(DeLengthOutOfRange)
-                } else {
-                    actual_len.len.try_into()
-                }
-            }
-            DataElementType::Actions => {
-                // doesn't matter which variant is used
-                if !ActionsDataElement::<Plaintext>::ACTIONS_LEN.contains(&actual_len.as_usize()) {
-                    Err(DeLengthOutOfRange)
-                } else {
-                    actual_len.len.try_into()
-                }
-            }
-        }
-    }
-
-    /// Convert the length in the header to the actual DE length.
-    ///
-    /// Returns `Err` if the encoded length is invalid for the type, or the corresponding actual length is out of range.
-    pub(crate) fn actual_len_for_encoded_len(
-        &self,
-        header_len: DeEncodedLength,
-    ) -> Result<DeActualLength, DeLengthOutOfRange> {
-        match self {
-            DataElementType::PrivateIdentity
-            | DataElementType::TrustedIdentity
-            // TODO provisioned 0 length
-            | DataElementType::ProvisionedIdentity => {
-                if !Self::VALID_ENCRYPTED_IDENTITY_DE_HEADER_LEN.contains(&header_len.as_usize()) {
-                    Err(DeLengthOutOfRange)
-                } else {
-                    header_len
-                        .len
-                        .checked_add(16)
-                        .ok_or(DeLengthOutOfRange)
-                        .and_then(|n| n.try_into())
-                }
-            }
-            DataElementType::PublicIdentity => {
-                if header_len.len != 0 {
-                    Err(DeLengthOutOfRange)
-                } else {
-                    header_len.len.try_into()
-                }
-            }
-            DataElementType::TxPower => {
-                if header_len.len != 1 {
-                    Err(DeLengthOutOfRange)
-                } else {
-                    header_len.len.try_into()
-                }
-            }
-            DataElementType::Actions => {
-                if !ActionsDataElement::<Plaintext>::ACTIONS_LEN.contains(&header_len.as_usize()) {
-                    Err(DeLengthOutOfRange)
-                } else {
-                    header_len.len.try_into()
-                }
-            }
-        }
-    }
-}
diff --git a/nearby/presence/np_adv/src/legacy/de_type/tests.rs b/nearby/presence/np_adv/src/legacy/de_type/tests.rs
deleted file mode 100644
index fc64321..0000000
--- a/nearby/presence/np_adv/src/legacy/de_type/tests.rs
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#![allow(clippy::unwrap_used)]
-
-extern crate std;
-
-use super::*;
-use crate::legacy::serialize::id_de_type_as_generic_de_type;
-use std::{collections, vec::Vec};
-use strum::IntoEnumIterator as _;
-
-#[test]
-fn no_plain_vs_identity_type_overlap() {
-    let plain_types =
-        PlainDataElementType::iter().map(|t| t.as_generic_de_type()).collect::<Vec<_>>();
-    let identity_types =
-        IdentityDataElementType::iter().map(id_de_type_as_generic_de_type).collect::<Vec<_>>();
-
-    for plain_de_type in plain_types.iter() {
-        assert!(!identity_types.iter().any(|i| i == plain_de_type));
-        assert_eq!(None, plain_de_type.try_as_identity_de_type());
-    }
-
-    for id_de_type in identity_types.iter() {
-        assert!(!plain_types.iter().any(|p| p == id_de_type));
-        assert_eq!(None, id_de_type.try_as_plain_de_type());
-    }
-}
-
-#[test]
-fn generic_type_is_either_plain_or_identity() {
-    let generic_types = DataElementType::iter().collect::<Vec<_>>();
-
-    for g in generic_types.iter() {
-        let total = g.try_as_identity_de_type().map(|_| 1).unwrap_or(0)
-            + g.try_as_plain_de_type().map(|_| 1).unwrap_or(0);
-
-        assert_eq!(1, total);
-    }
-}
-
-#[test]
-fn generic_de_type_codes_are_consistent() {
-    for det in DataElementType::iter() {
-        let actual = DataElementType::from_type_code(det.type_code());
-        assert_eq!(Some(det), actual)
-    }
-}
-
-#[test]
-fn generic_de_distinct_type_codes() {
-    let codes =
-        DataElementType::iter().map(|det| det.type_code()).collect::<collections::HashSet<_>>();
-    assert_eq!(codes.len(), DataElementType::iter().count());
-}
-
-#[test]
-fn generic_de_no_accidentally_mapped_type_codes() {
-    let codes =
-        DataElementType::iter().map(|det| det.type_code()).collect::<collections::HashSet<_>>();
-    for possible_code in 0..=15 {
-        if codes.contains(&possible_code.try_into().unwrap()) {
-            continue;
-        }
-
-        assert_eq!(None, DataElementType::from_type_code(possible_code.try_into().unwrap()));
-    }
-}
-
-#[test]
-fn actions_de_length_zero_rejected() {
-    let encoded = DeEncodedLength::try_from(0).unwrap();
-    let maybe_actual_len = DataElementType::Actions.actual_len_for_encoded_len(encoded);
-    assert_eq!(Err(DeLengthOutOfRange), maybe_actual_len);
-}
-
-#[test]
-fn de_length_actual_encoded_round_trip() {
-    for de_type in DataElementType::iter() {
-        // for all possible lengths, calculate actual -> encoded and the inverse
-        let actual_to_encoded = (0_u8..=255)
-            .filter_map(|num| num.try_into().ok())
-            .filter_map(|actual: DeActualLength| {
-                de_type.encoded_len_for_actual_len(actual).ok().map(|encoded| (actual, encoded))
-            })
-            .collect::<collections::HashMap<_, _>>();
-
-        let encoded_to_actual = (0_u8..=255)
-            .filter_map(|num| num.try_into().ok())
-            .filter_map(|encoded: DeEncodedLength| {
-                de_type.actual_len_for_encoded_len(encoded).ok().map(|actual| (encoded, actual))
-            })
-            .collect::<collections::HashMap<_, _>>();
-
-        // ensure the two maps are inverses of each other
-        assert_eq!(
-            actual_to_encoded,
-            encoded_to_actual.into_iter().map(|(encoded, actual)| (actual, encoded)).collect(),
-            "de type: {de_type:?}"
-        );
-    }
-}
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/intermediate/mod.rs b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/mod.rs
new file mode 100644
index 0000000..13ede05
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/mod.rs
@@ -0,0 +1,146 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! The first stage of deserialization: just header data (if any) and the bulk contents of the
+//! advertisement, with no disaggregation into individual data elements.
+
+use crate::header::V0Encoding;
+use crate::helpers::parse_byte_array;
+use crate::legacy::deserialize::{
+    AdvDeserializeError, DeIterator, DecryptError, DecryptedAdvContents,
+};
+#[cfg(test)]
+use crate::legacy::deserialize::{DataElementDeserializer, GenericDeIterator};
+use crate::legacy::{Plaintext, NP_MIN_ADV_CONTENT_LEN};
+use crypto_provider::CryptoProvider;
+use ldt_np_adv::V0Salt;
+use nom::combinator;
+
+#[cfg(test)]
+mod tests;
+
+/// The header components, if any, and the bytes that will later be decrypted and/or parsed into DEs.
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) enum IntermediateAdvContents<'d> {
+    /// Plaintext advertisements
+    Unencrypted(UnencryptedAdvContents<'d>),
+    /// Ciphertext advertisements
+    Ldt(LdtAdvContents<'d>),
+}
+
+impl<'d> IntermediateAdvContents<'d> {
+    #[cfg(test)]
+    pub(crate) fn as_unencrypted(&self) -> Option<&UnencryptedAdvContents<'d>> {
+        match self {
+            IntermediateAdvContents::Unencrypted(c) => Some(c),
+            IntermediateAdvContents::Ldt(_) => None,
+        }
+    }
+
+    #[cfg(test)]
+    pub(crate) fn as_ldt(&self) -> Option<&LdtAdvContents<'d>> {
+        match self {
+            IntermediateAdvContents::Unencrypted(_) => None,
+            IntermediateAdvContents::Ldt(c) => Some(c),
+        }
+    }
+
+    /// Performs basic structural checks on header and content, but doesn't deserialize DEs or
+    /// decrypt.
+    pub(crate) fn deserialize<C: CryptoProvider>(
+        encoding: V0Encoding,
+        input: &[u8],
+    ) -> Result<IntermediateAdvContents<'_>, AdvDeserializeError> {
+        match encoding {
+            V0Encoding::Unencrypted => {
+                if input.len() < NP_MIN_ADV_CONTENT_LEN {
+                    return Err(AdvDeserializeError::NoDataElements);
+                }
+                Ok(IntermediateAdvContents::Unencrypted(UnencryptedAdvContents { data: input }))
+            }
+            V0Encoding::Ldt => {
+                let (ciphertext, salt) =
+                    parse_v0_salt(input).map_err(|_| AdvDeserializeError::InvalidStructure)?;
+                LdtAdvContents::new::<C>(salt, ciphertext)
+                    .ok_or(AdvDeserializeError::InvalidStructure)
+                    .map(IntermediateAdvContents::Ldt)
+            }
+        }
+    }
+}
+
+/// The contents of a plaintext advertisement.
+#[derive(Debug, PartialEq, Eq)]
+pub struct UnencryptedAdvContents<'d> {
+    /// Contents of the advertisement after the version header.
+    ///
+    /// Contents are at least [NP_MIN_ADV_CONTENT_LEN].
+    data: &'d [u8],
+}
+
+impl<'d> UnencryptedAdvContents<'d> {
+    /// Returns an iterator over the v0 data elements
+    pub fn data_elements(&self) -> DeIterator<'d, Plaintext> {
+        DeIterator::new(self.data)
+    }
+
+    #[cfg(test)]
+    pub(in crate::legacy) fn generic_data_elements<D: DataElementDeserializer>(
+        &self,
+    ) -> GenericDeIterator<Plaintext, D> {
+        GenericDeIterator::new(self.data)
+    }
+}
+
+/// Contents of an encrypted advertisement before decryption.
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) struct LdtAdvContents<'d> {
+    /// Salt from the advertisement, converted into a padder.
+    /// Pre-calculated so it's only derived once across multiple decrypt attempts.
+    salt_padder: ldt::XorPadder<{ crypto_provider::aes::BLOCK_SIZE }>,
+    /// The salt instance used for encryption of this advertisement.
+    salt: V0Salt,
+    /// Ciphertext containing the identity token and any data elements.
+    /// Must be a valid length for LDT.
+    ciphertext: &'d [u8],
+}
+
+impl<'d> LdtAdvContents<'d> {
+    /// Returns `None` if `ciphertext` is not a valid LDT-XTS-AES ciphertext length.
+    pub(crate) fn new<C: CryptoProvider>(salt: V0Salt, ciphertext: &'d [u8]) -> Option<Self> {
+        if !ldt_np_adv::VALID_INPUT_LEN.contains(&ciphertext.len()) {
+            return None;
+        }
+        Some(Self { salt_padder: ldt_np_adv::salt_padder::<C>(salt), salt, ciphertext })
+    }
+
+    /// Try decrypting with an identity's LDT cipher and deserializing the resulting data elements.
+    ///
+    /// Returns the decrypted data if decryption and verification succeeded and the resulting DEs could be parsed
+    /// successfully, otherwise `Err`.
+    pub(crate) fn try_decrypt<C: CryptoProvider>(
+        &self,
+        cipher: &ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>,
+    ) -> Result<DecryptedAdvContents, DecryptError> {
+        let (identity_token, plaintext) = cipher
+            .decrypt_and_verify(self.ciphertext, &self.salt_padder)
+            .map_err(|_e| DecryptError::DecryptOrVerifyError)?;
+
+        Ok(DecryptedAdvContents::new(identity_token, self.salt, plaintext))
+    }
+}
+
+fn parse_v0_salt(input: &[u8]) -> nom::IResult<&[u8], V0Salt> {
+    combinator::map(parse_byte_array::<2>, V0Salt::from)(input)
+}
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/error_conditions.rs b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/error_conditions.rs
new file mode 100644
index 0000000..5cbd784
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/error_conditions.rs
@@ -0,0 +1,85 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#![allow(clippy::unwrap_used)]
+
+mod unencrypted_encoder {
+    use crate::header::V0Encoding;
+    use crate::legacy::deserialize::intermediate::IntermediateAdvContents;
+    use crate::legacy::deserialize::AdvDeserializeError;
+    use crypto_provider_default::CryptoProviderImpl;
+
+    #[test]
+    fn parse_zero_len_error() {
+        assert_eq!(
+            AdvDeserializeError::NoDataElements,
+            IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+                V0Encoding::Unencrypted,
+                &[],
+            )
+            .unwrap_err()
+        );
+    }
+}
+
+mod ldt_encoder {
+    use crate::header::V0Encoding;
+    use crate::legacy::deserialize::intermediate::IntermediateAdvContents;
+    use crate::legacy::deserialize::AdvDeserializeError;
+    use alloc::vec;
+    use crypto_provider_default::CryptoProviderImpl;
+    use ldt_np_adv::{V0_IDENTITY_TOKEN_LEN, V0_SALT_LEN};
+
+    #[test]
+    fn all_below_min_ldt_len_error() {
+        for len in 0..(V0_SALT_LEN + ldt_np_adv::VALID_INPUT_LEN.start) {
+            assert_eq!(
+                AdvDeserializeError::InvalidStructure,
+                IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+                    V0Encoding::Ldt,
+                    &vec![0; len],
+                )
+                .unwrap_err()
+            );
+        }
+
+        // 1 more byte is enough
+        let data = &[0; ldt_np_adv::VALID_INPUT_LEN.start + V0_SALT_LEN];
+        assert!(IntermediateAdvContents::deserialize::<CryptoProviderImpl>(V0Encoding::Ldt, data)
+            .is_ok())
+    }
+
+    #[test]
+    fn above_max_ldt_len_error() {
+        let salt = [0x55; V0_SALT_LEN];
+        // this is longer than can fit in a BLE 4.2 adv, but we're just testing LDT limits here
+        let payload_len = 2 + crypto_provider::aes::BLOCK_SIZE;
+        let data =
+            &[&salt, [0x11; V0_IDENTITY_TOKEN_LEN].as_slice(), &vec![0xCC; payload_len]].concat();
+        // 1 byte long
+        assert_eq!(V0_SALT_LEN + ldt_np_adv::VALID_INPUT_LEN.end, data.len());
+
+        assert_eq!(
+            AdvDeserializeError::InvalidStructure,
+            IntermediateAdvContents::deserialize::<CryptoProviderImpl>(V0Encoding::Ldt, data)
+                .unwrap_err()
+        );
+
+        // 1 fewer byte is enough
+        let data = &[&salt, [0x11; V0_IDENTITY_TOKEN_LEN].as_slice(), &vec![0xCC; payload_len - 1]]
+            .concat();
+        assert!(IntermediateAdvContents::deserialize::<CryptoProviderImpl>(V0Encoding::Ldt, data)
+            .is_ok())
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/happy_path.rs b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/happy_path.rs
new file mode 100644
index 0000000..f74c1a9
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/happy_path.rs
@@ -0,0 +1,106 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#![allow(clippy::unwrap_used)]
+
+mod unencrypted_encoder {
+    use crate::header::V0Encoding;
+    use crate::legacy::data_elements::de_type::{DeEncodedLength, DeTypeCode, MAX_DE_ENCODED_LEN};
+    use crate::legacy::deserialize::intermediate::IntermediateAdvContents;
+    use crate::legacy::serialize::encode_de_header;
+    use crate::legacy::NP_MAX_DE_CONTENT_LEN;
+    use crypto_provider_default::CryptoProviderImpl;
+
+    #[test]
+    fn parse_min_len() {
+        let header = encode_de_header(DeTypeCode::try_from(14).unwrap(), DeEncodedLength::from(0));
+        assert_eq!(
+            &[header],
+            IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+                V0Encoding::Unencrypted,
+                &[header],
+            )
+            .unwrap()
+            .as_unencrypted()
+            .unwrap()
+            .data
+        )
+    }
+
+    #[test]
+    fn parse_max_len() {
+        let header = encode_de_header(
+            DeTypeCode::try_from(15).unwrap(),
+            DeEncodedLength::try_from(MAX_DE_ENCODED_LEN).unwrap(),
+        );
+        let data = &[&[header], [0x22; NP_MAX_DE_CONTENT_LEN].as_slice()].concat();
+        assert_eq!(
+            data,
+            IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+                V0Encoding::Unencrypted,
+                data,
+            )
+            .unwrap()
+            .as_unencrypted()
+            .unwrap()
+            .data
+        )
+    }
+}
+
+mod ldt_encoder {
+    use crate::header::V0Encoding;
+    use crate::legacy::deserialize::intermediate::{IntermediateAdvContents, LdtAdvContents};
+    use crate::legacy::NP_MAX_ADV_CONTENT_LEN;
+    use crypto_provider_default::CryptoProviderImpl;
+    use ldt::LdtCipher;
+    use ldt_np_adv::{salt_padder, V0_IDENTITY_TOKEN_LEN, V0_SALT_LEN};
+
+    #[test]
+    fn parse_min_len() {
+        // not going to bother to make it look like encrypted DEs since this layer doesn't care
+        let salt = [0x55; V0_SALT_LEN];
+        let data = &[&salt, [0x11; V0_IDENTITY_TOKEN_LEN].as_slice(), &[0xCC; 2]].concat();
+        assert_eq!(
+            V0_SALT_LEN
+                + ldt_np_adv::NpLdtEncryptCipher::<CryptoProviderImpl>::VALID_INPUT_LEN.start,
+            data.len()
+        );
+
+        assert_ldt_contents(salt, data);
+    }
+
+    #[test]
+    fn parse_max_len() {
+        let salt = [0x55; V0_SALT_LEN];
+        let data = &[&salt, [0x11; V0_IDENTITY_TOKEN_LEN].as_slice(), &[0xCC; 7]].concat();
+        assert_eq!(NP_MAX_ADV_CONTENT_LEN, data.len());
+
+        assert_ldt_contents(salt, data);
+    }
+
+    fn assert_ldt_contents(salt: [u8; V0_SALT_LEN], data: &[u8]) {
+        assert_eq!(
+            &LdtAdvContents {
+                salt_padder: salt_padder::<CryptoProviderImpl>(salt.into()),
+                salt: salt.into(),
+                ciphertext: &data[V0_SALT_LEN..],
+            },
+            IntermediateAdvContents::deserialize::<CryptoProviderImpl>(V0Encoding::Ldt, data)
+                .unwrap()
+                .as_ldt()
+                .unwrap()
+        );
+    }
+}
diff --git a/nearby/util/pourover_macro_core/src/lib.rs b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/mod.rs
similarity index 79%
copy from nearby/util/pourover_macro_core/src/lib.rs
copy to nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/mod.rs
index ae06345..289fdd8 100644
--- a/nearby/util/pourover_macro_core/src/lib.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/mod.rs
@@ -1,10 +1,10 @@
-// Copyright 2024 Google LLC
+// Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
 //
-//     http://www.apache.org/licenses/LICENSE-2.0
+//      http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
 // distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,5 +12,5 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-mod jni_method;
-pub use jni_method::jni_method;
+mod error_conditions;
+mod happy_path;
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/mod.rs b/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
index d6c6b36..d305b57 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
@@ -16,449 +16,320 @@
 //!
 //! This module only deals with the _contents_ of an advertisement, not the advertisement header.
 
+use core::fmt;
 use core::marker::PhantomData;
 
+use crate::legacy::data_elements::tx_power::TxPowerDataElement;
 use crate::{
     credential::v0::V0,
-    de_type::EncryptedIdentityDataElementType,
     legacy::{
-        actions,
-        data_elements::{DataElement, *},
-        de_type::{DataElementType, DeEncodedLength, DeTypeCode, PlainDataElementType},
-        Ciphertext, PacketFlavor, Plaintext, ShortMetadataKey, NP_MAX_DE_CONTENT_LEN,
+        data_elements::{
+            actions,
+            de_type::{DeEncodedLength, DeTypeCode},
+            DataElementDeserializeError, DeserializeDataElement, LengthMapper,
+        },
+        Ciphertext, PacketFlavor,
     },
-    HasIdentityMatch, PlaintextIdentityMode,
+    DeLengthOutOfRange,
 };
 use array_view::ArrayView;
-use crypto_provider::CryptoProvider;
-use ldt_np_adv::{LegacySalt, NP_LEGACY_METADATA_KEY_LEN};
-use nom::{bytes, combinator, number, sequence};
-
-use super::BLE_ADV_SVC_CONTENT_LEN;
+use ldt_np_adv::{V0IdentityToken, V0Salt, NP_LDT_MAX_EFFECTIVE_PAYLOAD_LEN};
+use nom::{bytes, combinator, number, Finish};
 
 #[cfg(test)]
 mod tests;
 
-/// Deserialize an advertisement into data elements (if plaintext) or an identity type and
-/// ciphertext.
-pub(crate) fn deserialize_adv_contents<C: CryptoProvider>(
-    input: &[u8],
-) -> Result<IntermediateAdvContents<'_>, AdvDeserializeError> {
-    parse_raw_adv_contents::<C>(input).and_then(|raw_adv| match raw_adv {
-        RawAdvertisement::Plaintext(adv_contents) => {
-            if adv_contents.data_elements().next().is_none() {
-                return Err(AdvDeserializeError::NoPublicDataElements);
-            }
+pub(crate) mod intermediate;
 
-            Ok(IntermediateAdvContents::Plaintext(adv_contents))
-        }
-        RawAdvertisement::Ciphertext(eac) => Ok(IntermediateAdvContents::Ciphertext(eac)),
-    })
-}
-
-/// Parse an advertisement's contents to the level of raw data elements (i.e no decryption,
-/// no per-type deserialization, etc).
-///
-/// Consumes the entire input.
-fn parse_raw_adv_contents<C: CryptoProvider>(
-    input: &[u8],
-) -> Result<RawAdvertisement, AdvDeserializeError> {
-    if input.is_empty() {
-        return Err(AdvDeserializeError::MissingIdentity);
-    }
-    match parse_de(input) {
-        Ok((rem, identity_de)) => {
-            if let Some(identity_de_type) = identity_de.de_type.try_as_identity_de_type() {
-                match identity_de_type.as_encrypted_identity_de_type() {
-                    Some(encrypted_de_type) => {
-                        if matches!(parse_de(rem), Err(nom::Err::Error(..))) {
-                            match encrypted_de_type {
-                                // TODO handle length=0 provisioned identity DEs
-                                EncryptedIdentityDataElementType::Private
-                                | EncryptedIdentityDataElementType::Trusted
-                                | EncryptedIdentityDataElementType::Provisioned => combinator::map(
-                                    parse_encrypted_identity_de_contents,
-                                    |(salt, payload)| {
-                                        RawAdvertisement::Ciphertext(EncryptedAdvContents {
-                                            identity_type: encrypted_de_type,
-                                            salt_padder: ldt_np_adv::salt_padder::<16, C>(salt),
-                                            salt,
-                                            ciphertext: payload,
-                                        })
-                                    },
-                                )(
-                                    identity_de.contents,
-                                )
-                                .map(|(_rem, contents)| contents)
-                                .map_err(|_e| AdvDeserializeError::AdvertisementDeserializeError),
-                            }
-                        } else {
-                            Err(AdvDeserializeError::TooManyTopLevelDataElements)
-                        }
-                    }
-                    // It's an identity de, but not encrypted, so it must be public, and the rest
-                    // must be plain
-                    None => Ok(RawAdvertisement::Plaintext(PlaintextAdvContents {
-                        identity_type: PlaintextIdentityMode::Public,
-                        data: rem,
-                    })),
-                }
-            } else {
-                Err(AdvDeserializeError::MissingIdentity)
-            }
-        }
-        Err(nom::Err::Error(_)) | Err(nom::Err::Failure(_)) => {
-            Err(AdvDeserializeError::AdvertisementDeserializeError)
-        }
-        Err(nom::Err::Incomplete(_)) => {
-            panic!("Should not hit Incomplete when using nom::complete parsers")
-        }
-    }
-}
+use crate::credential::matched::HasIdentityMatch;
+use crate::legacy::data_elements::actions::ActionsDataElement;
+use crate::legacy::data_elements::de_type::{DataElementType, DeActualLength};
+use crate::legacy::Plaintext;
+/// exposed because the unencrypted case isn't just for intermediate: no further processing is needed
+pub use intermediate::UnencryptedAdvContents;
 
 /// Legacy advertisement parsing errors
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub(crate) enum AdvDeserializeError {
-    /// Parsing the overall advertisement or DE structure failed
-    AdvertisementDeserializeError,
-    /// Must not have any other top level data elements if there is an encrypted identity DE
-    TooManyTopLevelDataElements,
-    /// Missing identity DE
-    MissingIdentity,
-    /// Non-identity DE contents must not be empty
-    NoPublicDataElements,
-}
-
-/// Parse an individual DE into its header and contents.
-fn parse_de(input: &[u8]) -> nom::IResult<&[u8], RawDataElement, DataElementDeserializeError> {
-    let (remaining, (de_type, actual_len)) =
-        combinator::map_opt(number::complete::u8, |de_header| {
-            // header: LLLLTTTT
-            let len = de_header >> 4;
-            let de_type = de_header & 0x0F;
-            DeTypeCode::try_from(de_type).ok().and_then(DataElementType::from_type_code).and_then(
-                |de_type| {
-                    len.try_into()
-                        .ok()
-                        .and_then(|len: DeEncodedLength| {
-                            de_type.actual_len_for_encoded_len(len).ok()
-                        })
-                        .map(|len| (de_type, len))
-                },
-            )
-        })(input)?;
-
-    combinator::map(bytes::complete::take(actual_len.as_usize()), move |contents| RawDataElement {
-        de_type,
-        contents,
-    })(remaining)
-}
-
-/// Parse legacy encrypted identity DEs (private, trusted, provisioned) into salt and ciphertext
-/// (encrypted metadata key and at least 2 bytes of DEs).
-///
-/// Consumes the entire input.
-fn parse_encrypted_identity_de_contents(
-    de_contents: &[u8],
-) -> nom::IResult<&[u8], (ldt_np_adv::LegacySalt, &[u8])> {
-    combinator::all_consuming(sequence::tuple((
-        // 2-byte salt
-        combinator::map(
-            combinator::map_res(bytes::complete::take(2_usize), |slice: &[u8]| slice.try_into()),
-            |arr: [u8; 2]| arr.into(),
-        ),
-        // 14-byte encrypted metadata key plus encrypted DEs, which must together be at least 16
-        // bytes (AES block size), and at most a full DE minus the size of the salt.
-        bytes::complete::take_while_m_n(16_usize, NP_MAX_DE_CONTENT_LEN - 2, |_| true),
-    )))(de_contents)
+    /// Header or other structure was invalid
+    InvalidStructure,
+    /// DE contents must not be empty
+    NoDataElements,
 }
 
 /// A data element with content length determined and validated per its type's length rules, but
 /// no further decoding performed.
 #[derive(Debug, PartialEq, Eq)]
-struct RawDataElement<'d> {
-    de_type: DataElementType,
+pub(in crate::legacy) struct RawDataElement<'d, D: DataElementDeserializer> {
+    pub(in crate::legacy) de_type: D::DeTypeDisambiguator,
     /// Byte array payload of the data element, without the DE header.
-    contents: &'d [u8],
+    pub(in crate::legacy) contents: &'d [u8],
 }
 
-/// An advertisement broken down into data elements, but before decryption or mapping to higher
-/// level DE representations.
-#[derive(Debug, PartialEq, Eq)]
-enum RawAdvertisement<'d> {
-    Plaintext(PlaintextAdvContents<'d>),
-    Ciphertext(EncryptedAdvContents<'d>),
+impl<'d, D: DataElementDeserializer> RawDataElement<'d, D> {
+    /// Parse an individual DE into its header and contents.
+    fn parse(input: &'d [u8]) -> nom::IResult<&[u8], Self, DataElementDeserializeError> {
+        let (input, (de_type, actual_len)) = combinator::map_res(
+            combinator::map_opt(number::complete::u8, |de_header| {
+                // header: LLLLTTTT
+                let len = de_header >> 4;
+                let de_type_num = de_header & 0x0F;
+
+                // these can't fail since both inputs are 4 bits and will fit
+                DeTypeCode::try_from(de_type_num).ok().and_then(|de_type| {
+                    DeEncodedLength::try_from(len).ok().map(|encoded_len| (de_type, encoded_len))
+                })
+            }),
+            |(de_type, encoded_len)| {
+                D::map_encoded_len_to_actual_len(de_type, encoded_len).map_err(|e| match e {
+                    LengthError::InvalidLength => {
+                        DataElementDeserializeError::InvalidDeLength { de_type, len: encoded_len }
+                    }
+                    LengthError::InvalidType => {
+                        DataElementDeserializeError::InvalidDeType { de_type }
+                    }
+                })
+            },
+        )(input)?;
+
+        combinator::map(bytes::complete::take(actual_len.as_usize()), move |contents| {
+            RawDataElement { de_type, contents }
+        })(input)
+    }
 }
 
 /// An iterator that parses the given data elements iteratively. In environments
 /// where memory is not severely constrained, it is usually safer to collect
-/// this into `Result<Vec<PlainDataElement>>` so the validity of the whole
+/// this into `Result<Vec<DeserializedDataElement>>` so the validity of the whole
 /// advertisement can be checked before proceeding with further processing.
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub struct PlainDeIterator<'d, F>
-where
-    F: PacketFlavor,
-    actions::ActionsDataElement<F>: DataElement,
-{
-    /// Data to be parsed, containing a sequence of data elements in serialized
-    /// form. This should not contain the identity data elements.
-    data: &'d [u8],
-    _marker: PhantomData<F>,
+pub struct DeIterator<'d, F> {
+    delegate: GenericDeIterator<'d, F, StandardDeserializer>,
 }
 
-impl<'d, F> PlainDeIterator<'d, F>
-where
-    F: PacketFlavor,
-    actions::ActionsDataElement<F>: DataElement,
-{
-    fn raw_de_to_plain_de(
-        raw_de: RawDataElement<'d>,
-    ) -> Result<PlainDataElement<F>, DataElementDeserializeError> {
-        let de_type = raw_de
-            .de_type
-            .try_as_plain_de_type()
-            .ok_or(DataElementDeserializeError::DuplicateIdentityDataElement)?;
-        (RawPlainDataElement { de_type, contents: raw_de.contents }).try_deserialize()
+impl<'d, F> DeIterator<'d, F> {
+    pub(in crate::legacy) fn new(data: &'d [u8]) -> Self {
+        Self { delegate: GenericDeIterator::new(data) }
     }
 }
 
-impl<'d, F> Iterator for PlainDeIterator<'d, F>
-where
-    F: PacketFlavor,
-    actions::ActionsDataElement<F>: DataElement,
-{
-    type Item = Result<PlainDataElement<F>, DataElementDeserializeError>;
+impl<'d, F: PacketFlavor> Iterator for DeIterator<'d, F> {
+    type Item = Result<DeserializedDataElement<F>, DataElementDeserializeError>;
 
     fn next(&mut self) -> Option<Self::Item> {
-        let parse_result = nom::combinator::cut(nom::combinator::map_res(
-            parse_de,
-            Self::raw_de_to_plain_de,
+        self.delegate.next()
+    }
+}
+
+/// The generified innards of [DeIterator] so that it's possible to also use test-only
+/// deserializers.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub(in crate::legacy) struct GenericDeIterator<'d, F, D> {
+    /// Data to be parsed, containing a sequence of data elements in serialized
+    /// form.
+    data: &'d [u8],
+    _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() }
+    }
+}
+
+impl<'d, F: PacketFlavor, D: DataElementDeserializer> Iterator for GenericDeIterator<'d, F, D> {
+    type Item = Result<D::Deserialized<F>, DataElementDeserializeError>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.data.is_empty() {
+            return None;
+        }
+        let parse_result = combinator::cut(combinator::map_res(
+            RawDataElement::parse,
+            D::deserialize_de,
         ))(self.data);
-        match parse_result {
+
+        match parse_result.finish() {
             Ok((rem, de)) => {
                 self.data = rem;
                 Some(Ok(de))
             }
-            Err(nom::Err::Error(_)) => {
-                panic!("All Errors are turned into Failures with `cut` above");
-            }
-            Err(nom::Err::Failure(DataElementDeserializeError::NomError(
-                nom::error::ErrorKind::Eof,
-            ))) => {
-                if self.data.is_empty() {
-                    None
-                } else {
-                    Some(Err(DataElementDeserializeError::UnexpectedDataRemaining))
-                }
-            }
-            Err(nom::Err::Failure(e)) => Some(Err(e)),
-            Err(nom::Err::Incomplete(_)) => {
-                panic!("Incomplete unexpected when using nom::complete APIs")
-            }
+            Err(e) => Some(Err(e)),
         }
     }
 }
 
-/// A "plain" data element (not a container) without parsing the content.
-#[derive(Debug, PartialEq, Eq)]
-pub(crate) struct RawPlainDataElement<'d> {
-    de_type: PlainDataElementType,
-    /// Byte array payload of the data element, without the DE header.
-    contents: &'d [u8],
-}
-
-impl<'d> RawPlainDataElement<'d> {
-    /// Deserialize into a [PlainDataElement] to expose DE-type-specific data representations.
-    ///
-    /// Returns `None` if the contents of the raw DE can't be deserialized into the corresponding
-    /// DE's representation.
-    fn try_deserialize<F>(&self) -> Result<PlainDataElement<F>, DataElementDeserializeError>
-    where
-        F: PacketFlavor,
-        actions::ActionsDataElement<F>: DataElement,
-    {
-        match self.de_type {
-            PlainDataElementType::Actions => {
-                actions::ActionsDataElement::deserialize::<F>(self.contents)
-                    .map(PlainDataElement::Actions)
-            }
-            PlainDataElementType::TxPower => {
-                TxPowerDataElement::deserialize::<F>(self.contents).map(PlainDataElement::TxPower)
-            }
-        }
-    }
-}
-
-/// Contents of an encrypted advertisement before decryption.
-#[derive(Debug, PartialEq, Eq)]
-pub(crate) struct EncryptedAdvContents<'d> {
-    identity_type: EncryptedIdentityDataElementType,
-    /// Salt from the advertisement, converted into a padder.
-    /// Pre-calculated so it's only derived once across multiple decrypt attempts.
-    salt_padder: ldt::XorPadder<{ crypto_provider::aes::BLOCK_SIZE }>,
-    /// The salt instance used for encryption of this advertisement.
-    salt: LegacySalt,
-    /// Ciphertext containing the metadata key and any data elements
-    ciphertext: &'d [u8],
-}
-
-impl<'d> EncryptedAdvContents<'d> {
-    /// Try decrypting with an identity's LDT cipher and deserializing the resulting data elements.
-    ///
-    /// Returns the decrypted data if decryption and verification succeeded and the resulting DEs could be parsed
-    /// successfully, otherwise `Err`.
-    pub(crate) fn try_decrypt<C: CryptoProvider>(
-        &self,
-        cipher: &ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C>,
-    ) -> Result<DecryptedAdvContents, DecryptError> {
-        let plaintext = cipher
-            .decrypt_and_verify(self.ciphertext, &self.salt_padder)
-            .map_err(|_e| DecryptError::DecryptOrVerifyError)?;
-
-        // plaintext starts with 14 bytes of metadata key, then DEs.
-        let (remaining, metadata_key) = combinator::map_res(
-            bytes::complete::take(NP_LEGACY_METADATA_KEY_LEN),
-            |slice: &[u8]| slice.try_into(),
-        )(plaintext.as_slice())
-        .map_err(|_e: nom::Err<nom::error::Error<&[u8]>>| {
-            DecryptError::DeserializeError(AdvDeserializeError::AdvertisementDeserializeError)
-        })?;
-
-        let remaining_arr = ArrayView::try_from_slice(remaining)
-            .expect("Max remaining = 31 - 14 = 17 bytes < BLE_ADV_SVC_CONTENT_LEN");
-
-        Ok(DecryptedAdvContents::new(
-            self.identity_type,
-            ShortMetadataKey(metadata_key),
-            self.salt,
-            remaining_arr,
-        ))
-    }
-}
-
 /// Errors that can occur decrypting encrypted advertisements.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
 pub(crate) enum DecryptError {
     /// Decrypting or verifying the advertisement ciphertext failed
     DecryptOrVerifyError,
-    /// Decrypting succeeded, but deserializing from the plaintext failed
-    DeserializeError(AdvDeserializeError),
 }
 
-impl From<AdvDeserializeError> for DecryptError {
-    fn from(e: AdvDeserializeError) -> Self {
-        DecryptError::DeserializeError(e)
-    }
-}
-
-/// All v0 normal DE types with deserialized contents.
-#[derive(Debug, PartialEq, Eq)]
+/// All v0 DE types with deserialized contents.
+#[derive(Debug, PartialEq, Eq, Clone)]
 #[allow(missing_docs)]
-pub enum PlainDataElement<F: PacketFlavor> {
+pub enum DeserializedDataElement<F: PacketFlavor> {
     Actions(actions::ActionsDataElement<F>),
     TxPower(TxPowerDataElement),
 }
 
-impl<F: PacketFlavor> PlainDataElement<F> {
+impl<F: PacketFlavor> DeserializedDataElement<F> {
     /// Returns the DE type as a u8
     #[cfg(feature = "devtools")]
     pub fn de_type_code(&self) -> u8 {
         match self {
-            PlainDataElement::Actions(_) => DataElementType::Actions.type_code().as_u8(),
-            PlainDataElement::TxPower(_) => DataElementType::TxPower.type_code().as_u8(),
+            DeserializedDataElement::Actions(_) => ActionsDataElement::<F>::DE_TYPE_CODE.as_u8(),
+            DeserializedDataElement::TxPower(_) => TxPowerDataElement::DE_TYPE_CODE.as_u8(),
         }
     }
 
     /// Returns the serialized contents of the DE
     #[cfg(feature = "devtools")]
-    pub fn de_contents(&self) -> alloc::vec::Vec<u8> {
-        use crate::legacy::serialize::{DataElementBundle, ToDataElementBundle};
+    #[allow(clippy::unwrap_used)]
+    pub fn de_contents(&self) -> alloc::vec::Vec<u8>
+    where
+        actions::ActionsDataElement<F>: crate::legacy::data_elements::SerializeDataElement<F>,
+    {
+        use crate::legacy::data_elements::{DataElementSerializationBuffer, SerializeDataElement};
+
+        let mut sink = DataElementSerializationBuffer::new(super::NP_MAX_ADV_CONTENT_LEN).unwrap();
         match self {
-            PlainDataElement::Actions(a) => {
-                let bundle: DataElementBundle<F> = a.to_de_bundle();
-                bundle.contents_as_slice().to_vec()
-            }
-            PlainDataElement::TxPower(t) => {
-                let bundle: DataElementBundle<F> = t.to_de_bundle();
-                bundle.contents_as_slice().to_vec()
+            DeserializedDataElement::Actions(a) => a.serialize_contents(&mut sink),
+            DeserializedDataElement::TxPower(t) => {
+                SerializeDataElement::<F>::serialize_contents(t, &mut sink)
             }
         }
+        .unwrap();
+        sink.into_inner().into_inner().as_slice().to_vec()
     }
 }
 
-/// The contents of a plaintext advertisement after deserializing DE contents
-#[derive(Debug, PartialEq, Eq)]
-pub struct PlaintextAdvContents<'d> {
-    identity_type: PlaintextIdentityMode,
-    /// Contents of the advertisement excluding the identity DE
-    data: &'d [u8],
-}
-
-impl<'d> PlaintextAdvContents<'d> {
-    /// Returns the identity type used for the advertisement
-    pub fn identity(&self) -> PlaintextIdentityMode {
-        self.identity_type
-    }
-
-    /// Returns an iterator over the v0 data elements
-    pub fn data_elements(&self) -> PlainDeIterator<'d, Plaintext> {
-        PlainDeIterator { data: self.data, _marker: PhantomData }
-    }
-}
-
-/// Contents of an encrypted advertisement after decryption and deserializing DEs.
+/// Contents of an LDT advertisement after decryption.
 #[derive(Debug, PartialEq, Eq)]
 pub struct DecryptedAdvContents {
-    identity_type: EncryptedIdentityDataElementType,
-    metadata_key: ShortMetadataKey,
-    salt: LegacySalt,
-    /// The decrypted data in this advertisement. This should be a sequence of
-    /// serialized data elements, excluding the identity DE.
-    data: ArrayView<u8, { BLE_ADV_SVC_CONTENT_LEN }>,
+    identity_token: V0IdentityToken,
+    salt: V0Salt,
+    /// The decrypted data in this advertisement after the identity token.
+    /// This is hopefully a sequence of serialized data elements, but that hasn't been validated
+    /// yet at construction time.
+    data: ArrayView<u8, { NP_LDT_MAX_EFFECTIVE_PAYLOAD_LEN }>,
 }
 
 impl DecryptedAdvContents {
     /// Returns a new DecryptedAdvContents with the provided contents.
     fn new(
-        identity_type: EncryptedIdentityDataElementType,
-        metadata_key: ShortMetadataKey,
-        salt: LegacySalt,
-        data: ArrayView<u8, { BLE_ADV_SVC_CONTENT_LEN }>,
+        metadata_key: V0IdentityToken,
+        salt: V0Salt,
+        data: ArrayView<u8, { NP_LDT_MAX_EFFECTIVE_PAYLOAD_LEN }>,
     ) -> Self {
-        Self { identity_type, metadata_key, salt, data }
-    }
-
-    /// The type of identity DE used in the advertisement.
-    pub fn identity_type(&self) -> EncryptedIdentityDataElementType {
-        self.identity_type
+        Self { identity_token: metadata_key, salt, data }
     }
 
     /// Iterator over the data elements in an advertisement, except for any DEs related to resolving
     /// the identity or otherwise validating the payload (e.g. any identity DEs like Private
     /// Identity).
-    pub fn data_elements(&self) -> PlainDeIterator<Ciphertext> {
-        PlainDeIterator { data: self.data.as_slice(), _marker: PhantomData }
+    pub fn data_elements(&self) -> DeIterator<Ciphertext> {
+        DeIterator::new(self.data.as_slice())
     }
 
     /// The salt used for decryption of this advertisement.
-    pub fn salt(&self) -> LegacySalt {
+    pub fn salt(&self) -> V0Salt {
         self.salt
     }
+
+    #[cfg(test)]
+    pub(in crate::legacy) fn generic_data_elements<D: DataElementDeserializer>(
+        &self,
+    ) -> GenericDeIterator<Ciphertext, D> {
+        GenericDeIterator::new(self.data.as_slice())
+    }
 }
 
 impl HasIdentityMatch for DecryptedAdvContents {
     type Version = V0;
-    fn metadata_key(&self) -> ShortMetadataKey {
-        self.metadata_key
+    fn identity_token(&self) -> V0IdentityToken {
+        self.identity_token
     }
 }
 
-/// The contents of an advertisement after plaintext DEs, if any, have been deserialized, but
-/// before any decryption is done.
-#[derive(Debug, PartialEq, Eq)]
-pub(crate) enum IntermediateAdvContents<'d> {
-    /// Plaintext advertisements
-    Plaintext(PlaintextAdvContents<'d>),
-    /// Ciphertext advertisements
-    Ciphertext(EncryptedAdvContents<'d>),
+/// 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
+    type DeTypeDisambiguator: Copy;
+    /// The fully deserialized form of a DE
+    type Deserialized<F: PacketFlavor>: fmt::Debug + PartialEq + Eq + Clone;
+
+    /// Map the encoded len found in a DE header to the actual len that should be consumed from the
+    /// advertisement payload
+    fn map_encoded_len_to_actual_len(
+        de_type: DeTypeCode,
+        encoded_len: DeEncodedLength,
+    ) -> Result<(Self::DeTypeDisambiguator, DeActualLength), LengthError>;
+
+    /// Deserialize into a [Self::Deserialized] to expose DE-type-specific data representations.
+    ///
+    /// Returns `Err` if the contents of the raw DE can't be deserialized into the corresponding
+    /// DE's representation.
+    fn deserialize_de<F: PacketFlavor>(
+        raw_de: RawDataElement<Self>,
+    ) -> Result<Self::Deserialized<F>, DataElementDeserializeError>;
+}
+
+/// Possible errors when mapping DE encoded lengths to actual lengths
+pub(in crate::legacy) enum LengthError {
+    /// The DE type was known, but the encoded length was invalid
+    InvalidLength,
+    /// The DE type was not unrecognized
+    InvalidType,
+}
+
+impl From<DeLengthOutOfRange> for LengthError {
+    fn from(_value: DeLengthOutOfRange) -> Self {
+        Self::InvalidLength
+    }
+}
+
+/// The default deserialization strategy that maps type codes to [DataElementType], and deserializes
+/// to [DeserializedDataElement].
+#[derive(Debug, PartialEq, Eq, Clone)]
+struct StandardDeserializer;
+
+impl DataElementDeserializer for StandardDeserializer {
+    type DeTypeDisambiguator = DataElementType;
+    type Deserialized<F: PacketFlavor> = DeserializedDataElement<F>;
+
+    fn map_encoded_len_to_actual_len(
+        de_type: DeTypeCode,
+        encoded_len: DeEncodedLength,
+    ) -> Result<(Self::DeTypeDisambiguator, DeActualLength), LengthError> {
+        match de_type {
+            TxPowerDataElement::DE_TYPE_CODE => {
+                <TxPowerDataElement as DeserializeDataElement>::LengthMapper::map_encoded_len_to_actual_len(encoded_len)
+                    .map_err(|e| e.into())
+                    .map(|l| (DataElementType::TxPower, l))
+            }
+            ActionsDataElement::<Plaintext>::DE_TYPE_CODE => {
+                <ActionsDataElement<Plaintext> as DeserializeDataElement>::LengthMapper::map_encoded_len_to_actual_len(encoded_len)
+                    .map_err(|e| e.into())
+                    .map(|l| (DataElementType::Actions, l))
+            }
+            _ => Err(LengthError::InvalidType),
+        }
+    }
+
+    fn deserialize_de<F: PacketFlavor>(
+        raw_de: RawDataElement<Self>,
+    ) -> Result<Self::Deserialized<F>, DataElementDeserializeError> {
+        match raw_de.de_type {
+            DataElementType::Actions => {
+                actions::ActionsDataElement::deserialize::<F>(raw_de.contents)
+                    .map(DeserializedDataElement::Actions)
+            }
+            DataElementType::TxPower => TxPowerDataElement::deserialize::<F>(raw_de.contents)
+                .map(DeserializedDataElement::TxPower),
+        }
+    }
 }
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/tests.rs b/nearby/presence/np_adv/src/legacy/deserialize/tests.rs
deleted file mode 100644
index 3dfecd1..0000000
--- a/nearby/presence/np_adv/src/legacy/deserialize/tests.rs
+++ /dev/null
@@ -1,818 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#![allow(unused_results, clippy::unwrap_used)]
-
-extern crate alloc;
-extern crate std;
-
-use super::*;
-use crate::{
-    credential::{v0::V0, SimpleBroadcastCryptoMaterial},
-    de_type::IdentityDataElementType,
-    legacy::{
-        actions::{self, ActionBits, ActionsDataElement, Finder, NearbyShare},
-        de_type::DeActualLength,
-        random_data_elements::{random_de_ciphertext, random_de_plaintext},
-        serialize::{
-            encode_de_header_actual_len, id_de_type_as_generic_de_type, AdvBuilder,
-            DataElementBundle, Identity, LdtIdentity, ToDataElementBundle,
-        },
-        PacketFlavorEnum, BLE_ADV_SVC_CONTENT_LEN,
-    },
-    parse_adv_header, shared_data,
-    shared_data::TxPower,
-    AdvHeader, PublicIdentity,
-};
-use alloc::vec::Vec;
-use array_view::ArrayView;
-use crypto_provider_default::CryptoProviderImpl;
-use ldt_np_adv::LdtEncrypterXtsAes128;
-use nom::error::{self, ErrorKind};
-use rand_ext::rand::{prelude::SliceRandom, Rng as _};
-use std::vec;
-use strum::IntoEnumIterator as _;
-
-#[test]
-fn parse_empty_raw_adv() {
-    let adv_data = parse_raw_adv_contents::<CryptoProviderImpl>(&[]);
-    assert_eq!(AdvDeserializeError::MissingIdentity, adv_data.unwrap_err());
-}
-
-#[test]
-fn parse_raw_adv_1_de_long_private_identity() {
-    // 3 bytes of payload after metadata key
-    let adv = parse_raw_adv_contents::<CryptoProviderImpl>(&[
-        0x31, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
-        0x0F, 0x10, 0x11, 0x12, 0x13,
-    ])
-    .unwrap();
-    assert_eq!(
-        RawAdvertisement::Ciphertext(EncryptedAdvContents {
-            identity_type: EncryptedIdentityDataElementType::Private,
-            salt_padder: ldt_np_adv::salt_padder::<16, CryptoProviderImpl>(
-                ldt_np_adv::LegacySalt::from([0x01, 0x02])
-            ),
-            salt: ldt_np_adv::LegacySalt::from([0x01, 0x02]),
-            ciphertext: &[
-                0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
-                0x11, 0x12, 0x13
-            ]
-        }),
-        adv
-    );
-}
-
-#[test]
-fn parse_raw_adv_3_de_public_identity() {
-    let adv = parse_raw_adv_contents::<CryptoProviderImpl>(&[
-        0x03, // public identity
-        0x15, 0x05, // tx power 5
-        0x26, 0x00, 0x44, // actions
-    ])
-    .unwrap();
-    match adv {
-        RawAdvertisement::Plaintext(plaintext) => {
-            assert_eq!(PlaintextIdentityMode::Public, plaintext.identity_type);
-            let mut action_bits = ActionBits::default();
-            action_bits.set_action(NearbyShare::from(true));
-            action_bits.set_action(Finder::from(true));
-            assert_eq!(
-                vec![
-                    PlainDataElement::<Plaintext>::TxPower(TxPowerDataElement::from(
-                        TxPower::try_from(5).unwrap()
-                    )),
-                    PlainDataElement::Actions(ActionsDataElement::from(action_bits)),
-                ],
-                plaintext.data_elements().collect::<Result<Vec<_>, _>>().unwrap(),
-            );
-        }
-        RawAdvertisement::Ciphertext(_) => panic!("adv should be plaintext"),
-    }
-}
-
-#[test]
-fn parse_raw_adv_0_de_public_identity() {
-    let adv = parse_raw_adv_contents::<CryptoProviderImpl>(&[
-        0x03, // public identity
-    ])
-    .unwrap();
-    match adv {
-        RawAdvertisement::Plaintext(plaintext) => {
-            assert_eq!(PlaintextIdentityMode::Public, plaintext.identity_type);
-            assert_eq!(
-                Vec::<PlainDataElement<Plaintext>>::new(),
-                plaintext.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
-            );
-        }
-        RawAdvertisement::Ciphertext(_) => panic!("adv should be plaintext"),
-    }
-}
-
-#[test]
-fn parse_raw_adv_1_de_length_overrun() {
-    // battery uses the header length as is
-    let input = &[0xFB, 0x01, 0x02, 0x03];
-    assert_eq!(
-        nom::Err::Error(DataElementDeserializeError::NomError(ErrorKind::MapOpt)),
-        parse_de(input).unwrap_err(),
-    );
-}
-
-#[test]
-fn parse_raw_adv_public_identity_containing_public_identity() {
-    let input = &[
-        0x03, // public identity
-        0x03, // another public identity
-        0x15, 0x03, // tx power de
-    ];
-    match parse_raw_adv_contents::<CryptoProviderImpl>(input).unwrap() {
-        RawAdvertisement::Plaintext(content) => {
-            assert_eq!(
-                DataElementDeserializeError::DuplicateIdentityDataElement,
-                content.data_elements().collect::<Result<Vec<_>, _>>().unwrap_err(),
-            );
-        }
-        RawAdvertisement::Ciphertext(_) => panic!("Adv should be plaintext"),
-    }
-}
-
-#[test]
-fn parse_raw_adv_no_identity_containing_public_identity() {
-    let input = &[
-        0x15, 0x03, // tx power de
-        0x03, // public identity -- since it's not the first, this is a no identity adv
-        0x15, 0x03, // tx power de
-    ];
-    assert_eq!(
-        AdvDeserializeError::MissingIdentity,
-        parse_raw_adv_contents::<CryptoProviderImpl>(input).unwrap_err()
-    );
-}
-
-#[test]
-fn parse_raw_adv_1_de_long_private_identity_with_another_top_level_de() {
-    let input = &[
-        0x31, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
-        0x0F, 0x10, 0x11, 0x12, 0x13, // private identity
-        0x15, 0x03, // tx power de
-    ];
-    assert_eq!(
-        AdvDeserializeError::TooManyTopLevelDataElements,
-        parse_raw_adv_contents::<CryptoProviderImpl>(input).unwrap_err()
-    );
-}
-
-#[test]
-fn parse_raw_adv_private_identity_ciphertext_min_length() {
-    let short_input = &[
-        // private identity w/ salt, len = 1
-        0x11, 0x10, 0x11, // 15 ciphertext bytes, + 2 salt = 17 total
-        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E,
-    ];
-    assert_eq!(
-        AdvDeserializeError::AdvertisementDeserializeError,
-        parse_raw_adv_contents::<CryptoProviderImpl>(short_input).unwrap_err()
-    );
-
-    let ok_input = &[
-        // private identity w/ salt
-        0x21, 0x10, 0x11, // 16 ciphertext bytes, 18 bytes total de len, encoded len 2
-        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E,
-        0x2F,
-    ];
-    assert_eq!(
-        RawAdvertisement::Ciphertext(EncryptedAdvContents {
-            identity_type: EncryptedIdentityDataElementType::Private,
-            salt: [0x10, 0x11].into(),
-            salt_padder: ldt_np_adv::salt_padder::<16, CryptoProviderImpl>([0x10, 0x11].into()),
-            ciphertext: &ok_input[3..]
-        }),
-        parse_raw_adv_contents::<CryptoProviderImpl>(ok_input).unwrap()
-    );
-}
-
-#[test]
-fn parse_raw_adv_private_identity_ciphertext_too_long() {
-    let long_input = &[
-        // private identity w/ salt, len = 7
-        0x71, 0x10, 0x11, // 21 ciphertext bytes, + 2 salt
-        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E,
-        0x2F, 0x30, 0x31, 0x32, 0x33, 0x34,
-    ];
-    assert_eq!(
-        AdvDeserializeError::AdvertisementDeserializeError,
-        parse_raw_adv_contents::<CryptoProviderImpl>(long_input).unwrap_err()
-    );
-
-    // removing 1 byte makes it work
-    let ok_input = &[
-        // private identity w/ salt, len = 6
-        0x61, 0x10, 0x11, // 20 ciphertext bytes, + 2 salt
-        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E,
-        0x2F, 0x30, 0x31, 0x32, 0x33,
-    ];
-    assert_eq!(
-        RawAdvertisement::Ciphertext(EncryptedAdvContents {
-            identity_type: EncryptedIdentityDataElementType::Private,
-            salt_padder: ldt_np_adv::salt_padder::<16, CryptoProviderImpl>([0x10, 0x11].into()),
-            salt: [0x10, 0x11].into(),
-            ciphertext: &ok_input[3..]
-        }),
-        parse_raw_adv_contents::<CryptoProviderImpl>(ok_input).unwrap()
-    );
-}
-
-/// Test method body that creates an array, deserializes it into a DE, serializes it,
-/// and asserts that the same bytes are produced.
-///
-/// Evaluates to the deserialized DE.
-macro_rules! de_roundtrip_test {
-    ($de_type:ty, $type_variant:ident, $de_variant:ident, $flavor:ty,  $bytes:expr) => {{
-        let parsed_de_enum = parse_plain_de::<$flavor>(PlainDataElementType::$type_variant, $bytes);
-        if let PlainDataElement::$de_variant(de) = parsed_de_enum {
-            let expected = <$de_type>::deserialize::<$flavor>($bytes).unwrap();
-            assert_eq!(expected, de);
-
-            let de_bundle: DataElementBundle<$flavor> = de.to_de_bundle();
-            assert_eq!($bytes, de_bundle.contents_as_slice());
-
-            de
-        } else {
-            panic!("Unexpected variant: {:?}", parsed_de_enum);
-        }
-    }};
-}
-
-#[test]
-fn actions_de_contents_roundtrip_plaintext() {
-    let actions = actions::tests::all_plaintext_actions();
-    let bundle = actions::ActionsDataElement::from(actions).to_de_bundle();
-
-    de_roundtrip_test!(
-        actions::ActionsDataElement::<Plaintext>,
-        Actions,
-        Actions,
-        Plaintext,
-        bundle.contents_as_slice()
-    );
-}
-
-#[test]
-fn actions_de_contents_roundtrip_ciphertext() {
-    let actions = actions::tests::all_ciphertext_actions();
-    let bundle = actions::ActionsDataElement::from(actions).to_de_bundle();
-
-    de_roundtrip_test!(
-        actions::ActionsDataElement::<Ciphertext>,
-        Actions,
-        Actions,
-        Ciphertext,
-        bundle.contents_as_slice()
-    );
-}
-
-#[test]
-fn tx_power_de_contents_roundtrip_plaintext() {
-    let tx = shared_data::TxPower::try_from(-10).unwrap();
-    let bundle: DataElementBundle<Plaintext> = TxPowerDataElement::from(tx).to_de_bundle();
-
-    de_roundtrip_test!(TxPowerDataElement, TxPower, TxPower, Plaintext, bundle.contents_as_slice());
-}
-
-#[test]
-fn tx_power_de_contents_roundtrip_ciphertext() {
-    let tx = shared_data::TxPower::try_from(-10).unwrap();
-    let bundle: DataElementBundle<Ciphertext> = TxPowerDataElement::from(tx).to_de_bundle();
-
-    de_roundtrip_test!(
-        TxPowerDataElement,
-        TxPower,
-        TxPower,
-        Ciphertext,
-        bundle.contents_as_slice()
-    );
-}
-
-#[test]
-fn parse_de_invalid_de_len_error() {
-    let input = &[
-        // bogus 6-byte battery de -- only allows length = 3
-        0x6B, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
-    ];
-
-    assert_eq!(
-        nom::Err::Error(DataElementDeserializeError::NomError(ErrorKind::MapOpt)),
-        parse_de(&input[..]).unwrap_err()
-    );
-}
-
-#[test]
-fn raw_de_to_plain_de_matches_plain_des() {
-    assert_eq!(
-        PlainDataElement::TxPower(TxPowerDataElement::from(TxPower::try_from(1).unwrap())),
-        PlainDeIterator::<Plaintext>::raw_de_to_plain_de(RawDataElement {
-            de_type: DataElementType::TxPower,
-            contents: &[0x01]
-        })
-        .unwrap(),
-    );
-    assert_eq!(
-        PlainDataElement::Actions(ActionsDataElement::from(
-            ActionBits::try_from(0x00400000).unwrap()
-        )),
-        PlainDeIterator::<Plaintext>::raw_de_to_plain_de(RawDataElement {
-            de_type: DataElementType::Actions,
-            contents: &[0x00, 0x40]
-        })
-        .unwrap(),
-    );
-}
-
-#[test]
-fn raw_de_to_plain_de_rejects_identity_de_error() {
-    for idet in IdentityDataElementType::iter() {
-        assert_eq!(
-            DataElementDeserializeError::DuplicateIdentityDataElement,
-            PlainDeIterator::<Plaintext>::raw_de_to_plain_de(RawDataElement {
-                de_type: id_de_type_as_generic_de_type(idet),
-                contents: &[0x02],
-            })
-            .unwrap_err()
-        );
-    }
-}
-
-#[test]
-fn parse_encrypted_identity_contents_too_short_error() {
-    // 2 byte salt + 15 byte ciphertext: 1 too short
-    let mut input = [0u8; 17];
-    for (pos, e) in input.iter_mut().enumerate() {
-        *e = pos as u8
-    }
-    assert_eq!(
-        nom::Err::Error(error::Error { input: &input[2..], code: error::ErrorKind::TakeWhileMN }),
-        parse_encrypted_identity_de_contents(&input).unwrap_err()
-    );
-}
-
-#[test]
-fn parse_encrypted_identity_contents_ok() {
-    // 2 byte salt + minimum 16 byte ciphertext
-    let mut input = [0u8; 18];
-    for (pos, e) in input.iter_mut().enumerate() {
-        *e = pos as u8
-    }
-    assert_eq!(
-        ([].as_slice(), (ldt_np_adv::LegacySalt::from([0, 1]), &input[2..])),
-        parse_encrypted_identity_de_contents(&input).unwrap()
-    );
-}
-
-#[test]
-fn deserialize_adv_public_identity_empty_des() {
-    let input = &[
-        0x03, // public identity
-    ];
-    assert_eq!(
-        AdvDeserializeError::NoPublicDataElements,
-        deserialize_adv_contents::<CryptoProviderImpl>(input).unwrap_err()
-    );
-}
-
-#[test]
-fn plaintext_random_adv_contents_round_trip_public() {
-    plaintext_random_adv_contents_round_trip(PublicIdentity::default, PlaintextIdentityMode::Public)
-}
-
-#[test]
-fn ciphertext_random_adv_contents_round_trip() {
-    let mut rng = rand_ext::seeded_rng();
-    let de_types: Vec<PlainDataElementType> = PlainDataElementType::iter()
-        .filter(|t| t.supports_flavor(PacketFlavorEnum::Ciphertext))
-        .collect();
-    let identity_de_types: Vec<EncryptedIdentityDataElementType> =
-        EncryptedIdentityDataElementType::iter().collect();
-
-    for _ in 0..10_000 {
-        let mut des = Vec::new();
-
-        let identity_type = *identity_de_types.choose(&mut rng).unwrap();
-        let key_seed: [u8; 32] = rng.gen();
-        let salt: ldt_np_adv::LegacySalt = rng.gen::<[u8; 2]>().into();
-        let metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN] = rng.gen();
-        let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-        let metadata_key_hmac: [u8; 32] =
-            hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&metadata_key);
-        let cipher = ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, metadata_key_hmac);
-
-        let metadata_key = ShortMetadataKey(metadata_key);
-        let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, metadata_key);
-
-        let mut builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
-            identity_type,
-            salt,
-            &broadcast_cm,
-        ));
-
-        loop {
-            let de_type = *de_types.choose(&mut rng).unwrap();
-            let (de, bundle) = random_de_ciphertext(de_type, &mut rng).unwrap();
-
-            if builder.add_data_element(bundle.clone()).ok().is_none() {
-                // out of room
-                if des.is_empty() {
-                    // need at least one, so try again
-                    continue;
-                } else {
-                    // there's at least one so proceed to serialization
-                    break;
-                }
-            }
-
-            des.push(de);
-        }
-
-        let serialized = builder.into_advertisement().unwrap();
-        let (remaining, header) = parse_adv_header(serialized.as_slice()).unwrap();
-        let parsed_adv = deserialize_adv_contents::<CryptoProviderImpl>(remaining).unwrap();
-
-        assert_eq!(AdvHeader::V0, header);
-        if let IntermediateAdvContents::Ciphertext(eac) = parsed_adv {
-            assert_eq!(
-                EncryptedAdvContents {
-                    identity_type,
-                    salt_padder: ldt_np_adv::salt_padder::<16, CryptoProviderImpl>(salt),
-                    salt,
-                    // skip adv header, de header, salt
-                    ciphertext: &serialized.as_slice()[4..]
-                },
-                eac
-            );
-
-            let contents = eac.try_decrypt(&cipher).unwrap();
-            assert_eq!(identity_type, contents.identity_type);
-            assert_eq!(metadata_key, contents.metadata_key);
-            assert_eq!(salt, contents.salt);
-            assert_eq!(des, contents.data_elements().collect::<Result<Vec<_>, _>>().unwrap());
-        } else {
-            panic!("Unexpected variant: {:?}", parsed_adv);
-        }
-    }
-}
-
-#[test]
-fn decrypt_and_deserialize_ciphertext_adv_canned() {
-    let key_seed = [0x11_u8; 32];
-    let salt: ldt_np_adv::LegacySalt = [0x22; 2].into();
-    let metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN] = [0x33; NP_LEGACY_METADATA_KEY_LEN];
-
-    let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-    let metadata_key_hmac: [u8; 32] =
-        hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&metadata_key);
-    let cipher = ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, metadata_key_hmac);
-
-    let metadata_key = ShortMetadataKey(metadata_key);
-
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, metadata_key);
-
-    let mut builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
-        EncryptedIdentityDataElementType::Private,
-        salt,
-        &broadcast_cm,
-    ));
-
-    let tx = shared_data::TxPower::try_from(3).unwrap();
-    builder.add_data_element(TxPowerDataElement::from(tx)).unwrap();
-
-    let serialized = builder.into_advertisement().unwrap();
-
-    assert_eq!(
-        &[
-            0x0,  // adv header
-            0x21, // private DE
-            0x22, 0x22, // salt
-            // ciphertext
-            0x85, 0xBF, 0xA8, 0x83, 0x58, 0x7C, 0x50, 0xCF, 0x98, 0x38, 0xA7, 0x8A, 0xC0, 0x1C,
-            0x96, 0xF9
-        ],
-        serialized.as_slice()
-    );
-
-    let (remaining, header) = parse_adv_header(serialized.as_slice()).unwrap();
-    assert_eq!(AdvHeader::V0, header);
-
-    let parsed_adv = deserialize_adv_contents::<CryptoProviderImpl>(remaining).unwrap();
-    if let IntermediateAdvContents::Ciphertext(eac) = parsed_adv {
-        assert_eq!(
-            EncryptedAdvContents {
-                identity_type: EncryptedIdentityDataElementType::Private,
-                salt_padder: ldt_np_adv::salt_padder::<16, CryptoProviderImpl>(salt),
-                salt,
-                // skip adv header, de header, salt
-                ciphertext: &serialized.as_slice()[4..]
-            },
-            eac
-        );
-
-        let decrypted = eac.try_decrypt(&cipher).unwrap();
-        assert_eq!(EncryptedIdentityDataElementType::Private, decrypted.identity_type);
-        assert_eq!(metadata_key, decrypted.metadata_key);
-        assert_eq!(salt, decrypted.salt);
-        assert_eq!(
-            vec![PlainDataElement::TxPower(TxPowerDataElement::from(
-                TxPower::try_from(3).unwrap()
-            ))],
-            decrypted.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
-        );
-    } else {
-        panic!("Unexpected variant: {:?}", parsed_adv);
-    }
-}
-
-#[test]
-fn decrypt_and_deserialize_plaintext_adv_canned() {
-    let mut builder = AdvBuilder::new(PublicIdentity);
-
-    let actions = ActionBits::default();
-    builder.add_data_element(ActionsDataElement::from(actions)).unwrap();
-
-    let serialized = builder.into_advertisement().unwrap();
-
-    assert_eq!(
-        &[
-            0x0,  // adv header
-            0x03, // public DE
-            0x16, 0x00 // actions
-        ],
-        serialized.as_slice()
-    );
-
-    let (remaining, header) = parse_adv_header(serialized.as_slice()).unwrap();
-    assert_eq!(AdvHeader::V0, header);
-
-    let parsed_adv = deserialize_adv_contents::<CryptoProviderImpl>(remaining).unwrap();
-    if let IntermediateAdvContents::Plaintext(adv_contents) = parsed_adv {
-        assert_eq!(PlaintextIdentityMode::Public, adv_contents.identity());
-        assert_eq!(
-            vec![PlainDataElement::<Plaintext>::Actions(ActionsDataElement::from(
-                ActionBits::default()
-            ))],
-            adv_contents.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
-        );
-    } else {
-        panic!("Unexpected variant: {:?}", parsed_adv);
-    }
-}
-
-#[test]
-fn decrypt_with_wrong_key_seed_error() {
-    let salt = ldt_np_adv::LegacySalt::from([0x22; 2]);
-    let metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN] = [0x33; NP_LEGACY_METADATA_KEY_LEN];
-    let correct_key_seed = [0x11_u8; 32];
-
-    let (adv_content, correct_cipher) =
-        build_ciphertext_adv_contents(salt, &metadata_key, correct_key_seed);
-    let eac = parse_ciphertext_adv_contents(&correct_cipher, &adv_content.as_slice()[1..]);
-
-    // wrong key seed doesn't work (derives wrong ldt key, wrong hmac key)
-    let wrong_key_seed_cipher = {
-        let key_seed = [0x22_u8; 32];
-
-        let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-        let metadata_key_hmac: [u8; 32] =
-            hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&metadata_key);
-        ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, metadata_key_hmac)
-    };
-
-    assert_eq!(
-        DecryptError::DecryptOrVerifyError,
-        eac.try_decrypt(&wrong_key_seed_cipher,).unwrap_err()
-    );
-}
-
-#[test]
-fn decrypt_with_wrong_hmac_key_error() {
-    let salt = ldt_np_adv::LegacySalt::from([0x22; 2]);
-    let metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN] = [0x33; NP_LEGACY_METADATA_KEY_LEN];
-    let correct_key_seed = [0x11_u8; 32];
-
-    let (adv_content, correct_cipher_config) =
-        build_ciphertext_adv_contents(salt, &metadata_key, correct_key_seed);
-    let eac = parse_ciphertext_adv_contents(&correct_cipher_config, &adv_content.as_slice()[1..]);
-
-    let wrong_hmac_key_cipher = {
-        let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&[0x10_u8; 32]);
-        let metadata_key_hmac: [u8; 32] =
-            hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&metadata_key);
-
-        ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, metadata_key_hmac)
-    };
-
-    assert_eq!(
-        DecryptError::DecryptOrVerifyError,
-        eac.try_decrypt::<CryptoProviderImpl>(&wrong_hmac_key_cipher,).unwrap_err()
-    );
-}
-
-#[test]
-fn decrypt_with_wrong_hmac_error() {
-    let salt = ldt_np_adv::LegacySalt::from([0x22; 2]);
-    let metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN] = [0x33; NP_LEGACY_METADATA_KEY_LEN];
-    let correct_key_seed = [0x11_u8; 32];
-
-    let (adv_content, correct_cipher_config) =
-        build_ciphertext_adv_contents(salt, &metadata_key, correct_key_seed);
-    let eac = parse_ciphertext_adv_contents(&correct_cipher_config, &adv_content.as_slice()[1..]);
-
-    let wrong_hmac_key_cipher = {
-        let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&correct_key_seed);
-
-        ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, [0x77; 32])
-    };
-
-    assert_eq!(
-        DecryptError::DecryptOrVerifyError,
-        eac.try_decrypt(&wrong_hmac_key_cipher,).unwrap_err()
-    );
-}
-
-#[test]
-fn decrypt_and_deserialize_ciphertext_with_public_adv_inside_error() {
-    let key_seed = [0x11_u8; 32];
-    let salt: ldt_np_adv::LegacySalt = [0x22; 2].into();
-    let metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN] = [0x33; NP_LEGACY_METADATA_KEY_LEN];
-
-    let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-    let ldt_key = hkdf.legacy_ldt_key();
-    let metadata_key_hmac: [u8; 32] =
-        hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&metadata_key);
-    let cipher = ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, metadata_key_hmac);
-
-    let mut plaintext = vec![];
-    plaintext.extend_from_slice(&metadata_key);
-
-    let txpower_de = TxPowerDataElement::from(TxPower::try_from(5).unwrap());
-
-    plaintext.push(
-        encode_de_header_actual_len(DataElementType::TxPower, 1u8.try_into().unwrap()).unwrap(),
-    );
-
-    let plaintext_de_bundle: DataElementBundle<Plaintext> = txpower_de.to_de_bundle();
-    plaintext.extend_from_slice(plaintext_de_bundle.contents_as_slice());
-    // forge an otherwise impossible to express public identity
-    plaintext.push(
-        encode_de_header_actual_len(DataElementType::PublicIdentity, DeActualLength::ZERO).unwrap(),
-    );
-
-    assert_eq!(17, plaintext.len());
-
-    let ldt = LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(&ldt_key);
-    ldt.encrypt(&mut plaintext, &ldt_np_adv::salt_padder::<16, CryptoProviderImpl>(salt)).unwrap();
-    let ciphertext = plaintext;
-
-    let mut adv = vec![];
-    adv.push(0x00); // adv header
-    adv.push(
-        encode_de_header_actual_len(
-            DataElementType::PrivateIdentity,
-            (2 + ciphertext.len()).try_into().unwrap(),
-        )
-        .unwrap(),
-    ); // private DE
-    adv.extend_from_slice(&[0x22; 2]); // salt
-    adv.extend_from_slice(&ciphertext);
-
-    let parsed_adv = deserialize_adv_contents::<CryptoProviderImpl>(&adv[1..]).unwrap();
-    if let IntermediateAdvContents::Ciphertext(eac) = parsed_adv {
-        assert_eq!(
-            DataElementDeserializeError::DuplicateIdentityDataElement,
-            eac.try_decrypt(&cipher)
-                .unwrap()
-                .data_elements()
-                .collect::<Result<Vec<_>, _>>()
-                .unwrap_err()
-        )
-    } else {
-        panic!("Unexpected variant: {:?}", parsed_adv);
-    }
-}
-
-fn build_ciphertext_adv_contents<C: CryptoProvider>(
-    salt: ldt_np_adv::LegacySalt,
-    metadata_key: &[u8; 14],
-    correct_key_seed: [u8; 32],
-) -> (ArrayView<u8, { BLE_ADV_SVC_CONTENT_LEN }>, ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C>) {
-    let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&correct_key_seed);
-
-    let metadata_key_hmac: [u8; 32] =
-        hkdf.legacy_metadata_key_hmac_key().calculate_hmac(metadata_key.as_slice());
-
-    let correct_cipher = ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, metadata_key_hmac);
-
-    let broadcast_cm =
-        SimpleBroadcastCryptoMaterial::<V0>::new(correct_key_seed, ShortMetadataKey(*metadata_key));
-
-    let mut builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
-        EncryptedIdentityDataElementType::Private,
-        salt,
-        &broadcast_cm,
-    ));
-    builder.add_data_element(TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
-    (builder.into_advertisement().unwrap(), correct_cipher)
-}
-
-fn parse_ciphertext_adv_contents<'b>(
-    cipher: &ldt_np_adv::LdtNpAdvDecrypterXtsAes128<CryptoProviderImpl>,
-    adv_content: &'b [u8],
-) -> EncryptedAdvContents<'b> {
-    let eac = match deserialize_adv_contents::<CryptoProviderImpl>(adv_content).unwrap() {
-        IntermediateAdvContents::Plaintext(_) => panic!(),
-        // quick confirmation that we did get something
-        IntermediateAdvContents::Ciphertext(eac) => eac,
-    };
-
-    // correct cipher works
-    assert!(eac.try_decrypt(cipher).is_ok());
-
-    eac
-}
-
-/// Construct the serialized DE and parse it
-fn parse_plain_de<F>(de_type: PlainDataElementType, contents: &[u8]) -> PlainDataElement<F>
-where
-    F: PacketFlavor,
-    actions::ActionsDataElement<F>: DataElement,
-{
-    let mut buf = vec![];
-    buf.push(
-        encode_de_header_actual_len(
-            de_type.as_generic_de_type(),
-            contents.len().try_into().unwrap(),
-        )
-        .unwrap(),
-    );
-    buf.extend_from_slice(contents);
-
-    let mut plain_des = PlainDeIterator { data: &buf, _marker: PhantomData }
-        .collect::<Result<Vec<_>, _>>()
-        .unwrap();
-    assert_eq!(1, plain_des.len());
-    plain_des.swap_remove(0)
-}
-
-fn plaintext_random_adv_contents_round_trip<I: Identity<Flavor = Plaintext>, F: Fn() -> I>(
-    mk_identity: F,
-    identity_type: PlaintextIdentityMode,
-) {
-    let mut rng = rand_ext::seeded_rng();
-    let de_types: Vec<PlainDataElementType> = PlainDataElementType::iter()
-        .filter(|t| t.supports_flavor(PacketFlavorEnum::Plaintext))
-        .collect();
-
-    for _ in 0..10_000 {
-        let mut de_tuples = Vec::new();
-        let mut builder = AdvBuilder::new(mk_identity());
-
-        loop {
-            let de_type = *de_types.choose(&mut rng).unwrap();
-            let (de, bundle) = random_de_plaintext(de_type, &mut rng).unwrap();
-
-            if builder.add_data_element(bundle.clone()).ok().is_none() {
-                // out of room
-                break;
-            }
-
-            de_tuples.push((de, de_type, bundle));
-        }
-
-        let serialized = builder.into_advertisement().unwrap();
-
-        let (_rem, header) =
-            combinator::all_consuming(parse_adv_header)(&serialized.as_slice()[..1]).unwrap();
-        let parsed_adv =
-            parse_raw_adv_contents::<CryptoProviderImpl>(&serialized.as_slice()[1..]).unwrap();
-
-        assert_eq!(AdvHeader::V0, header);
-        if let RawAdvertisement::Plaintext(adv_contents) = parsed_adv {
-            assert_eq!(identity_type, adv_contents.identity_type);
-            assert_eq!(
-                de_tuples.into_iter().map(|(de, _de_type, _bundle)| de).collect::<Vec<_>>(),
-                adv_contents.data_elements().collect::<Result<Vec<_>, _>>().unwrap(),
-            );
-        } else {
-            panic!("Unexpected variant: {:?}", parsed_adv);
-        }
-    }
-}
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
new file mode 100644
index 0000000..8230548
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/deserialize/tests/error_conditions.rs
@@ -0,0 +1,321 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+mod unencrypted {
+    use crypto_provider_default::CryptoProviderImpl;
+
+    use crate::header::V0Encoding;
+    use crate::legacy::data_elements::actions::{ActionBits, ActionsDataElement, ActiveUnlock};
+    use crate::legacy::data_elements::de_type::{DeEncodedLength, DeTypeCode};
+    use crate::legacy::data_elements::tx_power::TxPowerDataElement;
+    use crate::legacy::data_elements::{DataElementDeserializeError, DeserializeDataElement};
+    use crate::legacy::deserialize::intermediate::IntermediateAdvContents;
+    use crate::legacy::serialize::tests::serialize;
+    use crate::legacy::{Ciphertext, PacketFlavorEnum, Plaintext};
+
+    #[test]
+    fn iterate_tx_power_invalid_de_len_error() {
+        assert_deser_error(
+            // bogus 6-byte tx power de -- only allows length = 1
+            &[0x65, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06],
+            DataElementDeserializeError::InvalidDeLength {
+                de_type: TxPowerDataElement::DE_TYPE_CODE,
+                len: DeEncodedLength::from(6),
+            },
+        );
+    }
+
+    #[test]
+    fn iterate_tx_power_invalid_power_error() {
+        assert_deser_error(
+            // power too high
+            &[0x15, 0x7F],
+            DataElementDeserializeError::DeserializeError {
+                de_type: TxPowerDataElement::DE_TYPE_CODE,
+            },
+        );
+    }
+
+    #[test]
+    fn iterate_actions_invalid_de_len_error() {
+        assert_deser_error(
+            // bogus 0-byte actions de
+            &[0x06],
+            DataElementDeserializeError::InvalidDeLength {
+                de_type: ActionsDataElement::<Plaintext>::DE_TYPE_CODE,
+                len: DeEncodedLength::from(0),
+            },
+        );
+    }
+
+    #[test]
+    fn iterate_actions_ciphertext_only_bit_error() {
+        let mut bits = ActionBits::default();
+        bits.set_action(ActiveUnlock::from(true));
+        assert_deser_error(
+            serialize(&ActionsDataElement::<Ciphertext>::from(bits)).as_slice(),
+            DataElementDeserializeError::FlavorNotSupported {
+                de_type: ActionsDataElement::<Plaintext>::DE_TYPE_CODE,
+                flavor: PacketFlavorEnum::Plaintext,
+            },
+        );
+    }
+
+    #[test]
+    fn iterate_invalid_de_type_error() {
+        assert_deser_error(
+            &[0x0F],
+            DataElementDeserializeError::InvalidDeType {
+                de_type: DeTypeCode::try_from(0x0F).unwrap(),
+            },
+        );
+    }
+
+    #[test]
+    fn iterate_truncated_contents_error() {
+        assert_deser_error(
+            // length 3, but only 2 bytes provided
+            &[0x36, 0x01, 0x02],
+            DataElementDeserializeError::InvalidStructure,
+        );
+    }
+
+    fn assert_deser_error(input: &[u8], err: DataElementDeserializeError) {
+        let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+            V0Encoding::Unencrypted,
+            input,
+        )
+        .unwrap();
+
+        assert_eq!(
+            err,
+            contents.as_unencrypted().unwrap().data_elements().next().unwrap().unwrap_err()
+        );
+    }
+}
+
+mod ldt {
+
+    // see unencrypted tests above for basic things that are the same between unencrypted and ldt,
+    // like how an invalid de type is handled
+
+    use crate::credential::matched::HasIdentityMatch;
+    use crate::credential::v0::V0BroadcastCredential;
+    use crate::header::V0Encoding;
+    use crate::legacy::data_elements::actions::tests::PlaintextOnly;
+    use crate::legacy::data_elements::actions::{ActionBits, ActionsDataElement};
+    use crate::legacy::data_elements::tx_power::TxPowerDataElement;
+    use crate::legacy::data_elements::{DataElementDeserializeError, DeserializeDataElement};
+    use crate::legacy::deserialize::intermediate::IntermediateAdvContents;
+    use crate::legacy::deserialize::DecryptError;
+    use crate::legacy::serialize::{AdvBuilder, LdtEncoder};
+    use crate::legacy::{Ciphertext, PacketFlavorEnum};
+    use crate::shared_data::TxPower;
+    use alloc::vec::Vec;
+    use crypto_provider_default::CryptoProviderImpl;
+    use ldt_np_adv::{
+        build_np_adv_decrypter, AuthenticatedNpLdtDecryptCipher, V0IdentityToken, V0Salt,
+        V0_IDENTITY_TOKEN_LEN,
+    };
+
+    #[test]
+    fn iterate_actions_invalid_flavor_error() {
+        let mut bits = ActionBits::default();
+        bits.set_action(PlaintextOnly::from(true));
+
+        let key_seed = [0; 32];
+        let identity_token = V0IdentityToken::from([0x33; V0_IDENTITY_TOKEN_LEN]);
+        let salt = V0Salt::from([0x01, 0x02]);
+        let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
+        let mut builder =
+            AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred));
+
+        builder.add_data_element(ActionsDataElement::from(bits)).unwrap();
+
+        let adv = builder.into_advertisement().unwrap();
+
+        let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+            V0Encoding::Ldt,
+            &adv.as_slice()[1..],
+        )
+        .unwrap();
+        let ldt = contents.as_ldt().unwrap();
+        let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+        let identity_token_hmac: [u8; 32] = hkdf
+            .v0_identity_token_hmac_key()
+            .calculate_hmac::<CryptoProviderImpl>(identity_token.as_slice());
+        let decrypter =
+            ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, identity_token_hmac);
+        let decrypted = ldt.try_decrypt(&decrypter).unwrap();
+
+        assert_eq!(salt, decrypted.salt());
+        assert_eq!(identity_token, decrypted.identity_token());
+
+        assert_eq!(
+            DataElementDeserializeError::FlavorNotSupported {
+                de_type: ActionsDataElement::<Ciphertext>::DE_TYPE_CODE,
+                flavor: PacketFlavorEnum::Ciphertext,
+            },
+            decrypted.data_elements().next().unwrap().unwrap_err()
+        )
+    }
+
+    #[test]
+    fn decrypter_wrong_identity_token_hmac_no_match() {
+        build_and_deser_with_invalid_decrypter_error(
+            |adv| adv,
+            |key_seed, _identity_token| {
+                ldt_np_adv::build_np_adv_decrypter_from_key_seed(
+                    &np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(key_seed),
+                    [0xFA; 32],
+                )
+            },
+        )
+    }
+
+    #[test]
+    fn decrypter_wrong_key_seed_no_match() {
+        build_and_deser_with_invalid_decrypter_error(
+            |adv| adv,
+            |key_seed, identity_token| {
+                let correct_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(key_seed);
+                ldt_np_adv::build_np_adv_decrypter_from_key_seed(
+                    // wrong key seed
+                    &np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&[0xFA; 32]),
+                    correct_hkdf
+                        .v0_identity_token_hmac_key()
+                        .calculate_hmac::<CryptoProviderImpl>(identity_token.as_slice()),
+                )
+            },
+        )
+    }
+
+    #[test]
+    fn decrypter_wrong_ldt_key_no_match() {
+        build_and_deser_with_invalid_decrypter_error(
+            |adv| adv,
+            |key_seed, identity_token| {
+                let hkdf = &np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(key_seed);
+                let bogus_key = ldt::LdtKey::from_concatenated(&[0xFA; 64]);
+                build_np_adv_decrypter(
+                    &bogus_key,
+                    hkdf.v0_identity_token_hmac_key()
+                        .calculate_hmac::<CryptoProviderImpl>(identity_token.as_slice()),
+                    hkdf.v0_identity_token_hmac_key(),
+                )
+            },
+        )
+    }
+
+    #[test]
+    fn decrypter_wrong_identity_token_hmac_key_no_match() {
+        build_and_deser_with_invalid_decrypter_error(
+            |adv| adv,
+            |key_seed, identity_token| {
+                let hkdf = &np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(key_seed);
+                build_np_adv_decrypter(
+                    &hkdf.v0_ldt_key(),
+                    hkdf.v0_identity_token_hmac_key()
+                        .calculate_hmac::<CryptoProviderImpl>(identity_token.as_slice()),
+                    [0xFA; 32].into(),
+                )
+            },
+        )
+    }
+
+    #[test]
+    fn mangled_de_ciphertext_no_match() {
+        build_and_deser_with_invalid_decrypter_error(
+            |mut adv| {
+                *adv.last_mut().unwrap() ^= 0x01;
+                adv
+            },
+            build_correct_decrypter,
+        )
+    }
+
+    #[test]
+    fn mangled_token_ciphertext_no_match() {
+        build_and_deser_with_invalid_decrypter_error(
+            |mut adv| {
+                adv[10] ^= 0x01;
+                adv
+            },
+            build_correct_decrypter,
+        )
+    }
+
+    #[test]
+    fn mangled_salt_no_match() {
+        build_and_deser_with_invalid_decrypter_error(
+            |mut adv| {
+                adv[1] ^= 0x01;
+                adv
+            },
+            build_correct_decrypter,
+        )
+    }
+
+    #[test]
+    fn extended_ciphertext_no_match() {
+        build_and_deser_with_invalid_decrypter_error(
+            |mut adv| {
+                adv.push(0xEE);
+                adv
+            },
+            build_correct_decrypter,
+        )
+    }
+
+    fn build_correct_decrypter(
+        key_seed: &[u8; 32],
+        identity_token: &V0IdentityToken,
+    ) -> AuthenticatedNpLdtDecryptCipher<CryptoProviderImpl> {
+        let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(key_seed);
+        ldt_np_adv::build_np_adv_decrypter_from_key_seed(
+            &hkdf,
+            hkdf.v0_identity_token_hmac_key()
+                .calculate_hmac::<CryptoProviderImpl>(identity_token.as_slice()),
+        )
+    }
+
+    fn build_and_deser_with_invalid_decrypter_error(
+        alter_adv: impl Fn(Vec<u8>) -> Vec<u8>,
+        build_decrypter: impl Fn(
+            &[u8; 32],
+            &V0IdentityToken,
+        ) -> AuthenticatedNpLdtDecryptCipher<CryptoProviderImpl>,
+    ) {
+        let key_seed = [0; 32];
+        let identity_token = V0IdentityToken::from([0x33; V0_IDENTITY_TOKEN_LEN]);
+        let salt = V0Salt::from([0x01, 0x02]);
+        let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
+        let mut builder =
+            AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred));
+
+        builder.add_data_element(TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
+
+        let adv = builder.into_advertisement().unwrap();
+        let altered_adv = alter_adv(adv.as_slice().to_vec());
+
+        let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+            V0Encoding::Ldt,
+            &altered_adv.as_slice()[1..],
+        )
+        .unwrap();
+        let ldt = contents.as_ldt().unwrap();
+        let decrypter = build_decrypter(&key_seed, &identity_token);
+        assert_eq!(DecryptError::DecryptOrVerifyError, ldt.try_decrypt(&decrypter).unwrap_err());
+    }
+}
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
new file mode 100644
index 0000000..1b07ff9
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/deserialize/tests/happy_path.rs
@@ -0,0 +1,587 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+mod unencrypted {
+    extern crate std;
+
+    use rand::prelude::SliceRandom;
+    use std::{prelude::rust_2021::*, vec};
+    use strum::IntoEnumIterator;
+
+    use crypto_provider_default::CryptoProviderImpl;
+
+    use crate::{
+        header::V0Encoding,
+        legacy::{
+            data_elements::{
+                actions::{ActionBits, ActionsDataElement, NearbyShare},
+                de_type::DataElementType,
+                tests::test_des::{
+                    random_test_de, TestDataElement, TestDataElementType, TestDeDeserializer,
+                },
+                tx_power::TxPowerDataElement,
+                DynamicSerializeDataElement, SerializeDataElement,
+            },
+            deserialize::{
+                intermediate::IntermediateAdvContents, DataElementDeserializer,
+                DeserializedDataElement, StandardDeserializer,
+            },
+            random_data_elements::random_de_plaintext,
+            serialize::{
+                tests::helpers::{LongDataElement, ShortDataElement},
+                tests::supports_flavor,
+                AddDataElementError, AdvBuilder, SerializedAdv, UnencryptedEncoder,
+            },
+            PacketFlavorEnum, Plaintext, BLE_4_ADV_SVC_MAX_CONTENT_LEN, NP_MAX_DE_CONTENT_LEN,
+            NP_MIN_ADV_CONTENT_LEN,
+        },
+        shared_data::TxPower,
+    };
+
+    #[test]
+    fn parse_min_len_adv() {
+        // 1 byte
+        let de = ShortDataElement::new(vec![]);
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+        builder.add_data_element(de.clone()).unwrap();
+        let data = builder.into_advertisement().unwrap();
+        // extra byte for version header
+        assert_eq!(1 + NP_MIN_ADV_CONTENT_LEN, data.len());
+
+        let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+            V0Encoding::Unencrypted,
+            &data.as_slice()[1..],
+        )
+        .unwrap();
+        let unencrypted = contents.as_unencrypted().unwrap();
+
+        assert_eq!(
+            vec![TestDataElement::Short(de)],
+            unencrypted
+                .generic_data_elements::<TestDeDeserializer>()
+                .collect::<Result<Vec<_>, _>>()
+                .unwrap()
+        );
+    }
+
+    #[test]
+    fn parse_max_len_adv() {
+        let de = LongDataElement::new(vec![0x22; NP_MAX_DE_CONTENT_LEN]);
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+        builder.add_data_element(de.clone()).unwrap();
+        let data = builder.into_advertisement().unwrap();
+        assert_eq!(BLE_4_ADV_SVC_MAX_CONTENT_LEN, data.len());
+
+        let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+            V0Encoding::Unencrypted,
+            &data.as_slice()[1..],
+        )
+        .unwrap();
+        let unencrypted = contents.as_unencrypted().unwrap();
+
+        assert_eq!(
+            vec![TestDataElement::Long(de)],
+            unencrypted
+                .generic_data_elements::<TestDeDeserializer>()
+                .collect::<Result<Vec<_>, _>>()
+                .unwrap()
+        );
+    }
+
+    #[test]
+    fn tx_power() {
+        let de = TxPowerDataElement::from(TxPower::try_from(7).unwrap());
+        let boxed: Box<dyn SerializeDataElement<Plaintext>> = Box::new(de.clone());
+        let _ = assert_build_adv_and_deser(
+            vec![boxed.as_ref().into()],
+            &[DeserializedDataElement::TxPower(de)],
+        );
+    }
+
+    #[test]
+    fn actions_min_len() {
+        let de = ActionsDataElement::from(ActionBits::default());
+
+        let boxed: Box<dyn SerializeDataElement<Plaintext>> = Box::new(de.clone());
+        let data = assert_build_adv_and_deser(
+            vec![boxed.as_ref().into()],
+            &[DeserializedDataElement::Actions(de)],
+        );
+        // version, de header, de contents
+        assert_eq!(3, data.len());
+    }
+
+    #[test]
+    fn typical_tx_power_and_actions() {
+        let tx = TxPowerDataElement::from(TxPower::try_from(7).unwrap());
+        let mut action_bits = ActionBits::default();
+        action_bits.set_action(NearbyShare::from(true));
+        let actions = ActionsDataElement::from(action_bits);
+
+        let tx_boxed: Box<dyn SerializeDataElement<Plaintext>> = Box::new(tx.clone());
+        let actions_boxed: Box<dyn SerializeDataElement<Plaintext>> = Box::new(actions.clone());
+        let expected =
+            vec![DeserializedDataElement::TxPower(tx), DeserializedDataElement::Actions(actions)];
+        let adv = assert_build_adv_and_deser(
+            vec![tx_boxed.as_ref().into(), actions_boxed.as_ref().into()],
+            &expected,
+        );
+
+        let contents = assert_deserialized_contents::<StandardDeserializer>(&expected, &adv);
+        assert_eq!(
+            expected,
+            contents
+                .as_unencrypted()
+                .unwrap()
+                .data_elements()
+                .collect::<Result<Vec<_>, _>>()
+                .unwrap()
+        );
+    }
+
+    #[test]
+    fn random_normal_des_rountrip() {
+        do_random_roundtrip_test::<StandardDeserializer, _>(
+            DataElementType::iter()
+                .filter(|t| supports_flavor(*t, PacketFlavorEnum::Plaintext))
+                .collect(),
+            random_de_plaintext,
+            |builder, de| match de {
+                DeserializedDataElement::Actions(a) => builder.add_data_element(a),
+                DeserializedDataElement::TxPower(tx) => builder.add_data_element(tx),
+            },
+        )
+    }
+
+    #[test]
+    fn random_test_des_rountrip() {
+        do_random_roundtrip_test::<TestDeDeserializer, _>(
+            TestDataElementType::iter().collect::<Vec<_>>(),
+            random_test_de,
+            |builder, de| match de {
+                TestDataElement::Short(s) => builder.add_data_element(s),
+                TestDataElement::Long(l) => builder.add_data_element(l),
+            },
+        )
+    }
+
+    fn do_random_roundtrip_test<D, F>(
+        de_types: Vec<D::DeTypeDisambiguator>,
+        build_de: F,
+        add_de: impl Fn(
+            &mut AdvBuilder<UnencryptedEncoder>,
+            D::Deserialized<Plaintext>,
+        ) -> Result<(), AddDataElementError>,
+    ) where
+        D: DataElementDeserializer,
+        F: Fn(D::DeTypeDisambiguator, &mut rand_ext::rand_pcg::Pcg64) -> D::Deserialized<Plaintext>,
+    {
+        let mut rng = rand_ext::seeded_rng();
+
+        for _ in 0..10_000 {
+            let mut des = Vec::new();
+            let mut builder = AdvBuilder::new(UnencryptedEncoder);
+
+            loop {
+                let de_type = *de_types.choose(&mut rng).unwrap();
+                let de = build_de(de_type, &mut rng);
+
+                let add_res = add_de(&mut builder, de.clone());
+
+                if let Err(e) = add_res {
+                    match e {
+                        AddDataElementError::InsufficientAdvSpace => {
+                            // out of room
+                            break;
+                        }
+                    }
+                }
+
+                des.push(de);
+            }
+
+            let serialized = builder.into_advertisement().unwrap();
+            assert_deserialized_contents::<D>(&des, &serialized);
+        }
+    }
+
+    fn assert_build_adv_and_deser(
+        des: Vec<DynamicSerializeDataElement<Plaintext>>,
+        expected: &[DeserializedDataElement<Plaintext>],
+    ) -> SerializedAdv {
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+        for de in des {
+            builder.add_data_element(de).unwrap();
+        }
+        let adv = builder.into_advertisement().unwrap();
+
+        assert_deserialized_contents::<StandardDeserializer>(expected, &adv);
+
+        adv
+    }
+
+    fn assert_deserialized_contents<'a, D: DataElementDeserializer>(
+        expected: &[D::Deserialized<Plaintext>],
+        adv: &'a SerializedAdv,
+    ) -> IntermediateAdvContents<'a> {
+        let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+            V0Encoding::Unencrypted,
+            &adv.as_slice()[1..],
+        )
+        .unwrap();
+
+        let unencrypted = contents.as_unencrypted().unwrap();
+        assert_eq!(
+            expected,
+            unencrypted
+                .generic_data_elements::<D>()
+                .collect::<Result<Vec<_>, _>>()
+                .unwrap()
+                .as_slice()
+        );
+        contents
+    }
+}
+
+mod ldt {
+    use crate::credential::matched::HasIdentityMatch;
+    use crate::legacy::data_elements::actions::CallTransfer;
+    use crate::{
+        credential::v0::V0BroadcastCredential,
+        header::V0Encoding,
+        legacy::{
+            data_elements::{
+                actions::{ActionBits, ActionsDataElement, NearbyShare},
+                de_type::DataElementType,
+                tests::test_des::{random_test_de, TestDataElementType},
+                tests::test_des::{TestDataElement, TestDeDeserializer},
+                tx_power::TxPowerDataElement,
+                DataElementSerializationBuffer, DynamicSerializeDataElement, SerializeDataElement,
+            },
+            deserialize::{
+                intermediate::IntermediateAdvContents, DecryptedAdvContents,
+                DeserializedDataElement,
+            },
+            deserialize::{DataElementDeserializer, StandardDeserializer},
+            random_data_elements::random_de_ciphertext,
+            serialize::{
+                tests::helpers::ShortDataElement, tests::supports_flavor, AddDataElementError,
+                AdvBuilder, LdtEncoder, SerializedAdv,
+            },
+            Ciphertext, PacketFlavorEnum, BLE_4_ADV_SVC_MAX_CONTENT_LEN, NP_MAX_ADV_CONTENT_LEN,
+        },
+        shared_data::TxPower,
+    };
+    use alloc::boxed::Box;
+    use alloc::vec;
+    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::Rng;
+    use strum::IntoEnumIterator;
+
+    #[test]
+    fn parse_min_len() {
+        // 2 bytes total is the minimum
+        let de = ShortDataElement::new(vec![7]);
+        let boxed: Box<dyn SerializeDataElement<Ciphertext>> = Box::new(de.clone());
+        let (adv, _decrypted) = build_and_assert_deserialized_matches::<TestDeDeserializer>(
+            vec![boxed.as_ref().into()],
+            &[TestDataElement::Short(de)],
+        );
+
+        // version and salt
+        assert_eq!(1 + 2 + ldt_np_adv::VALID_INPUT_LEN.start, adv.len());
+    }
+
+    #[test]
+    fn parse_max_len() {
+        // 7 bytes total
+        let de = ShortDataElement::new(vec![1; 6]);
+        let boxed: Box<dyn SerializeDataElement<Ciphertext>> = Box::new(de.clone());
+        let (adv, _decrypted) = build_and_assert_deserialized_matches::<TestDeDeserializer>(
+            vec![boxed.as_ref().into()],
+            &[TestDataElement::Short(de)],
+        );
+
+        assert_eq!(BLE_4_ADV_SVC_MAX_CONTENT_LEN, adv.len());
+    }
+
+    #[test]
+    fn tx_power() {
+        let de = TxPowerDataElement::from(TxPower::try_from(3).unwrap());
+        let boxed: Box<dyn SerializeDataElement<Ciphertext>> = Box::new(de.clone());
+        let _adv = build_and_assert_deserialized_matches::<StandardDeserializer>(
+            vec![boxed.as_ref().into()],
+            &[DeserializedDataElement::TxPower(de)],
+        );
+    }
+
+    #[test]
+    fn actions_min_len() {
+        let de = ActionsDataElement::from(ActionBits::default());
+        let boxed: Box<dyn SerializeDataElement<Ciphertext>> = Box::new(de.clone());
+        let (adv, _decrypted) = build_and_assert_deserialized_matches::<StandardDeserializer>(
+            vec![boxed.as_ref().into()],
+            &[DeserializedDataElement::Actions(de)],
+        );
+
+        // 2 byte actions DE
+        assert_eq!(1 + 2 + 14 + 2, adv.len());
+    }
+
+    #[test]
+    fn typical_tx_power_and_actions() {
+        let tx = TxPowerDataElement::from(TxPower::try_from(7).unwrap());
+        let mut action_bits = ActionBits::default();
+        action_bits.set_action(NearbyShare::from(true));
+        action_bits.set_action(CallTransfer::from(true));
+        let actions = ActionsDataElement::from(action_bits);
+        let tx_boxed: Box<dyn SerializeDataElement<Ciphertext>> = Box::new(tx.clone());
+        let actions_boxed: Box<dyn SerializeDataElement<Ciphertext>> = Box::new(actions.clone());
+        let expected =
+            [DeserializedDataElement::TxPower(tx), DeserializedDataElement::Actions(actions)];
+        let (_adv, decrypted) = build_and_assert_deserialized_matches::<StandardDeserializer>(
+            vec![tx_boxed.as_ref().into(), actions_boxed.as_ref().into()],
+            &expected,
+        );
+        assert_eq!(
+            &expected,
+            decrypted.data_elements().collect::<Result<Vec<_>, _>>().unwrap().as_slice()
+        );
+    }
+
+    #[test]
+    fn random_normal_des_roundtrip() {
+        do_random_roundtrip_test::<StandardDeserializer, _>(
+            DataElementType::iter()
+                .filter(|t| supports_flavor(*t, PacketFlavorEnum::Ciphertext))
+                .collect(),
+            random_de_ciphertext,
+            |builder, de| match de {
+                DeserializedDataElement::Actions(a) => builder.add_data_element(a),
+                DeserializedDataElement::TxPower(tx) => builder.add_data_element(tx),
+            },
+            |de| match de {
+                DeserializedDataElement::Actions(a) => serialized_len(a),
+                DeserializedDataElement::TxPower(tx) => serialized_len(tx),
+            },
+        )
+    }
+
+    #[test]
+    fn random_test_des_roundtrip() {
+        do_random_roundtrip_test::<TestDeDeserializer, _>(
+            TestDataElementType::iter().collect(),
+            random_test_de,
+            |builder, de| builder.add_data_element(de),
+            serialized_len,
+        )
+    }
+
+    fn do_random_roundtrip_test<D, F>(
+        de_types: Vec<D::DeTypeDisambiguator>,
+        build_de: F,
+        add_de: impl Fn(
+            &mut AdvBuilder<LdtEncoder<CryptoProviderImpl>>,
+            D::Deserialized<Ciphertext>,
+        ) -> Result<(), AddDataElementError>,
+        serialized_len: impl Fn(&D::Deserialized<Ciphertext>) -> usize,
+    ) where
+        D: DataElementDeserializer,
+        F: Fn(
+            D::DeTypeDisambiguator,
+            &mut rand_ext::rand_pcg::Pcg64,
+        ) -> D::Deserialized<Ciphertext>,
+    {
+        let mut rng = rand_ext::seeded_rng();
+
+        for _ in 0..10_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 identity_token = V0IdentityToken::from(rng.gen::<[u8; V0_IDENTITY_TOKEN_LEN]>());
+
+            let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
+
+            let mut builder =
+                AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred));
+
+            loop {
+                let de_type = *de_types.choose(&mut rng).unwrap();
+                let de = build_de(de_type, &mut rng);
+
+                if let Err(e) = add_de(&mut builder, de.clone()) {
+                    match e {
+                        AddDataElementError::InsufficientAdvSpace => {
+                            if current_len
+                                < ldt_np_adv::VALID_INPUT_LEN.start - V0_IDENTITY_TOKEN_LEN
+                            {
+                                // keep trying, not enough for LDT
+                                continue;
+                            }
+                            // out of room
+                            break;
+                        }
+                    }
+                }
+
+                current_len += serialized_len(&de);
+                added_des.push(de);
+            }
+
+            let adv = builder.into_advertisement().unwrap();
+            assert_deserialized_contents::<D>(&key_seed, identity_token, salt, &adv, &added_des);
+        }
+    }
+
+    fn build_and_assert_deserialized_matches<D: DataElementDeserializer>(
+        des: Vec<DynamicSerializeDataElement<Ciphertext>>,
+        expected: &[D::Deserialized<Ciphertext>],
+    ) -> (SerializedAdv, DecryptedAdvContents) {
+        let key_seed = [0; 32];
+        let identity_token = V0IdentityToken::from([0x33; V0_IDENTITY_TOKEN_LEN]);
+        let salt = V0Salt::from([0x01, 0x02]);
+        let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
+        let mut builder =
+            AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred));
+
+        for de in des {
+            builder.add_data_element(de).unwrap();
+        }
+        let adv = builder.into_advertisement().unwrap();
+
+        let decrypted =
+            assert_deserialized_contents::<D>(&key_seed, identity_token, salt, &adv, expected);
+        (adv, decrypted)
+    }
+
+    fn assert_deserialized_contents<D: DataElementDeserializer>(
+        key_seed: &[u8; 32],
+        identity_token: V0IdentityToken,
+        salt: V0Salt,
+        adv: &SerializedAdv,
+        expected: &[D::Deserialized<Ciphertext>],
+    ) -> DecryptedAdvContents {
+        let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+            V0Encoding::Ldt,
+            &adv.as_slice()[1..],
+        )
+        .unwrap();
+        let ldt = contents.as_ldt().unwrap();
+        let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(key_seed);
+        let identity_token_hmac: [u8; 32] = hkdf
+            .v0_identity_token_hmac_key()
+            .calculate_hmac::<CryptoProviderImpl>(identity_token.as_slice());
+        let decrypter =
+            ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, identity_token_hmac);
+        let decrypted = ldt.try_decrypt(&decrypter).unwrap();
+
+        assert_eq!(salt, decrypted.salt());
+        assert_eq!(identity_token, decrypted.identity_token());
+
+        assert_eq!(
+            expected,
+            decrypted.generic_data_elements::<D>().collect::<Result<Vec<_>, _>>().unwrap()
+        );
+
+        decrypted
+    }
+
+    /// serialized length including header
+    fn serialized_len<S: SerializeDataElement<Ciphertext>>(de: &S) -> usize {
+        let mut buf = DataElementSerializationBuffer::new(NP_MAX_ADV_CONTENT_LEN).unwrap();
+        de.serialize_contents(&mut buf).unwrap();
+        buf.len() + 1
+    }
+}
+
+mod coverage_gaming {
+    use alloc::format;
+
+    use crypto_provider_default::CryptoProviderImpl;
+
+    use crate::header::V0Encoding;
+    use crate::legacy::data_elements::de_type::DataElementType;
+    use crate::legacy::deserialize::intermediate::{IntermediateAdvContents, LdtAdvContents};
+    use crate::legacy::deserialize::{
+        AdvDeserializeError, DeIterator, RawDataElement, StandardDeserializer,
+    };
+    use crate::legacy::Plaintext;
+
+    #[test]
+    fn iac_debug_eq_test_helpers() {
+        let iac = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+            V0Encoding::Unencrypted,
+            &[0xFF],
+        )
+        .unwrap();
+        let _ = format!("{:?}", iac);
+        assert_eq!(iac, iac);
+    }
+
+    #[test]
+    fn iac_test_helpers() {
+        assert_eq!(
+            None,
+            IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+                V0Encoding::Unencrypted,
+                &[0xFF],
+            )
+            .unwrap()
+            .as_ldt()
+        );
+        assert_eq!(
+            None,
+            IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+                V0Encoding::Ldt,
+                &[0xFF; 18],
+            )
+            .unwrap()
+            .as_unencrypted()
+        );
+    }
+
+    #[test]
+    fn ade_debug_clone() {
+        let _ = format!("{:?}", AdvDeserializeError::NoDataElements.clone());
+    }
+
+    #[test]
+    fn rde_debug_eq() {
+        let rde = RawDataElement::<'_, StandardDeserializer> {
+            de_type: DataElementType::Actions,
+            contents: &[],
+        };
+
+        let _ = format!("{:?}", rde);
+        assert_eq!(rde, rde);
+    }
+
+    #[test]
+    fn de_iterator_debug_clone_eq() {
+        let i = DeIterator::<'_, Plaintext>::new(&[]);
+        let _ = format!("{:?}", i.clone());
+        assert_eq!(i, i);
+    }
+
+    #[test]
+    fn ldt_adv_contents_debug() {
+        let lac = LdtAdvContents::new::<CryptoProviderImpl>([0; 2].into(), &[0; 16]).unwrap();
+        let _ = format!("{:?}", lac);
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/tests/mod.rs b/nearby/presence/np_adv/src/legacy/deserialize/tests/mod.rs
new file mode 100644
index 0000000..0a016e1
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/deserialize/tests/mod.rs
@@ -0,0 +1,194 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#![allow(unused_results, clippy::unwrap_used)]
+
+extern crate std;
+
+use super::*;
+use crate::header::V0Encoding;
+use crate::legacy::deserialize::intermediate::{IntermediateAdvContents, LdtAdvContents};
+use crate::{
+    credential::v0::V0BroadcastCredential,
+    legacy::{
+        serialize::{AdvBuilder, LdtEncoder},
+        BLE_4_ADV_SVC_MAX_CONTENT_LEN,
+    },
+    shared_data::TxPower,
+};
+use crypto_provider::CryptoProvider;
+use crypto_provider_default::CryptoProviderImpl;
+use ldt_np_adv::V0_IDENTITY_TOKEN_LEN;
+
+mod error_conditions;
+mod happy_path;
+
+#[test]
+fn decrypt_with_wrong_key_seed_error() {
+    let salt = ldt_np_adv::V0Salt::from([0x22; 2]);
+    let metadata_key: [u8; V0_IDENTITY_TOKEN_LEN] = [0x33; V0_IDENTITY_TOKEN_LEN];
+    let correct_key_seed = [0x11_u8; 32];
+
+    let (adv_content, correct_cipher) =
+        build_ciphertext_adv_contents(salt, &metadata_key, correct_key_seed);
+    let eac = parse_ciphertext_adv_contents(&correct_cipher, &adv_content.as_slice()[1..]);
+
+    // wrong key seed doesn't work (derives wrong ldt key, wrong hmac key)
+    let wrong_key_seed_cipher = {
+        let key_seed = [0x22_u8; 32];
+
+        let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+        let metadata_key_hmac: [u8; 32] =
+            hkdf.v0_identity_token_hmac_key().calculate_hmac::<CryptoProviderImpl>(&metadata_key);
+        ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, metadata_key_hmac)
+    };
+
+    assert_eq!(
+        DecryptError::DecryptOrVerifyError,
+        eac.try_decrypt(&wrong_key_seed_cipher).unwrap_err()
+    );
+}
+
+#[test]
+fn decrypt_with_wrong_hmac_key_error() {
+    let salt = ldt_np_adv::V0Salt::from([0x22; 2]);
+    let metadata_key: [u8; V0_IDENTITY_TOKEN_LEN] = [0x33; V0_IDENTITY_TOKEN_LEN];
+    let correct_key_seed = [0x11_u8; 32];
+
+    let (adv_content, correct_cipher_config) =
+        build_ciphertext_adv_contents(salt, &metadata_key, correct_key_seed);
+    let eac = parse_ciphertext_adv_contents(&correct_cipher_config, &adv_content.as_slice()[1..]);
+
+    let wrong_hmac_key_cipher = {
+        let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&[0x10_u8; 32]);
+        let metadata_key_hmac: [u8; 32] =
+            hkdf.v0_identity_token_hmac_key().calculate_hmac::<CryptoProviderImpl>(&metadata_key);
+
+        ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, metadata_key_hmac)
+    };
+
+    assert_eq!(
+        DecryptError::DecryptOrVerifyError,
+        eac.try_decrypt::<CryptoProviderImpl>(&wrong_hmac_key_cipher).unwrap_err()
+    );
+}
+
+#[test]
+fn decrypt_with_wrong_hmac_error() {
+    let salt = ldt_np_adv::V0Salt::from([0x22; 2]);
+    let metadata_key: [u8; V0_IDENTITY_TOKEN_LEN] = [0x33; V0_IDENTITY_TOKEN_LEN];
+    let correct_key_seed = [0x11_u8; 32];
+
+    let (adv_content, correct_cipher_config) =
+        build_ciphertext_adv_contents(salt, &metadata_key, correct_key_seed);
+    let eac = parse_ciphertext_adv_contents(&correct_cipher_config, &adv_content.as_slice()[1..]);
+
+    let wrong_hmac_key_cipher = {
+        let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&correct_key_seed);
+
+        ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, [0x77; 32])
+    };
+
+    assert_eq!(
+        DecryptError::DecryptOrVerifyError,
+        eac.try_decrypt(&wrong_hmac_key_cipher).unwrap_err()
+    );
+}
+
+fn build_ciphertext_adv_contents<C: CryptoProvider>(
+    salt: ldt_np_adv::V0Salt,
+    metadata_key: &[u8; 14],
+    correct_key_seed: [u8; 32],
+) -> (
+    ArrayView<u8, { BLE_4_ADV_SVC_MAX_CONTENT_LEN }>,
+    ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>,
+) {
+    let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&correct_key_seed);
+
+    let metadata_key_hmac: [u8; 32] =
+        hkdf.v0_identity_token_hmac_key().calculate_hmac::<C>(metadata_key.as_slice());
+
+    let correct_cipher = ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, metadata_key_hmac);
+
+    let broadcast_cred =
+        V0BroadcastCredential::new(correct_key_seed, V0IdentityToken::from(*metadata_key));
+
+    let mut builder = AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred));
+    builder.add_data_element(TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
+    (builder.into_advertisement().unwrap(), correct_cipher)
+}
+
+fn parse_ciphertext_adv_contents<'b>(
+    cipher: &ldt_np_adv::AuthenticatedNpLdtDecryptCipher<CryptoProviderImpl>,
+    adv_content: &'b [u8],
+) -> LdtAdvContents<'b> {
+    let eac = match IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+        V0Encoding::Ldt,
+        adv_content,
+    )
+    .unwrap()
+    {
+        IntermediateAdvContents::Unencrypted(_) => panic!(),
+        // quick confirmation that we did get something
+        IntermediateAdvContents::Ldt(eac) => eac,
+    };
+
+    // correct cipher works
+    assert!(eac.try_decrypt(cipher).is_ok());
+
+    eac
+}
+
+mod coverage_gaming {
+    use crate::legacy::data_elements::actions::{ActionBits, ActionsDataElement};
+    use crate::legacy::deserialize::{
+        DecryptError, DecryptedAdvContents, DeserializedDataElement, StandardDeserializer,
+    };
+    use crate::legacy::Plaintext;
+    use alloc::format;
+    use array_view::ArrayView;
+    use ldt_np_adv::{V0_IDENTITY_TOKEN_LEN, V0_SALT_LEN};
+
+    #[test]
+    fn decrypt_error_debug_clone() {
+        let _ = format!("{:?}", DecryptError::DecryptOrVerifyError.clone());
+    }
+
+    #[test]
+    fn deserialized_data_element_debug() {
+        let _ = format!(
+            "{:?}",
+            DeserializedDataElement::<Plaintext>::Actions(ActionsDataElement::from(
+                ActionBits::default()
+            ))
+        );
+    }
+
+    #[test]
+    fn decrypted_adv_contents_debug_partial_eq() {
+        let dac = DecryptedAdvContents::new(
+            [0; V0_IDENTITY_TOKEN_LEN].into(),
+            [0; V0_SALT_LEN].into(),
+            ArrayView::try_from_slice(&[]).unwrap(),
+        );
+        let _ = format!("{:?}", dac);
+        assert_eq!(dac, dac)
+    }
+
+    #[test]
+    fn standard_deserializer_debug_eq_clone() {
+        assert_eq!(StandardDeserializer, StandardDeserializer);
+        let _ = format!("{:?}", StandardDeserializer.clone());
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/mod.rs b/nearby/presence/np_adv/src/legacy/mod.rs
index 59f2308..2bac636 100644
--- a/nearby/presence/np_adv/src/legacy/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/mod.rs
@@ -14,14 +14,20 @@
 
 //! V0 advertisement support.
 
-use crate::MetadataKey;
+use crate::credential::matched::{MatchedCredential, WithMatchedCredential};
+use crate::legacy::deserialize::intermediate::{IntermediateAdvContents, UnencryptedAdvContents};
+use crate::{
+    credential::{
+        book::CredentialBook, v0::V0DiscoveryCryptoMaterial, DiscoveryMetadataCryptoMaterial,
+    },
+    header::V0Encoding,
+    legacy::deserialize::{DecryptError, DecryptedAdvContents},
+    AdvDeserializationError,
+};
 use core::fmt;
 use crypto_provider::CryptoProvider;
-use ldt_np_adv::NP_LEGACY_METADATA_KEY_LEN;
 
-pub mod actions;
 pub mod data_elements;
-pub mod de_type;
 pub mod deserialize;
 pub mod serialize;
 
@@ -30,34 +36,19 @@
 
 /// Advertisement capacity after 5 bytes of BLE header and 2 bytes of svc UUID are reserved from a
 /// 31-byte advertisement
-pub const BLE_ADV_SVC_CONTENT_LEN: usize = 24;
-/// Maximum possible DE content: packet size minus 2 for adv header & DE header
-const NP_MAX_DE_CONTENT_LEN: usize = BLE_ADV_SVC_CONTENT_LEN - 2;
-
-/// "Short" 14-byte metadata key type employed for V0, which needs to be
-/// expanded to a regular-size 16-byte metadata key to decrypt metadata.
-#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
-pub struct ShortMetadataKey(pub [u8; NP_LEGACY_METADATA_KEY_LEN]);
-
-impl AsRef<[u8]> for ShortMetadataKey {
-    fn as_ref(&self) -> &[u8] {
-        &self.0
-    }
-}
-
-impl ShortMetadataKey {
-    /// Expand this short 14-byte metadata key to a 16-byte metadata key
-    /// which may be used to decrypt metadata.
-    pub fn expand<C: CryptoProvider>(&self) -> MetadataKey {
-        let expanded_bytes = np_hkdf::legacy_metadata_expanded_key::<C>(&self.0);
-        MetadataKey(expanded_bytes)
-    }
-}
+pub const BLE_4_ADV_SVC_MAX_CONTENT_LEN: usize = 24;
+/// Maximum possible advertisement NP-level content: packet size minus 1 for version header
+const NP_MAX_ADV_CONTENT_LEN: usize = BLE_4_ADV_SVC_MAX_CONTENT_LEN - 1;
+/// Minimum advertisement NP-level content.
+/// Only meaningful for unencrypted advertisements, as LDT advertisements already have salt, token, etc.
+const NP_MIN_ADV_CONTENT_LEN: usize = 1;
+/// Max length of an individual DE's content
+pub(crate) const NP_MAX_DE_CONTENT_LEN: usize = NP_MAX_ADV_CONTENT_LEN - 1;
 
 /// Marker type to allow disambiguating between plaintext and encrypted packets at compile time.
 ///
 /// See also [PacketFlavorEnum] for when runtime flavor checks are more suitable.
-pub trait PacketFlavor: fmt::Debug + Clone + Copy {
+pub trait PacketFlavor: fmt::Debug + Clone + Copy + PartialEq + Eq {
     /// The corresponding [PacketFlavorEnum] variant.
     const ENUM_VARIANT: PacketFlavorEnum;
 }
@@ -80,10 +71,90 @@
 
 /// An enum version of the implementors of [PacketFlavor] for use cases where runtime checking is
 /// a better fit than compile time checking.
-#[derive(Debug, Clone, Copy, strum_macros::EnumIter, PartialEq, Eq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum PacketFlavorEnum {
     /// Corresponds to [Plaintext].
     Plaintext,
     /// Corresponds to [Ciphertext].
     Ciphertext,
 }
+
+/// Deserialize and decrypt the contents of a v0 adv after the version header
+pub(crate) fn deser_decrypt_v0<'adv, 'cred, B, P>(
+    encoding: V0Encoding,
+    cred_book: &'cred B,
+    remaining: &'adv [u8],
+) -> Result<V0AdvertisementContents<'adv, B::Matched>, AdvDeserializationError>
+where
+    B: CredentialBook<'cred>,
+    P: CryptoProvider,
+{
+    match IntermediateAdvContents::deserialize::<P>(encoding, remaining)? {
+        IntermediateAdvContents::Unencrypted(p) => Ok(V0AdvertisementContents::Plaintext(p)),
+        IntermediateAdvContents::Ldt(c) => {
+            for (crypto_material, matched) in cred_book.v0_iter() {
+                let ldt = crypto_material.ldt_adv_cipher::<P>();
+                match c.try_decrypt(&ldt) {
+                    Ok(c) => {
+                        let metadata_nonce = crypto_material.metadata_nonce::<P>();
+                        return Ok(V0AdvertisementContents::Decrypted(WithMatchedCredential::new(
+                            matched,
+                            metadata_nonce,
+                            c,
+                        )));
+                    }
+                    Err(e) => match e {
+                        DecryptError::DecryptOrVerifyError => continue,
+                    },
+                }
+            }
+            Ok(V0AdvertisementContents::NoMatchingCredentials)
+        }
+    }
+}
+
+/// Advertisement content that was either already plaintext or has been decrypted.
+#[derive(Debug, PartialEq, Eq)]
+pub enum V0AdvertisementContents<'adv, M: MatchedCredential> {
+    /// Contents of a plaintext advertisement
+    Plaintext(UnencryptedAdvContents<'adv>),
+    /// Contents that was ciphertext in the original advertisement, and has been decrypted
+    /// with the credential in the [MatchedCredential]
+    Decrypted(WithMatchedCredential<M, DecryptedAdvContents>),
+    /// The advertisement was encrypted, but no credentials matched
+    NoMatchingCredentials,
+}
+
+#[cfg(test)]
+mod tests {
+    mod coverage_gaming {
+        use crate::legacy::{Ciphertext, PacketFlavorEnum, Plaintext};
+
+        extern crate std;
+
+        use std::format;
+
+        #[test]
+        fn plaintext_flavor() {
+            // debug
+            let _ = format!("{:?}", Plaintext);
+            // eq and clone
+            assert_eq!(Plaintext, Plaintext.clone())
+        }
+
+        #[test]
+        fn ciphertext_flavor() {
+            // debug
+            let _ = format!("{:?}", Ciphertext);
+            // eq and clone
+            assert_eq!(Ciphertext, Ciphertext.clone())
+        }
+
+        #[allow(clippy::clone_on_copy)]
+        #[test]
+        fn flavor_enum() {
+            // clone
+            let _ = PacketFlavorEnum::Plaintext.clone();
+        }
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/random_data_elements.rs b/nearby/presence/np_adv/src/legacy/random_data_elements.rs
index aca906b..e3fbde1 100644
--- a/nearby/presence/np_adv/src/legacy/random_data_elements.rs
+++ b/nearby/presence/np_adv/src/legacy/random_data_elements.rs
@@ -16,18 +16,18 @@
 
 extern crate std;
 
+use crate::legacy::data_elements::actions::tests::{
+    set_ciphertexttext_action, set_plaintext_action,
+};
 use crate::{
     legacy::{
-        actions::*,
-        data_elements::*,
-        de_type::PlainDataElementType,
-        deserialize::PlainDataElement,
-        serialize::{DataElementBundle, ToDataElementBundle},
+        data_elements::{actions::*, de_type::DataElementType, tx_power::TxPowerDataElement, *},
+        deserialize::DeserializedDataElement,
         Ciphertext, PacketFlavor, PacketFlavorEnum, Plaintext,
     },
-    shared_data::{ContextSyncSeqNum, TxPower},
+    shared_data::TxPower,
 };
-use rand_ext::rand::{self, distributions, prelude::SliceRandom as _};
+use rand_ext::rand::{distributions, prelude::SliceRandom as _};
 use std::prelude::rust_2021::*;
 use strum::IntoEnumIterator;
 
@@ -45,25 +45,16 @@
         let mut bits = ActionBits::default();
 
         for a in selected_actions {
-            match a {
-                ActionType::ContextSyncSeqNum => {
-                    bits.set_action(ContextSyncSeqNum::try_from(rng.gen_range(0..=15)).unwrap())
-                }
-                // generating boolean actions with `true` since we already did our random selection
-                // of which actions to use above
-                ActionType::NearbyShare => bits.set_action(NearbyShare::from(true)),
-                ActionType::Finder => bits.set_action(Finder::from(true)),
-                ActionType::FastPairSass => bits.set_action(FastPairSass::from(true)),
-                ActionType::ActiveUnlock
-                | ActionType::PresenceManager
-                | ActionType::InstantTethering
-                | ActionType::PhoneHub => unreachable!("not plaintext actions"),
-            }
+            // generating boolean actions with `true` since we already did our random selection
+            // of which actions to use above
+
+            set_plaintext_action(*a, true, &mut bits);
         }
 
         ActionsDataElement::from(bits)
     }
 }
+
 impl distributions::Distribution<ActionsDataElement<Ciphertext>> for distributions::Standard {
     fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> ActionsDataElement<Ciphertext> {
         let mut available_actions = ActionType::iter()
@@ -78,20 +69,9 @@
         let mut bits = ActionBits::default();
 
         for a in selected_actions {
-            match a {
-                ActionType::ContextSyncSeqNum => {
-                    bits.set_action(ContextSyncSeqNum::try_from(rng.gen_range(0..=15)).unwrap())
-                }
-                ActionType::ActiveUnlock => bits.set_action(ActiveUnlock::from(true)),
-                // generating boolean actions with `true` since we already did our random selection
-                // of which actions to use above
-                ActionType::NearbyShare => bits.set_action(NearbyShare::from(true)),
-                ActionType::PresenceManager => bits.set_action(PresenceManager::from(true)),
-                ActionType::InstantTethering => bits.set_action(InstantTethering::from(true)),
-                ActionType::PhoneHub => bits.set_action(PhoneHub::from(true)),
-                ActionType::Finder => bits.set_action(Finder::from(true)),
-                ActionType::FastPairSass => bits.set_action(FastPairSass::from(true)),
-            }
+            // generating boolean actions with `true` since we already did our random selection
+            // of which actions to use above
+            set_ciphertexttext_action(*a, true, &mut bits);
         }
 
         ActionsDataElement::from(bits)
@@ -112,60 +92,45 @@
     }
 }
 
-/// Generate a random instance of the requested DE and return it wrapped in [PlainDataElement] along
-/// with its bundle representation.
-pub(crate) fn rand_de_and_bundle<F, D, E, R>(
-    to_enum: E,
-    rng: &mut R,
-) -> (PlainDataElement<F>, DataElementBundle<F>)
+/// Generate a random instance of the requested DE and return it wrapped in [DeserializedDataElement].
+pub(crate) fn rand_de<F, D, E, R>(to_enum: E, rng: &mut R) -> DeserializedDataElement<F>
 where
     F: PacketFlavor,
-    D: ToDataElementBundle<F>,
-    E: Fn(D) -> PlainDataElement<F>,
+    D: SerializeDataElement<F>,
+    E: Fn(D) -> DeserializedDataElement<F>,
     R: rand::Rng,
     distributions::Standard: distributions::Distribution<D>,
 {
     let de = rng.gen::<D>();
-    let bundle = de.to_de_bundle();
-    (to_enum(de), bundle)
+    to_enum(de)
 }
 
 /// Generate a random instance of the requested de type, or `None` if that type does not support
 /// plaintext.
 pub(crate) fn random_de_plaintext<R>(
-    de_type: PlainDataElementType,
+    de_type: DataElementType,
     rng: &mut R,
-) -> Option<(PlainDataElement<Plaintext>, DataElementBundle<Plaintext>)>
+) -> DeserializedDataElement<Plaintext>
 where
     R: rand::Rng,
 {
-    let opt = match de_type {
-        PlainDataElementType::TxPower => Some(rand_de_and_bundle(PlainDataElement::TxPower, rng)),
-        PlainDataElementType::Actions => Some(rand_de_and_bundle(PlainDataElement::Actions, rng)),
-    };
-
-    // make sure flavor support is consistent
-    assert_eq!(opt.is_some(), de_type.supports_flavor(PacketFlavorEnum::Plaintext));
-
-    opt
+    match de_type {
+        DataElementType::TxPower => rand_de(DeserializedDataElement::TxPower, rng),
+        DataElementType::Actions => rand_de(DeserializedDataElement::Actions, rng),
+    }
 }
 
 /// Generate a random instance of the requested de type, or `None` if that type does not support
 /// ciphertext.
 pub(crate) fn random_de_ciphertext<R>(
-    de_type: PlainDataElementType,
+    de_type: DataElementType,
     rng: &mut R,
-) -> Option<(PlainDataElement<Ciphertext>, DataElementBundle<Ciphertext>)>
+) -> DeserializedDataElement<Ciphertext>
 where
     R: rand::Rng,
 {
-    let opt = match de_type {
-        PlainDataElementType::TxPower => Some(rand_de_and_bundle(PlainDataElement::TxPower, rng)),
-        PlainDataElementType::Actions => Some(rand_de_and_bundle(PlainDataElement::Actions, rng)),
-    };
-
-    // make sure flavor support is consistent
-    assert_eq!(opt.is_some(), de_type.supports_flavor(PacketFlavorEnum::Ciphertext));
-
-    opt
+    match de_type {
+        DataElementType::TxPower => rand_de(DeserializedDataElement::TxPower, rng),
+        DataElementType::Actions => rand_de(DeserializedDataElement::Actions, rng),
+    }
 }
diff --git a/nearby/presence/np_adv/src/legacy/serialize/header.rs b/nearby/presence/np_adv/src/legacy/serialize/header.rs
new file mode 100644
index 0000000..7859633
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/serialize/header.rs
@@ -0,0 +1,74 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use ldt_np_adv::{V0IdentityToken, V0Salt};
+
+/// Format || salt || token
+const ADV_HEADER_MAX_LEN: usize = 1 + 2 + 14;
+
+/// Serializes a V0 header.
+///
+/// Does not include the overall NP version header byte that defines the adv
+/// version.
+pub struct V0Header {
+    header_bytes: tinyvec::ArrayVec<[u8; ADV_HEADER_MAX_LEN]>,
+}
+
+impl V0Header {
+    pub(crate) fn unencrypted() -> Self {
+        let header_bytes = tinyvec::ArrayVec::new();
+        Self { header_bytes }
+    }
+
+    pub(crate) fn ldt_short_salt(salt: V0Salt, identity_token: V0IdentityToken) -> Self {
+        let mut header_bytes = tinyvec::ArrayVec::new();
+        header_bytes.extend_from_slice(salt.bytes().as_slice());
+        header_bytes.extend_from_slice(identity_token.as_slice());
+        Self { header_bytes }
+    }
+
+    /// The returned slice must be shorter than [crate::legacy::BLE_4_ADV_SVC_MAX_CONTENT_LEN] - 1.
+    pub(crate) fn as_slice(&self) -> &[u8] {
+        self.header_bytes.as_slice()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use ldt_np_adv::{V0_IDENTITY_TOKEN_LEN, V0_SALT_LEN};
+
+    const SHORT_SALT_BYTES: [u8; V0_SALT_LEN] = [0x10, 0x11];
+    const TOKEN_BYTES: [u8; V0_IDENTITY_TOKEN_LEN] =
+        [0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D];
+
+    #[test]
+    fn unencrypted_slice() {
+        assert_eq!(&[0_u8; 0], V0Header::unencrypted().as_slice());
+    }
+
+    #[rustfmt::skip]
+    #[test]
+    fn ldt_short_salt_slice() {
+        assert_eq!(
+            &[
+                // salt
+                0x10, 0x11,
+                // token
+                0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D
+            ],
+            V0Header::ldt_short_salt(SHORT_SALT_BYTES.into(), TOKEN_BYTES.into()).as_slice()
+        );
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/serialize/mod.rs b/nearby/presence/np_adv/src/legacy/serialize/mod.rs
index 3746796..1906fb2 100644
--- a/nearby/presence/np_adv/src/legacy/serialize/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/serialize/mod.rs
@@ -19,10 +19,9 @@
 //! Serializing a plaintext advertisement:
 //!
 //! ```
-//! use np_adv::{legacy::{data_elements::*, serialize::*}, shared_data::*, PublicIdentity};
-//! use np_adv::shared_data::TxPower;
+//! use np_adv::{legacy::{data_elements::tx_power::TxPowerDataElement, serialize::*}, shared_data::*};
 //!
-//! let mut builder = AdvBuilder::new(PublicIdentity::default());
+//! let mut builder = AdvBuilder::new(UnencryptedEncoder);
 //! builder
 //!     .add_data_element(TxPowerDataElement::from(TxPower::try_from(3).expect("3 is a valid TxPower value")))
 //!     .unwrap();
@@ -30,7 +29,6 @@
 //! assert_eq!(
 //!     &[
 //!         0x00, // Adv Header
-//!         0x03, // Public DE header
 //!         0x15, 0x03, // tx power de
 //!     ],
 //!     packet.as_slice()
@@ -40,29 +38,25 @@
 //! Serializing an encrypted advertisement:
 //!
 //! ```
-//! use np_adv::{shared_data::*, de_type::*, legacy::{de_type::*, data_elements::*, serialize::*, *}};
-//! use np_adv::credential::{v0::V0, SimpleBroadcastCryptoMaterial};
+//! use np_adv::{shared_data::*, legacy::{data_elements::de_type::*, data_elements::tx_power::TxPowerDataElement, serialize::*, *}};
+//! use np_adv::credential::{v0::{V0, V0BroadcastCredential}};
 //! use crypto_provider::CryptoProvider;
 //! use crypto_provider_default::CryptoProviderImpl;
-//! use ldt_np_adv::{salt_padder, LegacySalt, LdtEncrypterXtsAes128};
+//! use ldt_np_adv::{V0Salt, V0IdentityToken};
 //!
 //! // Generate these from proper CSPRNGs -- using fixed data here
-//! let metadata_key = ShortMetadataKey([0x33; 14]);
-//! let salt = LegacySalt::from([0x01, 0x02]);
+//! let metadata_key = V0IdentityToken::from([0x33; 14]);
+//! let salt = V0Salt::from([0x01, 0x02]);
 //! let key_seed = [0x44; 32];
-//! let ldt_enc = LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(
-//!     &np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed).legacy_ldt_key()
-//! );
 //!
-//! let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(
+//! let broadcast_cred = V0BroadcastCredential::new(
 //!     key_seed,
 //!     metadata_key,
 //! );
 //!
-//! let mut builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
-//!     EncryptedIdentityDataElementType::Private,
+//! let mut builder = AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(
 //!     salt,
-//!     &broadcast_cm,
+//!     &broadcast_cred,
 //! ));
 //!
 //! builder
@@ -71,195 +65,218 @@
 //!
 //! let packet = builder.into_advertisement().unwrap();
 //! ```
-use crate::credential::{v0::V0, BroadcastCryptoMaterial};
-use crate::{
-    de_type::{EncryptedIdentityDataElementType, IdentityDataElementType},
-    legacy::{
-        de_type::{DataElementType, DeActualLength, DeEncodedLength, PlainDataElementType},
-        Ciphertext, PacketFlavor, Plaintext, ShortMetadataKey, BLE_ADV_SVC_CONTENT_LEN,
-        NP_MAX_DE_CONTENT_LEN,
-    },
-    DeLengthOutOfRange, PublicIdentity,
-};
+use core::fmt;
+
 use array_view::ArrayView;
-use core::{convert, fmt, marker};
 use crypto_provider::CryptoProvider;
+use ldt::LdtCipher;
+use ldt_np_adv::{V0IdentityToken, V0_IDENTITY_TOKEN_LEN};
+use sink::Sink;
+
+use crate::credential::v0::V0BroadcastCredential;
+use crate::{
+    extended::to_array_view,
+    header::{VERSION_HEADER_V0_LDT, VERSION_HEADER_V0_UNENCRYPTED},
+    legacy::{
+        data_elements::{
+            de_type::{DeActualLength, DeEncodedLength, DeTypeCode},
+            DataElementSerializationBuffer, DataElementSerializeError, SerializeDataElement,
+        },
+        Ciphertext, PacketFlavor, Plaintext, BLE_4_ADV_SVC_MAX_CONTENT_LEN, NP_MAX_ADV_CONTENT_LEN,
+        NP_MIN_ADV_CONTENT_LEN,
+    },
+};
+
+mod header;
 
 #[cfg(test)]
-mod tests;
+pub(crate) mod tests;
 
-/// An identity used in serializing an advertisement.
-pub trait Identity: fmt::Debug {
+/// An encoder used in serializing an advertisement.
+pub trait AdvEncoder: fmt::Debug {
     /// The flavor of packet this identity produces
     type Flavor: PacketFlavor;
     /// The error returned if postprocessing fails
     type Error: fmt::Debug;
-    /// How much space needs to be reserved for this identity's prefix bytes
-    const OVERHEAD_LEN: usize;
+
+    /// The version header to put at the start of the advertisement.
+    const VERSION_HEADER: u8;
+
+    /// The V0-specific header to be written at the start of the advertisement
+    /// immediately after the version header.
+    fn header(&self) -> header::V0Header;
 
     /// Perform identity-specific manipulation to the serialized DEs to produce the final
     /// advertisement format.
     ///
-    /// `buf` is `OVERHEAD_LEN` bytes set aside for the identity's use, followed by all of the DEs
-    /// added to a packet. It does not include the NP top level header.
+    /// `buf` has the contents of [Self::header] written at the start, followed
+    /// by all of the DEs added to a packet. It does not include the NP top level header.
+    ///
+    /// The first `header_len` bytes of `buf` are the bytes produced by the call to [Self::header].
     ///
     /// Returns `Ok` if postprocessing was successful and the packet is finished, or `Err` if
     /// postprocessing failed and the packet should be discarded.
-    fn postprocess(&self, buf: &mut [u8]) -> Result<(), Self::Error>;
+    fn postprocess(&self, header_len: usize, buf: &mut [u8]) -> Result<(), Self::Error>;
 }
 
-lazy_static::lazy_static! {
-    // Avoid either a panic-able code path or an error case that never happens by precalculating.
-    static ref PUBLIC_IDENTITY_DE_HEADER: u8 =
-        encode_de_header_actual_len(DataElementType::PublicIdentity, DeActualLength::ZERO).expect("de length is in range");
-}
+/// An unencrypted encoder with no associated identity.
+#[derive(Debug)]
+pub struct UnencryptedEncoder;
 
-impl Identity for PublicIdentity {
+impl AdvEncoder for UnencryptedEncoder {
     type Flavor = Plaintext;
-    type Error = convert::Infallible;
-    // 1 byte for public DE header (0 content)
-    const OVERHEAD_LEN: usize = 1;
+    type Error = UnencryptedEncodeError;
+    const VERSION_HEADER: u8 = VERSION_HEADER_V0_UNENCRYPTED;
 
-    fn postprocess(&self, buf: &mut [u8]) -> Result<(), Self::Error> {
-        buf[0] = *PUBLIC_IDENTITY_DE_HEADER;
+    fn header(&self) -> header::V0Header {
+        header::V0Header::unencrypted()
+    }
 
-        Ok(())
+    fn postprocess(&self, _header_len: usize, buf: &mut [u8]) -> Result<(), Self::Error> {
+        if buf.len() < NP_MIN_ADV_CONTENT_LEN {
+            Err(UnencryptedEncodeError::InvalidLength)
+        } else {
+            Ok(())
+        }
     }
 }
 
-/// Identity used for encrypted packets (private, trusted, provisioned) that encrypts other DEs
+/// Unencrypted encoding errors
+#[derive(Debug, PartialEq, Eq)]
+pub enum UnencryptedEncodeError {
+    /// The advertisement content was outside the valid range
+    InvalidLength,
+}
+
+/// Encoder used for encrypted packets (private, trusted, provisioned) that encrypts other DEs
 /// (as well as the metadata key).
-pub struct LdtIdentity<C: CryptoProvider> {
-    de_type: EncryptedIdentityDataElementType,
-    salt: ldt_np_adv::LegacySalt,
-    metadata_key: ShortMetadataKey,
-    ldt_enc: ldt_np_adv::LdtEncrypterXtsAes128<C>,
+pub struct LdtEncoder<C: CryptoProvider> {
+    salt: ldt_np_adv::V0Salt,
+    identity_token: V0IdentityToken,
+    ldt_enc: ldt_np_adv::NpLdtEncryptCipher<C>,
 }
 
 // Exclude sensitive members
-impl<C: CryptoProvider> fmt::Debug for LdtIdentity<C> {
+impl<C: CryptoProvider> fmt::Debug for LdtEncoder<C> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "LdtIdentity {{ de_type: {:?}, salt: {:X?} }}", self.de_type, self.salt)
+        write!(f, "LdtEncoder {{ salt: {:X?} }}", self.salt)
     }
 }
 
-impl<C: CryptoProvider> LdtIdentity<C> {
+impl<C: CryptoProvider> LdtEncoder<C> {
     /// Build an `LdtIdentity` for the provided identity type, salt, and
     /// broadcast crypto-materials.
-    pub fn new<B: BroadcastCryptoMaterial<V0>>(
-        de_type: EncryptedIdentityDataElementType,
-        salt: ldt_np_adv::LegacySalt,
-        crypto_material: &B,
-    ) -> Self {
-        let metadata_key = crypto_material.metadata_key();
-        let key_seed = crypto_material.key_seed();
+    pub fn new(salt: ldt_np_adv::V0Salt, broadcast_cred: &V0BroadcastCredential) -> Self {
+        let identity_token = broadcast_cred.identity_token();
+        let key_seed = broadcast_cred.key_seed();
         let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
-        let ldt_key = key_seed_hkdf.legacy_ldt_key();
-        let ldt_enc = ldt_np_adv::LdtEncrypterXtsAes128::<C>::new(&ldt_key);
+        let ldt_key = key_seed_hkdf.v0_ldt_key();
+        let ldt_enc = ldt_np_adv::NpLdtEncryptCipher::<C>::new(&ldt_key);
 
-        Self { de_type, salt, metadata_key, ldt_enc }
+        Self { salt, identity_token, ldt_enc }
     }
 }
 
-impl<C: CryptoProvider> Identity for LdtIdentity<C> {
+impl<C: CryptoProvider> AdvEncoder for LdtEncoder<C> {
     type Flavor = Ciphertext;
-    type Error = LdtPostprocessError;
-    // Identity DE header + salt + metadata key
-    const OVERHEAD_LEN: usize = 17;
+    type Error = LdtEncodeError;
+    const VERSION_HEADER: u8 = VERSION_HEADER_V0_LDT;
 
-    fn postprocess(&self, buf: &mut [u8]) -> Result<(), LdtPostprocessError> {
-        let de_type = self.de_type.as_identity_data_element_type();
-        // there's space for the identity DE header byte, but we don't count that in the DE length
-        let actual_len: DeActualLength = buf
-            .len()
-            .checked_sub(1)
-            .ok_or(LdtPostprocessError::InvalidLength)
-            .and_then(|len| len.try_into().map_err(|_e| LdtPostprocessError::InvalidLength))?;
-        // header
-        buf[0] = encode_de_header_actual_len(id_de_type_as_generic_de_type(de_type), actual_len)
-            .map_err(|_e| LdtPostprocessError::InvalidLength)?;
-        buf[1..3].copy_from_slice(self.salt.bytes().as_slice());
-        buf[3..17].copy_from_slice(&self.metadata_key.0);
+    fn header(&self) -> header::V0Header {
+        header::V0Header::ldt_short_salt(self.salt, self.identity_token)
+    }
 
-        // encrypt everything after DE header and salt
-        self.ldt_enc.encrypt(&mut buf[3..], &ldt_np_adv::salt_padder::<16, C>(self.salt)).map_err(
-            |e| match e {
-                // too short, not enough DEs -- should be caught by length validation above, though
-                ldt::LdtError::InvalidLength(_) => LdtPostprocessError::InvalidLength,
-            },
-        )
+    fn postprocess(&self, header_len: usize, buf: &mut [u8]) -> Result<(), LdtEncodeError> {
+        // encrypt everything after v0 format and salt
+        self.ldt_enc
+            .encrypt(
+                &mut buf[header_len - V0_IDENTITY_TOKEN_LEN..],
+                &ldt_np_adv::salt_padder::<C>(self.salt),
+            )
+            .map_err(|e| match e {
+                // too short, not enough DEs
+                ldt::LdtError::InvalidLength(_) => LdtEncodeError::InvalidLength,
+            })
     }
 }
 
-/// Something went wrong, or preconditions were not met, during identity postprocessing.
+/// LDT encoding errors
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum LdtPostprocessError {
-    /// The minimum size (2 bytes) or maximum size (6 bytes) of DE payload was not met
+pub enum LdtEncodeError {
+    /// The minimum size (2 bytes) or maximum size (7 bytes for BLE 4.2
+    /// advertisements) of DE data was not met
     InvalidLength,
 }
 
+/// The serialized form of a V0 adv, suitable for setting as the value for the NP UUID svc data.
+pub type SerializedAdv = ArrayView<u8, BLE_4_ADV_SVC_MAX_CONTENT_LEN>;
+
 /// Accumulates DEs, then massages the serialized DEs with the configured identity to produce
 /// the final advertisement.
 #[derive(Debug)]
-pub struct AdvBuilder<I: Identity> {
+pub struct AdvBuilder<E: AdvEncoder> {
     /// The first byte is for the adv header, then the next I::OVERHEAD_LEN bytes are set aside for
-    /// use by [Identity::postprocess].
-    buffer: [u8; BLE_ADV_SVC_CONTENT_LEN],
+    /// use by [AdvEncoder::postprocess].
+    buffer: [u8; BLE_4_ADV_SVC_MAX_CONTENT_LEN],
     /// How much of the buffer is consumed.
-    /// Always <= buffer length
+    /// Always <= buffer length, and at least 2
     len: usize,
-    identity: I,
+    /// How long the header was
+    header_len: usize,
+    encoder: E,
 }
 
-impl<I: Identity> AdvBuilder<I> {
+impl<E: AdvEncoder> AdvBuilder<E> {
     /// Create an empty AdvBuilder with the provided identity.
-    pub fn new(identity: I) -> AdvBuilder<I> {
-        // adv header + identity overhead
-        let len = 1 + I::OVERHEAD_LEN;
+    pub fn new(encoder: E) -> AdvBuilder<E> {
+        let mut buffer = [0; BLE_4_ADV_SVC_MAX_CONTENT_LEN];
+        buffer[0] = E::VERSION_HEADER;
+
+        // encode the rest of the V0 header
+        let header = encoder.header();
+        let header_len = header.as_slice().len();
+        // len will be at least 1, important for max_de_len safety in add_data_element
+        let len = 1 + header_len;
         // check for broken identities
-        assert!(len < BLE_ADV_SVC_CONTENT_LEN);
+        debug_assert!(len < buffer.len());
+        buffer[1..=header_len].copy_from_slice(header.as_slice());
+
         AdvBuilder {
             // conveniently the first byte is already 0, which is the correct header for v0.
             // 3 bit version (000 since this is version 0), 5 bit reserved (also all 0)
-            buffer: [0; BLE_ADV_SVC_CONTENT_LEN],
+            buffer,
             len,
-            identity,
+            header_len,
+            encoder,
         }
     }
 
     /// Add the data element to the packet buffer, if there is space.
-    pub fn add_data_element<B: ToDataElementBundle<I::Flavor>>(
+    pub fn add_data_element<D: SerializeDataElement<E::Flavor>>(
         &mut self,
-        data_element: B,
+        data_element: D,
     ) -> Result<(), AddDataElementError> {
-        let remaining = self.buffer.len() - self.len;
-        let bundle = data_element.to_de_bundle();
-        // length including header byte
-        let de_slice = bundle.contents_as_slice();
-        let de_total_len = 1 + de_slice.len();
-        if remaining < de_total_len {
-            return Err(AddDataElementError::InsufficientAdvSpace);
-        }
+        // invariant: self.len <= buffer length
+        debug_assert!(self.len <= self.buffer.len());
+        let dest = &mut self.buffer[self.len..];
+        // because self.len is at least 1, the dest will be no more than
+        // `[BLE_ADV_SVC_CONTENT_LEN] - 1 = [NP_MAX_ADV_CONTENT_LEN]`
+        let de_buf = serialize_de(&data_element, dest.len())?;
+        dest[..de_buf.len()].copy_from_slice(de_buf.as_slice());
+        // invariant: de_buf fit in the remaining space, so adding its length is safe
+        self.len += de_buf.len();
+        debug_assert!(self.len <= self.buffer.len());
 
-        // header
-        self.buffer[self.len] =
-            encode_de_header(bundle.de_type.as_generic_de_type(), bundle.encoded_len);
-        self.len += 1;
-        // de contents
-        self.buffer[self.len..self.len + de_slice.len()].copy_from_slice(de_slice);
-        self.len += de_slice.len();
         Ok(())
     }
 
-    /// Return the finished advertisement (adv header + DEs), or `None` if the adv could not be
-    /// built.
-    pub fn into_advertisement(
-        mut self,
-    ) -> Result<ArrayView<u8, BLE_ADV_SVC_CONTENT_LEN>, I::Error> {
+    /// Return the finished advertisement (version header || V0 header || DEs),
+    /// or `None` if the adv could not be built.
+    pub fn into_advertisement(mut self) -> Result<SerializedAdv, E::Error> {
         // encrypt, if applicable
-        self.identity
-            // skip adv header for postprocessing
-            .postprocess(&mut self.buffer[1..self.len])
+        self.encoder
+            // skip NP version header for postprocessing
+            .postprocess(self.header_len, &mut self.buffer[1..self.len])
             .map(|_| ArrayView::try_from_array(self.buffer, self.len).expect("len is always valid"))
     }
 }
@@ -271,131 +288,67 @@
     InsufficientAdvSpace,
 }
 
-/// The serialized form of a data element.
-#[derive(Clone, Debug)]
-pub struct DataElementBundle<F: PacketFlavor> {
-    de_type: PlainDataElementType,
-    /// Data element payload
-    data: DePayload,
-    /// The header-encoded form of `data`'s length
-    encoded_len: DeEncodedLength,
-    /// Type marker for whether this DE can be used in encrypted or plaintext packets.
-    flavor: marker::PhantomData<F>,
+impl From<DataElementSerializeError> for AddDataElementError {
+    fn from(value: DataElementSerializeError) -> Self {
+        match value {
+            DataElementSerializeError::InsufficientSpace => Self::InsufficientAdvSpace,
+        }
+    }
 }
 
-impl<F: PacketFlavor> DataElementBundle<F> {
-    /// Returns `Err` if the provided `data` or requested [PacketFlavor] are not valid for
-    /// `de_type`.
-    pub(crate) fn try_from(
-        de_type: PlainDataElementType,
-        data: &[u8],
-    ) -> Result<DataElementBundle<F>, DeBundleError> {
-        if !de_type.supports_flavor(F::ENUM_VARIANT) {
-            return Err(DeBundleError::InvalidFlavor);
-        }
+/// Encode a DE type and length into a DE header byte.
+pub(crate) fn encode_de_header(code: DeTypeCode, header_len: DeEncodedLength) -> u8 {
+    // 4 high bits are length, 4 low bits are type
+    (header_len.as_u8() << 4) | code.as_u8()
+}
 
-        let mut buffer = [0; NP_MAX_DE_CONTENT_LEN];
-        buffer
-            .get_mut(0..data.len())
-            .map(|dest| dest.copy_from_slice(data))
-            .ok_or(DeBundleError::InvalidLength)?;
+/// Encode a DE into a buffer.
+///
+/// The buffer will contain the DE header and DE contents, if any, and will
+/// not exceed `max_de_len`.
+///
+/// # Panics
+/// `max_de_len` must be no larger than [NP_MAX_ADV_CONTENT_LEN].
+fn serialize_de<F: PacketFlavor, D: SerializeDataElement<F>>(
+    data_element: &D,
+    max_de_len: usize,
+) -> Result<SerializedDataElement, AddDataElementError> {
+    let mut de_buf = DataElementSerializationBuffer::new(max_de_len)
+        .expect("max_de_len must not exceed NP_MAX_DE_CONTENT_LEN");
 
-        let payload: DePayload = ArrayView::try_from_array(buffer, data.len())
-            .expect("data already copied into buffer")
-            .into();
+    // placeholder for header
+    de_buf.try_push(0).ok_or(AddDataElementError::InsufficientAdvSpace)?;
+    data_element.serialize_contents(&mut de_buf)?;
 
-        // get the encoded length now so we know the length is valid for the DE type
-        let encoded_len = de_type
-            .as_generic_de_type()
-            .encoded_len_for_actual_len(payload.len())
-            .map_err(|_| DeBundleError::InvalidLength)?;
+    let encoded_len = data_element.map_actual_len_to_encoded_len(
+        DeActualLength::try_from(de_buf.len() - 1)
+            .expect("DE fit in buffer after header, so it should be a valid size"),
+    );
 
-        Ok(DataElementBundle { de_type, data: payload, encoded_len, flavor: marker::PhantomData })
+    let mut vec = de_buf.into_inner().into_inner();
+    vec[0] = encode_de_header(data_element.de_type_code(), encoded_len);
+
+    debug_assert!(vec.len() <= max_de_len);
+
+    Ok(SerializedDataElement::new(to_array_view(vec)))
+}
+
+/// The serialized form of a data element, including its header.
+pub(crate) struct SerializedDataElement {
+    data: ArrayView<u8, NP_MAX_ADV_CONTENT_LEN>,
+}
+
+impl SerializedDataElement {
+    fn new(data: ArrayView<u8, NP_MAX_ADV_CONTENT_LEN>) -> Self {
+        Self { data }
     }
 
-    /// The data contained in the DE, excluding the header byte
-    pub(crate) fn contents_as_slice(&self) -> &[u8] {
+    /// The serialized DE, starting with the DE header byte
+    pub(crate) fn as_slice(&self) -> &[u8] {
         self.data.as_slice()
     }
-}
 
-/// Errors that can occur when building a [DataElementBundle]
-#[derive(Debug, PartialEq, Eq)]
-pub(crate) enum DeBundleError {
-    /// The DE type does not support the requested flavor
-    InvalidFlavor,
-    /// The data is too long to fit in a DE, or is invalid for the DE type
-    InvalidLength,
-}
-
-/// Implemented by higher-level types that represent data elements.
-pub trait ToDataElementBundle<F: PacketFlavor> {
-    /// Serialize `self` into the impl-specific byte encoding used for that DE type.
-    fn to_de_bundle(&self) -> DataElementBundle<F>;
-}
-
-// for cases where it's more convenient to already have a DataElementBundle than a DE
-impl<F: PacketFlavor> ToDataElementBundle<F> for DataElementBundle<F> {
-    fn to_de_bundle(&self) -> DataElementBundle<F> {
-        Self {
-            de_type: self.de_type,
-            data: self.data.clone(),
-            encoded_len: self.encoded_len,
-            flavor: marker::PhantomData,
-        }
-    }
-}
-
-// Biggest size a DE could possibly be is the entire payload
-#[derive(Clone, PartialEq, Eq, Debug)]
-struct DePayload {
-    payload: ArrayView<u8, { NP_MAX_DE_CONTENT_LEN }>,
-}
-
-impl DePayload {
-    /// The actual length of the payload
-    fn len(&self) -> DeActualLength {
-        self.payload
-            .len()
-            .try_into()
-            .expect("Payload is an array of the max size which always has a valid length")
-    }
-
-    fn as_slice(&self) -> &[u8] {
-        self.payload.as_slice()
-    }
-}
-
-impl From<ArrayView<u8, { NP_MAX_DE_CONTENT_LEN }>> for DePayload {
-    fn from(array: ArrayView<u8, { NP_MAX_DE_CONTENT_LEN }>) -> Self {
-        Self { payload: array }
-    }
-}
-
-/// Encode a DE type and length into a DE header byte.
-pub(crate) fn encode_de_header(de_type: DataElementType, header_len: DeEncodedLength) -> u8 {
-    // 4 high bits are length, 4 low bits are type
-    (header_len.as_u8() << 4) | de_type.type_code().as_u8()
-}
-
-/// Encode a DE type and length into a DE header byte.
-pub(crate) fn encode_de_header_actual_len(
-    de_type: DataElementType,
-    header_len: DeActualLength,
-) -> Result<u8, DeLengthOutOfRange> {
-    de_type
-        .encoded_len_for_actual_len(header_len)
-        // 4 high bits are length, 4 low bits are type
-        .map(|len| encode_de_header(de_type, len))
-}
-
-pub(crate) fn id_de_type_as_generic_de_type(
-    id_de_type: IdentityDataElementType,
-) -> DataElementType {
-    match id_de_type {
-        IdentityDataElementType::Private => DataElementType::PrivateIdentity,
-        IdentityDataElementType::Trusted => DataElementType::TrustedIdentity,
-        IdentityDataElementType::Public => DataElementType::PublicIdentity,
-        IdentityDataElementType::Provisioned => DataElementType::ProvisionedIdentity,
+    pub(crate) fn len(&self) -> usize {
+        self.data.len()
     }
 }
diff --git a/nearby/presence/np_adv/src/legacy/serialize/tests.rs b/nearby/presence/np_adv/src/legacy/serialize/tests.rs
deleted file mode 100644
index 7be30ba..0000000
--- a/nearby/presence/np_adv/src/legacy/serialize/tests.rs
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#![allow(clippy::unwrap_used)]
-
-extern crate std;
-
-use crate::legacy::actions::FastPairSass;
-use crate::legacy::actions::NearbyShare;
-use crate::{
-    credential::{v0::V0, SimpleBroadcastCryptoMaterial},
-    de_type::EncryptedIdentityDataElementType,
-    legacy::{actions::*, data_elements::*, serialize::*},
-    shared_data::TxPower,
-};
-use crypto_provider_default::CryptoProviderImpl;
-use ldt_np_adv::{salt_padder, LdtEncrypterXtsAes128, LegacySalt};
-use std::vec;
-
-#[test]
-fn public_identity_packet_serialization() {
-    let mut builder = AdvBuilder::new(PublicIdentity);
-
-    let tx_power = TxPower::try_from(3).unwrap();
-    let mut action = ActionBits::default();
-    action.set_action(NearbyShare::from(true));
-    builder.add_data_element(TxPowerDataElement::from(tx_power)).unwrap();
-    builder.add_data_element(ActionsDataElement::from(action)).unwrap();
-
-    let packet = builder.into_advertisement().unwrap();
-    assert_eq!(
-        &[
-            0x00, // Adv Header
-            0x03, // Public DE header
-            0x15, 0x03, // Tx Power DE with value 3
-            0x26, 0x00, 0x40, // Actions DE w/ bit 9
-        ],
-        packet.as_slice()
-    );
-}
-
-#[test]
-fn packet_limits_capacity() {
-    let mut builder = AdvBuilder::new(PublicIdentity);
-    // 2 + 1 left out of 24 payload bytes
-    builder.len = 21;
-    let mut bits = ActionBits::default();
-    bits.set_action(NearbyShare::from(true));
-    bits.set_action(FastPairSass::from(true));
-
-    assert_eq!(Ok(()), builder.add_data_element(ActionsDataElement::from(bits)));
-
-    // too small for 2+ 1 DE
-    builder.len = 22;
-    assert_eq!(
-        Err(AddDataElementError::InsufficientAdvSpace),
-        builder.add_data_element(ActionsDataElement::from(bits))
-    );
-}
-
-#[test]
-fn ldt_packet_serialization() {
-    // don't care about the HMAC since we're not decrypting
-    let key_seed = [0; 32];
-    let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-    let ldt = LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(&hkdf.legacy_ldt_key());
-    let metadata_key = ShortMetadataKey([0x33; 14]);
-    let salt = LegacySalt::from([0x01, 0x02]);
-
-    let mut ciphertext = vec![];
-    ciphertext.extend_from_slice(&metadata_key.0);
-    // tx power & action DEs
-    ciphertext.extend_from_slice(&[0x15, 0x03, 0x26, 0x00, 0x10]);
-    ldt.encrypt(&mut ciphertext, &salt_padder::<16, CryptoProviderImpl>(salt)).unwrap();
-
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, metadata_key);
-
-    let mut builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
-        EncryptedIdentityDataElementType::Private,
-        salt,
-        &broadcast_cm,
-    ));
-
-    let tx_power = TxPower::try_from(3).unwrap();
-    let mut action = ActionBits::default();
-    action.set_action(PhoneHub::from(true));
-    builder.add_data_element(TxPowerDataElement::from(tx_power)).unwrap();
-    builder.add_data_element(ActionsDataElement::from(action)).unwrap();
-
-    let packet = builder.into_advertisement().unwrap();
-    // header
-    let mut expected = vec![0x00];
-    // private header with five bytes after it
-    expected.push(0x51);
-    expected.extend_from_slice(salt.bytes());
-    expected.extend_from_slice(&ciphertext);
-    assert_eq!(&expected, packet.as_slice());
-}
-
-#[test]
-fn ldt_packet_cant_encrypt_without_des() {
-    let metadata_key = ShortMetadataKey([0x33; 14]);
-    let salt = LegacySalt::from([0x01, 0x02]);
-    let key_seed = [0xFE; 32];
-
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, metadata_key);
-
-    let builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
-        EncryptedIdentityDataElementType::Private,
-        salt,
-        &broadcast_cm,
-    ));
-
-    // not enough ciphertext
-    assert_eq!(Err(LdtPostprocessError::InvalidLength), builder.into_advertisement());
-}
-
-#[test]
-fn nearby_share_action() {
-    let mut builder = AdvBuilder::new(PublicIdentity);
-
-    let mut action = ActionBits::default();
-    action.set_action(NearbyShare::from(true));
-
-    let actions_de = ActionsDataElement::from(action);
-    builder.add_data_element(actions_de).unwrap();
-
-    let tx_power_de = TxPowerDataElement::from(TxPower::try_from(-100).unwrap());
-    builder.add_data_element(tx_power_de).unwrap();
-
-    assert_eq!(
-        &[
-            0x00, // version 0
-            0x03, // public identity
-            0x26, // length 2, DE type 6 for actions
-            0x00, 0x40, // bit 9 is active
-            0x15, // length 1, DE type 5 for tx power
-            0x9C, // tx power of -100
-        ],
-        builder.into_advertisement().unwrap().as_slice()
-    );
-}
diff --git a/nearby/presence/np_adv/src/legacy/serialize/tests/error_conditions.rs b/nearby/presence/np_adv/src/legacy/serialize/tests/error_conditions.rs
new file mode 100644
index 0000000..572276e
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/serialize/tests/error_conditions.rs
@@ -0,0 +1,193 @@
+// 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.
+
+mod unencrypted_encoder {
+    use alloc::vec;
+    use core::ops::Deref;
+
+    extern crate std;
+
+    use std::prelude::rust_2021::*;
+
+    use crate::legacy::serialize::tests::helpers::{LongDataElement, ShortDataElement};
+    use crate::legacy::serialize::{
+        AddDataElementError, AdvBuilder, UnencryptedEncodeError, UnencryptedEncoder,
+    };
+    use crate::legacy::{BLE_4_ADV_SVC_MAX_CONTENT_LEN, NP_MAX_DE_CONTENT_LEN};
+
+    #[test]
+    fn build_empty_adv_error() {
+        // empty isn't allowed, so 1 byte of payload is the smallest possible
+        let builder = AdvBuilder::new(UnencryptedEncoder);
+
+        assert_eq!(
+            UnencryptedEncodeError::InvalidLength,
+            builder.into_advertisement().unwrap_err()
+        );
+    }
+
+    #[test]
+    fn add_de_when_full_error() {
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+
+        builder
+            .add_data_element(LongDataElement::new(vec![1; NP_MAX_DE_CONTENT_LEN].clone()))
+            .unwrap();
+
+        // 1 more byte (DE header) is too much
+        assert_eq!(
+            AddDataElementError::InsufficientAdvSpace,
+            builder.add_data_element(ShortDataElement::new(vec![].clone())).unwrap_err()
+        );
+    }
+
+    #[test]
+    fn add_too_much_de_when_almost_full_error() {
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+
+        // leave 1 byte of room
+        builder
+            .add_data_element(LongDataElement::new(vec![1; NP_MAX_DE_CONTENT_LEN - 1].clone()))
+            .unwrap();
+
+        // DE header would fit, but 1 byte of DE content is too much
+        assert_eq!(BLE_4_ADV_SVC_MAX_CONTENT_LEN - 1, builder.len);
+        assert_eq!(
+            AddDataElementError::InsufficientAdvSpace,
+            builder.add_data_element(ShortDataElement::new(vec![2].clone())).unwrap_err()
+        );
+    }
+
+    #[test]
+    fn broken_de_impl_hits_expected_panic() {
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+
+        let panic_payload = std::panic::catch_unwind(move || {
+            // This DE type can't represent short lengths
+            builder.add_data_element(LongDataElement::new(vec![]))
+        })
+        .unwrap_err();
+
+        assert_eq!(
+            "Couldn't encode actual len: DeLengthOutOfRange",
+            panic_payload.downcast::<String>().unwrap().deref()
+        );
+    }
+}
+
+mod ldt_encoder {
+    use alloc::string::String;
+    use alloc::vec;
+    use core::ops::Deref;
+
+    extern crate std;
+
+    use crypto_provider_default::CryptoProviderImpl;
+    use ldt_np_adv::{V0IdentityToken, V0Salt};
+
+    use crate::credential::v0::V0BroadcastCredential;
+    use crate::legacy::serialize::tests::helpers::{LongDataElement, ShortDataElement};
+    use crate::legacy::serialize::{AddDataElementError, AdvBuilder, LdtEncodeError, LdtEncoder};
+    use crate::legacy::BLE_4_ADV_SVC_MAX_CONTENT_LEN;
+
+    #[test]
+    fn build_empty_adv_error() {
+        let identity_token = V0IdentityToken::from([0x33; 14]);
+        let salt = V0Salt::from([0x01, 0x02]);
+        let key_seed = [0xFE; 32];
+        let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
+
+        let builder = AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred));
+
+        // not enough ciphertext
+        assert_eq!(Err(LdtEncodeError::InvalidLength), builder.into_advertisement());
+    }
+
+    #[test]
+    fn build_adv_one_byte_error() {
+        let identity_token = V0IdentityToken::from([0x33; 14]);
+        let salt = V0Salt::from([0x01, 0x02]);
+        let key_seed = [0xFE; 32];
+        let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
+
+        let mut builder =
+            AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred));
+        // 1 byte of DE header
+        builder.add_data_element(ShortDataElement::new(vec![])).unwrap();
+
+        // not enough ciphertext
+        assert_eq!(Err(LdtEncodeError::InvalidLength), builder.into_advertisement());
+    }
+
+    #[test]
+    fn add_de_when_full_error() {
+        let identity_token = V0IdentityToken::from([0x33; 14]);
+        let salt = V0Salt::from([0x01, 0x02]);
+        let key_seed = [0xFE; 32];
+        let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
+
+        let mut builder =
+            AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred));
+        // 7 bytes will fill it
+        builder.add_data_element(ShortDataElement::new(vec![1; 6])).unwrap();
+
+        // 1 more byte is too many
+        assert_eq!(
+            AddDataElementError::InsufficientAdvSpace,
+            builder.add_data_element(ShortDataElement::new(vec![])).unwrap_err()
+        );
+    }
+
+    #[test]
+    fn add_too_much_de_when_almost_full_error() {
+        let identity_token = V0IdentityToken::from([0x33; 14]);
+        let salt = V0Salt::from([0x01, 0x02]);
+        let key_seed = [0xFE; 32];
+        let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
+
+        let mut builder =
+            AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred));
+        // 6 bytes will leave 1 byte
+        builder.add_data_element(ShortDataElement::new(vec![1; 5])).unwrap();
+
+        // 1 byte would fit, but 2 won't
+        assert_eq!(BLE_4_ADV_SVC_MAX_CONTENT_LEN - 1, builder.len);
+        assert_eq!(
+            AddDataElementError::InsufficientAdvSpace,
+            builder.add_data_element(ShortDataElement::new(vec![2])).unwrap_err()
+        );
+    }
+
+    #[test]
+    fn broken_de_impl_hits_expected_panic() {
+        let identity_token = V0IdentityToken::from([0x33; 14]);
+        let salt = V0Salt::from([0x01, 0x02]);
+        let key_seed = [0xFE; 32];
+        let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
+
+        let mut builder =
+            AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred));
+
+        let panic_payload = std::panic::catch_unwind(move || {
+            // This DE type can't represent short lengths
+            builder.add_data_element(LongDataElement::new(vec![]))
+        })
+        .unwrap_err();
+
+        assert_eq!(
+            "Couldn't encode actual len: DeLengthOutOfRange",
+            panic_payload.downcast::<String>().unwrap().deref()
+        );
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/serialize/tests/happy_path.rs b/nearby/presence/np_adv/src/legacy/serialize/tests/happy_path.rs
new file mode 100644
index 0000000..05fc20a
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/serialize/tests/happy_path.rs
@@ -0,0 +1,244 @@
+// 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.
+
+mod unencrypted_encoder {
+    use alloc::vec;
+
+    use crate::header::VERSION_HEADER_V0_UNENCRYPTED;
+    use crate::legacy::data_elements::actions::{ActionBits, ActionsDataElement, NearbyShare};
+    use crate::legacy::data_elements::tx_power::TxPowerDataElement;
+    use crate::legacy::serialize::tests::helpers::{LongDataElement, ShortDataElement};
+    use crate::legacy::serialize::{AdvBuilder, UnencryptedEncoder};
+    use crate::legacy::{
+        BLE_4_ADV_SVC_MAX_CONTENT_LEN, NP_MAX_ADV_CONTENT_LEN, NP_MAX_DE_CONTENT_LEN,
+    };
+    use crate::shared_data::TxPower;
+
+    #[test]
+    fn adv_min_size() {
+        // empty isn't allowed, so 1 byte of payload is the smallest possible
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+
+        builder.add_data_element(ShortDataElement::new(vec![])).unwrap();
+        let adv = builder.into_advertisement().unwrap();
+        assert_eq!(&[VERSION_HEADER_V0_UNENCRYPTED, 0x0E], adv.as_slice());
+    }
+
+    #[test]
+    fn adv_max_size_multi_de() {
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+
+        builder.add_data_element(ShortDataElement::new(vec![1; 10])).unwrap();
+        // max len after previous DE & headers
+        let de2_contents = vec![2; NP_MAX_ADV_CONTENT_LEN - 1 - 10 - 1];
+        builder.add_data_element(LongDataElement::new(de2_contents.clone())).unwrap();
+        let adv = builder.into_advertisement().unwrap();
+        assert_eq!(BLE_4_ADV_SVC_MAX_CONTENT_LEN, adv.len());
+        assert_eq!(
+            &[
+                [VERSION_HEADER_V0_UNENCRYPTED, 0xAE].as_slice(),
+                [1; 10].as_slice(),
+                &[0x4F],
+                &de2_contents
+            ]
+            .concat(),
+            adv.as_slice()
+        );
+    }
+
+    #[test]
+    fn adv_max_size_single_de() {
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+
+        builder
+            .add_data_element(LongDataElement::new(vec![1; NP_MAX_DE_CONTENT_LEN].clone()))
+            .unwrap();
+        let adv = builder.into_advertisement().unwrap();
+        assert_eq!(BLE_4_ADV_SVC_MAX_CONTENT_LEN, adv.len());
+        assert_eq!(
+            &[[VERSION_HEADER_V0_UNENCRYPTED, 0xFF].as_slice(), &[1; NP_MAX_DE_CONTENT_LEN],]
+                .concat(),
+            adv.as_slice()
+        );
+    }
+
+    #[rustfmt::skip]
+    #[test]
+    fn typical_tx_power_and_actions() {
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+        builder.add_data_element(TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
+
+        let mut action = ActionBits::default();
+        action.set_action(NearbyShare::from(true));
+        builder.add_data_element(ActionsDataElement::from(action)).unwrap();
+
+        let packet = builder.into_advertisement().unwrap();
+        assert_eq!(
+            &[
+                VERSION_HEADER_V0_UNENCRYPTED,
+                0x15, 0x03, // Tx Power DE with value 3
+                0x26, 0x00, 0x40, // Actions DE w/ bit 9
+            ],
+            packet.as_slice()
+        );
+    }
+}
+
+mod ldt_encoder {
+    use alloc::vec;
+
+    use crypto_provider_default::CryptoProviderImpl;
+    use ldt::LdtCipher;
+    use ldt_np_adv::{
+        salt_padder, NpLdtEncryptCipher, V0IdentityToken, V0Salt, V0_IDENTITY_TOKEN_LEN,
+    };
+
+    use crate::credential::v0::V0BroadcastCredential;
+    use crate::header::VERSION_HEADER_V0_LDT;
+    use crate::legacy::data_elements::actions::tests::LastBit;
+    use crate::legacy::data_elements::actions::{ActionBits, ActionsDataElement, PhoneHub};
+    use crate::legacy::data_elements::tx_power::TxPowerDataElement;
+    use crate::legacy::serialize::tests::helpers::ShortDataElement;
+    use crate::legacy::serialize::{AdvBuilder, LdtEncoder, SerializedAdv};
+    use crate::legacy::BLE_4_ADV_SVC_MAX_CONTENT_LEN;
+    use crate::shared_data::TxPower;
+
+    #[test]
+    fn adv_min_size() {
+        // need at least 2 bytes to make 1 full block
+        let _ = assert_ldt_adv(&[0x1E, 0xAA], |builder| {
+            builder.add_data_element(ShortDataElement::new(vec![0xAA])).unwrap();
+        });
+    }
+
+    #[test]
+    fn adv_max_size_multi_de() {
+        let adv = assert_ldt_adv(&[0x15, 0x03, 0x4E, 0x01, 0x01, 0x01, 0x01], |builder| {
+            builder
+                .add_data_element(TxPowerDataElement::from(TxPower::try_from(3).unwrap()))
+                .unwrap();
+            builder.add_data_element(ShortDataElement::new(vec![1; 4])).unwrap();
+        });
+        assert_eq!(BLE_4_ADV_SVC_MAX_CONTENT_LEN, adv.len());
+    }
+
+    #[test]
+    fn adv_max_size_single_de() {
+        let adv = assert_ldt_adv(&[0x6E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01], |builder| {
+            builder.add_data_element(ShortDataElement::new(vec![1; 6])).unwrap();
+        });
+        assert_eq!(BLE_4_ADV_SVC_MAX_CONTENT_LEN, adv.len());
+    }
+
+    #[test]
+    fn adv_tx_power_max_length_actions() {
+        let adv = assert_ldt_adv(&[0x15, 0x03, 0x36, 0x00, 0x00, 0x01], |builder| {
+            builder
+                .add_data_element(TxPowerDataElement::from(TxPower::try_from(3).unwrap()))
+                .unwrap();
+            let mut action = ActionBits::default();
+            action.set_action(LastBit::from(true));
+            builder.add_data_element(ActionsDataElement::from(action)).unwrap();
+        });
+        // TODO this should be the max adv size once action size gets increased
+        assert_eq!(BLE_4_ADV_SVC_MAX_CONTENT_LEN - 1, adv.len());
+    }
+
+    #[test]
+    fn adv_typical_tx_power_and_actions() {
+        let _ = assert_ldt_adv(&[0x15, 0x03, 0x26, 0x00, 0x10], |builder| {
+            builder
+                .add_data_element(TxPowerDataElement::from(TxPower::try_from(3).unwrap()))
+                .unwrap();
+            let mut action = ActionBits::default();
+            action.set_action(PhoneHub::from(true));
+            builder.add_data_element(ActionsDataElement::from(action)).unwrap();
+        });
+    }
+
+    /// Build an LDT advertisement using the DEs provided by `add_des`, and assert that the resulting
+    /// advertisement matches the result of encrypting `identity token || after_identity_token`
+    fn assert_ldt_adv(
+        after_identity_token: &[u8],
+        add_des: impl Fn(&mut AdvBuilder<LdtEncoder<CryptoProviderImpl>>),
+    ) -> SerializedAdv {
+        let key_seed = [0; 32];
+        let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+        let ldt = NpLdtEncryptCipher::<CryptoProviderImpl>::new(&hkdf.v0_ldt_key());
+        let identity_token = V0IdentityToken::from([0x33; V0_IDENTITY_TOKEN_LEN]);
+        let salt = V0Salt::from([0x01, 0x02]);
+
+        let mut plaintext = identity_token.as_slice().to_vec();
+        plaintext.extend_from_slice(after_identity_token);
+        ldt.encrypt(&mut plaintext, &salt_padder::<CryptoProviderImpl>(salt)).unwrap();
+        let ciphertext = plaintext;
+
+        let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
+
+        let mut builder =
+            AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred));
+        add_des(&mut builder);
+
+        let adv = builder.into_advertisement().unwrap();
+
+        let mut expected = vec![VERSION_HEADER_V0_LDT];
+        expected.extend_from_slice(&salt.bytes());
+        expected.extend_from_slice(&ciphertext);
+        assert_eq!(&expected, adv.as_slice());
+        adv
+    }
+}
+
+mod tx_de {
+    use crate::header::VERSION_HEADER_V0_UNENCRYPTED;
+    use crate::legacy::data_elements::tx_power::TxPowerDataElement;
+    use crate::legacy::serialize::{AdvBuilder, UnencryptedEncoder};
+    use crate::shared_data::TxPower;
+
+    #[test]
+    fn tx_power_produces_1_byte() {
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+
+        builder.add_data_element(TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
+        let adv = builder.into_advertisement().unwrap();
+        assert_eq!(&[VERSION_HEADER_V0_UNENCRYPTED, 0x15, 0x03], adv.as_slice());
+    }
+}
+
+mod actions_de {
+    use crate::header::VERSION_HEADER_V0_UNENCRYPTED;
+    use crate::legacy::data_elements::actions::tests::LastBit;
+    use crate::legacy::data_elements::actions::{ActionBits, ActionsDataElement};
+    use crate::legacy::serialize::{AdvBuilder, UnencryptedEncoder};
+
+    #[test]
+    fn no_bits_set_produces_1_byte() {
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+
+        builder.add_data_element(ActionsDataElement::from(ActionBits::default())).unwrap();
+        let adv = builder.into_advertisement().unwrap();
+        assert_eq!(&[VERSION_HEADER_V0_UNENCRYPTED, 0x16, 0x00], adv.as_slice());
+    }
+
+    #[test]
+    fn lowest_bit_set_produces_max_len() {
+        let mut builder = AdvBuilder::new(UnencryptedEncoder);
+
+        let mut bits = ActionBits::default();
+        bits.set_action(LastBit::from(true));
+        builder.add_data_element(ActionsDataElement::from(bits)).unwrap();
+        let adv = builder.into_advertisement().unwrap();
+        assert_eq!(&[VERSION_HEADER_V0_UNENCRYPTED, 0x36, 0x00, 0x00, 0x01], adv.as_slice());
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/serialize/tests/helpers.rs b/nearby/presence/np_adv/src/legacy/serialize/tests/helpers.rs
new file mode 100644
index 0000000..ea0c886
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/serialize/tests/helpers.rs
@@ -0,0 +1,176 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+extern crate std;
+
+use std::prelude::rust_2021::*;
+
+use sink::Sink;
+
+use crate::legacy::data_elements::{
+    DataElementDeserializeError, DeserializeDataElement, DirectMapPredicate, DirectMapper,
+    LengthMapper,
+};
+use crate::{
+    legacy::{
+        data_elements::{
+            de_type::{DeActualLength, DeEncodedLength, DeTypeCode, MAX_DE_ENCODED_LEN},
+            DataElementSerializationBuffer, DataElementSerializeError, SerializeDataElement,
+        },
+        PacketFlavor, NP_MAX_DE_CONTENT_LEN,
+    },
+    private::Sealed,
+    DeLengthOutOfRange,
+};
+
+/// A DE allowing arbitrary data lengths. Encoded length is offset from actual length.
+///
+/// Data longer than [NP_MAX_DE_CONTENT_LEN] or shorter than [NP_MAX_DE_CONTENT_LEN] - [MAX_DE_ENCODED_LEN]
+/// will fail to map the actual length.
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub(crate) struct LongDataElement {
+    data: Vec<u8>,
+}
+
+impl LongDataElement {
+    /// The offset lengths are shifted by when encoded
+    // `as usize` is safe since all u8s can fit. Can't use .into() or ::from() in a const.
+    pub(in crate::legacy) const OFFSET: usize =
+        NP_MAX_DE_CONTENT_LEN - (MAX_DE_ENCODED_LEN as usize);
+    pub fn new(data: Vec<u8>) -> Self {
+        Self { data }
+    }
+}
+
+impl Sealed for LongDataElement {}
+
+impl<F: PacketFlavor> SerializeDataElement<F> for LongDataElement {
+    fn de_type_code(&self) -> DeTypeCode {
+        Self::DE_TYPE_CODE
+    }
+
+    fn map_actual_len_to_encoded_len(&self, actual_len: DeActualLength) -> DeEncodedLength {
+        <Self as DeserializeDataElement>::LengthMapper::map_actual_len_to_encoded_len(actual_len)
+    }
+
+    fn serialize_contents(
+        &self,
+        sink: &mut DataElementSerializationBuffer,
+    ) -> Result<(), DataElementSerializeError> {
+        sink.try_extend_from_slice(&self.data).ok_or(DataElementSerializeError::InsufficientSpace)
+    }
+}
+
+impl DeserializeDataElement for LongDataElement {
+    const DE_TYPE_CODE: DeTypeCode = match DeTypeCode::try_from(15) {
+        Ok(t) => t,
+        Err(_) => unreachable!(),
+    };
+    type LengthMapper = OffsetMapper<{ Self::OFFSET }>;
+
+    fn deserialize<F: PacketFlavor>(
+        de_contents: &[u8],
+    ) -> Result<Self, DataElementDeserializeError> {
+        Ok(Self { data: de_contents.to_vec() })
+    }
+}
+
+impl From<Vec<u8>> for LongDataElement {
+    fn from(value: Vec<u8>) -> Self {
+        Self { data: value }
+    }
+}
+
+/// Subtracts `O` from actual lengths to yield encoded lengths, and vice versa.
+pub(in crate::legacy) struct OffsetMapper<const O: usize>;
+
+impl<const O: usize> LengthMapper for OffsetMapper<O> {
+    fn map_encoded_len_to_actual_len(
+        encoded_len: DeEncodedLength,
+    ) -> Result<DeActualLength, DeLengthOutOfRange> {
+        DeActualLength::try_from(encoded_len.as_usize() + O)
+    }
+
+    fn map_actual_len_to_encoded_len(actual_len: DeActualLength) -> DeEncodedLength {
+        actual_len
+            .as_u8()
+            .checked_sub(O.try_into().expect("Actual len is too short to use offset"))
+            .ok_or(DeLengthOutOfRange)
+            .and_then(DeEncodedLength::try_from)
+            .expect("Couldn't encode actual len")
+    }
+}
+
+/// A DE allowing arbitrary data with a straightforward actual len = encoded len scheme.
+///
+/// Data longer than [MAX_DE_ENCODED_LEN] will fail when mapping the length.
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub(crate) struct ShortDataElement {
+    data: Vec<u8>,
+}
+
+impl ShortDataElement {
+    pub fn new(data: Vec<u8>) -> Self {
+        Self { data }
+    }
+}
+
+impl Sealed for ShortDataElement {}
+
+impl<F: PacketFlavor> SerializeDataElement<F> for ShortDataElement {
+    fn de_type_code(&self) -> DeTypeCode {
+        Self::DE_TYPE_CODE
+    }
+
+    fn map_actual_len_to_encoded_len(&self, actual_len: DeActualLength) -> DeEncodedLength {
+        <Self as DeserializeDataElement>::LengthMapper::map_actual_len_to_encoded_len(actual_len)
+    }
+
+    fn serialize_contents(
+        &self,
+        sink: &mut DataElementSerializationBuffer,
+    ) -> Result<(), DataElementSerializeError> {
+        sink.try_extend_from_slice(&self.data).ok_or(DataElementSerializeError::InsufficientSpace)
+    }
+}
+
+impl DeserializeDataElement for ShortDataElement {
+    const DE_TYPE_CODE: DeTypeCode = match DeTypeCode::try_from(14) {
+        Ok(t) => t,
+        Err(_) => unreachable!(),
+    };
+
+    type LengthMapper = DirectMapper<YoloLengthPredicate>;
+
+    fn deserialize<F: PacketFlavor>(
+        de_contents: &[u8],
+    ) -> Result<Self, DataElementDeserializeError> {
+        Ok(Self { data: de_contents.to_vec() })
+    }
+}
+
+/// Approves all lengths
+pub(in crate::legacy) struct YoloLengthPredicate;
+
+impl DirectMapPredicate for YoloLengthPredicate {
+    fn is_valid(_len: usize) -> bool {
+        true
+    }
+}
+
+impl From<Vec<u8>> for ShortDataElement {
+    fn from(value: Vec<u8>) -> Self {
+        Self { data: value }
+    }
+}
diff --git a/nearby/presence/np_adv/src/legacy/serialize/tests/mod.rs b/nearby/presence/np_adv/src/legacy/serialize/tests/mod.rs
new file mode 100644
index 0000000..caaa7d8
--- /dev/null
+++ b/nearby/presence/np_adv/src/legacy/serialize/tests/mod.rs
@@ -0,0 +1,95 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#![allow(clippy::unwrap_used)]
+
+extern crate std;
+
+use crate::legacy::{
+    data_elements::de_type::DataElementType, data_elements::*, serialize::*, PacketFlavorEnum,
+};
+
+mod error_conditions;
+mod happy_path;
+pub(in crate::legacy) mod helpers;
+
+pub(crate) fn serialize<F: PacketFlavor, D: SerializeDataElement<F>>(
+    de: &D,
+) -> SerializedDataElement {
+    serialize_de(de, NP_MAX_ADV_CONTENT_LEN).unwrap()
+}
+
+pub(in crate::legacy) fn supports_flavor(t: DataElementType, flavor: PacketFlavorEnum) -> bool {
+    match t {
+        DataElementType::Actions => match flavor {
+            PacketFlavorEnum::Plaintext => true,
+            PacketFlavorEnum::Ciphertext => true,
+        },
+        DataElementType::TxPower => match flavor {
+            PacketFlavorEnum::Plaintext => true,
+            PacketFlavorEnum::Ciphertext => true,
+        },
+    }
+}
+
+mod coverage_gaming {
+    use crate::credential::v0::V0BroadcastCredential;
+    use crate::legacy::serialize::{
+        AddDataElementError, AdvBuilder, LdtEncodeError, LdtEncoder, UnencryptedEncodeError,
+        UnencryptedEncoder,
+    };
+    use alloc::format;
+    use crypto_provider_default::CryptoProviderImpl;
+    use ldt_np_adv::{V0IdentityToken, V0Salt};
+
+    #[test]
+    fn unencrypted_encoder() {
+        let _ = format!("{:?}", UnencryptedEncoder);
+    }
+
+    #[test]
+    fn unencrypted_encoder_error() {
+        let _ = format!("{:?}", UnencryptedEncodeError::InvalidLength);
+    }
+
+    #[test]
+    fn ldt_encoder_display() {
+        let identity_token = V0IdentityToken::from([0x33; 14]);
+        let salt = V0Salt::from([0x01, 0x02]);
+        let key_seed = [0xFE; 32];
+        let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
+        let ldt_encoder = LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred);
+
+        // doesn't leak crypto material
+        assert_eq!("LdtEncoder { salt: V0Salt { bytes: [1, 2] } }", format!("{:?}", ldt_encoder));
+    }
+
+    #[test]
+    fn ldt_encoder_error() {
+        // debug, clone
+        let _ = format!("{:?}", LdtEncodeError::InvalidLength.clone());
+    }
+
+    #[test]
+    fn add_data_element_error() {
+        // debug
+        let _ = format!("{:?}", AddDataElementError::InsufficientAdvSpace);
+    }
+
+    #[test]
+    fn adv_builder() {
+        // debug
+        let _ = format!("{:?}", AdvBuilder::new(UnencryptedEncoder));
+    }
+}
diff --git a/nearby/presence/np_adv/src/lib.rs b/nearby/presence/np_adv/src/lib.rs
index 0b6d7ae..eb2ef0f 100644
--- a/nearby/presence/np_adv/src/lib.rs
+++ b/nearby/presence/np_adv/src/lib.rs
@@ -21,56 +21,37 @@
 #![no_std]
 #![allow(clippy::expect_used, clippy::indexing_slicing, clippy::panic)]
 
-use crate::{
-    credential::{
-        book::CredentialBook, v0::V0DiscoveryCryptoMaterial, v0::V0, v1::V1DiscoveryCryptoMaterial,
-        v1::V1, DiscoveryCryptoMaterial, MatchedCredential, ProtocolVersion,
-        ReferencedMatchedCredential,
-    },
-    deserialization_arena::ArenaOutOfSpace,
-    extended::deserialize::{
-        encrypted_section::*, parse_sections, CiphertextSection, DataElementParseError,
-        DataElementParsingIterator, DecryptedSection, IntermediateSection, PlaintextSection,
-        Section, SectionDeserializeError,
-    },
-    legacy::deserialize::{
-        DecryptError, DecryptedAdvContents, IntermediateAdvContents, PlaintextAdvContents,
-    },
-};
-
 #[cfg(any(test, feature = "alloc"))]
 extern crate alloc;
-#[cfg(any(test, feature = "alloc"))]
-use alloc::vec::Vec;
 
-use array_vec::ArrayVecOption;
-#[cfg(feature = "devtools")]
-use array_view::ArrayView;
-use core::fmt::Debug;
-use crypto_provider::CryptoProvider;
-use deserialization_arena::{DeserializationArena, DeserializationArenaAllocator};
-#[cfg(feature = "devtools")]
-use extended::NP_ADV_MAX_SECTION_LEN;
-use extended::NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT;
-use legacy::{data_elements::DataElementDeserializeError, deserialize::AdvDeserializeError};
-use nom::{combinator, number};
 pub use strum;
 
-mod array_vec;
+use crate::credential::matched::MatchedCredential;
+use crate::extended::deserialize::{deser_decrypt_v1, V1AdvertisementContents};
+use crate::{
+    credential::book::CredentialBook,
+    header::NpVersionHeader,
+    legacy::{deser_decrypt_v0, V0AdvertisementContents},
+};
+use core::fmt::Debug;
+use crypto_provider::CryptoProvider;
+use deserialization_arena::DeserializationArena;
+use legacy::data_elements::DataElementDeserializeError;
+
+#[cfg(test)]
+mod tests;
+
 pub mod credential;
-pub mod de_type;
-#[cfg(test)]
-mod deser_v0_tests;
-#[cfg(test)]
-mod deser_v1_tests;
 pub mod deserialization_arena;
 pub mod extended;
 pub mod filter;
-#[cfg(test)]
-mod header_parse_tests;
 pub mod legacy;
 pub mod shared_data;
 
+mod array_vec;
+mod header;
+mod helpers;
+
 /// Canonical form of NP's service UUID.
 ///
 /// Note that UUIDs are encoded in BT frames in little-endian order, so these bytes may need to be
@@ -88,356 +69,16 @@
     B: CredentialBook<'cred>,
     P: CryptoProvider,
 {
-    let (remaining, header) =
-        parse_adv_header(adv).map_err(|_e| AdvDeserializationError::HeaderParseError)?;
+    let (remaining, header) = NpVersionHeader::parse(adv)
+        .map_err(|_e| AdvDeserializationError::VersionHeaderParseError)?;
     match header {
-        AdvHeader::V0 => {
-            deser_decrypt_v0::<B, P>(cred_book, remaining).map(DeserializedAdvertisement::V0)
-        }
-        AdvHeader::V1(header) => deser_decrypt_v1::<B, P>(arena, cred_book, remaining, header)
-            .map(DeserializedAdvertisement::V1),
-    }
-}
-
-/// The encryption scheme used for a V1 advertisement.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum V1EncryptionScheme {
-    /// Indicates MIC-based encryption and verification.
-    Mic,
-    /// Indicates signature-based encryption and verification.
-    Signature,
-}
-
-/// Error in decryption operations for `deser_decrypt_v1_section_bytes_for_dev_tools`.
-#[cfg(feature = "devtools")]
-#[derive(Debug, Clone)]
-pub enum AdvDecryptionError {
-    /// Cannot decrypt because the input section is not encrypted.
-    InputNotEncrypted,
-    /// Error parsing the given section.
-    ParseError,
-    /// No suitable credential found to decrypt the given section.
-    NoMatchingCredentials,
-}
-
-/// Decrypt, but do not further deserialize the v1 bytes, intended for developer tooling uses only.
-/// Production uses should use [deserialize_advertisement] instead, which deserializes to a
-/// structured format and provides extra type safety.
-#[cfg(feature = "devtools")]
-pub fn deser_decrypt_v1_section_bytes_for_dev_tools<'adv, 'cred, B, P>(
-    arena: DeserializationArena<'adv>,
-    cred_book: &'cred B,
-    header_byte: u8,
-    section_bytes: &'adv [u8],
-) -> Result<(ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, V1EncryptionScheme), AdvDecryptionError>
-where
-    B: CredentialBook<'cred>,
-    P: CryptoProvider,
-{
-    let header = V1Header { header_byte };
-    let int_sections =
-        parse_sections(header, section_bytes).map_err(|_| AdvDecryptionError::ParseError)?;
-    let cipher_section = match &int_sections[0] {
-        IntermediateSection::Plaintext(_) => Err(AdvDecryptionError::InputNotEncrypted)?,
-        IntermediateSection::Ciphertext(section) => section,
-    };
-
-    let mut allocator = arena.into_allocator();
-    for (crypto_material, _) in cred_book.v1_iter() {
-        if let Some(plaintext) = cipher_section
-            .try_resolve_identity_and_decrypt::<_, P>(&mut allocator, &crypto_material)
-        {
-            let pt = plaintext.expect(concat!(
-                "Should not run out of space because DeserializationArenaAllocator is big ",
-                "enough to hold a single advertisement, and we exit immediately upon ",
-                "successful decryption",
-            ));
-
-            let encryption_scheme = match cipher_section {
-                CiphertextSection::SignatureEncryptedIdentity(_) => V1EncryptionScheme::Signature,
-                CiphertextSection::MicEncryptedIdentity(_) => V1EncryptionScheme::Mic,
-            };
-            return Ok((pt, encryption_scheme));
+        NpVersionHeader::V0(encoding) => deser_decrypt_v0::<B, P>(encoding, cred_book, remaining)
+            .map(DeserializedAdvertisement::V0),
+        NpVersionHeader::V1(header) => {
+            deser_decrypt_v1::<B, P>(arena, cred_book, remaining, header)
+                .map(DeserializedAdvertisement::V1)
         }
     }
-    Err(AdvDecryptionError::NoMatchingCredentials)
-}
-
-/// A ciphertext section which has not yet been
-/// resolved to an identity, but for which some
-/// `SectionIdentityResolutionContents` have been
-/// pre-computed for speedy identity-resolution.
-struct ResolvableCiphertextSection<'a> {
-    identity_resolution_contents: SectionIdentityResolutionContents,
-    ciphertext_section: CiphertextSection<'a>,
-}
-
-/// A collection of possibly-deserialized sections which are separated according
-/// to whether/not they're intermediate encrypted sections (of either type)
-/// or fully-deserialized, with a running count of the number of malformed sections.
-/// Each potentially-valid section is tagged with a 0-based index derived from the original
-/// section ordering as they appeared within the original advertisement to ensure
-/// that the fully-deserialized advertisement may be correctly reconstructed.
-struct SectionsInProcessing<'adv, M: MatchedCredential> {
-    deserialized_sections: ArrayVecOption<
-        (usize, V1DeserializedSection<'adv, M>),
-        { NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT },
-    >,
-    encrypted_sections: ArrayVecOption<
-        (usize, ResolvableCiphertextSection<'adv>),
-        { NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT },
-    >,
-    malformed_sections_count: usize,
-}
-
-impl<'adv, M: MatchedCredential> SectionsInProcessing<'adv, M> {
-    /// Attempts to parse a V1 advertisement's contents after the version header
-    /// into a collection of not-yet-fully-deserialized sections which may
-    /// require credentials to be decrypted.
-    fn from_advertisement_contents<C: CryptoProvider>(
-        header: V1Header,
-        remaining: &'adv [u8],
-    ) -> Result<Self, AdvDeserializationError> {
-        let int_sections =
-            parse_sections(header, remaining).map_err(|_| AdvDeserializationError::ParseError {
-                details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError,
-            })?;
-        let mut deserialized_sections = ArrayVecOption::default();
-        let mut encrypted_sections = ArrayVecOption::default();
-        // keep track of ordering for later sorting during `self.finished_with_decryption_attempts()`.
-        for (idx, s) in int_sections.into_iter().enumerate() {
-            match s {
-                IntermediateSection::Plaintext(p) => {
-                    deserialized_sections.push((idx, V1DeserializedSection::Plaintext(p)))
-                }
-                IntermediateSection::Ciphertext(ciphertext_section) => {
-                    let identity_resolution_contents =
-                        ciphertext_section.contents().compute_identity_resolution_contents::<C>();
-                    let resolvable_ciphertext_section = ResolvableCiphertextSection {
-                        identity_resolution_contents,
-                        ciphertext_section,
-                    };
-                    encrypted_sections.push((idx, resolvable_ciphertext_section));
-                }
-            }
-        }
-        Ok(Self { deserialized_sections, encrypted_sections, malformed_sections_count: 0 })
-    }
-
-    /// Returns true iff we have resolved all sections to identities.
-    fn resolved_all_identities(&self) -> bool {
-        self.encrypted_sections.is_empty()
-    }
-
-    /// Runs through all of the encrypted sections in processing, and attempts
-    /// to use the given credential to decrypt them. Suitable for situations
-    /// where iterating over credentials is relatively slow compared to
-    /// the cost of iterating over sections-in-memory.
-    fn try_decrypt_with_credential<C: V1DiscoveryCryptoMaterial, P: CryptoProvider>(
-        &mut self,
-        arena: &mut DeserializationArenaAllocator<'adv>,
-        crypto_material: C,
-        match_data: M,
-    ) -> Result<(), ArenaOutOfSpace> {
-        let mut i = 0;
-        while i < self.encrypted_sections.len() {
-            let (section_idx, section): &(usize, ResolvableCiphertextSection) =
-                &self.encrypted_sections[i];
-            // Fast-path: Check for an identity match, ignore if there's no identity match.
-            let identity_resolution_contents = &section.identity_resolution_contents;
-            let identity_resolution_material = match &section.ciphertext_section {
-                CiphertextSection::MicEncryptedIdentity(_) => crypto_material
-                    .unsigned_identity_resolution_material::<P>()
-                    .into_raw_resolution_material(),
-                CiphertextSection::SignatureEncryptedIdentity(_) => crypto_material
-                    .signed_identity_resolution_material::<P>()
-                    .into_raw_resolution_material(),
-            };
-            match identity_resolution_contents.try_match::<P>(&identity_resolution_material) {
-                None => {
-                    // Try again with another section
-                    i += 1;
-                    continue;
-                }
-                Some(identity_match) => {
-                    // The identity matched, so now we need to more closely scrutinize
-                    // the provided ciphertext. Try to decrypt and parse the section.
-                    let metadata_nonce = crypto_material.metadata_nonce::<P>();
-                    let deserialization_result = match &section.ciphertext_section {
-                        CiphertextSection::SignatureEncryptedIdentity(c) => c
-                            .try_deserialize(
-                                arena,
-                                identity_match,
-                                &crypto_material.signed_verification_material::<P>(),
-                            )
-                            .map_err(SectionDeserializeError::from),
-                        CiphertextSection::MicEncryptedIdentity(c) => c
-                            .try_deserialize(
-                                arena,
-                                identity_match,
-                                &crypto_material.unsigned_verification_material::<P>(),
-                            )
-                            .map_err(SectionDeserializeError::from),
-                    };
-                    match deserialization_result {
-                        Ok(s) => {
-                            self.deserialized_sections.push((
-                                *section_idx,
-                                V1DeserializedSection::Decrypted(WithMatchedCredential::new(
-                                    match_data.clone(),
-                                    metadata_nonce,
-                                    s,
-                                )),
-                            ));
-                        }
-                        Err(e) => match e {
-                            SectionDeserializeError::IncorrectCredential => {
-                                // keep it around to try with another credential
-                                i += 1;
-                                continue;
-                            }
-                            SectionDeserializeError::ParseError => {
-                                // the credential worked, but the section itself was bogus
-                                self.malformed_sections_count += 1;
-                            }
-                            SectionDeserializeError::ArenaOutOfSpace => {
-                                return Err(ArenaOutOfSpace)
-                            }
-                        },
-                    }
-                    // By default, if we have an identity match, assume that decrypting the section worked,
-                    // or that the section was somehow invalid.
-                    // We don't care about maintaining order, so use O(1) remove
-                    let _ = self.encrypted_sections.swap_remove(i);
-                    // don't advance i -- it now points to a new element
-                }
-            }
-        }
-        Ok(())
-    }
-
-    /// Packages the current state of the deserialization process into a
-    /// `V1AdvertisementContents` representing a fully-deserialized V1 advertisement.
-    ///
-    /// This method should only be called after all sections were either successfully
-    /// decrypted or have had all relevant credentials checked against
-    /// them without obtaining a successful identity-match and/or subsequent
-    /// cryptographic verification of the section contents.
-    fn finished_with_decryption_attempts(mut self) -> V1AdvertisementContents<'adv, M> {
-        // Invalid sections = malformed sections + number of encrypted sections
-        // which we could not manage to decrypt with any of our credentials
-        let invalid_sections_count = self.malformed_sections_count + self.encrypted_sections.len();
-
-        // Put the deserialized sections back into the original ordering for
-        // the returned `V1AdvertisementContents`
-        // (Note: idx is unique, so unstable sort is ok)
-        self.deserialized_sections.sort_unstable_by_key(|(idx, _section)| *idx);
-        let ordered_sections = self.deserialized_sections.into_iter().map(|(_idx, s)| s).collect();
-        V1AdvertisementContents::new(ordered_sections, invalid_sections_count)
-    }
-}
-
-/// Deserialize and decrypt the contents of a v1 adv after the version header
-fn deser_decrypt_v1<'adv, 'cred, B, P>(
-    arena: DeserializationArena<'adv>,
-    cred_book: &'cred B,
-    remaining: &'adv [u8],
-    header: V1Header,
-) -> Result<V1AdvertisementContents<'adv, B::Matched>, AdvDeserializationError>
-where
-    B: CredentialBook<'cred>,
-    P: CryptoProvider,
-{
-    let mut sections_in_processing =
-        SectionsInProcessing::<'_, B::Matched>::from_advertisement_contents::<P>(
-            header, remaining,
-        )?;
-
-    let mut allocator = arena.into_allocator();
-
-    // Hot loop
-    // We assume that iterating credentials is more expensive than iterating sections
-    for (crypto_material, match_data) in cred_book.v1_iter() {
-        sections_in_processing
-            .try_decrypt_with_credential::<_, P>(&mut allocator, crypto_material, match_data)
-            .expect(concat!(
-                "Should not run out of space because DeserializationArenaAllocator is big ",
-                "enough to hold a single advertisement, and we exit immediately upon ",
-                "successful decryption",
-            ));
-        if sections_in_processing.resolved_all_identities() {
-            // No need to consider the other credentials
-            break;
-        }
-    }
-    Ok(sections_in_processing.finished_with_decryption_attempts())
-}
-
-/// Deserialize and decrypt the contents of a v0 adv after the version header
-fn deser_decrypt_v0<'adv, 'cred, B, P>(
-    cred_book: &'cred B,
-    remaining: &'adv [u8],
-) -> Result<V0AdvertisementContents<'adv, B::Matched>, AdvDeserializationError>
-where
-    B: CredentialBook<'cred>,
-    P: CryptoProvider,
-{
-    let contents = legacy::deserialize::deserialize_adv_contents::<P>(remaining)?;
-    match contents {
-        IntermediateAdvContents::Plaintext(p) => Ok(V0AdvertisementContents::Plaintext(p)),
-        IntermediateAdvContents::Ciphertext(c) => {
-            for (crypto_material, matched) in cred_book.v0_iter() {
-                let ldt = crypto_material.ldt_adv_cipher::<P>();
-                match c.try_decrypt(&ldt) {
-                    Ok(c) => {
-                        let metadata_nonce = crypto_material.metadata_nonce::<P>();
-                        return Ok(V0AdvertisementContents::Decrypted(WithMatchedCredential::new(
-                            matched,
-                            metadata_nonce,
-                            c,
-                        )));
-                    }
-                    Err(e) => match e {
-                        DecryptError::DecryptOrVerifyError => continue,
-                        DecryptError::DeserializeError(e) => {
-                            return Err(e.into());
-                        }
-                    },
-                }
-            }
-            Ok(V0AdvertisementContents::NoMatchingCredentials)
-        }
-    }
-}
-
-/// Parse a NP advertisement header.
-///
-/// This can be used on all versions of advertisements since it's the header that determines the
-/// version.
-///
-/// Returns a `nom::IResult` with the parsed header and the remaining bytes of the advertisement.
-fn parse_adv_header(adv: &[u8]) -> nom::IResult<&[u8], AdvHeader> {
-    // header bits: VVVxxxxx
-    let (remaining, (header_byte, version, _low_bits)) = combinator::verify(
-        // splitting a byte at a bit boundary to take lower 5 bits
-        combinator::map(number::complete::u8, |byte| (byte, byte >> 5, byte & 0x1F)),
-        |&(_header_byte, version, low_bits)| match version {
-            // reserved bits, for any version, must be zero
-            PROTOCOL_VERSION_LEGACY | PROTOCOL_VERSION_EXTENDED => low_bits == 0,
-            _ => false,
-        },
-    )(adv)?;
-    match version {
-        PROTOCOL_VERSION_LEGACY => Ok((remaining, AdvHeader::V0)),
-        PROTOCOL_VERSION_EXTENDED => Ok((remaining, AdvHeader::V1(V1Header { header_byte }))),
-        _ => unreachable!(),
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub(crate) enum AdvHeader {
-    V0,
-    V1(V1Header),
 }
 
 /// An NP advertisement with its header parsed.
@@ -470,219 +111,11 @@
     }
 }
 
-/// 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>,
-    invalid_sections: usize,
-}
-
-impl<'adv, M: MatchedCredential> V1AdvertisementContents<'adv, M> {
-    fn new(
-        sections: ArrayVecOption<
-            V1DeserializedSection<'adv, M>,
-            NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
-        >,
-        invalid_sections: usize,
-    ) -> Self {
-        Self { sections, invalid_sections }
-    }
-
-    /// Destructures this V1 advertisement into just the sections
-    /// which could be successfully deserialized and decrypted
-    pub fn into_sections(
-        self,
-    ) -> ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT> {
-        self.sections
-    }
-
-    /// The sections that could be successfully deserialized and decrypted
-    pub fn sections(&self) -> impl ExactSizeIterator<Item = &V1DeserializedSection<M>> {
-        self.sections.iter()
-    }
-
-    /// The number of sections that could not be parsed or decrypted.
-    pub fn invalid_sections_count(&self) -> usize {
-        self.invalid_sections
-    }
-}
-
-/// Advertisement content that was either already plaintext or has been decrypted.
-#[derive(Debug, PartialEq, Eq)]
-pub enum V0AdvertisementContents<'adv, M: MatchedCredential> {
-    /// Contents of an originally plaintext advertisement
-    Plaintext(PlaintextAdvContents<'adv>),
-    /// Contents that was ciphertext in the original advertisement, and has been decrypted
-    /// with the credential in the [MatchedCredential]
-    Decrypted(WithMatchedCredential<M, DecryptedAdvContents>),
-    /// The advertisement was encrypted, but no credentials matched
-    NoMatchingCredentials,
-}
-
-/// Advertisement content that was either already plaintext or has been decrypted.
-#[derive(Debug, PartialEq, Eq)]
-pub enum V1DeserializedSection<'adv, M: MatchedCredential> {
-    /// Section that was plaintext in the original advertisement
-    Plaintext(PlaintextSection<'adv>),
-    /// Section that was ciphertext in the original advertisement, and has been decrypted
-    /// with the credential in the [MatchedCredential]
-    Decrypted(WithMatchedCredential<M, DecryptedSection<'adv>>),
-}
-
-impl<'adv, M> Section<'adv, DataElementParseError> for V1DeserializedSection<'adv, M>
-where
-    M: MatchedCredential,
-{
-    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(),
-        }
-    }
-}
-
-/// 16-byte metadata keys, as employed for metadata decryption.
-#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
-pub struct MetadataKey(pub [u8; 16]);
-
-impl AsRef<[u8]> for MetadataKey {
-    fn as_ref(&self) -> &[u8] {
-        &self.0
-    }
-}
-
-/// Common trait to deserialized, decrypted V0 advs and V1 sections which
-/// exposes relevant data about matched identities.
-pub trait HasIdentityMatch {
-    /// The protocol version for which this advertisement
-    /// content has an identity-match.
-    type Version: ProtocolVersion;
-
-    /// Gets the decrypted plaintext version-specific
-    /// metadata key for the associated identity.
-    fn metadata_key(&self) -> <Self::Version as ProtocolVersion>::MetadataKey;
-}
-
-impl HasIdentityMatch for legacy::ShortMetadataKey {
-    type Version = V0;
-    fn metadata_key(&self) -> Self {
-        *self
-    }
-}
-
-impl HasIdentityMatch for MetadataKey {
-    type Version = V1;
-    fn metadata_key(&self) -> Self {
-        *self
-    }
-}
-
-#[cfg(any(test, feature = "alloc"))]
-/// Type for errors from [`WithMatchedCredential#decrypt_metadata`]
-#[derive(Debug)]
-pub enum MatchedMetadataDecryptionError<M: MatchedCredential> {
-    /// Retrieving the encrypted metadata failed for one reason
-    /// or another, so we didn't get a chance to try decryption.
-    RetrievalFailed(<M as MatchedCredential>::EncryptedMetadataFetchError),
-    /// The encrypted metadata could be retrieved, but it did
-    /// not successfully decrypt against the matched identity.
-    /// This could be an indication of data corruption or
-    /// of malformed crypto on the sender-side.
-    DecryptionFailed,
-}
-
-/// Decrypted advertisement content with the [MatchedCredential] from the credential that decrypted
-/// it, along with any other information which is relevant to the identity-match.
-#[derive(Debug, PartialEq, Eq)]
-pub struct WithMatchedCredential<M: MatchedCredential, T: HasIdentityMatch> {
-    matched: M,
-    /// The 12-byte metadata nonce as derived from the key-seed HKDF
-    /// to be used for decrypting the encrypted metadata in the attached
-    /// matched-credential.
-    metadata_nonce: [u8; 12],
-    contents: T,
-}
-impl<'a, M: MatchedCredential + Clone, T: HasIdentityMatch>
-    WithMatchedCredential<ReferencedMatchedCredential<'a, M>, T>
-{
-    /// Clones the referenced match-data to update this container
-    /// so that the match-data is owned, rather than borrowed.
-    pub fn clone_match_data(self) -> WithMatchedCredential<M, T> {
-        let matched = self.matched.as_ref().clone();
-        let metadata_nonce = self.metadata_nonce;
-        let contents = self.contents;
-
-        WithMatchedCredential { matched, metadata_nonce, contents }
-    }
-}
-impl<M: MatchedCredential, T: HasIdentityMatch> WithMatchedCredential<M, T> {
-    fn new(matched: M, metadata_nonce: [u8; 12], contents: T) -> Self {
-        Self { matched, metadata_nonce, contents }
-    }
-    /// Applies the given function to the wrapped contents, yielding
-    /// a new instance with the same matched-credential.
-    pub fn map<R: HasIdentityMatch>(
-        self,
-        mapping: impl FnOnce(T) -> R,
-    ) -> WithMatchedCredential<M, R> {
-        let contents = mapping(self.contents);
-        let matched = self.matched;
-        let metadata_nonce = self.metadata_nonce;
-        WithMatchedCredential { matched, metadata_nonce, contents }
-    }
-    /// Credential data for the credential that decrypted the content.
-    pub fn matched_credential(&self) -> &M {
-        &self.matched
-    }
-    /// The decrypted advertisement content.
-    pub fn contents(&self) -> &T {
-        &self.contents
-    }
-
-    #[cfg(any(test, feature = "alloc"))]
-    fn decrypt_metadata_from_fetch<C: CryptoProvider>(
-        &self,
-        encrypted_metadata: &[u8],
-    ) -> Result<Vec<u8>, MatchedMetadataDecryptionError<M>> {
-        let metadata_key = self.contents.metadata_key();
-        <<T as HasIdentityMatch>::Version as ProtocolVersion>::decrypt_metadata::<C>(
-            self.metadata_nonce,
-            metadata_key,
-            encrypted_metadata,
-        )
-        .map_err(|_| MatchedMetadataDecryptionError::DecryptionFailed)
-    }
-
-    #[cfg(any(test, feature = "alloc"))]
-    /// Attempts to decrypt the encrypted metadata
-    /// associated with the matched credential
-    /// based on the details of the identity-match.
-    pub fn decrypt_metadata<C: CryptoProvider>(
-        &self,
-    ) -> Result<Vec<u8>, MatchedMetadataDecryptionError<M>> {
-        self.matched
-            .fetch_encrypted_metadata()
-            .map_err(|e| MatchedMetadataDecryptionError::RetrievalFailed(e))
-            .and_then(|x| Self::decrypt_metadata_from_fetch::<C>(self, x.as_ref()))
-    }
-}
-
-/// Data in a V1 advertisement header.
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub(crate) struct V1Header {
-    header_byte: u8,
-}
-
-const PROTOCOL_VERSION_LEGACY: u8 = 0;
-const PROTOCOL_VERSION_EXTENDED: u8 = 1;
-
 /// Errors that can occur during advertisement deserialization.
 #[derive(PartialEq)]
 pub enum AdvDeserializationError {
     /// The advertisement header could not be parsed
-    HeaderParseError,
+    VersionHeaderParseError,
     /// The advertisement content could not be parsed
     ParseError {
         /// Potentially hazardous details about deserialization errors. Read the documentation for
@@ -694,7 +127,9 @@
 impl Debug for AdvDeserializationError {
     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         match self {
-            AdvDeserializationError::HeaderParseError => write!(f, "HeaderParseError"),
+            AdvDeserializationError::VersionHeaderParseError => {
+                write!(f, "VersionHeaderParseError")
+            }
             AdvDeserializationError::ParseError { .. } => write!(f, "ParseError"),
         }
     }
@@ -710,37 +145,24 @@
     AdvertisementDeserializeError,
     /// Deserializing an individual DE from its DE contents failed
     V0DataElementDeserializeError(DataElementDeserializeError),
-    /// Must not have any other top level data elements if there is an encrypted identity DE
-    TooManyTopLevelDataElements,
-    /// Must not have an identity DE inside an identity DE
-    InvalidDataElementHierarchy,
-    /// Must have an identity DE
-    MissingIdentity,
     /// Non-identity DE contents must not be empty
     NoPublicDataElements,
 }
 
-impl From<AdvDeserializeError> for AdvDeserializationError {
-    fn from(err: AdvDeserializeError) -> Self {
+impl From<legacy::deserialize::AdvDeserializeError> for AdvDeserializationError {
+    fn from(err: legacy::deserialize::AdvDeserializeError) -> Self {
         match err {
-            AdvDeserializeError::AdvertisementDeserializeError => {
+            legacy::deserialize::AdvDeserializeError::NoDataElements => {
+                AdvDeserializationError::ParseError {
+                    details_hazmat: AdvDeserializationErrorDetailsHazmat::NoPublicDataElements,
+                }
+            }
+            legacy::deserialize::AdvDeserializeError::InvalidStructure => {
                 AdvDeserializationError::ParseError {
                     details_hazmat:
                         AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError,
                 }
             }
-            AdvDeserializeError::TooManyTopLevelDataElements => {
-                AdvDeserializationError::ParseError {
-                    details_hazmat:
-                        AdvDeserializationErrorDetailsHazmat::TooManyTopLevelDataElements,
-                }
-            }
-            AdvDeserializeError::MissingIdentity => AdvDeserializationError::ParseError {
-                details_hazmat: AdvDeserializationErrorDetailsHazmat::MissingIdentity,
-            },
-            AdvDeserializeError::NoPublicDataElements => AdvDeserializationError::ParseError {
-                details_hazmat: AdvDeserializationErrorDetailsHazmat::NoPublicDataElements,
-            },
         }
     }
 }
@@ -750,15 +172,8 @@
 #[derive(Debug, PartialEq, Eq)]
 pub struct DeLengthOutOfRange;
 
-/// The identity mode for a deserialized plaintext section or advertisement.
-#[derive(PartialEq, Eq, Debug, Clone, Copy)]
-pub enum PlaintextIdentityMode {
-    /// A "Public Identity" DE was present in the section
-    Public,
+pub(crate) mod private {
+    /// A marker trait to prevent other crates from implementing traits that
+    /// are intended to be only implemented internally.
+    pub trait Sealed {}
 }
-
-/// A "public identity" -- a nonspecific "empty identity".
-///
-/// Used when serializing V0 advertisements or V1 sections.
-#[derive(Default, Debug)]
-pub struct PublicIdentity;
diff --git a/nearby/presence/np_adv/src/shared_data.rs b/nearby/presence/np_adv/src/shared_data.rs
index b511ed2..c7d241b 100644
--- a/nearby/presence/np_adv/src/shared_data.rs
+++ b/nearby/presence/np_adv/src/shared_data.rs
@@ -16,7 +16,7 @@
 
 /// Power in dBm, calibrated as per
 /// [Eddystone](https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power)
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 pub struct TxPower {
     /// Power in `[-100, 20]`
     power: i8,
@@ -42,7 +42,7 @@
 }
 
 /// Tx power was out of the valid range `[-100, 20]`.
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Eq)]
 pub struct TxPowerOutOfRange;
 
 /// MDP's context sync number, used to inform other devices that they have stale context.
@@ -73,5 +73,33 @@
 }
 
 /// Seq num must be in `[0-15]`.
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Eq)]
 pub struct ContextSyncSeqNumOutOfRange;
+
+#[allow(clippy::unwrap_used)]
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn tx_power_out_of_range() {
+        assert_eq!(TxPowerOutOfRange, TxPower::try_from(-101).unwrap_err());
+        assert_eq!(TxPowerOutOfRange, TxPower::try_from(21).unwrap_err());
+    }
+
+    #[test]
+    fn tx_power_ok() {
+        assert_eq!(-100, TxPower::try_from(-100).unwrap().power);
+        assert_eq!(20, TxPower::try_from(20).unwrap().power);
+    }
+
+    #[test]
+    fn context_sync_seq_num_out_of_range() {
+        assert_eq!(ContextSyncSeqNumOutOfRange, ContextSyncSeqNum::try_from(0x10).unwrap_err());
+    }
+
+    #[test]
+    fn context_sync_seq_num_ok() {
+        assert_eq!(0x0F, ContextSyncSeqNum::try_from(0x0F).unwrap().num);
+    }
+}
diff --git a/nearby/util/pourover_macro_core/src/lib.rs b/nearby/presence/np_adv/src/tests.rs
similarity index 83%
copy from nearby/util/pourover_macro_core/src/lib.rs
copy to nearby/presence/np_adv/src/tests.rs
index ae06345..eaa7ced 100644
--- a/nearby/util/pourover_macro_core/src/lib.rs
+++ b/nearby/presence/np_adv/src/tests.rs
@@ -4,7 +4,7 @@
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
 //
-//     http://www.apache.org/licenses/LICENSE-2.0
+//      http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
 // distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,5 +12,5 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-mod jni_method;
-pub use jni_method::jni_method;
+mod deser_v0_tests;
+pub(crate) mod deser_v1_tests;
diff --git a/nearby/presence/np_adv/src/deser_v0_tests.rs b/nearby/presence/np_adv/src/tests/deser_v0_tests.rs
similarity index 65%
rename from nearby/presence/np_adv/src/deser_v0_tests.rs
rename to nearby/presence/np_adv/src/tests/deser_v0_tests.rs
index f6643de..b9dc6a8 100644
--- a/nearby/presence/np_adv/src/deser_v0_tests.rs
+++ b/nearby/presence/np_adv/src/tests/deser_v0_tests.rs
@@ -18,34 +18,30 @@
 
 extern crate std;
 
+use crate::credential::matched::{EmptyMatchedCredential, HasIdentityMatch, MatchedCredential};
+use crate::credential::MatchableCredential;
+use crate::legacy::serialize::UnencryptedEncoder;
 use crate::{
     credential::{
         book::{CredentialBook, CredentialBookBuilder},
-        v0::{V0DiscoveryCredential, V0},
-        EmptyMatchedCredential, MatchableCredential, MatchedCredential,
-        SimpleBroadcastCryptoMaterial,
+        v0::{V0BroadcastCredential, V0DiscoveryCredential, V0},
     },
-    de_type::EncryptedIdentityDataElementType,
     deserialization_arena,
     deserialization_arena::DeserializationArena,
     deserialize_advertisement,
     legacy::{
-        actions::{ActionBits, ActionsDataElement, ToActionElement},
-        data_elements::DataElement,
-        deserialize::PlainDataElement,
-        serialize::{AdvBuilder, Identity, LdtIdentity},
-        ShortMetadataKey, BLE_ADV_SVC_CONTENT_LEN,
+        data_elements::actions::{ActionBits, ActionsDataElement},
+        deserialize::DeserializedDataElement,
+        serialize::{AdvBuilder, AdvEncoder, LdtEncoder},
+        BLE_4_ADV_SVC_MAX_CONTENT_LEN,
     },
-    shared_data::ContextSyncSeqNum,
-    HasIdentityMatch, PlaintextIdentityMode, PublicIdentity, V0AdvertisementContents,
+    V0AdvertisementContents,
 };
 use array_view::ArrayView;
-use core::marker::PhantomData;
 use crypto_provider::CryptoProvider;
 use crypto_provider_default::CryptoProviderImpl;
-use ldt_np_adv::LegacySalt;
+use ldt_np_adv::{V0IdentityToken, V0Salt, V0_IDENTITY_TOKEN_LEN};
 use std::{prelude::rust_2021::*, vec};
-use strum::IntoEnumIterator as _;
 
 #[test]
 fn v0_all_identities_resolvable() {
@@ -58,7 +54,7 @@
         let creds = identities
             .iter()
             .map(|i| MatchableCredential {
-                discovery_credential: i.discovery_credential(),
+                discovery_credential: i.discovery_credential::<CryptoProviderImpl>(),
                 match_data: EmptyMatchedCredential,
             })
             .collect::<Vec<_>>();
@@ -87,7 +83,7 @@
                 !adv_config.identity.map(|sci| sci.key_seed == i.key_seed).unwrap_or(false)
             })
             .map(|i| MatchableCredential {
-                discovery_credential: i.discovery_credential(),
+                discovery_credential: i.discovery_credential::<CryptoProviderImpl>(),
                 match_data: EmptyMatchedCredential,
             })
             .collect::<Vec<_>>();
@@ -146,13 +142,11 @@
     match adv_config.identity {
         None => match adv {
             V0AdvertisementContents::Plaintext(p) => {
-                let mut action_bits = ActionBits::default();
-                action_bits.set_action(ContextSyncSeqNum::try_from(3).unwrap());
+                let action_bits = ActionBits::default();
                 let de = ActionsDataElement::from(action_bits);
 
-                assert_eq!(adv_config.plaintext_mode.unwrap(), p.identity());
                 assert_eq!(
-                    vec![PlainDataElement::Actions(de)],
+                    vec![DeserializedDataElement::Actions(de)],
                     p.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
                 )
             }
@@ -160,24 +154,17 @@
         },
         Some(_) => match adv {
             V0AdvertisementContents::Decrypted(wmc) => {
-                assert!(adv_config.plaintext_mode.is_none());
-
                 // different generic type param, so can't re-use the DE from above
-                let mut action_bits = ActionBits::default();
-                action_bits.set_action(ContextSyncSeqNum::try_from(3).unwrap());
+                let action_bits = ActionBits::default();
                 let de = ActionsDataElement::from(action_bits);
 
                 assert_eq!(
-                    vec![PlainDataElement::Actions(de)],
+                    vec![DeserializedDataElement::Actions(de)],
                     wmc.contents().data_elements().collect::<Result<Vec<_>, _>>().unwrap()
                 );
                 assert_eq!(
-                    adv_config.identity.unwrap().identity_type,
-                    wmc.contents().identity_type()
-                );
-                assert_eq!(
-                    adv_config.identity.unwrap().legacy_metadata_key,
-                    wmc.contents().metadata_key()
+                    adv_config.identity.unwrap().identity_token,
+                    wmc.contents().identity_token()
                 );
             }
             _ => panic!("should be an encrypted adv"),
@@ -203,92 +190,70 @@
 /// Populate an advertisement with a randomly chosen identity and a DE
 fn adv_random_identity<'a, R: rand::Rng>(
     mut rng: &mut R,
-    identities: &'a Vec<TestIdentity<CryptoProviderImpl>>,
-) -> (ArrayView<u8, { BLE_ADV_SVC_CONTENT_LEN }>, AdvConfig<'a>) {
+    identities: &'a Vec<TestIdentity>,
+) -> (ArrayView<u8, { BLE_4_ADV_SVC_MAX_CONTENT_LEN }>, AdvConfig<'a>) {
     let identity = identities.choose(&mut rng).unwrap();
     if rng.gen_bool(0.5) {
-        let mut adv_builder = AdvBuilder::new(PublicIdentity);
+        let mut adv_builder = AdvBuilder::new(UnencryptedEncoder);
         add_de(&mut adv_builder);
 
-        (
-            adv_builder.into_advertisement().unwrap(),
-            AdvConfig::new(None, Some(PlaintextIdentityMode::Public)),
-        )
+        (adv_builder.into_advertisement().unwrap(), AdvConfig::new(None))
     } else {
-        let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(
-            identity.key_seed,
-            identity.legacy_metadata_key,
-        );
-        let mut adv_builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
-            identity.identity_type,
-            LegacySalt::from(rng.gen::<[u8; 2]>()),
-            &broadcast_cm,
+        let broadcast_cred = V0BroadcastCredential::new(identity.key_seed, identity.identity_token);
+        let mut adv_builder = AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(
+            V0Salt::from(rng.gen::<[u8; 2]>()),
+            &broadcast_cred,
         ));
         add_de(&mut adv_builder);
 
-        (adv_builder.into_advertisement().unwrap(), AdvConfig::new(Some(identity), None))
+        (adv_builder.into_advertisement().unwrap(), AdvConfig::new(Some(identity)))
     }
 }
 
-fn add_de<I>(adv_builder: &mut AdvBuilder<I>)
+fn add_de<E>(adv_builder: &mut AdvBuilder<E>)
 where
-    I: Identity,
-    ActionsDataElement<I::Flavor>: DataElement,
-    ContextSyncSeqNum: ToActionElement<I::Flavor>,
+    E: AdvEncoder,
 {
-    let mut action_bits = ActionBits::default();
-    action_bits.set_action(ContextSyncSeqNum::try_from(3).unwrap());
+    let action_bits = ActionBits::default();
     let de = ActionsDataElement::from(action_bits);
     adv_builder.add_data_element(de).unwrap();
 }
 
-struct TestIdentity<C: CryptoProvider> {
-    identity_type: EncryptedIdentityDataElementType,
+struct TestIdentity {
     key_seed: [u8; 32],
-    legacy_metadata_key: ShortMetadataKey,
-    _marker: PhantomData<C>,
+    identity_token: V0IdentityToken,
 }
 
-impl<C: CryptoProvider> TestIdentity<C> {
+impl TestIdentity {
     /// Generate a new identity with random crypto material
     fn random<R: rand::Rng + rand::CryptoRng>(rng: &mut R) -> Self {
         Self {
-            identity_type: *EncryptedIdentityDataElementType::iter()
-                .collect::<Vec<_>>()
-                .choose(rng)
-                .unwrap(),
             key_seed: rng.gen(),
-            legacy_metadata_key: ShortMetadataKey(rng.gen()),
-            _marker: PhantomData,
+            identity_token: rng.gen::<[u8; V0_IDENTITY_TOKEN_LEN]>().into(),
         }
     }
 
     /// Returns a discovery-credential using crypto material from this identity
-    fn discovery_credential(&self) -> V0DiscoveryCredential {
-        let hkdf = self.hkdf();
+    fn discovery_credential<C: CryptoProvider>(&self) -> V0DiscoveryCredential {
+        let hkdf = self.hkdf::<C>();
         V0DiscoveryCredential::new(
             self.key_seed,
-            hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&self.legacy_metadata_key.0),
+            hkdf.v0_identity_token_hmac_key().calculate_hmac::<C>(&self.identity_token.bytes()),
         )
     }
 
-    fn hkdf(&self) -> np_hkdf::NpKeySeedHkdf<C> {
+    fn hkdf<C: CryptoProvider>(&self) -> np_hkdf::NpKeySeedHkdf<C> {
         np_hkdf::NpKeySeedHkdf::new(&self.key_seed)
     }
 }
 
 struct AdvConfig<'a> {
     /// `Some` iff an encrypted identity should be used
-    identity: Option<&'a TestIdentity<CryptoProviderImpl>>,
-    /// `Some` iff `identity` is `None`
-    plaintext_mode: Option<PlaintextIdentityMode>,
+    identity: Option<&'a TestIdentity>,
 }
 
 impl<'a> AdvConfig<'a> {
-    fn new(
-        identity: Option<&'a TestIdentity<CryptoProviderImpl>>,
-        plaintext_mode: Option<PlaintextIdentityMode>,
-    ) -> Self {
-        Self { identity, plaintext_mode }
+    fn new(identity: Option<&'a TestIdentity>) -> Self {
+        Self { identity }
     }
 }
diff --git a/nearby/presence/np_adv/src/tests/deser_v1_tests.rs b/nearby/presence/np_adv/src/tests/deser_v1_tests.rs
new file mode 100644
index 0000000..d0865c7
--- /dev/null
+++ b/nearby/presence/np_adv/src/tests/deser_v1_tests.rs
@@ -0,0 +1,485 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#![allow(clippy::unwrap_used)]
+
+extern crate std;
+
+use std::{prelude::rust_2021::*, vec};
+
+use rand::{
+    distributions::{Distribution, Standard},
+    random,
+    rngs::StdRng,
+    Rng, SeedableRng as _,
+};
+
+use array_view::ArrayView;
+use crypto_provider::{ed25519, CryptoRng};
+use crypto_provider_default::CryptoProviderImpl;
+use np_hkdf::v1_salt::ExtendedV1Salt;
+
+use crate::{
+    credential::{book::*, matched::*, v0::*, v1::*, *},
+    extended::{
+        data_elements::GenericDataElement,
+        deserialize::{data_element::DataElement, section::intermediate::PlaintextSection, *},
+        salt::MultiSalt,
+        serialize::*,
+        *,
+    },
+    *,
+};
+
+mod happy_path;
+
+mod error_condition;
+
+impl<'adv, M: MatchedCredential> V1DeserializedSection<'adv, M> {
+    fn as_plaintext_section(&self) -> &PlaintextSection {
+        match self {
+            V1DeserializedSection::Plaintext(c) => c,
+            V1DeserializedSection::Decrypted(_) => {
+                panic!("Casting into invalid enum variant")
+            }
+        }
+    }
+
+    fn as_ciphertext_section(&self) -> &WithMatchedCredential<M, DecryptedSection<'adv>> {
+        match self {
+            V1DeserializedSection::Plaintext(_) => panic!("Casting into invalid enum variant"),
+            V1DeserializedSection::Decrypted(wmc) => wmc,
+        }
+    }
+}
+
+fn assert_section_equals(
+    section_config: &SectionConfig,
+    section: &V1DeserializedSection<
+        ReferencedMatchedCredential<MetadataMatchedCredential<Vec<u8>>>,
+    >,
+) {
+    match &section_config.identity_kind {
+        IdentityKind::Plaintext => {
+            let plaintext_section = section.as_plaintext_section();
+            assert_eq!(
+                section_config.data_elements,
+                plaintext_section
+                    .iter_data_elements()
+                    .map(|de| (&de.unwrap()).into())
+                    .collect::<Vec<_>>()
+            )
+        }
+        IdentityKind::Encrypted { verification_mode, identity } => {
+            let enc_section = section.as_ciphertext_section();
+
+            let decrypted_metadata = enc_section.decrypt_metadata::<CryptoProviderImpl>().unwrap();
+            assert_eq!(&identity.plaintext_metadata, &decrypted_metadata);
+
+            let expected_contents =
+                section_config.data_elements.clone().iter().fold(Vec::new(), |mut buf, de| {
+                    buf.extend_from_slice(de.de_header().serialize().as_slice());
+                    de.write_de_contents(&mut buf).unwrap();
+                    buf
+                });
+            let contents = enc_section.contents();
+            assert_eq!(&expected_contents, contents.plaintext());
+
+            assert_eq!(
+                section_config.data_elements,
+                contents
+                    .iter_data_elements()
+                    .map(|de| (&de.unwrap()).into())
+                    .collect::<Vec<GenericDataElement>>()
+            );
+            assert_eq!(&identity.identity_token, enc_section.contents().identity_token());
+            assert_eq!(verification_mode, &enc_section.contents().verification_mode());
+        }
+    }
+}
+
+fn deser_v1_error<'a, B, P>(
+    arena: DeserializationArena<'a>,
+    adv: &'a [u8],
+    cred_book: &'a B,
+) -> AdvDeserializationError
+where
+    B: CredentialBook<'a>,
+    P: CryptoProvider,
+{
+    let v1_contents = match crate::deserialize_advertisement::<_, P>(arena, adv, cred_book) {
+        Err(e) => e,
+        _ => panic!("Expecting an error!"),
+    };
+    v1_contents
+}
+
+fn deser_v1<'adv, B, P>(
+    arena: DeserializationArena<'adv>,
+    adv: &'adv [u8],
+    cred_book: &'adv B,
+) -> V1AdvertisementContents<'adv, B::Matched>
+where
+    B: CredentialBook<'adv>,
+    P: CryptoProvider,
+{
+    crate::deserialize_advertisement::<_, P>(arena, adv, cred_book)
+        .expect("Should be a valid advertisement")
+        .into_v1()
+        .expect("Should be V1")
+}
+
+fn build_empty_cred_book() -> PrecalculatedOwnedCredentialBook<EmptyMatchedCredential> {
+    PrecalculatedOwnedCredentialBook::new(
+        PrecalculatedOwnedCredentialSource::<V0, EmptyMatchedCredential>::new::<CryptoProviderImpl>(
+            Vec::new(),
+        ),
+        PrecalculatedOwnedCredentialSource::<V1, EmptyMatchedCredential>::new::<CryptoProviderImpl>(
+            Vec::new(),
+        ),
+    )
+}
+
+/// Populate a random number of sections with randomly chosen identities and random DEs
+fn fill_plaintext_adv<'a, R: rand::Rng>(
+    rng: &mut R,
+    adv_builder: &mut AdvBuilder,
+) -> SectionConfig<'a> {
+    adv_builder
+        .section_builder(UnencryptedSectionEncoder)
+        .map(|mut s| {
+            let mut sink = Vec::new();
+            // plaintext sections cannot be empty so we need to be sure at least 1 DE is generated
+            let (_, des) = fill_section_random_des::<_, _, 1>(rng, &mut sink, &mut s);
+            s.add_to_advertisement::<CryptoProviderImpl>();
+            SectionConfig::new(IdentityKind::Plaintext, des)
+        })
+        .unwrap()
+}
+
+impl Distribution<VerificationMode> for Standard {
+    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> VerificationMode {
+        match rng.gen_range(0..=2) {
+            0 => VerificationMode::Signature,
+            _ => VerificationMode::Mic,
+        }
+    }
+}
+
+pub(crate) fn add_sig_rand_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>(
+    rng: &mut R,
+    identity: &'a TestIdentity,
+    adv_builder: &mut AdvBuilder,
+) -> Result<SectionConfig<'a>, AddSectionError> {
+    let salt: ExtendedV1Salt = rng.gen::<[u8; 16]>().into();
+    add_sig_with_salt_to_adv::<_, C, M>(rng, identity, adv_builder, salt.into())
+}
+
+fn add_sig_with_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>(
+    rng: &mut R,
+    identity: &'a crate::tests::deser_v1_tests::TestIdentity,
+    adv_builder: &mut AdvBuilder,
+    salt: MultiSalt,
+) -> Result<crate::tests::deser_v1_tests::SectionConfig<'a>, AddSectionError> {
+    let broadcast_cred = identity.broadcast_credential();
+    let salt = match salt {
+        MultiSalt::Short(_) => {
+            panic!("Invalid salt type for signature encrpted adv")
+        }
+        MultiSalt::Extended(e) => e,
+    };
+    adv_builder.section_builder(SignedEncryptedSectionEncoder::new::<C>(salt, &broadcast_cred)).map(
+        |mut s| {
+            let mut sink = Vec::new();
+            let (_, des) = fill_section_random_des::<_, _, M>(rng, &mut sink, &mut s);
+            s.add_to_advertisement::<C>();
+            SectionConfig::new(
+                IdentityKind::Encrypted {
+                    verification_mode: VerificationMode::Signature,
+                    identity,
+                },
+                des,
+            )
+        },
+    )
+}
+
+pub(crate) fn add_mic_rand_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>(
+    rng: &mut R,
+    identity: &'a TestIdentity,
+    adv_builder: &mut AdvBuilder,
+) -> Result<SectionConfig<'a>, AddSectionError> {
+    let salt = if rng.gen_bool(0.5) {
+        MultiSalt::Short(rng.gen::<[u8; 2]>().into())
+    } else {
+        MultiSalt::Extended(rng.gen::<[u8; 16]>().into())
+    };
+
+    add_mic_with_salt_to_adv::<_, C, M>(rng, identity, adv_builder, salt)
+}
+
+pub(crate) fn add_mic_with_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>(
+    rng: &mut R,
+    identity: &'a TestIdentity,
+    adv_builder: &mut AdvBuilder,
+    salt: MultiSalt,
+) -> Result<crate::tests::deser_v1_tests::SectionConfig<'a>, AddSectionError> {
+    let broadcast_cred = identity.broadcast_credential();
+    adv_builder
+        .section_builder(MicEncryptedSectionEncoder::<_>::new_with_salt::<C>(salt, &broadcast_cred))
+        .map(|mut s| {
+            let mut sink = Vec::new();
+            let (_, des) = fill_section_random_des::<_, _, M>(rng, &mut sink, &mut s);
+            s.add_to_advertisement::<C>();
+            SectionConfig::new(
+                IdentityKind::Encrypted { verification_mode: VerificationMode::Mic, identity },
+                des,
+            )
+        })
+}
+
+/// Populate a random number of sections with randomly chosen identities and random DEs
+fn fill_with_encrypted_sections<'a, R: rand::Rng, C: CryptoProvider>(
+    mut rng: &mut R,
+    identities: &'a TestIdentities,
+    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) {
+        let identity = identities.pick_random_identity(&mut rng);
+        let mode: VerificationMode = random();
+        let res = match mode {
+            VerificationMode::Signature => {
+                add_sig_rand_salt_to_adv::<_, C, 0>(&mut rng, identity, adv_builder)
+            }
+            VerificationMode::Mic => {
+                add_mic_rand_salt_to_adv::<_, C, 0>(&mut rng, identity, adv_builder)
+            }
+        };
+        match res {
+            Ok(tuple) => expected.push(tuple),
+            Err(_) => {
+                // couldn't fit that section; maybe another smaller section will fit
+                continue;
+            }
+        }
+    }
+    expected
+}
+
+#[derive(Clone)]
+pub(crate) struct TestIdentity {
+    key_seed: [u8; 32],
+    identity_token: V1IdentityToken,
+    private_key: ed25519::PrivateKey,
+    plaintext_metadata: Vec<u8>,
+}
+
+impl TestIdentity {
+    /// Generate a new identity with random crypto material
+    fn random<R: rand::Rng, C: CryptoProvider>(rng: &mut R) -> Self {
+        Self {
+            key_seed: rng.gen(),
+            identity_token: rng.gen(),
+            private_key: ed25519::PrivateKey::generate::<C::Ed25519>(),
+            // varying length vec of random bytes
+            plaintext_metadata: (0..rng.gen_range(50..200)).map(|_| rng.gen()).collect(),
+        }
+    }
+    /// Returns a (simple, signed) broadcast credential using crypto material from this identity
+    fn broadcast_credential(&self) -> V1BroadcastCredential {
+        V1BroadcastCredential::new(self.key_seed, self.identity_token, self.private_key.clone())
+    }
+    /// Returns a discovery credential using crypto material from this identity
+    fn discovery_credential<C: CryptoProvider>(&self) -> V1DiscoveryCredential {
+        self.broadcast_credential().derive_discovery_credential::<C>()
+    }
+}
+
+pub(crate) struct SectionConfig<'a> {
+    identity_kind: IdentityKind<'a>,
+    data_elements: Vec<GenericDataElement>,
+}
+
+pub(crate) enum IdentityKind<'a> {
+    Plaintext,
+    Encrypted { verification_mode: VerificationMode, identity: &'a TestIdentity },
+}
+
+impl<'a> SectionConfig<'a> {
+    pub fn new(identity_kind: IdentityKind<'a>, data_elements: Vec<GenericDataElement>) -> Self {
+        Self { identity_kind, data_elements }
+    }
+}
+
+/// Returns the DEs created in both deserialized form ([DataElement]) and
+/// input form ([GenericDataElement]) where `M` is the minimum number of DE's
+/// generated
+fn fill_section_random_des<'adv, R: rand::Rng, I: SectionEncoder, const M: usize>(
+    mut rng: &mut R,
+    sink: &'adv mut Vec<u8>,
+    section_builder: &mut SectionBuilder<&mut AdvBuilder, I>,
+) -> (Vec<DataElement<'adv>>, Vec<GenericDataElement>) {
+    let mut expected_des = vec![];
+    let mut orig_des = vec![];
+    let mut de_ranges = vec![];
+
+    for _ in 0..rng.gen_range(M..=5) {
+        let de = random_de::<MAX_DE_LEN, _>(&mut rng);
+
+        let de_clone = de.clone();
+        if section_builder.add_de(|_| de_clone).is_err() {
+            break;
+        }
+
+        let orig_len = sink.len();
+        de.write_de_contents(sink).unwrap();
+        let contents_len = sink.len() - orig_len;
+        de_ranges.push(orig_len..orig_len + contents_len);
+        orig_des.push(de);
+    }
+
+    for (index, (de, range)) in orig_des.iter().zip(de_ranges).enumerate() {
+        expected_des.push(DataElement::new(
+            u8::try_from(index).unwrap().into(),
+            de.de_header().de_type,
+            &sink[range],
+        ));
+    }
+    (expected_des, orig_des)
+}
+
+/// generates a random DE, where `N` is the max length size of the DE
+fn random_de<const N: usize, R: rand::Rng>(rng: &mut R) -> GenericDataElement {
+    let mut array = [0_u8; MAX_DE_LEN];
+    rng.fill(&mut array[..]);
+    let data: ArrayView<u8, MAX_DE_LEN> =
+        ArrayView::try_from_array(array, rng.gen_range(0..=MAX_DE_LEN)).unwrap();
+    // skip the first few DEs that Google uses
+    GenericDataElement::try_from(rng.gen_range(0_u32..1000).into(), data.as_slice()).unwrap()
+}
+
+#[derive(Clone)]
+pub(crate) struct TestIdentities(pub(crate) Vec<TestIdentity>);
+
+impl TestIdentities {
+    pub(crate) fn generate<const N: usize, R: rand::Rng, C: CryptoProvider>(
+        rng: &mut R,
+    ) -> TestIdentities {
+        TestIdentities((0..N).map(|_| TestIdentity::random::<_, C>(rng)).collect::<Vec<_>>())
+    }
+
+    fn pick_random_identity<R: rand::Rng>(&self, rng: &mut R) -> &TestIdentity {
+        let chosen_index = rng.gen_range(0..self.0.len());
+        &self.0[chosen_index]
+    }
+
+    pub(crate) fn build_cred_book<C: CryptoProvider>(
+        &self,
+    ) -> PrecalculatedOwnedCredentialBook<MetadataMatchedCredential<Vec<u8>>> {
+        let creds = self
+            .0
+            .iter()
+            .map(|identity| {
+                let match_data = MetadataMatchedCredential::<Vec<u8>>::encrypt_from_plaintext::<
+                    V1,
+                    CryptoProviderImpl,
+                >(
+                    &np_hkdf::NpKeySeedHkdf::new(&identity.key_seed),
+                    identity.identity_token,
+                    &identity.plaintext_metadata,
+                );
+                let discovery_credential = identity.discovery_credential::<C>();
+                MatchableCredential { discovery_credential, match_data }
+            })
+            .collect::<Vec<_>>();
+        let cred_source = PrecalculatedOwnedCredentialSource::new::<CryptoProviderImpl>(creds);
+        PrecalculatedOwnedCredentialBook::new(
+            PrecalculatedOwnedCredentialSource::<V0, MetadataMatchedCredential<Vec<u8>>>::new::<
+                CryptoProviderImpl,
+            >(Vec::new()),
+            cred_source,
+        )
+    }
+}
+
+fn deserialize_rand_identities_finds_correct_one<E, F>(
+    build_encoder: F,
+    expected_mode: VerificationMode,
+) where
+    E: SectionEncoder,
+    F: Fn(&mut <CryptoProviderImpl as CryptoProvider>::CryptoRng, &V1BroadcastCredential) -> E,
+{
+    let mut rng = StdRng::from_entropy();
+    let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
+
+    for _ in 0..100 {
+        let identities = TestIdentities::generate::<100, _, CryptoProviderImpl>(&mut rng);
+        let identity = identities.pick_random_identity(&mut rng);
+        let book = identities.build_cred_book::<CryptoProviderImpl>();
+
+        let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+
+        let broadcast_cm = identity.broadcast_credential();
+        let mut section_builder =
+            adv_builder.section_builder(build_encoder(&mut crypto_rng, &broadcast_cm)).unwrap();
+
+        let mut expected_de_data = vec![];
+        let (expected_des, orig_des) = fill_section_random_des::<_, _, 0>(
+            &mut rng,
+            &mut expected_de_data,
+            &mut section_builder,
+        );
+        section_builder.add_to_advertisement::<CryptoProviderImpl>();
+
+        let section_config = SectionConfig::new(
+            IdentityKind::Encrypted { verification_mode: expected_mode, identity },
+            orig_des.clone(),
+        );
+
+        let adv = adv_builder.into_advertisement();
+
+        let arena = deserialization_arena!();
+        let decrypted_contents = deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &book);
+        assert_eq!(0, decrypted_contents.invalid_sections_count());
+        let sections = decrypted_contents.into_sections();
+        assert_eq!(1, sections.len());
+
+        assert_section_equals(&section_config, &sections[0]);
+
+        // verify data elements match original after collecting them from the section
+        let data_elements =
+            sections[0].as_ciphertext_section().contents().collect_data_elements().unwrap();
+        assert_eq!(expected_des, data_elements);
+    }
+}
+
+fn add_plaintext_section<R: rand::Rng>(
+    rng: &mut R,
+    builder: &mut AdvBuilder,
+) -> Result<(), AddSectionError> {
+    builder.section_builder(UnencryptedSectionEncoder).map(|mut sb| {
+        // use an upper bound on the De size to leave room for another section
+        sb.add_de(|_| random_de::<20, _>(rng)).unwrap();
+        sb.add_to_advertisement::<CryptoProviderImpl>();
+    })
+}
+
+fn append_mock_encrypted_section(adv: &mut Vec<u8>) {
+    adv.push(0b0000_0001u8); // format
+    adv.extend_from_slice(&[0xAA; 2]); // short salt
+    adv.extend_from_slice(&[0xBB; 16]); // identity token
+    adv.push(3); // payload length
+    adv.extend_from_slice(&[0xCC; 3]); // payload contents
+}
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
new file mode 100644
index 0000000..9f91dbd
--- /dev/null
+++ b/nearby/presence/np_adv/src/tests/deser_v1_tests/error_condition.rs
@@ -0,0 +1,257 @@
+// 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 super::*;
+use crate::deserialization_arena;
+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();
+    sb.add_to_advertisement::<CryptoProviderImpl>();
+    let adv = adv_builder.into_advertisement();
+
+    let arena = deserialization_arena!();
+    let cred_book = build_empty_cred_book();
+    let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+    assert_eq!(
+        v1_error,
+        AdvDeserializationError::ParseError {
+            details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
+        }
+    );
+}
+
+#[test]
+fn v1_adv_no_sections() {
+    let mut rng = StdRng::from_entropy();
+    let identities = TestIdentities::generate::<5, _, CryptoProviderImpl>(&mut rng);
+    let adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+    let adv = adv_builder.into_advertisement();
+
+    let arena = deserialization_arena!();
+    let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
+
+    let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+    assert_eq!(
+        v1_error,
+        AdvDeserializationError::ParseError {
+            details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
+        }
+    );
+}
+
+#[test]
+fn v1_no_creds_available_ciphertext() {
+    let mut rng = StdRng::from_entropy();
+    for _ in 0..100 {
+        let identities = TestIdentities::generate::<100, _, CryptoProviderImpl>(&mut rng);
+        let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+        let section_configs = fill_with_encrypted_sections::<_, CryptoProviderImpl>(
+            &mut rng,
+            &identities,
+            &mut adv_builder,
+        );
+        let adv = adv_builder.into_advertisement();
+
+        let cred_book = build_empty_cred_book();
+        let arena = deserialization_arena!();
+        let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+        assert_eq!(section_configs.len(), v1_contents.invalid_sections_count());
+        assert_eq!(0, v1_contents.sections().len());
+    }
+}
+
+#[test]
+fn v1_only_non_matching_identities_available_ciphertext() {
+    let mut rng = StdRng::from_entropy();
+    for _ in 0..100 {
+        let mut identities = TestIdentities::generate::<100, _, CryptoProviderImpl>(&mut rng);
+        let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+        let cloned_identities = identities.clone();
+        let section_configs = fill_with_encrypted_sections::<_, CryptoProviderImpl>(
+            &mut rng,
+            &cloned_identities,
+            &mut adv_builder,
+        );
+        let adv = adv_builder.into_advertisement();
+
+        // only retain identities in the book that haven't been used in the constructed adv sections
+        identities.0.retain(|identity| {
+            !section_configs.iter().any(|sc| match &sc.identity_kind {
+                IdentityKind::Plaintext => panic!("There are no plaintext sections"),
+                IdentityKind::Encrypted { identity: section_identity, verification_mode: _ } => {
+                    identity.key_seed == section_identity.key_seed
+                }
+            })
+        });
+
+        let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
+        let arena = deserialization_arena!();
+        let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+        assert_eq!(section_configs.len(), v1_contents.invalid_sections_count());
+        assert_eq!(0, v1_contents.sections().len());
+    }
+}
+
+// TODO: this may be valid in future changes and would need to be updated
+#[test]
+fn v1_plaintext_then_encrypted_sections() {
+    let mut rng = StdRng::from_entropy();
+    let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+    let identities = TestIdentities::generate::<5, _, CryptoProviderImpl>(&mut rng);
+
+    add_plaintext_section(&mut rng, &mut adv_builder).unwrap();
+    let mut adv = adv_builder.into_advertisement().as_slice().to_vec();
+    append_mock_encrypted_section(&mut adv);
+
+    let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
+    let arena = deserialization_arena!();
+
+    let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+    assert_eq!(
+        v1_error,
+        AdvDeserializationError::ParseError {
+            details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
+        }
+    );
+}
+
+#[test]
+fn v1_encrypted_then_plaintext_sections() {
+    let mut rng = StdRng::from_entropy();
+    let mut builder = AdvBuilder::new(AdvertisementType::Encrypted);
+    let identities = TestIdentities::generate::<1, _, CryptoProviderImpl>(&mut rng);
+
+    let _ = add_mic_rand_salt_to_adv::<_, CryptoProviderImpl, 0>(
+        &mut rng,
+        &identities.0[0],
+        &mut builder,
+    )
+    .unwrap();
+    let mut adv = builder.into_advertisement().as_slice().to_vec();
+
+    // Append plaintext section
+    adv.push(V1_ENCODING_UNENCRYPTED); //Header
+    adv.push(3); // section len
+    adv.extend_from_slice(&[0xFF; 3]); //section contents
+
+    let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
+    let arena = deserialization_arena!();
+
+    let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+    assert_eq!(
+        v1_error,
+        AdvDeserializationError::ParseError {
+            details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
+        }
+    );
+}
+
+#[test]
+fn v1_encrypted_matching_identity_but_mic_mismatch() {
+    for _ in 0..100 {
+        v1_deserialize_error_test_tampered_adv(
+            add_mic_rand_salt_to_adv::<StdRng, CryptoProviderImpl, 0>,
+            |adv| {
+                // 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]);
+            },
+        )
+    }
+}
+
+#[test]
+fn v1_encrypted_matching_identity_but_sig_mismatch() {
+    v1_deserialize_error_test_tampered_adv(
+        add_sig_rand_salt_to_adv::<StdRng, CryptoProviderImpl, 0>,
+        |adv| {
+            // 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]);
+        },
+    )
+}
+
+#[test]
+fn v1_encrypted_matching_identity_missing_signature() {
+    v1_deserialize_error_test_tampered_adv(
+        add_sig_rand_salt_to_adv::<StdRng, CryptoProviderImpl, 0>,
+        |adv| {
+            // only keep the last 63 byte bytes after the len, which is too short to fit a signature
+            // 1 byte header + 1 byte format + 16 byte salt + 16 byte token + 1 byte len + 63 bytes of contents
+            adv.truncate(1 + 1 + 16 + 16 + 1 + 63);
+            // modify the length byte to account for this otherwise this will fail to parse at all
+            // 1 byte header + 1 byte format + 16 byte salt + 16 byte token
+            let len_byte_index = 1 + 1 + 16 + 16;
+            adv[len_byte_index] = 63;
+        },
+    )
+}
+
+fn v1_deserialize_error_test_tampered_adv(
+    add_to_adv: impl for<'a> Fn(
+        &mut StdRng,
+        &'a TestIdentity,
+        &mut AdvBuilder,
+    ) -> Result<SectionConfig<'a>, AddSectionError>,
+    mangle_adv: impl Fn(&mut Vec<u8>),
+) {
+    let mut rng = StdRng::from_entropy();
+    let mut builder = AdvBuilder::new(AdvertisementType::Encrypted);
+    let identities = TestIdentities::generate::<1, _, CryptoProviderImpl>(&mut rng);
+
+    let _ = add_to_adv(&mut rng, &identities.0[0], &mut builder);
+    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);
+    assert_eq!(1, v1_contents.invalid_sections_count());
+    assert_eq!(0, v1_contents.sections().len());
+}
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
new file mode 100644
index 0000000..2171bd6
--- /dev/null
+++ b/nearby/presence/np_adv/src/tests/deser_v1_tests/happy_path.rs
@@ -0,0 +1,227 @@
+// 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.
+
+extern crate std;
+
+use super::*;
+use crate::{deserialization_arena, extended::salt::*};
+use rand::SeedableRng;
+
+#[test]
+fn deserialize_rand_identities_single_section_finds_correct_one_mic_short_salt() {
+    deserialize_rand_identities_finds_correct_one(
+        |_rng, bc| {
+            let salt = ShortV1Salt::from([0x3; 2]);
+            MicEncryptedSectionEncoder::<_>::new_with_salt::<CryptoProviderImpl>(salt, bc)
+        },
+        VerificationMode::Mic,
+    )
+}
+
+#[test]
+fn deserialize_rand_identities_single_section_finds_correct_one_mic_extended_salt() {
+    deserialize_rand_identities_finds_correct_one(
+        |rng, bc| {
+            let salt: ExtendedV1Salt = rng.gen();
+            MicEncryptedSectionEncoder::<_>::new_with_salt::<CryptoProviderImpl>(salt, bc)
+        },
+        VerificationMode::Mic,
+    )
+}
+
+#[test]
+fn deserialize_rand_identities_single_section_finds_correct_one_signed() {
+    deserialize_rand_identities_finds_correct_one(
+        SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>,
+        VerificationMode::Signature,
+    )
+}
+
+#[test]
+fn v1_plaintext() {
+    let mut rng = StdRng::from_entropy();
+    for _ in 0..100 {
+        let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+        let section_config = fill_plaintext_adv(&mut rng, &mut adv_builder);
+        let adv = adv_builder.into_advertisement();
+        let arena = deserialization_arena!();
+        let cred_book =
+            CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &[]);
+
+        let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+        assert_eq!(0, v1_contents.invalid_sections_count());
+        assert_eq!(1, v1_contents.sections().len());
+        let sections = v1_contents.into_sections();
+        assert_section_equals(&section_config, &sections[0]);
+    }
+}
+
+#[test]
+fn v1_all_identities_resolvable_ciphertext() {
+    let mut rng = StdRng::from_entropy();
+    for _ in 0..100 {
+        let identities = TestIdentities::generate::<100, _, CryptoProviderImpl>(&mut rng);
+        let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+        let section_configs = fill_with_encrypted_sections::<_, CryptoProviderImpl>(
+            &mut rng,
+            &identities,
+            &mut adv_builder,
+        );
+        let adv = adv_builder.into_advertisement();
+
+        let arena = deserialization_arena!();
+        let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
+
+        let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+        assert_eq!(0, v1_contents.invalid_sections_count());
+        assert_eq!(section_configs.len(), v1_contents.sections().len());
+        for (section_config, section) in section_configs.iter().zip(v1_contents.sections()) {
+            assert_section_equals(section_config, section);
+        }
+    }
+}
+
+#[test]
+fn v1_only_some_matching_identities_available_ciphertext() {
+    let mut rng = StdRng::from_entropy();
+    for _ in 0..100 {
+        let mut identities = TestIdentities::generate::<100, _, CryptoProviderImpl>(&mut rng);
+        let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+        let cloned_identities = identities.clone();
+        let section_configs = fill_with_encrypted_sections::<_, CryptoProviderImpl>(
+            &mut rng,
+            &cloned_identities,
+            &mut adv_builder,
+        );
+        let adv = adv_builder.into_advertisement();
+
+        // Hopefully one day we can use extract_if instead: https://github.com/rust-lang/rust/issues/43244
+        let mut removed = vec![];
+        // Remove some of the identities which have been used to build the adv
+        identities.0.retain(|identity| {
+            let res = !section_configs.iter().any(|sc| match &sc.identity_kind {
+                IdentityKind::Plaintext => panic!("There are no plaintext sections"),
+                IdentityKind::Encrypted { identity: section_identity, verification_mode: _ } => {
+                    // only remove half the identities that were used
+                    (identity.key_seed == section_identity.key_seed) && rng.gen()
+                }
+            });
+            if !(res) {
+                removed.push(identity.clone());
+            }
+            res
+        });
+
+        // Need to account for how many sections were affected since the same identity could be used
+        // to encode multiple different sections
+        let affected_sections = section_configs
+            .iter()
+            .filter(|sc| match sc.identity_kind {
+                IdentityKind::Plaintext => {
+                    panic!("There are no plaintext sections")
+                }
+                IdentityKind::Encrypted { identity: si, verification_mode: _ } => {
+                    removed.iter().any(|i| i.key_seed == si.key_seed)
+                }
+            })
+            .count();
+
+        let arena = deserialization_arena!();
+        let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
+        let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+
+        assert_eq!(affected_sections, v1_contents.invalid_sections_count());
+        assert_eq!(section_configs.len() - affected_sections, v1_contents.sections().len());
+
+        for (section_config, section) in section_configs
+            .iter()
+            // skip sections w/ removed identities
+            .filter(|sc| match sc.identity_kind {
+                IdentityKind::Plaintext => {
+                    panic!("There are no plaintext sections")
+                }
+                IdentityKind::Encrypted { identity: si, verification_mode: _ } => {
+                    !removed.iter().any(|i| i.key_seed == si.key_seed)
+                }
+            })
+            .zip(v1_contents.sections())
+        {
+            assert_section_equals(section_config, section);
+        }
+    }
+}
+
+#[test]
+fn v1_decrypted_mic_short_salt_matches() {
+    let mut rng = StdRng::from_entropy();
+    let salt = MultiSalt::Short(rng.gen::<[u8; 2]>().into());
+    v1_decrypted_adv_salt_matches(
+        &mut rng,
+        salt,
+        add_mic_with_salt_to_adv::<_, CryptoProviderImpl, 0>,
+    );
+}
+
+#[test]
+fn v1_decrypted_mic_extended_salt_matches() {
+    let mut rng = StdRng::from_entropy();
+    let salt = MultiSalt::Extended(rng.gen::<[u8; 16]>().into());
+    v1_decrypted_adv_salt_matches(
+        &mut rng,
+        salt,
+        add_mic_with_salt_to_adv::<_, CryptoProviderImpl, 0>,
+    );
+}
+
+#[test]
+fn v1_decrypted_sig_extended_salt_matches() {
+    let mut rng = StdRng::from_entropy();
+    let salt = MultiSalt::Extended(rng.gen::<[u8; 16]>().into());
+    v1_decrypted_adv_salt_matches(
+        &mut rng,
+        salt,
+        add_sig_with_salt_to_adv::<_, CryptoProviderImpl, 0>,
+    );
+}
+
+fn v1_decrypted_adv_salt_matches(
+    rng: &mut StdRng,
+    salt: MultiSalt,
+    add_to_adv: impl for<'a> Fn(
+        &mut StdRng,
+        &'a TestIdentity,
+        &mut AdvBuilder,
+        MultiSalt,
+    ) -> Result<SectionConfig<'a>, AddSectionError>,
+) {
+    let identities = TestIdentities::generate::<1, _, CryptoProviderImpl>(rng);
+    let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+
+    let _ = add_to_adv(rng, &identities.0[0], &mut adv_builder, salt);
+
+    let adv = adv_builder.into_advertisement();
+    let arena = deserialization_arena!();
+    let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
+
+    let sections =
+        deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book).into_sections();
+    let section = &sections[0];
+    let decrypted = match section {
+        V1DeserializedSection::Plaintext(_) => {
+            panic!("section is encrypted")
+        }
+        V1DeserializedSection::Decrypted(d) => d,
+    };
+    assert_eq!(salt, *decrypted.contents().salt())
+}
diff --git a/nearby/presence/np_adv/tests/examples_v0.rs b/nearby/presence/np_adv/tests/examples_v0.rs
index f7b7d5e..b00fd61 100644
--- a/nearby/presence/np_adv/tests/examples_v0.rs
+++ b/nearby/presence/np_adv/tests/examples_v0.rs
@@ -15,21 +15,25 @@
 
 use crypto_provider_default::CryptoProviderImpl;
 use ldt_np_adv::*;
-use np_adv::legacy::data_elements::TxPowerDataElement;
+use np_adv::credential::matched::{
+    EmptyMatchedCredential, HasIdentityMatch, MetadataMatchedCredential,
+};
+use np_adv::legacy::serialize::{AdvBuilder, LdtEncoder};
 use np_adv::{
     credential::{
         book::CredentialBookBuilder,
-        v0::{V0DiscoveryCredential, V0},
-        EmptyMatchedCredential, MatchableCredential, MetadataMatchedCredential,
-        SimpleBroadcastCryptoMaterial,
+        v0::{V0BroadcastCredential, V0DiscoveryCredential, V0},
+        MatchableCredential,
     },
-    de_type::*,
-    legacy::{deserialize::*, ShortMetadataKey},
+    legacy::{
+        data_elements::tx_power::TxPowerDataElement, deserialize::*, V0AdvertisementContents,
+    },
     shared_data::*,
     *,
 };
 use serde::{Deserialize, Serialize};
 
+#[rustfmt::skip]
 #[test]
 fn v0_deser_plaintext() {
     let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
@@ -41,21 +45,19 @@
     let adv = deserialize_advertisement::<_, CryptoProviderImpl>(
         arena,
         &[
-            0x00, // adv header
-            0x03, // public identity
+            0x00, // version header
             0x15, 0x03, // Length 1 Tx Power DE with value 3
         ],
         &cred_book,
     )
-    .expect("Should be a valid advertisement")
-    .into_v0()
-    .expect("Should be V0");
+        .expect("Should be a valid advertisement")
+        .into_v0()
+        .expect("Should be V0");
 
     match adv {
         V0AdvertisementContents::Plaintext(p) => {
-            assert_eq!(PlaintextIdentityMode::Public, p.identity());
             assert_eq!(
-                vec![PlainDataElement::TxPower(TxPowerDataElement::from(
+                vec![DeserializedDataElement::TxPower(TxPowerDataElement::from(
                     TxPower::try_from(3).unwrap()
                 ))],
                 p.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
@@ -84,42 +86,45 @@
     }
 }
 
+#[rustfmt::skip]
 #[test]
 fn v0_deser_ciphertext() {
     // These are kept fixed in this example for reproducibility.
     // In practice, these should instead be derived from a cryptographically-secure
     // random number generator.
     let key_seed = [0x11_u8; 32];
-    let metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN] = [0x33; NP_LEGACY_METADATA_KEY_LEN];
-    let metadata_key = ShortMetadataKey(metadata_key);
+    let identity_token = V0IdentityToken::from([0x33; V0_IDENTITY_TOKEN_LEN]);
 
-    let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, metadata_key);
+    let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
 
     let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-    let metadata_key_hmac: [u8; 32] =
-        hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&metadata_key.0);
+    let identity_token_hmac: [u8; 32] =
+        hkdf.v0_identity_token_hmac_key().calculate_hmac::<CryptoProviderImpl>(&identity_token.bytes());
 
     // Serialize and encrypt some identity metadata (sender-side)
     let sender_metadata =
         IdentityMetadata { name: "Alice".to_string(), email: "alice@gmail.com".to_string() };
     let sender_metadata_bytes = sender_metadata.to_bytes();
-    let encrypted_sender_metadata = MetadataMatchedCredential::<Vec<u8>>::encrypt_from_plaintext::<
-        _,
-        _,
-        CryptoProviderImpl,
-    >(&broadcast_cm, &sender_metadata_bytes);
+    let encrypted_sender_metadata = MetadataMatchedCredential::<Vec<u8>>::
+    encrypt_from_plaintext::<V0, CryptoProviderImpl>(&hkdf,
+      identity_token,
+      &sender_metadata_bytes);
 
     // output of building a packet using AdvBuilder
     let adv = &[
-        0x00, // adv header
-        0x21, // private DE w/ a 2 byte payload
+        0x04, // version header
         0x22, 0x22, // salt
         // ciphertext for metadata key & txpower DE
-        0x85, 0xBF, 0xA8, 0x83, 0x58, 0x7C, 0x50, 0xCF, 0x98, 0x38, 0xA7, 0x8A, 0xC0, 0x1C, 0x96,
-        0xF9,
+        0xD8, 0x22, 0x12, 0xEF, 0x16, 0xDB, 0xF8, 0x72, 0xF2, 0xA3, 0xA7,
+        0xC0, 0xFA, 0x52, 0x48, 0xEC
     ];
 
-    let discovery_credential = V0DiscoveryCredential::new(key_seed, metadata_key_hmac);
+    // make sure output hasn't changed
+    let mut builder = AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new([0x22; 2].into(), &broadcast_cred));
+    builder.add_data_element(TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
+    assert_eq!(adv, builder.into_advertisement().unwrap().as_slice());
+
+    let discovery_credential = V0DiscoveryCredential::new(key_seed, identity_token_hmac);
 
     let credentials: [MatchableCredential<V0, MetadataMatchedCredential<_>>; 1] =
         [MatchableCredential { discovery_credential, match_data: encrypted_sender_metadata }];
@@ -134,12 +139,12 @@
         adv,
         &cred_book,
     )
-    .expect("Should be a valid advertisement")
-    .into_v0()
-    .expect("Should be V0")
+        .expect("Should be a valid advertisement")
+        .into_v0()
+        .expect("Should be V0")
     {
         V0AdvertisementContents::Decrypted(c) => c,
-        _ => panic!("this examples is ciphertext"),
+        _ => panic!("this example is ciphertext"),
     };
 
     let decrypted_metadata_bytes = matched
@@ -152,12 +157,10 @@
 
     let decrypted = matched.contents();
 
-    assert_eq!(EncryptedIdentityDataElementType::Private, decrypted.identity_type());
-
-    assert_eq!(metadata_key, decrypted.metadata_key());
+    assert_eq!(identity_token, decrypted.identity_token());
 
     assert_eq!(
-        vec![PlainDataElement::TxPower(TxPowerDataElement::from(TxPower::try_from(3).unwrap())),],
+        vec![DeserializedDataElement::TxPower(TxPowerDataElement::from(TxPower::try_from(3).unwrap())),],
         decrypted.data_elements().collect::<Result<Vec<_>, _>>().unwrap(),
     );
 }
diff --git a/nearby/presence/np_adv/tests/examples_v1.rs b/nearby/presence/np_adv/tests/examples_v1.rs
index f4df30c..57e41fa 100644
--- a/nearby/presence/np_adv/tests/examples_v1.rs
+++ b/nearby/presence/np_adv/tests/examples_v1.rs
@@ -14,36 +14,45 @@
 
 #![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, clippy::panic)]
 
-use crypto_provider::{CryptoProvider, CryptoRng};
+use crypto_provider::{ed25519, CryptoProvider, CryptoRng};
 use crypto_provider_default::CryptoProviderImpl;
-use np_adv::extended::data_elements::TxPowerDataElement;
-use np_adv::extended::serialize::{AdvertisementType, PublicSectionEncoder, SingleTypeDataElement};
-use np_adv::extended::NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT;
-use np_adv::shared_data::TxPower;
+use np_adv::credential::matched::{
+    EmptyMatchedCredential, MetadataMatchedCredential, WithMatchedCredential,
+};
+use np_adv::extended::deserialize::{Section, V1DeserializedSection};
+use np_adv::extended::{V1IdentityToken, V1_ENCODING_UNENCRYPTED};
 use np_adv::{
     credential::{
         book::CredentialBookBuilder,
-        v1::{SimpleSignedBroadcastCryptoMaterial, V1DiscoveryCredential, V1},
-        EmptyMatchedCredential, MatchableCredential, MetadataMatchedCredential,
+        v1::{V1BroadcastCredential, V1DiscoveryCredential, V1},
+        MatchableCredential,
     },
-    de_type::*,
+    deserialization_arena, deserialize_advertisement,
     extended::{
-        deserialize::{Section, VerificationMode},
-        serialize::{AdvBuilder, SignedEncryptedSectionEncoder},
+        data_elements::TxPowerDataElement,
+        deserialize::VerificationMode,
+        serialize::{
+            AdvBuilder, AdvertisementType, SignedEncryptedSectionEncoder, SingleTypeDataElement,
+            UnencryptedSectionEncoder,
+        },
+        NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT,
     },
-    PlaintextIdentityMode, *,
+    shared_data::TxPower,
+    AdvDeserializationError, AdvDeserializationErrorDetailsHazmat,
 };
-use np_hkdf::v1_salt;
+use np_hkdf::{v1_salt, DerivedSectionKeys};
 use serde::{Deserialize, Serialize};
 
+type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
+
 #[test]
 fn v1_deser_plaintext() {
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
-    let mut section_builder = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+    let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
     section_builder
         .add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(6).unwrap()))
         .unwrap();
-    section_builder.add_to_advertisement();
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
     let adv = adv_builder.into_advertisement();
 
     let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
@@ -69,12 +78,11 @@
         V1DeserializedSection::Plaintext(s) => s,
         _ => panic!("this is a plaintext adv"),
     };
-    assert_eq!(PlaintextIdentityMode::Public, section.identity());
     let data_elements = section.iter_data_elements().collect::<Result<Vec<_>, _>>().unwrap();
     assert_eq!(1, data_elements.len());
 
     let de = &data_elements[0];
-    assert_eq!(v1_salt::DataElementOffset::from(1), de.offset());
+    assert_eq!(v1_salt::DataElementOffset::from(0), de.offset());
     assert_eq!(TxPowerDataElement::DE_TYPE, de.de_type());
     assert_eq!(&[6], de.contents());
 }
@@ -104,14 +112,13 @@
 fn v1_deser_ciphertext() {
     // identity material
     let mut rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
-    let metadata_key: [u8; 16] = rng.gen();
-    let metadata_key = MetadataKey(metadata_key);
-    let key_pair = np_ed25519::KeyPair::<CryptoProviderImpl>::generate();
+    let token_array: [u8; 16] = rng.gen();
+    let identity_token = V1IdentityToken::from(token_array);
+    let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
     let key_seed = rng.gen();
     let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
 
-    let broadcast_cm =
-        SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
+    let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token, private_key.clone());
 
     // Serialize and encrypt some identity metadata (sender-side)
     let sender_metadata = IdentityMetadata {
@@ -121,37 +128,40 @@
     };
     let sender_metadata_bytes = sender_metadata.to_bytes();
     let encrypted_sender_metadata = MetadataMatchedCredential::<Vec<u8>>::encrypt_from_plaintext::<
-        _,
-        _,
+        V1,
         CryptoProviderImpl,
-    >(&broadcast_cm, &sender_metadata_bytes);
+    >(&hkdf, identity_token, &sender_metadata_bytes);
 
     // prepare advertisement
     let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
 
     let mut section_builder = adv_builder
-        .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
+        .section_builder(SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
             &mut rng,
-            EncryptedIdentityDataElementType::Private,
-            &broadcast_cm,
+            &broadcast_cred,
         ))
         .unwrap();
     section_builder
         .add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(7).unwrap()))
         .unwrap();
-    section_builder.add_to_advertisement();
+    section_builder.add_to_advertisement::<CryptoProviderImpl>();
     let adv = adv_builder.into_advertisement();
 
-    let discovery_credential = V1DiscoveryCredential::new::<CryptoProviderImpl>(
+    let discovery_credential = V1DiscoveryCredential::new(
         key_seed,
+        [0; 32],
         [0; 32], // Zeroing out MIC HMAC, since it's unused in examples here.
-        hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key.0),
-        key_pair.public().to_bytes(),
-    )
-    .expect("Public key bytes are valid points on the curve since theyc ame from the keypair");
+        hkdf.v1_signature_keys()
+            .identity_token_hmac_key()
+            .calculate_hmac::<CryptoProviderImpl>(identity_token.bytes()),
+        private_key.derive_public_key::<Ed25519ProviderImpl>(),
+    );
 
     let credentials: [MatchableCredential<V1, MetadataMatchedCredential<_>>; 1] =
-        [MatchableCredential { discovery_credential, match_data: encrypted_sender_metadata }];
+        [MatchableCredential {
+            discovery_credential,
+            match_data: encrypted_sender_metadata.clone(),
+        }];
     let cred_book = CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(
         &[],
         &credentials,
@@ -182,21 +192,52 @@
 
     let section = matched.contents();
 
-    assert_eq!(EncryptedIdentityDataElementType::Private, section.identity_type());
     assert_eq!(VerificationMode::Signature, section.verification_mode());
-    assert_eq!(metadata_key, section.metadata_key());
+    assert_eq!(&identity_token, section.identity_token());
 
     let data_elements = section.iter_data_elements().collect::<Result<Vec<_>, _>>().unwrap();
     assert_eq!(1, data_elements.len());
 
     let de = &data_elements[0];
-    assert_eq!(v1_salt::DataElementOffset::from(2), de.offset());
+    assert_eq!(v1_salt::DataElementOffset::from(0), de.offset());
     assert_eq!(TxPowerDataElement::DE_TYPE, de.de_type());
     assert_eq!(&[7], de.contents());
+
+    // Uncomment if you need to regenerate C++ v1_private_identity_tests data
+    // {
+    //     use test_helper::hex_bytes;
+    //     use np_adv::extended::salt::MultiSalt;
+    //     use np_adv_credential_matched::MatchedCredential;
+    //     println!("adv:\n{}", hex_bytes(adv.as_slice()));
+    //     println!("key seed:\n{}", hex_bytes(key_seed));
+    //     println!(
+    //         "identity token hmac:\n{}",
+    //         hex_bytes(
+    //             hkdf.v1_signature_keys()
+    //                 .identity_token_hmac_key()
+    //                 .calculate_hmac(identity_token.bytes())
+    //         )
+    //     );
+    //     println!("public key:\n{}", hex_bytes(key_pair.public().to_bytes()));
+    //     println!(
+    //         "encrypted metadata:\n{}",
+    //         hex_bytes(encrypted_sender_metadata.fetch_encrypted_metadata().unwrap())
+    //     );
+    //     std::println!("offset is: {:?}", de.offset());
+    //     let derived_salt = match section.salt() {
+    //         MultiSalt::Short(_) => panic!(),
+    //         MultiSalt::Extended(s) => {
+    //             s.derive::<16, CryptoProviderImpl>(Some(de.offset())).unwrap()
+    //         }
+    //     };
+    //     println!("DE derived salt:\n{}", hex_bytes(derived_salt));
+    //     panic!();
+    // }
 }
 
 #[test]
 fn v1_deser_no_section() {
+    // TODO: we shouldn't allow this invalid advertisement to be serialized
     let adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
     let adv = adv_builder.into_advertisement();
     let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
@@ -220,19 +261,18 @@
 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 {
-        let mut section_builder =
-            adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+        let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
         section_builder
             .add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(7).unwrap()))
             .unwrap();
-        section_builder.add_to_advertisement();
+        section_builder.add_to_advertisement::<CryptoProviderImpl>();
     }
     let mut adv = adv_builder.into_advertisement().as_slice().to_vec();
     // Push an extra section
     adv.extend_from_slice(
         [
             0x01, // Section header
-            0x03, // Public identity
+            V1_ENCODING_UNENCRYPTED,
         ]
         .as_slice(),
     );
diff --git a/nearby/presence/np_adv_dynamic/Cargo.toml b/nearby/presence/np_adv_dynamic/Cargo.toml
index 2e36ec5..5eb6863 100644
--- a/nearby/presence/np_adv_dynamic/Cargo.toml
+++ b/nearby/presence/np_adv_dynamic/Cargo.toml
@@ -12,3 +12,4 @@
 np_adv = { workspace = true, features = ["alloc"] }
 crypto_provider.workspace = true
 sink.workspace = true
+np_hkdf.workspace = true
diff --git a/nearby/presence/np_adv_dynamic/src/extended.rs b/nearby/presence/np_adv_dynamic/src/extended.rs
index 07de504..c25be74 100644
--- a/nearby/presence/np_adv_dynamic/src/extended.rs
+++ b/nearby/presence/np_adv_dynamic/src/extended.rs
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 use crypto_provider::CryptoProvider;
+use np_adv::extended::salt::MultiSalt;
 use np_adv::{extended::data_elements::*, extended::serialize::*, shared_data::*};
 use sink::Sink;
 use std::fmt::{Display, Formatter};
@@ -62,22 +63,18 @@
     }
 }
 
-fn wrap_owning_section_builder<C: CryptoProvider, S: Into<BoxedSectionBuilder<AdvBuilder, C>>>(
+fn wrap_owning_section_builder<S: Into<BoxedSectionBuilder<AdvBuilder>>>(
     maybe_section_builder: Result<S, (AdvBuilder, AddSectionError)>,
-) -> Result<BoxedSectionBuilder<AdvBuilder, C>, (BoxedAdvBuilder, BoxedAddSectionError)> {
+) -> Result<BoxedSectionBuilder<AdvBuilder>, (BoxedAdvBuilder, BoxedAddSectionError)> {
     match maybe_section_builder {
         Ok(section_builder) => Ok(section_builder.into()),
         Err((adv_builder, err)) => Err((adv_builder.into(), err.into())),
     }
 }
 
-fn wrap_mut_ref_section_builder<
-    'a,
-    C: CryptoProvider,
-    S: Into<BoxedSectionBuilder<&'a mut AdvBuilder, C>>,
->(
+fn wrap_mut_ref_section_builder<'a, S: Into<BoxedSectionBuilder<&'a mut AdvBuilder>>>(
     maybe_section_builder: Result<S, AddSectionError>,
-) -> Result<BoxedSectionBuilder<&'a mut AdvBuilder, C>, BoxedAddSectionError> {
+) -> Result<BoxedSectionBuilder<&'a mut AdvBuilder>, BoxedAddSectionError> {
     let section_builder = maybe_section_builder?;
     Ok(section_builder.into())
 }
@@ -98,42 +95,42 @@
     /// builder, if the operation was successful. Otherwise,
     /// this advertisement builder will be returned back to the
     /// caller unaltered as part of the `Err` arm.
-    pub fn into_section_builder<C: CryptoProvider>(
+    pub fn into_section_builder(
         self,
-        identity: BoxedIdentity<C>,
-    ) -> Result<BoxedSectionBuilder<AdvBuilder, C>, (Self, BoxedAddSectionError)> {
+        identity: BoxedEncoder,
+    ) -> Result<BoxedSectionBuilder<AdvBuilder>, (Self, BoxedAddSectionError)> {
         match identity {
-            BoxedIdentity::PublicIdentity => wrap_owning_section_builder(
-                self.adv_builder.into_section_builder(PublicSectionEncoder::default()),
+            BoxedEncoder::Unencrypted => wrap_owning_section_builder(
+                self.adv_builder.into_section_builder(UnencryptedSectionEncoder),
             ),
-            BoxedIdentity::MicEncrypted(ident) => {
+            BoxedEncoder::MicEncrypted(ident) => {
                 wrap_owning_section_builder(self.adv_builder.into_section_builder(ident))
             }
-            BoxedIdentity::SignedEncrypted(ident) => {
+            BoxedEncoder::SignedEncrypted(ident) => {
                 wrap_owning_section_builder(self.adv_builder.into_section_builder(ident))
             }
         }
     }
 
-    /// Create a section builder using the given identity.
+    /// Create a section builder using the given encoder.
     ///
     /// Returns `Err` if the underlying advertisement builder
     /// yields an error when attempting to add a new section
     /// (typically because there's no more available adv space),
     /// or if the requested identity requires salt, and the
     /// advertisement builder is salt-less.
-    pub fn section_builder<C: CryptoProvider>(
+    pub fn section_builder(
         &mut self,
-        identity: BoxedIdentity<C>,
-    ) -> Result<BoxedSectionBuilder<&mut AdvBuilder, C>, BoxedAddSectionError> {
-        match identity {
-            BoxedIdentity::PublicIdentity => wrap_mut_ref_section_builder(
-                self.adv_builder.section_builder(PublicSectionEncoder::default()),
+        encoder: BoxedEncoder,
+    ) -> Result<BoxedSectionBuilder<&mut AdvBuilder>, BoxedAddSectionError> {
+        match encoder {
+            BoxedEncoder::Unencrypted => wrap_mut_ref_section_builder(
+                self.adv_builder.section_builder(UnencryptedSectionEncoder),
             ),
-            BoxedIdentity::MicEncrypted(ident) => {
+            BoxedEncoder::MicEncrypted(ident) => {
                 wrap_mut_ref_section_builder(self.adv_builder.section_builder(ident))
             }
-            BoxedIdentity::SignedEncrypted(ident) => {
+            BoxedEncoder::SignedEncrypted(ident) => {
                 wrap_mut_ref_section_builder(self.adv_builder.section_builder(ident))
             }
         }
@@ -145,29 +142,29 @@
     }
 }
 
-/// A wrapped v1 identity whose type is given at run-time.
-pub enum BoxedIdentity<C: CryptoProvider> {
-    /// Public identity.
-    PublicIdentity,
-    /// An encrypted identity leveraging MIC for verification.
-    MicEncrypted(MicEncryptedSectionEncoder<C>),
-    /// An encrypted identity leveraging signatures for verification.
-    SignedEncrypted(SignedEncryptedSectionEncoder<C>),
+/// A wrapped v1 encoder whose type is given at run-time.
+pub enum BoxedEncoder {
+    /// Unencrypted encoder.
+    Unencrypted,
+    /// An encrypted encoder leveraging MIC for verification.
+    MicEncrypted(MicEncryptedSectionEncoder<MultiSalt>),
+    /// An encrypted encoder leveraging signatures for verification.
+    SignedEncrypted(SignedEncryptedSectionEncoder),
 }
 
 /// A `SectionBuilder` whose corresponding Identity
 /// and salted-ness is given at run-time instead of
 /// at compile-time.
-pub enum BoxedSectionBuilder<R: AsMut<AdvBuilder>, C: CryptoProvider> {
+pub enum BoxedSectionBuilder<R: AsMut<AdvBuilder>> {
     /// A builder for a public section.
-    Public(Box<SectionBuilder<R, PublicSectionEncoder>>),
+    Public(Box<SectionBuilder<R, UnencryptedSectionEncoder>>),
     /// A builder for a MIC-verified section.
-    MicEncrypted(Box<SectionBuilder<R, MicEncryptedSectionEncoder<C>>>),
+    MicEncrypted(Box<SectionBuilder<R, MicEncryptedSectionEncoder<MultiSalt>>>),
     /// A builder for a signature-verified section.
-    SignedEncrypted(Box<SectionBuilder<R, SignedEncryptedSectionEncoder<C>>>),
+    SignedEncrypted(Box<SectionBuilder<R, SignedEncryptedSectionEncoder>>),
 }
 
-impl<C: CryptoProvider> BoxedSectionBuilder<AdvBuilder, C> {
+impl BoxedSectionBuilder<AdvBuilder> {
     /// Gets the 0-based index of the section currently under construction
     /// in the context of the containing advertisement.
     pub fn section_index(&self) -> usize {
@@ -179,28 +176,28 @@
     }
     /// Add this builder to the advertisement that created it,
     /// returning the containing advertisement builder.
-    pub fn add_to_advertisement(self) -> BoxedAdvBuilder {
+    pub fn add_to_advertisement<C: CryptoProvider>(self) -> BoxedAdvBuilder {
         let adv_builder = match self {
-            BoxedSectionBuilder::Public(x) => x.add_to_advertisement(),
-            BoxedSectionBuilder::MicEncrypted(x) => x.add_to_advertisement(),
-            BoxedSectionBuilder::SignedEncrypted(x) => x.add_to_advertisement(),
+            BoxedSectionBuilder::Public(x) => x.add_to_advertisement::<C>(),
+            BoxedSectionBuilder::MicEncrypted(x) => x.add_to_advertisement::<C>(),
+            BoxedSectionBuilder::SignedEncrypted(x) => x.add_to_advertisement::<C>(),
         };
         BoxedAdvBuilder::from(adv_builder)
     }
 }
 
-impl<'a, C: CryptoProvider> BoxedSectionBuilder<&'a mut AdvBuilder, C> {
+impl<'a> BoxedSectionBuilder<&'a mut AdvBuilder> {
     /// Add this builder to the advertisement that created it.
-    pub fn add_to_advertisement(self) {
+    pub fn add_to_advertisement<C: CryptoProvider>(self) {
         match self {
-            BoxedSectionBuilder::Public(x) => x.add_to_advertisement(),
-            BoxedSectionBuilder::MicEncrypted(x) => x.add_to_advertisement(),
-            BoxedSectionBuilder::SignedEncrypted(x) => x.add_to_advertisement(),
+            BoxedSectionBuilder::Public(x) => x.add_to_advertisement::<C>(),
+            BoxedSectionBuilder::MicEncrypted(x) => x.add_to_advertisement::<C>(),
+            BoxedSectionBuilder::SignedEncrypted(x) => x.add_to_advertisement::<C>(),
         }
     }
 }
 
-impl<R: AsMut<AdvBuilder>, C: CryptoProvider> BoxedSectionBuilder<R, C> {
+impl<R: AsMut<AdvBuilder>> BoxedSectionBuilder<R> {
     /// Returns true if this wraps a section builder which
     /// leverages some encrypted identity.
     pub fn is_encrypted(&self) -> bool {
@@ -211,15 +208,16 @@
         }
     }
     /// Gets the derived salt of the next DE to be added to the section,
-    /// if this section-builder corresponds to an encrypted section.
+    /// if this section-builder corresponds to an encrypted section that can
+    /// provide per-DE salts.
     /// Otherwise, returns nothing.
     ///
     /// Suitable for scenarios (like FFI) where a closure would be inappropriate
     /// for DE construction, and interaction with the client is preferred.
-    pub fn next_de_salt(&self) -> Option<DeSalt<C>> {
+    pub fn next_de_salt(&self) -> Option<DeSalt> {
         match self {
             BoxedSectionBuilder::Public(_) => None,
-            BoxedSectionBuilder::MicEncrypted(x) => Some(x.next_de_salt()),
+            BoxedSectionBuilder::MicEncrypted(x) => x.next_de_salt(),
             BoxedSectionBuilder::SignedEncrypted(x) => Some(x.next_de_salt()),
         }
     }
@@ -230,19 +228,16 @@
     /// if any salt has been specified for the surrounding advertisement.
     pub fn add_de_res<E>(
         &mut self,
-        build_de: impl FnOnce(Option<DeSalt<C>>) -> Result<BoxedWriteDataElement, E>,
+        build_de: impl FnOnce(Option<DeSalt>) -> Result<BoxedWriteDataElement, E>,
     ) -> Result<(), AddDataElementError<E>> {
         match self {
             BoxedSectionBuilder::Public(x) => {
                 let build_de_modified = |()| build_de(None);
                 x.add_de_res(build_de_modified)
             }
-            BoxedSectionBuilder::MicEncrypted(x) => {
-                let build_de_modified = |de_salt: DeSalt<C>| build_de(Some(de_salt));
-                x.add_de_res(build_de_modified)
-            }
+            BoxedSectionBuilder::MicEncrypted(x) => x.add_de_res(build_de),
             BoxedSectionBuilder::SignedEncrypted(x) => {
-                let build_de_modified = |de_salt: DeSalt<C>| build_de(Some(de_salt));
+                let build_de_modified = |de_salt: DeSalt| build_de(Some(de_salt));
                 x.add_de_res(build_de_modified)
             }
         }
@@ -250,32 +245,32 @@
     /// Like add_de_res, but for infalliable closures
     pub fn add_de(
         &mut self,
-        build_de: impl FnOnce(Option<DeSalt<C>>) -> BoxedWriteDataElement,
+        build_de: impl FnOnce(Option<DeSalt>) -> BoxedWriteDataElement,
     ) -> Result<(), AddDataElementError<()>> {
         self.add_de_res(|derived_salt| Ok::<_, ()>(build_de(derived_salt)))
     }
 }
 
-impl<R: AsMut<AdvBuilder>, C: CryptoProvider> From<SectionBuilder<R, PublicSectionEncoder>>
-    for BoxedSectionBuilder<R, C>
+impl<R: AsMut<AdvBuilder>> From<SectionBuilder<R, UnencryptedSectionEncoder>>
+    for BoxedSectionBuilder<R>
 {
-    fn from(section_builder: SectionBuilder<R, PublicSectionEncoder>) -> Self {
+    fn from(section_builder: SectionBuilder<R, UnencryptedSectionEncoder>) -> Self {
         BoxedSectionBuilder::Public(Box::new(section_builder))
     }
 }
 
-impl<R: AsMut<AdvBuilder>, C: CryptoProvider> From<SectionBuilder<R, MicEncryptedSectionEncoder<C>>>
-    for BoxedSectionBuilder<R, C>
+impl<R: AsMut<AdvBuilder>> From<SectionBuilder<R, MicEncryptedSectionEncoder<MultiSalt>>>
+    for BoxedSectionBuilder<R>
 {
-    fn from(section_builder: SectionBuilder<R, MicEncryptedSectionEncoder<C>>) -> Self {
+    fn from(section_builder: SectionBuilder<R, MicEncryptedSectionEncoder<MultiSalt>>) -> Self {
         BoxedSectionBuilder::MicEncrypted(Box::new(section_builder))
     }
 }
 
-impl<R: AsMut<AdvBuilder>, C: CryptoProvider>
-    From<SectionBuilder<R, SignedEncryptedSectionEncoder<C>>> for BoxedSectionBuilder<R, C>
+impl<R: AsMut<AdvBuilder>> From<SectionBuilder<R, SignedEncryptedSectionEncoder>>
+    for BoxedSectionBuilder<R>
 {
-    fn from(section_builder: SectionBuilder<R, SignedEncryptedSectionEncoder<C>>) -> Self {
+    fn from(section_builder: SectionBuilder<R, SignedEncryptedSectionEncoder>) -> Self {
         BoxedSectionBuilder::SignedEncrypted(Box::new(section_builder))
     }
 }
diff --git a/nearby/presence/np_adv_dynamic/src/legacy.rs b/nearby/presence/np_adv_dynamic/src/legacy.rs
index 83ce6ab..68eb1bc 100644
--- a/nearby/presence/np_adv_dynamic/src/legacy.rs
+++ b/nearby/presence/np_adv_dynamic/src/legacy.rs
@@ -12,14 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use array_view::ArrayView;
 use crypto_provider::CryptoProvider;
-use np_adv::legacy::actions::*;
-use np_adv::legacy::data_elements::*;
-use np_adv::legacy::serialize::*;
-use np_adv::legacy::*;
-use np_adv::shared_data::*;
-use np_adv::PublicIdentity;
+use np_adv::{
+    legacy::{
+        data_elements::{actions::*, tx_power::TxPowerDataElement, *},
+        serialize::*,
+        *,
+    },
+    shared_data::*,
+};
 use std::fmt::{Display, Formatter};
 
 /// Wrapper around a V0 advertisement builder which
@@ -29,10 +30,10 @@
 /// Generic over the Aes algorithm used for any encrypted identities,
 /// since that is generally specified at compile-time.
 pub enum BoxedAdvBuilder<C: CryptoProvider> {
-    /// Builder for public advertisements.
-    Public(AdvBuilder<PublicIdentity>),
-    /// Builder for LDT-encryptedadvertisements.
-    Ldt(AdvBuilder<LdtIdentity<C>>),
+    /// Builder for unencrypted advertisements.
+    Unencrypted(AdvBuilder<UnencryptedEncoder>),
+    /// Builder for LDT-encrypted advertisements.
+    Ldt(AdvBuilder<LdtEncoder<C>>),
 }
 
 /// Wrapper around possible errors which occur only during
@@ -41,7 +42,9 @@
 pub enum BoxedAdvConstructionError {
     /// An error originating from a problem with LDT
     /// encryption of the advertisement contents.
-    Ldt(LdtPostprocessError),
+    Ldt(LdtEncodeError),
+    /// An error from encoding an unencrypted adv
+    Unencrypted(UnencryptedEncodeError),
 }
 
 impl<C: CryptoProvider> BoxedAdvBuilder<C> {
@@ -49,15 +52,17 @@
     /// leverages some encrypted identity.
     pub fn is_encrypted(&self) -> bool {
         match self {
-            BoxedAdvBuilder::Public(_) => false,
+            BoxedAdvBuilder::Unencrypted(_) => false,
             BoxedAdvBuilder::Ldt(_) => true,
         }
     }
     /// Constructs a new BoxedAdvBuilder from the given BoxedIdentity
-    pub fn new(identity: BoxedIdentity<C>) -> Self {
+    pub fn new(identity: BoxedEncoder<C>) -> Self {
         match identity {
-            BoxedIdentity::Public(identity) => BoxedAdvBuilder::Public(AdvBuilder::new(identity)),
-            BoxedIdentity::LdtIdentity(identity) => BoxedAdvBuilder::Ldt(AdvBuilder::new(identity)),
+            BoxedEncoder::Unencrypted(encoder) => {
+                BoxedAdvBuilder::Unencrypted(AdvBuilder::new(encoder))
+            }
+            BoxedEncoder::LdtEncrypted(encoder) => BoxedAdvBuilder::Ldt(AdvBuilder::new(encoder)),
         }
     }
     /// Attempts to add a data element to the advertisement
@@ -66,10 +71,10 @@
     /// if something went wrong in the attempt to add the DE.
     pub fn add_data_element(
         &mut self,
-        data_element: ToBoxedDataElementBundle,
+        data_element: ToBoxedSerializeDataElement,
     ) -> Result<(), BoxedAddDataElementError> {
         match self {
-            BoxedAdvBuilder::Public(public_builder) => {
+            BoxedAdvBuilder::Unencrypted(public_builder) => {
                 //Verify that we can get the data element as plaintext
                 let maybe_plaintext_data_element = data_element.to_plaintext();
                 match maybe_plaintext_data_element {
@@ -93,15 +98,10 @@
     }
     /// Consume this BoxedAdvBuilder and attempt to create
     /// a serialized advertisement including the added DEs.
-    pub fn into_advertisement(
-        self,
-    ) -> Result<ArrayView<u8, BLE_ADV_SVC_CONTENT_LEN>, BoxedAdvConstructionError> {
+    pub fn into_advertisement(self) -> Result<SerializedAdv, BoxedAdvConstructionError> {
         match self {
-            BoxedAdvBuilder::Public(x) => {
-                match x.into_advertisement() {
-                    Ok(x) => Ok(x),
-                    Err(x) => match x {}, //Infallible
-                }
+            BoxedAdvBuilder::Unencrypted(x) => {
+                x.into_advertisement().map_err(BoxedAdvConstructionError::Unencrypted)
             }
             BoxedAdvBuilder::Ldt(x) => {
                 x.into_advertisement().map_err(BoxedAdvConstructionError::Ldt)
@@ -145,121 +145,100 @@
     }
 }
 
-/// Trait object reference to a `ToDataElementBundle<I>` with lifetime `'a`.
-/// Implements `ToDataElementBundle<I>` by deferring to the wrapped trait object.
-pub struct DynamicToDataElementBundle<'a, I: PacketFlavor> {
-    wrapped: &'a dyn ToDataElementBundle<I>,
-}
-
-impl<'a, I: PacketFlavor> From<&'a dyn ToDataElementBundle<I>>
-    for DynamicToDataElementBundle<'a, I>
-{
-    fn from(wrapped: &'a dyn ToDataElementBundle<I>) -> Self {
-        DynamicToDataElementBundle { wrapped }
-    }
-}
-
-impl<'a, I: PacketFlavor> ToDataElementBundle<I> for DynamicToDataElementBundle<'a, I> {
-    fn to_de_bundle(&self) -> DataElementBundle<I> {
-        self.wrapped.to_de_bundle()
-    }
-}
-
 /// Trait for types which can provide trait object
-/// references to either plaintext or ciphertext [ToDataElementBundle]
-pub trait ToMultiFlavorElementBundle {
-    /// Gets the associated trait object reference to a `ToDataElementBundle<Plaintext>`
+/// references to either plaintext or ciphertext [SerializeDataElement]
+pub trait ToMultiFlavorSerializeDataElement {
+    /// Gets the associated trait object reference to a `SerializeDataElement<Plaintext>`
     /// with the same lifetime as a reference to the implementor.
-    fn to_plaintext(&self) -> DynamicToDataElementBundle<Plaintext>;
+    fn to_plaintext(&self) -> DynamicSerializeDataElement<Plaintext>;
 
-    /// Gets the associated trait object reference to a `ToDataElementBundle<Ciphertext>`
+    /// Gets the associated trait object reference to a `SerializeDataElement<Ciphertext>`
     /// with the same lifetime as a reference to the implementor.
-    fn to_ciphertext(&self) -> DynamicToDataElementBundle<Ciphertext>;
+    fn to_ciphertext(&self) -> DynamicSerializeDataElement<Ciphertext>;
 }
 
-/// Blanket impl of [ToMultiFlavorElementBundle] for implementors of [ToDataElementBundle]
+/// Blanket impl of [ToMultiFlavorSerializeDataElement] for implementors of [SerializeDataElement]
 /// for both [Plaintext] and [Ciphertext] packet flavors.
-impl<T: ToDataElementBundle<Plaintext> + ToDataElementBundle<Ciphertext>> ToMultiFlavorElementBundle
-    for T
+impl<T: SerializeDataElement<Plaintext> + SerializeDataElement<Ciphertext>>
+    ToMultiFlavorSerializeDataElement for T
 {
-    fn to_plaintext(&self) -> DynamicToDataElementBundle<Plaintext> {
-        let reference: &dyn ToDataElementBundle<Plaintext> = self;
+    fn to_plaintext(&self) -> DynamicSerializeDataElement<Plaintext> {
+        let reference: &dyn SerializeDataElement<Plaintext> = self;
         reference.into()
     }
-    fn to_ciphertext(&self) -> DynamicToDataElementBundle<Ciphertext> {
-        let reference: &dyn ToDataElementBundle<Ciphertext> = self;
+    fn to_ciphertext(&self) -> DynamicSerializeDataElement<Ciphertext> {
+        let reference: &dyn SerializeDataElement<Ciphertext> = self;
         reference.into()
     }
 }
 
-/// Boxed trait object version of [ToDataElementBundle] which incorporates
+/// Boxed trait object version of [SerializeDataElement] which incorporates
 /// all possible variants on generatable packet flavoring
-/// (`Plaintext`, `Ciphertext`, or both, as a [ToMultiFlavorElementBundle])
-pub enum ToBoxedDataElementBundle {
+/// (`Plaintext`, `Ciphertext`, or both, as a [ToMultiFlavorSerializeDataElement])
+pub enum ToBoxedSerializeDataElement {
     /// The underlying DE is plaintext-only.
-    Plaintext(Box<dyn ToDataElementBundle<Plaintext>>),
+    Plaintext(Box<dyn SerializeDataElement<Plaintext>>),
     /// The underlying DE is ciphertext-only.
-    Ciphertext(Box<dyn ToDataElementBundle<Ciphertext>>),
+    Ciphertext(Box<dyn SerializeDataElement<Ciphertext>>),
     /// The underlying DE may exist in plaintext or
     /// in ciphertext advertisements.
-    Both(Box<dyn ToMultiFlavorElementBundle>),
+    Both(Box<dyn ToMultiFlavorSerializeDataElement>),
 }
 
-impl ToBoxedDataElementBundle {
-    /// If this [ToBoxedDataElementBundle] can generate plaintext, returns
-    /// a trait object reference to a `ToDataElementBundle<Plaintext>`
-    pub fn to_plaintext(&self) -> Option<DynamicToDataElementBundle<Plaintext>> {
+impl ToBoxedSerializeDataElement {
+    /// If this [ToBoxedSerializeDataElement] can generate plaintext, returns
+    /// a trait object reference to a `SerializeDataElement<Plaintext>`
+    pub fn to_plaintext(&self) -> Option<DynamicSerializeDataElement<Plaintext>> {
         match &self {
-            ToBoxedDataElementBundle::Plaintext(x) => Some(x.as_ref().into()),
-            ToBoxedDataElementBundle::Ciphertext(_) => None,
-            ToBoxedDataElementBundle::Both(x) => Some(x.as_ref().to_plaintext()),
+            ToBoxedSerializeDataElement::Plaintext(x) => Some(x.as_ref().into()),
+            ToBoxedSerializeDataElement::Ciphertext(_) => None,
+            ToBoxedSerializeDataElement::Both(x) => Some(x.as_ref().to_plaintext()),
         }
     }
-    /// If this [ToBoxedDataElementBundle] can generate ciphertext, returns
-    /// a trait object reference to a `ToDataElementBundle<Ciphertext>`
-    pub fn to_ciphertext(&self) -> Option<DynamicToDataElementBundle<Ciphertext>> {
+    /// If this [ToBoxedSerializeDataElement] can generate ciphertext, returns
+    /// a trait object reference to a `SerializeDataElement<Ciphertext>`
+    pub fn to_ciphertext(&self) -> Option<DynamicSerializeDataElement<Ciphertext>> {
         match &self {
-            ToBoxedDataElementBundle::Plaintext(_) => None,
-            ToBoxedDataElementBundle::Ciphertext(x) => Some(x.as_ref().into()),
-            ToBoxedDataElementBundle::Both(x) => Some(x.as_ref().to_ciphertext()),
+            ToBoxedSerializeDataElement::Plaintext(_) => None,
+            ToBoxedSerializeDataElement::Ciphertext(x) => Some(x.as_ref().into()),
+            ToBoxedSerializeDataElement::Both(x) => Some(x.as_ref().to_ciphertext()),
         }
     }
 }
 
-/// Boxed version of implementors of the Identity trait.
-/// A is the underlying Aes algorithm leveraged by ciphertext-based identities.
-pub enum BoxedIdentity<C: CryptoProvider> {
-    /// Public Identity.
-    Public(PublicIdentity),
-    /// An encrypted identity, using LDT encryption.
-    LdtIdentity(LdtIdentity<C>),
+/// Boxed version of implementors of the [AdvEncoder] trait.
+pub enum BoxedEncoder<C: CryptoProvider> {
+    /// Unencrypted encoding.
+    Unencrypted(UnencryptedEncoder),
+    /// An encrypted encoding, using LDT encryption.
+    LdtEncrypted(LdtEncoder<C>),
 }
 
-impl<C: CryptoProvider> From<PublicIdentity> for BoxedIdentity<C> {
-    fn from(public_identity: PublicIdentity) -> BoxedIdentity<C> {
-        BoxedIdentity::Public(public_identity)
+impl<C: CryptoProvider> From<UnencryptedEncoder> for BoxedEncoder<C> {
+    fn from(encoder: UnencryptedEncoder) -> BoxedEncoder<C> {
+        BoxedEncoder::Unencrypted(encoder)
     }
 }
 
-impl<C: CryptoProvider> From<LdtIdentity<C>> for BoxedIdentity<C> {
-    fn from(ldt_identity: LdtIdentity<C>) -> BoxedIdentity<C> {
-        BoxedIdentity::LdtIdentity(ldt_identity)
+impl<C: CryptoProvider> From<LdtEncoder<C>> for BoxedEncoder<C> {
+    fn from(encoder: LdtEncoder<C>) -> BoxedEncoder<C> {
+        BoxedEncoder::LdtEncrypted(encoder)
     }
 }
 
-impl From<TxPower> for ToBoxedDataElementBundle {
+impl From<TxPower> for ToBoxedSerializeDataElement {
     fn from(data: TxPower) -> Self {
-        ToBoxedDataElementBundle::Both(Box::new(TxPowerDataElement::from(data)))
+        ToBoxedSerializeDataElement::Both(Box::new(TxPowerDataElement::from(data)))
     }
 }
 
-impl From<BoxedActionBits> for ToBoxedDataElementBundle {
+impl From<BoxedActionBits> for ToBoxedSerializeDataElement {
     fn from(action_bits: BoxedActionBits) -> Self {
         match action_bits {
-            BoxedActionBits::Plaintext(action_bits) => {
-                ToBoxedDataElementBundle::Plaintext(Box::new(ActionsDataElement::from(action_bits)))
-            }
-            BoxedActionBits::Ciphertext(action_bits) => ToBoxedDataElementBundle::Ciphertext(
+            BoxedActionBits::Plaintext(action_bits) => ToBoxedSerializeDataElement::Plaintext(
+                Box::new(ActionsDataElement::from(action_bits)),
+            ),
+            BoxedActionBits::Ciphertext(action_bits) => ToBoxedSerializeDataElement::Ciphertext(
                 Box::new(ActionsDataElement::from(action_bits)),
             ),
         }
@@ -269,8 +248,10 @@
 /// Boxed version of `ToActionElement` which allows abstracting over
 /// what packet flavors are supported by a given action.
 pub enum ToBoxedActionElement {
-    /// A context-sync sequence number.
-    ContextSyncSeqNum(ContextSyncSeqNum),
+    /// Action bit for cross device SDK.
+    CrossDevSdk(bool),
+    /// Action bit for call transfer.
+    CallTransfer(bool),
     /// Action bit for active unlock.
     ActiveUnlock(bool),
     /// Action bit for nearby share.
@@ -279,12 +260,6 @@
     InstantTethering(bool),
     /// Action bit for PhoneHub.
     PhoneHub(bool),
-    /// Action bit for Finder.
-    Finder(bool),
-    /// Action bit for Fast Pair/SASS
-    FastPairSass(bool),
-    /// Action bit for Presence Manager.
-    PresenceManager(bool),
 }
 
 /// [`ActionBits`] with runtime-determined packet flavoring
@@ -323,25 +298,17 @@
         }
     }
 
-    /// Gets the context-sync sequence number from these boxed action bits.
-    pub fn get_context_sync_seq_num(&self) -> ContextSyncSeqNum {
-        match self {
-            BoxedActionBits::Plaintext(x) => x.context_sync_seq_num(),
-            BoxedActionBits::Ciphertext(x) => x.context_sync_seq_num(),
-        }
-    }
-
     /// Returns whether a boolean action type is set in these action bits, or `None`
     /// if the given action type does not represent a boolean (e.g: a context-sync
     /// sequence number).
-    pub fn has_action(&self, action_type: &ActionType) -> Option<bool> {
+    pub fn has_action(&self, action_type: ActionType) -> bool {
         match self {
             BoxedActionBits::Plaintext(x) => x.has_action(action_type),
             BoxedActionBits::Ciphertext(x) => x.has_action(action_type),
         }
     }
 
-    fn set<F: PacketFlavor, E: ToActionElement<F>>(
+    fn set<F: PacketFlavor, E: ActionElementFlavor<F>>(
         action_bits: &mut ActionBits<F>,
         to_element: E,
     ) -> Result<(), BoxedSetActionFlavorError> {
@@ -358,18 +325,24 @@
     ) -> Result<(), BoxedSetActionFlavorError> {
         match self {
             BoxedActionBits::Plaintext(action_bits) => match to_element {
-                ToBoxedActionElement::ContextSyncSeqNum(x) => Self::set(action_bits, x),
+                ToBoxedActionElement::CrossDevSdk(b) => {
+                    Self::set(action_bits, CrossDevSdk::from(b))
+                }
                 ToBoxedActionElement::NearbyShare(b) => {
                     Self::set(action_bits, NearbyShare::from(b))
                 }
-                ToBoxedActionElement::Finder(b) => Self::set(action_bits, Finder::from(b)),
-                ToBoxedActionElement::FastPairSass(b) => {
-                    Self::set(action_bits, FastPairSass::from(b))
-                }
-                _ => Err(BoxedSetActionFlavorError),
+                ToBoxedActionElement::CallTransfer(_)
+                | ToBoxedActionElement::ActiveUnlock(_)
+                | ToBoxedActionElement::InstantTethering(_)
+                | ToBoxedActionElement::PhoneHub(_) => Err(BoxedSetActionFlavorError),
             },
             BoxedActionBits::Ciphertext(action_bits) => match to_element {
-                ToBoxedActionElement::ContextSyncSeqNum(x) => Self::set(action_bits, x),
+                ToBoxedActionElement::CrossDevSdk(b) => {
+                    Self::set(action_bits, CrossDevSdk::from(b))
+                }
+                ToBoxedActionElement::CallTransfer(b) => {
+                    Self::set(action_bits, CallTransfer::from(b))
+                }
                 ToBoxedActionElement::ActiveUnlock(b) => {
                     Self::set(action_bits, ActiveUnlock::from(b))
                 }
@@ -380,10 +353,6 @@
                     Self::set(action_bits, InstantTethering::from(b))
                 }
                 ToBoxedActionElement::PhoneHub(b) => Self::set(action_bits, PhoneHub::from(b)),
-                ToBoxedActionElement::PresenceManager(b) => {
-                    Self::set(action_bits, PresenceManager::from(b))
-                }
-                _ => Err(BoxedSetActionFlavorError),
             },
         }
     }
diff --git a/nearby/presence/np_c_ffi/include/c/np_c_ffi.h b/nearby/presence/np_c_ffi/include/c/np_c_ffi.h
index 0ff152f..0eb9ecd 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
@@ -35,6 +35,19 @@
 #include <stdlib.h>
 
 /**
+ * The possible boolean action types which can be present in an Actions data element
+ */
+enum np_ffi_ActionType {
+  NP_FFI_ACTION_TYPE_CROSS_DEV_SDK = 1,
+  NP_FFI_ACTION_TYPE_CALL_TRANSFER = 4,
+  NP_FFI_ACTION_TYPE_ACTIVE_UNLOCK = 8,
+  NP_FFI_ACTION_TYPE_NEARBY_SHARE = 9,
+  NP_FFI_ACTION_TYPE_INSTANT_TETHERING = 10,
+  NP_FFI_ACTION_TYPE_PHONE_HUB = 11,
+};
+typedef uint8_t np_ffi_ActionType;
+
+/**
  * Result type for trying to add a V0 credential to a credential-slab.
  */
 enum np_ffi_AddV0CredentialToSlabResult {
@@ -161,37 +174,6 @@
 typedef uint8_t np_ffi_AdvertisementBuilderKind;
 
 /**
- * The possible boolean action types which can be present in an Actions data element
- */
-enum np_ffi_BooleanActionType {
-  NP_FFI_BOOLEAN_ACTION_TYPE_ACTIVE_UNLOCK = 8,
-  NP_FFI_BOOLEAN_ACTION_TYPE_NEARBY_SHARE = 9,
-  NP_FFI_BOOLEAN_ACTION_TYPE_INSTANT_TETHERING = 10,
-  NP_FFI_BOOLEAN_ACTION_TYPE_PHONE_HUB = 11,
-  NP_FFI_BOOLEAN_ACTION_TYPE_PRESENCE_MANAGER = 12,
-  NP_FFI_BOOLEAN_ACTION_TYPE_FINDER = 13,
-  NP_FFI_BOOLEAN_ACTION_TYPE_FAST_PAIR_SASS = 14,
-};
-typedef uint8_t np_ffi_BooleanActionType;
-
-/**
- * Discriminant for `BuildContextSyncSeqNumResult`.
- */
-enum np_ffi_BuildContextSyncSeqNumResultKind {
-  /**
-   * The sequence number was outside the allowed
-   * 0-15 single-nibble range.
-   */
-  NP_FFI_BUILD_CONTEXT_SYNC_SEQ_NUM_RESULT_KIND_OUT_OF_RANGE = 0,
-  /**
-   * The sequence number was in range,
-   * and so a `ContextSyncSeqNum` was constructed.
-   */
-  NP_FFI_BUILD_CONTEXT_SYNC_SEQ_NUM_RESULT_KIND_SUCCESS = 1,
-};
-typedef uint8_t np_ffi_BuildContextSyncSeqNumResultKind;
-
-/**
  * Discriminant for `BuildTxPowerResult`.
  */
 enum np_ffi_BuildTxPowerResultKind {
@@ -219,73 +201,14 @@
    */
   NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_KIND_SUCCESS = 0,
   /**
-   * There was no space left to create a new credential book
-   */
-  NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_KIND_NO_SPACE_LEFT = 1,
-  /**
    * The slab that we tried to create a credential-book from
    * actually was an invalid handle.
    */
-  NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_KIND_INVALID_SLAB_HANDLE = 2,
+  NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_KIND_INVALID_SLAB_HANDLE = 1,
 };
 typedef uint8_t np_ffi_CreateCredentialBookResultKind;
 
 /**
- * Discriminant for `CreateCredentialSlabResult`
- */
-enum np_ffi_CreateCredentialSlabResultKind {
-  /**
-   * There was no space left to create a new credential slab
-   */
-  NP_FFI_CREATE_CREDENTIAL_SLAB_RESULT_KIND_NO_SPACE_LEFT = 0,
-  /**
-   * We created a new credential slab behind the given handle.
-   * The associated payload may be obtained via
-   * `CreateCredentialSlabResult#into_success()`.
-   */
-  NP_FFI_CREATE_CREDENTIAL_SLAB_RESULT_KIND_SUCCESS = 1,
-};
-typedef uint8_t np_ffi_CreateCredentialSlabResultKind;
-
-/**
- * Discriminant for `CreateV0AdvertisementBuilderResult`
- */
-enum np_ffi_CreateV0AdvertisementBuilderResultKind {
-  /**
-   * The attempt to create a new advertisement builder
-   * failed since there are no more available
-   * slots for V0 advertisement builders in their handle-map.
-   */
-  NP_FFI_CREATE_V0_ADVERTISEMENT_BUILDER_RESULT_KIND_NO_SPACE_LEFT = 0,
-  /**
-   * The attempt succeeded. The wrapped advertisement builder
-   * may be obtained via
-   * `CreateV0AdvertisementBuilderResult#into_success`.
-   */
-  NP_FFI_CREATE_V0_ADVERTISEMENT_BUILDER_RESULT_KIND_SUCCESS = 1,
-};
-typedef uint8_t np_ffi_CreateV0AdvertisementBuilderResultKind;
-
-/**
- * Discriminant for `CreateV1AdvertisementBuilderResult`
- */
-enum np_ffi_CreateV1AdvertisementBuilderResultKind {
-  /**
-   * The attempt to create a new advertisement builder
-   * failed since there are no more available
-   * slots for V1 advertisement builders in their handle-map.
-   */
-  NP_FFI_CREATE_V1_ADVERTISEMENT_BUILDER_RESULT_KIND_NO_SPACE_LEFT = 0,
-  /**
-   * The attempt succeeded. The wrapped advertisement builder
-   * may be obtained via
-   * `CreateV1AdvertisementBuilderResult#into_success`.
-   */
-  NP_FFI_CREATE_V1_ADVERTISEMENT_BUILDER_RESULT_KIND_SUCCESS = 1,
-};
-typedef uint8_t np_ffi_CreateV1AdvertisementBuilderResultKind;
-
-/**
  * Discriminant for `CreateV1SectionBuilderResult`
  */
 enum np_ffi_CreateV1SectionBuilderResultKind {
@@ -325,11 +248,11 @@
   /**
    * The requested handle to deallocate was not present in the map
    */
-  NP_FFI_DEALLOCATE_RESULT_NOT_PRESENT = 0,
+  NP_FFI_DEALLOCATE_RESULT_NOT_PRESENT = 1,
   /**
    * The object behind the handle was successfully deallocated
    */
-  NP_FFI_DEALLOCATE_RESULT_SUCCESS = 1,
+  NP_FFI_DEALLOCATE_RESULT_SUCCESS = 2,
 } np_ffi_DeallocateResult;
 
 /**
@@ -382,14 +305,14 @@
    * The associated payload may be obtained via
    * `DeserializedV0Advertisement#into_legible`.
    */
-  NP_FFI_DESERIALIZED_V0_ADVERTISEMENT_KIND_LEGIBLE = 0,
+  NP_FFI_DESERIALIZED_V0_ADVERTISEMENT_KIND_LEGIBLE = 1,
   /**
    * The deserialized V0 advertisement is illegible,
    * likely meaning that the receiver does not hold
    * the proper credentials to be able to read
    * the received advertisement.
    */
-  NP_FFI_DESERIALIZED_V0_ADVERTISEMENT_KIND_NO_MATCHING_CREDENTIALS = 1,
+  NP_FFI_DESERIALIZED_V0_ADVERTISEMENT_KIND_NO_MATCHING_CREDENTIALS = 2,
 };
 typedef uint8_t np_ffi_DeserializedV0AdvertisementKind;
 
@@ -401,11 +324,11 @@
   /**
    * The deserialized identity was a plaintext identity.
    */
-  NP_FFI_DESERIALIZED_V0_IDENTITY_KIND_PLAINTEXT = 0,
+  NP_FFI_DESERIALIZED_V0_IDENTITY_KIND_PLAINTEXT = 1,
   /**
    * The deserialized identity was some decrypted identity.
    */
-  NP_FFI_DESERIALIZED_V0_IDENTITY_KIND_DECRYPTED = 1,
+  NP_FFI_DESERIALIZED_V0_IDENTITY_KIND_DECRYPTED = 2,
 };
 typedef uint8_t np_ffi_DeserializedV0IdentityKind;
 
@@ -425,28 +348,6 @@
 };
 typedef uint8_t np_ffi_DeserializedV1IdentityKind;
 
-/**
- * The DE type for an encrypted identity
- */
-enum np_ffi_EncryptedIdentityType {
-  /**
-   * Identity for broadcasts to nearby devices with the same
-   * logged-in-account (for some account).
-   */
-  NP_FFI_ENCRYPTED_IDENTITY_TYPE_PRIVATE = 1,
-  /**
-   * Identity for broadcasts to nearby devices which this
-   * device has declared to trust.
-   */
-  NP_FFI_ENCRYPTED_IDENTITY_TYPE_TRUSTED = 2,
-  /**
-   * Identity for broadcasts to devices which have been provisioned
-   * offline with this device.
-   */
-  NP_FFI_ENCRYPTED_IDENTITY_TYPE_PROVISIONED = 4,
-};
-typedef uint8_t np_ffi_EncryptedIdentityType;
-
 enum np_ffi_GetMetadataBufferPartsResultKind {
   NP_FFI_GET_METADATA_BUFFER_PARTS_RESULT_KIND_SUCCESS = 0,
   NP_FFI_GET_METADATA_BUFFER_PARTS_RESULT_KIND_ERROR = 1,
@@ -620,6 +521,12 @@
    * in an entirely unexpected way.
    */
   NP_FFI_PANIC_REASON_INVALID_STACK_DATA_STRUCTURE = 2,
+  /**
+   * The maximum amount of allocations per type is `u32::MAX`, this panic handler is invoked
+   * with this reason when this is exceeded. Clients should never need more than 4 Billions
+   * handles and would certainly run into other issues before reaching that point
+   */
+  NP_FFI_PANIC_REASON_EXCEEDED_MAX_HANDLE_ALLOCATIONS = 3,
 };
 typedef uint8_t np_ffi_PanicReason;
 
@@ -632,16 +539,21 @@
    */
   NP_FFI_SERIALIZE_V0_ADVERTISEMENT_RESULT_KIND_SUCCESS = 0,
   /**
+   * The advertisement builder handle was invalid.
+   */
+  NP_FFI_SERIALIZE_V0_ADVERTISEMENT_RESULT_KIND_INVALID_ADVERTISEMENT_BUILDER_HANDLE = 1,
+  /**
    * Serializing the advertisement to bytes failed
    * because the data in the advertisement wasn't
    * of an appropriate size for LDT encryption
    * to succeed.
    */
-  NP_FFI_SERIALIZE_V0_ADVERTISEMENT_RESULT_KIND_LDT_ERROR = 1,
+  NP_FFI_SERIALIZE_V0_ADVERTISEMENT_RESULT_KIND_LDT_ERROR = 2,
   /**
-   * The advertisement builder handle was invalid.
+   * Serializing an unencrypted adv failed because the adv data didn't meet the length
+   * requirements.
    */
-  NP_FFI_SERIALIZE_V0_ADVERTISEMENT_RESULT_KIND_INVALID_ADVERTISEMENT_BUILDER_HANDLE = 2,
+  NP_FFI_SERIALIZE_V0_ADVERTISEMENT_RESULT_KIND_UNENCRYPTED_ERROR = 3,
 };
 typedef uint8_t np_ffi_SerializeV0AdvertisementResultKind;
 
@@ -693,13 +605,13 @@
    * The associated payload may be obtained via
    * `V0DataElement#into_tx_power`.
    */
-  NP_FFI_V0_DATA_ELEMENT_KIND_TX_POWER = 0,
+  NP_FFI_V0_DATA_ELEMENT_KIND_TX_POWER = 1,
   /**
    * The Actions data-element.
    * The associated payload may be obtained via
    * `V0DataElement#into_actions`.
    */
-  NP_FFI_V0_DATA_ELEMENT_KIND_ACTIONS = 1,
+  NP_FFI_V0_DATA_ELEMENT_KIND_ACTIONS = 2,
 };
 typedef uint8_t np_ffi_V0DataElementKind;
 
@@ -721,6 +633,19 @@
 typedef uint8_t np_ffi_V1VerificationMode;
 
 /**
+ * Holds the count of handles currently allocated for each handle type
+ */
+typedef struct {
+  uint32_t cred_book;
+  uint32_t cred_slab;
+  uint32_t decrypted_metadata;
+  uint32_t v0_payload;
+  uint32_t legible_v1_sections;
+  uint32_t v0_advertisement_builder;
+  uint32_t v1_advertisement_builder;
+} np_ffi_CurrentHandleAllocations;
+
+/**
  * A `#[repr(C)]` handle to a value of type `CredentialBookInternals`
  */
 typedef struct {
@@ -732,7 +657,6 @@
  */
 enum np_ffi_CreateCredentialBookResult_Tag {
   NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_SUCCESS = 0,
-  NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_NO_SPACE_LEFT = 1,
   NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_INVALID_SLAB_HANDLE = 2,
 };
 typedef uint8_t np_ffi_CreateCredentialBookResult_Tag;
@@ -753,29 +677,12 @@
 } np_ffi_CredentialSlab;
 
 /**
- * Result type for `create_credential_slab`
- */
-typedef enum {
-  NP_FFI_CREATE_CREDENTIAL_SLAB_RESULT_NO_SPACE_LEFT,
-  NP_FFI_CREATE_CREDENTIAL_SLAB_RESULT_SUCCESS,
-} np_ffi_CreateCredentialSlabResult_Tag;
-
-typedef struct {
-  np_ffi_CreateCredentialSlabResult_Tag tag;
-  union {
-    struct {
-      np_ffi_CredentialSlab success;
-    };
-  };
-} np_ffi_CreateCredentialSlabResult;
-
-/**
  * Cryptographic information about a particular V0 discovery credential
  * necessary to match and decrypt encrypted V0 advertisements.
  */
 typedef struct {
   uint8_t key_seed[32];
-  uint8_t legacy_metadata_key_hmac[32];
+  uint8_t identity_token_hmac[32];
 } np_ffi_V0DiscoveryCredential;
 
 /**
@@ -803,8 +710,9 @@
  */
 typedef struct {
   uint8_t key_seed[32];
-  uint8_t expected_unsigned_metadata_key_hmac[32];
-  uint8_t expected_signed_metadata_key_hmac[32];
+  uint8_t expected_mic_short_salt_identity_token_hmac[32];
+  uint8_t expected_mic_extended_salt_identity_token_hmac[32];
+  uint8_t expected_signature_identity_token_hmac[32];
   uint8_t pub_key[32];
 } np_ffi_V1DiscoveryCredential;
 
@@ -908,8 +816,17 @@
  * Representation of a deserialized V1 advertisement
  */
 typedef struct {
+  /**
+   * The number of legible sections
+   */
   uint8_t num_legible_sections;
+  /**
+   * The number of sections that were unable to be decrypted
+   */
   uint8_t num_undecryptable_sections;
+  /**
+   * A handle to the set of legible (plain or decrypted) sections
+   */
   np_ffi_LegibleV1Sections legible_sections;
 } np_ffi_DeserializedV1Advertisement;
 
@@ -977,7 +894,7 @@
 } np_ffi_TxPower;
 
 /**
- * The bitfield data of a VOActions data element
+ * The bitfield data of a V0Actions data element
  */
 typedef struct {
   uint32_t bitfield;
@@ -1052,18 +969,14 @@
  */
 typedef struct {
   /**
-   * The identity type (private/provisioned/trusted)
-   */
-  np_ffi_EncryptedIdentityType identity_type;
-  /**
    * The ID of the credential which
    * matched the deserialized adv
    */
   uint32_t cred_id;
   /**
-   * The 14-byte legacy metadata key
+   * The 14-byte legacy identity token
    */
-  uint8_t metadata_key[14];
+  uint8_t identity_token[14];
   /**
    * The 2-byte advertisement salt
    */
@@ -1202,10 +1115,6 @@
  */
 typedef struct {
   /**
-   * The identity type (private/provisioned/trusted)
-   */
-  np_ffi_EncryptedIdentityType identity_type;
-  /**
    * The verification mode (MIC/Signature) which
    * was used to verify the decrypted adv contents.
    */
@@ -1218,7 +1127,7 @@
   /**
    * The 16-byte metadata key.
    */
-  uint8_t metadata_key[16];
+  uint8_t identity_token[16];
 } np_ffi_DeserializedV1IdentityDetails;
 
 /**
@@ -1297,8 +1206,9 @@
  */
 typedef enum {
   NP_FFI_SERIALIZE_V0_ADVERTISEMENT_RESULT_SUCCESS,
-  NP_FFI_SERIALIZE_V0_ADVERTISEMENT_RESULT_LDT_ERROR,
   NP_FFI_SERIALIZE_V0_ADVERTISEMENT_RESULT_INVALID_ADVERTISEMENT_BUILDER_HANDLE,
+  NP_FFI_SERIALIZE_V0_ADVERTISEMENT_RESULT_LDT_ERROR,
+  NP_FFI_SERIALIZE_V0_ADVERTISEMENT_RESULT_UNENCRYPTED_ERROR,
 } np_ffi_SerializeV0AdvertisementResult_Tag;
 
 typedef struct {
@@ -1311,29 +1221,12 @@
 } np_ffi_SerializeV0AdvertisementResult;
 
 /**
- * The result of attempting to create a new V0 advertisement builder.
- */
-typedef enum {
-  NP_FFI_CREATE_V0_ADVERTISEMENT_BUILDER_RESULT_NO_SPACE_LEFT,
-  NP_FFI_CREATE_V0_ADVERTISEMENT_BUILDER_RESULT_SUCCESS,
-} np_ffi_CreateV0AdvertisementBuilderResult_Tag;
-
-typedef struct {
-  np_ffi_CreateV0AdvertisementBuilderResult_Tag tag;
-  union {
-    struct {
-      np_ffi_V0AdvertisementBuilder success;
-    };
-  };
-} np_ffi_CreateV0AdvertisementBuilderResult;
-
-/**
  * Cryptographic information about a particular V0 broadcast credential
  * necessary to LDT-encrypt V0 advertisements.
  */
 typedef struct {
   uint8_t key_seed[32];
-  uint8_t metadata_key[14];
+  uint8_t identity_token[14];
 } np_ffi_V0BroadcastCredential;
 
 /**
@@ -1359,7 +1252,8 @@
 } np_ffi_V1AdvertisementBuilder;
 
 /**
- * A handle to a builder for V1 sections.
+ * 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.
  */
 typedef struct {
   np_ffi_V1AdvertisementBuilder adv_builder;
@@ -1392,7 +1286,7 @@
  */
 typedef struct {
   uint8_t key_seed[32];
-  uint8_t metadata_key[16];
+  uint8_t identity_token[16];
   uint8_t private_key[32];
 } np_ffi_V1BroadcastCredential;
 
@@ -1427,23 +1321,6 @@
 } np_ffi_SerializeV1AdvertisementResult;
 
 /**
- * The result of attempting to create a new V1 advertisement builder.
- */
-typedef enum {
-  NP_FFI_CREATE_V1_ADVERTISEMENT_BUILDER_RESULT_NO_SPACE_LEFT,
-  NP_FFI_CREATE_V1_ADVERTISEMENT_BUILDER_RESULT_SUCCESS,
-} np_ffi_CreateV1AdvertisementBuilderResult_Tag;
-
-typedef struct {
-  np_ffi_CreateV1AdvertisementBuilderResult_Tag tag;
-  union {
-    struct {
-      np_ffi_V1AdvertisementBuilder success;
-    };
-  };
-} np_ffi_CreateV1AdvertisementBuilderResult;
-
-/**
  * The result of attempting to get the derived V1 DE
  * 16-byte salt for the next-added DE to the section
  * builder behind the given handle.
@@ -1522,31 +1399,6 @@
 } np_ffi_SetV0ActionResult;
 
 /**
- * Representation of a context-sync sequence number.
- */
-typedef struct {
-  uint8_t value;
-} np_ffi_ContextSyncSeqNum;
-
-/**
- * Result type for attempting to construct a
- * ContextSyncSeqNum from an unsigned byte.
- */
-typedef enum {
-  NP_FFI_BUILD_CONTEXT_SYNC_SEQ_NUM_RESULT_OUT_OF_RANGE,
-  NP_FFI_BUILD_CONTEXT_SYNC_SEQ_NUM_RESULT_SUCCESS,
-} np_ffi_BuildContextSyncSeqNumResult_Tag;
-
-typedef struct {
-  np_ffi_BuildContextSyncSeqNumResult_Tag tag;
-  union {
-    struct {
-      np_ffi_ContextSyncSeqNum success;
-    };
-  };
-} np_ffi_BuildContextSyncSeqNumResult;
-
-/**
  * Overrides the global panic handler to be used when NP C FFI calls panic.
  * This method will only have an effect on the global panic-handler
  * the first time it's called, and this method will return `true`
@@ -1569,6 +1421,12 @@
 bool np_ffi_global_config_panic_handler(void (*handler)(np_ffi_PanicReason));
 
 /**
+ * Checks the current count of all outstanding handle allocations, useful for debugging,
+ * logging, and testing
+ */
+np_ffi_CurrentHandleAllocations np_ffi_global_config_get_current_allocation_count(void);
+
+/**
  * Sets an override to the number of shards to employ in the NP FFI's
  * internal handle-maps, which places an upper bound on the number
  * of writing threads which may make progress at any one time
@@ -1587,106 +1445,6 @@
 void np_ffi_global_config_set_num_shards(uint8_t num_shards);
 
 /**
- * Sets the maximum number of active handles to credential slabs
- * which may be active at any one time.
- * Default value: Max value.
- * Max value: `u32::MAX - 1`.
- *
- * Useful for bounding the maximum memory used by the client application
- * on credential slabs in constrained-memory environments.
- *
- * Setting this value will have no effect if the handle-maps for the
- * API have already begun being used by the client code, and any
- * values set will take effect upon the first usage of any API
- * call utilizing credential slabs.
- */
-void np_ffi_global_config_set_max_num_credential_slabs(uint32_t max_num_credential_slabs);
-
-/**
- * Sets the maximum number of active handles to credential books
- * which may be active at any one time.
- * Default value: Max value.
- * Max value: `u32::MAX - 1`.
- *
- * Useful for bounding the maximum memory used by the client application
- * on credential books in constrained-memory environments.
- *
- * Setting this value will have no effect if the handle-maps for the
- * API have already begun being used by the client code, and any
- * values set will take effect upon the first usage of any API
- * call utilizing credential books.
- */
-void np_ffi_global_config_set_max_num_credential_books(uint32_t max_num_credential_books);
-
-/**
- * Sets the maximum number of active handles to deserialized v0
- * advertisements which may be active at any one time.
- *
- * Useful for bounding the maximum memory used by the client application
- * on v0 advertisements in constrained-memory environments.
- *
- * Default value: Max value.
- * Max value: `u32::MAX - 1`.
- *
- * Setting this value will have no effect if the handle-maps for the
- * API have already begun being used by the client code, and any
- * values set will take effect upon the first usage of any API
- * call which references or returns a deserialized V0 advertisement.
- */
-void np_ffi_global_config_set_max_num_deserialized_v0_advertisements(uint32_t max_num_deserialized_v0_advertisements);
-
-/**
- * Sets the maximum number of active handles to deserialized v1
- * advertisements which may be active at any one time.
- *
- * Useful for bounding the maximum memory used by the client application
- * on v1 advertisements in constrained-memory environments.
- *
- * Default value: Max value.
- * Max value: `u32::MAX - 1`.
- *
- * Setting this value will have no effect if the handle-maps for the
- * API have already begun being used by the client code, and any
- * values set will take effect upon the first usage of any API
- * call which references or returns a deserialized V1 advertisement.
- */
-void np_ffi_global_config_set_max_num_deserialized_v1_advertisements(uint32_t max_num_deserialized_v1_advertisements);
-
-/**
- * Sets the maximum number of active handles to v0 advertisement
- * builders which may be active at any one time.
- *
- * Useful for bounding the maximum memory used by the client application
- * on v0 advertisements in constrained-memory environments.
- *
- * Default value: Max value.
- * Max value: `u32::MAX - 1`.
- *
- * Setting this value will have no effect if the handle-maps for the
- * API have already begun being used by the client code, and any
- * values set will take effect upon the first usage of any API
- * call which references or returns a V0 advertisement builder.
- */
-void np_ffi_global_config_set_max_num_v0_advertisement_builders(uint32_t max_num_v0_advertisement_builders);
-
-/**
- * Sets the maximum number of active handles to v1 advertisement
- * builders which may be active at any one time.
- *
- * Useful for bounding the maximum memory used by the client application
- * on v1 advertisements in constrained-memory environments.
- *
- * Default value: Max value.
- * Max value: `u32::MAX - 1`.
- *
- * Setting this value will have no effect if the handle-maps for the
- * API have already begun being used by the client code, and any
- * values set will take effect upon the first usage of any API
- * call which references or returns a V1 advertisement builder.
- */
-void np_ffi_global_config_set_max_num_v1_advertisement_builders(uint32_t max_num_v1_advertisement_builders);
-
-/**
  * Allocates a new credential-book from the given slab, returning a handle
  * to the created object. The slab will be deallocated by this call.
  */
@@ -1716,18 +1474,7 @@
 /**
  * Allocates a new credential-slab, returning a handle to the created object
  */
-np_ffi_CreateCredentialSlabResult np_ffi_create_credential_slab(void);
-
-/**
- * Gets the tag of a `CreateCredentialSlabResult` tagged enum.
- */
-np_ffi_CreateCredentialSlabResultKind np_ffi_CreateCredentialSlabResult_kind(np_ffi_CreateCredentialSlabResult result);
-
-/**
- * Casts a `CreateCredentialSlabResult` to the `SUCCESS` variant, panicking in the
- * case where the passed value is of a different enum variant.
- */
-np_ffi_CredentialSlab np_ffi_CreateCredentialSlabResult_into_SUCCESS(np_ffi_CreateCredentialSlabResult result);
+np_ffi_CredentialSlab np_ffi_create_credential_slab(void);
 
 /**
  * Adds the given V0 discovery credential with some associated
@@ -2033,27 +1780,15 @@
 np_ffi_DeallocateResult np_ffi_deallocate_v0_advertisement_builder(np_ffi_V0AdvertisementBuilder adv_builder);
 
 /**
- * Gets the tag of a `CreateV0AdvertisementBuilderResult` tagged-union.
- */
-np_ffi_CreateV0AdvertisementBuilderResultKind np_ffi_CreateV0AdvertisementBuilderResult_kind(np_ffi_CreateV0AdvertisementBuilderResult result);
-
-/**
- * Casts a `CreateV0AdvertisementBuilderResult` to the `Success` variant,
- * panicking in the case where the passed value is of a different enum variant.
- */
-np_ffi_V0AdvertisementBuilder np_ffi_CreateV0AdvertisementBuilderResult_into_SUCCESS(np_ffi_CreateV0AdvertisementBuilderResult result);
-
-/**
  * Creates a new V0 advertisement builder for a public advertisement.
  */
-np_ffi_CreateV0AdvertisementBuilderResult np_ffi_create_v0_public_advertisement_builder(void);
+np_ffi_V0AdvertisementBuilder np_ffi_create_v0_public_advertisement_builder(void);
 
 /**
  * Creates a new V0 advertisement builder for an encrypted advertisement.
  */
-np_ffi_CreateV0AdvertisementBuilderResult np_ffi_create_v0_encrypted_advertisement_builder(np_ffi_V0BroadcastCredential broadcast_cred,
-                                                                                           np_ffi_EncryptedIdentityType identity_type,
-                                                                                           np_ffi_FixedSizeArray_2 salt);
+np_ffi_V0AdvertisementBuilder np_ffi_create_v0_encrypted_advertisement_builder(np_ffi_V0BroadcastCredential broadcast_cred,
+                                                                               np_ffi_FixedSizeArray_2 salt);
 
 /**
  * Gets the tag of a `SerializeV0AdvertisementResult` tagged-union.
@@ -2097,7 +1832,6 @@
  */
 np_ffi_CreateV1SectionBuilderResult np_ffi_V1AdvertisementBuilder_encrypted_section_builder(np_ffi_V1AdvertisementBuilder adv_builder,
                                                                                             np_ffi_V1BroadcastCredential broadcast_cred,
-                                                                                            np_ffi_EncryptedIdentityType identity_type,
                                                                                             np_ffi_V1VerificationMode verification_mode);
 
 /**
@@ -2109,21 +1843,10 @@
 np_ffi_SerializeV1AdvertisementResult np_ffi_V1AdvertisementBuilder_into_advertisement(np_ffi_V1AdvertisementBuilder adv_builder);
 
 /**
- * Gets the tag of a `CreateV1AdvertisementBuilderResult` tagged-union.
- */
-np_ffi_CreateV1AdvertisementBuilderResultKind np_ffi_CreateV1AdvertisementBuilderResult_kind(np_ffi_CreateV1AdvertisementBuilderResult result);
-
-/**
- * Casts a `CreateV1AdvertisementBuilderResult` to the `Success` variant,
- * panicking in the case where the passed value is of a different enum variant.
- */
-np_ffi_V1AdvertisementBuilder np_ffi_CreateV1AdvertisementBuilderResult_into_SUCCESS(np_ffi_CreateV1AdvertisementBuilderResult result);
-
-/**
  * Creates a new V1 advertisement builder for the given advertisement
  * kind (public/encrypted).
  */
-np_ffi_CreateV1AdvertisementBuilderResult np_ffi_create_v1_advertisement_builder(np_ffi_AdvertisementBuilderKind kind);
+np_ffi_V1AdvertisementBuilder np_ffi_create_v1_advertisement_builder(np_ffi_AdvertisementBuilderKind kind);
 
 /**
  * Gets the tag of a `SerializeV1AdvertisementResult` tagged-union.
@@ -2256,12 +1979,7 @@
 /**
  * Return whether a boolean action type is set in this data element
  */
-bool np_ffi_V0Actions_has_action(np_ffi_V0Actions actions, np_ffi_BooleanActionType action_type);
-
-/**
- * Gets the 4 bit context sync sequence number as a u8 from this data element
- */
-np_ffi_ContextSyncSeqNum np_ffi_V0Actions_get_context_sync_sequence_number(np_ffi_V0Actions actions);
+bool np_ffi_V0Actions_has_action(np_ffi_V0Actions actions, np_ffi_ActionType action_type);
 
 /**
  * Attempts to set the given action bit to the given boolean value.
@@ -2272,44 +1990,16 @@
  * unaltered.
  */
 np_ffi_SetV0ActionResult np_ffi_V0Actions_set_action(np_ffi_V0Actions actions,
-                                                     np_ffi_BooleanActionType action_type,
+                                                     np_ffi_ActionType action_type,
                                                      bool value);
 
 /**
- * Sets the context sequence number for the given Actions DE.
- */
-np_ffi_V0Actions np_ffi_V0Actions_set_context_sync_sequence_number(np_ffi_V0Actions actions,
-                                                                   np_ffi_ContextSyncSeqNum value);
-
-/**
  * Returns the representation of the passed `V0Actions` as an unsigned
  * integer, where the bit-positions correspond to individual actions.
  */
 uint32_t np_ffi_V0Actions_as_u32(np_ffi_V0Actions actions);
 
 /**
- * Gets the tag of a `BuildContextSyncSeqNumResult` tagged-union.
- */
-np_ffi_BuildContextSyncSeqNumResultKind np_ffi_BuildContextSyncSeqNumResult_kind(np_ffi_BuildContextSyncSeqNumResult result);
-
-/**
- * Casts a `BuildContextSyncSeqNumResult` to the `Success` variant, panicking in the
- * case where the passed value is of a different enum variant.
- */
-np_ffi_ContextSyncSeqNum np_ffi_BuildContextSyncSeqNumResult_into_SUCCESS(np_ffi_BuildContextSyncSeqNumResult result);
-
-/**
- * Attempts to build a new context sync sequence number
- * from the given unsigned byte.
- */
-np_ffi_BuildContextSyncSeqNumResult np_ffi_ContextSyncSeqNum_build_from_unsigned_byte(uint8_t value);
-
-/**
- * Gets the value of the given context-sync sequence number as an unsigned byte.
- */
-uint8_t np_ffi_ContextSyncSeqNum_as_unsigned_byte(np_ffi_ContextSyncSeqNum seq_num);
-
-/**
  * Converts a `V1DataElement` to a `GenericV1DataElement` which
  * only maintains information about the DE's type-code and payload.
  */
diff --git a/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h b/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h
index 51322d3..d197bc8 100644
--- a/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h
+++ b/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h
@@ -61,6 +61,10 @@
 /// but a bare `loop { }` when this crate is compiled without.
 bool np_ffi_global_config_panic_handler(void (*handler)(PanicReason));
 
+/// Checks the current count of all outstanding handle allocations, useful for debugging,
+/// logging, and testing
+CurrentHandleAllocations np_ffi_global_config_get_current_allocation_count();
+
 /// Sets an override to the number of shards to employ in the NP FFI's
 /// internal handle-maps, which places an upper bound on the number
 /// of writing threads which may make progress at any one time
@@ -77,94 +81,6 @@
 /// API call.
 void np_ffi_global_config_set_num_shards(uint8_t num_shards);
 
-/// Sets the maximum number of active handles to credential slabs
-/// which may be active at any one time.
-/// Default value: Max value.
-/// Max value: `u32::MAX - 1`.
-///
-/// Useful for bounding the maximum memory used by the client application
-/// on credential slabs in constrained-memory environments.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call utilizing credential slabs.
-void np_ffi_global_config_set_max_num_credential_slabs(uint32_t max_num_credential_slabs);
-
-/// Sets the maximum number of active handles to credential books
-/// which may be active at any one time.
-/// Default value: Max value.
-/// Max value: `u32::MAX - 1`.
-///
-/// Useful for bounding the maximum memory used by the client application
-/// on credential books in constrained-memory environments.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call utilizing credential books.
-void np_ffi_global_config_set_max_num_credential_books(uint32_t max_num_credential_books);
-
-/// Sets the maximum number of active handles to deserialized v0
-/// advertisements which may be active at any one time.
-///
-/// Useful for bounding the maximum memory used by the client application
-/// on v0 advertisements in constrained-memory environments.
-///
-/// Default value: Max value.
-/// Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call which references or returns a deserialized V0 advertisement.
-void np_ffi_global_config_set_max_num_deserialized_v0_advertisements(uint32_t max_num_deserialized_v0_advertisements);
-
-/// Sets the maximum number of active handles to deserialized v1
-/// advertisements which may be active at any one time.
-///
-/// Useful for bounding the maximum memory used by the client application
-/// on v1 advertisements in constrained-memory environments.
-///
-/// Default value: Max value.
-/// Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call which references or returns a deserialized V1 advertisement.
-void np_ffi_global_config_set_max_num_deserialized_v1_advertisements(uint32_t max_num_deserialized_v1_advertisements);
-
-/// Sets the maximum number of active handles to v0 advertisement
-/// builders which may be active at any one time.
-///
-/// Useful for bounding the maximum memory used by the client application
-/// on v0 advertisements in constrained-memory environments.
-///
-/// Default value: Max value.
-/// Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call which references or returns a V0 advertisement builder.
-void np_ffi_global_config_set_max_num_v0_advertisement_builders(uint32_t max_num_v0_advertisement_builders);
-
-/// Sets the maximum number of active handles to v1 advertisement
-/// builders which may be active at any one time.
-///
-/// Useful for bounding the maximum memory used by the client application
-/// on v1 advertisements in constrained-memory environments.
-///
-/// Default value: Max value.
-/// Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call which references or returns a V1 advertisement builder.
-void np_ffi_global_config_set_max_num_v1_advertisement_builders(uint32_t max_num_v1_advertisement_builders);
-
 /// Allocates a new credential-book from the given slab, returning a handle
 /// to the created object. The slab will be deallocated by this call.
 CreateCredentialBookResult np_ffi_create_credential_book_from_slab(CredentialSlab slab);
@@ -183,14 +99,7 @@
 DeallocateResult np_ffi_deallocate_credential_book(CredentialBook credential_book);
 
 /// Allocates a new credential-slab, returning a handle to the created object
-CreateCredentialSlabResult np_ffi_create_credential_slab();
-
-/// Gets the tag of a `CreateCredentialSlabResult` tagged enum.
-CreateCredentialSlabResultKind np_ffi_CreateCredentialSlabResult_kind(CreateCredentialSlabResult result);
-
-/// Casts a `CreateCredentialSlabResult` to the `SUCCESS` variant, panicking in the
-/// case where the passed value is of a different enum variant.
-CredentialSlab np_ffi_CreateCredentialSlabResult_into_SUCCESS(CreateCredentialSlabResult result);
+CredentialSlab np_ffi_create_credential_slab();
 
 /// Adds the given V0 discovery credential with some associated
 /// match-data to this credential slab.
@@ -396,20 +305,12 @@
 /// the given handle.
 DeallocateResult np_ffi_deallocate_v0_advertisement_builder(V0AdvertisementBuilder adv_builder);
 
-/// Gets the tag of a `CreateV0AdvertisementBuilderResult` tagged-union.
-CreateV0AdvertisementBuilderResultKind np_ffi_CreateV0AdvertisementBuilderResult_kind(CreateV0AdvertisementBuilderResult result);
-
-/// Casts a `CreateV0AdvertisementBuilderResult` to the `Success` variant,
-/// panicking in the case where the passed value is of a different enum variant.
-V0AdvertisementBuilder np_ffi_CreateV0AdvertisementBuilderResult_into_SUCCESS(CreateV0AdvertisementBuilderResult result);
-
 /// Creates a new V0 advertisement builder for a public advertisement.
-CreateV0AdvertisementBuilderResult np_ffi_create_v0_public_advertisement_builder();
+V0AdvertisementBuilder np_ffi_create_v0_public_advertisement_builder();
 
 /// Creates a new V0 advertisement builder for an encrypted advertisement.
-CreateV0AdvertisementBuilderResult np_ffi_create_v0_encrypted_advertisement_builder(V0BroadcastCredential broadcast_cred,
-                                                                                    EncryptedIdentityType identity_type,
-                                                                                    FixedSizeArray<2> salt);
+V0AdvertisementBuilder np_ffi_create_v0_encrypted_advertisement_builder(V0BroadcastCredential broadcast_cred,
+                                                                        FixedSizeArray<2> salt);
 
 /// Gets the tag of a `SerializeV0AdvertisementResult` tagged-union.
 SerializeV0AdvertisementResultKind np_ffi_SerializeV0AdvertisementResult_kind(SerializeV0AdvertisementResult result);
@@ -445,7 +346,6 @@
 /// to fit within the enclosing advertisement.
 CreateV1SectionBuilderResult np_ffi_V1AdvertisementBuilder_encrypted_section_builder(V1AdvertisementBuilder adv_builder,
                                                                                      V1BroadcastCredential broadcast_cred,
-                                                                                     EncryptedIdentityType identity_type,
                                                                                      V1VerificationMode verification_mode);
 
 /// Attempts to serialize the contents of the advertisement builder
@@ -454,16 +354,9 @@
 /// advertisement builder handle being deallocated.
 SerializeV1AdvertisementResult np_ffi_V1AdvertisementBuilder_into_advertisement(V1AdvertisementBuilder adv_builder);
 
-/// Gets the tag of a `CreateV1AdvertisementBuilderResult` tagged-union.
-CreateV1AdvertisementBuilderResultKind np_ffi_CreateV1AdvertisementBuilderResult_kind(CreateV1AdvertisementBuilderResult result);
-
-/// Casts a `CreateV1AdvertisementBuilderResult` to the `Success` variant,
-/// panicking in the case where the passed value is of a different enum variant.
-V1AdvertisementBuilder np_ffi_CreateV1AdvertisementBuilderResult_into_SUCCESS(CreateV1AdvertisementBuilderResult result);
-
 /// Creates a new V1 advertisement builder for the given advertisement
 /// kind (public/encrypted).
-CreateV1AdvertisementBuilderResult np_ffi_create_v1_advertisement_builder(AdvertisementBuilderKind kind);
+V1AdvertisementBuilder np_ffi_create_v1_advertisement_builder(AdvertisementBuilderKind kind);
 
 /// Gets the tag of a `SerializeV1AdvertisementResult` tagged-union.
 SerializeV1AdvertisementResultKind np_ffi_SerializeV1AdvertisementResult_kind(SerializeV1AdvertisementResult result);
@@ -550,10 +443,7 @@
 V0Actions np_ffi_build_new_zeroed_V0Actions(AdvertisementBuilderKind kind);
 
 /// Return whether a boolean action type is set in this data element
-bool np_ffi_V0Actions_has_action(V0Actions actions, BooleanActionType action_type);
-
-/// Gets the 4 bit context sync sequence number as a u8 from this data element
-ContextSyncSeqNum np_ffi_V0Actions_get_context_sync_sequence_number(V0Actions actions);
+bool np_ffi_V0Actions_has_action(V0Actions actions, ActionType action_type);
 
 /// Attempts to set the given action bit to the given boolean value.
 /// This operation may fail if the requested action bit may not be
@@ -562,31 +452,13 @@
 /// the original action bits will be yielded back to the caller,
 /// unaltered.
 SetV0ActionResult np_ffi_V0Actions_set_action(V0Actions actions,
-                                              BooleanActionType action_type,
+                                              ActionType action_type,
                                               bool value);
 
-/// Sets the context sequence number for the given Actions DE.
-V0Actions np_ffi_V0Actions_set_context_sync_sequence_number(V0Actions actions,
-                                                            ContextSyncSeqNum value);
-
 /// Returns the representation of the passed `V0Actions` as an unsigned
 /// integer, where the bit-positions correspond to individual actions.
 uint32_t np_ffi_V0Actions_as_u32(V0Actions actions);
 
-/// Gets the tag of a `BuildContextSyncSeqNumResult` tagged-union.
-BuildContextSyncSeqNumResultKind np_ffi_BuildContextSyncSeqNumResult_kind(BuildContextSyncSeqNumResult result);
-
-/// Casts a `BuildContextSyncSeqNumResult` to the `Success` variant, panicking in the
-/// case where the passed value is of a different enum variant.
-ContextSyncSeqNum np_ffi_BuildContextSyncSeqNumResult_into_SUCCESS(BuildContextSyncSeqNumResult result);
-
-/// Attempts to build a new context sync sequence number
-/// from the given unsigned byte.
-BuildContextSyncSeqNumResult np_ffi_ContextSyncSeqNum_build_from_unsigned_byte(uint8_t value);
-
-/// Gets the value of the given context-sync sequence number as an unsigned byte.
-uint8_t np_ffi_ContextSyncSeqNum_as_unsigned_byte(ContextSyncSeqNum seq_num);
-
 /// Converts a `V1DataElement` to a `GenericV1DataElement` which
 /// only maintains information about the DE's type-code and payload.
 GenericV1DataElement np_ffi_V1DataElement_to_generic(V1DataElement de);
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 5e4e5a2..ba3ed34 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
@@ -38,6 +38,16 @@
 namespace np_ffi {
 namespace internal {
 
+/// The possible boolean action types which can be present in an Actions data element
+enum class ActionType : uint8_t {
+  CrossDevSdk = 1,
+  CallTransfer = 4,
+  ActiveUnlock = 8,
+  NearbyShare = 9,
+  InstantTethering = 10,
+  PhoneHub = 11,
+};
+
 /// Result type for trying to add a V0 credential to a credential-slab.
 enum class AddV0CredentialToSlabResult : uint8_t {
   /// We succeeded in adding the credential to the slab.
@@ -112,27 +122,6 @@
   Encrypted = 1,
 };
 
-/// The possible boolean action types which can be present in an Actions data element
-enum class BooleanActionType : uint8_t {
-  ActiveUnlock = 8,
-  NearbyShare = 9,
-  InstantTethering = 10,
-  PhoneHub = 11,
-  PresenceManager = 12,
-  Finder = 13,
-  FastPairSass = 14,
-};
-
-/// Discriminant for `BuildContextSyncSeqNumResult`.
-enum class BuildContextSyncSeqNumResultKind : uint8_t {
-  /// The sequence number was outside the allowed
-  /// 0-15 single-nibble range.
-  OutOfRange = 0,
-  /// The sequence number was in range,
-  /// and so a `ContextSyncSeqNum` was constructed.
-  Success = 1,
-};
-
 /// Discriminant for `BuildTxPowerResult`.
 enum class BuildTxPowerResultKind : uint8_t {
   /// The transmission power was outside the
@@ -149,45 +138,9 @@
   /// The associated payload may be obtained via
   /// `CreateCredentialBookResult#into_success()`.
   Success = 0,
-  /// There was no space left to create a new credential book
-  NoSpaceLeft = 1,
   /// The slab that we tried to create a credential-book from
   /// actually was an invalid handle.
-  InvalidSlabHandle = 2,
-};
-
-/// Discriminant for `CreateCredentialSlabResult`
-enum class CreateCredentialSlabResultKind : uint8_t {
-  /// There was no space left to create a new credential slab
-  NoSpaceLeft = 0,
-  /// We created a new credential slab behind the given handle.
-  /// The associated payload may be obtained via
-  /// `CreateCredentialSlabResult#into_success()`.
-  Success = 1,
-};
-
-/// Discriminant for `CreateV0AdvertisementBuilderResult`
-enum class CreateV0AdvertisementBuilderResultKind : uint8_t {
-  /// The attempt to create a new advertisement builder
-  /// failed since there are no more available
-  /// slots for V0 advertisement builders in their handle-map.
-  NoSpaceLeft = 0,
-  /// The attempt succeeded. The wrapped advertisement builder
-  /// may be obtained via
-  /// `CreateV0AdvertisementBuilderResult#into_success`.
-  Success = 1,
-};
-
-/// Discriminant for `CreateV1AdvertisementBuilderResult`
-enum class CreateV1AdvertisementBuilderResultKind : uint8_t {
-  /// The attempt to create a new advertisement builder
-  /// failed since there are no more available
-  /// slots for V1 advertisement builders in their handle-map.
-  NoSpaceLeft = 0,
-  /// The attempt succeeded. The wrapped advertisement builder
-  /// may be obtained via
-  /// `CreateV1AdvertisementBuilderResult#into_success`.
-  Success = 1,
+  InvalidSlabHandle = 1,
 };
 
 /// Discriminant for `CreateV1SectionBuilderResult`
@@ -213,9 +166,9 @@
 /// succeeded or failed due to the requested handle not being present.
 enum class DeallocateResult {
   /// The requested handle to deallocate was not present in the map
-  NotPresent = 0,
+  NotPresent = 1,
   /// The object behind the handle was successfully deallocated
-  Success = 1,
+  Success = 2,
 };
 
 /// Discriminant for `DecryptMetadataResult`.
@@ -248,21 +201,21 @@
   /// The deserialized V0 advertisement was legible.
   /// The associated payload may be obtained via
   /// `DeserializedV0Advertisement#into_legible`.
-  Legible = 0,
+  Legible = 1,
   /// The deserialized V0 advertisement is illegible,
   /// likely meaning that the receiver does not hold
   /// the proper credentials to be able to read
   /// the received advertisement.
-  NoMatchingCredentials = 1,
+  NoMatchingCredentials = 2,
 };
 
 /// Discriminant for deserialized information about the V0
 /// identity utilized by a deserialized V0 advertisement.
 enum class DeserializedV0IdentityKind : uint8_t {
   /// The deserialized identity was a plaintext identity.
-  Plaintext = 0,
+  Plaintext = 1,
   /// The deserialized identity was some decrypted identity.
-  Decrypted = 1,
+  Decrypted = 2,
 };
 
 /// Discriminant for `DeserializedV1Identity`.
@@ -274,19 +227,6 @@
   Decrypted = 1,
 };
 
-/// The DE type for an encrypted identity
-enum class EncryptedIdentityType : uint8_t {
-  /// Identity for broadcasts to nearby devices with the same
-  /// logged-in-account (for some account).
-  Private = 1,
-  /// Identity for broadcasts to nearby devices which this
-  /// device has declared to trust.
-  Trusted = 2,
-  /// Identity for broadcasts to devices which have been provisioned
-  /// offline with this device.
-  Provisioned = 4,
-};
-
 enum class GetMetadataBufferPartsResultKind : uint8_t {
   Success = 0,
   Error = 1,
@@ -402,19 +342,26 @@
   /// be messing with stack-allocated data structures for this library
   /// in an entirely unexpected way.
   InvalidStackDataStructure = 2,
+  /// The maximum amount of allocations per type is `u32::MAX`, this panic handler is invoked
+  /// with this reason when this is exceeded. Clients should never need more than 4 Billions
+  /// handles and would certainly run into other issues before reaching that point
+  ExceededMaxHandleAllocations = 3,
 };
 
 /// Discriminant for `SerializeV0AdvertisementResult`.
 enum class SerializeV0AdvertisementResultKind : uint8_t {
   /// Serializing the advertisement to bytes was successful.
   Success = 0,
+  /// The advertisement builder handle was invalid.
+  InvalidAdvertisementBuilderHandle = 1,
   /// Serializing the advertisement to bytes failed
   /// because the data in the advertisement wasn't
   /// of an appropriate size for LDT encryption
   /// to succeed.
-  LdtError = 1,
-  /// The advertisement builder handle was invalid.
-  InvalidAdvertisementBuilderHandle = 2,
+  LdtError = 2,
+  /// Serializing an unencrypted adv failed because the adv data didn't meet the length
+  /// requirements.
+  UnencryptedError = 3,
 };
 
 /// Discriminant for `SerializeV1AdvertisementResult`.
@@ -445,11 +392,11 @@
   /// A transmission Power (Tx Power) data-element.
   /// The associated payload may be obtained via
   /// `V0DataElement#into_tx_power`.
-  TxPower = 0,
+  TxPower = 1,
   /// The Actions data-element.
   /// The associated payload may be obtained via
   /// `V0DataElement#into_actions`.
-  Actions = 1,
+  Actions = 2,
 };
 
 /// Information about the verification scheme used
@@ -462,6 +409,17 @@
   Signature = 1,
 };
 
+/// Holds the count of handles currently allocated for each handle type
+struct CurrentHandleAllocations {
+  uint32_t cred_book;
+  uint32_t cred_slab;
+  uint32_t decrypted_metadata;
+  uint32_t v0_payload;
+  uint32_t legible_v1_sections;
+  uint32_t v0_advertisement_builder;
+  uint32_t v1_advertisement_builder;
+};
+
 /// A `#[repr(C)]` handle to a value of type `CredentialBookInternals`
 struct CredentialBook {
   uint64_t handle_id;
@@ -471,7 +429,6 @@
 union CreateCredentialBookResult {
   enum class Tag : uint8_t {
     Success = 0,
-    NoSpaceLeft = 1,
     InvalidSlabHandle = 2,
   };
 
@@ -491,28 +448,11 @@
   uint64_t handle_id;
 };
 
-/// Result type for `create_credential_slab`
-struct CreateCredentialSlabResult {
-  enum class Tag {
-    NoSpaceLeft,
-    Success,
-  };
-
-  struct Success_Body {
-    CredentialSlab _0;
-  };
-
-  Tag tag;
-  union {
-    Success_Body success;
-  };
-};
-
 /// Cryptographic information about a particular V0 discovery credential
 /// necessary to match and decrypt encrypted V0 advertisements.
 struct V0DiscoveryCredential {
   uint8_t key_seed[32];
-  uint8_t legacy_metadata_key_hmac[32];
+  uint8_t identity_token_hmac[32];
 };
 
 /// A representation of a MatchedCredential which is passable across the FFI boundary
@@ -534,8 +474,9 @@
 /// necessary to match and decrypt encrypted V1 advertisement sections.
 struct V1DiscoveryCredential {
   uint8_t key_seed[32];
-  uint8_t expected_unsigned_metadata_key_hmac[32];
-  uint8_t expected_signed_metadata_key_hmac[32];
+  uint8_t expected_mic_short_salt_identity_token_hmac[32];
+  uint8_t expected_mic_extended_salt_identity_token_hmac[32];
+  uint8_t expected_signature_identity_token_hmac[32];
   uint8_t pub_key[32];
 };
 
@@ -627,8 +568,11 @@
 
 /// Representation of a deserialized V1 advertisement
 struct DeserializedV1Advertisement {
+  /// The number of legible sections
   uint8_t num_legible_sections;
+  /// The number of sections that were unable to be decrypted
   uint8_t num_undecryptable_sections;
+  /// A handle to the set of legible (plain or decrypted) sections
   LegibleV1Sections legible_sections;
 };
 
@@ -687,7 +631,7 @@
   int8_t tx_power;
 };
 
-/// The bitfield data of a VOActions data element
+/// The bitfield data of a V0Actions data element
 struct V0ActionBits {
   uint32_t bitfield;
 };
@@ -758,13 +702,11 @@
 /// Information about the identity which matched a
 /// decrypted V0 advertisement.
 struct DeserializedV0IdentityDetails {
-  /// The identity type (private/provisioned/trusted)
-  EncryptedIdentityType identity_type;
   /// The ID of the credential which
   /// matched the deserialized adv
   uint32_t cred_id;
-  /// The 14-byte legacy metadata key
-  uint8_t metadata_key[14];
+  /// The 14-byte legacy identity token
+  uint8_t identity_token[14];
   /// The 2-byte advertisement salt
   uint8_t salt[2];
 };
@@ -873,8 +815,6 @@
 /// Information about the identity which matched
 /// a decrypted V1 section.
 struct DeserializedV1IdentityDetails {
-  /// The identity type (private/provisioned/trusted)
-  EncryptedIdentityType identity_type;
   /// The verification mode (MIC/Signature) which
   /// was used to verify the decrypted adv contents.
   V1VerificationMode verification_mode;
@@ -882,7 +822,7 @@
   /// matched the deserialized section.
   uint32_t cred_id;
   /// The 16-byte metadata key.
-  uint8_t metadata_key[16];
+  uint8_t identity_token[16];
 };
 
 /// The result of attempting to get the identity details
@@ -944,8 +884,9 @@
 struct SerializeV0AdvertisementResult {
   enum class Tag {
     Success,
-    LdtError,
     InvalidAdvertisementBuilderHandle,
+    LdtError,
+    UnencryptedError,
   };
 
   struct Success_Body {
@@ -958,28 +899,11 @@
   };
 };
 
-/// The result of attempting to create a new V0 advertisement builder.
-struct CreateV0AdvertisementBuilderResult {
-  enum class Tag {
-    NoSpaceLeft,
-    Success,
-  };
-
-  struct Success_Body {
-    V0AdvertisementBuilder _0;
-  };
-
-  Tag tag;
-  union {
-    Success_Body success;
-  };
-};
-
 /// Cryptographic information about a particular V0 broadcast credential
 /// necessary to LDT-encrypt V0 advertisements.
 struct V0BroadcastCredential {
   uint8_t key_seed[32];
-  uint8_t metadata_key[14];
+  uint8_t identity_token[14];
 };
 
 /// A `#[repr(C)]` handle to a value of type `V1AdvertisementBuilderInternals`
@@ -993,7 +917,8 @@
   V1AdvertisementBuilderHandle handle;
 };
 
-/// A handle to a builder for V1 sections.
+/// 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 {
   V1AdvertisementBuilder adv_builder;
   uint8_t section_index;
@@ -1023,7 +948,7 @@
 /// necessary to encrypt V1 MIC-verified and signature-verified sections.
 struct V1BroadcastCredential {
   uint8_t key_seed[32];
-  uint8_t metadata_key[16];
+  uint8_t identity_token[16];
   uint8_t private_key[32];
 };
 
@@ -1046,23 +971,6 @@
   };
 };
 
-/// The result of attempting to create a new V1 advertisement builder.
-struct CreateV1AdvertisementBuilderResult {
-  enum class Tag {
-    NoSpaceLeft,
-    Success,
-  };
-
-  struct Success_Body {
-    V1AdvertisementBuilder _0;
-  };
-
-  Tag tag;
-  union {
-    Success_Body success;
-  };
-};
-
 /// The result of attempting to get the derived V1 DE
 /// 16-byte salt for the next-added DE to the section
 /// builder behind the given handle.
@@ -1137,29 +1045,6 @@
   };
 };
 
-/// Representation of a context-sync sequence number.
-struct ContextSyncSeqNum {
-  uint8_t value;
-};
-
-/// Result type for attempting to construct a
-/// ContextSyncSeqNum from an unsigned byte.
-struct BuildContextSyncSeqNumResult {
-  enum class Tag {
-    OutOfRange,
-    Success,
-  };
-
-  struct Success_Body {
-    ContextSyncSeqNum _0;
-  };
-
-  Tag tag;
-  union {
-    Success_Body success;
-  };
-};
-
 } // namespace internal
 } // namespace np_ffi
 
diff --git a/nearby/presence/np_c_ffi/src/credentials.rs b/nearby/presence/np_c_ffi/src/credentials.rs
index 7dc8ac7..d03f0f6 100644
--- a/nearby/presence/np_c_ffi/src/credentials.rs
+++ b/nearby/presence/np_c_ffi/src/credentials.rs
@@ -13,12 +13,16 @@
 // limitations under the License.
 //! Credential-related data-types and functions
 
-use crate::{unwrap, PanicReason};
+use crate::{panic, unwrap, PanicReason};
 use core::slice;
 use np_ffi_core::common::*;
-use np_ffi_core::credentials::CredentialBook;
-use np_ffi_core::credentials::CredentialSlab;
-use np_ffi_core::credentials::*;
+use np_ffi_core::credentials::{
+    create_credential_book_from_slab, create_credential_slab, deallocate_credential_book,
+    deallocate_credential_slab, AddV0CredentialToSlabResult, AddV1CredentialToSlabResult,
+    CredentialBook, CredentialSlab, MatchedCredential, V0DiscoveryCredential,
+    V1DiscoveryCredential,
+};
+use np_ffi_core::declare_enum_cast;
 use np_ffi_core::deserialize::DecryptedMetadata;
 use np_ffi_core::deserialize::{
     DecryptMetadataResult, DecryptMetadataResultKind, GetMetadataBufferPartsResult,
@@ -32,7 +36,61 @@
 pub extern "C" fn np_ffi_create_credential_book_from_slab(
     slab: CredentialSlab,
 ) -> CreateCredentialBookResult {
-    create_credential_book_from_slab(slab)
+    create_credential_book_from_slab(slab).into()
+}
+
+/// Result type for `create_credential_book`
+#[repr(u8)]
+#[allow(missing_docs)]
+pub enum CreateCredentialBookResult {
+    Success(CredentialBook) = 0,
+    InvalidSlabHandle = 2,
+}
+
+//TODO: unwrap allocation errors at the ffi-core layer and remove this, after design has been
+// agreed upon
+impl From<np_ffi_core::credentials::CreateCredentialBookResult> for CreateCredentialBookResult {
+    fn from(value: np_ffi_core::credentials::CreateCredentialBookResult) -> Self {
+        match value {
+            np_ffi_core::credentials::CreateCredentialBookResult::Success(v) => {
+                CreateCredentialBookResult::Success(v)
+            }
+            np_ffi_core::credentials::CreateCredentialBookResult::InvalidSlabHandle => {
+                CreateCredentialBookResult::InvalidSlabHandle
+            }
+            np_ffi_core::credentials::CreateCredentialBookResult::NoSpaceLeft => {
+                panic(PanicReason::ExceededMaxHandleAllocations)
+            }
+        }
+    }
+}
+
+/// Discriminant for `CreateCredentialBookResult`
+#[repr(u8)]
+pub enum CreateCredentialBookResultKind {
+    /// We created a new credential book behind the given handle.
+    /// The associated payload may be obtained via
+    /// `CreateCredentialBookResult#into_success()`.
+    Success = 0,
+    /// The slab that we tried to create a credential-book from
+    /// actually was an invalid handle.
+    InvalidSlabHandle = 1,
+}
+
+impl np_ffi_core::utils::FfiEnum for CreateCredentialBookResult {
+    type Kind = CreateCredentialBookResultKind;
+    fn kind(&self) -> Self::Kind {
+        match self {
+            CreateCredentialBookResult::Success(_) => CreateCredentialBookResultKind::Success,
+            CreateCredentialBookResult::InvalidSlabHandle => {
+                CreateCredentialBookResultKind::InvalidSlabHandle
+            }
+        }
+    }
+}
+
+impl CreateCredentialBookResult {
+    declare_enum_cast! {into_success, Success, CredentialBook}
 }
 
 /// Gets the tag of a `CreateCredentialBookResult` tagged enum.
@@ -70,25 +128,8 @@
 
 /// Allocates a new credential-slab, returning a handle to the created object
 #[no_mangle]
-pub extern "C" fn np_ffi_create_credential_slab() -> CreateCredentialSlabResult {
-    create_credential_slab()
-}
-
-/// Gets the tag of a `CreateCredentialSlabResult` tagged enum.
-#[no_mangle]
-pub extern "C" fn np_ffi_CreateCredentialSlabResult_kind(
-    result: CreateCredentialSlabResult,
-) -> CreateCredentialSlabResultKind {
-    result.kind()
-}
-
-/// Casts a `CreateCredentialSlabResult` to the `SUCCESS` variant, panicking in the
-/// case where the passed value is of a different enum variant.
-#[no_mangle]
-pub extern "C" fn np_ffi_CreateCredentialSlabResult_into_SUCCESS(
-    result: CreateCredentialSlabResult,
-) -> CredentialSlab {
-    unwrap(result.into_success(), PanicReason::EnumCastFailed)
+pub extern "C" fn np_ffi_create_credential_slab() -> CredentialSlab {
+    unwrap(create_credential_slab().into_success(), PanicReason::ExceededMaxHandleAllocations)
 }
 
 /// Representation of a V0 credential that contains additional data to provide back to caller once it
diff --git a/nearby/presence/np_c_ffi/src/deserialize/v0.rs b/nearby/presence/np_c_ffi/src/deserialize/v0.rs
index be68720..243f3ff 100644
--- a/nearby/presence/np_c_ffi/src/deserialize/v0.rs
+++ b/nearby/presence/np_c_ffi/src/deserialize/v0.rs
@@ -14,7 +14,6 @@
 
 use crate::{unwrap, PanicReason};
 use np_ffi_core::common::DeallocateResult;
-use np_ffi_core::deserialize::v0::V0Payload;
 use np_ffi_core::deserialize::v0::*;
 use np_ffi_core::deserialize::DecryptMetadataResult;
 use np_ffi_core::utils::FfiEnum;
diff --git a/nearby/presence/np_c_ffi/src/lib.rs b/nearby/presence/np_c_ffi/src/lib.rs
index 2572887..5111aff 100644
--- a/nearby/presence/np_c_ffi/src/lib.rs
+++ b/nearby/presence/np_c_ffi/src/lib.rs
@@ -20,9 +20,9 @@
 pub mod v0;
 pub mod v1;
 
-use lock_adapter::std::RwLock;
+use lock_adapter::stdlib::RwLock;
 use lock_adapter::RwLock as _;
-use np_ffi_core::common::InvalidStackDataStructure;
+use np_ffi_core::common::{CurrentHandleAllocations, InvalidStackDataStructure};
 
 /// Structure for categorized reasons for why a NP C FFI call may
 /// be panicking.
@@ -47,6 +47,10 @@
     /// be messing with stack-allocated data structures for this library
     /// in an entirely unexpected way.
     InvalidStackDataStructure = 2,
+    /// The maximum amount of allocations per type is `u32::MAX`, this panic handler is invoked
+    /// with this reason when this is exceeded. Clients should never need more than 4 Billions
+    /// handles and would certainly run into other issues before reaching that point
+    ExceededMaxHandleAllocations = 3,
 }
 
 /// Structure which maintains information about the panic-handler
@@ -141,6 +145,13 @@
     panic_handler.set_handler(handler)
 }
 
+/// Checks the current count of all outstanding handle allocations, useful for debugging,
+/// logging, and testing
+#[no_mangle]
+pub extern "C" fn np_ffi_global_config_get_current_allocation_count() -> CurrentHandleAllocations {
+    np_ffi_core::common::global_config_get_current_allocation_count()
+}
+
 /// Sets an override to the number of shards to employ in the NP FFI's
 /// internal handle-maps, which places an upper bound on the number
 /// of writing threads which may make progress at any one time
@@ -159,125 +170,3 @@
 pub extern "C" fn np_ffi_global_config_set_num_shards(num_shards: u8) {
     np_ffi_core::common::global_config_set_num_shards(num_shards)
 }
-
-/// Sets the maximum number of active handles to credential slabs
-/// which may be active at any one time.
-/// Default value: Max value.
-/// Max value: `u32::MAX - 1`.
-///
-/// Useful for bounding the maximum memory used by the client application
-/// on credential slabs in constrained-memory environments.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call utilizing credential slabs.
-#[no_mangle]
-pub extern "C" fn np_ffi_global_config_set_max_num_credential_slabs(max_num_credential_slabs: u32) {
-    np_ffi_core::common::global_config_set_max_num_credential_slabs(max_num_credential_slabs)
-}
-
-/// Sets the maximum number of active handles to credential books
-/// which may be active at any one time.
-/// Default value: Max value.
-/// Max value: `u32::MAX - 1`.
-///
-/// Useful for bounding the maximum memory used by the client application
-/// on credential books in constrained-memory environments.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call utilizing credential books.
-#[no_mangle]
-pub extern "C" fn np_ffi_global_config_set_max_num_credential_books(max_num_credential_books: u32) {
-    np_ffi_core::common::global_config_set_max_num_credential_books(max_num_credential_books)
-}
-
-/// Sets the maximum number of active handles to deserialized v0
-/// advertisements which may be active at any one time.
-///
-/// Useful for bounding the maximum memory used by the client application
-/// on v0 advertisements in constrained-memory environments.
-///
-/// Default value: Max value.
-/// Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call which references or returns a deserialized V0 advertisement.
-#[no_mangle]
-pub extern "C" fn np_ffi_global_config_set_max_num_deserialized_v0_advertisements(
-    max_num_deserialized_v0_advertisements: u32,
-) {
-    np_ffi_core::common::global_config_set_max_num_deserialized_v0_advertisements(
-        max_num_deserialized_v0_advertisements,
-    )
-}
-
-/// Sets the maximum number of active handles to deserialized v1
-/// advertisements which may be active at any one time.
-///
-/// Useful for bounding the maximum memory used by the client application
-/// on v1 advertisements in constrained-memory environments.
-///
-/// Default value: Max value.
-/// Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call which references or returns a deserialized V1 advertisement.
-#[no_mangle]
-pub extern "C" fn np_ffi_global_config_set_max_num_deserialized_v1_advertisements(
-    max_num_deserialized_v1_advertisements: u32,
-) {
-    np_ffi_core::common::global_config_set_max_num_deserialized_v1_advertisements(
-        max_num_deserialized_v1_advertisements,
-    )
-}
-
-/// Sets the maximum number of active handles to v0 advertisement
-/// builders which may be active at any one time.
-///
-/// Useful for bounding the maximum memory used by the client application
-/// on v0 advertisements in constrained-memory environments.
-///
-/// Default value: Max value.
-/// Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call which references or returns a V0 advertisement builder.
-#[no_mangle]
-pub extern "C" fn np_ffi_global_config_set_max_num_v0_advertisement_builders(
-    max_num_v0_advertisement_builders: u32,
-) {
-    np_ffi_core::common::global_config_set_max_num_v0_advertisement_builders(
-        max_num_v0_advertisement_builders,
-    )
-}
-
-/// Sets the maximum number of active handles to v1 advertisement
-/// builders which may be active at any one time.
-///
-/// Useful for bounding the maximum memory used by the client application
-/// on v1 advertisements in constrained-memory environments.
-///
-/// Default value: Max value.
-/// Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call which references or returns a V1 advertisement builder.
-#[no_mangle]
-pub extern "C" fn np_ffi_global_config_set_max_num_v1_advertisement_builders(
-    max_num_v1_advertisement_builders: u32,
-) {
-    np_ffi_core::common::global_config_set_max_num_v1_advertisement_builders(
-        max_num_v1_advertisement_builders,
-    )
-}
diff --git a/nearby/presence/np_c_ffi/src/serialize/v0.rs b/nearby/presence/np_c_ffi/src/serialize/v0.rs
index 962522e..867c33b 100644
--- a/nearby/presence/np_c_ffi/src/serialize/v0.rs
+++ b/nearby/presence/np_c_ffi/src/serialize/v0.rs
@@ -15,7 +15,7 @@
 //! NP Rust C FFI functionality for V0 advertisement serialization.
 
 use crate::{panic_if_invalid, unwrap, PanicReason};
-use np_ffi_core::common::{ByteBuffer, DeallocateResult, EncryptedIdentityType, FixedSizeArray};
+use np_ffi_core::common::{ByteBuffer, DeallocateResult, FixedSizeArray};
 use np_ffi_core::credentials::V0BroadcastCredential;
 use np_ffi_core::serialize::v0::*;
 use np_ffi_core::utils::FfiEnum;
@@ -55,38 +55,25 @@
     adv_builder.deallocate()
 }
 
-/// Gets the tag of a `CreateV0AdvertisementBuilderResult` tagged-union.
-#[no_mangle]
-pub extern "C" fn np_ffi_CreateV0AdvertisementBuilderResult_kind(
-    result: CreateV0AdvertisementBuilderResult,
-) -> CreateV0AdvertisementBuilderResultKind {
-    result.kind()
-}
-
-/// Casts a `CreateV0AdvertisementBuilderResult` to the `Success` variant,
-/// panicking in the case where the passed value is of a different enum variant.
-#[no_mangle]
-pub extern "C" fn np_ffi_CreateV0AdvertisementBuilderResult_into_SUCCESS(
-    result: CreateV0AdvertisementBuilderResult,
-) -> V0AdvertisementBuilder {
-    unwrap(result.into_success(), PanicReason::EnumCastFailed)
-}
-
 /// Creates a new V0 advertisement builder for a public advertisement.
 #[no_mangle]
-pub extern "C" fn np_ffi_create_v0_public_advertisement_builder(
-) -> CreateV0AdvertisementBuilderResult {
-    create_v0_public_advertisement_builder()
+pub extern "C" fn np_ffi_create_v0_public_advertisement_builder() -> V0AdvertisementBuilder {
+    unwrap(
+        create_v0_public_advertisement_builder().into_success(),
+        PanicReason::ExceededMaxHandleAllocations,
+    )
 }
 
 /// Creates a new V0 advertisement builder for an encrypted advertisement.
 #[no_mangle]
 pub extern "C" fn np_ffi_create_v0_encrypted_advertisement_builder(
     broadcast_cred: V0BroadcastCredential,
-    identity_type: EncryptedIdentityType,
     salt: FixedSizeArray<2>,
-) -> CreateV0AdvertisementBuilderResult {
-    create_v0_encrypted_advertisement_builder(broadcast_cred, identity_type, salt)
+) -> V0AdvertisementBuilder {
+    unwrap(
+        create_v0_encrypted_advertisement_builder(broadcast_cred, salt).into_success(),
+        PanicReason::ExceededMaxHandleAllocations,
+    )
 }
 
 /// Gets the tag of a `SerializeV0AdvertisementResult` tagged-union.
diff --git a/nearby/presence/np_c_ffi/src/serialize/v1.rs b/nearby/presence/np_c_ffi/src/serialize/v1.rs
index c8ba135..484425b 100644
--- a/nearby/presence/np_c_ffi/src/serialize/v1.rs
+++ b/nearby/presence/np_c_ffi/src/serialize/v1.rs
@@ -15,7 +15,7 @@
 //! NP Rust C FFI functionality for V1 advertisement serialization.
 
 use crate::{unwrap, PanicReason};
-use np_ffi_core::common::{ByteBuffer, EncryptedIdentityType, FixedSizeArray};
+use np_ffi_core::common::{ByteBuffer, FixedSizeArray};
 use np_ffi_core::credentials::V1BroadcastCredential;
 use np_ffi_core::serialize::v1::*;
 use np_ffi_core::serialize::AdvertisementBuilderKind;
@@ -56,10 +56,9 @@
 pub extern "C" fn np_ffi_V1AdvertisementBuilder_encrypted_section_builder(
     adv_builder: V1AdvertisementBuilder,
     broadcast_cred: V1BroadcastCredential,
-    identity_type: EncryptedIdentityType,
     verification_mode: V1VerificationMode,
 ) -> CreateV1SectionBuilderResult {
-    adv_builder.encrypted_section_builder(broadcast_cred, identity_type, verification_mode)
+    adv_builder.encrypted_section_builder(broadcast_cred, verification_mode)
 }
 
 /// Attempts to serialize the contents of the advertisement builder
@@ -73,30 +72,16 @@
     adv_builder.into_advertisement()
 }
 
-/// Gets the tag of a `CreateV1AdvertisementBuilderResult` tagged-union.
-#[no_mangle]
-pub extern "C" fn np_ffi_CreateV1AdvertisementBuilderResult_kind(
-    result: CreateV1AdvertisementBuilderResult,
-) -> CreateV1AdvertisementBuilderResultKind {
-    result.kind()
-}
-
-/// Casts a `CreateV1AdvertisementBuilderResult` to the `Success` variant,
-/// panicking in the case where the passed value is of a different enum variant.
-#[no_mangle]
-pub extern "C" fn np_ffi_CreateV1AdvertisementBuilderResult_into_SUCCESS(
-    result: CreateV1AdvertisementBuilderResult,
-) -> V1AdvertisementBuilder {
-    unwrap(result.into_success(), PanicReason::EnumCastFailed)
-}
-
 /// Creates a new V1 advertisement builder for the given advertisement
 /// kind (public/encrypted).
 #[no_mangle]
 pub extern "C" fn np_ffi_create_v1_advertisement_builder(
     kind: AdvertisementBuilderKind,
-) -> CreateV1AdvertisementBuilderResult {
-    create_v1_advertisement_builder(kind)
+) -> V1AdvertisementBuilder {
+    unwrap(
+        create_v1_advertisement_builder(kind).into_success(),
+        PanicReason::ExceededMaxHandleAllocations,
+    )
 }
 
 /// Gets the tag of a `SerializeV1AdvertisementResult` tagged-union.
diff --git a/nearby/presence/np_c_ffi/src/v0.rs b/nearby/presence/np_c_ffi/src/v0.rs
index 17302aa..7bcb980 100644
--- a/nearby/presence/np_c_ffi/src/v0.rs
+++ b/nearby/presence/np_c_ffi/src/v0.rs
@@ -110,27 +110,13 @@
 
 /// Return whether a boolean action type is set in this data element
 #[no_mangle]
-pub extern "C" fn np_ffi_V0Actions_has_action(
-    actions: V0Actions,
-    action_type: BooleanActionType,
-) -> bool {
+pub extern "C" fn np_ffi_V0Actions_has_action(actions: V0Actions, action_type: ActionType) -> bool {
     match actions.has_action(action_type) {
         Ok(b) => b,
         Err(_) => panic(PanicReason::InvalidStackDataStructure),
     }
 }
 
-/// Gets the 4 bit context sync sequence number as a u8 from this data element
-#[no_mangle]
-pub extern "C" fn np_ffi_V0Actions_get_context_sync_sequence_number(
-    actions: V0Actions,
-) -> ContextSyncSeqNum {
-    match actions.get_context_sync_seq_num() {
-        Ok(b) => b,
-        Err(_) => panic(PanicReason::InvalidStackDataStructure),
-    }
-}
-
 /// Attempts to set the given action bit to the given boolean value.
 /// This operation may fail if the requested action bit may not be
 /// set for the kind of containing advertisement (public/encrypted)
@@ -140,56 +126,15 @@
 #[no_mangle]
 pub extern "C" fn np_ffi_V0Actions_set_action(
     actions: V0Actions,
-    action_type: BooleanActionType,
+    action_type: ActionType,
     value: bool,
 ) -> SetV0ActionResult {
     panic_if_invalid(actions.set_action(action_type, value))
 }
 
-/// Sets the context sequence number for the given Actions DE.
-#[no_mangle]
-pub extern "C" fn np_ffi_V0Actions_set_context_sync_sequence_number(
-    actions: V0Actions,
-    value: ContextSyncSeqNum,
-) -> V0Actions {
-    panic_if_invalid(actions.set_context_sync_seq_num(value))
-}
-
 /// Returns the representation of the passed `V0Actions` as an unsigned
 /// integer, where the bit-positions correspond to individual actions.
 #[no_mangle]
 pub extern "C" fn np_ffi_V0Actions_as_u32(actions: V0Actions) -> u32 {
     actions.as_u32()
 }
-
-/// Gets the tag of a `BuildContextSyncSeqNumResult` tagged-union.
-#[no_mangle]
-pub extern "C" fn np_ffi_BuildContextSyncSeqNumResult_kind(
-    result: BuildContextSyncSeqNumResult,
-) -> BuildContextSyncSeqNumResultKind {
-    result.kind()
-}
-
-/// Casts a `BuildContextSyncSeqNumResult` to the `Success` variant, panicking in the
-/// case where the passed value is of a different enum variant.
-#[no_mangle]
-pub extern "C" fn np_ffi_BuildContextSyncSeqNumResult_into_SUCCESS(
-    result: BuildContextSyncSeqNumResult,
-) -> ContextSyncSeqNum {
-    unwrap(result.into_success(), PanicReason::EnumCastFailed)
-}
-
-/// Attempts to build a new context sync sequence number
-/// from the given unsigned byte.
-#[no_mangle]
-pub extern "C" fn np_ffi_ContextSyncSeqNum_build_from_unsigned_byte(
-    value: u8,
-) -> BuildContextSyncSeqNumResult {
-    ContextSyncSeqNum::build_from_unsigned_byte(value)
-}
-
-/// Gets the value of the given context-sync sequence number as an unsigned byte.
-#[no_mangle]
-pub extern "C" fn np_ffi_ContextSyncSeqNum_as_unsigned_byte(seq_num: ContextSyncSeqNum) -> u8 {
-    seq_num.as_u8()
-}
diff --git a/nearby/presence/np_cpp_ffi/benchmarks/np_c_ffi_benches.cc b/nearby/presence/np_cpp_ffi/benchmarks/np_c_ffi_benches.cc
index 02bd4ca..1822741 100644
--- a/nearby/presence/np_cpp_ffi/benchmarks/np_c_ffi_benches.cc
+++ b/nearby/presence/np_cpp_ffi/benchmarks/np_c_ffi_benches.cc
@@ -24,13 +24,7 @@
 
 void V0PlaintextCApi(benchmark::State &state) {
   auto num_ciphers = state.range(0);
-  auto slab_result = np_ffi::internal::np_ffi_create_credential_slab();
-  assert(
-      np_ffi::internal::np_ffi_CreateCredentialSlabResult_kind(slab_result) ==
-      np_ffi::internal::CreateCredentialSlabResultKind::Success);
-  auto slab = np_ffi::internal::np_ffi_CreateCredentialSlabResult_into_SUCCESS(
-      slab_result);
-
+  auto slab = np_ffi::internal::np_ffi_create_credential_slab();
   auto book_result =
       np_ffi::internal::np_ffi_create_credential_book_from_slab(slab);
   assert(
diff --git a/nearby/presence/np_cpp_ffi/benchmarks/np_cpp_benches.cc b/nearby/presence/np_cpp_ffi/benchmarks/np_cpp_benches.cc
index 2a3b010..392d679 100644
--- a/nearby/presence/np_cpp_ffi/benchmarks/np_cpp_benches.cc
+++ b/nearby/presence/np_cpp_ffi/benchmarks/np_cpp_benches.cc
@@ -16,18 +16,14 @@
 #include "shared_test_util.h"
 
 // needed for running directly against the C API (ie: bypassing the C++ wrapper)
+#include "benchmark/benchmark.h"
 #include "np_cpp_ffi_functions.h"
 #include "np_cpp_ffi_types.h"
 
-#include "benchmark/benchmark.h"
-
 nearby_protocol::CredentialBook CreateEmptyCredBook() {
-  auto cred_slab = nearby_protocol::CredentialSlab::TryCreate();
-  assert(cred_slab.ok());
-  auto cred_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(cred_slab.value());
-  assert(cred_book.ok());
-  return std::move(*cred_book);
+  nearby_protocol::CredentialSlab cred_slab;
+  nearby_protocol::CredentialBook cred_book(cred_slab);
+  return std::move(cred_book);
 }
 
 void V0Plaintext(benchmark::State &state) {
@@ -65,19 +61,16 @@
     ->Unit(benchmark::kMicrosecond);
 
 class V0Encrypted : public benchmark::Fixture {
-public:
+ public:
   std::optional<nearby_protocol::CredentialBook> cred_book_;
   void SetUp(const ::benchmark::State &state) override {
     // populate credential book
     auto num_creds = state.range(0);
 
-    auto slab = nearby_protocol::CredentialSlab::TryCreate();
-    assert(slab.ok());
-
+    nearby_protocol::CredentialSlab slab;
     for (int i = 1; i < num_creds; i++) {
       auto credential = GenerateRandomCredentialV0();
-      auto result = slab->AddV0Credential(credential);
-      assert(result.ok());
+      slab.AddV0Credential(credential);
     }
 
     // now at the end of the list add the matching credential
@@ -86,13 +79,11 @@
     std::array<uint8_t, 32> key_seed = {};
     std::fill_n(key_seed.begin(), 32, 0x11);
     nearby_protocol::V0MatchableCredential v0_cred(
-        key_seed, V0AdvLegacyMetadataKeyHmac, match_data);
-    auto add_result = slab->AddV0Credential(v0_cred);
-    assert(add_result.ok());
+        key_seed, V0AdvLegacyIdentityTokenHmac, match_data);
+    slab.AddV0Credential(v0_cred);
 
-    auto cred_book = nearby_protocol::CredentialBook::TryCreateFromSlab(*slab);
-    assert(cred_book.ok());
-    cred_book_ = std::move(*cred_book);
+    nearby_protocol::CredentialBook cred_book(slab);
+    cred_book_ = std::move(cred_book);
   }
   void TearDown(const ::benchmark::State &state) override {}
 };
@@ -119,29 +110,27 @@
     ->Unit(benchmark::kMicrosecond);
 
 class V1SigEncryptedSingleSection : public benchmark::Fixture {
-public:
+ public:
   std::optional<nearby_protocol::CredentialBook> cred_book_;
   void SetUp(const ::benchmark::State &state) override {
     // populate credential book
     auto num_creds = state.range(0);
-    auto slab = nearby_protocol::CredentialSlab::TryCreate();
-    assert(slab.ok());
+    nearby_protocol::CredentialSlab slab;
     for (int i = 1; i < num_creds; i++) {
       auto credential = GenerateRandomCredentialV1();
-      auto result = slab->AddV1Credential(credential);
+      auto result = slab.AddV1Credential(credential);
       assert(result.ok());
     }
     // now at the end of the list add the matching credential
     nearby_protocol::MatchedCredentialData match_data(123,
                                                       V1AdvEncryptedMetadata);
     nearby_protocol::V1MatchableCredential v1_cred(
-        V1AdvKeySeed, V1AdvExpectedUnsignedMetadataKeyHmac,
-        V1AdvExpectedSignedMetadataKeyHmac, V1AdvPublicKey, match_data);
-    auto add_result = slab->AddV1Credential(v1_cred);
+        V1AdvKeySeed, V1AdvExpectedMicExtendedSaltIdentityTokenHmac,
+        V1AdvExpectedSignatureIdentityTokenHmac, V1AdvPublicKey, match_data);
+    auto add_result = slab.AddV1Credential(v1_cred);
     assert(add_result.ok());
-    auto cred_book = nearby_protocol::CredentialBook::TryCreateFromSlab(*slab);
-    assert(cred_book.ok());
-    cred_book_ = std::move(*cred_book);
+    nearby_protocol::CredentialBook cred_book(slab);
+    cred_book_ = std::move(cred_book);
   }
   void TearDown(const ::benchmark::State &state) override {}
 };
@@ -187,7 +176,7 @@
 }
 
 class LoadCredentialBook : public benchmark::Fixture {
-public:
+ public:
   std::vector<V1CredentialData> creds_;
   // generate all the data in setup so the time for generation is not included
   // in the measurement
@@ -204,19 +193,17 @@
 BENCHMARK_DEFINE_F(LoadCredentialBook, SingleMatchingCredential)
 (benchmark::State &state) {
   for ([[maybe_unused]] auto _ : state) {
-    auto slab = nearby_protocol::CredentialSlab::TryCreate();
-    assert(slab.ok());
+    nearby_protocol::CredentialSlab slab;
     for (auto cred : creds_) {
       nearby_protocol::MatchedCredentialData m(cred.cred_id,
                                                cred.encrypted_metadata_bytes);
       nearby_protocol::V1MatchableCredential v1_cred(
           cred.key_seed, cred.expected_unsigned_metadata_key_hmac,
           cred.expected_signed_metadata_key_hmac, cred.pub_key, m);
-      auto result = slab->AddV1Credential(v1_cred);
+      auto result = slab.AddV1Credential(v1_cred);
       assert(result.ok());
     }
-    auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(*slab);
-    assert(book.ok());
+    nearby_protocol::CredentialBook book(slab);
     benchmark::DoNotOptimize(book);
   }
 }
diff --git a/nearby/presence/np_cpp_ffi/fuzz/deserialization_fuzzer.cc b/nearby/presence/np_cpp_ffi/fuzz/deserialization_fuzzer.cc
index d18b83e..7108e0a 100644
--- a/nearby/presence/np_cpp_ffi/fuzz/deserialization_fuzzer.cc
+++ b/nearby/presence/np_cpp_ffi/fuzz/deserialization_fuzzer.cc
@@ -47,17 +47,15 @@
 void HandleAdvertisementResult(nearby_protocol::DeserializeAdvertisementResult);
 
 void PlaintextDeserializer(std::span<const uint8_t> adv_bytes) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate();
-  EXPECT_TRUE(slab.ok());
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(*slab);
-  EXPECT_TRUE(book.ok());
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto buffer = nearby_protocol::ByteBuffer<255>::TryFromSpan(adv_bytes);
   EXPECT_TRUE(buffer.ok());
 
   nearby_protocol::RawAdvertisementPayload payload(
       (nearby_protocol::ByteBuffer<255>(*buffer)));
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(payload, *book);
+      nearby_protocol::Deserializer::DeserializeAdvertisement(payload, book);
 
   // Since we are seeding with valid data, we can add extra calls into the
   // result processing APIs to ensure none of the internal asserts are
@@ -78,22 +76,24 @@
   uint32_t credential_id;
   std::array<uint8_t, 32> key_seed;
   std::array<uint8_t, 32> legacy_metadata_key_hmac;
-  std::array<uint8_t, 32> expected_unsigned_metadata_key_hmac;
-  std::array<uint8_t, 32> expected_signed_metadata_key_hmac;
+  std::array<uint8_t, 32> expected_unsigned_identity_token_hmac;
+  std::array<uint8_t, 32> expected_signed_identity_token_hmac;
   std::array<uint8_t, 32> pub_key;
   std::vector<uint8_t> encrypted_metadata_bytes;
 };
 
 static struct IdentityData V0TestCaseIdentityData {
   .credential_id = static_cast<uint32_t>(rand()), .key_seed = V0AdvKeySeed,
-  .legacy_metadata_key_hmac = V0AdvLegacyMetadataKeyHmac,
+  .legacy_metadata_key_hmac = V0AdvLegacyIdentityTokenHmac,
   .encrypted_metadata_bytes = V0AdvEncryptedMetadata
 };
 
 static struct IdentityData V1TestCaseIdentityData {
   .credential_id = static_cast<uint32_t>(rand()), .key_seed = V1AdvKeySeed,
-  .expected_unsigned_metadata_key_hmac = V1AdvExpectedUnsignedMetadataKeyHmac,
-  .expected_signed_metadata_key_hmac = V1AdvExpectedSignedMetadataKeyHmac,
+  .expected_unsigned_identity_token_hmac =
+      V1AdvExpectedMicExtendedSaltIdentityTokenHmac,
+  .expected_signed_identity_token_hmac =
+      V1AdvExpectedSignatureIdentityTokenHmac,
   .pub_key = V1AdvPublicKey, .encrypted_metadata_bytes = V1AdvEncryptedMetadata,
 };
 
@@ -105,9 +105,7 @@
 // and combinations of matching and undecryptable sections etc.
 void DeserializeWithCredentials(std::span<const IdentityData> identities,
                                 std::span<const uint8_t> adv_bytes) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate();
-  EXPECT_TRUE(slab.ok());
-
+  nearby_protocol::CredentialSlab slab;
   // populate book with fuzzer generated credential data
   for (auto data : identities) {
     nearby_protocol::MatchedCredentialData match_data(
@@ -115,23 +113,22 @@
     nearby_protocol::V0MatchableCredential v0_cred(
         data.key_seed, data.legacy_metadata_key_hmac, match_data);
     // adding v0 credentials is infallible
-    EXPECT_TRUE(slab->AddV0Credential(v0_cred).ok());
+    slab.AddV0Credential(v0_cred);
 
     nearby_protocol::V1MatchableCredential v1_cred(
-        data.key_seed, data.expected_unsigned_metadata_key_hmac,
-        data.expected_signed_metadata_key_hmac, data.pub_key, match_data);
-    [[maybe_unused]] auto result = slab->AddV1Credential(v1_cred);
+        data.key_seed, data.expected_unsigned_identity_token_hmac,
+        data.expected_signed_identity_token_hmac, data.pub_key, match_data);
+    [[maybe_unused]] auto result = slab.AddV1Credential(v1_cred);
   }
 
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(*slab);
-  EXPECT_TRUE(book.ok());
+  nearby_protocol::CredentialBook book(slab);
   auto buffer = nearby_protocol::ByteBuffer<255>::TryFromSpan(adv_bytes);
   EXPECT_TRUE(buffer.ok());
 
   nearby_protocol::RawAdvertisementPayload payload(
       (nearby_protocol::ByteBuffer<255>(*buffer)));
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(payload, *book);
+      nearby_protocol::Deserializer::DeserializeAdvertisement(payload, book);
 
   // Since we are seeding with valid data, we can add extra calls into the
   // result processing APIs to ensure none of the internal asserts are
diff --git a/nearby/presence/np_cpp_ffi/include/nearby_protocol.h b/nearby/presence/np_cpp_ffi/include/nearby_protocol.h
index 1cc73d7..5e064f9 100644
--- a/nearby/presence/np_cpp_ffi/include/nearby_protocol.h
+++ b/nearby/presence/np_cpp_ffi/include/nearby_protocol.h
@@ -61,22 +61,20 @@
 namespace nearby_protocol {
 
 // Re-exporting cbindgen generated types which are used in the public API
+using np_ffi::internal::ActionType;
 using np_ffi::internal::AddV0CredentialToSlabResult;
 using np_ffi::internal::AddV0DEResult;
 using np_ffi::internal::AddV1CredentialToSlabResult;
 using np_ffi::internal::AdvertisementBuilderKind;
-using np_ffi::internal::BooleanActionType;
 using np_ffi::internal::CreateCredentialBookResultKind;
-using np_ffi::internal::CreateCredentialSlabResultKind;
-using np_ffi::internal::CreateV0AdvertisementBuilderResultKind;
 using np_ffi::internal::CreateV1SectionBuilderResultKind;
+using np_ffi::internal::CurrentHandleAllocations;
 using np_ffi::internal::DeserializeAdvertisementResultKind;
 using np_ffi::internal::DeserializedV0AdvertisementKind;
 using np_ffi::internal::DeserializedV0IdentityDetails;
 using np_ffi::internal::DeserializedV0IdentityKind;
 using np_ffi::internal::DeserializedV1IdentityDetails;
 using np_ffi::internal::DeserializedV1IdentityKind;
-using np_ffi::internal::EncryptedIdentityType;
 using np_ffi::internal::GetV0DEResultKind;
 using np_ffi::internal::PanicReason;
 using np_ffi::internal::SerializeV0AdvertisementResultKind;
@@ -140,43 +138,17 @@
   // np_ffi_global_config_set_num_shards in np_cpp_ffi_functions.h for more info
   static void SetNumShards(uint8_t num_shards);
 
-  // Sets the maximum number of active handles to credential slabs which may be
-  // active at any one time. See
-  // np_ffi_global_config_set_max_num_credential_slabs in np_cpp_ffi_functions.h
-  // for more info
-  static void SetMaxNumCredentialSlabs(uint32_t max_num_credential_slabs);
-
-  // Sets the maximum number of active handles to credential books which may be
-  // active at any one time. See
-  // np_ffi_global_config_set_max_num_credential_books in np_cpp_ffi_functions.h
-  // for more info
-  static void SetMaxNumCredentialBooks(uint32_t max_num_credential_books);
-
-  // Sets the maximum number of active handles to deserialized v0 advertisements
-  // which may be active at any one time. See
-  // np_ffi_global_config_set_max_num_deserialized_v0_advertisements for more
-  // info.
-  static void SetMaxNumDeserializedV0Advertisements(
-      uint32_t max_num_deserialized_v0_advertisements);
-
-  // Sets the maximum number of active handles to deserialized v1 advertisements
-  // which may be active at any one time
-  static void SetMaxNumDeserializedV1Advertisements(
-      uint32_t max_num_deserialized_v1_advertisements);
-
-  // Sets the maximum number of active handles to v0 advertisement builders
-  // which may be active at any one time
-  static void SetMaxNumV0AdvertisementBuilders(
-      uint32_t max_num_v0_advertisement_builders);
+  // Checks the current count of all outstanding handle allocations, useful for
+  // debugging, logging, and testing
+  static CurrentHandleAllocations GetCurrentHandleAllocationCount();
 };
 
 // Holds the credentials used in the construction of a credential book
 // using CredentialBook::TryCreateFromSlab()
 class CredentialSlab {
  public:
-  // Don't allow copy constructor, copy-assignment or default constructor, since
+  // Don't allow copy constructor or copy-assignment since
   // this class wraps a handle to externally allocated resources.
-  CredentialSlab() = delete;
   CredentialSlab(const CredentialSlab &other) = delete;
   CredentialSlab &operator=(const CredentialSlab &other) = delete;
 
@@ -185,26 +157,23 @@
   CredentialSlab(CredentialSlab &&other) noexcept;
   CredentialSlab &operator=(CredentialSlab &&other) noexcept;
 
+  // Creates a new instance of a CredentialSlab, returns the CredentialSlab on
+  // success or a Status code on failure
+  CredentialSlab();
+
   // The destructor for a CredentialSlab, this will be called when a
   // CredentialSlab instance goes out of scope and will free the underlying
   // resources
   ~CredentialSlab();
 
-  // Creates a new instance of a CredentialSlab, returns the CredentialSlab on
-  // success or a Status code on failure
-  [[nodiscard]] static absl::StatusOr<CredentialSlab> TryCreate();
-
   // Adds a V0 credential to the slab
-  [[nodiscard]] absl::Status AddV0Credential(V0MatchableCredential v0_cred);
+  void AddV0Credential(V0MatchableCredential v0_cred);
 
   // Adds a V1 credential to the slab
   [[nodiscard]] absl::Status AddV1Credential(V1MatchableCredential v1_cred);
 
  private:
   friend class CredentialBook;
-  explicit CredentialSlab(np_ffi::internal::CredentialSlab credential_slab)
-      : credential_slab_(credential_slab), moved_(false) {}
-
   np_ffi::internal::CredentialSlab credential_slab_;
   bool moved_;
 };
@@ -234,14 +203,10 @@
   // returning the CredentialBook on success or a Status code on failure.
   // The passed credential-slab will be deallocated if this operation
   // is successful.
-  [[nodiscard]] static absl::StatusOr<CredentialBook> TryCreateFromSlab(
-      CredentialSlab &slab);
+  CredentialBook(CredentialSlab &slab);
 
  private:
   friend class Deserializer;
-  explicit CredentialBook(np_ffi::internal::CredentialBook credential_book)
-      : credential_book_(credential_book), moved_(false) {}
-
   np_ffi::internal::CredentialBook credential_book_;
   bool moved_;
 };
@@ -311,9 +276,9 @@
   V0BroadcastCredential() = delete;
 
   // Creates a new V0Broadcast credential with the given
-  // key seed and metadata key.
+  // key seed and identity token.
   [[nodiscard]] V0BroadcastCredential(std::array<uint8_t, 32> key_seed,
-                                      std::array<uint8_t, 14> metadata_key);
+                                      std::array<uint8_t, 14> identity_token);
 
  private:
   friend class V0AdvertisementBuilder;
@@ -578,12 +543,12 @@
   // Decrypts the metadata of the credential which matched with this
   // advertisement, or returns an error if the metadata key is invalid and
   // unable to successfully decrypt the metadata.
-  [[nodiscard]] absl::StatusOr<std::vector<uint8_t>> DecryptMetadata() const;
+  [[nodiscard]] absl::StatusOr<std::vector<uint8_t>> TryDecryptMetadata() const;
 
   // Gets the details of the identity data element of this payload or returns an
   // error if the payload does not have an identity (public advertisement)
   [[nodiscard]] absl::StatusOr<DeserializedV0IdentityDetails>
-  GetIdentityDetails() const;
+  TryGetIdentityDetails() const;
 
  private:
   friend class LegibleDeserializedV0Advertisement;
@@ -638,28 +603,6 @@
   np_ffi::internal::V0DataElement v0_data_element_;
 };
 
-// A Context Sync Sequence Number [0-15]
-class ContextSyncSeqNum {
- public:
-  ContextSyncSeqNum() = delete;
-
-  // Gets the value of this context-sync sequence number.
-  [[nodiscard]] uint8_t GetAsU8() const;
-
-  // Attempts to construct a context-sync sequence number with the given
-  // value contained in an unsigned byte. If the number may not be
-  // represented in a single nibble, this method will return an
-  // invalid argument error.
-  [[nodiscard]] static absl::StatusOr<ContextSyncSeqNum> TryBuildFromU8(
-      uint8_t value);
-
- private:
-  friend class V0Actions;
-  explicit ContextSyncSeqNum(np_ffi::internal::ContextSyncSeqNum seq_num)
-      : seq_num_(seq_num) {}
-  np_ffi::internal::ContextSyncSeqNum seq_num_;
-};
-
 // A V0 Actions Data Element
 class V0Actions {
  public:
@@ -670,20 +613,14 @@
   [[nodiscard]] uint32_t GetAsU32() const;
 
   /// Return whether a boolean action type is present in this data element
-  [[nodiscard]] bool HasAction(BooleanActionType action) const;
-
-  /// Gets the 4 bit context sync sequence number from this data element
-  [[nodiscard]] ContextSyncSeqNum GetContextSyncSequenceNumber() const;
+  [[nodiscard]] bool HasAction(ActionType action) const;
 
   /// Attempts to set the given action bit to the given boolean value.
   /// This operation may fail with an invalid argument error
   /// if the requested action bit may not be set given the encoding
   /// of the containing advertisement.
   /// In this case, the action bits will be unaltered by this call.
-  absl::Status TrySetAction(BooleanActionType action, bool value);
-
-  /// Sets the context sync sequence number.
-  void SetContextSyncSequenceNumber(ContextSyncSeqNum seq_num);
+  absl::Status TrySetAction(ActionType action, bool value);
 
   // Constructs an all-zeroed V0 actions DE for the given advertisement builder
   // kind.
@@ -746,7 +683,8 @@
       uint8_t index) const;
 
   // Decrypts the metadata of the credential which matched with this section
-  [[nodiscard]] absl::StatusOr<std::vector<uint8_t>> DecryptMetadata() const;
+  // or returns an error in the case that the metadata could not be decrypted
+  [[nodiscard]] absl::StatusOr<std::vector<uint8_t>> TryDecryptMetadata() const;
 
   // Gets the details of the identity data element of this section or returns an
   // error if the section does not contain an identity (public section)
@@ -829,20 +767,14 @@
 
   // Creates a new V0 advertisement builder for a public advertisement,
   // or returns a Status code on failure.
-  [[nodiscard]] static absl::StatusOr<V0AdvertisementBuilder> TryCreatePublic();
+  [[nodiscard]] static V0AdvertisementBuilder CreatePublic();
 
   // Creates a new V0 advertisement builder for an encrypted advertisement,
   // or returns a Status code on failure.
-  [[nodiscard]] static absl::StatusOr<V0AdvertisementBuilder>
-  TryCreateEncrypted(V0BroadcastCredential broadcast_cred,
-                     EncryptedIdentityType identity_type,
-                     std::array<uint8_t, 2> salt);
+  [[nodiscard]] static V0AdvertisementBuilder CreateEncrypted(
+      V0BroadcastCredential broadcast_cred, std::array<uint8_t, 2> salt);
 
  private:
-  [[nodiscard]] static absl::StatusOr<V0AdvertisementBuilder>
-  CreateV0AdvertisementBuilderResultToInternal(
-      np_ffi::internal::CreateV0AdvertisementBuilderResult result);
-
   explicit V0AdvertisementBuilder(
       np_ffi::internal::V0AdvertisementBuilder adv_builder)
       : adv_builder_(adv_builder), moved_(false) {}
diff --git a/nearby/presence/np_cpp_ffi/nearby_protocol.cc b/nearby/presence/np_cpp_ffi/nearby_protocol.cc
index 360f7ad..510844a 100644
--- a/nearby/presence/np_cpp_ffi/nearby_protocol.cc
+++ b/nearby/presence/np_cpp_ffi/nearby_protocol.cc
@@ -74,54 +74,13 @@
   np_ffi::internal::np_ffi_global_config_set_num_shards(num_shards);
 }
 
-void GlobalConfig::SetMaxNumCredentialSlabs(
-    const uint32_t max_num_credential_slabs) {
-  np_ffi::internal::np_ffi_global_config_set_max_num_credential_slabs(
-      max_num_credential_slabs);
+CurrentHandleAllocations GlobalConfig::GetCurrentHandleAllocationCount() {
+  return np_ffi::internal::np_ffi_global_config_get_current_allocation_count();
 }
 
-void GlobalConfig::SetMaxNumCredentialBooks(
-    const uint32_t max_num_credential_books) {
-  np_ffi::internal::np_ffi_global_config_set_max_num_credential_books(
-      max_num_credential_books);
-}
-
-void GlobalConfig::SetMaxNumDeserializedV0Advertisements(
-    const uint32_t max_num_deserialized_v0_advertisements) {
-  np_ffi::internal::
-      np_ffi_global_config_set_max_num_deserialized_v0_advertisements(
-          max_num_deserialized_v0_advertisements);
-}
-
-void GlobalConfig::SetMaxNumDeserializedV1Advertisements(
-    const uint32_t max_num_deserialized_v1_advertisements) {
-  np_ffi::internal::
-      np_ffi_global_config_set_max_num_deserialized_v1_advertisements(
-          max_num_deserialized_v1_advertisements);
-}
-
-void GlobalConfig::SetMaxNumV0AdvertisementBuilders(
-    uint32_t max_num_v0_advertisement_builders) {
-  np_ffi::internal::np_ffi_global_config_set_max_num_v0_advertisement_builders(
-      max_num_v0_advertisement_builders);
-}
-
-absl::StatusOr<CredentialSlab> CredentialSlab::TryCreate() {
-  auto result = np_ffi::internal::np_ffi_create_credential_slab();
-  auto kind = np_ffi::internal::np_ffi_CreateCredentialSlabResult_kind(result);
-
-  switch (kind) {
-    case CreateCredentialSlabResultKind::Success: {
-      auto slab = CredentialSlab(
-          np_ffi::internal::np_ffi_CreateCredentialSlabResult_into_SUCCESS(
-              result));
-      return slab;
-    }
-    case CreateCredentialSlabResultKind::NoSpaceLeft: {
-      return absl::ResourceExhaustedError(
-          "No space left to create credential slab");
-    }
-  }
+CredentialSlab::CredentialSlab() {
+  this->credential_slab_ = np_ffi::internal::np_ffi_create_credential_slab();
+  this->moved_ = false;
 }
 
 CredentialSlab::~CredentialSlab() {
@@ -155,20 +114,13 @@
   return *this;
 }
 
-absl::Status CredentialSlab::AddV0Credential(
-    const V0MatchableCredential v0_cred) {
+void CredentialSlab::AddV0Credential(const V0MatchableCredential v0_cred) {
   assert_panic(!this->moved_);
   auto result = np_ffi::internal::np_ffi_CredentialSlab_add_v0_credential(
       this->credential_slab_, v0_cred.internal_);
-  switch (result) {
-    case AddV0CredentialToSlabResult::Success: {
-      return absl::OkStatus();
-    }
-    case AddV0CredentialToSlabResult::InvalidHandle: {
-      return absl::InvalidArgumentError(
-          "invalid credential slab handle provided");
-    }
-  }
+  // Add V0 is infallible since the handle is guaranteed to be correct by the
+  // C++ wrapper
+  assert_panic(result == AddV0CredentialToSlabResult::Success);
 }
 
 absl::Status CredentialSlab::AddV1Credential(
@@ -191,29 +143,17 @@
   }
 }
 
-absl::StatusOr<CredentialBook> CredentialBook::TryCreateFromSlab(
-    CredentialSlab& slab) {
+CredentialBook::CredentialBook(CredentialSlab& slab) {
   assert_panic(!slab.moved_);
   auto result = np_ffi::internal::np_ffi_create_credential_book_from_slab(
       slab.credential_slab_);
   auto kind = np_ffi::internal::np_ffi_CreateCredentialBookResult_kind(result);
-  switch (kind) {
-    case CreateCredentialBookResultKind::Success: {
-      auto book =
-          np_ffi::internal::np_ffi_CreateCredentialBookResult_into_SUCCESS(
-              result);
-      slab.moved_ = true;
-      return CredentialBook(book);
-    }
-    case CreateCredentialBookResultKind::NoSpaceLeft: {
-      return absl::ResourceExhaustedError(
-          "No space left to create credential book");
-    }
-    case CreateCredentialBookResultKind::InvalidSlabHandle: {
-      return absl::NotFoundError(
-          "The slab referenced by the given handle was not found.");
-    }
-  }
+  // Handle is guaranteed to be valid by the C++ wrapper
+  assert_panic(kind == CreateCredentialBookResultKind::Success);
+  slab.moved_ = true;
+  this->credential_book_ =
+      np_ffi::internal::np_ffi_CreateCredentialBookResult_into_SUCCESS(result);
+  this->moved_ = false;
 }
 
 CredentialBook::~CredentialBook() {
@@ -500,12 +440,13 @@
       return result;
     }
     case np_ffi::internal::DecryptMetadataResultKind::Error: {
-      return absl::InvalidArgumentError("Invalid V0 payload handle");
+      return absl::InvalidArgumentError(
+          "Decrypt attempt failed, identity is missing or invalid");
     }
   }
 }
 
-absl::StatusOr<DeserializedV0IdentityDetails> V0Payload::GetIdentityDetails()
+absl::StatusOr<DeserializedV0IdentityDetails> V0Payload::TryGetIdentityDetails()
     const {
   assert_panic(!this->moved_);
   auto result = np_ffi::internal::np_ffi_V0Payload_get_identity_details(
@@ -513,7 +454,8 @@
   auto kind = np_ffi::internal::np_ffi_GetV0IdentityDetailsResult_kind(result);
   switch (kind) {
     case np_ffi::internal::GetV0IdentityDetailsResultKind::Error: {
-      return absl::InvalidArgumentError("Invalid handle");
+      return absl::InvalidArgumentError(
+          "Identity details not available for public advertisements");
     }
     case np_ffi::internal::GetV0IdentityDetailsResultKind::Success: {
       return np_ffi::internal::np_ffi_GetV0IdentityDetailsResult_into_SUCCESS(
@@ -522,7 +464,7 @@
   }
 }
 
-absl::StatusOr<std::vector<uint8_t>> V0Payload::DecryptMetadata() const {
+absl::StatusOr<std::vector<uint8_t>> V0Payload::TryDecryptMetadata() const {
   assert_panic(!this->moved_);
   auto decrypt_result =
       np_ffi::internal::np_ffi_V0Payload_decrypt_metadata(this->v0_payload_);
@@ -558,17 +500,11 @@
   return np_ffi::internal::np_ffi_V0Actions_as_u32(actions_);
 }
 
-bool V0Actions::HasAction(BooleanActionType action) const {
+bool V0Actions::HasAction(ActionType action) const {
   return np_ffi::internal::np_ffi_V0Actions_has_action(actions_, action);
 }
 
-ContextSyncSeqNum V0Actions::GetContextSyncSequenceNumber() const {
-  return ContextSyncSeqNum(
-      np_ffi::internal::np_ffi_V0Actions_get_context_sync_sequence_number(
-          actions_));
-}
-
-absl::Status V0Actions::TrySetAction(BooleanActionType action, bool value) {
+absl::Status V0Actions::TrySetAction(ActionType action, bool value) {
   auto result =
       np_ffi::internal::np_ffi_V0Actions_set_action(actions_, action, value);
   auto kind = np_ffi::internal::np_ffi_SetV0ActionResult_kind(result);
@@ -587,42 +523,11 @@
   }
 }
 
-void V0Actions::SetContextSyncSequenceNumber(ContextSyncSeqNum seq_num) {
-  actions_ =
-      np_ffi::internal::np_ffi_V0Actions_set_context_sync_sequence_number(
-          actions_, seq_num.seq_num_);
-}
-
 V0Actions V0Actions::BuildNewZeroed(AdvertisementBuilderKind kind) {
   auto actions = np_ffi::internal::np_ffi_build_new_zeroed_V0Actions(kind);
   return V0Actions(actions);
 }
 
-uint8_t ContextSyncSeqNum::GetAsU8() const {
-  return np_ffi::internal::np_ffi_ContextSyncSeqNum_as_unsigned_byte(seq_num_);
-}
-
-absl::StatusOr<ContextSyncSeqNum> ContextSyncSeqNum::TryBuildFromU8(
-    uint8_t value) {
-  auto result =
-      np_ffi::internal::np_ffi_ContextSyncSeqNum_build_from_unsigned_byte(
-          value);
-  auto kind =
-      np_ffi::internal::np_ffi_BuildContextSyncSeqNumResult_kind(result);
-  switch (kind) {
-    case np_ffi::internal::BuildContextSyncSeqNumResultKind::Success: {
-      return ContextSyncSeqNum(
-          np_ffi::internal::np_ffi_BuildContextSyncSeqNumResult_into_SUCCESS(
-              result));
-    }
-    case np_ffi::internal::BuildContextSyncSeqNumResultKind::OutOfRange: {
-      return absl::InvalidArgumentError(
-          "Attempted to build a context sync sequence number from a non-nibble "
-          "value.");
-    }
-  }
-}
-
 int8_t TxPower::GetAsI8() const {
   return np_ffi::internal::np_ffi_TxPower_as_signed_byte(tx_power_);
 }
@@ -733,7 +638,7 @@
   }
 }
 
-absl::StatusOr<std::vector<uint8_t>> DeserializedV1Section::DecryptMetadata()
+absl::StatusOr<std::vector<uint8_t>> DeserializedV1Section::TryDecryptMetadata()
     const {
   assert_panic(this->owning_v1_advertisement_ != nullptr);
   auto decrypt_result =
@@ -751,7 +656,8 @@
   auto kind = np_ffi::internal::np_ffi_GetV1IdentityDetailsResult_kind(result);
   switch (kind) {
     case np_ffi::internal::GetV1IdentityDetailsResultKind::Error: {
-      return absl::InvalidArgumentError("Invalid handle");
+      return absl::InvalidArgumentError(
+          "Identity details are not available for public advertisements");
     }
     case np_ffi::internal::GetV1IdentityDetailsResultKind::Success: {
       return np_ffi::internal::np_ffi_GetV1IdentityDetailsResult_into_SUCCESS(
@@ -812,31 +718,31 @@
     const MatchedCredentialData matched_credential_data) {
   np_ffi::internal::V0DiscoveryCredential discovery_cred{};
   CopyToRawArray(discovery_cred.key_seed, key_seed);
-  CopyToRawArray(discovery_cred.legacy_metadata_key_hmac,
-                 legacy_metadata_key_hmac);
+  CopyToRawArray(discovery_cred.identity_token_hmac, legacy_metadata_key_hmac);
   this->internal_ = {discovery_cred, matched_credential_data.data_};
 }
 
 V1MatchableCredential::V1MatchableCredential(
     const std::array<uint8_t, 32> key_seed,
-    const std::array<uint8_t, 32> expected_unsigned_metadata_key_hmac,
-    const std::array<uint8_t, 32> expected_signed_metadata_key_hmac,
+    const std::array<uint8_t, 32>
+        expected_mic_extended_salt_identity_token_hmac,
+    const std::array<uint8_t, 32> expected_signature_identity_token_hmac,
     const std::array<uint8_t, 32> pub_key,
     const MatchedCredentialData matched_credential_data) {
   np_ffi::internal::V1DiscoveryCredential discovery_cred{};
   CopyToRawArray(discovery_cred.key_seed, key_seed);
-  CopyToRawArray(discovery_cred.expected_unsigned_metadata_key_hmac,
-                 expected_unsigned_metadata_key_hmac);
-  CopyToRawArray(discovery_cred.expected_signed_metadata_key_hmac,
-                 expected_signed_metadata_key_hmac);
+  CopyToRawArray(discovery_cred.expected_mic_extended_salt_identity_token_hmac,
+                 expected_mic_extended_salt_identity_token_hmac);
+  CopyToRawArray(discovery_cred.expected_signature_identity_token_hmac,
+                 expected_signature_identity_token_hmac);
   CopyToRawArray(discovery_cred.pub_key, pub_key);
   this->internal_ = {discovery_cred, matched_credential_data.data_};
 }
 
 V0BroadcastCredential::V0BroadcastCredential(
-    std::array<uint8_t, 32> key_seed, std::array<uint8_t, 14> metadata_key) {
+    std::array<uint8_t, 32> key_seed, std::array<uint8_t, 14> identity_token) {
   CopyToRawArray(internal_.key_seed, key_seed);
-  CopyToRawArray(internal_.metadata_key, metadata_key);
+  CopyToRawArray(internal_.identity_token, identity_token);
 }
 
 V0AdvertisementBuilder::~V0AdvertisementBuilder() {
@@ -920,6 +826,10 @@
           "The advertisement contents were not of a proper size for LDT "
           "encryption");
     }
+    case SerializeV0AdvertisementResultKind::UnencryptedError: {
+      return absl::OutOfRangeError(
+          "The advertisement contents did not meet the length requirements");
+    }
     case SerializeV0AdvertisementResultKind::
         InvalidAdvertisementBuilderHandle: {
       return absl::InvalidArgumentError(
@@ -928,30 +838,10 @@
   }
 }
 
-[[nodiscard]] absl::StatusOr<V0AdvertisementBuilder>
-V0AdvertisementBuilder::CreateV0AdvertisementBuilderResultToInternal(
-    np_ffi::internal::CreateV0AdvertisementBuilderResult result) {
-  auto kind =
-      np_ffi::internal::np_ffi_CreateV0AdvertisementBuilderResult_kind(result);
-  switch (kind) {
-    case CreateV0AdvertisementBuilderResultKind::Success: {
-      auto adv_builder = np_ffi::internal::
-          np_ffi_CreateV0AdvertisementBuilderResult_into_SUCCESS(result);
-      return V0AdvertisementBuilder(adv_builder);
-    }
-    case CreateV0AdvertisementBuilderResultKind::NoSpaceLeft: {
-      return absl::ResourceExhaustedError(
-          "No space left to create v0 advertisement builder");
-    }
-  }
-}
-
-[[nodiscard]] absl::StatusOr<V0AdvertisementBuilder>
-V0AdvertisementBuilder::TryCreatePublic() {
+[[nodiscard]] V0AdvertisementBuilder V0AdvertisementBuilder::CreatePublic() {
   auto result =
       np_ffi::internal::np_ffi_create_v0_public_advertisement_builder();
-  return V0AdvertisementBuilder::CreateV0AdvertisementBuilderResultToInternal(
-      result);
+  return V0AdvertisementBuilder(result);
 }
 
 template <uintptr_t N>
@@ -962,15 +852,12 @@
   return result;
 }
 
-[[nodiscard]] absl::StatusOr<V0AdvertisementBuilder>
-V0AdvertisementBuilder::TryCreateEncrypted(V0BroadcastCredential broadcast_cred,
-                                           EncryptedIdentityType identity_type,
-                                           std::array<uint8_t, 2> salt) {
+[[nodiscard]] V0AdvertisementBuilder V0AdvertisementBuilder::CreateEncrypted(
+    V0BroadcastCredential broadcast_cred, std::array<uint8_t, 2> salt) {
   auto result =
       np_ffi::internal::np_ffi_create_v0_encrypted_advertisement_builder(
-          broadcast_cred.internal_, identity_type, ToFFIArray(salt));
-  return V0AdvertisementBuilder::CreateV0AdvertisementBuilderResultToInternal(
-      result);
+          broadcast_cred.internal_, ToFFIArray(salt));
+  return V0AdvertisementBuilder(result);
 }
 
-}  // namespace nearby_protocol
\ No newline at end of file
+}  // namespace nearby_protocol
diff --git a/nearby/presence/np_cpp_ffi/sample/main.cc b/nearby/presence/np_cpp_ffi/sample/main.cc
index eb0cae0..8a5c044 100644
--- a/nearby/presence/np_cpp_ffi/sample/main.cc
+++ b/nearby/presence/np_cpp_ffi/sample/main.cc
@@ -14,22 +14,28 @@
 
 #include <bitset>
 #include <iostream>
+#include <string>
+#include <utility>
 
 #include "absl/strings/escaping.h"
 #include "nearby_protocol.h"
+#include "np_cpp_ffi_types.h"
 
-static void SamplePanicHandler(nearby_protocol::PanicReason reason);
+void SamplePanicHandler(nearby_protocol::PanicReason reason);
 
-void HandleAdvertisementResult(nearby_protocol::DeserializeAdvertisementResult);
+void HandleAdvertisementResult(
+    nearby_protocol::DeserializeAdvertisementResult /*result*/);
 
-void HandleV0Adv(nearby_protocol::DeserializedV0Advertisement);
-void HandleLegibleV0Adv(nearby_protocol::LegibleDeserializedV0Advertisement);
-void HandleV0IdentityKind(nearby_protocol::DeserializedV0IdentityKind);
-void HandleDataElement(nearby_protocol::V0DataElement);
+void HandleV0Adv(nearby_protocol::DeserializedV0Advertisement /*result*/);
+void HandleLegibleV0Adv(
+    nearby_protocol::LegibleDeserializedV0Advertisement /*legible_adv*/);
+void HandleV0IdentityKind(
+    nearby_protocol::DeserializedV0IdentityKind /*identity*/);
+void HandleDataElement(nearby_protocol::V0DataElement /*de*/);
 
-void HandleV1Adv(nearby_protocol::DeserializedV1Advertisement);
-void HandleV1Section(nearby_protocol::DeserializedV1Section);
-void HandleV1DataElement(nearby_protocol::V1DataElement);
+void HandleV1Adv(nearby_protocol::DeserializedV1Advertisement /*adv*/);
+void HandleV1Section(const nearby_protocol::DeserializedV1Section& /*section*/);
+void HandleV1DataElement(nearby_protocol::V1DataElement /*de*/);
 
 int main() {
   auto result =
@@ -45,54 +51,48 @@
   std::cout << "\n========= Example V0 Adv ==========\n";
   std::cout << "Hex bytes: 00031503260046\n\n";
 
-  // Create an empty credential slab and verify that it is successful
-  auto cred_slab_result = nearby_protocol::CredentialSlab::TryCreate();
-  if (!cred_slab_result.ok()) {
-    std::cout << cred_slab_result.status().ToString();
-    return -1;
-  }
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
 
-  // Create an empty credential book from the empty slab, and verify success.
-  auto cred_book_result = nearby_protocol::CredentialBook::TryCreateFromSlab(
-      cred_slab_result.value());
-  if (!cred_book_result.ok()) {
-    std::cout << cred_book_result.status().ToString();
-    return -1;
-  }
-
-  auto v0_byte_string =
+  const auto* v0_byte_string =
       "00"       // Adv Header
       "03"       // Public DE header
       "1503"     // Length 1 Tx Power DE with value 3
       "260046";  // Length 2 Actions
 
-  auto v0_bytes = absl::HexStringToBytes(v0_byte_string);
+  std::string v0_bytes;
+  if (!absl::HexStringToBytes(v0_byte_string, &v0_bytes)) {
+    return -1;
+  }
   auto v0_buffer = nearby_protocol::ByteBuffer<255>::TryFromString(v0_bytes);
-  nearby_protocol::RawAdvertisementPayload v0_payload(v0_buffer.value());
+  nearby_protocol::RawAdvertisementPayload const v0_payload(v0_buffer.value());
 
   // Try to deserialize a V0 payload
   auto deserialize_v0_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          v0_payload, cred_book_result.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(v0_payload,
+                                                              credential_book);
   HandleAdvertisementResult(std::move(deserialize_v0_result));
 
   std::cout << "\n========= Example V1 Adv ==========\n";
   std::cout << "Hex bytes: 20040326004603031505\n\n";
 
-  auto v1_byte_string =
+  const auto* v1_byte_string =
       "20"       // V1 Advertisement header
       "04"       // Section Header
       "03"       // Public Identity DE header
       "260046";  // Length 2 Actions DE
 
-  auto v1_bytes = absl::HexStringToBytes(v1_byte_string);
+  std::string v1_bytes;
+  if (!absl::HexStringToBytes(v1_byte_string, &v1_bytes)) {
+    return -1;
+  }
   auto v1_buffer = nearby_protocol::ByteBuffer<255>::TryFromString(v1_bytes);
-  nearby_protocol::RawAdvertisementPayload v1_payload(v1_buffer.value());
+  nearby_protocol::RawAdvertisementPayload const v1_payload(v1_buffer.value());
 
   // Try to deserialize a V1 payload
   auto deserialize_v1_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          v1_payload, cred_book_result.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(v1_payload,
+                                                              credential_book);
   HandleAdvertisementResult(std::move(deserialize_v1_result));
 
   std::cout << "\n========= User input sample ==========\n\n";
@@ -101,19 +101,25 @@
     std::cout << "Enter the hex of the advertisement you would like to parse "
                  "(see above examples): ";
     std::cin >> user_input;
-    auto bytes = absl::HexStringToBytes(user_input);
+    std::string bytes;
+    auto hex_result = absl::HexStringToBytes(user_input, &bytes);
+    if (!hex_result) {
+      std::cout << "Provided string is not valid hex";
+      continue;
+    }
     auto buffer = nearby_protocol::ByteBuffer<255>::TryFromString(bytes);
     if (!buffer.ok()) {
       std::cout << "Too many bytes provided, must fit into a max length 255 "
                    "byte BLE advertisement\n";
       continue;
     }
-    nearby_protocol::RawAdvertisementPayload user_input_payload(buffer.value());
+    nearby_protocol::RawAdvertisementPayload const user_input_payload(
+        buffer.value());
 
     // Try to deserialize user input
     auto user_input_result =
         nearby_protocol::Deserializer::DeserializeAdvertisement(
-            user_input_payload, cred_book_result.value());
+            user_input_payload, credential_book);
     HandleAdvertisementResult(std::move(user_input_result));
 
     char choice;
@@ -128,7 +134,7 @@
   }
 }
 
-static void SamplePanicHandler(nearby_protocol::PanicReason reason) {
+void SamplePanicHandler(nearby_protocol::PanicReason reason) {
   std::cout << "Panicking! Reason: ";
   switch (reason) {
     case nearby_protocol::PanicReason::EnumCastFailed: {
@@ -143,6 +149,9 @@
       std::cout << "InvalidStackDataStructure \n";
       break;
     }
+    case np_ffi::internal::PanicReason::ExceededMaxHandleAllocations:
+      std::cout << "ExceededMaxHandleAllocations \n";
+      break;
   }
   std::abort();
 }
@@ -182,7 +191,8 @@
   HandleV0IdentityKind(legible_adv.GetIdentityKind());
 
   auto num_des = legible_adv.GetNumberOfDataElements();
-  std::cout << "\t\tAdv contains " << unsigned(num_des) << " data elements \n";
+  std::cout << "\t\tAdv contains " << static_cast<unsigned>(num_des)
+            << " data elements \n";
   auto payload = legible_adv.IntoPayload();
   for (int i = 0; i < num_des; i++) {
     auto de_result = payload.TryGetDataElement(i);
@@ -214,7 +224,8 @@
     case nearby_protocol::V0DataElementKind::TxPower: {
       std::cout << "\t\t\tDE Type is TxPower\n";
       auto tx_power = de.AsTxPower();
-      std::cout << "\t\t\tpower: " << int(tx_power.GetAsI8()) << "\n";
+      std::cout << "\t\t\tpower: " << static_cast<int>(tx_power.GetAsI8())
+                << "\n";
       return;
     }
     case nearby_protocol::V0DataElementKind::Actions: {
@@ -229,11 +240,11 @@
 
 void HandleV1Adv(nearby_protocol::DeserializedV1Advertisement adv) {
   auto legible_sections = adv.GetNumLegibleSections();
-  std::cout << "\tAdv has " << unsigned(legible_sections)
+  std::cout << "\tAdv has " << static_cast<unsigned>(legible_sections)
             << " legible sections \n";
 
   auto encrypted_sections = adv.GetNumUndecryptableSections();
-  std::cout << "\tAdv has " << unsigned(encrypted_sections)
+  std::cout << "\tAdv has " << static_cast<unsigned>(encrypted_sections)
             << " undecryptable sections\n";
 
   for (auto i = 0; i < legible_sections; i++) {
@@ -247,7 +258,7 @@
   }
 }
 
-void HandleV1Section(nearby_protocol::DeserializedV1Section section) {
+void HandleV1Section(const nearby_protocol::DeserializedV1Section& section) {
   switch (section.GetIdentityKind()) {
     case np_ffi::internal::DeserializedV1IdentityKind::Plaintext: {
       std::cout << "\t\tIdentity is Plaintext\n";
@@ -260,7 +271,8 @@
   }
 
   auto num_des = section.NumberOfDataElements();
-  std::cout << "\t\tSection has " << unsigned(num_des) << " data elements \n";
+  std::cout << "\t\tSection has " << static_cast<unsigned>(num_des)
+            << " data elements \n";
   for (auto i = 0; i < num_des; i++) {
     auto de_result = section.TryGetDataElement(i);
     if (!de_result.ok()) {
@@ -275,7 +287,7 @@
 
 void HandleV1DataElement(nearby_protocol::V1DataElement de) {
   std::cout << "\t\t\tData Element type code: "
-            << unsigned(de.GetDataElementTypeCode()) << "\n";
+            << static_cast<unsigned>(de.GetDataElementTypeCode()) << "\n";
   std::cout << "\t\t\tPayload bytes as hex: "
             << absl::BytesToHexString(de.GetPayload().ToString()) << "\n";
 }
diff --git a/nearby/presence/np_cpp_ffi/shared/shared_test_util.cc b/nearby/presence/np_cpp_ffi/shared/shared_test_util.cc
index 8f172c1..58e1d20 100644
--- a/nearby/presence/np_cpp_ffi/shared/shared_test_util.cc
+++ b/nearby/presence/np_cpp_ffi/shared/shared_test_util.cc
@@ -29,6 +29,8 @@
     case nearby_protocol::PanicReason::InvalidStackDataStructure: {
       return "InvalidStackDataStructure";
     }
+    case np_ffi::internal::PanicReason::ExceededMaxHandleAllocations:
+      return "ExceededMaxHandleAllocations";
   }
 }
 
diff --git a/nearby/presence/np_cpp_ffi/shared/shared_test_util.h b/nearby/presence/np_cpp_ffi/shared/shared_test_util.h
index da57b63..83e79a7 100644
--- a/nearby/presence/np_cpp_ffi/shared/shared_test_util.h
+++ b/nearby/presence/np_cpp_ffi/shared/shared_test_util.h
@@ -19,25 +19,27 @@
 
 // Plaintext advertisement bytes
 constexpr std::array<uint8_t, 2> V0AdvEmptyBytes{0x00, 0x03};
-constexpr std::array<uint8_t, 2> V1AdvEmptyBytes{0x00, 0x20};
-constexpr std::array<uint8_t, 4> V0AdvPlaintextBytes{0x00, 0x03, 0x15, 0x03};
-constexpr std::array<uint8_t, 7> V0AdvPlaintextMultiDeBytes{
-    0x00, 0x03, 0x15, 0x05, 0x26, 0x00, 0x46,
+constexpr std::array<uint8_t, 2> V1AdvEmptyBytes{0x00, 0x00};
+constexpr std::array<uint8_t, 3> V0AdvPlaintextBytes{0x00, 0x15, 0x03};
+constexpr std::array<uint8_t, 6> V0AdvPlaintextMultiDeBytes{
+    0x00, 0x15, 0x05, 0x26, 0x40, 0x40,
 };
-constexpr std::array<uint8_t, 5> V1AdvPlaintextBytes{0x20, 0x03, 0x03, 0x15,
+constexpr std::array<uint8_t, 5> V1AdvPlaintextBytes{0x20,  // Version header
+                                                     0x00,  // format
+                                                     0x02,  // section len
+                                                     0x15,  // Tx power value 3
                                                      0x03};
 
 // V0 encrypted advertisement data - ripped out of np_adv/tests/examples_v0.rs
-constexpr std::array<uint8_t, 20> V0AdvEncryptedBytes{
-    0x00, 0x21, 0x22, 0x22, 0x85, 0xBF, 0xA8, 0x83, 0x58, 0x7C,
-    0x50, 0xCF, 0x98, 0x38, 0xA7, 0x8A, 0xC0, 0x1C, 0x96, 0xF9,
-};
+constexpr std::array<uint8_t, 19> V0AdvEncryptedBytes{
+    0x04, 0x22, 0x22, 0xD8, 0x22, 0x12, 0xEF, 0x16, 0xDB, 0xF8,
+    0x72, 0xF2, 0xA3, 0xA7, 0xC0, 0xFA, 0x52, 0x48, 0xEC};
 inline std::vector<uint8_t> V0AdvEncryptedMetadata = {
-    0x26, 0xC5, 0xEA, 0xD4, 0xED, 0x58, 0xF8, 0xFC, 0xE8, 0xF4, 0xAB, 0x0C,
-    0x93, 0x2B, 0x75, 0xAA, 0x74, 0x39, 0x67, 0xDB, 0x1E, 0xF2, 0x33, 0xB5,
-    0x43, 0xCC, 0x94, 0xAA, 0xA3, 0xBB, 0xB9, 0x4C, 0xBF, 0x57, 0x77, 0xD0,
-    0x43, 0x0C, 0x7F, 0xF7, 0x36, 0x03, 0x29, 0xE0, 0x57, 0xBA, 0x97, 0x7F,
-    0xF2, 0xD1, 0x51, 0xDB, 0xC9, 0x01, 0x47, 0xE7, 0x48, 0x36,
+    0x8D, 0xD4, 0x7A, 0x58, 0x47, 0x30, 0x65, 0x89, 0x06, 0x16, 0x04, 0xCA,
+    0x68, 0xCE, 0xB4, 0x18, 0xA7, 0x40, 0xB8, 0x36, 0xB0, 0x94, 0xB4, 0xC0,
+    0x6C, 0xAF, 0x7C, 0x4A, 0x3B, 0x68, 0x28, 0x54, 0x8F, 0x36, 0xD5, 0x68,
+    0x12, 0x6C, 0xC2, 0x63, 0x31, 0x4A, 0x48, 0xE4, 0x3B, 0xB3, 0xCF, 0xD4,
+    0x10, 0x49, 0x59, 0xE7, 0x2B, 0xDE, 0xEB, 0x78, 0x44, 0x68,
 };
 inline std::string ExpectedV0DecryptedMetadata(
     R"({"name":"Alice","email":"alice@gmail.com"})");
@@ -46,52 +48,47 @@
     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
 };
-constexpr std::array<uint8_t, 32> V0AdvLegacyMetadataKeyHmac = {
-    0x88, 0x33, 0xDE, 0xD5, 0x4D, 0x00, 0x92, 0xE8, 0x80, 0x70, 0xD5,
-    0x1F, 0x18, 0xEC, 0x22, 0x45, 0x75, 0x7C, 0x24, 0xDF, 0xE3, 0x8C,
-    0xB2, 0xDE, 0x77, 0xB6, 0x78, 0x85, 0xFC, 0xA5, 0x67, 0x4D,
-};
+constexpr std::array<uint8_t, 32> V0AdvLegacyIdentityTokenHmac = {
+    0x09, 0xFE, 0x9E, 0x81, 0xB7, 0x3E, 0x5E, 0xCC, 0x76, 0x59, 0x57,
+    0x71, 0xE0, 0x1F, 0xFB, 0x34, 0x38, 0xE7, 0x5F, 0x24, 0xA7, 0x69,
+    0x56, 0xA0, 0xB8, 0xEA, 0x67, 0xD1, 0x1C, 0x3E, 0x36, 0xFD};
 
 // V1 encrypted advertisement data - ripped out of np_adv/tests/examples_v1.rs
-constexpr std::array<uint8_t, 105> V1AdvEncryptedBytes{
-    0x20, 0x67, 0x91, 0x10, 0x08, 0xAD, 0x69, 0x46, 0x04, 0x5D, 0xAE, 0x6D,
-    0xB7, 0xF7, 0x5C, 0xD3, 0xB8, 0xAC, 0xF0, 0xBF, 0x75, 0x90, 0x01, 0xBE,
-    0x73, 0x33, 0xA4, 0x76, 0x84, 0x4A, 0x09, 0x0F, 0x2B, 0x99, 0x47, 0xDF,
-    0x8B, 0x46, 0xCA, 0x16, 0xCE, 0x13, 0xB5, 0x6E, 0x53, 0xAE, 0x28, 0x56,
-    0x44, 0x0E, 0xA6, 0x8D, 0xEB, 0xA1, 0x11, 0xAF, 0x4E, 0x1B, 0xE0, 0x8E,
-    0xF5, 0xBA, 0x90, 0x4F, 0x2E, 0x94, 0xFC, 0xDE, 0xA6, 0x7F, 0x5D, 0xC8,
-    0x37, 0xB7, 0xEF, 0xCA, 0xAC, 0x8B, 0x9F, 0x1B, 0xD4, 0xC6, 0x11, 0x85,
-    0xD3, 0x67, 0x39, 0x32, 0xD1, 0x82, 0xCA, 0x4E, 0xB9, 0x46, 0x03, 0x83,
-    0x68, 0x56, 0x0B, 0xCC, 0xFD, 0x7A, 0x2A, 0xBE, 0x07,
-};
+constexpr std::array<uint8_t, 101> V1AdvEncryptedBytes{
+    0x20, 0x03, 0x9C, 0xED, 0x67, 0x93, 0x04, 0x6A, 0xBB, 0x1B, 0x1C, 0x40,
+    0x45, 0x1F, 0x34, 0x6C, 0x03, 0xF3, 0xAA, 0x4F, 0x01, 0x8B, 0x7A, 0x5F,
+    0x8A, 0x7B, 0x06, 0x29, 0xA4, 0x12, 0x27, 0x12, 0x63, 0xB4, 0x42, 0x76,
+    0xDE, 0xE9, 0x01, 0x6D, 0xF5, 0x70, 0x1C, 0x09, 0xDF, 0xBE, 0x2E, 0x32,
+    0xE5, 0x64, 0x8A, 0xDF, 0xB0, 0xB5, 0xE0, 0xC1, 0xD9, 0x76, 0x99, 0x4C,
+    0x71, 0xFB, 0xB2, 0xBF, 0xA2, 0xB9, 0xC2, 0xEA, 0x95, 0xE6, 0x6B, 0xFD,
+    0xD7, 0x93, 0x4D, 0xB5, 0x2D, 0xC2, 0x8D, 0x1E, 0x31, 0x32, 0x00, 0x88,
+    0x38, 0xBA, 0x73, 0x39, 0x5E, 0x23, 0xC3, 0x6C, 0x2D, 0x50, 0x22, 0x5B,
+    0xB5, 0xF4, 0x9C, 0x96, 0x37};
 constexpr std::array<uint8_t, 32> V1AdvKeySeed = {
-    0x31, 0x43, 0x63, 0x1E, 0xCA, 0xE8, 0x97, 0x4B, 0x96, 0x50, 0xCC,
-    0x1C, 0x48, 0x25, 0x0E, 0x81, 0x58, 0x06, 0x81, 0x51, 0xF9, 0xEB,
-    0x25, 0x23, 0x03, 0xD4, 0x97, 0x6D, 0x95, 0x19, 0x91, 0x39,
-};
-constexpr std::array<uint8_t, 32> V1AdvExpectedUnsignedMetadataKeyHmac = {0};
-constexpr std::array<uint8_t, 32> V1AdvExpectedSignedMetadataKeyHmac = {
-    0x1C, 0xBC, 0xEB, 0xDC, 0x17, 0xB5, 0x91, 0xE5, 0x07, 0x9D, 0x70,
-    0xC1, 0xE8, 0x4B, 0xCC, 0xDB, 0x4B, 0x0F, 0x76, 0x83, 0x59, 0x62,
-    0x0A, 0x2D, 0x55, 0x0B, 0x3B, 0x36, 0xA4, 0x92, 0x8B, 0x13,
-};
+    0xF1, 0xEF, 0x9E, 0x34, 0xDC, 0x28, 0xBC, 0x37, 0x5B, 0x6E, 0x4A,
+    0xC4, 0x52, 0xE6, 0x9C, 0xD3, 0x6D, 0xA9, 0xAB, 0x21, 0x5B, 0x02,
+    0xB9, 0x10, 0x27, 0xC3, 0xA9, 0x53, 0xB5, 0x29, 0x4C, 0x36};
+constexpr std::array<uint8_t, 32>
+    V1AdvExpectedMicExtendedSaltIdentityTokenHmac = {0};
+constexpr std::array<uint8_t, 32> V1AdvExpectedSignatureIdentityTokenHmac = {
+    0x10, 0x05, 0x45, 0x27, 0x8F, 0xE6, 0x86, 0x5B, 0x51, 0xB0, 0x39,
+    0xEA, 0xB8, 0xD7, 0x5F, 0x61, 0x41, 0xDD, 0x92, 0x62, 0xDD, 0x1A,
+    0x9D, 0xDA, 0x98, 0xDA, 0xF2, 0xDC, 0xA9, 0xD2, 0x45, 0x50};
 constexpr std::array<uint8_t, 32> V1AdvPublicKey = {
-    0x6D, 0x0D, 0xB6, 0x09, 0x10, 0xB1, 0x4E, 0xC4, 0x7E, 0x10, 0x16,
-    0x14, 0x9C, 0x9F, 0xF2, 0x14, 0x0F, 0xEC, 0x53, 0x76, 0xE3, 0x07,
-    0xD9, 0xD3, 0x9E, 0xAE, 0xE7, 0x45, 0x2C, 0x03, 0xEC, 0x6D,
-};
+    0x39, 0xA0, 0x74, 0x81, 0x70, 0x46, 0xFF, 0x72, 0x59, 0xC7, 0x78,
+    0x6C, 0x30, 0x7B, 0xC8, 0x71, 0x26, 0x34, 0xFF, 0x13, 0x61, 0x8C,
+    0xE6, 0x22, 0x46, 0x62, 0x56, 0xFD, 0xC1, 0x7A, 0x01, 0xAE};
 inline std::vector<uint8_t> V1AdvEncryptedMetadata = {
-    0x09, 0xB8, 0xC6, 0x6B, 0x71, 0x43, 0x55, 0x4C, 0xB9, 0x9D, 0xBF,
-    0xE4, 0xAF, 0x3E, 0xA2, 0x56, 0x0E, 0x6C, 0xBC, 0xDC, 0x3F, 0x3F,
-    0x0D, 0x28, 0xD4, 0x50, 0xA9, 0xEA, 0xC3, 0x60, 0xB0, 0x81, 0x31,
-    0xE2, 0x67, 0xB5, 0xC8, 0x15, 0x0C, 0xCA, 0x0B, 0x9B, 0x2C, 0x80,
-    0xC1, 0xB1, 0xF6, 0x5F, 0xE1, 0x51, 0xF9, 0xE2, 0x23, 0x56, 0xD4,
-    0x0B, 0x89, 0xA7, 0xF3, 0x4D, 0xE8, 0x79, 0x26, 0x44, 0x7E, 0x62,
-    0xDE, 0x53, 0x13, 0x15, 0x3D, 0xFC, 0x04, 0x2E, 0x2D, 0x08, 0x43,
-    0x2E, 0xE1, 0x96, 0xE8, 0x0F, 0xD0, 0xFC, 0xDE, 0x03, 0x86, 0x23,
-    0xB6, 0x98, 0x85, 0x27, 0x67, 0xD8, 0x1D, 0xC3, 0xE2, 0xE0, 0xA4,
-    0x32, 0x1A, 0x5F, 0x51, 0x0B, 0xA8, 0xD8, 0xA7, 0x23, 0xA4, 0x57,
-};
+    0x7B, 0xE9, 0x66, 0x00, 0x9E, 0x14, 0x50, 0xDE, 0x96, 0xCB, 0x79,
+    0x38, 0x93, 0xC5, 0x15, 0xE9, 0xC2, 0x6B, 0xE8, 0x03, 0x9F, 0x6C,
+    0xCA, 0x94, 0xAD, 0x24, 0x13, 0x27, 0xC1, 0xDE, 0xBC, 0xC2, 0x29,
+    0x88, 0xC0, 0xA5, 0x3B, 0xCA, 0x98, 0x4A, 0x42, 0xC3, 0xDE, 0xF5,
+    0x1C, 0xAE, 0xFF, 0xC0, 0x02, 0xD2, 0xD2, 0x97, 0x8B, 0x52, 0x93,
+    0x71, 0x07, 0x39, 0x87, 0x89, 0xCB, 0xCD, 0x79, 0x0A, 0x19, 0x0E,
+    0x73, 0xEB, 0x24, 0xFA, 0x8F, 0x4C, 0xA7, 0xF3, 0x95, 0x0B, 0xED,
+    0xEF, 0x27, 0x49, 0x8D, 0xE8, 0x7B, 0x88, 0x33, 0x16, 0x83, 0xF6,
+    0xF1, 0xE6, 0x43, 0x22, 0x70, 0x70, 0xFD, 0x9B, 0xA6, 0x52, 0x35,
+    0x0B, 0xBF, 0xBA, 0x0C, 0x20, 0xA3, 0x0C, 0xE7, 0xC7, 0xD0, 0x70};
 inline std::string ExpectedV1DecryptedMetadata(
     "{\"uuid\":\"378845e1-2616-420d-86f5-674177a7504d\","
     "\"display_name\":\"Alice\",\"location\":\"Wonderland\"}");
diff --git a/nearby/presence/np_cpp_ffi/tests/byte_buffer_tests.cc b/nearby/presence/np_cpp_ffi/tests/byte_buffer_tests.cc
index f76f396..e75d77d 100644
--- a/nearby/presence/np_cpp_ffi/tests/byte_buffer_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/byte_buffer_tests.cc
@@ -28,7 +28,9 @@
 TEST_F(NpCppTest, ByteBufferMaxLength) {
   // Each hex byte takes up 2 characters so length 510 string = 255 bytes of hex
   auto str_bytes = generate_hex_string(510);
-  auto bytes = absl::HexStringToBytes(str_bytes);
+
+  std::string bytes;
+  ASSERT_TRUE(absl::HexStringToBytes(str_bytes, &bytes));
   auto buffer = nearby_protocol::ByteBuffer<
       nearby_protocol::MAX_ADV_PAYLOAD_SIZE>::TryFromString(bytes);
   ASSERT_TRUE(buffer.ok());
@@ -81,14 +83,16 @@
 TEST_F(NpCppTest, ByteBufferInvalidLength) {
   // 256 bytes should fail
   auto str_bytes = generate_hex_string(512);
-  auto bytes = absl::HexStringToBytes(str_bytes);
+  std::string bytes;
+  ASSERT_TRUE(absl::HexStringToBytes(str_bytes, &bytes));
   auto buffer = nearby_protocol::ByteBuffer<
       nearby_protocol::MAX_ADV_PAYLOAD_SIZE>::TryFromString(bytes);
   ASSERT_FALSE(buffer.ok());
 }
 
 TEST_F(NpCppTest, ByteBufferRoundTrip) {
-  auto bytes = absl::HexStringToBytes("2003031503");
+  std::string bytes;
+  ASSERT_TRUE(absl::HexStringToBytes("2003031503", &bytes));
   auto buffer = nearby_protocol::ByteBuffer<
       nearby_protocol::MAX_ADV_PAYLOAD_SIZE>::TryFromString(bytes);
   auto string = buffer.value().ToString();
@@ -96,19 +100,22 @@
 }
 
 TEST_F(NpCppTest, ByteBufferPayloadWrongSize) {
-  auto bytes = absl::HexStringToBytes("1111111111111111111111");
+  std::string bytes;
+  ASSERT_TRUE(absl::HexStringToBytes("1111111111111111111111", &bytes));
   auto buffer = nearby_protocol::ByteBuffer<10>::TryFromString(bytes);
   ASSERT_FALSE(buffer.ok());
 }
 
 TEST_F(NpCppTest, ByteBufferEmptyString) {
-  auto bytes = absl::HexStringToBytes("");
+  std::string bytes;
+  ASSERT_TRUE(absl::HexStringToBytes("", &bytes));
   auto buffer = nearby_protocol::ByteBuffer<10>::TryFromString(bytes);
   ASSERT_TRUE(buffer.ok());
 }
 
 TEST_F(NpCppTest, ByteBufferToVector) {
-  auto bytes = absl::HexStringToBytes("1234567890");
+  std::string bytes;
+  ASSERT_TRUE(absl::HexStringToBytes("1234567890", &bytes));
   auto buffer = nearby_protocol::ByteBuffer<100>::TryFromString(bytes);
   auto vec = buffer.value().ToVector();
   const std::vector<uint8_t> expected{0x12, 0x34, 0x56, 0x78, 0x90};
@@ -116,17 +123,21 @@
 }
 
 TEST_F(NpCppTest, ByteBufferEndToEndPayloadAsString) {
-  const std::string bytes = absl::HexStringToBytes("2003031503");
+  std::string bytes;
+  ASSERT_TRUE(
+      absl::HexStringToBytes("20"     // NP Version Header V1
+                             "00"     // Format = unencrypted
+                             "02"     // section length = 2
+                             "1503",  // tx power value 3
+                             &bytes));
   auto buffer = nearby_protocol::ByteBuffer<
       nearby_protocol::MAX_ADV_PAYLOAD_SIZE>::TryFromString(bytes);
   ASSERT_TRUE(buffer.ok());
 
   const nearby_protocol::RawAdvertisementPayload adv(buffer.value());
 
-  auto credential_slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(credential_slab)
-          .value();
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto str = nearby_protocol::Deserializer::DeserializeAdvertisement(
                  adv, credential_book)
                  .IntoV1()
@@ -136,6 +147,9 @@
                  .value()
                  .GetPayload()
                  .ToString();
-  ASSERT_EQ(str, absl::HexStringToBytes("03"));
+
+  std::string expected;
+  ASSERT_TRUE(absl::HexStringToBytes("03", &expected));
+  ASSERT_EQ(str, expected);
 }
-// NOLINTEND(readability-magic-numbers)
\ No newline at end of file
+// NOLINTEND(readability-magic-numbers)
diff --git a/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc b/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc
index 32aa173..7ead975 100644
--- a/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc
@@ -21,31 +21,9 @@
 #include "np_cpp_test.h"
 #include "shared_test_util.h"
 
-TEST_F(NpCppTest, TestSetMaxCredBooks) {
-  auto slab1_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab1_result.ok());
-  auto book1_result =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(slab1_result.value());
-  ASSERT_TRUE(book1_result.ok());
-
-  auto slab2_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab2_result.ok());
-  auto book2_result =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(slab2_result.value());
-  ASSERT_TRUE(book2_result.ok());
-
-  auto slab3_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab3_result.ok());
-  auto book3_result =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(slab3_result.value());
-
-  ASSERT_FALSE(book3_result.ok());
-  ASSERT_TRUE(absl::IsResourceExhausted(book3_result.status()));
-}
-
-TEST_F(NpCppTest, TestBookMoveConstructor) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+TEST_F(NpCppTest, TestCredBookMoveConstructor) {
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto deserialize_result =
       nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvPlaintext,
                                                               book);
@@ -76,9 +54,35 @@
                "");
 }
 
-TEST_F(NpCppTest, TestBookMoveAssignment) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+TEST_F(NpCppTest, TestCredBookDestructor) {
+  nearby_protocol::CredentialSlab slab1;
+  auto current_allocations =
+      nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+  ASSERT_EQ(current_allocations.cred_slab, 1U);
+  nearby_protocol::CredentialBook book1(slab1);
+  current_allocations =
+      nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+  ASSERT_EQ(current_allocations.cred_book, 1U);
+  ASSERT_EQ(current_allocations.cred_slab, 0U);
+
+  {
+    nearby_protocol::CredentialSlab slab2;
+    nearby_protocol::CredentialBook book2(slab2);
+    current_allocations =
+        nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+    ASSERT_EQ(current_allocations.cred_book, 2U);
+  }
+
+  // After above RAII class goes out of scope, its de-allocation should be
+  // reflected in the handle allocation count
+  current_allocations =
+      nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+  ASSERT_EQ(current_allocations.cred_book, 1U);
+}
+
+TEST_F(NpCppTest, TestCredBookMoveAssignment) {
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto deserialize_result =
       nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvPlaintext,
                                                               book);
@@ -86,9 +90,8 @@
             nearby_protocol::DeserializeAdvertisementResultKind::V0);
 
   // create a second empty credential book
-  auto other_slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto other_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(other_slab).value();
+  nearby_protocol::CredentialSlab other_slab;
+  nearby_protocol::CredentialBook other_book(other_slab);
   other_book = std::move(book);
 
   // new credential book should still be successful
diff --git a/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc b/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc
index 719323f..ebaf874 100644
--- a/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc
@@ -23,101 +23,48 @@
 #include "np_cpp_test.h"
 
 // NOLINTBEGIN(readability-magic-numbers)
-TEST_F(NpCppTest, TestSetMaxCredSlabs) {
-  auto slab1_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab1_result.ok());
-
-  auto slab2_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab2_result.ok());
-
-  auto slab3_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab3_result.ok());
-
-  auto slab4_result = nearby_protocol::CredentialSlab::TryCreate();
-
-  ASSERT_FALSE(slab4_result.ok());
-  ASSERT_TRUE(absl::IsResourceExhausted(slab4_result.status()));
-}
-
 TEST_F(NpCppTest, TestSlabMoveConstructor) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
+  nearby_protocol::CredentialSlab slab;
   // It should be possible to move the slab into a new object
   // and use the moved version to successfully construct a
   // credential-book.
   nearby_protocol::CredentialSlab next_slab(std::move(slab));
+  nearby_protocol::CredentialBook book(next_slab);
 
-  auto maybe_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(next_slab);
-  ASSERT_TRUE(maybe_book.ok());
-
-  // Now, both slabs should be moved-out-of, since `TryCreateFromSlab` takes
+  // Now, both slabs should be moved-out-of, since `CreateFromSlab` takes
   // ownership. Verify that this is the case, and attempts to re-use the slabs
   // result in an assert failure.
-  ASSERT_DEATH([[maybe_unused]] auto failure =
-                   nearby_protocol::CredentialBook::TryCreateFromSlab(
-                       slab),  // NOLINT(bugprone-use-after-move)
+  ASSERT_DEATH([[maybe_unused]] nearby_protocol::CredentialBook failure(
+                   slab),  // NOLINT(bugprone-use-after-move)
                "");
-  ASSERT_DEATH(
-      [[maybe_unused]] auto failure =
-          nearby_protocol::CredentialBook::TryCreateFromSlab(next_slab),
-      "");
-}
-
-TEST_F(NpCppTest, TestSlabDestructor) {
-  {
-    auto slab1_result = nearby_protocol::CredentialSlab::TryCreate();
-    ASSERT_TRUE(slab1_result.ok());
-
-    auto slab2_result = nearby_protocol::CredentialSlab::TryCreate();
-    ASSERT_TRUE(slab2_result.ok());
-
-    auto slab3_result = nearby_protocol::CredentialSlab::TryCreate();
-    ASSERT_TRUE(slab3_result.ok());
-
-    auto slab4_result = nearby_protocol::CredentialSlab::TryCreate();
-
-    ASSERT_FALSE(slab4_result.ok());
-    ASSERT_TRUE(absl::IsResourceExhausted(slab4_result.status()));
-  }
-
-  // Now that the above variables have gone out of scope we should verify that
-  // the destructor succeeded in cleaning up those resources
-  auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab_result.ok());
+  ASSERT_DEATH([[maybe_unused]] nearby_protocol::CredentialBook failure(
+                   next_slab),  // NOLINT(bugprone-use-after-move)
+               "");
 }
 
 TEST_F(NpCppTest, TestSlabMoveAssignment) {
-  auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab_result.ok());
-
-  // create a second slab
-  auto other_slab_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(other_slab_result.ok());
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialSlab other_slab;
 
   // move assignment should override currently assigned slab with new one,
   // freeing the existing one.
-  auto other_slab = std::move(slab_result.value());
-  auto maybe_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(other_slab);
-  ASSERT_TRUE(maybe_book.ok());
+  other_slab = std::move(slab);
+  nearby_protocol::CredentialBook book(other_slab);
 
   // The old object should now lead to use after moved assert failure
-  ASSERT_DEATH([[maybe_unused]] auto failure =
-                   nearby_protocol::CredentialBook::TryCreateFromSlab(
-                       slab_result.value()),  // NOLINT(bugprone-use-after-move)
+  ASSERT_DEATH([[maybe_unused]] nearby_protocol::CredentialBook failure(
+                   slab),  // NOLINT(bugprone-use-after-move)
                "");
 
   // moving again should still lead to a use after moved assert failure
-  auto another_moved_book = std::move(slab_result.value());
-  ASSERT_DEATH([[maybe_unused]] auto failure =
-                   nearby_protocol::CredentialBook::TryCreateFromSlab(
-                       another_moved_book),  // NOLINT(bugprone-use-after-move)
+  auto another_moved_book = std::move(slab);
+  ASSERT_DEATH([[maybe_unused]] nearby_protocol::CredentialBook failure(
+                   another_moved_book),  // NOLINT(bugprone-use-after-move)
                "");
 }
 
 TEST_F(NpCppTest, TestAddV0Credential) {
-  auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab_result.ok());
+  nearby_protocol::CredentialSlab slab;
 
   uint8_t metadata[] = {1, 2, 3};
   const std::span<uint8_t> metadata_span(metadata);
@@ -128,18 +75,13 @@
 
   const nearby_protocol::V0MatchableCredential v0_cred(
       key_seed, legacy_metadata_key_hmac, match_data);
-  auto add_result = slab_result.value().AddV0Credential(v0_cred);
-  ASSERT_EQ(add_result, absl::OkStatus());
+  slab.AddV0Credential(v0_cred);
 }
 
 TEST_F(NpCppTest, TestAddV0CredentialAfterMoved) {
-  auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab_result.ok());
-
+  nearby_protocol::CredentialSlab slab;
   // creating a book will move the slab
-  auto maybe_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(slab_result.value());
-  ASSERT_TRUE(maybe_book.ok());
+  nearby_protocol::CredentialBook book(slab);
 
   uint8_t metadata[] = {1, 2, 3};
   const std::span<uint8_t> metadata_span(metadata);
@@ -148,15 +90,11 @@
   const std::array<uint8_t, 32> legacy_metadata_key_hmac{1, 2, 3};
   const nearby_protocol::V0MatchableCredential v0_cred(
       key_seed, legacy_metadata_key_hmac, match_data);
-
-  ASSERT_DEATH([[maybe_unused]] auto add_result =
-                   slab_result.value().AddV0Credential(v0_cred);
-               , "");
+  ASSERT_DEATH(slab.AddV0Credential(v0_cred);, "");
 }
 
 TEST_F(NpCppTest, TestAddV1Credential) {
-  auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab_result.ok());
+  nearby_protocol::CredentialSlab slab;
 
   uint8_t metadata[] = {1, 2, 3};
   const std::span<uint8_t> metadata_span(metadata);
@@ -169,19 +107,14 @@
       key_seed, expected_unsigned_metadata_key_hmac,
       expected_signed_metadata_key_hmac, pub_key, match_data);
 
-  auto add_result = slab_result.value().AddV1Credential(v1_cred);
+  auto add_result = slab.AddV1Credential(v1_cred);
   ASSERT_EQ(add_result, absl::OkStatus());
 }
 
 TEST_F(NpCppTest, TestAddV1CredentialAfterMoved) {
-  auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab_result.ok());
-
+  nearby_protocol::CredentialSlab slab;
   // creating a book will move the slab
-  auto maybe_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(slab_result.value());
-  ASSERT_TRUE(maybe_book.ok());
-
+  nearby_protocol::CredentialBook book(slab);
   uint8_t metadata[] = {1, 2, 3};
   const std::span<uint8_t> metadata_span(metadata);
   const nearby_protocol::MatchedCredentialData match_data(111, metadata_span);
@@ -193,16 +126,13 @@
       key_seed, expected_unsigned_metadata_key_hmac,
       expected_signed_metadata_key_hmac, pub_key, match_data);
 
-  ASSERT_DEATH([[maybe_unused]] auto add_result =
-                   slab_result.value().AddV1Credential(v1_cred);
+  ASSERT_DEATH([[maybe_unused]] auto add_result = slab.AddV1Credential(v1_cred);
                , "");
 }
 
 // make sure the book can be populated with many credentials
 TEST_F(NpCppTest, TestAddManyCredentials) {
-  auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab_result.ok());
-
+  nearby_protocol::CredentialSlab slab;
   // Should be able to load the slab up with many credentials
   for (int i = 0; i < 500; i++) {
     uint8_t metadata[] = {1, 2, 3};
@@ -212,8 +142,7 @@
     const std::array<uint8_t, 32> legacy_metadata_key_hmac{1, 2, 3};
     const nearby_protocol::V0MatchableCredential v0_cred(
         key_seed, legacy_metadata_key_hmac, match_data);
-    auto add_result = slab_result->AddV0Credential(v0_cred);
-    ASSERT_EQ(add_result, absl::OkStatus());
+    slab.AddV0Credential(v0_cred);
 
     const std::array<uint8_t, 32> v1_key_seed{1, 2, 3};
     const std::array<uint8_t, 32> v1_expected_unsigned_metadata_key_hmac{1, 2,
@@ -224,10 +153,23 @@
         v1_key_seed, v1_expected_unsigned_metadata_key_hmac,
         v1_expected_signed_metadata_key_hmac, v1_pub_key, match_data);
 
-    auto add_v1_result = slab_result->AddV1Credential(v1_cred);
+    auto add_v1_result = slab.AddV1Credential(v1_cred);
     ASSERT_EQ(add_v1_result, absl::OkStatus());
   }
-  ASSERT_TRUE(
-      nearby_protocol::CredentialBook::TryCreateFromSlab(*slab_result).ok());
+  nearby_protocol::CredentialBook book(slab);
+}
+
+TEST_F(NpCppTest, TestSlabDestructor) {
+  {
+    nearby_protocol::CredentialSlab slab;
+    nearby_protocol::CredentialSlab slab2;
+    nearby_protocol::CredentialSlab slab3;
+    auto alloc_count =
+        nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+    ASSERT_EQ(alloc_count.cred_slab, 3U);
+  }
+  auto alloc_count =
+      nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+  ASSERT_EQ(alloc_count.cred_slab, 0U);
 }
 // NOLINTEND(readability-magic-numbers)
\ No newline at end of file
diff --git a/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc b/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc
index 37ef27a..af83461 100644
--- a/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc
@@ -24,8 +24,8 @@
 #include "shared_test_util.h"
 
 TEST_F(NpCppTest, TestResultMoveConstructor) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
       V0AdvPlaintext, book);
   ASSERT_EQ(result.GetKind(),
@@ -56,25 +56,18 @@
 }
 
 TEST_F(NpCppTest, DeserializeFromStringView) {
-  auto bytes = absl::HexStringToBytes("00031503");
+  std::string bytes;
+  ASSERT_TRUE(absl::HexStringToBytes("001503", &bytes));
   auto buffer = nearby_protocol::ByteBuffer<
       nearby_protocol::MAX_ADV_PAYLOAD_SIZE>::TryFromString(bytes);
   ASSERT_TRUE(buffer.ok());
 
   const nearby_protocol::RawAdvertisementPayload adv(buffer.value());
-
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
-
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          adv, maybe_credential_book.value());
-
+      nearby_protocol::Deserializer::DeserializeAdvertisement(adv,
+                                                              credential_book);
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V0);
   auto v0_adv = deserialize_result.IntoV0();
@@ -99,8 +92,8 @@
 }
 
 TEST_F(NpCppTest, TestResultMoveAssignment) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
       V0AdvPlaintext, book);
   ASSERT_EQ(result.GetKind(),
@@ -137,79 +130,54 @@
   const std::array<uint8_t, 1> InvalidHeaderPayloadBytes{0xFF};
   const nearby_protocol::RawAdvertisementPayload InvalidHeaderPayload(
       (nearby_protocol::ByteBuffer<255>(InvalidHeaderPayloadBytes)));
-
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
-
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto deserialize_result =
       nearby_protocol::Deserializer::DeserializeAdvertisement(
-          InvalidHeaderPayload, maybe_credential_book.value());
+          InvalidHeaderPayload, credential_book);
 
   // Errors cannot be casted into further types
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::Error);
-  ASSERT_DEATH(
-      { [[maybe_unused]] auto failure = deserialize_result.IntoV0(); }, "");
-  ASSERT_DEATH(
-      { [[maybe_unused]] auto failure = deserialize_result.IntoV1(); }, "");
+  ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV0(); },
+               "");
+  ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV1(); },
+               "");
 }
 
 TEST_F(NpCppTest, TestInvalidV0Cast) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
-
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V1AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V1AdvPlaintext,
+                                                              credential_book);
 
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V1);
-  ASSERT_DEATH(
-      { [[maybe_unused]] auto failure = deserialize_result.IntoV0(); }, "");
+  ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV0(); },
+               "");
 }
 
 TEST_F(NpCppTest, TestInvalidV1Cast) {
   // Create an empty credential book and verify that is is successful
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
-
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvPlaintext,
+                                                              credential_book);
 
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V0);
-  ASSERT_DEATH(
-      { [[maybe_unused]] auto failure = deserialize_result.IntoV1(); }, "");
+  ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV1(); },
+               "");
 }
 
 TEST_F(NpCppTest, V0UseResultTwice) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
-
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvPlaintext,
+                                                              credential_book);
   ASSERT_EQ(deserialize_result.GetKind(),
             np_ffi::internal::DeserializeAdvertisementResultKind::V0);
 
@@ -217,22 +185,16 @@
   auto v0_adv = deserialize_result.IntoV0();
   // Calling intoV0 for a second time is a programmer error and will result
   // in a crash.
-  ASSERT_DEATH(
-      { [[maybe_unused]] auto failure = deserialize_result.IntoV0(); }, "");
+  ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV0(); },
+               "");
 }
 
 TEST_F(NpCppTest, V1UseResultTwice) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
-
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V1AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V1AdvPlaintext,
+                                                              credential_book);
   ASSERT_EQ(deserialize_result.GetKind(),
             np_ffi::internal::DeserializeAdvertisementResultKind::V1);
 
@@ -240,22 +202,16 @@
   auto v1_adv = deserialize_result.IntoV1();
   // Calling intoV0 for a second time is a programmer error and will result
   // in a crash.
-  ASSERT_DEATH(
-      { [[maybe_unused]] auto failure = deserialize_result.IntoV1(); }, "");
+  ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV1(); },
+               "");
 }
 
 TEST_F(NpCppTest, IntoV0AfterOutOfScope) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
-
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvPlaintext,
+                                                              credential_book);
   ASSERT_EQ(deserialize_result.GetKind(),
             np_ffi::internal::DeserializeAdvertisementResultKind::V0);
 
@@ -264,22 +220,16 @@
 
   // Calling intoV0 for a second time is a programmer error and will result
   // in a crash.
-  ASSERT_DEATH(
-      { [[maybe_unused]] auto failure = deserialize_result.IntoV0(); }, "");
+  ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV0(); },
+               "");
 }
 
 TEST_F(NpCppTest, IntoV1AfterOutOfScope) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
-
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V1AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V1AdvPlaintext,
+                                                              credential_book);
   ASSERT_EQ(deserialize_result.GetKind(),
             np_ffi::internal::DeserializeAdvertisementResultKind::V1);
 
@@ -288,22 +238,16 @@
 
   // Calling intoV0 for a second time is a programmer error and will result
   // in a crash.
-  ASSERT_DEATH(
-      { [[maybe_unused]] auto failure = deserialize_result.IntoV1(); }, "");
+  ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV1(); },
+               "");
 }
 
 TEST_F(NpCppTest, V0ResultKindAfterOutOfScope) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
-
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvPlaintext,
+                                                              credential_book);
   ASSERT_EQ(deserialize_result.GetKind(),
             np_ffi::internal::DeserializeAdvertisementResultKind::V0);
 
@@ -317,17 +261,11 @@
 }
 
 TEST_F(NpCppTest, V1ResultKindAfterOutOfScope) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
-
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V1AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V1AdvPlaintext,
+                                                              credential_book);
   ASSERT_EQ(deserialize_result.GetKind(),
             np_ffi::internal::DeserializeAdvertisementResultKind::V1);
 
diff --git a/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h b/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h
index 6cbf8c9..c8337a5 100644
--- a/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h
+++ b/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h
@@ -26,11 +26,6 @@
       ASSERT_TRUE(
           nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
       panic_handler_set = true;
-      nearby_protocol::GlobalConfig::SetMaxNumDeserializedV0Advertisements(2);
-      nearby_protocol::GlobalConfig::SetMaxNumDeserializedV1Advertisements(2);
-      nearby_protocol::GlobalConfig::SetMaxNumCredentialSlabs(3);
-      nearby_protocol::GlobalConfig::SetMaxNumCredentialBooks(2);
-      nearby_protocol::GlobalConfig::SetMaxNumV0AdvertisementBuilders(2);
     } else {
       ASSERT_FALSE(
           nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
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 3d74295..bd1f771 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
@@ -27,28 +27,19 @@
 
 // NOLINTBEGIN(readability-magic-numbers)
 TEST_F(NpCppTest, V0PrivateIdentityDeserializationSimpleCase) {
-  auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab_result.ok());
-
+  nearby_protocol::CredentialSlab slab;
   const std::span<uint8_t> metadata_span(V0AdvEncryptedMetadata);
   const nearby_protocol::MatchedCredentialData match_data(123, metadata_span);
-
   std::array<uint8_t, 32> key_seed = {};
   std::fill_n(key_seed.begin(), 32, 0x11);
-
   const nearby_protocol::V0MatchableCredential v0_cred(
-      key_seed, V0AdvLegacyMetadataKeyHmac, match_data);
-
-  auto add_result = slab_result->AddV0Credential(v0_cred);
-  ASSERT_EQ(add_result, absl::OkStatus());
-
-  auto book_result =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(*slab_result);
-  ASSERT_TRUE(book_result.ok());
+      key_seed, V0AdvLegacyIdentityTokenHmac, match_data);
+  slab.AddV0Credential(v0_cred);
+  nearby_protocol::CredentialBook book(slab);
 
   auto deserialize_result =
       nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvEncryptedPayload, *book_result);
+          V0AdvEncryptedPayload, book);
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V0);
 
@@ -66,16 +57,14 @@
   auto de = payload.TryGetDataElement(0);
   ASSERT_TRUE(de.ok());
 
-  auto metadata = payload.DecryptMetadata();
+  auto metadata = payload.TryDecryptMetadata();
   ASSERT_TRUE(metadata.ok());
   ASSERT_EQ(ExpectedV0DecryptedMetadata,
             std::string(metadata->begin(), metadata->end()));
 
-  auto identity_details = payload.GetIdentityDetails();
+  auto identity_details = payload.TryGetIdentityDetails();
   ASSERT_TRUE(identity_details.ok());
-  ASSERT_EQ(identity_details->cred_id, 123);
-  ASSERT_EQ(identity_details->identity_type,
-            nearby_protocol::EncryptedIdentityType::Private);
+  ASSERT_EQ(identity_details->cred_id, 123u);
 
   auto de_type = de->GetKind();
   ASSERT_EQ(de_type, nearby_protocol::V0DataElementKind::TxPower);
@@ -85,8 +74,8 @@
 }
 
 nearby_protocol::CredentialBook CreateEmptyCredBook() {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   return book;
 }
 
@@ -108,30 +97,22 @@
 }
 
 TEST_F(NpCppTest, V0PrivateIdentityNoMatchingCreds) {
-  auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab_result.ok());
-
+  nearby_protocol::CredentialSlab slab;
   uint8_t metadata[] = {0};
   const std::span<uint8_t> metadata_span(metadata);
   const nearby_protocol::MatchedCredentialData match_data(123, metadata_span);
-
   // A randomly picked key seed, does NOT match what was used for the canned adv
   std::array<uint8_t, 32> key_seed = {};
   std::fill_n(key_seed.begin(), 31, 0x11);
-
   const nearby_protocol::V0MatchableCredential v0_cred(
-      key_seed, V0AdvLegacyMetadataKeyHmac, match_data);
+      key_seed, V0AdvLegacyIdentityTokenHmac, match_data);
+  slab.AddV0Credential(v0_cred);
 
-  auto add_result = slab_result->AddV0Credential(v0_cred);
-  ASSERT_EQ(add_result, absl::OkStatus());
-
-  auto book_result =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(*slab_result);
-  ASSERT_TRUE(book_result.ok());
+  nearby_protocol::CredentialBook book(slab);
 
   auto deserialize_result =
       nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvEncryptedPayload, *book_result);
+          V0AdvEncryptedPayload, book);
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V0);
 
@@ -146,31 +127,31 @@
 
 // Make sure the correct credential is matched out of multiple provided
 TEST_F(NpCppTest, V0PrivateIdentityMultipleCredentials) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
+  nearby_protocol::CredentialSlab slab;
   const std::span<uint8_t> metadata_span(V0AdvEncryptedMetadata);
   std::array<uint8_t, 32> key_seed = {};
   // Non matching credential
   const nearby_protocol::MatchedCredentialData match_data(123, metadata_span);
   std::fill_n(key_seed.begin(), 32, 0x12);
   const nearby_protocol::V0MatchableCredential v0_cred(
-      key_seed, V0AdvLegacyMetadataKeyHmac, match_data);
-  ASSERT_TRUE(slab.AddV0Credential(v0_cred).ok());
+      key_seed, V0AdvLegacyIdentityTokenHmac, match_data);
+  slab.AddV0Credential(v0_cred);
 
   // Matching credential
   const nearby_protocol::MatchedCredentialData match_data2(456, metadata_span);
   std::fill_n(key_seed.begin(), 32, 0x11);
   const nearby_protocol::V0MatchableCredential v0_cred2(
-      key_seed, V0AdvLegacyMetadataKeyHmac, match_data2);
-  ASSERT_TRUE(slab.AddV0Credential(v0_cred2).ok());
+      key_seed, V0AdvLegacyIdentityTokenHmac, match_data2);
+  slab.AddV0Credential(v0_cred2);
 
   // Non matching credential
   const nearby_protocol::MatchedCredentialData match_data3(789, metadata_span);
   std::fill_n(key_seed.begin(), 32, 0x13);
   const nearby_protocol::V0MatchableCredential v0_cred3(
-      key_seed, V0AdvLegacyMetadataKeyHmac, match_data3);
-  ASSERT_TRUE(slab.AddV0Credential(v0_cred3).ok());
+      key_seed, V0AdvLegacyIdentityTokenHmac, match_data3);
+  slab.AddV0Credential(v0_cred3);
 
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialBook book(slab);
   auto legible_adv = nearby_protocol::Deserializer::DeserializeAdvertisement(
                          V0AdvEncryptedPayload, book)
                          .IntoV0()
@@ -183,10 +164,8 @@
   ASSERT_TRUE(payload.TryGetDataElement(0).ok());
 
   // Make sure the correct credential matches
-  auto identity_details = payload.GetIdentityDetails();
+  auto identity_details = payload.TryGetIdentityDetails();
   ASSERT_TRUE(identity_details.ok());
-  ASSERT_EQ(identity_details->cred_id, 456);
-  ASSERT_EQ(identity_details->identity_type,
-            nearby_protocol::EncryptedIdentityType::Private);
+  ASSERT_EQ(identity_details->cred_id, 456u);
 }
 // NOLINTEND(readability-magic-numbers)
diff --git a/nearby/presence/np_cpp_ffi/tests/v0_encrypted_serialization_tests.cc b/nearby/presence/np_cpp_ffi/tests/v0_encrypted_serialization_tests.cc
index ebc82a4..def4fb6 100644
--- a/nearby/presence/np_cpp_ffi/tests/v0_encrypted_serialization_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/v0_encrypted_serialization_tests.cc
@@ -39,12 +39,8 @@
   std::array<uint8_t, 2> salt = {};
   std::fill_n(salt.begin(), 2, 0x22);
 
-  auto identity_type = nearby_protocol::EncryptedIdentityType::Private;
-
-  auto adv_builder =
-      nearby_protocol::V0AdvertisementBuilder::TryCreateEncrypted(
-          broadcast_cred, identity_type, salt)
-          .value();
+  auto adv_builder = nearby_protocol::V0AdvertisementBuilder::CreateEncrypted(
+      broadcast_cred, salt);
 
   auto tx_power = nearby_protocol::TxPower::TryBuildFromI8(3).value();
   auto de = nearby_protocol::V0DataElement(tx_power);
@@ -59,4 +55,4 @@
   ASSERT_EQ(actual, expected);
 }
 
-// NOLINTEND(readability-magic-numbers)
\ No newline at end of file
+// NOLINTEND(readability-magic-numbers)
diff --git a/nearby/presence/np_cpp_ffi/tests/v0_unencrypted_deserialization_tests.cc b/nearby/presence/np_cpp_ffi/tests/v0_unencrypted_deserialization_tests.cc
index 4760009..42819d6 100644
--- a/nearby/presence/np_cpp_ffi/tests/v0_unencrypted_deserialization_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/v0_unencrypted_deserialization_tests.cc
@@ -24,8 +24,8 @@
 #include "shared_test_util.h"
 
 TEST_F(NpCppTest, InvalidCast) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto deserialize_result =
       nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvPlaintext,
                                                               book);
@@ -34,18 +34,17 @@
 
   // Now try to cast the result into the wrong type and verify the process
   // aborts
-  ASSERT_DEATH(
-      { [[maybe_unused]] auto failure = deserialize_result.IntoV1(); }, "");
+  ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV1(); },
+               "");
 }
 
 TEST_F(NpCppTest, V0DeserializeSingleDataElementTxPower) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(slab);
-  ASSERT_TRUE(maybe_credential_book.ok());
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook credential_book(slab);
+
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvPlaintext,
+                                                              credential_book);
 
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V0);
@@ -71,20 +70,15 @@
 }
 
 TEST_F(NpCppTest, V0LengthOneActionsDataElement) {
-  const std::array<uint8_t, 4> V0AdvPlaintextLengthOneActions{0x00, 0x03, 0x16,
-                                                              0x00};
+  const std::array<uint8_t, 3> V0AdvPlaintextLengthOneActions{0x00, 0x16, 0x00};
   const nearby_protocol::RawAdvertisementPayload adv(
       (nearby_protocol::ByteBuffer<255>(V0AdvPlaintextLengthOneActions)));
 
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          adv, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(adv,
+                                                              credential_book);
 
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V0);
@@ -110,20 +104,16 @@
 }
 
 TEST_F(NpCppTest, V0LengthTwoActionsDataElement) {
-  const std::array<uint8_t, 5> V0AdvPlaintextLengthTwoActions{0x00, 0x03, 0x26,
-                                                              0xD0, 0x46};
+  const std::array<uint8_t, 4> V0AdvPlaintextLengthTwoActions{0x00, 0x26, 0x40,
+                                                              0x40};
   const nearby_protocol::RawAdvertisementPayload adv(
       (nearby_protocol::ByteBuffer<255>(V0AdvPlaintextLengthTwoActions)));
 
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          adv, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(adv,
+                                                              credential_book);
 
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V0);
@@ -145,35 +135,24 @@
 
   ASSERT_EQ(de.GetKind(), nearby_protocol::V0DataElementKind::Actions);
   auto actions = de.AsActions();
-  ASSERT_EQ(actions.GetAsU32(), 0xD0460000);
+  ASSERT_EQ(actions.GetAsU32(), 0x40400000);
 
-  ASSERT_TRUE(
-      actions.HasAction(nearby_protocol::BooleanActionType::NearbyShare));
-  ASSERT_TRUE(actions.HasAction(nearby_protocol::BooleanActionType::Finder));
-  ASSERT_TRUE(
-      actions.HasAction(nearby_protocol::BooleanActionType::FastPairSass));
+  ASSERT_TRUE(actions.HasAction(nearby_protocol::ActionType::CrossDevSdk));
+  ASSERT_TRUE(actions.HasAction(nearby_protocol::ActionType::NearbyShare));
 
+  ASSERT_FALSE(actions.HasAction(nearby_protocol::ActionType::ActiveUnlock));
   ASSERT_FALSE(
-      actions.HasAction(nearby_protocol::BooleanActionType::ActiveUnlock));
-  ASSERT_FALSE(
-      actions.HasAction(nearby_protocol::BooleanActionType::InstantTethering));
-  ASSERT_FALSE(actions.HasAction(nearby_protocol::BooleanActionType::PhoneHub));
-  ASSERT_FALSE(
-      actions.HasAction(nearby_protocol::BooleanActionType::PresenceManager));
-
-  ASSERT_EQ(actions.GetContextSyncSequenceNumber().GetAsU8(), 0xD);
+      actions.HasAction(nearby_protocol::ActionType::InstantTethering));
+  ASSERT_FALSE(actions.HasAction(nearby_protocol::ActionType::PhoneHub));
 }
 
 TEST_F(NpCppTest, V0MultipleDataElements) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
+
   auto deserialize_result =
       nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvPlaintextMultiDe, maybe_credential_book.value());
+          V0AdvPlaintextMultiDe, credential_book);
 
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V0);
@@ -203,28 +182,24 @@
 
   ASSERT_EQ(second_de.GetKind(), nearby_protocol::V0DataElementKind::Actions);
   auto actions = second_de.AsActions();
-  ASSERT_EQ(actions.GetAsU32(), (uint32_t)0x00460000);
-  ASSERT_EQ(actions.GetContextSyncSequenceNumber().GetAsU8(), (uint8_t)0);
+  ASSERT_EQ(actions.GetAsU32(), (uint32_t)0x40400000);
 }
 
 TEST_F(NpCppTest, V0EmptyPayload) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
+
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvEmpty, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty,
+                                                              credential_book);
 
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::Error);
 }
 
 TEST_F(NpCppTest, TestV0AdvMoveConstructor) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
       V0AdvPlaintext, book);
   ASSERT_EQ(result.GetKind(),
@@ -251,8 +226,8 @@
 }
 
 TEST_F(NpCppTest, TestV0AdvMoveAssignment) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
       V0AdvPlaintext, book);
   ASSERT_EQ(result.GetKind(),
@@ -288,56 +263,37 @@
     nearby_protocol::CredentialBook &book) {
   auto adv = nearby_protocol::Deserializer::DeserializeAdvertisement(
       V0AdvPlaintext, book);
+  assert(adv.GetKind() ==
+         np_ffi::internal::DeserializeAdvertisementResultKind::V0);
   return adv;
 }
 
 TEST_F(NpCppTest, V0AdvDestructor) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-  auto book_result = nearby_protocol::CredentialBook::TryCreateFromSlab(
-      maybe_credential_slab.value());
-  ASSERT_TRUE(book_result.ok());
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   {
-    auto deserialize_result = CreateAdv(book_result.value());
-    auto deserialize_result2 = CreateAdv(book_result.value());
-    // Deserialize 2 advertisements, which will take up 2 slots in the handle
-    // map
-    ASSERT_EQ(deserialize_result.GetKind(),
-              np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-    ASSERT_EQ(deserialize_result2.GetKind(),
-              np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-
-    // Going over max amount should result in error
-    auto deserialize_result3 =
-        nearby_protocol::Deserializer::DeserializeAdvertisement(
-            V0AdvPlaintext, book_result.value());
-    ASSERT_EQ(deserialize_result3.GetKind(),
-              np_ffi::internal::DeserializeAdvertisementResultKind::Error);
+    auto deserialize_result = CreateAdv(book);
+    auto deserialize_result2 = CreateAdv(book);
+    auto allocations =
+        nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+    ASSERT_EQ(allocations.v0_payload, 2U);
 
     // Calling IntoV0() should move the underlying resources into the v0
     // object when both go out of scope only one should be freed
     auto v0_adv = deserialize_result.IntoV0();
   }
-
-  // Now that the first v0 adv is out of scope, it should be de-allocated which
-  // will create room for one more to be created.
-  auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvPlaintext, book_result.value());
-  ASSERT_EQ(deserialize_result.GetKind(),
-            np_ffi::internal::DeserializeAdvertisementResultKind::V0);
+  auto allocations =
+      nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+  ASSERT_EQ(allocations.v0_payload, 0U);
 }
 
 TEST_F(NpCppTest, V0AdvUseAfterMove) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
+
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvPlaintext,
+                                                              credential_book);
 
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V0);
@@ -353,8 +309,8 @@
 }
 
 TEST_F(NpCppTest, TestLegibleAdvMoveConstructor) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
       V0AdvPlaintext, book);
   ASSERT_EQ(result.GetKind(),
@@ -391,8 +347,8 @@
 }
 
 TEST_F(NpCppTest, TestLegibleAdvMoveAssignment) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
       V0AdvPlaintext, book);
   ASSERT_EQ(result.GetKind(),
@@ -436,19 +392,20 @@
   auto adv = nearby_protocol::Deserializer::DeserializeAdvertisement(
       V0AdvPlaintext, book);
   auto v0_adv = adv.IntoV0();
-  return v0_adv.IntoLegible();
+  auto legible = v0_adv.IntoLegible();
+  assert(legible.GetIdentityKind() ==
+         nearby_protocol::DeserializedV0IdentityKind::Plaintext);
+  assert(legible.GetNumberOfDataElements() == 1U);
+  return legible;
 }
 
 TEST_F(NpCppTest, V0LegibleAdvUseAfterMove) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto legible_adv = CreateLegibleAdv(book);
 
   // Should be able to use the valid legible adv even though its original parent
   // is now out of scope.
-  ASSERT_EQ(legible_adv.GetIdentityKind(),
-            nearby_protocol::DeserializedV0IdentityKind::Plaintext);
-  ASSERT_EQ(legible_adv.GetNumberOfDataElements(), 1);
   [[maybe_unused]] auto payload = legible_adv.IntoPayload();
 
   // now that the legible adv has moved into the payload it should no longer be
@@ -462,32 +419,19 @@
 }
 
 TEST_F(NpCppTest, LegibleAdvDestructor) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   {
     auto legible_adv = CreateLegibleAdv(book);
     auto legible_adv2 = CreateLegibleAdv(book);
-
-    // check that legible advs are valid.
-    ASSERT_EQ(legible_adv.GetIdentityKind(),
-              nearby_protocol::DeserializedV0IdentityKind::Plaintext);
-    ASSERT_EQ(legible_adv.GetNumberOfDataElements(), 1);
-    ASSERT_EQ(legible_adv2.GetIdentityKind(),
-              nearby_protocol::DeserializedV0IdentityKind::Plaintext);
-    ASSERT_EQ(legible_adv2.GetNumberOfDataElements(), 1);
-
-    // allocation slots should be full
-    ASSERT_EQ(nearby_protocol::Deserializer::DeserializeAdvertisement(
-                  V0AdvPlaintext, book)
-                  .GetKind(),
-              nearby_protocol::DeserializeAdvertisementResultKind::Error);
+    auto allocations =
+        nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+    ASSERT_EQ(allocations.v0_payload, 2U);
   }
-
-  // Verify the handle was de-allocated when legible adv went out of scope
-  auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
-      V0AdvPlaintext, book);
-  ASSERT_EQ(result.GetKind(),
-            nearby_protocol::DeserializeAdvertisementResultKind::V0);
+  // Verify the handles were de-allocated when legible advs went out of scope
+  auto allocations =
+      nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+  ASSERT_EQ(allocations.v0_payload, 0U);
 }
 
 nearby_protocol::V0Payload CreatePayload(
@@ -496,35 +440,9 @@
   return legible_adv.IntoPayload();
 }
 
-TEST_F(NpCppTest, V0PayloadDestructor) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
-  {
-    auto payload = CreatePayload(book);
-    auto payload2 = CreatePayload(book);
-
-    // check that payload adv is valid even though its parent is out of scope
-    ASSERT_TRUE(payload.TryGetDataElement(0).ok());
-    ASSERT_TRUE(payload2.TryGetDataElement(0).ok());
-
-    // allocation slots should be full
-    ASSERT_EQ(nearby_protocol::Deserializer::DeserializeAdvertisement(
-                  V0AdvPlaintext, book)
-                  .GetKind(),
-              nearby_protocol::DeserializeAdvertisementResultKind::Error);
-  }
-
-  // Now that the payload is out of scope its destructor should have been called
-  // freeing the parent handle
-  auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
-      V0AdvPlaintext, book);
-  ASSERT_EQ(result.GetKind(),
-            nearby_protocol::DeserializeAdvertisementResultKind::V0);
-}
-
 TEST_F(NpCppTest, TestV0PayloadMoveConstructor) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
       V0AdvPlaintext, book);
   ASSERT_EQ(result.GetKind(),
@@ -552,8 +470,8 @@
 }
 
 TEST_F(NpCppTest, TestV0PayloadMoveAssignment) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
       V0AdvPlaintext, book);
   ASSERT_EQ(result.GetKind(),
@@ -585,16 +503,34 @@
                "");
 }
 
+TEST_F(NpCppTest, V0PayloadDestructor) {
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
+  {
+    auto payload = CreatePayload(book);
+    auto payload2 = CreatePayload(book);
+
+    // check that payload adv is valid even though its parent is out of scope
+    ASSERT_TRUE(payload.TryGetDataElement(0).ok());
+    ASSERT_TRUE(payload2.TryGetDataElement(0).ok());
+    auto allocations =
+        nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+    ASSERT_EQ(allocations.v0_payload, 2U);
+  }
+
+  // Verify the handle was de-allocated when legible advs went out of scope
+  auto allocations =
+      nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+  ASSERT_EQ(allocations.v0_payload, 0U);
+}
+
 TEST_F(NpCppTest, InvalidDataElementCast) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
+
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvPlaintext,
+                                                              credential_book);
 
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V0);
@@ -618,15 +554,12 @@
 }
 
 TEST_F(NpCppTest, InvalidDataElementIndex) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
+
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V0AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvPlaintext,
+                                                              credential_book);
 
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V0);
diff --git a/nearby/presence/np_cpp_ffi/tests/v0_unencrypted_serialization_tests.cc b/nearby/presence/np_cpp_ffi/tests/v0_unencrypted_serialization_tests.cc
index 0ce4d24..ca28233 100644
--- a/nearby/presence/np_cpp_ffi/tests/v0_unencrypted_serialization_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/v0_unencrypted_serialization_tests.cc
@@ -28,45 +28,27 @@
   ASSERT_FALSE(out_of_range_result.ok());
 }
 
-TEST_F(NpCppTest, ContextSyncSeqNumMustBeInRange) {
-  // Check that out-of-range fails
-  auto out_of_range_result =
-      nearby_protocol::ContextSyncSeqNum::TryBuildFromU8(17);
-  ASSERT_FALSE(out_of_range_result.ok());
-
-  // Check that if it's in range, we can get a context sync seq num and set it
-  // appropriately within an actions field.
-  auto actions = nearby_protocol::V0Actions::BuildNewZeroed(
-      nearby_protocol::AdvertisementBuilderKind::Public);
-  auto seq_num = nearby_protocol::ContextSyncSeqNum::TryBuildFromU8(15).value();
-  actions.SetContextSyncSequenceNumber(seq_num);
-  ASSERT_EQ(actions.GetContextSyncSequenceNumber().GetAsU8(),
-            seq_num.GetAsU8());
-}
-
 TEST_F(NpCppTest, V0UnencryptedActionFlavorMustMatch) {
   auto actions = nearby_protocol::V0Actions::BuildNewZeroed(
       nearby_protocol::AdvertisementBuilderKind::Public);
 
   // Try to set an encrypted-only action.
-  auto mismatch_result = actions.TrySetAction(
-      nearby_protocol::BooleanActionType::InstantTethering, true);
+  auto mismatch_result =
+      actions.TrySetAction(nearby_protocol::ActionType::InstantTethering, true);
   ASSERT_FALSE(mismatch_result.ok());
   // Verify that nothing changed about the actions.
-  ASSERT_EQ(actions.GetAsU32(), 0);
+  ASSERT_EQ(actions.GetAsU32(), 0u);
 
   // Try again, but with a plaintext-compatible action.
-  auto success_result = actions.TrySetAction(
-      nearby_protocol::BooleanActionType::NearbyShare, true);
+  auto success_result =
+      actions.TrySetAction(nearby_protocol::ActionType::NearbyShare, true);
   ASSERT_TRUE(success_result.ok());
-  ASSERT_TRUE(
-      actions.HasAction(nearby_protocol::BooleanActionType::NearbyShare));
+  ASSERT_TRUE(actions.HasAction(nearby_protocol::ActionType::NearbyShare));
 }
 
 // Corresponds to V0DeserializeSingleDataElementTxPower
 TEST_F(NpCppTest, V0SerializeSingleDataElementTxPower) {
-  auto adv_builder =
-      nearby_protocol::V0AdvertisementBuilder::TryCreatePublic().value();
+  auto adv_builder = nearby_protocol::V0AdvertisementBuilder::CreatePublic();
 
   auto tx_power = nearby_protocol::TxPower::TryBuildFromI8(3).value();
   auto de = nearby_protocol::V0DataElement(tx_power);
@@ -78,8 +60,7 @@
   auto actual = serialized_bytes.ToVector();
 
   const std::vector<uint8_t> expected{
-      0x00,       // Adv Header
-      0x03,       // Public DE header
+      0x00,       // Version header
       0x15, 0x03  // Length 1 Tx Power DE with value 3
   };
   ASSERT_EQ(actual, expected);
@@ -87,8 +68,7 @@
 
 // Corresponds to V0DeserializeLengthOneActionsDataElement
 TEST_F(NpCppTest, V0SerializeLengthOneActionsDataElement) {
-  auto adv_builder =
-      nearby_protocol::V0AdvertisementBuilder::TryCreatePublic().value();
+  auto adv_builder = nearby_protocol::V0AdvertisementBuilder::CreatePublic();
   auto actions = nearby_protocol::V0Actions::BuildNewZeroed(
       nearby_protocol::AdvertisementBuilderKind::Public);
   auto de = nearby_protocol::V0DataElement(actions);
@@ -100,8 +80,7 @@
   auto actual = serialized_bytes.ToVector();
 
   const std::vector<uint8_t> expected{
-      0x00,       // Adv Header
-      0x03,       // Public DE header
+      0x00,       // Version header
       0x16, 0x00  // Length 1 Actions DE
   };
 
@@ -110,25 +89,16 @@
 
 // Corresponds to V0DeserializeLengthTwoActionsDataElement
 TEST_F(NpCppTest, V0SerializeLengthTwoActionsDataElement) {
-  auto adv_builder =
-      nearby_protocol::V0AdvertisementBuilder::TryCreatePublic().value();
+  auto adv_builder = nearby_protocol::V0AdvertisementBuilder::CreatePublic();
   auto actions = nearby_protocol::V0Actions::BuildNewZeroed(
       nearby_protocol::AdvertisementBuilderKind::Public);
 
   ASSERT_TRUE(
-      actions
-          .TrySetAction(nearby_protocol::BooleanActionType::NearbyShare, true)
+      actions.TrySetAction(nearby_protocol::ActionType::NearbyShare, true)
           .ok());
   ASSERT_TRUE(
-      actions.TrySetAction(nearby_protocol::BooleanActionType::Finder, true)
+      actions.TrySetAction(nearby_protocol::ActionType::CrossDevSdk, true)
           .ok());
-  ASSERT_TRUE(
-      actions
-          .TrySetAction(nearby_protocol::BooleanActionType::FastPairSass, true)
-          .ok());
-  auto seq_num =
-      nearby_protocol::ContextSyncSeqNum::TryBuildFromU8(0xD).value();
-  actions.SetContextSyncSequenceNumber(seq_num);
 
   auto de = nearby_protocol::V0DataElement(actions);
 
@@ -139,51 +109,8 @@
   auto actual = serialized_bytes.ToVector();
 
   const std::vector<uint8_t> expected{
-      0x00,             // Adv Header
-      0x03,             // Public DE header
-      0x26, 0xD0, 0x46  // Length 2 Actions DE
-  };
-
-  ASSERT_EQ(actual, expected);
-}
-// Corresponds to V0DeserializeMultipleDataElements
-TEST_F(NpCppTest, V0SerializeMultipleDataElements) {
-  auto adv_builder =
-      nearby_protocol::V0AdvertisementBuilder::TryCreatePublic().value();
-
-  auto tx_power = nearby_protocol::TxPower::TryBuildFromI8(5).value();
-  auto tx_power_de = nearby_protocol::V0DataElement(tx_power);
-  auto add_tx_power_de_result = adv_builder.TryAddDE(tx_power_de);
-  ASSERT_TRUE(add_tx_power_de_result.ok());
-
-  auto actions = nearby_protocol::V0Actions::BuildNewZeroed(
-      nearby_protocol::AdvertisementBuilderKind::Public);
-
-  ASSERT_TRUE(
-      actions
-          .TrySetAction(nearby_protocol::BooleanActionType::NearbyShare, true)
-          .ok());
-  ASSERT_TRUE(
-      actions.TrySetAction(nearby_protocol::BooleanActionType::Finder, true)
-          .ok());
-  ASSERT_TRUE(
-      actions
-          .TrySetAction(nearby_protocol::BooleanActionType::FastPairSass, true)
-          .ok());
-
-  auto actions_de = nearby_protocol::V0DataElement(actions);
-
-  auto add_actions_de_result = adv_builder.TryAddDE(actions_de);
-  ASSERT_TRUE(add_actions_de_result.ok());
-
-  auto serialized_bytes = adv_builder.TrySerialize().value();
-  auto actual = serialized_bytes.ToVector();
-
-  const std::vector<uint8_t> expected{
-      0x00,              // Adv Header
-      0x03,              // Public DE header
-      0x15, 0x05,        // Tx Power value 5
-      0x26, 0x00, 0x46,  // Length 2 Actions
+      0x00,             // Version header
+      0x26, 0x40, 0x40  // Length 2 Actions DE
   };
 
   ASSERT_EQ(actual, expected);
@@ -192,14 +119,13 @@
 // TODO: Reinstate this test.
 // TEST_F(NpCppTest, V0SerializeEmptyPayload) {
 //  auto adv_builder =
-//  nearby_protocol::V0AdvertisementBuilder::TryCreatePublic().value(); auto
+//  nearby_protocol::V0AdvertisementBuilder::CreatePublic(); auto
 //  serialize_result = adv_builder.TrySerialize();
 //  ASSERT_FALSE(serialize_result.ok());
 //}
 
 TEST_F(NpCppTest, TestV0AdvBuilderMoveConstructor) {
-  auto adv_builder =
-      nearby_protocol::V0AdvertisementBuilder::TryCreatePublic().value();
+  auto adv_builder = nearby_protocol::V0AdvertisementBuilder::CreatePublic();
   // Move it, and ensure it's still valid.
   nearby_protocol::V0AdvertisementBuilder moved_adv_builder(
       std::move(adv_builder));
@@ -227,16 +153,18 @@
   {
     // Get us up to the limit on the number of adv builders
     auto adv_builder_one =
-        nearby_protocol::V0AdvertisementBuilder::TryCreatePublic().value();
+        nearby_protocol::V0AdvertisementBuilder::CreatePublic();
     auto adv_builder_two =
-        nearby_protocol::V0AdvertisementBuilder::TryCreatePublic().value();
-    // Assert that we're at the limit
-    ASSERT_FALSE(
-        nearby_protocol::V0AdvertisementBuilder::TryCreatePublic().ok());
+        nearby_protocol::V0AdvertisementBuilder::CreatePublic();
+    auto current_allocations =
+        nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+    ASSERT_EQ(current_allocations.v0_advertisement_builder, 2U);
     // Destructors should run.
   }
   // The space from the adv builders should've been reclaimed now.
-  ASSERT_TRUE(nearby_protocol::V0AdvertisementBuilder::TryCreatePublic().ok());
+  auto current_allocations =
+      nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+  ASSERT_EQ(current_allocations.v0_advertisement_builder, 0U);
 }
 
 // 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 825b36c..65b9730 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
@@ -15,7 +15,6 @@
 #include <array>
 #include <cstdint>
 #include <span>
-#include <string>
 #include <vector>
 
 #include "absl/status/status.h"
@@ -26,26 +25,20 @@
 #include "shared_test_util.h"
 
 TEST_F(NpCppTest, V1PrivateIdentitySimpleCase) {
-  auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(slab_result.ok());
-
+  nearby_protocol::CredentialSlab slab;
   const std::span<uint8_t> metadata_span(V1AdvEncryptedMetadata);
   const nearby_protocol::MatchedCredentialData match_data(123, metadata_span);
-
   const nearby_protocol::V1MatchableCredential v1_cred(
-      V1AdvKeySeed, V1AdvExpectedUnsignedMetadataKeyHmac,
-      V1AdvExpectedSignedMetadataKeyHmac, V1AdvPublicKey, match_data);
+      V1AdvKeySeed, V1AdvExpectedMicExtendedSaltIdentityTokenHmac,
+      V1AdvExpectedSignatureIdentityTokenHmac, V1AdvPublicKey, match_data);
 
-  auto add_result = slab_result->AddV1Credential(v1_cred);
+  auto add_result = slab.AddV1Credential(v1_cred);
   ASSERT_EQ(add_result, absl::OkStatus());
 
-  auto book_result =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(*slab_result);
-  ASSERT_TRUE(book_result.ok());
-
+  nearby_protocol::CredentialBook book(slab);
   auto deserialize_result =
       nearby_protocol::Deserializer::DeserializeAdvertisement(V1AdvEncrypted,
-                                                              *book_result);
+                                                              book);
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V1);
 
@@ -59,28 +52,27 @@
             nearby_protocol::DeserializedV1IdentityKind::Decrypted);
   ASSERT_EQ(section->NumberOfDataElements(), 1);
 
-  auto metadata = section->DecryptMetadata();
+  auto metadata = section->TryDecryptMetadata();
   ASSERT_TRUE(metadata.ok());
   ASSERT_EQ(ExpectedV1DecryptedMetadata,
             std::string(metadata->begin(), metadata->end()));
 
   auto identity_details = section->GetIdentityDetails();
   ASSERT_TRUE(identity_details.ok());
-  ASSERT_EQ(identity_details->cred_id, 123);
+  ASSERT_EQ(identity_details->cred_id, 123U);
   ASSERT_EQ(identity_details->verification_mode,
             nearby_protocol::V1VerificationMode::Signature);
-  ASSERT_EQ(identity_details->identity_type,
-            nearby_protocol::EncryptedIdentityType::Private);
 
   auto de = section->TryGetDataElement(0);
   ASSERT_TRUE(de.ok());
-  ASSERT_EQ(de->GetDataElementTypeCode(), 5);
+  ASSERT_EQ(de->GetDataElementTypeCode(), 5U);
   ASSERT_EQ(de->GetPayload().ToVector(), std::vector<uint8_t>{7});
 
   auto offset = de->GetOffset();
   auto derived_salt = section->DeriveSaltForOffset(offset);
   ASSERT_TRUE(derived_salt.ok());
-  const std::array<uint8_t, 16> expected = {
-      94, 154, 245, 152, 164, 22, 131, 157, 8, 79, 28, 77, 236, 57, 17, 97};
+  const std::array<uint8_t, 16> expected = {0xD5, 0x63, 0x47, 0x39, 0x77, 0x84,
+                                            0x38, 0xF2, 0x91, 0xBC, 0x24, 0x21,
+                                            0xAD, 0x80, 0x88, 0x16};
   ASSERT_EQ(*derived_salt, expected);
-}
\ No newline at end of file
+}
diff --git a/nearby/presence/np_cpp_ffi/tests/v1_unencrypted_deserialization_tests.cc b/nearby/presence/np_cpp_ffi/tests/v1_unencrypted_deserialization_tests.cc
index f7e233b..4f50e0c 100644
--- a/nearby/presence/np_cpp_ffi/tests/v1_unencrypted_deserialization_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/v1_unencrypted_deserialization_tests.cc
@@ -24,16 +24,12 @@
 #include "shared_test_util.h"
 
 TEST_F(NpCppTest, V1SimpleTestCase) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
+  nearby_protocol::CredentialSlab credential_slab;
+  nearby_protocol::CredentialBook credential_book(credential_slab);
 
   auto deserialize_result =
-      nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V1AdvPlaintext, maybe_credential_book.value());
+      nearby_protocol::Deserializer::DeserializeAdvertisement(V1AdvPlaintext,
+                                                              credential_book);
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V1);
 
@@ -66,8 +62,8 @@
 }
 
 TEST_F(NpCppTest, TestV1AdvMoveConstructor) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
       V1AdvPlaintext, book);
   ASSERT_EQ(result.GetKind(),
@@ -102,8 +98,8 @@
 }
 
 TEST_F(NpCppTest, TestV1AdvMoveAssignment) {
-  auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
-  auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
       V1AdvPlaintext, book);
   ASSERT_EQ(result.GetKind(),
@@ -161,44 +157,43 @@
 }
 
 TEST_F(NpCppTest, TestSectionOwnership) {
-  auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate();
-  ASSERT_TRUE(maybe_credential_slab.ok());
-  auto maybe_credential_book =
-      nearby_protocol::CredentialBook::TryCreateFromSlab(
-          maybe_credential_slab.value());
-  ASSERT_TRUE(maybe_credential_book.ok());
-
+  nearby_protocol::CredentialSlab slab;
+  nearby_protocol::CredentialBook book(slab);
   {
-    auto section = GetSection(maybe_credential_book.value());
+    auto section = GetSection(book);
     ASSERT_EQ(section.GetIdentityKind(),
               nearby_protocol::DeserializedV1IdentityKind::Plaintext);
     ASSERT_EQ(section.NumberOfDataElements(), 1);
     ASSERT_TRUE(section.TryGetDataElement(0).ok());
 
-    auto section2 = GetSection(maybe_credential_book.value());
+    auto section2 = GetSection(book);
     ASSERT_EQ(section2.GetIdentityKind(),
               nearby_protocol::DeserializedV1IdentityKind::Plaintext);
     ASSERT_EQ(section2.NumberOfDataElements(), 1);
     ASSERT_TRUE(section2.TryGetDataElement(0).ok());
 
-    ASSERT_FALSE(TryDeserializeNewV1Adv(maybe_credential_book.value()));
+    auto allocations =
+        nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+    ASSERT_EQ(allocations.legible_v1_sections, 2U);
   }
 
-  // now that the section has gone out of scope, deserializing a new adv should
-  // succeed
-  ASSERT_TRUE(TryDeserializeNewV1Adv(maybe_credential_book.value()));
+  // now that the section has gone out of scope the allocation should be
+  // released
+  auto allocations =
+      nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
+  ASSERT_EQ(allocations.legible_v1_sections, 0U);
 }
 
 /*
  * Multiple sections are not supported in plaintext advertisements
  * TODO Update the below test to use encrypted sections
 TEST(NpCppTest, V1MultipleSections) {
-  auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
-  ASSERT_TRUE(maybe_credential_book.ok());
+  auto credential_book = nearby_protocol::CredentialBook::Create();
+  ASSERT_TRUE(credential_book.ok());
 
   auto deserialize_result =
       nearby_protocol::Deserializer::DeserializeAdvertisement(
-          V1AdvMultipleSections, maybe_credential_book.value());
+          V1AdvMultipleSections, credential_book);
   ASSERT_EQ(deserialize_result.GetKind(),
             nearby_protocol::DeserializeAdvertisementResultKind::V1);
 
diff --git a/nearby/presence/np_ed25519/src/lib.rs b/nearby/presence/np_ed25519/src/lib.rs
index fe2ab5a..b5123e1 100644
--- a/nearby/presence/np_ed25519/src/lib.rs
+++ b/nearby/presence/np_ed25519/src/lib.rs
@@ -21,101 +21,34 @@
 #![no_std]
 
 use array_view::ArrayView;
-use crypto_provider::ed25519::{
-    Ed25519Provider, KeyPair as _, PrivateKey, PublicKey as _, RawPrivateKey, RawPrivateKeyPermit,
-    RawPublicKey, RawSignature, Signature as _, SignatureError,
-};
-use crypto_provider::CryptoProvider;
+use crypto_provider::ed25519::{Ed25519Provider, PrivateKey, PublicKey, Signature, SignatureError};
 use sink::{Sink, SinkWriter};
 use tinyvec::ArrayVec;
 
-/// Convenient type-alias for a crypto-provider's Ed25519 key-pair
-type CpKeyPair<C> = <<C as CryptoProvider>::Ed25519 as Ed25519Provider>::KeyPair;
-
-/// Type-alias for the Ed25519 public key type
-type CpPublicKey<C> = <<C as CryptoProvider>::Ed25519 as Ed25519Provider>::PublicKey;
-
-/// Type-alias for the Ed25519 signature type
-type CpSignature<C> = <<C as CryptoProvider>::Ed25519 as Ed25519Provider>::Signature;
-
 /// Maximum length of the combined (context len byte) + (context bytes) + (signing payload)
 /// byte-array which an ed25519 signature will be computed over. This is deliberately
 /// chosen to be large enough to incorporate an entire v1 adv as the signing payload.
 pub const MAX_SIGNATURE_BUFFER_LEN: usize = 512;
 
-/// Representation of an Ed25519 key-pair using the given
-/// [`CryptoProvider`] to back its implementation.
-/// Contains both the public and secret halves of an
-/// asymmetric key, and so it may be used to
-/// both sign and verify message signatures.
-pub struct KeyPair<C: CryptoProvider>(CpKeyPair<C>);
-
-impl<C: CryptoProvider> KeyPair<C> {
-    /// Returns the `KeyPair`'s private key bytes.
-    /// This method is only usable in situations where
-    /// the caller has permission to handle the raw bytes
-    /// of a private key.
-    pub fn raw_private_key(&self, permit: &RawPrivateKeyPermit) -> RawPrivateKey {
-        self.0.raw_private_key(permit)
-    }
-
-    /// Builds this key-pair from an array of its private key bytes in the format
-    /// yielded by `private_key`.
-    /// This method is only usable in situations where
-    /// the caller has permission to handle the raw bytes
-    /// of a private key.
-    pub fn from_raw_private_key(private_key: &RawPrivateKey, permit: &RawPrivateKeyPermit) -> Self {
-        Self(CpKeyPair::<C>::from_raw_private_key(private_key, permit))
-    }
-
-    /// Returns the private key of this key-pair.
-    pub fn private_key(&self) -> PrivateKey {
-        self.0.private_key()
-    }
-
-    /// Builds this key-pair from a private key.
-    pub fn from_private_key(private_key: &PrivateKey) -> Self {
-        Self(CpKeyPair::<C>::from_private_key(private_key))
-    }
-
-    /// Sign the given message with the given context and
-    /// return a digital signature. The message is represented
-    /// using a [`SinkWriter`] to allow the caller to construct
-    /// the payload to sign without requiring a fully-assembled
-    /// payload available as a slice.
-    ///
-    /// If the message writer writes too much data (greater than 256 bytes),
-    /// this will return `None` instead of a valid signature,
-    /// and so uses in `np_adv` will use `.expect` on the returned value
-    /// to indicate that this length constraint has been considered.
-    pub fn sign_with_context<W: SinkWriter<DataType = u8>>(
-        &self,
-        context: &SignatureContext,
-        msg_writer: W,
-    ) -> Option<Signature<C>> {
-        let mut buffer = context.create_signature_buffer();
-        buffer.try_extend_from_writer(msg_writer).map(|_| Signature(self.0.sign(buffer.as_ref())))
-    }
-
-    /// Gets the public key of this key-pair
-    pub fn public(&self) -> PublicKey<C> {
-        PublicKey { public_key: self.0.public() }
-    }
-
-    /// Generates an ed25519 keypair from a CSPRNG
-    /// generate is not available in `no-std`
-    #[cfg(feature = "std")]
-    pub fn generate() -> Self {
-        Self(CpKeyPair::<C>::generate())
-    }
+/// Sign the given message with the given context and
+/// return a digital signature. The message is represented
+/// using a [`SinkWriter`] to allow the caller to construct
+/// the payload to sign without requiring a fully-assembled
+/// payload available as a slice.
+///
+/// If the message writer writes too much data (greater than 256 bytes),
+/// this will return `None` instead of a valid signature,
+/// and so uses in `np_adv` will use `.expect` on the returned value
+/// to indicate that this length constraint has been considered.
+pub fn sign_with_context<E: Ed25519Provider, W: SinkWriter<DataType = u8>>(
+    private_key: &PrivateKey,
+    context: &SignatureContext,
+    msg_writer: W,
+) -> Option<Signature> {
+    let mut buffer = context.create_signature_buffer();
+    buffer.try_extend_from_writer(msg_writer).map(|_| private_key.sign::<E>(buffer.as_ref()))
 }
 
-/// Error raised when attempting to deserialize a key-pair
-/// from a byte-array, but the bytes do not represent a valid
-/// ed25519 key-pair
-#[derive(Debug)]
-pub struct InvalidKeyPairBytes;
-
 /// Errors yielded when attempting to verify an ed25519 signature.
 #[derive(Debug, PartialEq, Eq)]
 pub enum SignatureVerificationError {
@@ -131,104 +64,38 @@
     }
 }
 
-/// Representation of an Ed25519 public key used for
-/// signature verification.
-pub struct PublicKey<C: CryptoProvider> {
-    public_key: CpPublicKey<C>,
-}
-
-impl<C: CryptoProvider> PublicKey<C> {
-    /// Constructs a public key for NP adv signature verification
-    /// from a public key under the given crypto-provider
-    pub fn new(public_key: CpPublicKey<C>) -> Self {
-        Self { public_key }
-    }
-    /// Succeeds if the signature was a valid signature created via the corresponding
-    /// keypair to this public key using the given [`SignatureContext`] on the given
-    /// message payload. The message payload is represented
-    /// using a [`SinkWriter`] to allow the caller to construct
-    /// the payload to sign without requiring a fully-assembled
-    /// payload available as a slice.
-    ///
-    /// If the message writer writes too much data (greater than 256 bytes),
-    /// this will return `None` instead of a valid signature,
-    /// and so uses in `np_adv` will use `.expect` on the returned value
-    /// to indicate that this length constraint has been considered.
-    pub fn verify_signature_with_context<W: SinkWriter<DataType = u8>>(
-        &self,
-        context: &SignatureContext,
-        msg_writer: W,
-        signature: &Signature<C>,
-    ) -> Result<(), SignatureVerificationError> {
-        let mut buffer = context.create_signature_buffer();
-        let maybe_write_success = buffer.try_extend_from_writer(msg_writer);
-        match maybe_write_success {
-            Some(_) => {
-                self.public_key.verify_strict(buffer.as_ref(), &signature.0)?;
-                Ok(())
-            }
-            None => Err(SignatureVerificationError::PayloadTooBig),
+/// Succeeds if the signature was a valid signature created via the corresponding
+/// keypair to this public key using the given [`SignatureContext`] on the given
+/// message payload. The message payload is represented
+/// using a [`SinkWriter`] to allow the caller to construct
+/// the payload to sign without requiring a fully-assembled
+/// payload available as a slice.
+///
+/// If the message writer writes too much data (greater than 256 bytes),
+/// this will return `None` instead of a valid signature,
+/// and so uses in `np_adv` will use `.expect` on the returned value
+/// to indicate that this length constraint has been considered.
+pub fn verify_signature_with_context<E: Ed25519Provider, W: SinkWriter<DataType = u8>>(
+    public_key: &PublicKey,
+    context: &SignatureContext,
+    msg_writer: W,
+    signature: Signature,
+) -> Result<(), SignatureVerificationError> {
+    let mut buffer = context.create_signature_buffer();
+    let maybe_write_success = buffer.try_extend_from_writer(msg_writer);
+    match maybe_write_success {
+        Some(_) => {
+            public_key.verify_strict::<E>(buffer.as_ref(), signature)?;
+            Ok(())
         }
-    }
-
-    /// Builds an ed25519 public key from an array of bytes in
-    /// the format yielded by `to_bytes`.
-    pub fn from_bytes(bytes: &RawPublicKey) -> Result<Self, InvalidPublicKeyBytes> {
-        CpPublicKey::<C>::from_bytes(bytes)
-            .map(|public_key| Self { public_key })
-            .map_err(|_| InvalidPublicKeyBytes)
-    }
-
-    /// Yields the bytes of this ed25519 public key
-    pub fn to_bytes(&self) -> RawPublicKey {
-        self.public_key.to_bytes()
-    }
-}
-
-impl<C: CryptoProvider> Clone for PublicKey<C> {
-    fn clone(&self) -> Self {
-        #[allow(clippy::expect_used)]
-        Self::from_bytes(&self.to_bytes()).expect("This should always succeed since self will always contain valid public key bytes, which is verified on creation")
-    }
-}
-
-/// Error raised when attempting to deserialize a public key
-/// from a byte-array, but the bytes do not represent a valid
-/// ed25519 public key
-#[derive(Debug)]
-pub struct InvalidPublicKeyBytes;
-
-/// Representation of an Ed25519 message signature,
-/// which can be checked against a [`PublicKey`]
-/// for validity.
-pub struct Signature<C: CryptoProvider>(CpSignature<C>);
-
-impl<C: CryptoProvider> Signature<C> {
-    /// Returns a slice of the signature bytes
-    pub fn to_bytes(&self) -> RawSignature {
-        self.0.to_bytes()
-    }
-}
-
-/// Error raised when attempting to construct a [`Signature`]
-/// from a byte-array which is not of the proper length or format.
-#[derive(Debug)]
-pub struct InvalidSignatureBytes;
-
-impl<C: CryptoProvider> TryFrom<&[u8]> for Signature<C> {
-    type Error = InvalidSignatureBytes;
-    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
-        bytes
-            .try_into()
-            .map(|sig| Self(CpSignature::<C>::from_bytes(sig)))
-            .map_err(|_| InvalidSignatureBytes)
+        None => Err(SignatureVerificationError::PayloadTooBig),
     }
 }
 
 /// Minimum length (in bytes) for a [`SignatureContext`] (which cannot be empty).
 pub const MIN_SIGNATURE_CONTEXT_LEN: usize = 1;
 
-/// Maximum length (in bytes) for a [`SignatureContext`] (which uses a 8-bit length field).
+/// Maximum length (in bytes) for a [`SignatureContext`] (which uses an 8-bit length field).
 pub const MAX_SIGNATURE_CONTEXT_LEN: usize = 255;
 
 /// (Non-empty) context bytes to use in the construction of NP's
diff --git a/nearby/presence/np_ffi_core/Cargo.toml b/nearby/presence/np_ffi_core/Cargo.toml
index a6b21b0..7c3c112 100644
--- a/nearby/presence/np_ffi_core/Cargo.toml
+++ b/nearby/presence/np_ffi_core/Cargo.toml
@@ -18,6 +18,8 @@
 crypto_provider_default = { workspace = true, default-features = false }
 lock_adapter.workspace = true
 lazy_static.workspace = true
+strum.workspace = true
+strum_macros.workspace = true
 
 [features]
 default = ["rustcrypto"]
diff --git a/nearby/presence/np_ffi_core/src/common.rs b/nearby/presence/np_ffi_core/src/common.rs
index e90a072..592e1cd 100644
--- a/nearby/presence/np_ffi_core/src/common.rs
+++ b/nearby/presence/np_ffi_core/src/common.rs
@@ -18,11 +18,10 @@
 use crypto_provider::{CryptoProvider, CryptoRng};
 use crypto_provider_default::CryptoProviderImpl;
 use handle_map::HandleNotPresentError;
-use lock_adapter::std::{RwLock, RwLockWriteGuard};
+use lock_adapter::stdlib::{RwLock, RwLockWriteGuard};
 use lock_adapter::RwLock as _;
-use std::string::String;
 
-pub(crate) const DEFAULT_MAX_HANDLES: u32 = u32::MAX - 1;
+const MAX_HANDLES: u32 = u32::MAX - 1;
 
 /// Configuration for top-level constants to be used
 /// by the rest of the FFI which are independent of
@@ -41,40 +40,6 @@
     ///   assuming that that call completes successfully.
     /// - In all other cases, 16 shards will be used by default.
     num_shards: u8,
-
-    /// The maximum number of credential slabs which may be active
-    /// at any one time. By default, this value will be set to
-    /// `u32::MAX - 1`, which is the upper-bound on this value.
-    max_num_credential_slabs: u32,
-
-    /// The maximum number of credential books which may be active
-    /// at any one time. By default, this value will be set to
-    /// `u32::MAX - 1`, which is the upper-bound on this value.
-    max_num_credential_books: u32,
-
-    /// The maximum number of deserialized v0 advertisements
-    /// which may be active at any one time. By default, this
-    /// value will be set to `u32::MAX - 1`, which is the upper-bound
-    /// on this value.
-    max_num_deserialized_v0_advertisements: u32,
-
-    /// The maximum number of deserialized v1 advertisements
-    /// which may be active at any one time. By default, this
-    /// value will be set to `u32::MAX - 1`, which is the upper-bound
-    /// on this value.
-    max_num_deserialized_v1_advertisements: u32,
-
-    /// The maximum number of v0 advertisement builders
-    /// which may be active at any one time. By default, this
-    /// value will be set to `u32::MAX - 1`, which is the upper-bound
-    /// on this value.
-    max_num_v0_advertisement_builders: u32,
-
-    /// The maximum number of v1 advertisement builders
-    /// which may be active at any one time. By default, this
-    /// value will be set to `u32::MAX - 1`, which is the upper-bound
-    /// on this value.
-    max_num_v1_advertisement_builders: u32,
 }
 
 impl Default for CommonConfig {
@@ -85,15 +50,7 @@
 
 impl CommonConfig {
     pub(crate) const fn new() -> Self {
-        Self {
-            num_shards: 0,
-            max_num_credential_slabs: DEFAULT_MAX_HANDLES,
-            max_num_credential_books: DEFAULT_MAX_HANDLES,
-            max_num_deserialized_v0_advertisements: DEFAULT_MAX_HANDLES,
-            max_num_deserialized_v1_advertisements: DEFAULT_MAX_HANDLES,
-            max_num_v0_advertisement_builders: DEFAULT_MAX_HANDLES,
-            max_num_v1_advertisement_builders: DEFAULT_MAX_HANDLES,
-        }
+        Self { num_shards: 0 }
     }
     #[cfg(feature = "std")]
     pub(crate) fn num_shards(&self) -> u8 {
@@ -114,108 +71,23 @@
             self.num_shards
         }
     }
-    pub(crate) fn max_num_credential_slabs(&self) -> u32 {
-        self.max_num_credential_slabs
-    }
-    pub(crate) fn max_num_credential_books(&self) -> u32 {
-        self.max_num_credential_books
-    }
-    pub(crate) fn max_num_deserialized_v0_advertisements(&self) -> u32 {
-        self.max_num_deserialized_v0_advertisements
-    }
-    pub(crate) fn max_num_deserialized_v1_advertisements(&self) -> u32 {
-        self.max_num_deserialized_v1_advertisements
-    }
-    pub(crate) fn max_num_v0_advertisement_builders(&self) -> u32 {
-        self.max_num_v0_advertisement_builders
-    }
-    pub(crate) fn max_num_v1_advertisement_builders(&self) -> u32 {
-        self.max_num_v1_advertisement_builders
-    }
     pub(crate) fn set_num_shards(&mut self, num_shards: u8) {
         self.num_shards = num_shards
     }
+}
 
-    /// Sets the maximum number of active handles to credential-books
-    /// which may be active at any one time.
-    /// Max value: `u32::MAX - 1`.
-    pub fn set_max_num_credential_books(&mut self, max_num_credential_books: u32) {
-        self.max_num_credential_books = DEFAULT_MAX_HANDLES.min(max_num_credential_books)
-    }
-
-    /// Sets the maximum number of active handles to credential-slabs
-    /// which may be active at any one time.
-    /// Max value: `u32::MAX - 1`.
-    pub fn set_max_num_credential_slabs(&mut self, max_num_credential_slabs: u32) {
-        self.max_num_credential_slabs = DEFAULT_MAX_HANDLES.min(max_num_credential_slabs)
-    }
-
-    /// Sets the maximum number of active handles to deserialized v0
-    /// advertisements which may be active at any one time.
-    /// Max value: `u32::MAX - 1`.
-    pub fn set_max_num_deserialized_v0_advertisements(
-        &mut self,
-        max_num_deserialized_v0_advertisements: u32,
-    ) {
-        self.max_num_deserialized_v0_advertisements =
-            DEFAULT_MAX_HANDLES.min(max_num_deserialized_v0_advertisements)
-    }
-
-    /// Sets the maximum number of active handles to deserialized v1
-    /// advertisements which may be active at any one time.
-    /// Max value: `u32::MAX - 1`.
-    pub fn set_max_num_deserialized_v1_advertisements(
-        &mut self,
-        max_num_deserialized_v1_advertisements: u32,
-    ) {
-        self.max_num_deserialized_v1_advertisements =
-            DEFAULT_MAX_HANDLES.min(max_num_deserialized_v1_advertisements)
-    }
-    /// Sets the maximum number of active handles to v0 advertisement
-    /// builders which may be active at any one time.
-    /// Max value: `u32::MAX - 1`.
-    pub fn set_max_num_v0_advertisement_builders(
-        &mut self,
-        max_num_v0_advertisement_builders: u32,
-    ) {
-        self.max_num_v0_advertisement_builders =
-            DEFAULT_MAX_HANDLES.min(max_num_v0_advertisement_builders)
-    }
-    /// Sets the maximum number of active handles to v1 advertisement
-    /// builders which may be active at any one time.
-    /// Max value: `u32::MAX - 1`.
-    pub fn set_max_num_v1_advertisement_builders(
-        &mut self,
-        max_num_v1_advertisement_builders: u32,
-    ) {
-        self.max_num_v1_advertisement_builders =
-            DEFAULT_MAX_HANDLES.min(max_num_v1_advertisement_builders)
+pub(crate) fn default_handle_map_dimensions() -> handle_map::HandleMapDimensions {
+    handle_map::HandleMapDimensions {
+        num_shards: global_num_shards(),
+        max_active_handles: MAX_HANDLES,
     }
 }
 
 static COMMON_CONFIG: RwLock<CommonConfig> = RwLock::new(CommonConfig::new());
 
-pub(crate) fn global_num_shards() -> u8 {
+fn global_num_shards() -> u8 {
     COMMON_CONFIG.read().num_shards()
 }
-pub(crate) fn global_max_num_credential_slabs() -> u32 {
-    COMMON_CONFIG.read().max_num_credential_slabs()
-}
-pub(crate) fn global_max_num_credential_books() -> u32 {
-    COMMON_CONFIG.read().max_num_credential_books()
-}
-pub(crate) fn global_max_num_deserialized_v0_advertisements() -> u32 {
-    COMMON_CONFIG.read().max_num_deserialized_v0_advertisements()
-}
-pub(crate) fn global_max_num_deserialized_v1_advertisements() -> u32 {
-    COMMON_CONFIG.read().max_num_deserialized_v1_advertisements()
-}
-pub(crate) fn global_max_num_v0_advertisement_builders() -> u32 {
-    COMMON_CONFIG.read().max_num_v0_advertisement_builders()
-}
-pub(crate) fn global_max_num_v1_advertisement_builders() -> u32 {
-    COMMON_CONFIG.read().max_num_v1_advertisement_builders()
-}
 
 /// Sets an override to the number of shards to employ in the NP FFI's
 /// internal handle-maps, which places an upper bound on the number
@@ -236,94 +108,43 @@
     config.set_num_shards(num_shards);
 }
 
-/// Sets the maximum number of active handles to credential slabs
-/// which may be active at any one time. Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call utilizing credential slabs.
-pub fn global_config_set_max_num_credential_slabs(max_num_credential_slabs: u32) {
-    let mut config = COMMON_CONFIG.write();
-    config.set_max_num_credential_slabs(max_num_credential_slabs);
-}
-/// Sets the maximum number of active handles to credential books
-/// which may be active at any one time. Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call utilizing credential books.
-pub fn global_config_set_max_num_credential_books(max_num_credential_books: u32) {
-    let mut config = COMMON_CONFIG.write();
-    config.set_max_num_credential_books(max_num_credential_books);
+/// Holds the count of handles currently allocated for each handle type
+#[repr(C)]
+pub struct CurrentHandleAllocations {
+    cred_book: u32,
+    cred_slab: u32,
+    decrypted_metadata: u32,
+    v0_payload: u32,
+    legible_v1_sections: u32,
+    v0_advertisement_builder: u32,
+    v1_advertisement_builder: u32,
 }
 
-/// Sets the maximum number of active handles to deserialized v0
-/// advertisements which may be active at any one time.
-/// Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call which references or returns a deserialized V0 advertisement.
-pub fn global_config_set_max_num_deserialized_v0_advertisements(
-    max_num_deserialized_v0_advertisements: u32,
-) {
-    let mut config = COMMON_CONFIG.write();
-    config.set_max_num_deserialized_v0_advertisements(max_num_deserialized_v0_advertisements);
+/// Returns the count of currently allocated handle types being held by the rust layer. Useful
+/// for debugging, logging, and testing.
+pub fn global_config_get_current_allocation_count() -> CurrentHandleAllocations {
+    CurrentHandleAllocations {
+        cred_book: crate::credentials::credential_book::get_current_allocation_count(),
+        cred_slab: crate::credentials::credential_slab::get_current_allocation_count(),
+        decrypted_metadata: crate::deserialize::decrypted_metadata::get_current_allocation_count(),
+        v0_payload: crate::deserialize::v0::v0_payload::get_current_allocation_count(),
+        legible_v1_sections:
+            crate::deserialize::v1::legible_v1_sections::get_current_allocation_count(),
+        v0_advertisement_builder:
+            crate::serialize::v0::advertisement_builder::get_current_allocation_count(),
+        v1_advertisement_builder:
+            crate::serialize::v1::advertisement_builder::get_current_allocation_count(),
+    }
 }
 
-/// Sets the maximum number of active handles to deserialized v1
-/// advertisements which may be active at any one time.
-/// Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call which references or returns a deserialized V1 advertisement.
-pub fn global_config_set_max_num_deserialized_v1_advertisements(
-    max_num_deserialized_v1_advertisements: u32,
-) {
-    let mut config = COMMON_CONFIG.write();
-    config.set_max_num_deserialized_v1_advertisements(max_num_deserialized_v1_advertisements);
-}
-
-/// Sets the maximum number of active handles to v0 advertisement
-/// builders which may be active at any one time.
-/// Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call which references or returns a v0 advertisement builder.
-pub fn global_config_set_max_num_v0_advertisement_builders(max_num_v0_advertisement_builders: u32) {
-    let mut config = COMMON_CONFIG.write();
-    config.set_max_num_v0_advertisement_builders(max_num_v0_advertisement_builders);
-}
-
-/// Sets the maximum number of active handles to v1 advertisement
-/// builders which may be active at any one time.
-/// Max value: `u32::MAX - 1`.
-///
-/// Setting this value will have no effect if the handle-maps for the
-/// API have already begun being used by the client code, and any
-/// values set will take effect upon the first usage of any API
-/// call which references or returns a v1 advertisement builder.
-pub fn global_config_set_max_num_v1_advertisement_builders(max_num_v1_advertisement_builders: u32) {
-    let mut config = COMMON_CONFIG.write();
-    config.set_max_num_v1_advertisement_builders(max_num_v1_advertisement_builders);
-}
-// API surfaces:
-
 /// A result-type enum which tells the caller whether/not a deallocation
 /// succeeded or failed due to the requested handle not being present.
 #[repr(C)]
 pub enum DeallocateResult {
     /// The requested handle to deallocate was not present in the map
-    NotPresent = 0,
+    NotPresent = 1,
     /// The object behind the handle was successfully deallocated
-    Success = 1,
+    Success = 2,
 }
 
 impl From<Result<(), HandleNotPresentError>> for DeallocateResult {
@@ -431,43 +252,6 @@
     CRYPTO_RNG.write()
 }
 
-/// The DE type for an encrypted identity
-#[derive(Clone, Copy)]
-#[repr(u8)]
-pub enum EncryptedIdentityType {
-    /// Identity for broadcasts to nearby devices with the same
-    /// logged-in-account (for some account).
-    Private = 1,
-    /// Identity for broadcasts to nearby devices which this
-    /// device has declared to trust.
-    Trusted = 2,
-    /// Identity for broadcasts to devices which have been provisioned
-    /// offline with this device.
-    Provisioned = 4,
-}
-
-impl From<EncryptedIdentityType> for np_adv::de_type::EncryptedIdentityDataElementType {
-    fn from(val: EncryptedIdentityType) -> np_adv::de_type::EncryptedIdentityDataElementType {
-        use np_adv::de_type::EncryptedIdentityDataElementType;
-        match val {
-            EncryptedIdentityType::Private => EncryptedIdentityDataElementType::Private,
-            EncryptedIdentityType::Trusted => EncryptedIdentityDataElementType::Trusted,
-            EncryptedIdentityType::Provisioned => EncryptedIdentityDataElementType::Provisioned,
-        }
-    }
-}
-
-impl From<np_adv::de_type::EncryptedIdentityDataElementType> for EncryptedIdentityType {
-    fn from(value: np_adv::de_type::EncryptedIdentityDataElementType) -> Self {
-        use np_adv::de_type::EncryptedIdentityDataElementType;
-        match value {
-            EncryptedIdentityDataElementType::Private => Self::Private,
-            EncryptedIdentityDataElementType::Trusted => Self::Trusted,
-            EncryptedIdentityDataElementType::Provisioned => Self::Provisioned,
-        }
-    }
-}
-
 /// Error returned if the bit representation of a supposedly-Rust-constructed
 /// -and-validated type actually doesn't correspond to the format of the
 /// data structure expected on the Rust side of the boundary, and performing
diff --git a/nearby/presence/np_ffi_core/src/credentials.rs b/nearby/presence/np_ffi_core/src/credentials.rs
index dad61ee..1ad303f 100644
--- a/nearby/presence/np_ffi_core/src/credentials.rs
+++ b/nearby/presence/np_ffi_core/src/credentials.rs
@@ -15,33 +15,30 @@
 
 use crate::common::*;
 use crate::utils::{FfiEnum, LocksLongerThan};
-use crypto_provider::ed25519::InvalidPublicKeyBytes;
+use crypto_provider::{ed25519, CryptoProvider};
 use crypto_provider_default::CryptoProviderImpl;
-use handle_map::{
-    declare_handle_map, HandleLike, HandleMapDimensions, HandleMapFullError,
-    HandleMapTryAllocateError,
-};
+use handle_map::{declare_handle_map, HandleLike, HandleMapFullError, HandleMapTryAllocateError};
+use np_adv::extended;
 use std::sync::Arc;
 
+type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
+
 /// Cryptographic information about a particular V0 discovery credential
 /// necessary to match and decrypt encrypted V0 advertisements.
 #[repr(C)]
 pub struct V0DiscoveryCredential {
     key_seed: [u8; 32],
-    legacy_metadata_key_hmac: [u8; 32],
+    identity_token_hmac: [u8; 32],
 }
 
 impl V0DiscoveryCredential {
     /// Constructs a new V0 discovery credential with the given 32-byte key-seed
     /// and the given 32-byte HMAC for the (14-byte) legacy metadata key.
-    pub fn new(key_seed: [u8; 32], legacy_metadata_key_hmac: [u8; 32]) -> Self {
-        Self { key_seed, legacy_metadata_key_hmac }
+    pub fn new(key_seed: [u8; 32], identity_token_hmac: [u8; 32]) -> Self {
+        Self { key_seed, identity_token_hmac }
     }
     fn into_internal(self) -> np_adv::credential::v0::V0DiscoveryCredential {
-        np_adv::credential::v0::V0DiscoveryCredential::new(
-            self.key_seed,
-            self.legacy_metadata_key_hmac,
-        )
+        np_adv::credential::v0::V0DiscoveryCredential::new(self.key_seed, self.identity_token_hmac)
     }
 }
 
@@ -50,8 +47,9 @@
 #[repr(C)]
 pub struct V1DiscoveryCredential {
     key_seed: [u8; 32],
-    expected_unsigned_metadata_key_hmac: [u8; 32],
-    expected_signed_metadata_key_hmac: [u8; 32],
+    expected_mic_short_salt_identity_token_hmac: [u8; 32],
+    expected_mic_extended_salt_identity_token_hmac: [u8; 32],
+    expected_signature_identity_token_hmac: [u8; 32],
     pub_key: [u8; 32],
 }
 
@@ -61,26 +59,30 @@
     /// the metadata key, and the given public key for signature verification.
     pub fn new(
         key_seed: [u8; 32],
-        expected_unsigned_metadata_key_hmac: [u8; 32],
-        expected_signed_metadata_key_hmac: [u8; 32],
+        expected_mic_short_salt_identity_token_hmac: [u8; 32],
+        expected_mic_extended_salt_identity_token_hmac: [u8; 32],
+        expected_signature_identity_token_hmac: [u8; 32],
         pub_key: [u8; 32],
     ) -> Self {
         Self {
             key_seed,
-            expected_unsigned_metadata_key_hmac,
-            expected_signed_metadata_key_hmac,
+            expected_mic_short_salt_identity_token_hmac,
+            expected_mic_extended_salt_identity_token_hmac,
+            expected_signature_identity_token_hmac,
             pub_key,
         }
     }
     fn into_internal(
         self,
-    ) -> Result<np_adv::credential::v1::V1DiscoveryCredential, InvalidPublicKeyBytes> {
-        np_adv::credential::v1::V1DiscoveryCredential::new::<CryptoProviderImpl>(
+    ) -> Result<np_adv::credential::v1::V1DiscoveryCredential, ed25519::InvalidPublicKeyBytes> {
+        let public_key = ed25519::PublicKey::from_bytes::<Ed25519ProviderImpl>(self.pub_key)?;
+        Ok(np_adv::credential::v1::V1DiscoveryCredential::new(
             self.key_seed,
-            self.expected_unsigned_metadata_key_hmac,
-            self.expected_signed_metadata_key_hmac,
-            self.pub_key,
-        )
+            self.expected_mic_short_salt_identity_token_hmac,
+            self.expected_mic_extended_salt_identity_token_hmac,
+            self.expected_signature_identity_token_hmac,
+            public_key,
+        ))
     }
 }
 
@@ -107,8 +109,11 @@
     /// (some arbitrary `u32` identifier) and encrypted metadata bytes,
     /// copied from the given slice.
     pub fn new(cred_id: u32, encrypted_metadata_bytes: &[u8]) -> Self {
-        let encrypted_metadata_bytes = encrypted_metadata_bytes.to_vec().into_boxed_slice();
-        let encrypted_metadata_bytes = Arc::from(encrypted_metadata_bytes);
+        Self::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 {
         Self { cred_id, encrypted_metadata_bytes }
     }
     /// Gets the pre-specified numerical identifier for this matched-credential.
@@ -125,7 +130,7 @@
 
 impl Eq for MatchedCredential {}
 
-impl np_adv::credential::MatchedCredential for MatchedCredential {
+impl np_adv::credential::matched::MatchedCredential for MatchedCredential {
     type EncryptedMetadata = Arc<[u8]>;
     type EncryptedMetadataFetchError = core::convert::Infallible;
     fn fetch_encrypted_metadata(&self) -> Result<Arc<[u8]>, core::convert::Infallible> {
@@ -155,9 +160,10 @@
         discovery_credential: V0DiscoveryCredential,
         match_data: MatchedCredential,
     ) {
-        let discovery_credential = discovery_credential.into_internal();
-        let matchable_credential =
-            np_adv::credential::MatchableCredential { discovery_credential, match_data };
+        let matchable_credential = np_adv::credential::MatchableCredential {
+            discovery_credential: discovery_credential.into_internal(),
+            match_data,
+        };
         self.v0_creds.push(matchable_credential);
     }
     /// Adds the given V1 discovery credential with the given
@@ -167,10 +173,10 @@
         &mut self,
         discovery_credential: V1DiscoveryCredential,
         match_data: MatchedCredential,
-    ) -> Result<(), InvalidPublicKeyBytes> {
-        discovery_credential.into_internal().map(|discovery_credential| {
+    ) -> Result<(), ed25519::InvalidPublicKeyBytes> {
+        discovery_credential.into_internal().map(|dc| {
             let matchable_credential =
-                np_adv::credential::MatchableCredential { discovery_credential, match_data };
+                np_adv::credential::MatchableCredential { discovery_credential: dc, match_data };
             self.v1_creds.push(matchable_credential);
         })
     }
@@ -232,23 +238,16 @@
     handle_id: u64,
 }
 
-fn get_credential_slab_handle_map_dimensions() -> HandleMapDimensions {
-    HandleMapDimensions {
-        num_shards: global_num_shards(),
-        max_active_handles: global_max_num_credential_slabs(),
-    }
-}
-
 declare_handle_map!(
     credential_slab,
-    super::get_credential_slab_handle_map_dimensions(),
+    crate::common::default_handle_map_dimensions(),
     super::CredentialSlab,
     super::CredentialSlabInternals
 );
 
 impl CredentialSlab {
-    /// Adds the given V0 discovery credential with some associated
-    /// match-data to this credential slab.
+    /// Adds the given V0 discovery credential with some associated match-data to this credential
+    /// slab. This uses the handle but does not transfer ownership of it.
     pub fn add_v0(
         &self,
         discovery_credential: V0DiscoveryCredential,
@@ -262,8 +261,8 @@
             Err(_) => AddV0CredentialToSlabResult::InvalidHandle,
         }
     }
-    /// Adds the given V1 discovery credential with some associated
-    /// match-data to this credential slab.
+    /// Adds the given V1 discovery credential with some associated match-data to this credential
+    /// slab. This uses the handle but does not transfer ownership of it.
     pub fn add_v1(
         &self,
         discovery_credential: V1DiscoveryCredential,
@@ -279,7 +278,8 @@
     }
 }
 
-/// Allocates a new credential-slab, returning a handle to the created object
+/// Allocates a new credential-slab, returning a handle to the created object. The caller is given
+/// ownership of the created handle.
 pub fn create_credential_slab() -> CreateCredentialSlabResult {
     CredentialSlab::allocate(CredentialSlabInternals::new).into()
 }
@@ -313,13 +313,6 @@
     }
 }
 
-fn get_credential_book_handle_map_dimensions() -> HandleMapDimensions {
-    HandleMapDimensions {
-        num_shards: global_num_shards(),
-        max_active_handles: global_max_num_credential_books(),
-    }
-}
-
 /// A `#[repr(C)]` handle to a value of type `CredentialBookInternals`
 #[repr(C)]
 #[derive(Clone, Copy, PartialEq, Eq)]
@@ -329,7 +322,7 @@
 
 declare_handle_map!(
     credential_book,
-    super::get_credential_book_handle_map_dimensions(),
+    crate::common::default_handle_map_dimensions(),
     super::CredentialBook,
     super::CredentialBookInternals
 );
@@ -359,7 +352,10 @@
 
 impl LocksLongerThan<CredentialSlab> for CredentialBook {}
 
-/// Allocates a new credential-book, returning a handle to the created object
+/// Allocates a new credential-book, returning a handle to the created object. This takes ownership
+/// of the `credential_slab` handle except in the case where `NoSpaceLeft` is the error returned.
+/// In that case the caller will retain ownership of the slab handle. The caller is given ownership
+/// of the returned credential book handle if present.
 pub fn create_credential_book_from_slab(
     credential_slab: CredentialSlab,
 ) -> CreateCredentialBookResult {
@@ -399,12 +395,14 @@
     declare_enum_cast! {into_success, Success, CredentialBook}
 }
 
-/// Deallocates a credential-book by its handle
+/// Deallocates a credential-book by its handle. This takes ownership of the credential book
+/// handle.
 pub fn deallocate_credential_book(credential_book: CredentialBook) -> DeallocateResult {
     credential_book.deallocate().map(|_| ()).into()
 }
 
-/// Deallocates a credential-slab by its handle
+/// Deallocates a credential-slab by its handle. This takes ownership of the credential slab
+/// handle.
 pub fn deallocate_credential_slab(credential_slab: CredentialSlab) -> DeallocateResult {
     credential_slab.deallocate().map(|_| ()).into()
 }
@@ -414,7 +412,7 @@
 #[repr(C)]
 pub struct V0BroadcastCredential {
     key_seed: [u8; 32],
-    metadata_key: [u8; 14],
+    identity_token: [u8; 14],
 }
 
 impl V0BroadcastCredential {
@@ -425,15 +423,13 @@
     /// of the raw bytes of sensitive cryptographic info over FFI,
     /// foreign-lang code around how this information is maintained
     /// deserves close scrutiny.
-    pub fn new(key_seed: [u8; 32], metadata_key: [u8; 14]) -> Self {
-        Self { key_seed, metadata_key }
+    pub fn new(key_seed: [u8; 32], identity_token: ldt_np_adv::V0IdentityToken) -> Self {
+        Self { key_seed, identity_token: identity_token.bytes() }
     }
-    pub(crate) fn into_internal(
-        self,
-    ) -> np_adv::credential::SimpleBroadcastCryptoMaterial<np_adv::credential::v0::V0> {
-        np_adv::credential::SimpleBroadcastCryptoMaterial::new(
+    pub(crate) fn into_internal(self) -> np_adv::credential::v0::V0BroadcastCredential {
+        np_adv::credential::v0::V0BroadcastCredential::new(
             self.key_seed,
-            np_adv::legacy::ShortMetadataKey(self.metadata_key),
+            self.identity_token.into(),
         )
     }
 }
@@ -443,7 +439,7 @@
 #[repr(C)]
 pub struct V1BroadcastCredential {
     key_seed: [u8; 32],
-    metadata_key: [u8; 16],
+    identity_token: [u8; 16],
     private_key: [u8; 32],
 }
 
@@ -457,16 +453,18 @@
     /// sensitive cryptographic info) over FFI, foreign-lang
     /// code around how this information is maintained
     /// deserves close scrutiny.
-    pub const fn new(key_seed: [u8; 32], metadata_key: [u8; 16], private_key: [u8; 32]) -> Self {
-        Self { key_seed, metadata_key, private_key }
+    pub const fn new(
+        key_seed: [u8; 32],
+        identity_token: extended::V1IdentityToken,
+        private_key: [u8; 32],
+    ) -> Self {
+        Self { key_seed, identity_token: identity_token.into_bytes(), private_key }
     }
-    pub(crate) fn into_internal(
-        self,
-    ) -> np_adv::credential::v1::SimpleSignedBroadcastCryptoMaterial {
+    pub(crate) fn into_internal(self) -> np_adv::credential::v1::V1BroadcastCredential {
         let permit = crypto_provider::ed25519::RawPrivateKeyPermit::default();
-        np_adv::credential::v1::SimpleSignedBroadcastCryptoMaterial::new(
+        np_adv::credential::v1::V1BroadcastCredential::new(
             self.key_seed,
-            np_adv::MetadataKey(self.metadata_key),
+            self.identity_token.into(),
             crypto_provider::ed25519::PrivateKey::from_raw_private_key(self.private_key, &permit),
         )
     }
diff --git a/nearby/presence/np_ffi_core/src/deserialize.rs b/nearby/presence/np_ffi_core/src/deserialize.rs
index 09df8a4..dffa8d1 100644
--- a/nearby/presence/np_ffi_core/src/deserialize.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize.rs
@@ -19,9 +19,7 @@
 use crate::deserialize::v1::*;
 use crate::utils::FfiEnum;
 use crypto_provider_default::CryptoProviderImpl;
-use handle_map::{
-    declare_handle_map, HandleLike, HandleMapDimensions, HandleMapFullError, HandleNotPresentError,
-};
+use handle_map::{declare_handle_map, HandleLike, HandleMapFullError, HandleNotPresentError};
 use np_adv::deserialization_arena;
 
 pub mod v0;
@@ -73,7 +71,8 @@
     declare_enum_cast! {into_v0, V0, DeserializedV0Advertisement}
     declare_enum_cast! {into_v1, V1, DeserializedV1Advertisement}
 
-    /// Deallocates any internal data referenced by a `DeserializeAdvertisementResult`
+    /// Deallocates any internal data referenced by a `DeserializeAdvertisementResult`. This takes
+    /// ownership of any internal handles.
     pub fn deallocate(self) -> DeallocateResult {
         match self {
             DeserializeAdvertisementResult::Error => DeallocateResult::Success,
@@ -133,8 +132,9 @@
     }
 }
 
-/// Attempts to deserialize an advertisement with the given payload.
-/// Suitable for langs which have a suitably expressive slice-type.
+/// Attempts to deserialize an advertisement with the given payload. Suitable for langs which have
+/// a suitably expressive slice-type. This uses the given `credential_book` handle but does not
+/// take ownership of it. The caller is given ownership of any handles in the returned structure.
 pub fn deserialize_advertisement_from_slice(
     adv_payload: &[u8],
     credential_book: CredentialBook,
@@ -147,8 +147,10 @@
     }
 }
 
-/// Attempts to deserialize an advertisement with the given payload.
-/// Suitable for langs which don't have an expressive-enough slice type.
+/// Attempts to deserialize an advertisement with the given payload.  Suitable for langs which
+/// don't have an expressive-enough slice type. This uses the given `credential_book` handle but
+/// does not take ownership of it. The caller is given ownership of any handles in the returned
+/// structure.
 pub fn deserialize_advertisement(
     adv_payload: &RawAdvertisementPayload,
     credential_book: CredentialBook,
@@ -215,15 +217,11 @@
 
 declare_handle_map!(
     decrypted_metadata,
-    super::get_decrypted_metadata_handle_map_dimensions(),
+    crate::common::default_handle_map_dimensions(),
     super::DecryptedMetadata,
     super::DecryptedMetadataInternals
 );
 
-fn get_decrypted_metadata_handle_map_dimensions() -> HandleMapDimensions {
-    HandleMapDimensions { num_shards: global_num_shards(), max_active_handles: DEFAULT_MAX_HANDLES }
-}
-
 /// The pointer and length of the decrypted metadata byte buffer
 #[repr(C)]
 pub struct MetadataBufferParts {
@@ -271,7 +269,8 @@
 }
 
 impl DecryptedMetadata {
-    /// Gets the raw parts, pointer + length representation of the metadata byte buffer
+    /// Gets the raw parts, pointer + length representation of the metadata byte buffer. This uses
+    /// the handle but does not take ownership of it.
     pub fn get_metadata_buffer_parts(&self) -> GetMetadataBufferPartsResult {
         match self.get() {
             Ok(metadata_internals) => {
@@ -285,8 +284,8 @@
         }
     }
 
-    /// Frees the underlying decrypted metadata buffer
-    pub fn deallocate_metadata(&self) -> DeallocateResult {
+    /// Frees the underlying decrypted metadata buffer. This takes ownership of the handle.
+    pub fn deallocate_metadata(self) -> DeallocateResult {
         self.deallocate().map(|_| ()).into()
     }
 }
diff --git a/nearby/presence/np_ffi_core/src/deserialize/v0.rs b/nearby/presence/np_ffi_core/src/deserialize/v0.rs
index c4f117f..79528a0 100644
--- a/nearby/presence/np_ffi_core/src/deserialize/v0.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize/v0.rs
@@ -22,9 +22,9 @@
 use crate::utils::{FfiEnum, LocksLongerThan};
 use crate::v0::V0DataElement;
 use crypto_provider_default::CryptoProviderImpl;
-use handle_map::{declare_handle_map, HandleLike, HandleMapDimensions, HandleMapFullError};
-use np_adv::HasIdentityMatch;
-use std::vec::Vec;
+use handle_map::{declare_handle_map, HandleLike, HandleMapFullError};
+use np_adv::credential::matched::HasIdentityMatch;
+use np_adv::legacy;
 
 /// Discriminant for possible results of V0 advertisement deserialization
 #[derive(Clone, Copy)]
@@ -33,12 +33,12 @@
     /// The deserialized V0 advertisement was legible.
     /// The associated payload may be obtained via
     /// `DeserializedV0Advertisement#into_legible`.
-    Legible = 0,
+    Legible = 1,
     /// The deserialized V0 advertisement is illegible,
     /// likely meaning that the receiver does not hold
     /// the proper credentials to be able to read
     /// the received advertisement.
-    NoMatchingCredentials = 1,
+    NoMatchingCredentials = 2,
 }
 
 /// Represents a deserialized V0 advertisement
@@ -62,8 +62,9 @@
 }
 
 impl DeserializedV0Advertisement {
-    /// Attempts to deallocate memory utilized internally by this V0 advertisement
-    /// (which contains a handle to actual advertisement contents behind-the-scenes).
+    /// Attempts to deallocate memory utilized internally by this V0 advertisement (which contains
+    /// a handle to actual advertisement contents behind-the-scenes). This function takes ownership
+    /// of that internal handle.
     pub fn deallocate(self) -> DeallocateResult {
         match self {
             DeserializedV0Advertisement::Legible(adv) => adv.deallocate(),
@@ -72,25 +73,25 @@
     }
 
     pub(crate) fn allocate_with_contents(
-        contents: np_adv::V0AdvertisementContents<
-            np_adv::credential::ReferencedMatchedCredential<MatchedCredential>,
+        contents: legacy::V0AdvertisementContents<
+            np_adv::credential::matched::ReferencedMatchedCredential<MatchedCredential>,
         >,
     ) -> Result<Self, DeserializeAdvertisementError> {
         match contents {
-            np_adv::V0AdvertisementContents::Plaintext(plaintext_contents) => {
+            legacy::V0AdvertisementContents::Plaintext(plaintext_contents) => {
                 let adv = LegibleDeserializedV0Advertisement::allocate_with_plaintext_contents(
                     plaintext_contents,
                 )?;
                 Ok(Self::Legible(adv))
             }
-            np_adv::V0AdvertisementContents::Decrypted(decrypted_contents) => {
+            legacy::V0AdvertisementContents::Decrypted(decrypted_contents) => {
                 let decrypted_contents = decrypted_contents.clone_match_data();
                 let adv = LegibleDeserializedV0Advertisement::allocate_with_decrypted_contents(
                     decrypted_contents,
                 )?;
                 Ok(Self::Legible(adv))
             }
-            np_adv::V0AdvertisementContents::NoMatchingCredentials => {
+            legacy::V0AdvertisementContents::NoMatchingCredentials => {
                 Ok(Self::NoMatchingCredentials)
             }
         }
@@ -109,7 +110,7 @@
 
 impl LegibleDeserializedV0Advertisement {
     pub(crate) fn allocate_with_plaintext_contents(
-        contents: np_adv::legacy::deserialize::PlaintextAdvContents,
+        contents: legacy::deserialize::UnencryptedAdvContents,
     ) -> Result<Self, DeserializeAdvertisementError> {
         let data_elements = contents
             .data_elements()
@@ -120,9 +121,9 @@
         Ok(Self { num_des, payload, identity_kind: DeserializedV0IdentityKind::Plaintext })
     }
     pub(crate) fn allocate_with_decrypted_contents(
-        contents: np_adv::WithMatchedCredential<
+        contents: np_adv::credential::matched::WithMatchedCredential<
             MatchedCredential,
-            np_adv::legacy::deserialize::DecryptedAdvContents,
+            legacy::deserialize::DecryptedAdvContents,
         >,
     ) -> Result<Self, DeserializeAdvertisementError> {
         let data_elements = contents
@@ -133,28 +134,23 @@
         let num_des = data_elements.len() as u8;
 
         let salt = contents.contents().salt();
-        let identity_type = contents.contents().identity_type();
 
         // Reduce the information contained in the contents to just
         // the metadata key, since we're done copying over the DEs
         // and other data into an FFI-friendly form.
-        let match_data = contents.map(|x| x.metadata_key());
+        let match_data = contents.map(|x| x.identity_token());
 
-        let payload = V0Payload::allocate_with_decrypted_contents(
-            identity_type,
-            salt,
-            match_data,
-            data_elements,
-        )?;
+        let payload = V0Payload::allocate_with_decrypted_contents(salt, match_data, data_elements)?;
 
         Ok(Self { num_des, payload, identity_kind: DeserializedV0IdentityKind::Decrypted })
     }
-    /// Gets the number of data-elements in this adv's payload
-    /// Suitable as an iteration bound for `Self.into_payload().get_de(...)`.
+    /// Gets the number of data-elements in this adv's payload. This number is suitable as an
+    /// iteration bound for `Self.into_payload().get_de(...)`.
     pub fn num_des(&self) -> u8 {
         self.num_des
     }
-    /// Destructures this legible advertisement into just the payload
+    /// Get a copy of the payload handle. This does not copy the underlying payload. The copied
+    /// handle shares a lifetime with the payload handle in this struct; it's the same handle.
     pub fn payload(&self) -> V0Payload {
         self.payload
     }
@@ -163,7 +159,8 @@
     pub fn identity_kind(&self) -> DeserializedV0IdentityKind {
         self.identity_kind
     }
-    /// Deallocates the underlying handle of the payload
+    /// Deallocates the underlying handle of the payload. This function takes ownership of the
+    /// payload handle.
     pub fn deallocate(self) -> DeallocateResult {
         self.payload.deallocate().map(|_| ()).into()
     }
@@ -175,9 +172,9 @@
 #[repr(u8)]
 pub enum DeserializedV0IdentityKind {
     /// The deserialized identity was a plaintext identity.
-    Plaintext = 0,
+    Plaintext = 1,
     /// The deserialized identity was some decrypted identity.
-    Decrypted = 1,
+    Decrypted = 2,
 }
 
 /// Information about the identity which matched a
@@ -185,13 +182,11 @@
 #[derive(Clone, Copy)]
 #[repr(C)]
 pub struct DeserializedV0IdentityDetails {
-    /// The identity type (private/provisioned/trusted)
-    identity_type: EncryptedIdentityType,
     /// The ID of the credential which
     /// matched the deserialized adv
     cred_id: u32,
-    /// The 14-byte legacy metadata key
-    metadata_key: [u8; 14],
+    /// The 14-byte legacy identity token
+    identity_token: [u8; 14],
     /// The 2-byte advertisement salt
     salt: [u8; 2],
 }
@@ -199,27 +194,19 @@
 impl DeserializedV0IdentityDetails {
     pub(crate) fn new(
         cred_id: u32,
-        identity_type: np_adv::de_type::EncryptedIdentityDataElementType,
-        salt: ldt_np_adv::LegacySalt,
-        metadata_key: np_adv::legacy::ShortMetadataKey,
+        salt: ldt_np_adv::V0Salt,
+        identity_token: ldt_np_adv::V0IdentityToken,
     ) -> Self {
-        let metadata_key = metadata_key.0;
-        let salt = *salt.bytes();
-        let identity_type = identity_type.into();
-        Self { identity_type, cred_id, salt, metadata_key }
+        let salt = salt.bytes();
+        Self { cred_id, salt, identity_token: identity_token.bytes() }
     }
-    /// Returns the ID of the credential which
-    /// matched the deserialized adv
+    /// Returns the ID of the credential which matched the deserialized adv
     pub fn cred_id(&self) -> u32 {
         self.cred_id
     }
-    /// Returns the identity type (private/provisioned/trusted)
-    pub fn identity_type(&self) -> EncryptedIdentityType {
-        self.identity_type
-    }
     /// Returns the 14-byte legacy metadata key
-    pub fn metadata_key(&self) -> [u8; 14] {
-        self.metadata_key
+    pub fn identity_token(&self) -> [u8; 14] {
+        self.identity_token
     }
     /// Returns the 2-byte advertisement salt
     pub fn salt(&self) -> [u8; 2] {
@@ -275,22 +262,23 @@
     /// The metadata key, together with the matched
     /// credential and enough information to decrypt
     /// the credential metadata, if desired.
-    match_data: np_adv::WithMatchedCredential<MatchedCredential, np_adv::legacy::ShortMetadataKey>,
+    match_data: np_adv::credential::matched::WithMatchedCredential<
+        MatchedCredential,
+        ldt_np_adv::V0IdentityToken,
+    >,
 }
 
 impl DeserializedV0IdentityInternals {
     pub(crate) fn new(
-        identity_type: np_adv::de_type::EncryptedIdentityDataElementType,
-        salt: ldt_np_adv::LegacySalt,
-        match_data: np_adv::WithMatchedCredential<
+        salt: ldt_np_adv::V0Salt,
+        match_data: np_adv::credential::matched::WithMatchedCredential<
             MatchedCredential,
-            np_adv::legacy::ShortMetadataKey,
+            ldt_np_adv::V0IdentityToken,
         >,
     ) -> Self {
         let cred_id = match_data.matched_credential().id();
-        let metadata_key = match_data.contents();
-        let details =
-            DeserializedV0IdentityDetails::new(cred_id, identity_type, salt, *metadata_key);
+        let identity_token = match_data.contents();
+        let details = DeserializedV0IdentityDetails::new(cred_id, salt, *identity_token);
         Self { details, match_data }
     }
     /// Gets the directly-transmissible details about
@@ -342,13 +330,6 @@
     }
 }
 
-fn get_v0_payload_handle_map_dimensions() -> HandleMapDimensions {
-    HandleMapDimensions {
-        num_shards: global_num_shards(),
-        max_active_handles: global_max_num_deserialized_v0_advertisements(),
-    }
-}
-
 /// A `#[repr(C)]` handle to a value of type `V0PayloadInternals`
 #[repr(C)]
 #[derive(Clone, Copy, PartialEq, Eq)]
@@ -358,7 +339,7 @@
 
 declare_handle_map!(
     v0_payload,
-    super::get_v0_payload_handle_map_dimensions(),
+    crate::common::default_handle_map_dimensions(),
     super::V0Payload,
     super::V0PayloadInternals
 );
@@ -369,9 +350,7 @@
 
 impl V0Payload {
     pub(crate) fn allocate_with_plaintext_data_elements(
-        data_elements: Vec<
-            np_adv::legacy::deserialize::PlainDataElement<np_adv::legacy::Plaintext>,
-        >,
+        data_elements: Vec<legacy::deserialize::DeserializedDataElement<legacy::Plaintext>>,
     ) -> Result<Self, HandleMapFullError> {
         Self::allocate(move || {
             let des = data_elements.into_iter().map(V0DataElement::from).collect();
@@ -380,24 +359,21 @@
         })
     }
     pub(crate) fn allocate_with_decrypted_contents(
-        identity_type: np_adv::de_type::EncryptedIdentityDataElementType,
-        salt: ldt_np_adv::LegacySalt,
-        match_data: np_adv::WithMatchedCredential<
+        salt: ldt_np_adv::V0Salt,
+        match_data: np_adv::credential::matched::WithMatchedCredential<
             MatchedCredential,
-            np_adv::legacy::ShortMetadataKey,
+            ldt_np_adv::V0IdentityToken,
         >,
-        data_elements: Vec<
-            np_adv::legacy::deserialize::PlainDataElement<np_adv::legacy::Ciphertext>,
-        >,
+        data_elements: Vec<legacy::deserialize::DeserializedDataElement<legacy::Ciphertext>>,
     ) -> Result<Self, HandleMapFullError> {
         Self::allocate(move || {
             let des = data_elements.into_iter().map(V0DataElement::from).collect();
-            let identity =
-                Some(DeserializedV0IdentityInternals::new(identity_type, salt, match_data));
+            let identity = Some(DeserializedV0IdentityInternals::new(salt, match_data));
             V0PayloadInternals { des, identity }
         })
     }
-    /// Gets the data-element with the given index in this v0 adv payload
+    /// Gets the data-element with the given index in this v0 adv payload. This uses the handle but
+    /// does not take ownership of it.
     pub fn get_de(&self, index: u8) -> GetV0DEResult {
         match self.get() {
             Ok(read_guard) => read_guard.get_de(index),
@@ -405,9 +381,9 @@
         }
     }
 
-    /// Gets the identity details for this V0 payload,
-    /// if this payload was associated with an identity
-    /// (i.e: non-public advertisements).
+    /// Gets the identity details for this V0 payload, if this payload was associated with an
+    /// identity (i.e: encrypted advertisements). This uses the handle but does not take ownership
+    /// of it.
     pub fn get_identity_details(&self) -> GetV0IdentityDetailsResult {
         match self.get() {
             Ok(read_guard) => read_guard.get_identity_details(),
@@ -415,8 +391,9 @@
         }
     }
 
-    /// Attempts to decrypt the metadata for the matched
-    /// credential for this V0 payload (if any)
+    /// Attempts to decrypt the metadata for the matched credential for this V0 payload (if any).
+    /// This uses the handle but does not take ownership of it. The caller is given ownership of
+    /// the returned `DecryptedMetadata` handle if present.
     pub fn decrypt_metadata(&self) -> DecryptMetadataResult {
         match self.get() {
             Ok(read_guard) => match read_guard.decrypt_metadata() {
@@ -427,7 +404,7 @@
         }
     }
 
-    /// Deallocates any underlying data held by a V0Payload
+    /// Deallocates any underlying data held by a `V0Payload`. This takes ownership of the handle.
     pub fn deallocate_payload(&self) -> DeallocateResult {
         self.deallocate().map(|_| ()).into()
     }
diff --git a/nearby/presence/np_ffi_core/src/deserialize/v1.rs b/nearby/presence/np_ffi_core/src/deserialize/v1.rs
index 3bc056d..af54fec 100644
--- a/nearby/presence/np_ffi_core/src/deserialize/v1.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize/v1.rs
@@ -14,25 +14,36 @@
 //! Core NP Rust FFI structures and methods for v1 advertisement deserialization.
 
 use super::DeserializeAdvertisementError;
-use crate::common::*;
-use crate::credentials::CredentialBook;
-use crate::credentials::MatchedCredential;
-use crate::deserialize::{allocate_decrypted_metadata_handle, DecryptMetadataResult};
-use crate::utils::*;
-use crate::v1::V1VerificationMode;
+use crate::{
+    common::*,
+    credentials::{CredentialBook, MatchedCredential},
+    deserialize::{allocate_decrypted_metadata_handle, DecryptMetadataResult},
+    utils::*,
+    v1::V1VerificationMode,
+};
 use array_view::ArrayView;
+use crypto_provider::CryptoProvider;
 use crypto_provider_default::CryptoProviderImpl;
-use handle_map::{declare_handle_map, HandleLike, HandleMapDimensions};
-use np_adv::extended::deserialize::DataElementParseError;
-use np_adv::HasIdentityMatch;
-use std::vec::Vec;
+use handle_map::{declare_handle_map, HandleLike};
+use np_adv::{
+    credential::matched::WithMatchedCredential,
+    extended::{
+        deserialize::{
+            data_element::DataElementParseError, V1AdvertisementContents, V1DeserializedSection,
+        },
+        salt::MultiSalt,
+    },
+};
 
 /// Representation of a deserialized V1 advertisement
 #[repr(C)]
 pub struct DeserializedV1Advertisement {
-    num_legible_sections: u8,
-    num_undecryptable_sections: u8,
-    legible_sections: LegibleV1Sections,
+    /// The number of legible sections
+    pub num_legible_sections: u8,
+    /// The number of sections that were unable to be decrypted
+    pub num_undecryptable_sections: u8,
+    /// A handle to the set of legible (plain or decrypted) sections
+    pub legible_sections: LegibleV1Sections,
 }
 
 impl DeserializedV1Advertisement {
@@ -46,8 +57,9 @@
         self.num_undecryptable_sections
     }
 
-    /// Gets the legible section with the given index
-    /// (which is bounded in `0..self.num_legible_sections()`)
+    /// Gets the legible section with the given index (which is bounded in
+    /// `0..self.num_legible_sections()`). This uses the internal handle but does not take
+    /// ownership of it.
     pub fn get_section(&self, legible_section_index: u8) -> GetV1SectionResult {
         match self.legible_sections.get() {
             Ok(sections_read_guard) => {
@@ -57,15 +69,16 @@
         }
     }
 
-    /// Attempts to deallocate memory utilized internally by this V1 advertisement
-    /// (which contains a handle to actual advertisement contents behind-the-scenes).
+    /// Attempts to deallocate memory utilized internally by this V1 advertisement (which contains
+    /// a handle to actual advertisement contents behind-the-scenes). This function takes ownership
+    /// of the internal handle.
     pub fn deallocate(self) -> DeallocateResult {
         self.legible_sections.deallocate().map(|_| ()).into()
     }
 
     pub(crate) fn allocate_with_contents(
-        contents: np_adv::V1AdvertisementContents<
-            np_adv::credential::ReferencedMatchedCredential<MatchedCredential>,
+        contents: V1AdvertisementContents<
+            np_adv::credential::matched::ReferencedMatchedCredential<MatchedCredential>,
         >,
     ) -> Result<Self, DeserializeAdvertisementError> {
         // 16-section limit enforced by np_adv
@@ -118,9 +131,9 @@
 impl<'adv>
     TryFrom<
         Vec<
-            np_adv::V1DeserializedSection<
+            V1DeserializedSection<
                 'adv,
-                np_adv::credential::ReferencedMatchedCredential<'adv, MatchedCredential>,
+                np_adv::credential::matched::ReferencedMatchedCredential<'adv, MatchedCredential>,
             >,
         >,
     > for LegibleV1SectionsInternals
@@ -129,9 +142,9 @@
 
     fn try_from(
         contents: Vec<
-            np_adv::V1DeserializedSection<
+            V1DeserializedSection<
                 'adv,
-                np_adv::credential::ReferencedMatchedCredential<'adv, MatchedCredential>,
+                np_adv::credential::matched::ReferencedMatchedCredential<'adv, MatchedCredential>,
             >,
         >,
     ) -> Result<Self, Self::Error> {
@@ -143,23 +156,16 @@
     }
 }
 
-fn get_legible_v1_sections_handle_map_dimensions() -> HandleMapDimensions {
-    HandleMapDimensions {
-        num_shards: global_num_shards(),
-        max_active_handles: global_max_num_deserialized_v1_advertisements(),
-    }
-}
-
 /// A `#[repr(C)]` handle to a value of type `LegibleV1SectionsInternals`
 #[repr(C)]
 #[derive(Clone, Copy, PartialEq, Eq)]
-struct LegibleV1Sections {
+pub struct LegibleV1Sections {
     handle_id: u64,
 }
 
 declare_handle_map!(
     legible_v1_sections,
-    super::get_legible_v1_sections_handle_map_dimensions(),
+    crate::common::default_handle_map_dimensions(),
     super::LegibleV1Sections,
     super::LegibleV1SectionsInternals
 );
@@ -169,8 +175,8 @@
 impl LegibleV1Sections {
     pub(crate) fn allocate_with_contents(
         contents: Vec<
-            np_adv::V1DeserializedSection<
-                np_adv::credential::ReferencedMatchedCredential<MatchedCredential>,
+            V1DeserializedSection<
+                np_adv::credential::matched::ReferencedMatchedCredential<MatchedCredential>,
             >,
         >,
     ) -> Result<Self, DeserializeAdvertisementError> {
@@ -178,6 +184,31 @@
             .map_err(|_| DeserializeAdvertisementError)?;
         Self::allocate(move || section).map_err(|e| e.into())
     }
+
+    /// Gets the legible section with the given index (which is bounded in
+    /// `0..self.num_legible_sections()`). This function uses this handle but does not take
+    /// ownership of it.
+    pub fn get_section(&self, legible_section_index: u8) -> GetV1SectionResult {
+        match self.get() {
+            Ok(sections_read_guard) => {
+                sections_read_guard.get_section(*self, legible_section_index)
+            }
+            Err(_) => GetV1SectionResult::Error,
+        }
+    }
+
+    /// Get a data element by section index and de index. Similar to `get_section().get_de()` but
+    /// will only lock the HandleMap once. This function uses this handle but does not take
+    /// ownership of it.
+    pub fn get_section_de(&self, legible_section_index: u8, de_index: u8) -> GetV1DEResult {
+        let Ok(sections) = self.get() else {
+            return GetV1DEResult::Error;
+        };
+        let Some(section) = sections.get_section_internals(legible_section_index) else {
+            return GetV1DEResult::Error;
+        };
+        section.get_de(de_index)
+    }
 }
 
 /// Discriminant for `GetV1SectionResult`
@@ -313,31 +344,33 @@
     /// passed offset is 255 (causes overflow) or if the section
     /// is leveraging a public identity, and hence, doesn't have
     /// an associated salt.
-    pub(crate) fn derive_16_byte_salt_for_offset(&self, de_offset: u8) -> GetV1DE16ByteSaltResult {
+    pub(crate) fn derive_16_byte_salt_for_offset<C: CryptoProvider>(
+        &self,
+        de_offset: u8,
+    ) -> GetV1DE16ByteSaltResult {
         self.identity
             .as_ref()
-            .and_then(|x| x.derive_16_byte_salt_for_offset(de_offset))
+            .and_then(|x| x.derive_16_byte_salt_for_offset::<C>(de_offset))
             .map_or(GetV1DE16ByteSaltResult::Error, GetV1DE16ByteSaltResult::Success)
     }
 }
 
 impl<'adv>
     TryFrom<
-        np_adv::V1DeserializedSection<
+        V1DeserializedSection<
             'adv,
-            np_adv::credential::ReferencedMatchedCredential<'adv, MatchedCredential>,
+            np_adv::credential::matched::ReferencedMatchedCredential<'adv, MatchedCredential>,
         >,
     > for DeserializedV1SectionInternals
 {
     type Error = DataElementParseError;
 
     fn try_from(
-        section: np_adv::V1DeserializedSection<
-            np_adv::credential::ReferencedMatchedCredential<'adv, MatchedCredential>,
+        section: V1DeserializedSection<
+            np_adv::credential::matched::ReferencedMatchedCredential<'adv, MatchedCredential>,
         >,
     ) -> Result<Self, Self::Error> {
         use np_adv::extended::deserialize::Section;
-        use np_adv::V1DeserializedSection;
         match section {
             V1DeserializedSection::Plaintext(section) => {
                 let des = section
@@ -354,19 +387,14 @@
                     .map(|r| r.map(|de| V1DataElement::from(&de)))
                     .collect::<Result<Vec<_>, _>>()?;
 
-                let identity_type = section.identity_type();
                 let verification_mode = section.verification_mode();
-                let salt = section.salt();
+                let salt = *section.salt();
 
                 let match_data = with_matched.clone_match_data();
-                let match_data = match_data.map(|x| x.metadata_key());
+                let match_data = match_data.map(|x| *x.identity_token());
 
-                let identity = Some(DeserializedV1IdentityInternals::new(
-                    identity_type,
-                    verification_mode,
-                    salt,
-                    match_data,
-                ));
+                let identity =
+                    Some(DeserializedV1IdentityInternals::new(verification_mode, salt, match_data));
                 Ok(Self { des, identity })
             }
         }
@@ -392,26 +420,21 @@
     /// The metadata key, together with the matched
     /// credential and enough information to decrypt
     /// the credential metadata, if desired.
-    match_data: np_adv::WithMatchedCredential<MatchedCredential, np_adv::MetadataKey>,
+    match_data: WithMatchedCredential<MatchedCredential, np_adv::extended::V1IdentityToken>,
     /// The 16-byte section salt
-    salt: np_adv::extended::deserialize::RawV1Salt,
+    salt: MultiSalt,
 }
 
 impl DeserializedV1IdentityInternals {
     pub(crate) fn new(
-        identity_type: np_adv::de_type::EncryptedIdentityDataElementType,
         verification_mode: np_adv::extended::deserialize::VerificationMode,
-        salt: np_adv::extended::deserialize::RawV1Salt,
-        match_data: np_adv::WithMatchedCredential<MatchedCredential, np_adv::MetadataKey>,
+        salt: MultiSalt,
+        match_data: WithMatchedCredential<MatchedCredential, np_adv::extended::V1IdentityToken>,
     ) -> Self {
         let cred_id = match_data.matched_credential().id();
-        let metadata_key = match_data.contents();
-        let details = DeserializedV1IdentityDetails::new(
-            cred_id,
-            identity_type,
-            verification_mode,
-            *metadata_key,
-        );
+        let identity_token = match_data.contents();
+        let details =
+            DeserializedV1IdentityDetails::new(cred_id, verification_mode, *identity_token);
         Self { details, match_data, salt }
     }
     /// Gets the directly-transmissible details about
@@ -427,13 +450,18 @@
     }
     /// For a given data-element offset, derives a 16-byte DE salt
     /// for a DE in that position within this section.
-    pub(crate) fn derive_16_byte_salt_for_offset(
+    pub(crate) fn derive_16_byte_salt_for_offset<C: CryptoProvider>(
         &self,
         de_offset: u8,
     ) -> Option<FixedSizeArray<16>> {
-        let section_salt = np_hkdf::v1_salt::V1Salt::<CryptoProviderImpl>::from(self.salt);
         let de_offset = np_hkdf::v1_salt::DataElementOffset::from(de_offset);
-        section_salt.derive::<16>(Some(de_offset)).map(FixedSizeArray::from_array)
+
+        match self.salt {
+            MultiSalt::Short(_) => None,
+            MultiSalt::Extended(s) => {
+                s.derive::<16, C>(Some(de_offset)).map(FixedSizeArray::from_array)
+            }
+        }
     }
 }
 
@@ -482,8 +510,6 @@
 #[derive(Clone, Copy)]
 #[repr(C)]
 pub struct DeserializedV1IdentityDetails {
-    /// The identity type (private/provisioned/trusted)
-    identity_type: EncryptedIdentityType,
     /// The verification mode (MIC/Signature) which
     /// was used to verify the decrypted adv contents.
     verification_mode: V1VerificationMode,
@@ -491,38 +517,29 @@
     /// matched the deserialized section.
     cred_id: u32,
     /// The 16-byte metadata key.
-    metadata_key: [u8; 16],
+    identity_token: [u8; 16],
 }
 
 impl DeserializedV1IdentityDetails {
     pub(crate) fn new(
         cred_id: u32,
-        identity_type: np_adv::de_type::EncryptedIdentityDataElementType,
         verification_mode: np_adv::extended::deserialize::VerificationMode,
-        metadata_key: np_adv::MetadataKey,
+        identity_token: np_adv::extended::V1IdentityToken,
     ) -> Self {
-        let metadata_key = metadata_key.0;
-        let identity_type = identity_type.into();
         let verification_mode = verification_mode.into();
-        Self { cred_id, identity_type, verification_mode, metadata_key }
+        Self { cred_id, verification_mode, identity_token: identity_token.into_bytes() }
     }
-    /// Returns the ID of the credential which
-    /// matched the deserialized section.
+    /// Returns the ID of the credential which matched the deserialized section.
     pub fn cred_id(&self) -> u32 {
         self.cred_id
     }
-    /// Returns the identity type (private/provisioned/trusted)
-    pub fn identity_type(&self) -> EncryptedIdentityType {
-        self.identity_type
-    }
-    /// Returns the verification mode (MIC/Signature)
-    /// employed for the decrypted section.
+    /// Returns the verification mode (MIC/Signature) employed for the decrypted section.
     pub fn verification_mode(&self) -> V1VerificationMode {
         self.verification_mode
     }
-    /// Returns the 16-byte section metadata key.
-    pub fn metadata_key(&self) -> [u8; 16] {
-        self.metadata_key
+    /// Returns the 16-byte section identity token.
+    pub fn identity_token(&self) -> [u8; 16] {
+        self.identity_token
     }
 }
 
@@ -554,33 +571,31 @@
             GetV1DEResult::Error,
         )
     }
-    /// Attempts to get the details of the identity employed
-    /// for the section referenced by this handle. May fail
-    /// if the handle is invalid, or if the advertisement
-    /// section leverages a public identity.
+    /// Attempts to get the details of the identity employed for the section referenced by this
+    /// handle. May fail if the handle is invalid, or if the advertisement section leverages a
+    /// public identity. This function does not take ownership of the handle.
     pub fn get_identity_details(&self) -> GetV1IdentityDetailsResult {
         self.apply_to_section_internals(
             DeserializedV1SectionInternals::get_identity_details,
             GetV1IdentityDetailsResult::Error,
         )
     }
-    /// Attempts to decrypt the metadata for the matched
-    /// credential for the V1 section referenced by
-    /// this handle (if any).
+    /// Attempts to decrypt the metadata for the matched credential for the V1 section referenced
+    /// by this handle (if any). This uses but does not take ownership of the handle.
     pub fn decrypt_metadata(&self) -> DecryptMetadataResult {
         self.apply_to_section_internals(
             DeserializedV1SectionInternals::decrypt_metadata,
             DecryptMetadataResult::Error,
         )
     }
-    /// Attempts to derive a 16-byte DE salt for a DE in this section
-    /// with the given DE offset. This operation may fail if the
-    /// passed offset is 255 (causes overflow) or if the section
-    /// is leveraging a public identity, and hence, doesn't have
-    /// an associated salt.
+    /// Attempts to derive a 16-byte DE salt for a DE in this section with the given DE offset.
+    /// This operation may fail if the passed offset is 255 (causes overflow) or if the section is
+    /// leveraging a public identity, and hence, doesn't have an associated salt.
     pub fn derive_16_byte_salt_for_offset(&self, de_offset: u8) -> GetV1DE16ByteSaltResult {
         self.apply_to_section_internals(
-            move |section_ref| section_ref.derive_16_byte_salt_for_offset(de_offset),
+            move |section_ref| {
+                section_ref.derive_16_byte_salt_for_offset::<CryptoProviderImpl>(de_offset)
+            },
             GetV1DE16ByteSaltResult::Error,
         )
     }
@@ -661,8 +676,8 @@
     }
 }
 
-impl<'a> From<&'a np_adv::extended::deserialize::DataElement<'a>> for V1DataElement {
-    fn from(de: &'a np_adv::extended::deserialize::DataElement<'a>) -> Self {
+impl<'a> From<&'a np_adv::extended::deserialize::data_element::DataElement<'a>> for V1DataElement {
+    fn from(de: &'a np_adv::extended::deserialize::data_element::DataElement<'a>) -> Self {
         let offset = de.offset().as_u8();
         let de_type = V1DEType::from(de.de_type());
         let contents_as_slice = de.contents();
@@ -698,8 +713,7 @@
     pub fn de_type(&self) -> V1DEType {
         self.de_type
     }
-    /// Destructures this `GenericV1DataElement` into just the
-    /// DE payload byte-buffer.
+    /// Destructures this `GenericV1DataElement` into just the DE payload byte-buffer.
     pub fn into_payload(self) -> ByteBuffer<127> {
         self.payload
     }
diff --git a/nearby/presence/np_ffi_core/src/serialize/v0.rs b/nearby/presence/np_ffi_core/src/serialize/v0.rs
index 2d6b413..736925b 100644
--- a/nearby/presence/np_ffi_core/src/serialize/v0.rs
+++ b/nearby/presence/np_ffi_core/src/serialize/v0.rs
@@ -19,9 +19,8 @@
 use crate::utils::FfiEnum;
 use crate::v0::V0DataElement;
 use crypto_provider_default::CryptoProviderImpl;
-use handle_map::{declare_handle_map, HandleLike, HandleMapDimensions, HandleMapFullError};
-use np_adv;
-use np_adv_dynamic;
+use handle_map::{declare_handle_map, HandleLike, HandleMapFullError};
+use np_adv_dynamic::legacy::BoxedAdvConstructionError;
 
 /// A handle to a builder for V0 advertisements.
 #[derive(Clone, Copy)]
@@ -32,26 +31,29 @@
 }
 
 impl V0AdvertisementBuilder {
-    /// Attempts to add the given data element to the V0
-    /// advertisement builder behind this handle.
+    /// Gets the kind of advertisement builder (public/encrypted).
+    pub fn kind(&self) -> AdvertisementBuilderKind {
+        self.kind
+    }
+
+    /// Attempts to add the given data element to the V0 advertisement builder behind this handle.
+    /// This function does not take ownership of the handle.
     pub fn add_de(&self, de: V0DataElement) -> Result<AddV0DEResult, InvalidStackDataStructure> {
         match self.handle.get_mut() {
             Ok(mut adv_builder_write_guard) => adv_builder_write_guard.add_de(de),
             Err(_) => Ok(AddV0DEResult::InvalidAdvertisementBuilderHandle),
         }
     }
-    /// Attempts to serialize the contents of the advertisement builder
-    /// behind this handle to bytes. Assuming that the handle is valid,
-    /// this operation will always result in the contents behind the
-    /// advertisement builder handle being deallocated.
+    /// Attempts to serialize the contents of the advertisement builder behind this handle to
+    /// bytes. This function takes ownership of the handle.
     pub fn into_advertisement(self) -> SerializeV0AdvertisementResult {
         match self.handle.deallocate() {
             Ok(adv_builder) => adv_builder.into_advertisement(),
             Err(_) => SerializeV0AdvertisementResult::InvalidAdvertisementBuilderHandle,
         }
     }
-    /// Attempts to deallocate the V0 advertisement builder
-    /// behind this handle.
+    /// Attempts to deallocate the V0 advertisement builder behind this handle. This function takes
+    /// ownership of the handle.
     pub fn deallocate(self) -> DeallocateResult {
         self.handle.deallocate().map(|_| ()).into()
     }
@@ -108,45 +110,42 @@
     declare_enum_cast! {into_success, Success, V0AdvertisementBuilder }
 }
 
-/// Creates a new V0 advertisement builder for a public advertisement.
+/// Creates a new V0 advertisement builder for a public advertisement. The caller is given
+/// ownership of the created handle.
 pub fn create_v0_public_advertisement_builder() -> CreateV0AdvertisementBuilderResult {
     V0AdvertisementBuilderHandle::allocate(V0AdvertisementBuilderInternals::new_public)
         .map(|handle| V0AdvertisementBuilder { kind: AdvertisementBuilderKind::Public, handle })
         .into()
 }
 
-/// Creates a new V0 advertisement builder for an encrypted advertisement.
+/// Creates a new V0 advertisement builder for an encrypted advertisement. The caller is given
+/// ownership of the created handle.
 pub fn create_v0_encrypted_advertisement_builder(
     broadcast_cred: V0BroadcastCredential,
-    identity_type: EncryptedIdentityType,
     salt: FixedSizeArray<2>,
 ) -> CreateV0AdvertisementBuilderResult {
     V0AdvertisementBuilderHandle::allocate(move || {
-        V0AdvertisementBuilderInternals::new_ldt(broadcast_cred, identity_type, salt.into_array())
+        V0AdvertisementBuilderInternals::new_ldt(broadcast_cred, salt.into_array())
     })
     .map(|handle| V0AdvertisementBuilder { kind: AdvertisementBuilderKind::Encrypted, handle })
     .into()
 }
 
-impl V0AdvertisementBuilder {
-    /// Gets the kind of advertisement builder (public/encrypted)
-    pub fn kind(&self) -> AdvertisementBuilderKind {
-        self.kind
-    }
-}
-
 /// Discriminant for `SerializeV0AdvertisementResult`.
 #[repr(u8)]
 pub enum SerializeV0AdvertisementResultKind {
     /// Serializing the advertisement to bytes was successful.
     Success = 0,
+    /// The advertisement builder handle was invalid.
+    InvalidAdvertisementBuilderHandle = 1,
     /// Serializing the advertisement to bytes failed
     /// because the data in the advertisement wasn't
     /// of an appropriate size for LDT encryption
     /// to succeed.
-    LdtError = 1,
-    /// The advertisement builder handle was invalid.
-    InvalidAdvertisementBuilderHandle = 2,
+    LdtError = 2,
+    /// Serializing an unencrypted adv failed because the adv data didn't meet the length
+    /// requirements.
+    UnencryptedError = 3,
 }
 
 /// The result of attempting to serialize the contents
@@ -155,8 +154,9 @@
 #[allow(missing_docs)]
 pub enum SerializeV0AdvertisementResult {
     Success(ByteBuffer<24>),
-    LdtError,
     InvalidAdvertisementBuilderHandle,
+    LdtError,
+    UnencryptedError,
 }
 
 impl SerializeV0AdvertisementResult {
@@ -168,10 +168,11 @@
     fn kind(&self) -> SerializeV0AdvertisementResultKind {
         match self {
             Self::Success(_) => SerializeV0AdvertisementResultKind::Success,
-            Self::LdtError => SerializeV0AdvertisementResultKind::LdtError,
             Self::InvalidAdvertisementBuilderHandle => {
                 SerializeV0AdvertisementResultKind::InvalidAdvertisementBuilderHandle
             }
+            Self::LdtError => SerializeV0AdvertisementResultKind::LdtError,
+            Self::UnencryptedError => SerializeV0AdvertisementResultKind::UnencryptedError,
         }
     }
 }
@@ -183,32 +184,23 @@
 
 impl V0AdvertisementBuilderInternals {
     pub(crate) fn new_public() -> Self {
-        Self::new(np_adv::PublicIdentity.into())
+        Self::new(np_adv::legacy::serialize::UnencryptedEncoder.into())
     }
-    pub(crate) fn new_ldt(
-        broadcast_cred: V0BroadcastCredential,
-        identity_type: EncryptedIdentityType,
-        salt: [u8; 2],
-    ) -> Self {
+    pub(crate) fn new_ldt(broadcast_cred: V0BroadcastCredential, salt: [u8; 2]) -> Self {
         // TODO: What do about salts? Need to prevent re-use fo the same salt,
         // but have no current rich representation of used salts...
-        let salt = ldt_np_adv::LegacySalt::from(salt);
-        let identity_type = identity_type.into();
+        let salt = ldt_np_adv::V0Salt::from(salt);
         let internal_broadcast_cred = broadcast_cred.into_internal();
-        let identity = np_adv::legacy::serialize::LdtIdentity::new(
-            identity_type,
-            salt,
-            &internal_broadcast_cred,
-        );
+        let identity = np_adv::legacy::serialize::LdtEncoder::new(salt, &internal_broadcast_cred);
         Self::new(identity.into())
     }
-    fn new(identity: np_adv_dynamic::legacy::BoxedIdentity<CryptoProviderImpl>) -> Self {
+    fn new(encoder: np_adv_dynamic::legacy::BoxedEncoder<CryptoProviderImpl>) -> Self {
         let adv_builder =
-            np_adv_dynamic::legacy::BoxedAdvBuilder::<CryptoProviderImpl>::new(identity);
+            np_adv_dynamic::legacy::BoxedAdvBuilder::<CryptoProviderImpl>::new(encoder);
         Self { adv_builder }
     }
     fn add_de(&mut self, de: V0DataElement) -> Result<AddV0DEResult, InvalidStackDataStructure> {
-        let to_boxed = np_adv_dynamic::legacy::ToBoxedDataElementBundle::try_from(de)?;
+        let to_boxed = np_adv_dynamic::legacy::ToBoxedSerializeDataElement::try_from(de)?;
         use np_adv::legacy::serialize::AddDataElementError;
         use np_adv_dynamic::legacy::BoxedAddDataElementError;
         match self.adv_builder.add_data_element(to_boxed) {
@@ -222,22 +214,19 @@
         }
     }
     fn into_advertisement(self) -> SerializeV0AdvertisementResult {
-        match self.adv_builder.into_advertisement() {
-            Ok(serialized_bytes) => {
-                let byte_buffer = ByteBuffer::from_array_view(serialized_bytes);
-                SerializeV0AdvertisementResult::Success(byte_buffer)
-            }
-            Err(np_adv_dynamic::legacy::BoxedAdvConstructionError::Ldt(_)) => {
-                SerializeV0AdvertisementResult::LdtError
-            }
-        }
-    }
-}
-
-fn get_v0_advertisement_builder_handle_map_dimensions() -> HandleMapDimensions {
-    HandleMapDimensions {
-        num_shards: global_num_shards(),
-        max_active_handles: global_max_num_v0_advertisement_builders(),
+        self.adv_builder
+            .into_advertisement()
+            .map(|serialized_bytes| {
+                SerializeV0AdvertisementResult::Success(ByteBuffer::from_array_view(
+                    serialized_bytes,
+                ))
+            })
+            .unwrap_or_else(|e| match e {
+                BoxedAdvConstructionError::Ldt(_) => SerializeV0AdvertisementResult::LdtError,
+                BoxedAdvConstructionError::Unencrypted(_) => {
+                    SerializeV0AdvertisementResult::UnencryptedError
+                }
+            })
     }
 }
 
@@ -250,7 +239,7 @@
 
 declare_handle_map!(
     advertisement_builder,
-    super::get_v0_advertisement_builder_handle_map_dimensions(),
+    crate::common::default_handle_map_dimensions(),
     super::V0AdvertisementBuilderHandle,
     super::V0AdvertisementBuilderInternals
 );
diff --git a/nearby/presence/np_ffi_core/src/serialize/v1.rs b/nearby/presence/np_ffi_core/src/serialize/v1.rs
index d48a725..c87446f 100644
--- a/nearby/presence/np_ffi_core/src/serialize/v1.rs
+++ b/nearby/presence/np_ffi_core/src/serialize/v1.rs
@@ -19,9 +19,7 @@
 use crate::utils::FfiEnum;
 use crate::v1::V1VerificationMode;
 use crypto_provider_default::CryptoProviderImpl;
-use handle_map::{declare_handle_map, HandleLike, HandleMapDimensions, HandleMapFullError};
-use np_adv;
-use np_adv_dynamic;
+use handle_map::{declare_handle_map, HandleLike, HandleMapFullError};
 
 /// A handle to a builder for V1 advertisements.
 #[derive(Clone, Copy)]
@@ -32,21 +30,24 @@
 }
 
 impl V1AdvertisementBuilder {
-    /// Attempts to create a builder for a new public section within
-    /// this advertisement, returning a handle to the newly-created
-    /// section builder if successful.
+    /// 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.
     ///
-    /// 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.
+    /// This method may fail if there is another currently-active section builder for the same
+    /// advertisement builder, if the kind of section being added does not match the advertisement
+    /// type (public/encrypted), or if the section would not manage to fit within the enclosing
+    /// advertisement.
     pub fn public_section_builder(&self) -> CreateV1SectionBuilderResult {
         self.section_builder_internals(|internals| internals.public_section_builder())
     }
-    /// Attempts to create a builder for a new encrypted section within
-    /// this advertisement, returning a handle to the newly-created
-    /// section builder if successful.
+
+    /// 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.
     ///
     /// The identity details for the new section builder may be specified
     /// via providing the broadcast credential data, the kind of encrypted
@@ -61,18 +62,17 @@
     pub fn encrypted_section_builder(
         &self,
         broadcast_cred: V1BroadcastCredential,
-        identity_type: EncryptedIdentityType,
         verification_mode: V1VerificationMode,
     ) -> CreateV1SectionBuilderResult {
         self.section_builder_internals(move |internals| {
-            internals.encrypted_section_builder(broadcast_cred, identity_type, verification_mode)
+            internals.encrypted_section_builder(broadcast_cred, verification_mode)
         })
     }
 
-    /// Attempts to serialize the contents of the advertisement builder
-    /// behind this handle to bytes. Assuming that the handle is valid,
-    /// this operation will always result in the contents behind the
-    /// advertisement builder handle being deallocated.
+    /// 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() {
             Ok(adv_builder) => adv_builder.into_advertisement(),
@@ -162,25 +162,13 @@
         .into()
 }
 
-impl V1AdvertisementBuilder {
-    /// Gets the kind of advertisement builder (public/encrypted)
-    pub fn kind(&self) -> AdvertisementBuilderKind {
-        self.kind
-    }
-}
-
 pub(crate) enum V1AdvertisementBuilderState {
     /// Internal state for when we have an active advertisement
     /// builder, but no currently-active section builder.
     Advertisement(np_adv_dynamic::extended::BoxedAdvBuilder),
     /// Internal state for when we have both an active advertisement
     /// builder and an active section builder.
-    Section(
-        np_adv_dynamic::extended::BoxedSectionBuilder<
-            np_adv::extended::serialize::AdvBuilder,
-            CryptoProviderImpl,
-        >,
-    ),
+    Section(np_adv_dynamic::extended::BoxedSectionBuilder<np_adv::extended::serialize::AdvBuilder>),
 }
 
 /// Internal version of errors which may be raised when
@@ -279,12 +267,12 @@
     /// of the newly-added section builder.
     pub(crate) fn section_builder_internal(
         &mut self,
-        identity: np_adv_dynamic::extended::BoxedIdentity<CryptoProviderImpl>,
+        identity: np_adv_dynamic::extended::BoxedEncoder,
     ) -> Result<usize, SectionBuilderError> {
         let state = self.state.take();
         match state {
             Some(V1AdvertisementBuilderState::Advertisement(adv_builder)) => {
-                match adv_builder.into_section_builder::<CryptoProviderImpl>(identity) {
+                match adv_builder.into_section_builder(identity) {
                     Ok(section_builder) => {
                         let section_index = section_builder.section_index();
                         self.state = Some(V1AdvertisementBuilderState::Section(section_builder));
@@ -307,38 +295,34 @@
     }
 
     pub(crate) fn public_section_builder(&mut self) -> Result<usize, SectionBuilderError> {
-        let identity = np_adv_dynamic::extended::BoxedIdentity::PublicIdentity;
+        let identity = np_adv_dynamic::extended::BoxedEncoder::Unencrypted;
         self.section_builder_internal(identity)
     }
     pub(crate) fn encrypted_section_builder(
         &mut self,
         broadcast_cred: V1BroadcastCredential,
-        identity_type: EncryptedIdentityType,
         verification_mode: V1VerificationMode,
     ) -> Result<usize, SectionBuilderError> {
         let mut rng = get_global_crypto_rng();
         let rng = rng.get_rng();
-        let identity_type = identity_type.into();
         let internal_broadcast_cred = broadcast_cred.into_internal();
-        let identity = match verification_mode {
+        let encoder = match verification_mode {
             V1VerificationMode::Mic => {
-                let encoder = np_adv::extended::serialize::MicEncryptedSectionEncoder::<
-                    CryptoProviderImpl,
-                >::new_random_salt(
-                    rng, identity_type, &internal_broadcast_cred
-                );
-                np_adv_dynamic::extended::BoxedIdentity::MicEncrypted(encoder)
+                let encoder =
+                    np_adv::extended::serialize::MicEncryptedSectionEncoder::<_>::new_wrapped_salt::<
+                        CryptoProviderImpl,
+                    >(rng, &internal_broadcast_cred);
+                np_adv_dynamic::extended::BoxedEncoder::MicEncrypted(encoder)
             }
             V1VerificationMode::Signature => {
-                let encoder = np_adv::extended::serialize::SignedEncryptedSectionEncoder::<
-                    CryptoProviderImpl,
-                >::new_random_salt(
-                    rng, identity_type, &internal_broadcast_cred
-                );
-                np_adv_dynamic::extended::BoxedIdentity::SignedEncrypted(encoder)
+                let encoder =
+                    np_adv::extended::serialize::SignedEncryptedSectionEncoder::new_random_salt::<
+                        CryptoProviderImpl,
+                    >(rng, &internal_broadcast_cred);
+                np_adv_dynamic::extended::BoxedEncoder::SignedEncrypted(encoder)
             }
         };
-        self.section_builder_internal(identity)
+        self.section_builder_internal(encoder)
     }
     fn into_advertisement(self) -> SerializeV1AdvertisementResult {
         match self.state {
@@ -351,13 +335,6 @@
     }
 }
 
-fn get_v1_advertisement_builder_handle_map_dimensions() -> HandleMapDimensions {
-    HandleMapDimensions {
-        num_shards: global_num_shards(),
-        max_active_handles: global_max_num_v1_advertisement_builders(),
-    }
-}
-
 /// A `#[repr(C)]` handle to a value of type `V1AdvertisementBuilderInternals`
 #[repr(C)]
 #[derive(Clone, Copy, PartialEq, Eq)]
@@ -367,7 +344,7 @@
 
 declare_handle_map!(
     advertisement_builder,
-    super::get_v1_advertisement_builder_handle_map_dimensions(),
+    crate::common::default_handle_map_dimensions(),
     super::V1AdvertisementBuilderHandle,
     super::V1AdvertisementBuilderInternals
 );
@@ -501,21 +478,18 @@
 }
 
 impl NextV1DE16ByteSaltResult {
-    declare_enum_cast! {into_success, Success, FixedSizeArray<16> }
-}
-
-impl From<Option<np_adv::extended::serialize::DeSalt<CryptoProviderImpl>>>
-    for NextV1DE16ByteSaltResult
-{
-    fn from(maybe_salt: Option<np_adv::extended::serialize::DeSalt<CryptoProviderImpl>>) -> Self {
-        match maybe_salt.and_then(|salt| salt.derive::<16>()) {
+    fn new_from_de_salt(salt: Option<np_adv::extended::serialize::DeSalt>) -> Self {
+        match salt.and_then(|salt| salt.derive::<16, CryptoProviderImpl>()) {
             Some(salt) => NextV1DE16ByteSaltResult::Success(FixedSizeArray::from_array(salt)),
             None => NextV1DE16ByteSaltResult::Error,
         }
     }
+
+    declare_enum_cast! {into_success, Success, FixedSizeArray<16> }
 }
 
-/// A handle to a builder for V1 sections.
+/// 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.
 #[derive(Clone, Copy)]
 #[repr(C)]
 pub struct V1SectionBuilder {
@@ -537,7 +511,8 @@
                         // matches the index of the section currently under construction.
                         let actual_section_index = section_builder.section_index() as u8;
                         if self.section_index == actual_section_index {
-                            let updated_adv_builder = section_builder.add_to_advertisement();
+                            let updated_adv_builder =
+                                section_builder.add_to_advertisement::<CryptoProviderImpl>();
                             adv_builder.state = Some(V1AdvertisementBuilderState::Advertisement(
                                 updated_adv_builder,
                             ));
@@ -564,7 +539,9 @@
     /// is a public section.
     pub fn next_de_salt(&self) -> NextV1DE16ByteSaltResult {
         self.try_apply_to_internals(
-            |section_builder| section_builder.next_de_salt().into(),
+            |section_builder| {
+                NextV1DE16ByteSaltResult::new_from_de_salt(section_builder.next_de_salt())
+            },
             NextV1DE16ByteSaltResult::Error,
         )
     }
@@ -598,7 +575,6 @@
         func: impl FnOnce(
             &mut np_adv_dynamic::extended::BoxedSectionBuilder<
                 np_adv::extended::serialize::AdvBuilder,
-                CryptoProviderImpl,
             >,
         ) -> R,
         invalid_handle_error: R,
@@ -658,9 +634,6 @@
 mod tests {
     use super::*;
 
-    const EMPTY_BROADCAST_CRED: V1BroadcastCredential =
-        V1BroadcastCredential::new([0; 32], [0; 16], [0; 32]);
-
     fn state_is_advertisement_building(adv_builder_state: &V1AdvertisementBuilderState) -> bool {
         matches!(adv_builder_state, V1AdvertisementBuilderState::Advertisement(_))
     }
@@ -675,11 +648,7 @@
             adv_builder.state.as_ref().expect("Adv builder state should be present.");
         assert!(state_is_advertisement_building(adv_builder_state));
         let section_index = adv_builder
-            .encrypted_section_builder(
-                EMPTY_BROADCAST_CRED,
-                EncryptedIdentityType::Private,
-                V1VerificationMode::Mic,
-            )
+            .encrypted_section_builder(empty_broadcast_cred(), V1VerificationMode::Mic)
             .expect("Should be able to build the first section.");
         assert_eq!(section_index, 0);
 
@@ -689,12 +658,12 @@
         assert!(!state_is_advertisement_building(adv_builder_state));
 
         let double_build_error = adv_builder
-            .encrypted_section_builder(
-                EMPTY_BROADCAST_CRED,
-                EncryptedIdentityType::Private,
-                V1VerificationMode::Mic,
-            )
+            .encrypted_section_builder(empty_broadcast_cred(), V1VerificationMode::Mic)
             .expect_err("Shouldn't be able to start a new section builder with an unclosed one.");
         assert_eq!(double_build_error, SectionBuilderError::UnclosedActiveSection);
     }
+
+    fn empty_broadcast_cred() -> V1BroadcastCredential {
+        V1BroadcastCredential::new([0; 32], [0; 16].into(), [0; 32])
+    }
 }
diff --git a/nearby/presence/np_ffi_core/src/utils.rs b/nearby/presence/np_ffi_core/src/utils.rs
index b1495b9..4876645 100644
--- a/nearby/presence/np_ffi_core/src/utils.rs
+++ b/nearby/presence/np_ffi_core/src/utils.rs
@@ -17,6 +17,10 @@
 
 /// Type-level predicate for handle types which uniformly hold a lock
 /// for longer than some other handle type in API calls.
+///
+/// Largely an informative marker trait used to indicate the
+/// lock ordering on types.
+#[allow(dead_code)]
 pub(crate) trait LocksLongerThan<H: HandleLike>: HandleLike {}
 
 /// Trait which canonicalizes the relationship between FFI
diff --git a/nearby/presence/np_ffi_core/src/v0.rs b/nearby/presence/np_ffi_core/src/v0.rs
index 89a427c..16b7b21 100644
--- a/nearby/presence/np_ffi_core/src/v0.rs
+++ b/nearby/presence/np_ffi_core/src/v0.rs
@@ -18,8 +18,11 @@
 use crate::common::InvalidStackDataStructure;
 use crate::serialize::AdvertisementBuilderKind;
 use crate::utils::FfiEnum;
-use np_adv::legacy::actions::ActionsDataElement;
-use np_adv::legacy::{data_elements as np_adv_de, Ciphertext, PacketFlavorEnum, Plaintext};
+use np_adv::{
+    legacy::data_elements::actions::ActionsDataElement,
+    legacy::{data_elements as np_adv_de, Ciphertext, PacketFlavorEnum, Plaintext},
+};
+use strum::IntoEnumIterator;
 
 /// Discriminant for `V0DataElement`.
 #[repr(u8)]
@@ -27,11 +30,11 @@
     /// A transmission Power (Tx Power) data-element.
     /// The associated payload may be obtained via
     /// `V0DataElement#into_tx_power`.
-    TxPower = 0,
+    TxPower = 1,
     /// The Actions data-element.
     /// The associated payload may be obtained via
     /// `V0DataElement#into_actions`.
-    Actions = 1,
+    Actions = 2,
 }
 
 /// Representation of a V0 data element.
@@ -43,7 +46,7 @@
     Actions(V0Actions),
 }
 
-impl TryFrom<V0DataElement> for np_adv_dynamic::legacy::ToBoxedDataElementBundle {
+impl TryFrom<V0DataElement> for np_adv_dynamic::legacy::ToBoxedSerializeDataElement {
     type Error = InvalidStackDataStructure;
     fn try_from(de: V0DataElement) -> Result<Self, InvalidStackDataStructure> {
         match de {
@@ -53,14 +56,14 @@
     }
 }
 
-impl<F: np_adv::legacy::PacketFlavor> From<np_adv::legacy::deserialize::PlainDataElement<F>>
+impl<F: np_adv::legacy::PacketFlavor> From<np_adv::legacy::deserialize::DeserializedDataElement<F>>
     for V0DataElement
 {
-    fn from(de: np_adv::legacy::deserialize::PlainDataElement<F>) -> Self {
-        use np_adv::legacy::deserialize::PlainDataElement;
+    fn from(de: np_adv::legacy::deserialize::DeserializedDataElement<F>) -> Self {
+        use np_adv::legacy::deserialize::DeserializedDataElement;
         match de {
-            PlainDataElement::Actions(x) => V0DataElement::Actions(x.into()),
-            PlainDataElement::TxPower(x) => V0DataElement::TxPower(x.into()),
+            DeserializedDataElement::Actions(x) => V0DataElement::Actions(x.into()),
+            DeserializedDataElement::TxPower(x) => V0DataElement::TxPower(x.into()),
         }
     }
 }
@@ -124,8 +127,7 @@
 }
 
 impl TxPower {
-    /// Attempts to construct a new TxPower from
-    /// the given signed-byte value.
+    /// Attempts to construct a new TxPower from the given signed-byte value.
     pub fn build_from_signed_byte(tx_power: i8) -> BuildTxPowerResult {
         match np_adv::shared_data::TxPower::try_from(tx_power) {
             Ok(_) => BuildTxPowerResult::Success(Self { tx_power }),
@@ -138,13 +140,13 @@
     }
 }
 
-impl From<np_adv_de::TxPowerDataElement> for TxPower {
-    fn from(de: np_adv_de::TxPowerDataElement) -> Self {
+impl From<np_adv_de::tx_power::TxPowerDataElement> for TxPower {
+    fn from(de: np_adv_de::tx_power::TxPowerDataElement) -> Self {
         Self { tx_power: de.tx_power_value() }
     }
 }
 
-impl TryFrom<TxPower> for np_adv_dynamic::legacy::ToBoxedDataElementBundle {
+impl TryFrom<TxPower> for np_adv_dynamic::legacy::ToBoxedSerializeDataElement {
     type Error = InvalidStackDataStructure;
     fn try_from(value: TxPower) -> Result<Self, InvalidStackDataStructure> {
         np_adv::shared_data::TxPower::try_from(value.as_i8())
@@ -153,80 +155,6 @@
     }
 }
 
-/// Discriminant for `BuildContextSyncSeqNumResult`.
-#[repr(u8)]
-#[derive(Clone, Copy)]
-pub enum BuildContextSyncSeqNumResultKind {
-    /// The sequence number was outside the allowed
-    /// 0-15 single-nibble range.
-    OutOfRange = 0,
-    /// The sequence number was in range,
-    /// and so a `ContextSyncSeqNum` was constructed.
-    Success = 1,
-}
-
-/// Result type for attempting to construct a
-/// ContextSyncSeqNum from an unsigned byte.
-#[repr(C)]
-#[allow(missing_docs)]
-pub enum BuildContextSyncSeqNumResult {
-    OutOfRange,
-    Success(ContextSyncSeqNum),
-}
-
-impl FfiEnum for BuildContextSyncSeqNumResult {
-    type Kind = BuildContextSyncSeqNumResultKind;
-    fn kind(&self) -> Self::Kind {
-        match self {
-            Self::OutOfRange => BuildContextSyncSeqNumResultKind::OutOfRange,
-            Self::Success(_) => BuildContextSyncSeqNumResultKind::Success,
-        }
-    }
-}
-
-impl BuildContextSyncSeqNumResult {
-    declare_enum_cast! {into_success, Success, ContextSyncSeqNum}
-}
-/// Representation of a context-sync sequence number.
-#[derive(Clone, Copy)]
-#[repr(C)]
-pub struct ContextSyncSeqNum {
-    value: u8,
-}
-
-impl ContextSyncSeqNum {
-    /// Attempts to build a new context sync sequence number
-    /// from the given unsigned byte.
-    pub fn build_from_unsigned_byte(value: u8) -> BuildContextSyncSeqNumResult {
-        match np_adv::shared_data::ContextSyncSeqNum::try_from(value) {
-            Ok(_) => BuildContextSyncSeqNumResult::Success(Self { value }),
-            Err(_) => BuildContextSyncSeqNumResult::OutOfRange,
-        }
-    }
-    /// Yields this context sync sequence number
-    /// as a u8.
-    pub fn as_u8(&self) -> u8 {
-        self.value
-    }
-}
-
-impl From<np_adv::shared_data::ContextSyncSeqNum> for ContextSyncSeqNum {
-    fn from(value: np_adv::shared_data::ContextSyncSeqNum) -> Self {
-        let value = value.as_u8();
-        Self { value }
-    }
-}
-
-impl TryFrom<ContextSyncSeqNum> for np_adv_dynamic::legacy::ToBoxedActionElement {
-    type Error = InvalidStackDataStructure;
-    fn try_from(value: ContextSyncSeqNum) -> Result<Self, Self::Error> {
-        let value = value.as_u8();
-        np_adv::shared_data::ContextSyncSeqNum::try_from(value)
-            .map(np_adv_dynamic::legacy::ToBoxedActionElement::ContextSyncSeqNum)
-            .map_err(|_| InvalidStackDataStructure)
-    }
-}
-
 /// Representation of the Actions DE in V0.
 #[derive(Clone, Copy)]
 #[repr(C)]
@@ -237,7 +165,7 @@
     Encrypted(V0ActionBits),
 }
 
-impl TryFrom<V0Actions> for np_adv_dynamic::legacy::ToBoxedDataElementBundle {
+impl TryFrom<V0Actions> for np_adv_dynamic::legacy::ToBoxedSerializeDataElement {
     type Error = InvalidStackDataStructure;
     fn try_from(value: V0Actions) -> Result<Self, InvalidStackDataStructure> {
         let boxed_action_bits = np_adv_dynamic::legacy::BoxedActionBits::try_from(value)?;
@@ -245,9 +173,7 @@
     }
 }
 
-impl<F: np_adv::legacy::PacketFlavor> From<np_adv::legacy::actions::ActionsDataElement<F>>
-    for V0Actions
-{
+impl<F: np_adv::legacy::PacketFlavor> From<ActionsDataElement<F>> for V0Actions {
     fn from(value: ActionsDataElement<F>) -> Self {
         match F::ENUM_VARIANT {
             PacketFlavorEnum::Plaintext => {
@@ -262,57 +188,102 @@
 
 #[repr(C)]
 #[derive(Clone, Copy)]
-/// The bitfield data of a VOActions data element
+/// The bitfield data of a V0Actions data element
 pub struct V0ActionBits {
     bitfield: u32,
 }
 
-#[derive(Clone, Copy)]
+impl From<u32> for V0ActionBits {
+    fn from(bitfield: u32) -> Self {
+        // No need to validate here. Validation of this struct is done at all places where it is
+        // taken as a parameter. See [`InvalidStackDataStructure`]. Since this struct is
+        // `#[repr(C)]` if rules were enforced here, they could be broken by a foreign language
+        // user.
+        Self { bitfield }
+    }
+}
+
+#[derive(Clone, Copy, strum_macros::EnumIter)]
 #[allow(missing_docs)]
 #[repr(u8)]
 /// The possible boolean action types which can be present in an Actions data element
-pub enum BooleanActionType {
+pub enum ActionType {
+    CrossDevSdk = 1,
+    CallTransfer = 4,
     ActiveUnlock = 8,
     NearbyShare = 9,
     InstantTethering = 10,
     PhoneHub = 11,
-    PresenceManager = 12,
-    Finder = 13,
-    FastPairSass = 14,
 }
 
-impl BooleanActionType {
+#[derive(Clone, Copy, Debug)]
+/// The given int is out of range for conversion.
+pub struct TryFromIntError;
+
+impl From<core::num::TryFromIntError> for TryFromIntError {
+    fn from(_: core::num::TryFromIntError) -> Self {
+        Self
+    }
+}
+
+impl TryFrom<u8> for ActionType {
+    type Error = TryFromIntError;
+    fn try_from(n: u8) -> Result<Self, Self::Error> {
+        // cast is safe since it's a repr(u8) unit enum
+        Self::iter().find(|t| *t as u8 == n).ok_or(TryFromIntError)
+    }
+}
+
+impl ActionType {
     pub(crate) fn as_boxed_action_element(
         &self,
         value: bool,
     ) -> np_adv_dynamic::legacy::ToBoxedActionElement {
         use np_adv_dynamic::legacy::ToBoxedActionElement;
         match self {
+            Self::CrossDevSdk => ToBoxedActionElement::CrossDevSdk(value),
+            Self::CallTransfer => ToBoxedActionElement::CallTransfer(value),
             Self::ActiveUnlock => ToBoxedActionElement::ActiveUnlock(value),
             Self::NearbyShare => ToBoxedActionElement::NearbyShare(value),
             Self::InstantTethering => ToBoxedActionElement::InstantTethering(value),
             Self::PhoneHub => ToBoxedActionElement::PhoneHub(value),
-            Self::PresenceManager => ToBoxedActionElement::PresenceManager(value),
-            Self::Finder => ToBoxedActionElement::Finder(value),
-            Self::FastPairSass => ToBoxedActionElement::FastPairSass(value),
         }
     }
 }
 
-impl From<BooleanActionType> for np_adv::legacy::actions::ActionType {
-    fn from(value: BooleanActionType) -> Self {
+impl From<ActionType> for np_adv::legacy::data_elements::actions::ActionType {
+    fn from(value: ActionType) -> Self {
         match value {
-            BooleanActionType::ActiveUnlock => np_adv::legacy::actions::ActionType::ActiveUnlock,
-            BooleanActionType::NearbyShare => np_adv::legacy::actions::ActionType::NearbyShare,
-            BooleanActionType::InstantTethering => {
-                np_adv::legacy::actions::ActionType::InstantTethering
+            ActionType::CrossDevSdk => {
+                np_adv::legacy::data_elements::actions::ActionType::CrossDevSdk
             }
-            BooleanActionType::PhoneHub => np_adv::legacy::actions::ActionType::PhoneHub,
-            BooleanActionType::Finder => np_adv::legacy::actions::ActionType::Finder,
-            BooleanActionType::FastPairSass => np_adv::legacy::actions::ActionType::FastPairSass,
-            BooleanActionType::PresenceManager => {
-                np_adv::legacy::actions::ActionType::PresenceManager
+            ActionType::CallTransfer => {
+                np_adv::legacy::data_elements::actions::ActionType::CallTransfer
             }
+            ActionType::ActiveUnlock => {
+                np_adv::legacy::data_elements::actions::ActionType::ActiveUnlock
+            }
+            ActionType::NearbyShare => {
+                np_adv::legacy::data_elements::actions::ActionType::NearbyShare
+            }
+            ActionType::InstantTethering => {
+                np_adv::legacy::data_elements::actions::ActionType::InstantTethering
+            }
+            ActionType::PhoneHub => np_adv::legacy::data_elements::actions::ActionType::PhoneHub,
+        }
+    }
+}
+
+// ensure bidirectional mapping
+impl From<np_adv::legacy::data_elements::actions::ActionType> for ActionType {
+    fn from(value: np_adv_de::actions::ActionType) -> Self {
+        match value {
+            np_adv_de::actions::ActionType::CrossDevSdk => ActionType::CrossDevSdk,
+            np_adv_de::actions::ActionType::CallTransfer => ActionType::CallTransfer,
+            np_adv_de::actions::ActionType::ActiveUnlock => ActionType::ActiveUnlock,
+            np_adv_de::actions::ActionType::NearbyShare => ActionType::NearbyShare,
+            np_adv_de::actions::ActionType::InstantTethering => ActionType::InstantTethering,
+            np_adv_de::actions::ActionType::PhoneHub => ActionType::PhoneHub,
         }
     }
 }
@@ -334,17 +305,19 @@
     fn try_from(actions: V0Actions) -> Result<Self, InvalidStackDataStructure> {
         match actions {
             V0Actions::Plaintext(action_bits) => {
-                let bits = np_adv::legacy::actions::ActionBits::<Plaintext>::try_from(
-                    action_bits.bitfield,
-                )
-                .map_err(|_| InvalidStackDataStructure)?;
+                let bits =
+                    np_adv::legacy::data_elements::actions::ActionBits::<Plaintext>::try_from(
+                        action_bits.bitfield,
+                    )
+                    .map_err(|_| InvalidStackDataStructure)?;
                 Ok(bits.into())
             }
             V0Actions::Encrypted(action_bits) => {
-                let bits = np_adv::legacy::actions::ActionBits::<Ciphertext>::try_from(
-                    action_bits.bitfield,
-                )
-                .map_err(|_| InvalidStackDataStructure)?;
+                let bits =
+                    np_adv::legacy::data_elements::actions::ActionBits::<Ciphertext>::try_from(
+                        action_bits.bitfield,
+                    )
+                    .map_err(|_| InvalidStackDataStructure)?;
                 Ok(bits.into())
             }
         }
@@ -410,13 +383,10 @@
 
     /// Return whether a boolean action type is set in this data element
     #[allow(clippy::expect_used)]
-    pub fn has_action(
-        &self,
-        action_type: BooleanActionType,
-    ) -> Result<bool, InvalidStackDataStructure> {
+    pub fn has_action(&self, action_type: ActionType) -> Result<bool, InvalidStackDataStructure> {
         let boxed_action_bits = np_adv_dynamic::legacy::BoxedActionBits::try_from(*self)?;
         let action_type = action_type.into();
-        Ok(boxed_action_bits.has_action(&action_type).expect("BooleanActionType only has one bit"))
+        Ok(boxed_action_bits.has_action(action_type))
     }
 
     /// Attempts to set the given action bit to the given boolean value.
@@ -427,7 +397,7 @@
     /// unaltered.
     pub fn set_action(
         self,
-        action_type: BooleanActionType,
+        action_type: ActionType,
         value: bool,
     ) -> Result<SetV0ActionResult, InvalidStackDataStructure> {
         let mut boxed_action_bits = np_adv_dynamic::legacy::BoxedActionBits::try_from(self)?;
@@ -437,25 +407,4 @@
             Err(_) => Ok(SetV0ActionResult::Error(self)),
         }
     }
-
-    /// Sets the context sequence number for this data element.
-    #[allow(clippy::expect_used)]
-    pub fn set_context_sync_seq_num(
-        self,
-        value: ContextSyncSeqNum,
-    ) -> Result<Self, InvalidStackDataStructure> {
-        let mut boxed_action_bits = np_adv_dynamic::legacy::BoxedActionBits::try_from(self)?;
-        let boxed_action_element = np_adv_dynamic::legacy::ToBoxedActionElement::try_from(value)?;
-        boxed_action_bits
-            .set_action(boxed_action_element)
-            .expect("Context sync sequence number always may be present");
-        Ok(boxed_action_bits.into())
-    }
-
-    /// Return the context sequence number from this data element
-    #[allow(clippy::expect_used)]
-    pub fn get_context_sync_seq_num(&self) -> Result<ContextSyncSeqNum, InvalidStackDataStructure> {
-        let boxed_action_bits = np_adv_dynamic::legacy::BoxedActionBits::try_from(*self)?;
-        Ok(boxed_action_bits.get_context_sync_seq_num().into())
-    }
 }
diff --git a/nearby/presence/np_hkdf/Cargo.toml b/nearby/presence/np_hkdf/Cargo.toml
index 45e40da..cd7fb4c 100644
--- a/nearby/presence/np_hkdf/Cargo.toml
+++ b/nearby/presence/np_hkdf/Cargo.toml
@@ -17,9 +17,10 @@
 xts_aes.workspace = true
 
 [dev-dependencies]
-crypto_provider_default.workspace = true
+crypto_provider_default = { workspace = true, features = ["rustcrypto"] }
 rand_ext.workspace = true
 test_helper.workspace = true
+test_vector_hkdf.workspace = true
 
 anyhow.workspace = true
 criterion.workspace = true
diff --git a/nearby/presence/np_hkdf/benches/np_hkdf.rs b/nearby/presence/np_hkdf/benches/np_hkdf.rs
index bec3b0e..0380d78 100644
--- a/nearby/presence/np_hkdf/benches/np_hkdf.rs
+++ b/nearby/presence/np_hkdf/benches/np_hkdf.rs
@@ -17,6 +17,7 @@
 use criterion::{black_box, criterion_group, criterion_main, Criterion};
 use crypto_provider::{CryptoProvider, CryptoRng};
 use crypto_provider_default::CryptoProviderImpl;
+use np_hkdf::DerivedSectionKeys;
 use rand_ext::random_bytes;
 
 pub fn build_np_hkdf(c: &mut Criterion) {
@@ -45,7 +46,7 @@
                 .collect::<Vec<_>>();
             b.iter(|| {
                 for hkdf in keys.iter() {
-                    black_box(hkdf.extended_unsigned_metadata_key_hmac_key());
+                    black_box(hkdf.v1_mic_extended_salt_keys().identity_token_hmac_key());
                 }
             });
         });
@@ -62,7 +63,7 @@
                 .collect::<Vec<_>>();
             b.iter(|| {
                 for hkdf in keys.iter() {
-                    black_box(np_hkdf::UnsignedSectionKeys::aes_key(hkdf));
+                    black_box(hkdf.v1_mic_extended_salt_keys().aes_key());
                 }
             });
         });
@@ -79,7 +80,7 @@
                 .collect::<Vec<_>>();
             b.iter(|| {
                 for hkdf in keys.iter() {
-                    black_box(hkdf.legacy_ldt_key());
+                    black_box(hkdf.v0_ldt_key());
                 }
             });
         });
diff --git a/nearby/presence/np_hkdf/resources/test/hkdf-test-vectors.json b/nearby/presence/np_hkdf/resources/test/hkdf-test-vectors.json
index b4e283c..d0af65f 100644
--- a/nearby/presence/np_hkdf/resources/test/hkdf-test-vectors.json
+++ b/nearby/presence/np_hkdf/resources/test/hkdf-test-vectors.json
@@ -1,2802 +1,3502 @@
 [
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "99D59E9A0A1761D40F4F28E1D56F4C25",
-      "derived_salt_first_section_no_de": "4D02FD0C02250E221A8C63A1A9865AE2",
-      "derived_salt_first_section_third_de": "8A72AE14A6EA06DAD91CC5970FBD005F",
-      "section_salt": "C561AB4667AA9D7E126E79F060BAD04F"
+    "key_seed_hkdf": {
+      "key_seed": "C73539A8BBF0270B0F15AC17442D664D0599FD63A6C9B913E1A4FDE3FD445229",
+      "v0_identity_token_hmac_key": "3EDE3135EFBB36DAA573469C4154DD3801D7A1A9DBF909EC9589C0F290395E09",
+      "v0_ldt_key": "FA79263B28CC411B6EF1D07A13D99E021D16E85C31963C86CA6413466F05F4ADC05EF9EAE5986F2C36525B366D88A05AC51CF105B09C98469341A6EC6E37C553",
+      "v0_metadata_nonce": "8A2952BCB4BB1A5131595884",
+      "v1_metadata_nonce": "2F68AE0A2D4A63436948F8A3",
+      "v1_mic_extended_salt_aes_key": "3C6AB7573EB60372377088D37FA71F28",
+      "v1_mic_extended_salt_identity_token_hmac_key": "AEAA5C7B9BEBC7ED47ACEEF2B4B15C957C7F18333DF05371CC5194928E4A75E1",
+      "v1_mic_extended_salt_mic_hmac_key": "77FAC801B7F4BFB45B66493109E5D2B8A1811046FE1B26FDD4911DFE83F22AE6",
+      "v1_mic_short_salt_aes_key": "6A4EA007544EB1F1F4FC3A09733A7636",
+      "v1_mic_short_salt_identity_token_hmac_key": "24CCCA1794C5267D0F31BFFD05933A3056DC866AE5CCC812D0BE992E741E3C70",
+      "v1_mic_short_salt_mic_hmac_key": "900CCE3E377FBF1A075F5FB96B2A670981624069DFCF8A99FF3CCB73AEDEC153",
+      "v1_signature_identity_token_hmac_key": "8C707D9DCB8C7965EF0DFB90C8781312135C0A8AD10388B939082947B0EEC5CF",
+      "v1_signature_section_aes_key": "EDB08293628753CF3BE7C28A65FF1A9D"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "05B8575BCA2486B5A1DD5D7D",
-      "extended_signed_metadata_key_hmac_key": "072C62C8F3A83F2ED0EEDD3107249160FF2ABEEDBEBA4607EF517A60F6640C7C",
-      "extended_signed_section_aes_key": "4DF7DE7DECCF54059C259EC7529D8AF8",
-      "extended_unsigned_metadata_key_hmac_key": "DC9FC71D78B822B6763ED1058E77DFCEF41B5F9F4810330AE034F962700EE225",
-      "extended_unsigned_section_aes_key": "A4E14FCB637F18E3B908635B67F4695B",
-      "extended_unsigned_section_mic_hmac_key": "C456B4C30D6E8F33B2D4010D656E3321ACF572EE09C831A87177B04F0F692AEF",
-      "key_seed": "E78C9A018A4B81DD837D9C7E2995B265E3363AAE9E9F7A46D1E0ECC81689D004",
-      "legacy_ldt_key": "E584BE2B557549287AAF196A765A0BB7F26F81210FFBA9AFE314ED0A4912BC4098779896121E6F0DAFFB30A70F358854C984611C4C249D48D8793F35512CBCE7",
-      "legacy_metadata_key_hmac_key": "EF9805022265A8B11D56B8E9EB2A17307EFC258EA1B8CEF70380BE6A3A3B1F43",
-      "legacy_metadata_nonce": "2ED7B79A45C428DFB747460B"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "F0EB",
+      "expanded_salt": "5267CC24373508BDD9CC45FE1BBFF13F"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "DC3DF85D25399054C997ABA4D0550C72",
+      "v0_identity_token": "84C66824B29BA5741EC22F1D3E77"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "202A",
-      "expanded_salt": "BBB2F537324B844F10631A119EB7C1C0"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "8579",
+      "short_salt_nonce": "38C673E98F69BE47A67CD13D"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "EFB7E58C4EF1CE656C9D2691CD46880E",
-      "legacy_metadata_key": "E36847D19B3DB1715E6542D66205"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "DA0DEC97DD1B417639F5BABAD8F3EA65",
+      "derived_salt_nonce": "3D412EFE82132F641657B86BBB145803",
+      "derived_salt_third_de": "2F1011C59BF1CC8A1870CB124E8C2753",
+      "section_extended_salt": "C88D4D9DF5287AE88CC991A84CDEDAA4"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "3B9EB63E2B808E95384B37049EC52296",
-      "derived_salt_first_section_no_de": "DA32C8270314D33E521BD6D455CEBAED",
-      "derived_salt_first_section_third_de": "FED4AB32F733BBD3A1F6CC3568925182",
-      "section_salt": "E6F154A2B223214012758A37E5EEEF10"
+    "key_seed_hkdf": {
+      "key_seed": "3938C7007A29B73F1EE49941B99A569D154FF1CFA6B67701E84BA14D303E1414",
+      "v0_identity_token_hmac_key": "A1F9362118EFA8F8C10597C11D8E3351A8803C2832961916C4057DC6CB48DCFC",
+      "v0_ldt_key": "895E703E6017716499E617F2915C4655D25840D38CCA27E32941FDD82AA3FC2C2CB3B7F29444E96F7539B50951822CC56C137287A863FAF9709AFDF470392155",
+      "v0_metadata_nonce": "33920520D06381D8CB88C56A",
+      "v1_metadata_nonce": "D2AC980FCF2027E5C5317F6D",
+      "v1_mic_extended_salt_aes_key": "3DBE8FC106A237FBA6DDACA6EF045882",
+      "v1_mic_extended_salt_identity_token_hmac_key": "7FCEB04961C81354ECB3B8C933C0E57441EE02244615DC091690D34B85730038",
+      "v1_mic_extended_salt_mic_hmac_key": "B97F39D53AA3497347DC3D7DDB5F6399C3EC78A97844A4B1B6CA33D62A5610CF",
+      "v1_mic_short_salt_aes_key": "BD67C930144A6D49052DDD3100DEAC8C",
+      "v1_mic_short_salt_identity_token_hmac_key": "83741493B76DCB32684A90514D76E06AEFA48BF6CCA7E5FD88F62A21457D6B7C",
+      "v1_mic_short_salt_mic_hmac_key": "9C2C874DBFFA6A5DB88D8FAB3BC2E6B8453452A666407A970AB09586A729F64B",
+      "v1_signature_identity_token_hmac_key": "CE915CC5776983A0FA0D9952A2DB5B9362C413351C147E7A060161B94F668AE6",
+      "v1_signature_section_aes_key": "F5E624240876543285520E1E8E74F795"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "ABA8038F9AF71FC6C1DA0AA4",
-      "extended_signed_metadata_key_hmac_key": "D65DA42911CD5ED334C530F869DB03C6614CA35E6FA866D9E38E69C777ABB6DA",
-      "extended_signed_section_aes_key": "A017DA593FF63BDF6FE6F77ECFD2AB5E",
-      "extended_unsigned_metadata_key_hmac_key": "A38246B6BD7F50873ACB4C04E0EC64FC189F64055D297E0965D98EEE19173A10",
-      "extended_unsigned_section_aes_key": "6727FBCBDC7B6C29F3ED05346661ADD6",
-      "extended_unsigned_section_mic_hmac_key": "E5DC924147B60D7F39EAA86AD504DB3C37230DBF79EDAB9005150135443864BF",
-      "key_seed": "80FCB0995D122213A13491854547AE8DC1F9DA0FBCDC425EF8CAE2F1621B5123",
-      "legacy_ldt_key": "791FFF186FAB641481FA4F9CED1FA84BE86FD44118214C4D911C7FED6E72367DD7E7D516CA45EBA6F39140D735EAE911C9FD41FF64AA5A1D6FAB6945EF95ACC7",
-      "legacy_metadata_key_hmac_key": "FB0D2011320B53ADC656F21ADAC39599A84660CC06E01E6BF71FA3A13EC7017F",
-      "legacy_metadata_nonce": "65E4371B55813FF7B1EF282B"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "11F3",
+      "expanded_salt": "08286FAACBAC794BF268D0733E82E545"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "96661B2739A97E383181D6025942488A",
+      "v0_identity_token": "DFB8131F26DD0C193982BBDCB69B"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "8527",
-      "expanded_salt": "41DF3C96FD61381D21C6B33C4ABF109B"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "4218",
+      "short_salt_nonce": "F43CD28A64290DCCB5ED553A"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "D3CFD17D054FAEBD7E7FC121915090B3",
-      "legacy_metadata_key": "788B30E87219D982203B007557BE"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "2A5D5B1EE6E62646232D1ABECBF33804",
+      "derived_salt_nonce": "2890730B9A82A68AF81B88B2880E0CCB",
+      "derived_salt_third_de": "C493E8AB86C2BD5B5B7EA619D2E0E92F",
+      "section_extended_salt": "3BC347425EE1B8962BF6D5D340D53C9D"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "29F63E30C4CA477654EA633EF851EF2C",
-      "derived_salt_first_section_no_de": "9F1DA62A3038226B6E1A0B7BA3AE6735",
-      "derived_salt_first_section_third_de": "078A761182B0E4C9FAB615B1B5BD76F3",
-      "section_salt": "3EFE885D2597608370E56E69A0EBBE08"
+    "key_seed_hkdf": {
+      "key_seed": "520437E77A58A99A75D71BEABD7D787B8C570335DE0E1D9C0EDA2659EBA786E8",
+      "v0_identity_token_hmac_key": "CD255D8326E90A105F6CE79A2CD291A2775D414DF5E1BB2C5AB1FD2AD76BB6E3",
+      "v0_ldt_key": "279DD7CCD679EA8406C1682148A1312BED0A0DB6414809A23079125258011134C5574FBEE4BF9E1B74E1F5D0F911B52B902B0009D0BB7100CBE9872648E3DA5E",
+      "v0_metadata_nonce": "6F23E1EB5657CCE8961D23DC",
+      "v1_metadata_nonce": "B745A0F923AA18DE10CC11E5",
+      "v1_mic_extended_salt_aes_key": "8274C309B074B3A1D80E0D77AECF4897",
+      "v1_mic_extended_salt_identity_token_hmac_key": "443DDC3EB2D51ACBD8C79ED2B732946408FE79CC0336A21C5AB774B2373864C1",
+      "v1_mic_extended_salt_mic_hmac_key": "972931866621E2B8DD7FB76ABB8797C18B26C74974ED471F501102645DC76ECC",
+      "v1_mic_short_salt_aes_key": "E240E070654F3794C276D799CB23D5CE",
+      "v1_mic_short_salt_identity_token_hmac_key": "9AA904713C1F1146CEACD567C6BC4BC7B84F92660426D1FD633A42ECD3D4196D",
+      "v1_mic_short_salt_mic_hmac_key": "648A1D4915934BE8EA5C24CF747E2A17A2CEF1E655D98B0877F3B1DCF04441B9",
+      "v1_signature_identity_token_hmac_key": "2B3210B9D391E4D2CDA95368B8B66572975348748C382588AAF4664E2EFFC2D4",
+      "v1_signature_section_aes_key": "9FC3EB118E5472E3D0E1806BC8EEB6C4"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "BC01",
+      "expanded_salt": "1945A447FF4F02934C6381466192FA3A"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "3F20992295F10AD806832D05",
-      "extended_signed_metadata_key_hmac_key": "74273A7D5F63F7E8F319716489804EF7B0693EE94831B24B94DEC7916B3A7E75",
-      "extended_signed_section_aes_key": "95C76B3AB159ECC6AA7AF3806E065EC7",
-      "extended_unsigned_metadata_key_hmac_key": "91ACB7139211ABCEEFD27C3371D8E8E3DA4AA1EF62DE05331A6A97671E4C1E13",
-      "extended_unsigned_section_aes_key": "A6A6E1AB38E8D1E1734D8E6F2ED72F55",
-      "extended_unsigned_section_mic_hmac_key": "81DF7CFC39442A2A065D795A1B836FE11A1E9EDA04DDF5C206A6C5E35DB37442",
-      "key_seed": "49F89927FDFEE5496504B4A130919BA8967C66E28BEAD4525DFEFCC704A18423",
-      "legacy_ldt_key": "C298669D7FFAE75E0DF93574FBCB1FF2ED135A0211256FCD6942981B832FBE35AD444D1678697DAF07F5D8A9E117CC86C3E57BB6BDB8689DF9CF969F097E68BA",
-      "legacy_metadata_key_hmac_key": "AF3EBA58151897927BE02FD78E68AAF90675CD8D556800B736752426ECD48764",
-      "legacy_metadata_nonce": "BED3836FD96FD34EB7FC9983"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "1AB857638876426CA721C616DE5BF65C",
+      "v0_identity_token": "F996D02678003CD71CE17004C2D7"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "BE30",
-      "expanded_salt": "66BC953964F357EE08DD1C7668A79D6A"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "FDD7",
+      "short_salt_nonce": "E6CDA2BF1E867E1C76A878A7"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "8C16CCF3C80E07746B1F8471932A1FE5",
-      "legacy_metadata_key": "74135EB8B0F379AAA4D4478ADE7F"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "8A985AC6265F3067E13ABAA6218D6758",
+      "derived_salt_nonce": "DA89867F71A70412D2B00D8DA0454838",
+      "derived_salt_third_de": "A36B229A20A2A972401A843F84FA8D33",
+      "section_extended_salt": "967D99CE3EE6D79AE98B8037056760B9"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "82AD47824247F00924ECF0A43621BC57",
-      "derived_salt_first_section_no_de": "C737B65B994BF8CD872CA41BDB8A53A7",
-      "derived_salt_first_section_third_de": "9BB8BB55E1DE5129A43AC0EA937B4430",
-      "section_salt": "72C9E759C172DA23B71FB6B0C6ED8CC7"
+    "key_seed_hkdf": {
+      "key_seed": "94830D89A2B314388EF7B63C3C3F1D9C5558F07B1ABFF28FC336FB5F41079FA9",
+      "v0_identity_token_hmac_key": "727F82E28FC3F96E5BFE00A479C96FD8419921516CA1ABC2FD42C373FE9E328B",
+      "v0_ldt_key": "64A40560C4ABFF49B54A893522F75B2DB2A21BE79B44E7F9CAC3AD0E8E5FAB91BAFA76B2264EA081EE92DD94B6B0AD11295BACC6D44C9B7224E975542FE837C5",
+      "v0_metadata_nonce": "E1FFED6F1FBE0A7086DB29FD",
+      "v1_metadata_nonce": "1E765544661B31503FB0CC04",
+      "v1_mic_extended_salt_aes_key": "B3773653274143AD72C95F3C01A71D4C",
+      "v1_mic_extended_salt_identity_token_hmac_key": "811D7EE92ECC15D14A2EB005C17F072BB331834EFE623F0E2EF5E042672995AD",
+      "v1_mic_extended_salt_mic_hmac_key": "025D81497467CE23085944DB9AD50A5B8BBEF3F5036E9750A8EE3801CEA18EB9",
+      "v1_mic_short_salt_aes_key": "10F24F181454ED4663CC803642768DAF",
+      "v1_mic_short_salt_identity_token_hmac_key": "6B42C12D243ABBB7F2236EAFFE0C1A8B1654E25E3CF6416677D3462DF7402062",
+      "v1_mic_short_salt_mic_hmac_key": "52EECEDEEF2758E036A9748E8BF06287F8C6E70CCDB6017C9961A97E64B26FD4",
+      "v1_signature_identity_token_hmac_key": "591CE684D72DA16E9168381A2A8167F409E83662AF6713F3F68D8B753DA3174B",
+      "v1_signature_section_aes_key": "240792A759473821A1D8CBF99AE54AA4"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "480A",
+      "expanded_salt": "73CA056DC334741E6E6567853BE9AC0E"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "80921E0D404179D9F04DC155",
-      "extended_signed_metadata_key_hmac_key": "B9B1F73D7FCFD25EB6A62B788032C71104926922C436A137B95A46D7B63C5EE6",
-      "extended_signed_section_aes_key": "85C1D9D14D09C0722DCFF12643CF2669",
-      "extended_unsigned_metadata_key_hmac_key": "F89C26264B27E1A265C60D69A20AE73222CE9E2F68E0F1D71C6DEF936C4092D0",
-      "extended_unsigned_section_aes_key": "43639BFCEBDF4EE61629156E04F92EA0",
-      "extended_unsigned_section_mic_hmac_key": "AF81B5A56EC2D2541E46AB2F9F583A48155A2F9BB272372E8064E446BE8A182A",
-      "key_seed": "286E2029CF579A26495BE93926BC663732C46DE57049C1A5E2BEBFE03A12A096",
-      "legacy_ldt_key": "ED36084F2492B19D07D5A09D174C16A01B8AFEAF27C86EC145CEAD08C8BE67A80BCFF79C40F7E63019B81821A715DFEFCA273F467A176C61D7A024BC245A6B8E",
-      "legacy_metadata_key_hmac_key": "2A48789FF8B93282B4D483566E9FF4DFB65AF5FBB869BAEF6C408465A310879D",
-      "legacy_metadata_nonce": "6803BD4BD4D435930449C17E"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "C68D9064D5CBC769AA2ABBB035248851",
+      "v0_identity_token": "0CF9B87C144D6D444DBC98D3307A"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "3353",
-      "expanded_salt": "C86C20A42866236AB1CD9F1E815DDFD5"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "A5A8",
+      "short_salt_nonce": "20358E029A4BA3EF97796505"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "95E24D5EA5EBA3152427BF48A3781BDC",
-      "legacy_metadata_key": "4FE2300F2CA3C469959DC0A5510D"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "002B67A8C992EAEBCA8259FEC28289A4",
+      "derived_salt_nonce": "F202A0CB1EE7D8FCC9BABB9EB6304EF5",
+      "derived_salt_third_de": "F44C487244901DE5D96ABFD481BAC4CB",
+      "section_extended_salt": "69167A8D033BBB5E257E729AE62ED6E7"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "98C14D785DD885F3D039C8F84EB65B81",
-      "derived_salt_first_section_no_de": "94C66E08758D83DCE457073C6B911483",
-      "derived_salt_first_section_third_de": "1BAED8E606422F18BC5CCD3F649BAEE6",
-      "section_salt": "9D82CCB568AEFB50C4D1061D29220A63"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "05B2831B07DF537F173ED291",
-      "extended_signed_metadata_key_hmac_key": "D120FC54257B1EA765E2D210C6F77D5066404601BBAC598725BDE993CCA9D90B",
-      "extended_signed_section_aes_key": "52605A8345524539B05A4DD5DD7A51EC",
-      "extended_unsigned_metadata_key_hmac_key": "ED44D698082772261DB4F47F37999E5474523A75A5F046A671B9138086A31B7A",
-      "extended_unsigned_section_aes_key": "8A0EC8AE2F2177300A418614507F0F27",
-      "extended_unsigned_section_mic_hmac_key": "E7456EDF9F5B5F614FDF8C88BF45B897307E2A48405FE3DC0F4B6966E9693C04",
-      "key_seed": "4F1E079363BF2CF5C475F0D927C36B81FBEC8B4F323A0417184414914AC95ADB",
-      "legacy_ldt_key": "BCB0EFC03494AD2260CE8925A140E58FA12FF5C6D59B467D6C4C6F616D0DEE6212DF667F5144F29E6DB4F04443EC2143A429F49189D4B96B077469D12FB609B7",
-      "legacy_metadata_key_hmac_key": "E43D59616850DA473A9820F0F4215FB23E0BCBFF91360EC9A8DCB2166DD360FF",
-      "legacy_metadata_nonce": "EDE3166B65C4FFCE477B0344"
+      "key_seed": "98EB553FA5F60CCA72B5C7DD2C98E4FF839C728EEDA1A5B78C182CB94C58C714",
+      "v0_identity_token_hmac_key": "5D87D3A84F06E24BFE1E22E7E5FFEADABF6D583587B2615DC39117568E1C958D",
+      "v0_ldt_key": "C676CF2C50219632BF484FBCDBC3B6ED9BAF0114B03B7EDB3B4F6A8A3C977F07A72B19F74C71F800B65FBDB5147EDB38AFAF849B82BB7F9717DF1C1ED206F534",
+      "v0_metadata_nonce": "824779F6CC15BF01C3BDB03C",
+      "v1_metadata_nonce": "79B82382BAE70FE9F2C3EEEC",
+      "v1_mic_extended_salt_aes_key": "A1DFA7F844D8357A3E0E554770F26517",
+      "v1_mic_extended_salt_identity_token_hmac_key": "EBBE559CBF3D41AF04F599C3AC66A57C14A7983BB59805520E0F26BECAFB04FD",
+      "v1_mic_extended_salt_mic_hmac_key": "3B571385F9A85C437D7A0FDEF9193B643FB30A82C94FF4BEF03499E8FF8DE773",
+      "v1_mic_short_salt_aes_key": "7945B907207E26989E99CF22D75C5134",
+      "v1_mic_short_salt_identity_token_hmac_key": "F4C50FCBF4772A730A1A962A61DBA99993BC4E1FC823B09C591889A07B0848EF",
+      "v1_mic_short_salt_mic_hmac_key": "5BB027194D3274626B9F12F33EDF3894F48C74EC8643E979171F311AE56C6196",
+      "v1_signature_identity_token_hmac_key": "4606A5AE3B1A042E620FAE09883B0E2B60F2CBA0F55243C9E82BE0BCBA64AA4C",
+      "v1_signature_section_aes_key": "62ED38EF19133F282C78AD3BE7493419"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "D3B5",
-      "expanded_salt": "75DF7B4DAE71EAA2531A2A66BD67CC34"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "D77E",
+      "expanded_salt": "7224B61774E1B0F193E6E2B9C6828D47"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "40D0E2FF3CC0401794857BA46D4018D6",
+      "v0_identity_token": "2D00EC3B8F0F5E82C0FDF2ABEFE0"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "DE54",
+      "short_salt_nonce": "FCF5569BBB730792EAC492EB"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "24921B2F2ADDC07DE0E4818DF0FA218D",
-      "legacy_metadata_key": "F36505BC7D272E0A61E6F825CBCD"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "FDBF480AFE38C1A3B518C9E4E37DF73B",
+      "derived_salt_nonce": "D0AB883D19A80B9A9B0F71FCB3BA9C4D",
+      "derived_salt_third_de": "559EF43087D81A97644A1564939C902D",
+      "section_extended_salt": "7D6E587DCCB086EE382CDA8ECA378555"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "A188BBEE6CBAB5C972EE985C21ABF21E",
-      "derived_salt_first_section_no_de": "5170EA5075FD2D3346430FE8781E352C",
-      "derived_salt_first_section_third_de": "A7280620B2E6907C765DB1B005C56485",
-      "section_salt": "C362D9DEEAB32835AADB2713E86AA3BE"
+    "key_seed_hkdf": {
+      "key_seed": "CE5928B1C8F46246E5C922C80DD9558FA8AF4F213E21AFB8A99B0FEAEB9B56DB",
+      "v0_identity_token_hmac_key": "0899E9BB8C98AFAA6AC98097F66B0342806FDD2B90676D2C20107CD6947BD3AD",
+      "v0_ldt_key": "4134C4E3F15361F79134E2FCF5ECB398EE7E4ECB43A839622859BA72CAF476732ECD67EE3B354236EB64ADD7C0D2E0CCAB5E051D12792F39B8846616BF4A84DE",
+      "v0_metadata_nonce": "8566A1CC7A91F6A022B999E5",
+      "v1_metadata_nonce": "396A0AA737EA6C93E27FE7DA",
+      "v1_mic_extended_salt_aes_key": "800E76656036F4A04879808F114BAE67",
+      "v1_mic_extended_salt_identity_token_hmac_key": "FE24751061A9F50086411F85352EFF5B5DF0E1247998A203B33CC609EB63AB50",
+      "v1_mic_extended_salt_mic_hmac_key": "F095007E76CB193DA11718890BE7D735C9B57DD99C85E597893E9A51DD15A93E",
+      "v1_mic_short_salt_aes_key": "52A881282D67F6ADE798591223985FF8",
+      "v1_mic_short_salt_identity_token_hmac_key": "60CDFB8EDEB884516A7A3D8501E3357AFD42A77CD5FE6578D2F5957FDA03F5CB",
+      "v1_mic_short_salt_mic_hmac_key": "8555D441A574D3375E50F9A450022F4A982E8D580982908C7B6D81C628312968",
+      "v1_signature_identity_token_hmac_key": "E850671925AB258049D3E3A4822FD1C78A4D80F556FB92EB7D7B5DA01F5CE641",
+      "v1_signature_section_aes_key": "8A1ACA177E160A79924EB4D2C9435F98"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "AFB1",
+      "expanded_salt": "6D35C770B1F09DC2DEE7453C22EC018C"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "BD6393FDA5A002CF968AD7F9",
-      "extended_signed_metadata_key_hmac_key": "6CAFF8DE3681C4445926AF1CB615B8A7EA39687BD24597384002F0EF1BFE7FC2",
-      "extended_signed_section_aes_key": "03A107EEEDE319EC3C14B287533F26F9",
-      "extended_unsigned_metadata_key_hmac_key": "48107D08007213F545238626291ADF3963F71D64A7E42D859B77F5B5FED3DEDB",
-      "extended_unsigned_section_aes_key": "918832BE7D5E3E5DCB8D6FFCFC53324C",
-      "extended_unsigned_section_mic_hmac_key": "A0E2D0EE71984DD82D3AC1BDDD030913D4BC9BD276E71E2BEDB57290B0AD652B",
-      "key_seed": "7B1FB28E5884B2B7DEEDC2620A87E729C1AA5E3810C56F59FAAE375BA22750D8",
-      "legacy_ldt_key": "00A9B6FD99AF13743AA3B6E31381E4321EF15F21D4DF1D8B904307896ABB7ACE185526619BDE388E462847F178F32439245D8A4AD45AA26759DAFB751B6BB544",
-      "legacy_metadata_key_hmac_key": "27F5E15D51C393501B5C9AC3254A8FADDE756110C8E8A952DE2FD58AB3EFD16A",
-      "legacy_metadata_nonce": "DEC66637548279AB556A1219"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "23A208FB72F76B27842D0EF4590681EF",
+      "v0_identity_token": "3080D30C82A0F6AB7A5FA83675C0"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "781C",
-      "expanded_salt": "28C257CC0BDEFCDC63D28B14D38A5E9B"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "96AC",
+      "short_salt_nonce": "9BF830CCD7DB20F7A3115702"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "DD89372C6BC8038EF866314AE0015DBB",
-      "legacy_metadata_key": "D0D0C08326310877A07002234539"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "668E2A14D9B304AC23D0F56CDFE0726A",
+      "derived_salt_nonce": "866E7A3B3D3F0A094F8FF58C221754BE",
+      "derived_salt_third_de": "137E0CE6D82B8DE9AFDFBE0DEB447B33",
+      "section_extended_salt": "4AC4230A5F3B911F69A07DCE728586E4"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "425882E16247B61965C2CB122571CFCF",
-      "derived_salt_first_section_no_de": "0C8429B057889FF103029651B0178DB1",
-      "derived_salt_first_section_third_de": "61F70C6EA361790E715295EB500A9DF6",
-      "section_salt": "2BE5976B6054DC8EC9B517B03FA25754"
+    "key_seed_hkdf": {
+      "key_seed": "A036E6CC7FC866CA0BE5B4C017CF177396E403F7D5CA8F94C6FF9DA7C010299C",
+      "v0_identity_token_hmac_key": "D85A9EF927E02B5A0ECEF8B1949846AEC6D7FFD049A465288F8BE406E63EEBEB",
+      "v0_ldt_key": "C95083F599D7D994F72142C844822652EC9C914F132AFB2CDA4C7BA88B4235A76CA1BC44FD42CAFFAA7A553BB74E93F30E4E873DD1348F99D0125577D06274F8",
+      "v0_metadata_nonce": "23D7F03405EED43619F8A02B",
+      "v1_metadata_nonce": "ADB6676920779A3F6D24F542",
+      "v1_mic_extended_salt_aes_key": "B3A11A9DDB232EC9634B28EB5DD002D7",
+      "v1_mic_extended_salt_identity_token_hmac_key": "E9E49F8A8FE50E066C83BAF02D9277BBAC5EC629D16FD5B02F90E04EF76E682E",
+      "v1_mic_extended_salt_mic_hmac_key": "0E5A3BCD461378A07659FBBD5E22AD8A658D3871FDA1B94111F5F468298A8D74",
+      "v1_mic_short_salt_aes_key": "C3B2251C9ECEBABBF766D5185B5F18AB",
+      "v1_mic_short_salt_identity_token_hmac_key": "ADF35872686CF343B3CCAC85D02789B9AE1F9D30A8DDCFF90EB87B17232F928C",
+      "v1_mic_short_salt_mic_hmac_key": "EE54C5BB9C1BFE150A389B0A87E4181694721FA52C7DFAD2012375D95356CDCF",
+      "v1_signature_identity_token_hmac_key": "D1E4984F45D313CD9FFBC84344861B3E4695CD514BED3150C16533E8D342CDE1",
+      "v1_signature_section_aes_key": "15EB95365271D866D98BD422014205B1"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "9BCE",
+      "expanded_salt": "CAB8E837BB60A1FDF5F164F1930B0E8D"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "22E652B9D05090C3CD17A386",
-      "extended_signed_metadata_key_hmac_key": "B1F52377B751B3CD5456DF79607FF00F47388F2063BE559A151B7B9985030D9A",
-      "extended_signed_section_aes_key": "6C6A2C0C4FB7B10E71D4B9C90DCF97D4",
-      "extended_unsigned_metadata_key_hmac_key": "AE15A2095E41AFC7E33ED5A4F1210E36BC8B08761DCA1A50C8AAAF4D18804096",
-      "extended_unsigned_section_aes_key": "FF93EDCD2458BD330CB8F6461CB5F3FE",
-      "extended_unsigned_section_mic_hmac_key": "2ED5F7C30A3EBF854A6EC62863D9C22CA14BB8B3EE34C2E4CA48AA1972E6D74D",
-      "key_seed": "674EAC48402573CBF8D9C71F862F2F773330F1C8777B86D6C1C054DCBDD7F5BF",
-      "legacy_ldt_key": "C98FED8E0671B3B73E31019FACA33127F2EAE11BBC8D6D503E7C4C36DCDF42F8711EAFAD6E64AE2EDB6D2870203B64EF74388AA0FDFDEC9372AE06E4B14639BD",
-      "legacy_metadata_key_hmac_key": "C39A23027BA99068BB113712449125BE8B4E891C92B9B822EF14FEEFF64D01F3",
-      "legacy_metadata_nonce": "03759271308F135FCD00566D"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "42C1EF92D5E71149BCCC7B3B2237410F",
+      "v0_identity_token": "C3B8624C9BA609C1BDCEA9CF373C"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "F14E",
-      "expanded_salt": "1F35B6CAACCB4DABC88F014F186FD58A"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "8466",
+      "short_salt_nonce": "AEFEF9A66BCC84F652A6B6F8"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "4DBA128B48257E51906815A44EBB31C8",
-      "legacy_metadata_key": "1BCB5E77E87246FB145B53599415"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "62CA3E3C25D90BC9601B292D3DBAF36B",
+      "derived_salt_nonce": "DDFA02449C05C95B5D958B876CA189DE",
+      "derived_salt_third_de": "40EAA779A52CE6249681C6C2A6E33839",
+      "section_extended_salt": "9054B345A1B790A68CFF7CFC52D75525"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "C210EEC7E5F37ED5DD585CA78E4F461C",
-      "derived_salt_first_section_no_de": "512F88EC4D2C1D98155A79C50766881E",
-      "derived_salt_first_section_third_de": "5C51EAFD14CCB470D455818FC233951D",
-      "section_salt": "1F6F7DCED1EAB1B9AC53C24BF29D9E74"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "6EAE4DC82AB5941A1E09FEF9",
-      "extended_signed_metadata_key_hmac_key": "8646A00AC8922FCCC259807B3B9EAEC556701927DC03ACADCB1DE1517F2C6C3B",
-      "extended_signed_section_aes_key": "DA63146BE1A81A806C6DFADB9076E6C8",
-      "extended_unsigned_metadata_key_hmac_key": "2F437D2C9BD771D02A87C0E17CB053958F62DC13559413C871FC01B4E5FB496E",
-      "extended_unsigned_section_aes_key": "9366D940C998995D7846B28F0FCA05E1",
-      "extended_unsigned_section_mic_hmac_key": "092BC3A92351F4673A37BFEC4BC62F8FA042EE9E5ACF391B2668142015570658",
-      "key_seed": "6028F5FEE8F66DC80D4F1C4155D7DCF9495C4D98DD481AB2BC30C9FDF0672A2E",
-      "legacy_ldt_key": "AC718CAC29033FF1FF300BC44B99BFD97F5419AE42BD79837E19D52C115C01FE32F0B2AA678D756B088BDD276CF6402B729A69F2689FFF6A035B5C9B6363A49C",
-      "legacy_metadata_key_hmac_key": "B0A90F0DBC1ABA5AEE0F4A8CABD5764EBC83F3BD618656BDA509E594A91E4D52",
-      "legacy_metadata_nonce": "4788CA94F7A0AB258486D3F7"
+      "key_seed": "71B63EB56B07CBC9322E666347CEA44BC4FC6344696D7D5F879E2B2538C6C0DA",
+      "v0_identity_token_hmac_key": "CD3092A8E4B648FD3DC6C80A449FB248F05934A116A775F6C70A9D5FD07E5A0F",
+      "v0_ldt_key": "3C2138F65AC20985A4BC677B4CF36F9D09B316E25506AAFFC407E2B72468B6BC71961D5958D4846793392C7389053BB7C248BC6E47269E9619098FB55D38E254",
+      "v0_metadata_nonce": "459816206C609FE86EBA6D7F",
+      "v1_metadata_nonce": "14B7E15A016D8BC3849BDE28",
+      "v1_mic_extended_salt_aes_key": "C6B3C93BB0063BB443EB0A4AC6EE4BF8",
+      "v1_mic_extended_salt_identity_token_hmac_key": "0D189A29519293EA6995D6658527ADEDF51CFDD500F4E602BEDC242740308898",
+      "v1_mic_extended_salt_mic_hmac_key": "2D65B2CD24E315B3B60237770E5F14AEE994E718C023B32BBAE93F00A49160CE",
+      "v1_mic_short_salt_aes_key": "C886A50B76F6FA70D653F47D58E32DB5",
+      "v1_mic_short_salt_identity_token_hmac_key": "73D1231C70DF32D46609A2BCC11E0F1C09298F772ED3C1ED61F3473F6B0D109E",
+      "v1_mic_short_salt_mic_hmac_key": "8BA22F5A56E0C7D945A43286C432875B2A8BA4D340B71FDB32A5078BACF81D05",
+      "v1_signature_identity_token_hmac_key": "8F3E043B38AD640DDB32F836B000FEB140E293FEFFE22B048DABCC6AD0D77EB8",
+      "v1_signature_section_aes_key": "F0B1C0499D7F735D789BF19C6B27BDA0"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "18BD",
-      "expanded_salt": "1702E72B0270FAB301ECFAE901FEB45F"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "02DD",
+      "expanded_salt": "0559A2DDCA64BD2AC3BFB68B58D8D33C"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "64E3B4DD353BD25E6A6DB0C693D3A38C",
+      "v0_identity_token": "615B490B1C2BD88CB66FDB5020B0"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "C0A1",
+      "short_salt_nonce": "FC5A9CD5C30AE35F2439A989"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "ED1208D4B3435736B92075B4E2DFBA3D",
-      "legacy_metadata_key": "AF7F068F3FD3FEF1B9ECA66F33A0"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "C7C542850D51FC65A3748B637D4BF2B6",
+      "derived_salt_nonce": "E7BC6A195FD35BBBBD36890C84DFD6BF",
+      "derived_salt_third_de": "08B163B3C00BCF2302E50BCD54DE256C",
+      "section_extended_salt": "B045F1FC3AA558F7DB3436B43FE3703D"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "B27EF32036E2AD671536F7CDFCF3D8D4",
-      "derived_salt_first_section_no_de": "3841B50190E59B72F7F47281ED980317",
-      "derived_salt_first_section_third_de": "1658D434AD2BD8190467E009476C1F7B",
-      "section_salt": "F9172022012C774D058C8383D2D4235F"
+    "key_seed_hkdf": {
+      "key_seed": "4E0E7EA2FD1505C0917AF77650AF066B4CA785C71E94EADFF9179A700B871B99",
+      "v0_identity_token_hmac_key": "F03B77CA61C2E4F3CAFEB1ACC40A5B6EC7DC91E4EA5A453603A31C7C15398FDD",
+      "v0_ldt_key": "E2F5B9301BDB44BDFDDEF6A186AED98E40E044EE0246823157ED03BEA1BDC2416BA43B7579E762141DB967AA5AE4D3A7B29E2DE79225CF58976B1D0A848253F9",
+      "v0_metadata_nonce": "E40E15488FD763B95BE80EEA",
+      "v1_metadata_nonce": "E27F419A42791059D8ECD258",
+      "v1_mic_extended_salt_aes_key": "DA459644331D2AB5BBFA36F4D7FF3C3C",
+      "v1_mic_extended_salt_identity_token_hmac_key": "9435C376887ED2BDBE06AE6E216B20F3A5F513D5C73CF7A464FB00C96AF42156",
+      "v1_mic_extended_salt_mic_hmac_key": "EBA1ADD68E7A7175C6EA4BAC7C9DA02408DA8B42B079CE25883E286BE7D027F0",
+      "v1_mic_short_salt_aes_key": "B4FBAAC9FB28B31447917346BF960A6A",
+      "v1_mic_short_salt_identity_token_hmac_key": "8261D61215141838D5F9BE7B561917B4BE89A7CD558403E146FD5F9FDB232308",
+      "v1_mic_short_salt_mic_hmac_key": "776E5708C049603DE6CD23D46F5867BD4CFCBD68865DA033F76284C02248C5FE",
+      "v1_signature_identity_token_hmac_key": "8A10585EE8E9DE84E26A0284C4C0AADD7726DC54FCE6FB2196C6314661B8D765",
+      "v1_signature_section_aes_key": "1324A78CBC23F567A880B1975F0ED40C"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "10EE",
+      "expanded_salt": "FD6A9A6E112D7D76ADDC2D8BD40B7E29"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "F7BFDC47005FE9892DD1A940",
-      "extended_signed_metadata_key_hmac_key": "9864300E87DD5F34284AA47C00381B36C69813423B6BFCB14250715BAFF34259",
-      "extended_signed_section_aes_key": "93C04B6BA7CF6F4A9B27615C900086B0",
-      "extended_unsigned_metadata_key_hmac_key": "4CF4F78468301DC365A2C27BF4D544CE525F3CACDB471868FFCFF896D896155C",
-      "extended_unsigned_section_aes_key": "908CC6D593CD9401D47CB850BF4812F9",
-      "extended_unsigned_section_mic_hmac_key": "68283A8DDFC70B5B523E8ADB8DD98C209DD458140E3ED6C61509D4259DA8D4BE",
-      "key_seed": "F04F58F4C6158F4E05EABEADADC3453F3A75FC3B6D331B45FD5589D0B16D5F1C",
-      "legacy_ldt_key": "AEC307B3A752EB1C5FDFC64843A93ABF5D2674A9505C736D131B1C84BB89A17D09CAC89BEFB1274B56262CAB56F04EB013A4475B518170C321AF4DFEB3F3CF27",
-      "legacy_metadata_key_hmac_key": "2C16FE1F32E5077BBB9C659C55BD9A77600FCA5D45FCE0430127514E513973A5",
-      "legacy_metadata_nonce": "628F79EC288BDD800A4ED6BF"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "A0879258EB4E46C9AD228B720035C301",
+      "v0_identity_token": "2CBAAAF3C96B0F18CDB02D332249"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "94A0",
-      "expanded_salt": "9201A2F2DAB3488533230CD580588267"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "61F0",
+      "short_salt_nonce": "615C3235852E9D1CD0063CE4"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "735ACB0A5B5A47743F22EAAD1F64F8BB",
-      "legacy_metadata_key": "1DD0B9A032ADAB96501E851CD707"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "6272F7C2A41C3A15288E20DF6298EAD9",
+      "derived_salt_nonce": "E2F217DE0828333D9F49E122636431EA",
+      "derived_salt_third_de": "74DF8645F7FFB751482B84D2B291D3DA",
+      "section_extended_salt": "189063CFAF8E1915CCF88CB306E65B16"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "64C97FC6CB9A499116614733309E473D",
-      "derived_salt_first_section_no_de": "3B2B9FCD5DEAEE859EFE8B7C2C8FF150",
-      "derived_salt_first_section_third_de": "38999F9FDE0BB4A02621675A265C0F53",
-      "section_salt": "E152E9CA82C846268793A13AC2CFEE01"
+    "key_seed_hkdf": {
+      "key_seed": "0C4BF3FFE9CB060E30C97ADD9DC99E4AA2E6EB6AEC723BF8BDAD409051D3BD37",
+      "v0_identity_token_hmac_key": "ABFB8A91549E636D07DFB7E57A817C9C2891AA6E626E38A3EE063248CE912F86",
+      "v0_ldt_key": "93A179BDCCA21151352CEF07195B0D8F8B7018E75078A0E1B99F7E4A0D239CC08059CA82EF69848E5E3894885A4EA80B7B664005BC6C1EAE6B93094FFD2129BF",
+      "v0_metadata_nonce": "78FF83DFB55958CAC1F5983C",
+      "v1_metadata_nonce": "7FB9A6EDE0C5D91DF8E567A7",
+      "v1_mic_extended_salt_aes_key": "090D4308933E3CECCC986F09A37CF87F",
+      "v1_mic_extended_salt_identity_token_hmac_key": "5A8BC4F150492B66307B1DDB38AE19E594AAA280FEC8EC9E79F4B1E3DEFFA525",
+      "v1_mic_extended_salt_mic_hmac_key": "1F8DCC645A797A805CF72D23FD6DCF3243735737BE0E11870614BD547C56D3D7",
+      "v1_mic_short_salt_aes_key": "E49F8D00B07699825E9F82D7A668F8D3",
+      "v1_mic_short_salt_identity_token_hmac_key": "9AFBB83B9CE4C2A0A1F865B4386E87BB31273D46BC1F5C272D1F2F098F46C579",
+      "v1_mic_short_salt_mic_hmac_key": "A68EDA18BF3D5CE9CFB3EEC57922F0E4EE0DA69C36F353C9302976ACA66CEF6E",
+      "v1_signature_identity_token_hmac_key": "55602CC889D8C5B0C8E2F37149831B6C97DB377B2168A8A06BC667C8D017AA29",
+      "v1_signature_section_aes_key": "9EC0098FC8386B2260A44033CAF969FF"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "FAF0",
+      "expanded_salt": "349DD2805CAD12CBD795101D47403486"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "0FAE226D0C037417DF2CC8F1",
-      "extended_signed_metadata_key_hmac_key": "B03AF5D25B64EA34DEA4A04B30E234D0C45D0844AD3D76E9D4BDF72B9E1634EF",
-      "extended_signed_section_aes_key": "419A9B17073BB3A7DEA566D14B322675",
-      "extended_unsigned_metadata_key_hmac_key": "8CD58449D0AF3398E3649F977A8FBC55573464F4E25818E8ED94230A270E5B42",
-      "extended_unsigned_section_aes_key": "52CFA465829A0AF17FCD496C302E7094",
-      "extended_unsigned_section_mic_hmac_key": "B098BCE7ECBE5DC667C7DCD99B186B3294C442783741A2AF02973B9C1CEC3E7A",
-      "key_seed": "26005EBE2B54878D7D51E8AE0A133F601F845706CFFE22EF5406E848D743CDBD",
-      "legacy_ldt_key": "86F94EB0B3514972F54E4B072694BE295FACB1C9B1CFFEC97F7AEEE7E7A4B2EEF8D83CF3F46A1046553F0FBA739B512BEB70E8294AB3A8E9D354DC15904F8FE4",
-      "legacy_metadata_key_hmac_key": "6C5F27B8758F150413A0E723AD2C87A89FC8E0922D6AFC9C4BEE7D640564F20E",
-      "legacy_metadata_nonce": "C9E22F8D09D4F4593A61FE7C"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "901F6D216BEBF74D2D0E46E033634FBE",
+      "v0_identity_token": "B297A12E68BF33DB8ADA62316B4F"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "35EE",
-      "expanded_salt": "6DD6FC15DC3871D1664A44C78A886FC4"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "44F8",
+      "short_salt_nonce": "BA72171618BC1AE229E46B79"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "D1B46D2631F70EEDEB29AA47D4437686",
-      "legacy_metadata_key": "1416021C29E743088B02B41B571E"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "F3B0AFF9A87AD9F157FCB595A0400440",
+      "derived_salt_nonce": "58B7445E6B36BA40A10BA36B5C1E2EE2",
+      "derived_salt_third_de": "087236B6E0C93C6153A061CE6A145D2A",
+      "section_extended_salt": "E574B5E9485189F792010872C55A1A16"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "9651CD6607E54363129153CA5315FB86",
-      "derived_salt_first_section_no_de": "0A54495999A555463622F3875F8B0589",
-      "derived_salt_first_section_third_de": "E47744FCFD5DC037621555A11E000988",
-      "section_salt": "5EECCF54ED89546D031A0103EDB17A2E"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "57F6D756297E1693FB8A1D4A",
-      "extended_signed_metadata_key_hmac_key": "1A7B18552F2818F7B17106C0D32337D33F69903604CFA6807DA5DCF2746108E3",
-      "extended_signed_section_aes_key": "194961312A320FBF060414DB8650ED9A",
-      "extended_unsigned_metadata_key_hmac_key": "25B117B4EE587290C4F7CEC17115E104FF96E7EFED13033C1DD4705BAFF3694A",
-      "extended_unsigned_section_aes_key": "B5E64E57E00D022C4A1ED7F1841B5A1D",
-      "extended_unsigned_section_mic_hmac_key": "D499BB4DA43B7ECCB3499AC1B5B0E251AEC407EBCA917F0EB08838BD618476DD",
-      "key_seed": "D3457A18BF634AD2166223C38B4A1BD74C66DCCE809D410CAD1218F56EA363D4",
-      "legacy_ldt_key": "D32BB668B2AA256B27339F45F228672FD51BCD73C3C0C044E9A9E27E282D867BF2A7ABDE2300452BDBE49B79C3457F65F38186A59D9B88FD5164365212DAB6C6",
-      "legacy_metadata_key_hmac_key": "10A19D2EEA33F510D969E3A7036D1F288079A6D242EA2B1376DBEBB02F49C503",
-      "legacy_metadata_nonce": "CA9A9DBF70527FBBDD94E59D"
+      "key_seed": "ECE5B0557E2B77C7267F12D916A058AE7A989E2EB7E3DDA012EE4615DA3E072B",
+      "v0_identity_token_hmac_key": "51A45AC67195344FA0FF0F6B45A555E0113EF7A976E1371C38ABC1092A739AA4",
+      "v0_ldt_key": "0C857A8818273B2087858DC361082FD28F1A526DC3C8C17524294AE9C4B4369FE678F3D1ACF4AAFC6D2CAAF5E9E20EFBB5CBB6E4D42BBA520D244762E3569B15",
+      "v0_metadata_nonce": "2397ED85E3AA65D707364D55",
+      "v1_metadata_nonce": "81FE5070C63D916C065E8960",
+      "v1_mic_extended_salt_aes_key": "02F8434EA7ED25A95997308A704DBEAF",
+      "v1_mic_extended_salt_identity_token_hmac_key": "DC250FE0C182459E9502568122871806C76721D8EC15EA858823C6943A22FAF1",
+      "v1_mic_extended_salt_mic_hmac_key": "C073CA0A8FBFA53DE6D573FF2C4DC981C8567DCD41674F666A9B6F2A3E5DDBB8",
+      "v1_mic_short_salt_aes_key": "3CA3A347F198B2771543DFDA16EBF066",
+      "v1_mic_short_salt_identity_token_hmac_key": "526F4A2BADF226817B6CFBECAC622C76ECB29F08D94E76A19E0E5AA47E60BCC6",
+      "v1_mic_short_salt_mic_hmac_key": "B1BFA6EB1CF610F7ABAB39846A4096A201400C96FF365FA28DC17E21A10B4843",
+      "v1_signature_identity_token_hmac_key": "CDB40BBD6E8C6F09EACB8C49F6620036AF9EDADBE2C4DDDF4F1576BD16E60D97",
+      "v1_signature_section_aes_key": "89D7606A0A0A3BD01ECC6C5753B73976"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "00CA",
-      "expanded_salt": "B2D541DDC7E007FD5D9B394E66C33319"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "9F8F",
+      "expanded_salt": "C440F5F693A391089F493578A3246EE4"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "0A4F31037E77231B0B51C8A447E3E329",
-      "legacy_metadata_key": "03E5B8D922677CE5DC011BAFFBEF"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "08286E0B99391DA30EC2274DBB00E849",
+      "v0_identity_token": "D00DCF154C31C752B929FAF666BD"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "E02D",
+      "short_salt_nonce": "622C675E322ED508F3D36C63"
+    },
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "18DACA43407EBFAC3F779E06A67455F9",
+      "derived_salt_nonce": "F82E736F62D198165C6D347EA4E39C6E",
+      "derived_salt_third_de": "23619B13FF4E31FCACFD529756674E09",
+      "section_extended_salt": "BEE23DC5EBEB75C788B766ACEFA64377"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "9B1DCFE8149C1EBD905A5F2B71341C05",
-      "derived_salt_first_section_no_de": "BEF89573FEA45BEE4E9BF988824904F6",
-      "derived_salt_first_section_third_de": "41E32C74EDC315E7929DDA7FBC91CC8E",
-      "section_salt": "EBF3C70C17A01679A563A39FA46D7F0C"
+    "key_seed_hkdf": {
+      "key_seed": "9E694C7731B4C8C077DE1E5233B3F6E8E47E436D920D8C4E4BEA39FEF681978E",
+      "v0_identity_token_hmac_key": "1A6329E5D9948D1DA2B988FF7A1A703222A085894926BD27AA92BCFF19563469",
+      "v0_ldt_key": "D79115F0BBD9AD968C1F99C08A1F6EE03997499BD1549F66CD1603B40B18FE15CB61057F6ACFFE7DF23628AA5D7A7E6A1AB3022ED735A0EEA7B025FC16897D6B",
+      "v0_metadata_nonce": "60D20D161EE6B97FCEE5E2DD",
+      "v1_metadata_nonce": "42876252FC40CC971CD39A6E",
+      "v1_mic_extended_salt_aes_key": "584DD6D94E5DB46E5EDC4BE2B854368C",
+      "v1_mic_extended_salt_identity_token_hmac_key": "0E71CD49557CD6C9CD914B1624E2880188F8ABDB142E546C4D04570FA606E3AD",
+      "v1_mic_extended_salt_mic_hmac_key": "07863900E27135BC852442C80BFCBA09FE418A460F91DCF30F88F194F2B15DE5",
+      "v1_mic_short_salt_aes_key": "E0DA5AA00A18AB165C15005E75A02F7E",
+      "v1_mic_short_salt_identity_token_hmac_key": "C6870BECE7AC586F61563E3FA8F460728218E93644B40E0DDDC621A01042FCC0",
+      "v1_mic_short_salt_mic_hmac_key": "CCBA93DACA9023B7633E5408DAD5EFF1FBE3A00E9F76E9AC8507DE5E651A5FD4",
+      "v1_signature_identity_token_hmac_key": "E6A09F1A28C2DE26841044AAAC67C4CEDF963992767C960796E204BBA6B61432",
+      "v1_signature_section_aes_key": "F610CCD8C43EF4D6E738CCD0C1C24C09"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "605D86FAD007BC4EA0878430",
-      "extended_signed_metadata_key_hmac_key": "EBA52456957CA17961D098C55159D6D79C3451E5738468586D4187B15300ED2D",
-      "extended_signed_section_aes_key": "76FD6B925AA863C06D42FBEC0F29B98C",
-      "extended_unsigned_metadata_key_hmac_key": "C80E7E9DE0E1357E95AEBABEA46C14C4065D8FB1B4B4CF755413B3F5EBD930C7",
-      "extended_unsigned_section_aes_key": "AF6EB46E7DE399B15CBBB1F54BA7436A",
-      "extended_unsigned_section_mic_hmac_key": "0F3EB356450F0A6C99BCBA4D69899631CCB0E18D5CAAFBF844D61B741EA44E9C",
-      "key_seed": "3652F27504B90685287D9F9EFC800244A4F250F153B825F00389A5578D50F9D5",
-      "legacy_ldt_key": "656543A5C4CA235799BFA3EDEF3734AA57830F3EB0D4BC94BC42A9E4265D0DA8838B9E69414F07E0F933784DD054194E660F604F345EFBCD694C2F466499747F",
-      "legacy_metadata_key_hmac_key": "836A63241862CFE7B29FD00B05A9438D293E05E460C0E77C56804461803DCCDA",
-      "legacy_metadata_nonce": "E188B583B00F2910B16EF19F"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "1B22",
+      "expanded_salt": "95D2C439CAE94171D8F64ECC4D79CED2"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "D5D6FD18F488483BED09863555AD3BA3",
+      "v0_identity_token": "3C7E4793630A8229E8D7E2337E04"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "E5D8",
-      "expanded_salt": "FBEED7B8E11129FBB42F282F988B1A81"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "B898",
+      "short_salt_nonce": "D983D29159D24108F06DB7D3"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "95124B323D4373E93FD74091B97406D6",
-      "legacy_metadata_key": "CE875009453430A68CDB21A04D5F"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "67BC64977B228888CC5CD6466E3112A2",
+      "derived_salt_nonce": "44279BC1027CA9D501947166840CF2AE",
+      "derived_salt_third_de": "D656A3294C7E089F94ACF3FDFE93D463",
+      "section_extended_salt": "D482C2A6D420C5F6993527865FD38E45"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "A04BF6A2FF522EFB9682A288C93BB656",
-      "derived_salt_first_section_no_de": "57DC6F81CE5B7432540EB145659C56D1",
-      "derived_salt_first_section_third_de": "769504709A8FCC5EAC8927039CE9DA64",
-      "section_salt": "B10E4B3856910D59B98435355E69AB25"
+    "key_seed_hkdf": {
+      "key_seed": "CE2C5489AE9BBD4E63911BB5D51B8EDC726A6F30557C012A8DD37206DCC4DA0B",
+      "v0_identity_token_hmac_key": "8BCA9E29B97BD9610DFBEB35DF02261C16A3A6E7AF8EE60C45E7A8EC00057F6A",
+      "v0_ldt_key": "51A6880BFA961E52F8961CD397D3905AB3EF02B6431394390C330CB0480AEF3B48F6D02CCB8E7496985B802F13254AE6747E5AE585C8D0D390E81A17406199C0",
+      "v0_metadata_nonce": "0AACCDF41FBF33135BFE11E8",
+      "v1_metadata_nonce": "04411E15C45DB0FB941F1B09",
+      "v1_mic_extended_salt_aes_key": "31E9CF730E31A1A56C75624790869DC6",
+      "v1_mic_extended_salt_identity_token_hmac_key": "400E94EEFE9F40AC2761E04EF84D4C9A5506AF2BCA87F1FFC3C41DB0CE42EF6F",
+      "v1_mic_extended_salt_mic_hmac_key": "35AC887D2973BF40F73476F789E095A220919AF1C4A308A20C4B72957A6F0847",
+      "v1_mic_short_salt_aes_key": "18A8B80F7FFE5B3818837D04BAF23FC8",
+      "v1_mic_short_salt_identity_token_hmac_key": "5CD8B34230B6BDED50B42E1EAF30F46136432AA2599F66A4F7AEE7442E974BA5",
+      "v1_mic_short_salt_mic_hmac_key": "91AC6D599B6D5EDDF95472D946A8C60292896C395DF3216FE6B0297BF0FBC890",
+      "v1_signature_identity_token_hmac_key": "848F82B20E686C242A2D7F1933E0CD872DBBDF1E7B436856F19424BD47078DD3",
+      "v1_signature_section_aes_key": "7A88551E0D0B4C248BD88C7214423C1F"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "C0FC2A8AD5C0AD828ACD91F5",
-      "extended_signed_metadata_key_hmac_key": "720F677655EB635563DE99FA07E045D7074C1E099401341E7A7A3C93B55BAD55",
-      "extended_signed_section_aes_key": "BDE744442E32EBA45FCC5C6340342AE9",
-      "extended_unsigned_metadata_key_hmac_key": "1B380539C060707E986587B52E0EFB9EBC7907F939467741D385A3A706137078",
-      "extended_unsigned_section_aes_key": "AD4E22885C75C1767DDD55943AA675C9",
-      "extended_unsigned_section_mic_hmac_key": "3D1170B39B5766B83A74425A1CECFDD73786EDBDC4943C1063D29609AA6281DF",
-      "key_seed": "E05713CE94A34C790D245E5340F0034B7FCE8C470639AACFB649E351C4517996",
-      "legacy_ldt_key": "B77B168EB6924B7DAF554027BF77FEC4C5B0D5FFD370AA0D8A2D6AC2BAB0C678DF858B7B28A143BB4AD12E7D9CAD3B51D939E23C0AE1289797078334530D8BAE",
-      "legacy_metadata_key_hmac_key": "B564B6695F774376499A95B28B11591EFBDD492948B9840B7207949154A46510",
-      "legacy_metadata_nonce": "4DDA74813EC3FDD85ECF79A6"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "7DAA",
+      "expanded_salt": "8B186389B95F08E844499318AF2CC0B0"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "558D0863258D8230FD3C931CD2EDBF7A",
+      "v0_identity_token": "2713C2F2B47DFA957381C10C2649"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "F388",
-      "expanded_salt": "8967E476B2B0068E17A1B71FCFCBDFF1"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "A20B",
+      "short_salt_nonce": "412810F614DD5448E7BA6171"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "E36AFC309B6E668C2C3A0CE5FAC3DBD3",
-      "legacy_metadata_key": "8A97CFB9BF30ECCFA0EFD7E11022"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "F613A207D445ED2EC7CBE2A5787EFE60",
+      "derived_salt_nonce": "92A3862125E69F44E8E6087DD72828B6",
+      "derived_salt_third_de": "54C805F3DE7A3652271EBD5E097A9D95",
+      "section_extended_salt": "8D5F11C001A419648067D4A1966B0EED"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "D5B0B8A104592ED2AA6605FAD6E2B786",
-      "derived_salt_first_section_no_de": "546BD228DA5EC1C6706BBD0B6512260F",
-      "derived_salt_first_section_third_de": "D9093EABE9731CFE5CD5EDC03B3593B7",
-      "section_salt": "625AC46C5387C640952FD435DCE7BF8F"
+    "key_seed_hkdf": {
+      "key_seed": "C79A63177DA5F9CABC80DA27464F9407A50F686B9253CD8D6CE16A2FAAD90DC3",
+      "v0_identity_token_hmac_key": "154DA7DA9EDCA6AB3B336D1BBF3A3DFC2B959C76BB3DC530DC336469A22EE49A",
+      "v0_ldt_key": "AA7A2CD44C71098E3F94A8B0155650502F257681BDC8B856C7B2AF5B1136D96F86089FF3719296C183DF56484D0A688308536BE2EEF9FC667E15BDC4D3BB0279",
+      "v0_metadata_nonce": "2FB6567C04443950D5F10C82",
+      "v1_metadata_nonce": "A847F747EFE0FA2F8596B2BD",
+      "v1_mic_extended_salt_aes_key": "855D9EB68E68D7AAEB46C71C66FC1BC0",
+      "v1_mic_extended_salt_identity_token_hmac_key": "4FDCB83D8EF4C5AACFF25300FADB3816408CF671D9874FC3CAFD8ED6755B3C0B",
+      "v1_mic_extended_salt_mic_hmac_key": "CD6FA52AA997B94740B8645D647103C671B5795BC46F1E12B7AEFC7067641D96",
+      "v1_mic_short_salt_aes_key": "6B695E1C8FB3DFDF4C1964B4B3424A03",
+      "v1_mic_short_salt_identity_token_hmac_key": "900252ED6385B4E457CE563A5E5FC4BD39784EA4BC64958349E7CD427A68493C",
+      "v1_mic_short_salt_mic_hmac_key": "72DDECC2AB00222B3C70A9B343664B901614F01CD2E7033934DEC4077D6EC14B",
+      "v1_signature_identity_token_hmac_key": "05CB39213C947B9C4D82F311372A1780E4BB4AFE8480747841B0A681FF092A0D",
+      "v1_signature_section_aes_key": "E130CCBF8595EEFA23A6925AAD6E394C"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "C2FA",
+      "expanded_salt": "5B94F9CB6A7867A0D27C9CBDF4B0EA73"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "256918D6714CA183AEA76E3A",
-      "extended_signed_metadata_key_hmac_key": "44E27B5CF5EB7F40E301D513BFCC0E7AD1D064268ECED1C93E08521C2DA9CDAB",
-      "extended_signed_section_aes_key": "C717E7186766C821588610DDA234BD1A",
-      "extended_unsigned_metadata_key_hmac_key": "5BBC3B99F800486E7D1ABF0998E8553CF827B4CB79D556CFC43954397909EFBA",
-      "extended_unsigned_section_aes_key": "49E46036FD7EEDF2E316C8E3A2C83E18",
-      "extended_unsigned_section_mic_hmac_key": "9EF8F3A9C044C579691C59EE221859361ED13B4C588E1576C019CBB83411A0C4",
-      "key_seed": "E2633861C773DE669266B36268FD535E3B04A5269F5BA3B58DD1B2B2800141AF",
-      "legacy_ldt_key": "B68B9BFB869949766927D96D8E11A8F7AE6B4D0CE4AD11FFEB56CCDE74B4329315EF8BDE16F5B89B6A05373EBDB09ED7272D20F51ABFABF50C901FF4A5B6D45B",
-      "legacy_metadata_key_hmac_key": "B20510C6C750BFE0991CA3CD6CA3A6D1132A09CD845BDC3C2E006CFA4243682C",
-      "legacy_metadata_nonce": "F82A5657110463EB4993D5EE"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "7EEAF724D0D00C551EFB2985D1E28519",
+      "v0_identity_token": "FF3A952F373296C0F260CAADD55D"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "98AA",
-      "expanded_salt": "13661848812A9F40B2C9550857E3ABF4"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "6B11",
+      "short_salt_nonce": "9DCF2866433ED70F26A6DCE4"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "521F75308E4F3ACBA82DA74134C46309",
-      "legacy_metadata_key": "4F767CCD14102B29ED949CE30218"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "8882901B8F1F333196F1B78FC5D012C1",
+      "derived_salt_nonce": "EE7A39E6A119642C002E260A972369EC",
+      "derived_salt_third_de": "20B064790320BEDAC44CE80C37B4FCC4",
+      "section_extended_salt": "1E7F5C402A5D4994A8188E52A0AD2D72"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "DE24781BCD7E2BD9F39DBD8EDF8EE2E8",
-      "derived_salt_first_section_no_de": "647E6F36D4F8529748EC831811ECEE59",
-      "derived_salt_first_section_third_de": "A755924F3874F3E5661E8AA0F3F62173",
-      "section_salt": "5A6632D4602C01565447BD6939610E7D"
+    "key_seed_hkdf": {
+      "key_seed": "1C745DE11ADD00D3CA19D37B5CAB3D60F5F86659B1848A11D3059872B376BB15",
+      "v0_identity_token_hmac_key": "E08EA49AC02F69DCAA441DFF1DA7DE2181202DBC15F006244458C2431E4DBF90",
+      "v0_ldt_key": "2E91782584FF49C855E1B842594481C6DB3496D21E19EE85CC8A1FEA83FB52D68CA22FF441DD68F2EAE28636AB143D3A03392847E766C70737357B2EDAF35D8A",
+      "v0_metadata_nonce": "36DF9D6DD2D171BAC182D212",
+      "v1_metadata_nonce": "0AC3126A538404024CBDE6E8",
+      "v1_mic_extended_salt_aes_key": "7DA766C835B14E1AD6352D8956C0B606",
+      "v1_mic_extended_salt_identity_token_hmac_key": "3830C1826FF962656DF74B541087104929EA9096D1632516E560208BAFF950A5",
+      "v1_mic_extended_salt_mic_hmac_key": "A88A2984350349201CBA4531C95928DD34D2536CDC6958D6EB77A992AE1A3815",
+      "v1_mic_short_salt_aes_key": "ADD9E7D962DD812FBEF7D19E09E5C6E4",
+      "v1_mic_short_salt_identity_token_hmac_key": "44B9B99CF2B3A3514BF22123387F8D45F2197C71348B8FC3EE885A875AE10451",
+      "v1_mic_short_salt_mic_hmac_key": "62EAB192B0DD022F91733FB357D7DD912C94EBCB45B91F04AA5C0C9A9E7A85BE",
+      "v1_signature_identity_token_hmac_key": "A427FEC4ED745D0353E0B24CA11D69CE4FFC5117FA26F906B2ED023AE806B854",
+      "v1_signature_section_aes_key": "6A4CA8910A38C31640CD06D2045E2D0C"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "0834",
+      "expanded_salt": "A708695452CF14D8E469B91F26BC7E3C"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "1280F2BF6B120289C45DC96B",
-      "extended_signed_metadata_key_hmac_key": "6D058C55D5B2CBB0B4BBBD51028C0B973F469A1B8E3DAC59D52A1886D088F125",
-      "extended_signed_section_aes_key": "37E713DAE40FD31E2F92C329CAAFEBF8",
-      "extended_unsigned_metadata_key_hmac_key": "1A20B4BFE3EB4EDADE909358ACEBF1C020E9539BA5140BC2EF986EBEEEAB45AE",
-      "extended_unsigned_section_aes_key": "3F872A127339C6C6C511AA00604C411D",
-      "extended_unsigned_section_mic_hmac_key": "A837546D57AB36B017645E740D6631016E14E5B16DBF71E058DEC61F5F8AF1A9",
-      "key_seed": "02FED3D1E3307A9FCE2B11203249FDC09CF979ACD85DF62359860EDCE2DC5507",
-      "legacy_ldt_key": "B4D501B6B7DD28A54A49E3F57F99C782285B30CD69203E5AF7F838CC4A0380DF6EC89D1D67113E4517C6EE916460017B655F5A4AC373AE8FD68410DF230BB138",
-      "legacy_metadata_key_hmac_key": "E1707BBBB1337ED59704AF606EA54550074F5B3D5C0310738C04128D634A8558",
-      "legacy_metadata_nonce": "4DB188779B676BC7B70F234B"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "D57B964F7DC6F9BA1061A25550D8693D",
+      "v0_identity_token": "F3AA301AA094B20B2B0E7A02BB81"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "3B69",
-      "expanded_salt": "A05E1016967C2E7DA79688AF74615B4F"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "766E",
+      "short_salt_nonce": "FAE06666F8180F410474F3F9"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "FA9ABBC8316622DC2A9843BA0097E0DE",
-      "legacy_metadata_key": "AE83451CAC8181606BE17AC715C4"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "F6BF0314DB3A1C1330A8E6E9A2CE5E7A",
+      "derived_salt_nonce": "7D5985FCF0EC057383CF3BAC7B113A43",
+      "derived_salt_third_de": "84F6F682DE9C87BDD9AABFFCDC14A822",
+      "section_extended_salt": "CEE571EA0F50EC647161523CBC93367E"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "40ECE473568BE946121A394FE60B8737",
-      "derived_salt_first_section_no_de": "7548394C226D05F10C9B557BD0359040",
-      "derived_salt_first_section_third_de": "2E8189954D8068F3CB284740CE552CCD",
-      "section_salt": "B3A517BB5CA6D1627B9A0255B31CFE31"
+    "key_seed_hkdf": {
+      "key_seed": "C767661E4DAD8FDF897BA00727A98FEFB87AC252BC0975BFFF3FC4AE3844FA89",
+      "v0_identity_token_hmac_key": "652640BD7236CFF84A9E87D57775FA4A81F7BEBAE501AF8B64AEDC0C844102E5",
+      "v0_ldt_key": "3EE26E3AD6244AB6720D572EB4AFA66A6B4F474E911DD9795E9903BBD90B8D2ED94C2F7EE7CDD0D66B9B092D40F0AD1E3EF53D2D959ABA9EDF561A9F7E3AD675",
+      "v0_metadata_nonce": "4A57C7D89DA48A086131D3C0",
+      "v1_metadata_nonce": "4746E50ED394092187E13D8B",
+      "v1_mic_extended_salt_aes_key": "B487D5205DBE5C0CF663BEDA55163823",
+      "v1_mic_extended_salt_identity_token_hmac_key": "114B6C0BEBA4663ED08D6757606B528F7C3256EF2BE148F7BF75F6DC401BFEF8",
+      "v1_mic_extended_salt_mic_hmac_key": "BF34BAC8C099EFDD3BF41C051675CDEC259C27786E50CD326FFD409AC8ED6026",
+      "v1_mic_short_salt_aes_key": "F3482440016A1E6CA2E1C79EF3B9F3B2",
+      "v1_mic_short_salt_identity_token_hmac_key": "BBB7762E694FF6DC4CA9F31A537094364495BADCCB73FC69327C86D8A84FDBC7",
+      "v1_mic_short_salt_mic_hmac_key": "E35BD2293E90BD3AC97E98C2983EAA7B7F106AC55B3D88D332539F928CDB8E09",
+      "v1_signature_identity_token_hmac_key": "7F9ED591367A0EB8A4F23D145BA75249FD5842B59E8CF8C8BB9E69E942E555AF",
+      "v1_signature_section_aes_key": "955D996A026288A647EA707B0A33C9EB"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "CB9E33EEDAA33993FC4B5318",
-      "extended_signed_metadata_key_hmac_key": "08AD406C9CCDB3892CFF81B5FDABB9ED3B08A24FFE60FBA487A9DE2EAB871A26",
-      "extended_signed_section_aes_key": "973BF0F1F7B463EB9A97E1681BD1D3DF",
-      "extended_unsigned_metadata_key_hmac_key": "E1DABB2A2F8D3CB975196C9BF900491D463BBEE01E408B3E595E718EBD1AA2AE",
-      "extended_unsigned_section_aes_key": "CB15F4F517DBD310BCD5FEA2DFEBBF26",
-      "extended_unsigned_section_mic_hmac_key": "865316C4BD1F483DF2FB1F819AFD6798B26344E9CB7D5F33C0FE5AD5C8DBC1E0",
-      "key_seed": "4129B6D0A4B84BA1F7241CCC11F9C75EDCB74E50DAEDA9B94E7E5EAFD418921F",
-      "legacy_ldt_key": "A3322720422B4E51C75B1C66F3BD3DF06DE13496B38FBA0BEBC4CE11B12238F3C779A2136C2B616F5E680A9A708E5E2CCC3F8AE9F0047AEA0B1AEA088C823EC2",
-      "legacy_metadata_key_hmac_key": "43C5B9A88F6818DCEA6F93D49466F31BFFD5A4EBEC3C2ECF5A872BE86F5B99F6",
-      "legacy_metadata_nonce": "A3CAF797ABC475FB19F5B602"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "B490",
+      "expanded_salt": "F5A6423790B75B0A73E26647F83E003F"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "1143418D5D928DC46A5764F53E7065FF",
+      "v0_identity_token": "5A6EB28E3F9F076AAF47F1AD6F52"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "AF5C",
-      "expanded_salt": "C7F68B27D565702CF382B680B8E7055F"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "5B71",
+      "short_salt_nonce": "446675E4C5503E587A7ECD0E"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "1CC170AD6BC9799CB87FECFEB7FB593B",
-      "legacy_metadata_key": "4D860E0531136A82B22D80EED522"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "CB1D2C8F670DE3E029FA9F91D0FC1494",
+      "derived_salt_nonce": "F82D6616C9669EC7708B6C085A45E8F8",
+      "derived_salt_third_de": "8ACC53A66823593B9F10479985B6D9B0",
+      "section_extended_salt": "839CE97E9C32C0C81368ED8F8057FC02"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "EB90E68FE9C38FF5A956B2DA2D44D399",
-      "derived_salt_first_section_no_de": "014EA160B58368629BA76FF16A844341",
-      "derived_salt_first_section_third_de": "9ABC5ED7EF8973C8FF865B6174CF18D8",
-      "section_salt": "04CDB186B08FB26AF67486FA439CA9AA"
+    "key_seed_hkdf": {
+      "key_seed": "F8D48367C0C15E86ABD63D96C632504F7ED68866436F6CF694D7926BFDB460E6",
+      "v0_identity_token_hmac_key": "02840A91CA2DB99828CA95C4A6102EB14D28554B01ACF4EDB70A8CFAFD0ABF1D",
+      "v0_ldt_key": "DDFB10BC34B1A507695E663B936379D7F8F5E3A58960B0D54D286752784414F4AC7E3D86F480BBE83F3D43F99E45E2787A57D234D26C43881BA12BDA5B8A84F7",
+      "v0_metadata_nonce": "31DF1F26F5C80D57D312E8FF",
+      "v1_metadata_nonce": "EF1FE145CA3643D332ED685A",
+      "v1_mic_extended_salt_aes_key": "681A2CB23B847D46E355E0625988C8DF",
+      "v1_mic_extended_salt_identity_token_hmac_key": "CD02ED35449659F983FB2070D2D353311FD04A1529DF128F097FB28447898FF9",
+      "v1_mic_extended_salt_mic_hmac_key": "26B0F39B6880F40FBB6D006D3FDF20FA75D61FE70A21AA279295303AEEAAE57C",
+      "v1_mic_short_salt_aes_key": "A0082298B9261FA98FF77171F7FC6D35",
+      "v1_mic_short_salt_identity_token_hmac_key": "DA9EEED06B73B51582DBB749CAE10B30A072B4CFFA13B3055BBB0DCFAE423D7C",
+      "v1_mic_short_salt_mic_hmac_key": "28D6FDBDB521166652ABA223A3FB626DAFB5E5600D72B7EEBCE1A95E1E62CBAA",
+      "v1_signature_identity_token_hmac_key": "6A82842B2EA2C3C17C26B2867FDCFDC4733F67D4486CB92E1436B282A2E63AED",
+      "v1_signature_section_aes_key": "066127DEAD9D2611AFCC7B6216F8F180"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "3820",
+      "expanded_salt": "ADCD23D7DD53599418BFA63474D69B5B"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "69EFD440262ABCAD6340712C",
-      "extended_signed_metadata_key_hmac_key": "ABE74AB6340DB962F20CC9D40EAD220DB2E644547F110AAE1AFA59E98ACA5073",
-      "extended_signed_section_aes_key": "C30E2EE7462A487197359E2B1E6B592F",
-      "extended_unsigned_metadata_key_hmac_key": "46BC94A1996B114BB3EDFF2EE8ECA2A897EEC62704CB2EDDFFD5B97330E770D4",
-      "extended_unsigned_section_aes_key": "F3ADC71F3D1948E5609C740FE8DC5DFD",
-      "extended_unsigned_section_mic_hmac_key": "45F4745792A7129CD8864CDFB715B7047356DF99F7A3ECDFA4F1191C2EEF39DA",
-      "key_seed": "8D228D34A11D8C6E89BEAD97C32CF1CDEB83128EDE806AB12AC1C315104A12C4",
-      "legacy_ldt_key": "58B087E71D5AFBE1B2C9D40770425BF2F950EC54A80BD266A8C3DC184F4FCE7F6799BC221FA5FE4025EA898632E2F3D3C8A983E9468B99AC033002779395CEB0",
-      "legacy_metadata_key_hmac_key": "79C5E3C3D6BBCB4FD9BE972683D273484578E592AB7808B1A2DBCB6A0AF0EB20",
-      "legacy_metadata_nonce": "22C6BF85F720FB130D698F43"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "D886612612F2CF8F0C8D2FEC71A2FF37",
+      "v0_identity_token": "63DA95A649039DDE7129B93519E8"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "AA36",
-      "expanded_salt": "1DCEFB9DA72BA3A4AEF988EA5C803E39"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "9A3F",
+      "short_salt_nonce": "7D16B77869830EE92A4EBFC5"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "60989D8A1EB13AE51250E2698F544BD0",
-      "legacy_metadata_key": "E9B69DC6D89A0FB49D43B34F5311"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "659B8A721546530DA9795AF4F85F3172",
+      "derived_salt_nonce": "C86E1DF850ABF4050CFF5069AF064842",
+      "derived_salt_third_de": "5409D2E3012D6087942D9C6A9AD01055",
+      "section_extended_salt": "618434E133FE816E6B612F6CA5057231"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "64555F0FD7574CDB19BA389CDFBC543B",
-      "derived_salt_first_section_no_de": "3E0E5C26DD09AA19522AE9FF60E299B5",
-      "derived_salt_first_section_third_de": "2177B3044D79C03B816CD8E8433C5B3B",
-      "section_salt": "BFE9AF6A7740CD26E60E7C5A8AB281C0"
+    "key_seed_hkdf": {
+      "key_seed": "1908DD830FC3A7EE0E0FE2B559BC1DF6B5C80F9576452FCA38995CED1EF3D9DF",
+      "v0_identity_token_hmac_key": "3CEB70A041FC1EA8969A7FE0ED5C0B26919747CE6E32793B7928D14934CF1921",
+      "v0_ldt_key": "82B1C998FD20CB2053674D3BE231763B9B4445B16D39E23C054985F315717C2D842817F74EE526D4F3CB50FA7CCEB30C3DA0B5DF9D39B19A8E51917E56905CBF",
+      "v0_metadata_nonce": "DE3382B2F3E85062C9C945E0",
+      "v1_metadata_nonce": "73FC91F971F797CF852BD47C",
+      "v1_mic_extended_salt_aes_key": "7479E783DF7D3F949C85D44150538152",
+      "v1_mic_extended_salt_identity_token_hmac_key": "4E8FB9F60DC947E7E6ECCD42D235842453C070432703EC381BC44F65F17A944B",
+      "v1_mic_extended_salt_mic_hmac_key": "081D31934C99EB52072EC7ED6D5DB9B238E8E9E6A1E66F612C3A53CE664895E5",
+      "v1_mic_short_salt_aes_key": "87B5CFFAF5E8BF3F76F901E8A8167EE1",
+      "v1_mic_short_salt_identity_token_hmac_key": "02926351AC11AB30159026DE19F8860825CDDEA95AE3B4054564EE4F42AAB436",
+      "v1_mic_short_salt_mic_hmac_key": "E7B62E504AF1F2EF99A86EB37FB14AB5642FCAA0EAD4BB0636D65A41B455A876",
+      "v1_signature_identity_token_hmac_key": "8E1E4E0D0082011E31322CF3CCF5CB6DD94F041C529255A3E96215C2EA887B86",
+      "v1_signature_section_aes_key": "113BDF8FDEB7FDDC45EB8D0CBFAA35DD"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "34D8",
+      "expanded_salt": "E1726AEE6C32008D70E77FA3CA10A3D5"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "8F2CC361A133B662D60E70B4",
-      "extended_signed_metadata_key_hmac_key": "14F28363041B1AC420116ADBE5E862E36C4F209C68D784AAF659003123931F20",
-      "extended_signed_section_aes_key": "F68813A0A51D3A7D6460C05109EF1730",
-      "extended_unsigned_metadata_key_hmac_key": "6AC0B8555E39508D3F72BA8984E530E59A42777BEB16A2A6CFC760345AB6AB13",
-      "extended_unsigned_section_aes_key": "F3DF15F42B2DCD3617C60847F88EC279",
-      "extended_unsigned_section_mic_hmac_key": "149F3635D7BB841DE87493388AF27E75F0D5C374DF9108C5865EB3F1B631AD1A",
-      "key_seed": "0D4D259D5D0E5D7C9B2A569A1C17D389E618B782D1BCE04F8484035492CE11C4",
-      "legacy_ldt_key": "E1628A30487D83BB90ED7B5643AE314CD9FD98C9679F37C25D922DFAA0FFA66999FDBEFC099EAC5250C6CBCBA2314085DBDEBC4341A2074226D3E92773B1946A",
-      "legacy_metadata_key_hmac_key": "9549B17906EDCEC2E747744703EF977F5236528197E55617F93755C860CBD0B5",
-      "legacy_metadata_nonce": "BAA28085EE97A8DF527A5594"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "9FA9C4CEEB36C5D01308572338AA4DBC",
+      "v0_identity_token": "4468295C7F2D6B52CCB9519B5C18"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "57EB",
-      "expanded_salt": "D275F5AB0B47DB46096D6A2ECDF58D52"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "51A7",
+      "short_salt_nonce": "B61ACE6DA49F07333D36D94E"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "F3B453CA100ED3E10C91E12A7481F4CB",
-      "legacy_metadata_key": "859FCE97618BF2DB58D184DC53DE"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "B2DB55B0EE6A19591335FD4A004AED74",
+      "derived_salt_nonce": "A18DB7BB4DEB0BAC4C300C1DFF08300B",
+      "derived_salt_third_de": "65FE8535564AE2CFB9D3D19CD5F1DF30",
+      "section_extended_salt": "FC87C8E278F6CF79DFA952B70D3804CD"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "74F425482431EB42FF996BAC1A227F0F",
-      "derived_salt_first_section_no_de": "CAC0DBDE04AB7B70FBF8A40E18814286",
-      "derived_salt_first_section_third_de": "B751EC2415541106D79351A34AA5D2DC",
-      "section_salt": "4B0BF5D2EE37C72845CD0776F7AAA512"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "183FB3B68B7A48FFF0C161AC",
-      "extended_signed_metadata_key_hmac_key": "50027ADA2FF20DD6132B1729D7CDDF3D6619EC2F0B676D68946B199C65EDF004",
-      "extended_signed_section_aes_key": "2C4916F70427AB1247D581CDEF656639",
-      "extended_unsigned_metadata_key_hmac_key": "211294989AB217BBC4794503C433B7B886612E2D9A8B824B0EDD590F2DEC8B7B",
-      "extended_unsigned_section_aes_key": "FEB3354D18E5ADA3E7684F96EDB719DB",
-      "extended_unsigned_section_mic_hmac_key": "144845A3ED634D768F0F4E4BCEED5E9F23D77A033D67FDBE6F1BA225FA0A71A1",
-      "key_seed": "2B4907289260929651D6339AE8157C6F509BF34AA7E2672B5C671837C62A0363",
-      "legacy_ldt_key": "2A1BDC158660033902E9EC6F47BCF81C99EF5AE759CB03352320324BEACAC4D62F816CA1A190BCC28A276838F565FA5F41E2A7BA8574FA77D61F2A83DCDF665B",
-      "legacy_metadata_key_hmac_key": "1266E50907BFA5ED5990F295F10B1562D4780775A3CC95F2F5D7DD9912644961",
-      "legacy_metadata_nonce": "27B1A368FC746D389CAB7604"
+      "key_seed": "D9C3778AA9B5E3B681176C33F23D930EECF65E3B03F57383173FF1C961C43632",
+      "v0_identity_token_hmac_key": "6D9F5B36A298BC2BB69628BE19C027B5B82EA49BBF39938CA796BFFBA80BDD6D",
+      "v0_ldt_key": "515D7D78685E974EFEC733637B866911EC2DA076275B073B53B8474C52B4EF97A0BB4BE23CEDD04EDD320BE099E42C17690B9FE34543D0BC56F10E706185C735",
+      "v0_metadata_nonce": "294F93F189066B9962BE5F28",
+      "v1_metadata_nonce": "7C2775AFCE63CDF40320A5D7",
+      "v1_mic_extended_salt_aes_key": "3083165096C985AFD9634D7C8C27B241",
+      "v1_mic_extended_salt_identity_token_hmac_key": "072CD33806FF7BC19C06232406E694417FC54B2A5CFB07F7F6B8E9F41B05CA5E",
+      "v1_mic_extended_salt_mic_hmac_key": "6787832FCFCE8782338608C8F35419AC877A130B4AA7866A3C7CB22ABD67179D",
+      "v1_mic_short_salt_aes_key": "A3798AC54A640D75DE9460BFF32D5A5E",
+      "v1_mic_short_salt_identity_token_hmac_key": "43DADD838D1B76E2EAE5A94FB1B839173FBD84CA47BC35CC2AA508D9D3516823",
+      "v1_mic_short_salt_mic_hmac_key": "F92977DD9EBAA721832F6E5D0CED0E29F940847AB0FB87494F323EC770EAF6F3",
+      "v1_signature_identity_token_hmac_key": "28A9E655ECBB19E0C72E389AF86D18CC1DD99B837CA39F80EFBEF46562ED30DC",
+      "v1_signature_section_aes_key": "5882688026AFC05D0A53C691D3C205AF"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "2CF1",
-      "expanded_salt": "2D9360CAD492BF9B3F8BA5659FBFD479"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "BB77",
+      "expanded_salt": "5EEC79CBDE9B59FB7CFA09BBD4C21F6A"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "B04A6E310A7D824C2ECBAC27C078CF20",
-      "legacy_metadata_key": "2E262ABE8F4651CE3875F82BBF18"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "9C80752325978A05003338C9AA98CA51",
+      "v0_identity_token": "CDCED4F23EC9AEAF9254F5851E9F"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "B8FC",
+      "short_salt_nonce": "C4625EB21060C7AC6EA7734A"
+    },
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "2040080121468E2437D7C4AF1AB57CBF",
+      "derived_salt_nonce": "ED3C0CD523D93752269A7AE558DA70D1",
+      "derived_salt_third_de": "7EF0A5B6DB0312484B90580CC5CCDBF6",
+      "section_extended_salt": "DD49906314C2165F3AA4887C7C2CBDE9"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "A36E49ABD02685DAC38CE1D19D0B8B9E",
-      "derived_salt_first_section_no_de": "D531C60F8CA7C63115E838F71519894F",
-      "derived_salt_first_section_third_de": "722AD6D5D32103BE533568A2C3903320",
-      "section_salt": "581234FE7E1189244C5C61BAFDA5DDB4"
+    "key_seed_hkdf": {
+      "key_seed": "BDB42D1F4E1C41E35B00D54D726D90A18D9EDF326C5A95792C6A8BCB47F3D8DC",
+      "v0_identity_token_hmac_key": "4DF296ADF1A25ACA84D578855464E8A91903FB5FB2925D4F7AA4EEB667C3F514",
+      "v0_ldt_key": "A34CB3B18ED974E8C932C3F776D8D640D044E8EB23395507EF2E27143DE60549E44A2CCFB545900C84020984F1CA4ACE171E2150CF3E37CEA49128423E96EB6D",
+      "v0_metadata_nonce": "4007F919792DE5E3D9E35D6E",
+      "v1_metadata_nonce": "E95F4B6766CCD3571F404E51",
+      "v1_mic_extended_salt_aes_key": "5355412E0561A0BC09520C23F88E33AB",
+      "v1_mic_extended_salt_identity_token_hmac_key": "8324D08430C2B565D5E4E3FD00068564C096FCEFEAF026A714033726242A6EB5",
+      "v1_mic_extended_salt_mic_hmac_key": "80025D8E9667BD088B5F9D297EEB6AE6323204DED5D6A5823E28896F58D703A4",
+      "v1_mic_short_salt_aes_key": "3FB967135677C8DDDA512EE813CB7143",
+      "v1_mic_short_salt_identity_token_hmac_key": "E9A7AF77D3A9368BF553BCA5721EA1F9981877501C5D999B437FCCF1E92086EA",
+      "v1_mic_short_salt_mic_hmac_key": "7A93B87E26BF91DFE19D314D0D1E50B85CCE0BCB90AA127706A328F927092721",
+      "v1_signature_identity_token_hmac_key": "39062B01DFC854FE594F8D6542FA644CD5C969E6F227511F4865D974CC6DEC2A",
+      "v1_signature_section_aes_key": "4B1167F08673796EC85DAFE62137B809"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "45E5E1444674572E775B8224",
-      "extended_signed_metadata_key_hmac_key": "E0B347400DE60F9FA04DA4ADBAF3B6BD04C644F6793CE46F85CB71479ECF3EF9",
-      "extended_signed_section_aes_key": "B6DDDDAF922C963512160E75225D09F4",
-      "extended_unsigned_metadata_key_hmac_key": "AF0BE49B0E816D8F7712328C5F4E6ECC82DDC48892B3BDE3F1C4C01E5FA03136",
-      "extended_unsigned_section_aes_key": "848215F6E61B20D5928039AD45AD2132",
-      "extended_unsigned_section_mic_hmac_key": "8B905A2A15AD9CF4FB406828D832C9A035D4230C0F539D7647C55022232A9FE8",
-      "key_seed": "32DD54629DB08642CDD3D8FADA206AAC7283C4CA3E2A8C555AD8DD6598720562",
-      "legacy_ldt_key": "4274705A9EB207FFA0F1D42694C47173DAB548F1255772A438CB64BC9A03E80C50DC01E2F201BE9A1FF32E40C9008B4EAF1242A11CA3C0B4D398419369228C8C",
-      "legacy_metadata_key_hmac_key": "EDE5263B245DA34B73AD72B20E1D07CDC64A814F65676AD59F056B31D3525B34",
-      "legacy_metadata_nonce": "AE685D297E2D64B79DC9C2B7"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "F252",
+      "expanded_salt": "EA39B41EE8AE4A6EA4EEA497AEA49449"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "53559C87298B9802AA805237D5652703",
+      "v0_identity_token": "CEFBB6A76E7EF5CDA4D26ED815C7"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "8CA2",
-      "expanded_salt": "49637910997ECD98BC7F3190BAB36A4C"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "9E68",
+      "short_salt_nonce": "0956F02109DC68655CB62A5E"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "BF435149C0B6A7FA723E18855A5D02B2",
-      "legacy_metadata_key": "96FD3B89B68B0B14A3781DDF9538"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "93BD8DE56900BCEDD84B9A1978AA94EE",
+      "derived_salt_nonce": "66881A93AE754517DC4D61AD12F5E742",
+      "derived_salt_third_de": "BBFD5FCD7F36BA90D88A9A092F8ECA29",
+      "section_extended_salt": "4D1E91C6292D4A910828E53CA3F09EE3"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "DCBBEE3140866817D5B3E64BF6AFD60F",
-      "derived_salt_first_section_no_de": "C7DBA60E81BB4A0F806B82F9DD4DE170",
-      "derived_salt_first_section_third_de": "00C8B3CA67C7C566A850C3E17B7E3A29",
-      "section_salt": "80FC0A629CE7F0CD7B603F171F162D24"
+    "key_seed_hkdf": {
+      "key_seed": "3B47B274E1880F0D735A3A461608C79800450B43A7CF5144AC1106AEBAC9826A",
+      "v0_identity_token_hmac_key": "A45E22C2CEA0F70D106AB51893CA33D0DF68701EAD7AAACD3D5A9942FC5F0DFB",
+      "v0_ldt_key": "FEA32307FE627BC3C5D33D748ED4A00AD9002CA2B10EB72F85BDF82F0275B9583B73FC02ABEC26E891AFB24C340EFE8EA4C27E7C4D28FE44ADA6BF10E899F561",
+      "v0_metadata_nonce": "6E5B371C46E3D16E4D5A66BF",
+      "v1_metadata_nonce": "7F261DF35ACEEDAA4B576F1A",
+      "v1_mic_extended_salt_aes_key": "BC74140188830CF38B8C241095BEB16C",
+      "v1_mic_extended_salt_identity_token_hmac_key": "385EE3B943000B9ADE0DEB825876F9F56EEECB6A72A0FFE15FA2FA958B1AD6F9",
+      "v1_mic_extended_salt_mic_hmac_key": "4B68A5067578BC2F7618F8B47F1FB64F3D304FFAE521225A895555BA5D8D9340",
+      "v1_mic_short_salt_aes_key": "DE089E6F3B702F1BA314CACA9E7F2788",
+      "v1_mic_short_salt_identity_token_hmac_key": "B2B5C5D2DA5C6FF0E1AF067248893051884AC530852A1EDD47CB494FAFFE06A1",
+      "v1_mic_short_salt_mic_hmac_key": "0263DBCBE0F69B101A639B2CF16346841E5ED159FAC9DCE93F92019A207D5819",
+      "v1_signature_identity_token_hmac_key": "E9F139462C5CF1CCB22E216ADB16B13C04206D39DABAFAAD83C342010BDCDD78",
+      "v1_signature_section_aes_key": "2DDFA0C2E734D9A8698821320CF1FE8D"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "CA66CE6D69EA86A41AB0F413",
-      "extended_signed_metadata_key_hmac_key": "6EA8DF938647F825C60C6251F0CC1CD82FC642A0E254EF2620A746F46C9E1258",
-      "extended_signed_section_aes_key": "0F0B94A88E78E605C74B5B91AE62BFFD",
-      "extended_unsigned_metadata_key_hmac_key": "6C5D462BCD86343B68C4435D5B1EF6CF9AFB3C38F226AA84B7ADE93CDB23E83A",
-      "extended_unsigned_section_aes_key": "E2C02DADB79D7CBFEA71F94C4A65575D",
-      "extended_unsigned_section_mic_hmac_key": "38350921EF19BA63EA5D6A5511230EA652628101961EA4504A597C5FE4510591",
-      "key_seed": "3836F8FDE174BAA5BFCE8F199CD45AF774C245312F90939B6D6FF51892F5C549",
-      "legacy_ldt_key": "EC34EF3B3714001DD85534A5D3600A70645B612F7E8937362E43C683149F6F5621C3E2F700EC3E9E40AE468116316DB52F85746E6B9856FECFD94BA6BA852AF9",
-      "legacy_metadata_key_hmac_key": "7B2DE270684F7A3872B5752AACE00BDA88CA3D832833432EF1FCEA8D04523E59",
-      "legacy_metadata_nonce": "5A9EC7E213B30C88FB1F32BC"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "3D53",
+      "expanded_salt": "5D92CE2F393EB11F68AE3399C3C6AA13"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "6541E71B9A62F202963E4C541DBE6DE1",
+      "v0_identity_token": "1E65BA9B2D61AF735D60781F99F2"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "EE17",
-      "expanded_salt": "D206579E8B5DA9132DBF4D1582A846E5"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "3F94",
+      "short_salt_nonce": "85714E38445BE9A4C2D012CF"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "BECFD5FC4083874524BD1E5A11A15CEF",
-      "legacy_metadata_key": "FD47DB9B922B170C9332494805DB"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "A7C1B418774DF806054C029FA82533F7",
+      "derived_salt_nonce": "4919EFBFB04402814AB0C6A805820947",
+      "derived_salt_third_de": "C55544C75B968C2EAAD88699050D4362",
+      "section_extended_salt": "2BE31AA1F7D93AB925E36C8E96D2FCC3"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "BE32AD26A190F96FC445AABBCB1CE959",
-      "derived_salt_first_section_no_de": "8F061686BF5B1411A332C523C4E0A209",
-      "derived_salt_first_section_third_de": "232C9AB5D992F3411339C60540915063",
-      "section_salt": "8C2DCAFB7C90877729124D34A7D5B6E5"
+    "key_seed_hkdf": {
+      "key_seed": "F13F7BA40A43DFB17FB31FA4EE756C48B9FD924F19EF3A86CA524B838A4ABF88",
+      "v0_identity_token_hmac_key": "280DA0042A5A21602B1D11DCAD91AC32691973312481855D05C221E4B3CD4B20",
+      "v0_ldt_key": "AF35739784D80E88F6AA2948111E905B95DE18AACEF41358A1A1C99D9C95BC67D5A62523FAA0E929AA7F83479F1A5D30FBB8E349A04A085F5AB28562B3BBC681",
+      "v0_metadata_nonce": "0A9504BB121E12DE3E8BFEFA",
+      "v1_metadata_nonce": "C63F427096971F1752F6221F",
+      "v1_mic_extended_salt_aes_key": "01EC1263EDE7F6EA26031C28ED8E6EC3",
+      "v1_mic_extended_salt_identity_token_hmac_key": "58C76F2F174E8B026E32BA4A09896898E39A21A4B4C6AFB5CBFFDD3F17217686",
+      "v1_mic_extended_salt_mic_hmac_key": "59DD79D819E0C6E50E4189CC394B7FDE1D1AF17E29C9EEC63727F64430A1DE9A",
+      "v1_mic_short_salt_aes_key": "AD577ED2A071A7EA5BE2DBE3D9440CAE",
+      "v1_mic_short_salt_identity_token_hmac_key": "E6A27A0F3EA35DA104296F11B2D3D7104DCDD066FDD3930D2A8B4370C0497F27",
+      "v1_mic_short_salt_mic_hmac_key": "F5D81846C62C536E3D6B19F3A33F6DAB14C708EA183E6D598DABD4D567DB2C21",
+      "v1_signature_identity_token_hmac_key": "03BD502076C9F2CA53775BB46DB7D0120297DCE95C9D455EBF0D5C588A31FCA7",
+      "v1_signature_section_aes_key": "8559B5ADD92D397DE221F606DA353EB6"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "5B56",
+      "expanded_salt": "0F102F204F31D295E3B9FF1956F387E2"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "23CDFE5E54488821F6ED0A27",
-      "extended_signed_metadata_key_hmac_key": "2B262B746F258E0AE1CECEDC1C761535AB94C83536FB700A8C005F7DD418C388",
-      "extended_signed_section_aes_key": "DBAABCE45273B37FD68B63B6957E5785",
-      "extended_unsigned_metadata_key_hmac_key": "E177B64123EFE628A6268D943BB7824EFF52212CACF2E3F59C07E165E4F16637",
-      "extended_unsigned_section_aes_key": "B085C6252DC18DCD41D7F83A28071565",
-      "extended_unsigned_section_mic_hmac_key": "32CD283375EF7007BA71BA8540A8AA91A05AFD714FE80809A9F92F0754F3CC72",
-      "key_seed": "2E489B259C0566C778FA0ED37E2E7FA41DF3A664CAF4EF875A4427EDB479BB23",
-      "legacy_ldt_key": "68682B202007B1B9DDA6871451170B1B093C2A051C7C5416E71313ADC01B5F61C9E849A9C2C1CECC480F8658C6A649F195EAC21EC41749E4E9034894B1A13462",
-      "legacy_metadata_key_hmac_key": "9B6569331C5F5BC18C8444EDE38A946F59FF525D71C9DD3A0C0AF93BD799EF34",
-      "legacy_metadata_nonce": "A6A810AA0DCBD8FB7C957713"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "D6CDB0E747062077D2AAAD00D039B656",
+      "v0_identity_token": "EE348B9FE43A8F854A90FD246EA3"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "76E3",
-      "expanded_salt": "DB8C04B9A21DCCEC75C4D2C7806F57AE"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "F5EC",
+      "short_salt_nonce": "A1FC444CC695E3DA1BB17AF0"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "0243B20CDF5D6EC2B5F40DE90BD517C6",
-      "legacy_metadata_key": "10D3F1C4B9422B343140B77FC230"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "04AB6D4EDA19FC328F24C74CAC5C5DD8",
+      "derived_salt_nonce": "9331647748783F90E401965AC5B87106",
+      "derived_salt_third_de": "42CC023EEE82C7E84914B2E8190BF169",
+      "section_extended_salt": "B877CD0EC34F48EF8AA9E00297D82C64"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "CC2F52EBC86DB28E36451CDAF13079E4",
-      "derived_salt_first_section_no_de": "DF2CF0AF9608449FBD2545BEBE111B51",
-      "derived_salt_first_section_third_de": "0B60FF977CD4A5D900642F660C496EC8",
-      "section_salt": "3D36E649B2617F943C57E5845043BD7B"
+    "key_seed_hkdf": {
+      "key_seed": "B6400810BB927EFD733610B7E06F0D73DF1AE94274346CE0AB15CF95EC2A074D",
+      "v0_identity_token_hmac_key": "663D9BFAC9D198A1874C1AF13E986B22D94CEA5DB82FB8CC26DB95BCAA78C363",
+      "v0_ldt_key": "52C970B142592DF27DE130EF7C1D51F6BF2D191E046FF815A1C340AD8A10CC7D1C7A917F1989706C7E1AF8749BF7C2E97388BAFFF56EFB7DCDCEDDC3DFE1B42F",
+      "v0_metadata_nonce": "1340C3F2C220EAB0864590BC",
+      "v1_metadata_nonce": "739B0EB3925CD78302454F25",
+      "v1_mic_extended_salt_aes_key": "983C9CBDDD7F5D76D9D501819218974B",
+      "v1_mic_extended_salt_identity_token_hmac_key": "A07289F3FCFB896BDE3289DD42B12D30C2440FA3A556DD58F7C87C305CFFA533",
+      "v1_mic_extended_salt_mic_hmac_key": "41DE5A06B3108EFDAFD5B8072E5438D0D411EC26C46C2EF62742D31E7601442D",
+      "v1_mic_short_salt_aes_key": "2A3B1E230B36FCC470FD65299A7EDA9E",
+      "v1_mic_short_salt_identity_token_hmac_key": "CCA9DE895F08980C2709A0FD4CF1B835E915BEB5E5BF192D5D30AC918E100C75",
+      "v1_mic_short_salt_mic_hmac_key": "80139B754D19E3896A1E1A5025DD04FC8E699B879484D8294A77EDF648E5643B",
+      "v1_signature_identity_token_hmac_key": "C6D68149007593DD02EE8790467405844C978F9352DDF2545C813022497DA7D1",
+      "v1_signature_section_aes_key": "738E9ABCFB9D5461E3F8E249C5BED287"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "77F7",
+      "expanded_salt": "999BAACA7F0F4EC499FB4984FAD96C5A"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "92124C3375F66E88EFE35C53",
-      "extended_signed_metadata_key_hmac_key": "A0D58D77AF063A719AF9FF4EE9073C16B9BE7624AAB46951032E2DCA7E58F9A9",
-      "extended_signed_section_aes_key": "2B11051C494B15EFF095B1CE27731780",
-      "extended_unsigned_metadata_key_hmac_key": "0DAC827E8E114BE7A5377C68A902C3F9F9ADAA3799DCAB115B4402D6986B6693",
-      "extended_unsigned_section_aes_key": "9EBE28389C5D4037FE9CE66E5815318A",
-      "extended_unsigned_section_mic_hmac_key": "575A04282694E59058B0B62D8EE68F9F97063EB4CAADBBBBDD49CCD58C00AF3A",
-      "key_seed": "0A188883B6CA017E98A98E6F25D090AD8B59432D5978D9B54F41A7E4F60BD880",
-      "legacy_ldt_key": "C393CA2F2A769B0C37F6F394C806FAB5E6D73AE829B7AD0AA58351D65A8436D09F67C076069BE57AE561CFEC65DF13F31A2D78C583835335B9E79D42667884E6",
-      "legacy_metadata_key_hmac_key": "CC4DDD317D155D714F0BCDCF55F7C7596B9593B5A331561D1E075D63605F1311",
-      "legacy_metadata_nonce": "9D15BA91B10DA670512B7F69"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "0800A0075D6EBBB3D36CC9E504DD12C2",
+      "v0_identity_token": "4F53F51F11499E9B94A03C1A12F6"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "0786",
-      "expanded_salt": "90BF5725A647F3877EACDFE93F00743A"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "FCB0",
+      "short_salt_nonce": "7B035FDE619F1DC6DDC82276"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "44412FD614B0F95EB598CBD223F560A3",
-      "legacy_metadata_key": "3A127741C99A09FD4F7416C678B9"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "CF464723DEB71D8CB55827A0DF0D10F2",
+      "derived_salt_nonce": "5DB21C018408C6854005F5854F4FC0DD",
+      "derived_salt_third_de": "4F8C68AF4DFC9F31706823CEC2CFC31F",
+      "section_extended_salt": "F6F7A20A1113FA0CBADEE03D8737309C"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "BF6F7B9731537CD65957FE062847164E",
-      "derived_salt_first_section_no_de": "7A4C1F1972B35CB2F1D8ED706210BF86",
-      "derived_salt_first_section_third_de": "86D64154463B1BA93C139DDB0476F29E",
-      "section_salt": "006BC21D03AFA7BD1316E76B52AF72AA"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "460BECD309A3EE6B35175031",
-      "extended_signed_metadata_key_hmac_key": "63CB2E1C16F5B94BFD5DCF725B322E2EC4CFED49BECD757FA4A5066524875C15",
-      "extended_signed_section_aes_key": "7D3E000289F97FEAC66DBA48492A985E",
-      "extended_unsigned_metadata_key_hmac_key": "3C9232DEF5692ECB5F753CF1CE5AE15F1E251881D947B0EF726D86720C1E03A4",
-      "extended_unsigned_section_aes_key": "A2FCCB33B71B1A8D807DC1D9241584D2",
-      "extended_unsigned_section_mic_hmac_key": "52C00EB623D242318A865DAD8FB76A775F6D5D80F5CF030CED9777F6B704E9B8",
-      "key_seed": "B48D65795DEEF4190966FEA215BB96F6052507BF909CB2DD5345E9FDEDF321CC",
-      "legacy_ldt_key": "22F49B6998414150E646326B75F7AC83CF5DF705B33C582310A963116845F5E00B2937634AA8338AF18E9B45EC6C892708924A0868051A44ED4561D9D091F47F",
-      "legacy_metadata_key_hmac_key": "8A28B3F414B387807148BB37C2C74977C2755E41863DAAC7B15E3B7C006A5E6A",
-      "legacy_metadata_nonce": "1CD3B2E498B4D30985B1E11A"
+      "key_seed": "F100C4BBC5B202507F057930DF27C5920882EA60BC5A770D33793133CF247746",
+      "v0_identity_token_hmac_key": "473297E18614DEF67B65E9C7857176A793754ACAA1530102AA81A082D58F7D02",
+      "v0_ldt_key": "1BB3499572A9AAD2C719A8961D95A1040DEE023D5933CBF52938A2A4880C5339C7EBB4F54DBD9697FF470A5F50171C223707B568E48CC4287DD309A855912E11",
+      "v0_metadata_nonce": "FC4F23D0384FCF4830D1E499",
+      "v1_metadata_nonce": "0AAD6D64DE1D90C577F60248",
+      "v1_mic_extended_salt_aes_key": "17A86CACFCE67DCCCF875B0AEA29A5D8",
+      "v1_mic_extended_salt_identity_token_hmac_key": "93DCD02530F3C29DB62667D5FF2FF8C5EC81341A7F642C0BAAF3D16B82E051CF",
+      "v1_mic_extended_salt_mic_hmac_key": "020BF714AFCC97E16CFED3A9CEE5490B0C72B649DADCB7F0D31E1B9EFEBC048D",
+      "v1_mic_short_salt_aes_key": "C82D0BB3858BCF0818755B90B87467AD",
+      "v1_mic_short_salt_identity_token_hmac_key": "99700748DF89599EC8CD6A0464EC084651BD335DD8D8D82956E405D0994F2325",
+      "v1_mic_short_salt_mic_hmac_key": "FC3B070CECBABBF42ADE9193735C6D66322A12A1C3703C59194B823E5BC460E7",
+      "v1_signature_identity_token_hmac_key": "700DC0F4F8D9AC2B6A190EC7D041F0E6143C99CBB4C39C83419C5CB2382504CB",
+      "v1_signature_section_aes_key": "1D268051F9FCA0FD696862C2DD230F97"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "2619",
-      "expanded_salt": "11CD75FA9ED0CC386D81040E5E28BD5A"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "0739",
+      "expanded_salt": "57E814971788FDBAEFD41AE890DA236E"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "07F23FA7ECDCFFFFC98A7B46B0382C69",
+      "v0_identity_token": "92DEBCB60F3BB1E5D5D3BE1FBED2"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "9A12",
+      "short_salt_nonce": "BADA73EFD61495DD37336600"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "6F0D67269728308DBCBB1DF1D485512C",
-      "legacy_metadata_key": "F3FA997825C9B8CE27114093994F"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "22820FA9A35165D2ABF6C7C256681574",
+      "derived_salt_nonce": "67DC58AD06F080D8A671163E36A4F0F1",
+      "derived_salt_third_de": "B5DE19F288B57D8338FCB5134D7FE17D",
+      "section_extended_salt": "14C2765287EFFFE0153EF93894134505"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "C4394DDD7E50753EA4F6A380BEAA6D75",
-      "derived_salt_first_section_no_de": "9FBAB23311FF8DC91ED5581625BE5C31",
-      "derived_salt_first_section_third_de": "7EA2A3C3E08970ED0ADDDA2EE28989E3",
-      "section_salt": "F7802897A51C9CC8CB4F39237AA5AF75"
+    "key_seed_hkdf": {
+      "key_seed": "D0E768CF45FC018A6C3B9B78634E0D8ECCA218220BAB479F14327CBE273AACD8",
+      "v0_identity_token_hmac_key": "7706A0543B4EB81B1555D989161EF86F617DFE7A93F87EC62D239709DCE3F2BD",
+      "v0_ldt_key": "8B3422872ED8BA90E9378F20B5C066846358D7238979499D2E32DC657C359C0511BDA2624B8F7F1C6BD93C08A7DA716035D720DEBE02AC0A3F9E0CDFC8B979FD",
+      "v0_metadata_nonce": "69E6E6255195E8ABF8C191DA",
+      "v1_metadata_nonce": "2A6B45C6C1A4B8B0AFECD887",
+      "v1_mic_extended_salt_aes_key": "DFD9F7A823818E211715841828D9EDC2",
+      "v1_mic_extended_salt_identity_token_hmac_key": "AD329A4E049CA3B4F621DAF087A88ECC5B318AC36D92EB3604ED1FD7AC997DD2",
+      "v1_mic_extended_salt_mic_hmac_key": "C4C4E9B64AFC367473E38D66B110403284C3C03AE00F1BBADC230C93D5D97BB7",
+      "v1_mic_short_salt_aes_key": "7365641B35E3A6A5863106192EE1E1D1",
+      "v1_mic_short_salt_identity_token_hmac_key": "F4DB33EC5EB13864F6B46DCDAFA9399E0B5F17C111D275C9BD906FC46D45D38B",
+      "v1_mic_short_salt_mic_hmac_key": "107A3B99DFA9D082BA9C5A52D066F0517EB0448B4CD3CE2671D6E191E2E2ED08",
+      "v1_signature_identity_token_hmac_key": "67E13E77F909D2E4A54AF23DF1319098A1CF63EC9D69ADBE1C1993CDA198AD7A",
+      "v1_signature_section_aes_key": "0CCE0847357476165E32DAB0D93660A5"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "D429",
+      "expanded_salt": "B293DDC05C6AE00B98174666AC83A92B"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "32EBBA46F34FE6447FCB9F03",
-      "extended_signed_metadata_key_hmac_key": "9D457FEC0DED032FCA7C4663788C3413D985458D3B7DD7C77CA808CEC7A7EE23",
-      "extended_signed_section_aes_key": "C89264716F9ED0AD1A591D4E7B535537",
-      "extended_unsigned_metadata_key_hmac_key": "A478F927217CE04B52D69110C6FC26C773B68C9CF862C586474F498B38AF736A",
-      "extended_unsigned_section_aes_key": "726704CD26B963222D3ACE2E8510FDA8",
-      "extended_unsigned_section_mic_hmac_key": "6912E3F7E93AA50850A908EBEAC4595C5542BD274247902C4EEE48E9EE5C55DD",
-      "key_seed": "880B555B9AA8600F636A51A179B7E8F577425885545DF12D1CD6E01C87D3FE92",
-      "legacy_ldt_key": "AFD7043040B31C0F86D1CF833660C533AA297AA161B8D2EFEA77BD0B1A1A2FBE3A194A52BF2950CC1E307695D750E818EB6CF2161FC3C1A0AB54B647FF9275F0",
-      "legacy_metadata_key_hmac_key": "CDAC9BFD8382DDBB3941D3051353EFCD37015CA03452C66273EECBEFA22E03E5",
-      "legacy_metadata_nonce": "C80E0C617C7A3CB8D2863481"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "4AA95B37D4A63A1DC834AE394C30EFB2",
+      "v0_identity_token": "B650B408B882856F757A55FC2AB6"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "7165",
-      "expanded_salt": "C7DAAE6B6635A8AB95D2300CB6416269"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "B35D",
+      "short_salt_nonce": "38893FBF7D1BEE182635466F"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "42BCC04B27A64629343177218F57BE0A",
-      "legacy_metadata_key": "A876ABB90315F25712F38499B85C"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "70EF8C20670E91B0201666736D87FFF0",
+      "derived_salt_nonce": "135C24DF13767FF03E133BB99D20B0F6",
+      "derived_salt_third_de": "3B537B5FF5AE9AB51B076C50F88DF467",
+      "section_extended_salt": "BE300446F843EC41349FC25CBE09D972"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "0F6D736CE65D5C65A3B2D6559F5FD402",
-      "derived_salt_first_section_no_de": "554244AF344CA2C83F253966F6102027",
-      "derived_salt_first_section_third_de": "13F1B8E4B07D0A51B509F878A71C50A0",
-      "section_salt": "FA523DAC59FFB86119FFC29D753AA473"
+    "key_seed_hkdf": {
+      "key_seed": "E81B91F6D8C1095C1D42F68DE4CEC8D0976A6491F8AC899966A121D0A6F17A43",
+      "v0_identity_token_hmac_key": "75C08671B8EC4C5FEB972AA4F02FBC725BF42263C5693DB01E9C94ACFBC4460E",
+      "v0_ldt_key": "F53EF38DB4429D264B4C0EE8EC671327060BDE5C340F0CAB7EBD0E9C3BC8F7C4A9B49C0CF4A535DB8717F8E308D28FCA5D9ED92D0EB555C3C810C07558772A33",
+      "v0_metadata_nonce": "7AD4B6D7845B86851A7B4953",
+      "v1_metadata_nonce": "1AD2218AB0B01CCB1181D201",
+      "v1_mic_extended_salt_aes_key": "5BAF591B371061B32B72C8E1E56B8B56",
+      "v1_mic_extended_salt_identity_token_hmac_key": "1EDCEDD0EDC416E7D8DFC2E2CFDFECF6002080DDAE2C1E13AD0E517561621ABC",
+      "v1_mic_extended_salt_mic_hmac_key": "3041D70D7B869284630A49C8272CAA14F3BA2826BAD8EB8EF3FB2CCBEA2AC1F4",
+      "v1_mic_short_salt_aes_key": "24B3475C2818DD82B2B861890B521CA6",
+      "v1_mic_short_salt_identity_token_hmac_key": "17C3C1FEE32642EBAAF0B4D9F769346394FE12D0EF967535CA1AC7C9EDA28770",
+      "v1_mic_short_salt_mic_hmac_key": "020D702CD9F9FA8D8D4214F4C2E326DCA16E782AB23DB36CD4A846B36312605B",
+      "v1_signature_identity_token_hmac_key": "CBCD66F3A1395C1C119396056A69180BF48BFE314DC331E78E6B81C06A897168",
+      "v1_signature_section_aes_key": "7630A756E997DD3EE511628239C2EB42"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "BD6A",
+      "expanded_salt": "CC3CFE649FCEF543F9D77FC2D14F7DD1"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "2DF7FDECC381703DE06DFC8B",
-      "extended_signed_metadata_key_hmac_key": "002E77DA262FAF5BEFB7668D09710373FD69821DC3F563320202DFC6F7F8DAFF",
-      "extended_signed_section_aes_key": "A9B5C0BC08AFE740B6FD1E6BA8220E53",
-      "extended_unsigned_metadata_key_hmac_key": "2FB80909E593CEDE519FA19B034FFC6D189E0B772B0C4FC686C3732C82B34F19",
-      "extended_unsigned_section_aes_key": "E8D6F9C80CCF633B9F37574DBC38ECCA",
-      "extended_unsigned_section_mic_hmac_key": "4A3E71A87ED54DFECD1FC9EDC09371D329E8B231A189D072F827863C3377FD7F",
-      "key_seed": "B641968304F3D65286618FE936844A883932A98F172FAEB1CF5DC6E77007488D",
-      "legacy_ldt_key": "9D925DBD151593EAC40F2C7FEA7BE2EC81891FE87971CF1C7F38369BDB8B12EBA067EEA5BCDDC36E5D8430CC67C9F8C85C272E868D75404E64A087FFAB92D7CA",
-      "legacy_metadata_key_hmac_key": "F8AA867BD12B81AE663E62C650E705DD1F2A1D9ABE38A99A2E1D51EF7E2DA26A",
-      "legacy_metadata_nonce": "46D5A1760D44590DCE76FCE6"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "F3212F08D87FA7C44FCDA7E2C4A0F0C4",
+      "v0_identity_token": "ED93E5F3F71967B749570A37D16E"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "587E",
-      "expanded_salt": "D3CDEC97B00233FD542022C2EB5F3848"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "22FD",
+      "short_salt_nonce": "32484DC7B135A80C39081760"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "0D01ADC26CDE59CB3350AC0374ADD752",
-      "legacy_metadata_key": "F9664E87F9E82683417A6C69FC6A"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "78CE8B13F129273503E1536058A43149",
+      "derived_salt_nonce": "C7F3873BDD429C39E5560D733BD3AFCC",
+      "derived_salt_third_de": "9795B0AC7799E44E875429A7A24B1547",
+      "section_extended_salt": "F86AB59158954445BC6366F8E823155F"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "D2B338926A1201F6CD659AC639B1862F",
-      "derived_salt_first_section_no_de": "B3E7AA07BD4FAFB10015CD6C196E067C",
-      "derived_salt_first_section_third_de": "4D84FFA3D9A34766CCBD37DF356A9FFC",
-      "section_salt": "1D99919B07153CBA8C4D3311A118B9FC"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "F8495F6B16683501AE5B19E7",
-      "extended_signed_metadata_key_hmac_key": "75B5C62C60C3019BC2D9D4CD91CCAA2E76D33EDC43AD13384123F3B5D52D67C0",
-      "extended_signed_section_aes_key": "BCC8DB06C53733E8F793BD7D64DBF46E",
-      "extended_unsigned_metadata_key_hmac_key": "A90E9E7F829DDFB44D1C10AD1BC1BF08401781B8C22FCE5DE73AC6DC0AC66BEB",
-      "extended_unsigned_section_aes_key": "9F6E49E8CAD1919314298C3A354FC392",
-      "extended_unsigned_section_mic_hmac_key": "C316406D229A27AF3CB3C13E906287EF8B5036B658FE824A076C8B6E74D2B885",
-      "key_seed": "BEB2E5288A42FD7AC9E4BE6D4998F32CA5AD102DD6BBF21D9A63E92ECE14F7C8",
-      "legacy_ldt_key": "7B180A8344D1E4A7E154BDE1DFD16888E3778686A35DCDB75485EF2A93762E0932D6D79EA0826E5ABAF79DEC504B7D539C6324CFF9B7D4C1F19B60B65D3B9BCD",
-      "legacy_metadata_key_hmac_key": "BE281084F50E511096BF52D7FAA5AE64949796EFAD2615C4FD16495016C5CDE1",
-      "legacy_metadata_nonce": "42ED5DFB6ABF8FC2CA75DDB4"
+      "key_seed": "711FC606359D4A42B851EBE5E51877DB08F075A7D54C7F3EA2D1FD4D1CAE4844",
+      "v0_identity_token_hmac_key": "673BFCEA7A139058558C1767A4466B9CA9EEEE6AD126C437828F22B5C2A7AB8B",
+      "v0_ldt_key": "4073B6432E80244E5311E22C3E2FA74C303C98CAC85945E276DD8D77D31D6AA6C3E89897D3271C35CB5A4A4D3D744D92934926FBC81CC264DB701F5DF56F5911",
+      "v0_metadata_nonce": "3CFA4E242A06A8E43E7EB43F",
+      "v1_metadata_nonce": "98FDBD9939089072019C5C42",
+      "v1_mic_extended_salt_aes_key": "FF33518D26448E2D4DFA5A67B3B21474",
+      "v1_mic_extended_salt_identity_token_hmac_key": "3142FEDB54903B750D43E984D94D5501BDF361B27994737A0A77A22E87C2778E",
+      "v1_mic_extended_salt_mic_hmac_key": "110D45543242D23BD5896052CB4EA44B9DA4AD1B6D6E4D5D795D7F23262FBEA6",
+      "v1_mic_short_salt_aes_key": "DB686DB1702E76B6A2E835D249E0143B",
+      "v1_mic_short_salt_identity_token_hmac_key": "4BC8DCD602C7894832F1CEC3D1C715B9EA2DA59AAD81D50108E4CEF6404A92E5",
+      "v1_mic_short_salt_mic_hmac_key": "BA49A19BBC61BB50BEC6F2C102E4D099DF3D204E3C5CD6C7CEFF4F22B62B5804",
+      "v1_signature_identity_token_hmac_key": "685A096405A541B4BA786E24DA79E8780FCD448563352CEDF12A7746A7808D75",
+      "v1_signature_section_aes_key": "737D543B38D9BC756BA6BF8D0E345839"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "9288",
-      "expanded_salt": "EB04D1DB34235FE2814ABE28338AF6BD"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "6328",
+      "expanded_salt": "C41E3A15A684898BD69AB353CF9403D1"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "C248C7A2DF1596ECF285CD70F0B8FF6B",
+      "v0_identity_token": "880A532423DC3D24F9B01EB74747"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "703C",
+      "short_salt_nonce": "C2533EAB891577CD94E07FDA"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "E1478A4ABBA0D34E1DBB8440636BA1DE",
-      "legacy_metadata_key": "3B2669DBE04D22E4146856F24964"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "DD5E5F7DB850DB95FD63F5F6097094CA",
+      "derived_salt_nonce": "40BF0CC82087F3D8EE4E1070EE4E0D91",
+      "derived_salt_third_de": "D14D7004CA8C8776275296A563487325",
+      "section_extended_salt": "B774D26C0DA92679B9F85BA62EF47501"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "2315A6A6A255AFDF9CB9BC23D7481C70",
-      "derived_salt_first_section_no_de": "2F871D368ECA88997CA4EA877F8BCDA1",
-      "derived_salt_first_section_third_de": "F4D547600C7C3CC1ECEB4D83C821D191",
-      "section_salt": "D82414D60C56BC7571AC11EA22FD95A1"
+    "key_seed_hkdf": {
+      "key_seed": "60A9A540CBADC6792695C2F83BFF8862F8345D9E52B8356398F78EA5EC5649FF",
+      "v0_identity_token_hmac_key": "1F216C2E0B900D0689FAC04FA32ACCA32D420E041AC5CAC6651CEC29ADF20FE0",
+      "v0_ldt_key": "ADFA524EC6DB187734F14812267DD46299AFF92C96CAC67AD898E0C2B51B00BA007A8C226C06BF0A711669687830D4E6A7C8A7E9AF8FA427A91A461D7BA38D77",
+      "v0_metadata_nonce": "51DCDFFCDEAA8FB85DF5D077",
+      "v1_metadata_nonce": "B9DE8526EBA518CADE736887",
+      "v1_mic_extended_salt_aes_key": "BC00D162975B27283A910929DB17F7D6",
+      "v1_mic_extended_salt_identity_token_hmac_key": "D9D2FA28386C9FCB05171F063FC464630CE1CC52CD47B6B235814B77695DA0FF",
+      "v1_mic_extended_salt_mic_hmac_key": "55BD72D9FE9ECD40946E57894549E0B468E0A62E2BEC97FD744FAE68C45783A7",
+      "v1_mic_short_salt_aes_key": "8240C926DD7C8887AB1D99C10D994BD1",
+      "v1_mic_short_salt_identity_token_hmac_key": "01F8D73775C16E0B2165023A3FBDF31F2FAB14F9269B26E0397D9F75105CB4A3",
+      "v1_mic_short_salt_mic_hmac_key": "4DE142833A00DC99C9A98AB93893AC000BE38083B7F31F3FB112E93B32B7F3E2",
+      "v1_signature_identity_token_hmac_key": "2D31AA445FED272EF8CC6C8BD869D30F1F7E2D3D013BC179AD19ECB59DBDA3D8",
+      "v1_signature_section_aes_key": "4548FC98A94CEB5037232B78069656F1"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "BB38",
+      "expanded_salt": "DD8BEE6CB4F2F732966A4334D98A6A6F"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "3225229E97DE8EF6EF3738A4",
-      "extended_signed_metadata_key_hmac_key": "8F2D19779DE3D0BD45A93F9D546C7D70BA7D24A3039FC10FBED142099A7E48B0",
-      "extended_signed_section_aes_key": "552CF2D3F72D8AD6AE4613521D744069",
-      "extended_unsigned_metadata_key_hmac_key": "4DD906B20F0F537A4759DEC498B3F5F35A26A850F458B7AF9D140304C138DA3D",
-      "extended_unsigned_section_aes_key": "E915FB586132C17710BDDBBC7BE734D3",
-      "extended_unsigned_section_mic_hmac_key": "00FAB34E3EF9BABCC38BB24476583539C41F2C0CA69FBCD21C278742108158B2",
-      "key_seed": "B835493E7A34BA1C0ED885CF78EB30CED4203067CFB0E3501CF1509C44A10EA4",
-      "legacy_ldt_key": "63DDFA294943D9D30A1B8A911EF3C9643719AF8B1E30F6CC7FE9EA490E1F5544D22A65721399122A0A6324F215CC2FB178447E5D9F7F54FC326B994C27B257BB",
-      "legacy_metadata_key_hmac_key": "15C70495ED3FD7EF63650611395B8BD9D6015DEDFE41685077911059081639BC",
-      "legacy_metadata_nonce": "BBE949BBD59C4F963E7E2821"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "58EDE47F3974330EECF1A2E5AC5659BF",
+      "v0_identity_token": "0848F6E9EEA846D61D0BD7F3145E"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "54C1",
-      "expanded_salt": "0DB76FBA8F6AC46D1C0E4D3BF4F9760B"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "DBCD",
+      "short_salt_nonce": "8DC07DE237C5C0973E1F142A"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "6815ACAD17F576FCFAC31D7CA348B9C7",
-      "legacy_metadata_key": "D56BC285FCBC28F14904C110D022"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "53F10F9CA5E77717F922663B49169B24",
+      "derived_salt_nonce": "4947E98A773F91E71B31C94603FE2A27",
+      "derived_salt_third_de": "82576C8D1063FA9B9CFE825EC4BC8D44",
+      "section_extended_salt": "71D1DA2128B0D32EDA53591B8B1E2214"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "A63D90F8D31E70B5F12FA49567853940",
-      "derived_salt_first_section_no_de": "DBA2B7268B2EDFCE658C2DF7D1F50E18",
-      "derived_salt_first_section_third_de": "923531B496A572D8E6C45ECDC3598D78",
-      "section_salt": "228003CED4DE76F9E2E12998C3ACF2ED"
+    "key_seed_hkdf": {
+      "key_seed": "DEC336D867DC86E9DA91F93D9F113D8A99EC2AEEFA5DD7076B98CCA93B775711",
+      "v0_identity_token_hmac_key": "36AFC7B183AF0C8815E337171C0F2B1ED7E3CC03C462D8360D86A9FBE61F763D",
+      "v0_ldt_key": "9A045E5092D0802835C2D77F9AE24B0C6572E27E10734AD0D7472C087E5A5BB1D94FA44F1094BF3C77640E483C4798C744602EA06658E401F3DD13DB5E9F2978",
+      "v0_metadata_nonce": "02E64EE4D3783A3B84279BCD",
+      "v1_metadata_nonce": "3BEB93AF74FF27D9CB898AB6",
+      "v1_mic_extended_salt_aes_key": "A981814F6939740E0FE916C93350E687",
+      "v1_mic_extended_salt_identity_token_hmac_key": "11CAE48A1DEE7B9BF057D721989DD4D6EC5E3BC7057B572EAB0EC91EC246C51A",
+      "v1_mic_extended_salt_mic_hmac_key": "2827D2973DA029745965B868ED9634EB7394D442143E79E72CD09BBC9F59F0FA",
+      "v1_mic_short_salt_aes_key": "5013ADB5508464143F2D2A96C807E459",
+      "v1_mic_short_salt_identity_token_hmac_key": "859EAD154B93576E407471495FD37A54193F9CFEE0078342EF48F677E2A20AEE",
+      "v1_mic_short_salt_mic_hmac_key": "2C5C56B063989DD245791D8398E146A585C4F91C8C29240814E606C0AF4CB6C3",
+      "v1_signature_identity_token_hmac_key": "A7A0CF65D8121AD48101E5A10512CC09F6B50FC64142A030188B6ACA57EE2219",
+      "v1_signature_section_aes_key": "2AD28911FAA0C9D0FD4295ACB09823F2"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "C6E5",
+      "expanded_salt": "77DF9D9647500CC9FD17BDB3F4E68BE9"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "9D7BBE2671E7D6C30CD6F810",
-      "extended_signed_metadata_key_hmac_key": "33EB25BFFFC7B15197FAEB32C928CF5A128D01D00735C3EF7727F1F761F51BAE",
-      "extended_signed_section_aes_key": "7D5EE6F4E07F8C5F07E258ECD20D4D34",
-      "extended_unsigned_metadata_key_hmac_key": "792E4F635AD3FD8AA5054F8EC58AADF6BDF7471EDF9EAA2445C944C3C10B6463",
-      "extended_unsigned_section_aes_key": "9DDB1E227FD6E4F9FA7D29BB7A5A1C4F",
-      "extended_unsigned_section_mic_hmac_key": "9611D448338D20F3C6A6464C9CBE3841CD703E6FCBEBAC5EB44D3F5F5D6997B4",
-      "key_seed": "D116B27178BB5F9D9979CBAA97159E4D4410DE3D26CE2AC3ABA7BDAB63A6B85B",
-      "legacy_ldt_key": "5628DCC812A21B82C0619D613EE0E4B67082C89E316D23EA3409563EB8E297377F472FF25C4A0A70B2889BB27293873724B8C0E94A83D9EE2E080D3A5CFE0429",
-      "legacy_metadata_key_hmac_key": "76D7B1B4B3E7432884C9A49F46FF26F1DCE8D583344AFDC7A6635254EEB5CFA7",
-      "legacy_metadata_nonce": "3C2CA3B55509876DACCE58B2"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "920EA91C74EF23E1607E7112BD9F5783",
+      "v0_identity_token": "190E0DD127CCE7E0A7B69779408D"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "A386",
-      "expanded_salt": "F59E3504B2B86D465989D0F5B5F04E7A"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "BB49",
+      "short_salt_nonce": "063DDF6B3AE8F83186DF04FA"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "D4522DEB87180CBAD14338BCF3B1107E",
-      "legacy_metadata_key": "F1A6EEEAEFD437330DFB706FBC43"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "64F5ECC15737A6BD0CD7148884A7389B",
+      "derived_salt_nonce": "FF7ACEE09C30922CC4367ACD2014FBE9",
+      "derived_salt_third_de": "A4B5B9D6F05A84BC5AA915AC954CA94C",
+      "section_extended_salt": "2D31BE7AB1A84EDF74D9B66B6DF3E7D4"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "06C173FF92776658E2DB2394907E31C7",
-      "derived_salt_first_section_no_de": "CC2D079DF426A5359DB15CF4DF6C894D",
-      "derived_salt_first_section_third_de": "5FD567F78807129F25151725F939CA7A",
-      "section_salt": "375DB6FAE577F7F2BBB1483161B62D98"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "4DB0F75206E7B33C55CD1A87",
-      "extended_signed_metadata_key_hmac_key": "F01DA0E7048DC47AF21AE6E1E00705711F397C80E4E5872FD484CB2A353EA84B",
-      "extended_signed_section_aes_key": "EA0D9B928C271079D8DE740792217A2E",
-      "extended_unsigned_metadata_key_hmac_key": "0FE25C53F513D8C4079663AD26D3EFB76C9E53403A32CF3DD1AC5C4F0B46D401",
-      "extended_unsigned_section_aes_key": "BEFF54630EB0851BABD6C9080B3650AB",
-      "extended_unsigned_section_mic_hmac_key": "41E49DFF047BB3D9DFEBB5D86CA2FED61CCA22A8DA4AFE08010B4B2E026605DC",
-      "key_seed": "8E94D64DA399A2EF9428419B72C2D6B96EBD83470CC5894564DC624BCAB5F437",
-      "legacy_ldt_key": "2B802CC087114906FA80A9C68E0991C1D99CAE898EF25F2EF7FEC84E4B8F194AA4608F022C7912B8879BADAF1689140CB37BD15C4646BE36C61CDE4199AD27B3",
-      "legacy_metadata_key_hmac_key": "290F16A7EF5A15726241870605899132979F304FC452B6E7D3CB80822AE3DA7F",
-      "legacy_metadata_nonce": "9FDD6FDAE7D43E1937EFD1B8"
+      "key_seed": "D91552CA6C061EA51B80DDEA22465FDC3B0254D5F3D78F066E085A2711FE31FB",
+      "v0_identity_token_hmac_key": "2C3247978B974787D1F1037BF6C69B2F65B26B74DDD33F83EB397BE7E17B5FC5",
+      "v0_ldt_key": "2FFE770039FC6E6C2CCBA99EAEB23A3788FDEC93C54EA11CEC0E3CF8158FD17614CEDD82122608FC93B4831F1FB8AFCB724FA79F5F8013395D0B20273BC895BA",
+      "v0_metadata_nonce": "681F0C626EEB0EF9102F02CA",
+      "v1_metadata_nonce": "ED7B5E3B33DD153551E7EDFE",
+      "v1_mic_extended_salt_aes_key": "6E2FB2E25A3DE00492B7D00564EE4B9A",
+      "v1_mic_extended_salt_identity_token_hmac_key": "6FA4C0C757B012B5475EE27B9ECB13D0DEB28B11E050E62084099848AECABED0",
+      "v1_mic_extended_salt_mic_hmac_key": "5F0411676B1FA1C17C08568A88BB477DA9C2016560888D6D791D342CA8C9B812",
+      "v1_mic_short_salt_aes_key": "AC6C5BDDE8C655C7354206D748065E45",
+      "v1_mic_short_salt_identity_token_hmac_key": "7F04F2F8BAA4D0F564D291E17E00A4EA547A33D22C144608D6CA64D048571502",
+      "v1_mic_short_salt_mic_hmac_key": "6E2F84D1A9BE9234BBD6F8138068B7520B8E056B1B0949EEA5B720972DDA5D5E",
+      "v1_signature_identity_token_hmac_key": "983863BD77F0A3A79443DE302AD320F51EDACD4E735385260CD8703414AC897A",
+      "v1_signature_section_aes_key": "E62E81BBD49561DD9C20FDD414C1BF8A"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "1161",
-      "expanded_salt": "CAFA25E3F8E00053BC3825E8FF5D5078"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "C20B",
+      "expanded_salt": "20C6AC9CF63183FE2C3578F69D5E81CD"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "D918D076ABA54A6B7FB5406522616208",
-      "legacy_metadata_key": "6C7A1A6D9A337FD3FE62B2E2ADF9"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "4E76651BCC3F7AFDEC8631FB3156CA56",
+      "v0_identity_token": "70DE62B41AE48BFBED5AEF89B4B2"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "FF14",
+      "short_salt_nonce": "9EC52904170660BFC9946FD0"
+    },
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "D3DFF59C7AEA3C8E820B92E76B80ED03",
+      "derived_salt_nonce": "16DE3412C96734F65C24B77E6A597BAA",
+      "derived_salt_third_de": "73FC3E0306353363F7AA4F8910C1F86F",
+      "section_extended_salt": "F0181D6CC67C40620D61C0377D06627A"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "D2C405851BBC1268709BCA5E92ACB57B",
-      "derived_salt_first_section_no_de": "8F84A4AB6444AFD08FE7110D0D16C74D",
-      "derived_salt_first_section_third_de": "4C3C84632D69672E8921C5067A7DFB56",
-      "section_salt": "5D950CB5E7D9F44E9429359415FED1B4"
+    "key_seed_hkdf": {
+      "key_seed": "D6B3A8F4CD3E869B904A4B1F0E34AC2F37509D40592A3884C3E69067632ACCEF",
+      "v0_identity_token_hmac_key": "D27AD5EC8129C043262F5A6F48B2B1737A5DDB08C6205D7D7CDF0FD8BF9D87B8",
+      "v0_ldt_key": "3B685DC172EFD153B0C6875F3E52B565A97993C96DC3FDB476E47A0945AE33B11B47AB78301330A079D7E68E6F89925EA528BED0EE437053FA1C0CCF9C294059",
+      "v0_metadata_nonce": "D3F45B2A2A8330C89A0D5A8C",
+      "v1_metadata_nonce": "3FD567346D88B78F94C5369F",
+      "v1_mic_extended_salt_aes_key": "E01D5674316E8357CB925409DAC3BE13",
+      "v1_mic_extended_salt_identity_token_hmac_key": "26EA66B124B9E03456EC07CEF36D6A24F1CB985CCE9F8285DA4139547D71B341",
+      "v1_mic_extended_salt_mic_hmac_key": "9EB1CD357FA7BB0AB5B17E95661DB20F30A9AF2DE76F06CDC6FF19D4B533A15F",
+      "v1_mic_short_salt_aes_key": "949AE776AA4D9F7B80E23D25EC3F0CB9",
+      "v1_mic_short_salt_identity_token_hmac_key": "58B69B49CF4DB03F5784823D95C94E868AFE1B1AE547131F32A0410CA8B4C205",
+      "v1_mic_short_salt_mic_hmac_key": "166A695C761FE5D1C0CA5577CDAE59BADC4D6B34ED6A02FF8C7C5B9F9D829D34",
+      "v1_signature_identity_token_hmac_key": "B8A5017381D6CA6EC9A1CB4676F00BA3F3F345BCA33C348CB7CE60AF1A9A7279",
+      "v1_signature_section_aes_key": "8BF7B8345F2B0BE8DB7CBE7E96D9E3AC"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "F061EE619FB13FE95DE267E8",
-      "extended_signed_metadata_key_hmac_key": "F276A731C51D5936687E7F908F88224BA28BDC2D6A4F94999765822DEE917D6B",
-      "extended_signed_section_aes_key": "CD9E5F7C922F58A56A17CB8F62B9DD09",
-      "extended_unsigned_metadata_key_hmac_key": "174295A2B19CD4F9A821895D098CC63CB4CF17AC16CA7BD0DABF7136C2D3D194",
-      "extended_unsigned_section_aes_key": "1CEC9AA427A98480A6642003700F4A5A",
-      "extended_unsigned_section_mic_hmac_key": "6780C7E76F1A85B3E31A1382505FE21BA04AAFE268C9810C626E72C9203C638E",
-      "key_seed": "234DD6BBDADEA172BB97EA68E22A0B0321F8DBD65EAAAA38F95940D73770F35A",
-      "legacy_ldt_key": "7E7C646B51F1867F6D7CEA56F2AC7EE80DE9D9DE2015E9CBD7525ABD0FA05656CAEFE120EEEF521CEB7D9E296555CF8A249862D97F0D3FCAEDE9A4E8C309092E",
-      "legacy_metadata_key_hmac_key": "1DA50172EAFA031A15FADA0313BA770324BC8A0D1223FD5EBC127A331459F2B2",
-      "legacy_metadata_nonce": "74C72A883D6BA2CFB5F68830"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "30D1",
+      "expanded_salt": "F75AF6269831BE87E645A48C8F2731FF"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "8057B45E2557983E5B173883A75396DC",
+      "v0_identity_token": "963C9D4D52A09EC1EC5BE7FCE09C"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "F836",
-      "expanded_salt": "F8809BFDD69EE831A7686D8359BBBAE4"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "AA48",
+      "short_salt_nonce": "3BE65DEA17A00BF9D49A1C38"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "A97B037072AC4D59472E20AE1C8688CE",
-      "legacy_metadata_key": "4E18A6F5EEDEDFAE9AEE43A53094"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "2B99B7DBFD1AD40DBE93E10E259A6188",
+      "derived_salt_nonce": "FA549B59FBB527C1422799C1749F0304",
+      "derived_salt_third_de": "7570E91C96332E0248AB19ED32AE2A31",
+      "section_extended_salt": "0F3D3F0272988D59B1F3B1C6189E7E89"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "146332F0DF9B6701D5F6ED9173B966D7",
-      "derived_salt_first_section_no_de": "CA5CEB6D8BA41402859B0706E603C25D",
-      "derived_salt_first_section_third_de": "E93B2C310878FED27F89298CA9F77731",
-      "section_salt": "EB9DA18BDB815DBF4BF9906E8AAFB491"
+    "key_seed_hkdf": {
+      "key_seed": "CD466940D42BCF990370FCB55B63201CDA22DCA5BBCE0CF1DDCAE350E8B085E1",
+      "v0_identity_token_hmac_key": "683039034AA4C0DCEE3755EA41EA4A3DE983519B99FB961E8E8ED53FB4A96483",
+      "v0_ldt_key": "4FD74D82D4A29FE1254412C2A8FC74CDE4A10F3646E3CE11A4B34FBF18D8FC0B50597225A7BDC05FBF9791050298EE47ECED684F9404BC82CAA00471CB18C07C",
+      "v0_metadata_nonce": "B15E214FCECA2AF6263F0071",
+      "v1_metadata_nonce": "2B41C0A066C4E06435814541",
+      "v1_mic_extended_salt_aes_key": "849E8DED91FA247EC22FCE659E4458BB",
+      "v1_mic_extended_salt_identity_token_hmac_key": "6709E5FCA4295FD242F0EA72816B18EC308677791291539D458CF0C1BB3F2F09",
+      "v1_mic_extended_salt_mic_hmac_key": "A0CB3B5F81BF78216E18957A46B0DC4E38570FE1C69B1D6E06D29C3E2D56C5CC",
+      "v1_mic_short_salt_aes_key": "A67440D7A821523A84FCA1633161125E",
+      "v1_mic_short_salt_identity_token_hmac_key": "7DB92F89448C4D4059E48D199C36ACC879F0E450B0D8EECABBEC8C387B6A160F",
+      "v1_mic_short_salt_mic_hmac_key": "5378B75FE4E4A8505FAA231CA43CB0DFC1BFB53D07FFA2FC61391214AF21D293",
+      "v1_signature_identity_token_hmac_key": "31F296D8BBC8E6C8D877190FF808746B522B781F5D98E234DE608194E60A928D",
+      "v1_signature_section_aes_key": "C1E5CF90392F864D15390E483F08562E"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "588AB130BE0197CBF1E240AB",
-      "extended_signed_metadata_key_hmac_key": "948A762DFB558385CDAE56420D2AF863F8AD6ACB9EC442775C20F1D12793D08A",
-      "extended_signed_section_aes_key": "FDFA11ABDAC005CD0C6DBAFE22CB84F5",
-      "extended_unsigned_metadata_key_hmac_key": "A8ABEFCD7AF7D09F7862E825FC5D641A23CF32210676F1CB8AFD375F180DE059",
-      "extended_unsigned_section_aes_key": "895814EEFA0514080381B2466C5560DF",
-      "extended_unsigned_section_mic_hmac_key": "770CD91393852E19BB1A5878B8BDA5E4B9AF9A0640856804C470EDE355DB0914",
-      "key_seed": "519521C296DA4C8A13A47835506AA306C9782F3F1C946D061616CA248E64CD9E",
-      "legacy_ldt_key": "84406D27E7717B520A082163CE7DF98BCC0261E8342C105F210C31FDE6B5521547E51414E67D00BDDDB57046564FD491A88E4C5CB913D28FDA7C972EA21A9DA0",
-      "legacy_metadata_key_hmac_key": "24032DA88D3510F18FEAF898A414BCCC55EF1E56350D07B6A4AAAC85AD09C863",
-      "legacy_metadata_nonce": "F0392AFD52B25E66D0D16B50"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "82B1",
+      "expanded_salt": "1BE40D25CD9776534A2DDB5611A72A24"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "902BF2B1A996C43B041F9B420BC96648",
+      "v0_identity_token": "E51F72D506023394F7291D484052"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "AF9B",
-      "expanded_salt": "488FC6BDA553D9894C6C397E009D56F9"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "0C5F",
+      "short_salt_nonce": "408CD97C38B4878C593CA9FF"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "AF67A28D0088401BE912DFF010C7CD2B",
-      "legacy_metadata_key": "0A0692A83815A72C28C2E1A0C324"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "2CB4C2F731719080646D748288FD8A09",
+      "derived_salt_nonce": "C65251DDEEE83B1C0C7B1B6417EBFAA3",
+      "derived_salt_third_de": "E3ADDE6C9B9124A4ABDC4026178DCBAF",
+      "section_extended_salt": "20638EE0EBF0D34A51A53EF7563C15E3"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "B57FD4C4A95B5FA80E77FB0A4342B558",
-      "derived_salt_first_section_no_de": "804C7287DBC4705759D1543D3BB19796",
-      "derived_salt_first_section_third_de": "185B5BE5742AF28DA4D9BA8FFDC8E609",
-      "section_salt": "1D6723044D269740F2E422789242AD4A"
+    "key_seed_hkdf": {
+      "key_seed": "BDBD1DD888D7FDCF43BF9D7F6674ACFF1DF2E2C660FBC745E43313739FAC437B",
+      "v0_identity_token_hmac_key": "FA13440C9B6024C2DABA4BA5D15F00E2366B7846D588199DCB58B38C1C6E9922",
+      "v0_ldt_key": "BE949780CD96A922B71160362959CE07376AED81979109E62E5BBB877A85E6C4EC0414697132AF7771485206B9BE8672D947996AB8878E479DDB3A29AE2C2373",
+      "v0_metadata_nonce": "7D5D37D4DC2C957A04376DA3",
+      "v1_metadata_nonce": "6131D2EB386B29CC1F335147",
+      "v1_mic_extended_salt_aes_key": "04B76123C0DA3C10F84D5303240799FD",
+      "v1_mic_extended_salt_identity_token_hmac_key": "536A471425BF8B929C332DD55423F010A15B4290CD453E78E4D53EE35E285E96",
+      "v1_mic_extended_salt_mic_hmac_key": "B151727B25FF7630AACE3803BCB85CB942EA1638E45F1DFD70663D8B8143A6FD",
+      "v1_mic_short_salt_aes_key": "266FAADB0647BF79A759C9F747F0236B",
+      "v1_mic_short_salt_identity_token_hmac_key": "F4C7A6A7F8F6FE6C77AB0A95A7A716966C7828BF5E7D1A7204984823639ECB20",
+      "v1_mic_short_salt_mic_hmac_key": "3EEF8D8F921DEBFC0B38B2C969488FC7EEAD618EEF189F0D6EE427539133095F",
+      "v1_signature_identity_token_hmac_key": "0B4E8908F5113876B22700FD9505474EBC4FAF369F850F6FEAB2CFFC8C218857",
+      "v1_signature_section_aes_key": "BAD090C852284E25461B7152B1B4D5D5"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "3345",
+      "expanded_salt": "F64A0AF0402F5ABC4D541C1DD1FA7C89"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "E4EDFA97754C39393AEEF09A",
-      "extended_signed_metadata_key_hmac_key": "AE6C660ACBE3D637F9D01CFB4EA927FF4B1DEB1446EB096688BCB54F5C1A5389",
-      "extended_signed_section_aes_key": "DC58AF65EDAB2BDB393DDD7033F781CD",
-      "extended_unsigned_metadata_key_hmac_key": "578C17FC68D03B2A9FFA8352C4ECD9D15DE2F97B5500DB4A9FEE70A93E28AD2D",
-      "extended_unsigned_section_aes_key": "573CC36574396A27D5CD12DE72470637",
-      "extended_unsigned_section_mic_hmac_key": "9D8CFA87DB083EF2FC4C495E6A7E3F8483C608A186AC9EFB6C22144B4A654034",
-      "key_seed": "A34102D098DBC12FBAAFAC0DE0F0959450DF888C7323AF55CF04B976E6B9152C",
-      "legacy_ldt_key": "78065CCC471C92F1328DF45AF7180DCF3073A56F599B861706DB9691DEE1580AAC1FBE1215CF24AF403A0BEA04E27C87E91E24DE63CB61C35576326B32A50F97",
-      "legacy_metadata_key_hmac_key": "E8B03892D17EFBAAFC23A96ABEF538A2AC1B34A9A76E5C32E70D67CA73491EFB",
-      "legacy_metadata_nonce": "889DB55C293C1227BCFA48AB"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "D6DA7B5AE8C730D4E0C56F0D2F3C033B",
+      "v0_identity_token": "155B071F6DBF3F06763299B0DA91"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "4644",
-      "expanded_salt": "8E7143D60063DC6AFF6A285C0CAED64A"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "8534",
+      "short_salt_nonce": "7F613AC000317D4C02D5D869"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "B61F6E359C97537CC9B30A51581DCAE4",
-      "legacy_metadata_key": "D3E6FF7D90D25CD472C672173992"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "C9C285BA4E31877EB2B2E031CFEA0B30",
+      "derived_salt_nonce": "35A6A82C0B520C126652A1376AD58DF4",
+      "derived_salt_third_de": "2EEC85204A794C280C774C8F2C037204",
+      "section_extended_salt": "EC6182A82E58AD95A9EDCACA06791FF7"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "0CA731695CA701A5DA7CA3F15898E7B4",
-      "derived_salt_first_section_no_de": "C576F3146312E1744C5AAB5553880FD8",
-      "derived_salt_first_section_third_de": "39E3A93EEE518127370FD04B92CA2506",
-      "section_salt": "DC536229ABCC11D225875AFE64B192BF"
+    "key_seed_hkdf": {
+      "key_seed": "307F26401920ABF523527895C6DD0F6BF4B20773505B1BE846B0D048F5A6FA87",
+      "v0_identity_token_hmac_key": "17B4D37D0642C6716EB50B800D65C3410F8A04F34927CDF15275E645C59D45FE",
+      "v0_ldt_key": "09BF32231643E1E288D213670302BAB93CE75A5733953005A01478F7A1B8740B5B08277F5A62B3C82F55AA5417E71E5FADC2AFF385B8683DB86832B0735ACC01",
+      "v0_metadata_nonce": "615BD483D3F879E35D3CA43C",
+      "v1_metadata_nonce": "147E38826C4682E5AA5E1678",
+      "v1_mic_extended_salt_aes_key": "2212A788DC4EE1ED998A958A0E4733A3",
+      "v1_mic_extended_salt_identity_token_hmac_key": "D28E7D6BDAD5F4C6B2A9FF58DA57133DC92F4C34F60DC107528B423361DF5493",
+      "v1_mic_extended_salt_mic_hmac_key": "95EE789B7EE2D188C993872CD7F7928AADCBDB063EF37B9BF4A1E42FE48BF27A",
+      "v1_mic_short_salt_aes_key": "FC2D104399AED84B2E04C6EC5B1D6434",
+      "v1_mic_short_salt_identity_token_hmac_key": "C378873BBA5D9686626014ADB68EA7DE71CC905C6212AF0D8659D6D6FF2D364B",
+      "v1_mic_short_salt_mic_hmac_key": "954651192C31CDDB288CB209E9644C9BF38566DAA16B828AB8BA51B40760C5DA",
+      "v1_signature_identity_token_hmac_key": "77C78656128EDC316410216D4A7ABF39E9A83F5C975B4C0672F4FBA61C855342",
+      "v1_signature_section_aes_key": "AF94A29FB78A0FE335FEC5E8B5C8DB93"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "5094",
+      "expanded_salt": "D74D67EAD97A17C8E712DAFC65DFF5FC"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "D28B1E59115D3A1862CA1F90",
-      "extended_signed_metadata_key_hmac_key": "59A3E079DC5F8697219BEB4ED10CBD2BE60551E59B9FC30296574304026D05D2",
-      "extended_signed_section_aes_key": "15491B6EF07D98836A01228ED8225758",
-      "extended_unsigned_metadata_key_hmac_key": "ED9C566500737283B6CBE3288D119DC773E50C22A2736E0F95F6EF4BD583C67A",
-      "extended_unsigned_section_aes_key": "FE8FBBDB0EEEED2024B35F30E49E06B8",
-      "extended_unsigned_section_mic_hmac_key": "24EAB7D66302B766B2BFE287A784673467022D56E7A4706B3EE7BAE1A04787FB",
-      "key_seed": "09A152AA290EC4BF418AFB60B3F66BE866078A5F59FC3BB39728DC80EFDDC5A1",
-      "legacy_ldt_key": "E3265FA3C650A2F0DAF5CBE890CE5430807AD6C7E423858F4D8A47DACD39BD87C5439DC6D952CF87DBD0840736D5FFDF0EB5FE2661A9141B2CB3C8B28DBB7F77",
-      "legacy_metadata_key_hmac_key": "6497FF0A4F742EBD8C908673D696FFA289C2C1DB64434ED082F9D2C7D8862196",
-      "legacy_metadata_nonce": "21BEB33970139E57B2BB897A"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "79BAFF7725786B619730FB1B5E660879",
+      "v0_identity_token": "03829BDD7852E472F79492371156"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "DD89",
-      "expanded_salt": "0F01369801DE549FC4946F13D754CCE9"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "F32C",
+      "short_salt_nonce": "EF90714317133008CC791097"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "EA4CA525B09663EBCABF017A6AF3FE89",
-      "legacy_metadata_key": "0D4C5F0560894C41FA9951DF6EE3"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "FC45A7467A362CB4A7BD1B51717012AF",
+      "derived_salt_nonce": "DBDD7802D5F950078856A9182E623962",
+      "derived_salt_third_de": "3C2970E4D337BA8D15FB3CEB02305027",
+      "section_extended_salt": "6C61349ED1E575BEE8632E0704C63FA0"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "621132026D7F9CCCF26A682B7761183E",
-      "derived_salt_first_section_no_de": "24DAE7CEF38D92425B0C2C94B88566BC",
-      "derived_salt_first_section_third_de": "813BBB1469B4319669B8093FC8FBAA15",
-      "section_salt": "42D31FE0C4495AB88C2CC34728DF59B9"
+    "key_seed_hkdf": {
+      "key_seed": "0E57BC7F84E2B88D3E6E6DD1550544428123BDCE8087A273CEF083D508E9CBEE",
+      "v0_identity_token_hmac_key": "07AC05011C6C5798F10523C178095F30D09C5970D295290BA1C149A8296F0087",
+      "v0_ldt_key": "991B35604C8E261E12252B7EB4A4B5E7AA5D1E282346E35ABC119F740B72263B25F5CD970591A5A691C44930A6D53D17EDE0E6279C0811AD7F95BA846959125E",
+      "v0_metadata_nonce": "662688EA68FB63CBED7727B4",
+      "v1_metadata_nonce": "C34766435CAA1EA79616B2DC",
+      "v1_mic_extended_salt_aes_key": "D6A8080BBF67B2A5F31F50CC3F065D98",
+      "v1_mic_extended_salt_identity_token_hmac_key": "0F61BEE5E9E628D2E6AE6BA85FEDE8644C0F7A67913C12DBB7A9A2CC0F8003C4",
+      "v1_mic_extended_salt_mic_hmac_key": "54034D6726FE5EB1B3ABE64CFDA9F59B27363AE3E31134381439AD97E9D102CF",
+      "v1_mic_short_salt_aes_key": "563BE2F028CCAFACB90F495AB1C6E90D",
+      "v1_mic_short_salt_identity_token_hmac_key": "179DC9E04EF5284CDB8226A46355E771C618934D46052339E580CFA4A1042921",
+      "v1_mic_short_salt_mic_hmac_key": "65238FBDB477D87AA79E6BF2FEFE763D26C95FC2BE480C007318A1FE0A2B2F87",
+      "v1_signature_identity_token_hmac_key": "8548488B07F32D86F41B584DACC3FAA5D1FBE99B85CA152CA358C585D27E8C05",
+      "v1_signature_section_aes_key": "649A99512E6B524CD6AAFB9349EEC4AD"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "99E72102DE2485A8CD026DCF",
-      "extended_signed_metadata_key_hmac_key": "49534D2085653D32DCBD06492CE860375D41A3D91C5517D98759257E6D22D46F",
-      "extended_signed_section_aes_key": "F7091AD2941314A7934D2C74D2D2CB35",
-      "extended_unsigned_metadata_key_hmac_key": "A0BA2094C1CBDD22854EB846FA4DDEC387B0C1E13A7BB833C6E7110ADA47F6AC",
-      "extended_unsigned_section_aes_key": "ED03585313F4DCE95E765913E100E591",
-      "extended_unsigned_section_mic_hmac_key": "EC361DD6B444EAED4BF342CA6479319B2FA391062D6E627A50F40730EFC50B5B",
-      "key_seed": "154EB89F568C5D04EDC4AB0A61DD783D0EBF04437A188B2175B495C8D5019655",
-      "legacy_ldt_key": "171DEF9D37747E6F9D6BBABE17B2F7E049608D67518DE48445AD0FD1901F6016C95AEFBE451AE7E256541B8307A16A7E0B7D259219DC81D4985EAA3FDB743E44",
-      "legacy_metadata_key_hmac_key": "9BC8F5BF79FD354DAE71DE2CA4B91AAC36D6A875D3287D721D3EDB4AF52AB3C5",
-      "legacy_metadata_nonce": "A5C09B3E1E02FC388CE5D124"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "BD68",
+      "expanded_salt": "332DA8CBB87E4B90D053E1463E6E25C6"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "889AA04ADC0A6867A691A37458CCA261",
+      "v0_identity_token": "CA3D2CDE92558D49C685F9B8DFDA"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "42AF",
-      "expanded_salt": "D2BB747C495DD8231531B9149C9C866F"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "002E",
+      "short_salt_nonce": "268B4FA03588CAAD663128E5"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "7831F6B9B71D322F74FA91734B8C193C",
-      "legacy_metadata_key": "6C33C973C499174E058F63F0D59B"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "3BB392471E3DC9FD55124F895C7715FB",
+      "derived_salt_nonce": "D55F2D27065CEC26BA6A3CDA4A99420C",
+      "derived_salt_third_de": "F0C487B8010500EC28E1E162C3ECDA7C",
+      "section_extended_salt": "B6B17B39F5C7EED0037906B686AD650C"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "3D772AF59FA77F172C0C61E75864316A",
-      "derived_salt_first_section_no_de": "45BDC255A9DC922C4D816CE216609E29",
-      "derived_salt_first_section_third_de": "C7256E328AFE8C6ADAA9007494408E01",
-      "section_salt": "3AA898649E58CC9FB5D7CC5FC9B26C7D"
+    "key_seed_hkdf": {
+      "key_seed": "625636745F59A39E26742EB6DF2271730742517E40EB51C3959C5D2F4D70A530",
+      "v0_identity_token_hmac_key": "0C8AC8EF01326F2AD11E8858B69A43B5F4B6B47BC544439D191CD8D31B5B857C",
+      "v0_ldt_key": "0DE470CD371D7AE3F4866BD032BACAA9D7C6704BB978CA8F8C5BAFAE165B410130422C2B4E224A89F436752B8132056C26E1ED10ECE94DF85D223933ACC7E554",
+      "v0_metadata_nonce": "5FC268D65631597D5ACF7E03",
+      "v1_metadata_nonce": "75061912973730B3C8F98A6C",
+      "v1_mic_extended_salt_aes_key": "5F0F57E6CD52CCCCA1454153370C730B",
+      "v1_mic_extended_salt_identity_token_hmac_key": "C7E773D20038E16FA0BDC823C470DEDF88C831B8D9E57B833BF5AEF29F3609D3",
+      "v1_mic_extended_salt_mic_hmac_key": "88229AC78E07751043236D0963BC121E2A4EB6489D0C19008E3210F57277CD90",
+      "v1_mic_short_salt_aes_key": "8C3C9A5E608DDD8195590F70B852C9EF",
+      "v1_mic_short_salt_identity_token_hmac_key": "9D1C4285C18AA6B490C11557CAD4A909ADC0711EB5F0FE493D0A837B84DB9E1F",
+      "v1_mic_short_salt_mic_hmac_key": "95550CE1FF69EAAF1BA1E8AE6560F29F2E068D0C8C78EEA78A6BB8A898B09C83",
+      "v1_signature_identity_token_hmac_key": "3893648367F0710E2E1FD4608C7A7892DF05A40CE1DA124C99F7483016B2A9AC",
+      "v1_signature_section_aes_key": "9E4A0F1FD50A640743596D58FDAB2A8C"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "A14A",
+      "expanded_salt": "6EB825B90A6FA254A206AE337AB5519C"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "EAA31B86CCF3CA56AE358D43",
-      "extended_signed_metadata_key_hmac_key": "52DFF69B372CDF7A4E4844CFF34AFF3B97EAEFC323FD0BFEE409062D8D283ABA",
-      "extended_signed_section_aes_key": "51AE16500538B6819A646EB9AD330FF4",
-      "extended_unsigned_metadata_key_hmac_key": "8772D7EFEF2CA10A882DE8F73A4F5949575BF58E5293FEA863ACBABFC7E12EAA",
-      "extended_unsigned_section_aes_key": "70847AF7A37D9C84957AABC667F6F066",
-      "extended_unsigned_section_mic_hmac_key": "5E659C4B61ED807910D78AEB440F13CA494228B7F0332854B2D54C984254BFFD",
-      "key_seed": "314E89C6DD5D18FE4B824727DFD96F2844000FE144F0BB3AB004B4A8584DEA1C",
-      "legacy_ldt_key": "4E0F8B2D2B62F991AC23EEE229F133DF03674A412318DAE3B7D31B1EB3F5FC1394FBF81FE0CC0088C08624C058F8A4F5069A6B7DCFB26B2C24F59FE8999EADD6",
-      "legacy_metadata_key_hmac_key": "F3595FCB9F437ED53C7BB6D2A52EB890BCA1D8B823722383D76830B50E58086F",
-      "legacy_metadata_nonce": "BE5139909CF99938E465B5F3"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "F32FE88F6A32C1FB8D6A7936DB0D701D",
+      "v0_identity_token": "4D181E23F8727578CDDFFE6C0807"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "E9D7",
-      "expanded_salt": "8143B10A7353AFD1884BD8652E38C1A1"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "B4F7",
+      "short_salt_nonce": "689506D55DA43C98229E35E8"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "22B61200D9EEC2A81E8FAC74ADF6F9E6",
-      "legacy_metadata_key": "6EBCAA307ED2B72FFD2CD6F249C5"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "6C28C3ABDE8FFDA08F9953CB6B24320F",
+      "derived_salt_nonce": "410E75A3CA41E3C125C11FBB5FEF5698",
+      "derived_salt_third_de": "7A39A6831FE1C42FD179D229AB3BA997",
+      "section_extended_salt": "50BAC8BFC5B415D834FB0E214FD0A435"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "9D4357DFBE20C028112104A515ACC3E2",
-      "derived_salt_first_section_no_de": "A23C5D2DB650AD57227C15A86F1BF40E",
-      "derived_salt_first_section_third_de": "7A4B1899973D71DDCB4F92E1665072B3",
-      "section_salt": "68C6FD12C8C56CB58DF60455A0FE7AD1"
+    "key_seed_hkdf": {
+      "key_seed": "19CC012CC9001734B8364F869F1A509B73FA8136E244B9D3B748729B34A79FC1",
+      "v0_identity_token_hmac_key": "91126F93E7F6C136D0C45B2F4ADB05E90FB2820050C1AE0661268F615D656B50",
+      "v0_ldt_key": "65E151200435492E2E08BF9BD546BB5E874D98423E143068353AA241C017C996E11ED54A21A0E0271119A64E347B1F3D2A677F102025296C9AD99FAE60526944",
+      "v0_metadata_nonce": "AF7153D5E3454D574F3AC87A",
+      "v1_metadata_nonce": "B95A9D3509FB843EDCDE7C30",
+      "v1_mic_extended_salt_aes_key": "858513A0BD8CA526CB01C37D872CC17F",
+      "v1_mic_extended_salt_identity_token_hmac_key": "93C55B03439CB5070EB21AE8A4CA9BBA65F3B5455639ABEF55BA06D9DE5D097B",
+      "v1_mic_extended_salt_mic_hmac_key": "F62B10BBE49EDB6710E99F12D0A7A5C4F76E05158905098D3B7E7221675057A4",
+      "v1_mic_short_salt_aes_key": "12A98E8CAE6329555EEB63400A56D627",
+      "v1_mic_short_salt_identity_token_hmac_key": "401BF4799CB21A9277D3F643D51C5C2E5D574E53F20D5A4D6039BF8501B4B9A4",
+      "v1_mic_short_salt_mic_hmac_key": "77937075451137DA6E774BA77C5A25B51896BD85429245EB45FDF8D84D40ECD1",
+      "v1_signature_identity_token_hmac_key": "2A938DF61D8F4FC208BB76E2C1509AD85C880079F1C194287CAECE0AB03EFD25",
+      "v1_signature_section_aes_key": "305B71CD7CC16B254D7DB937EED3E384"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "CE71",
+      "expanded_salt": "52C4B782535F782334FC24FA40F001D6"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "76EEC201AB36A236EC186625",
-      "extended_signed_metadata_key_hmac_key": "A0F331B488ED78B4190BB62063B6A66CDA7A3503E1CBC08368E39565C307C062",
-      "extended_signed_section_aes_key": "BC854924304C1F14181F985F2A7875E0",
-      "extended_unsigned_metadata_key_hmac_key": "0A298E55442FA855F3EF35E748D52E1EABBB0FA1B3E17D24957F964F413A76B5",
-      "extended_unsigned_section_aes_key": "6ACFA834A2FA81A1A4A7D6DB9988078F",
-      "extended_unsigned_section_mic_hmac_key": "B34EFE5E312B504B811E32330C120106154D5303F431DED9FB1FC845C71CE193",
-      "key_seed": "FFE98778134B058DDA1C17F3B368FF6DE1C9FCE69BF5AC63C133177E707AB259",
-      "legacy_ldt_key": "976F3552251D50BB5E6B10A291ECA51F7E5C0C62F13035B55420530F464364C74CB41036BB8294CA2A438F946EEF622071841005B6D804412959092ACDE0A22F",
-      "legacy_metadata_key_hmac_key": "1278970FF627F1F077A0AF1C370317EAA0CCC33843B47DCD94C59A908069A70A",
-      "legacy_metadata_nonce": "23C0DB064837D54FF3883DFC"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "17C531BAE241B406BB2922009FAD2294",
+      "v0_identity_token": "A612CAE0C2C60B0847C95556DC4A"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "F2D0",
-      "expanded_salt": "BF636415E72B8974D9CEA57DFBD9713C"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "F217",
+      "short_salt_nonce": "C7B4FA588C9333953524A7CA"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "869D047A3EFED540B16C867918B5CA06",
-      "legacy_metadata_key": "D6C78751DF42B853DCB42F723B6D"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "DE16277E7CEC4B1FACAA17BC54474A8E",
+      "derived_salt_nonce": "D4FF5F07C589D8138D2FDB2A24BC0C89",
+      "derived_salt_third_de": "D53C34584D2EA2FCF446979A19CD7EB4",
+      "section_extended_salt": "F946C81199DE69612EB07197BC151E2C"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "F6AF11DAE90B198388EA1D778C10CA4A",
-      "derived_salt_first_section_no_de": "3AEC2DD5E0D29A9EE0DE8B6C00DB2285",
-      "derived_salt_first_section_third_de": "29C1901D0646729360364695E47F59AA",
-      "section_salt": "B0955AB7ADF6FD11F1AAECB1A777A329"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "E1CD4A0A0F44E1B9EBDC99E4",
-      "extended_signed_metadata_key_hmac_key": "B2EFABA87A2E8BCC4ADB9CA2C94B425E824A0B9E9ADE39C000E7AFC3D4084D8C",
-      "extended_signed_section_aes_key": "A59CFDB9BAB8A6774B67B80F1D5EB576",
-      "extended_unsigned_metadata_key_hmac_key": "E975F5B3AF3C931B94F7D7ABD332BA9AF8C40F822BFBE93685714E49FA34BE81",
-      "extended_unsigned_section_aes_key": "B9E68CEA1E1836ADA1C2B6F72F19814E",
-      "extended_unsigned_section_mic_hmac_key": "9054020C39D0AC975FA1446386B282FBA102D3B13B94E06751182223E8BACD20",
-      "key_seed": "92CC48D1E46FD37FD8D92063C275E5601F7956B1D79EB4DA6406989177F795E6",
-      "legacy_ldt_key": "79873B89C6801423B759014737AD82E8774EFC2648416703942B31D831438D9C7AE96912B7CE48F10A5A81222BFD11E7BA68188FF8A68594ACCC5F3EFD183376",
-      "legacy_metadata_key_hmac_key": "DB7883B5FC9939D08A478BA41CBC8D205359391813FF7AA1B1CE8E5446835005",
-      "legacy_metadata_nonce": "CAD17CB88CBB262FA2648FAA"
+      "key_seed": "E145E4CDA18D67961C0286D0377554B146858D4D83365D406199928CDCAC0C89",
+      "v0_identity_token_hmac_key": "51094471DCCA8C3E1DE0075F09137AC7CC16A0A1C593EA16B01D21BA8BD2212A",
+      "v0_ldt_key": "4F2A178BCDA7D33A469327F1DD4A936F3690E9D6AC48FC69A30639833AB409641F30296928E25A32598936D1FC872A8356984FD3ACA78AAB96DE6EB2C8B15AD9",
+      "v0_metadata_nonce": "D8B9FC255A175733AEB21289",
+      "v1_metadata_nonce": "3B1A920FC87B0CD4DB45FE58",
+      "v1_mic_extended_salt_aes_key": "284E68D89D1634B46CFB893C4DFACE25",
+      "v1_mic_extended_salt_identity_token_hmac_key": "A53C4B56D2E8C308D2DC7D1A730F16059C8F352AB43350EDB6BC9FF0E8D5C8C6",
+      "v1_mic_extended_salt_mic_hmac_key": "4E98FDB0A10DECFF15DD50201B666FD37DECD7714DC39375807CA551EA4566B2",
+      "v1_mic_short_salt_aes_key": "FC0DABBB4752FEC56777FB109C1FCF70",
+      "v1_mic_short_salt_identity_token_hmac_key": "26B67259B23EE7FF7732B63D29FB88C6EFF6B2963742314E7EE443D062D577FE",
+      "v1_mic_short_salt_mic_hmac_key": "494B430E241D388B8E15EAC8B79FB531D8202019A03F171A70FEF735DEA4A839",
+      "v1_signature_identity_token_hmac_key": "1BB63967B26B3A8182DB6E45CE90217C48F7C7289049669114F2F60D59A4982D",
+      "v1_signature_section_aes_key": "C12D865CE3C3B92E3D86E11D31C4B7D0"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "2760",
-      "expanded_salt": "ACE0FB227A299B29532B691723192E4C"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "AA4B",
+      "expanded_salt": "055DB6E3745BDA470CE4F4C132880845"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "F594F0C71C5C1FE594655C37747429C4",
+      "v0_identity_token": "B4C958171BC3890DBE38F108DAAA"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "97AE",
+      "short_salt_nonce": "C9FDD6FB248C1AB3CFF1BEF9"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "CECFCF062962713BF67A2C253C34C430",
-      "legacy_metadata_key": "514B537E9F65528BB70C726EAB75"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "90549FA7AC9880D8007E17CDEE74FE89",
+      "derived_salt_nonce": "55FB1B0D6681696C15F559A6A0B817BD",
+      "derived_salt_third_de": "DA0D6A896BC2718BAD6B2582B3EE79C7",
+      "section_extended_salt": "1FCA0AE82AE6340C91647C5564742C84"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "711E72342E2DF4D9DB3E21CB79E15B1A",
-      "derived_salt_first_section_no_de": "4AF7B282B09493FE2AB824CE11CF5A3D",
-      "derived_salt_first_section_third_de": "BDECA0D713545F2C06E92ECFD90F32FA",
-      "section_salt": "5A7297E0F00FB0AE50DB911EA8320E32"
+    "key_seed_hkdf": {
+      "key_seed": "E4B6ED39E885C5F41D6F4D90C81C4087346E365A3E18278A7CDCA536C7229ADE",
+      "v0_identity_token_hmac_key": "482AFD56528CDADE549636FDABCE9587455323DF5F6BB2F9FD0C66F14F2FA4B4",
+      "v0_ldt_key": "0789774D2056B6A5EA53D55137B61BAE5C313FEB8BF68E3DE555EA01964B54A6F0E1E481429DE7D2BE8FEFD3C16C438EA167C6910C457F1C189251F54A01436E",
+      "v0_metadata_nonce": "F497EF21C812E0A4ACC14FC3",
+      "v1_metadata_nonce": "F1738F20E3B5B10D3E328511",
+      "v1_mic_extended_salt_aes_key": "1E4783B23CC12AF94BCC9642958C53D5",
+      "v1_mic_extended_salt_identity_token_hmac_key": "1AB2BDD7562FEF156CE157BDE824FAC8C927861B7DD1CBAA2D26DAD763A1EB90",
+      "v1_mic_extended_salt_mic_hmac_key": "46ECCA964CC3D606C16840222CB68074FE307938809FC97EA68C5E9EAC01E754",
+      "v1_mic_short_salt_aes_key": "A4EB5E86AF11AB3A4F8D70BA2D37784A",
+      "v1_mic_short_salt_identity_token_hmac_key": "32E95A4CFC685F4F3987F8D89C13B33C43E5F8BA1A1C76518975667B7EDD5A98",
+      "v1_mic_short_salt_mic_hmac_key": "80179BE73B86A9D0AFF72833F594BCF83905914646F4DB288EC11F875F1591BB",
+      "v1_signature_identity_token_hmac_key": "94A64B97AA4C3A474CFD819BA6FC161F2E2865B1E2B08B041CA219490F786F6A",
+      "v1_signature_section_aes_key": "8025F8AA3FC09DC5EF67F59C63D81FDB"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "28B7",
+      "expanded_salt": "6A3253C12DC5A5A9C9F2BB5FF84575A3"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "53C12B415D361B1E7CFD2BE9",
-      "extended_signed_metadata_key_hmac_key": "1A903300F25E61572D66E6F92E4F20526EFB141748AC018547F164847EC33029",
-      "extended_signed_section_aes_key": "8EDD20D6C52E46FDB3005E2F11D40E78",
-      "extended_unsigned_metadata_key_hmac_key": "51AC4D0DCC343FB2A8442E497D086767A0F904FD8FF0C2F5C1C3C7F462E2690D",
-      "extended_unsigned_section_aes_key": "DDD633E7ABBC286E409104446AACE0F7",
-      "extended_unsigned_section_mic_hmac_key": "57061A968056E7ED330F1C8F65242B18335B0203B3AE5F863F861FBA07155CFE",
-      "key_seed": "9E095A9E979E0556C03FE1276821A0905A530027A4E48B445FF150DA40A47BC5",
-      "legacy_ldt_key": "74EDD1187CF248366FADC5DD69BB431EDB9FB39621221F62A67FED5CB3D0129EE8B5526BEEC86BC1CA0193239184A23D70D901B12F89079E1560A25AC839F6FE",
-      "legacy_metadata_key_hmac_key": "5B34A6319FD27843706A8E1A7A1E779200E3D9BE8DA1246CC5E32D88DE816486",
-      "legacy_metadata_nonce": "F23109B82D2099F6F94B4C88"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "942E2707FA20651603FB9BCD1483D57E",
+      "v0_identity_token": "79F541A8D757F44FB52031C6EF7E"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "FE58",
-      "expanded_salt": "9C7EA9924564F03D6340019D4BCBBA47"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "1299",
+      "short_salt_nonce": "911ACEFB50B97795D2A96537"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "439B31109FE781639C510638CD037A12",
-      "legacy_metadata_key": "1F22EFEAA64F55866D64968A732A"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "5BAA0ED9252F176221DE2D9E287C731A",
+      "derived_salt_nonce": "220F098C6ADCF4E24B6762E4CA4DC7A6",
+      "derived_salt_third_de": "FDB6D13DB24556E6A1E926CA5080B20E",
+      "section_extended_salt": "C10AF6288A750A0C71B7FBB3057BBAA2"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "F1CA484DCFEEEAF8652F00199C50A97F",
-      "derived_salt_first_section_no_de": "9F52A5DAA201CD3307BC80F9A3FF5A64",
-      "derived_salt_first_section_third_de": "BCC55DC7AD531D04D24F88CD03FC9947",
-      "section_salt": "96E68F69CE0AF1CA5947D950AC03167C"
+    "key_seed_hkdf": {
+      "key_seed": "F93748CF451F971291D31EB2D25948BF10687D3F3955D5A6EB6BAB4510E003E8",
+      "v0_identity_token_hmac_key": "BA87E890F118FF9516B6054C89212CBA85D3413E236865E71CC939AF0A48D26B",
+      "v0_ldt_key": "7D6E7092EF91CE57885E3F7250DC889064C9B657A8BEECC800EE8DA3A5E508D22CDBF4C8164CAD7EF9067CE25B1A51693998A3FCC4974B80AA26D0A08FC1ECEA",
+      "v0_metadata_nonce": "CBE2FDBF7E222F39257C4697",
+      "v1_metadata_nonce": "37181E07F73F64E6D39549D9",
+      "v1_mic_extended_salt_aes_key": "442BB1C9309855CC0C81B319AC4942BE",
+      "v1_mic_extended_salt_identity_token_hmac_key": "905919A49E80A3F5DC2471AC9CEB9588AE9266DBDF346F9FEB05980466A24A48",
+      "v1_mic_extended_salt_mic_hmac_key": "1BE4DF51E2A244F17793D9C3D1EE2DDC9FC992BFBE378C828DBD1D3121F4D9EC",
+      "v1_mic_short_salt_aes_key": "FDB274DA8F75B1DA00CEAAD2BDD6BD06",
+      "v1_mic_short_salt_identity_token_hmac_key": "750DFF2B2046595041B400945833A3434B39D98C23975040C6192F66959E40B7",
+      "v1_mic_short_salt_mic_hmac_key": "19F78A8FE548117BA40809FD8F602F2FB9A57F6EFE01756A93A81A6BBEA0BEF6",
+      "v1_signature_identity_token_hmac_key": "48FAF93A1F41C85646B0204CCEA6C145E7CEB69BA38F3FCF3979E4F9A34747BC",
+      "v1_signature_section_aes_key": "34324281067F3278984109B434594C84"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "9021",
+      "expanded_salt": "024D17ED3BC1DD62FFC281DBA42F6CE9"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "790DA0B3358A34B85EAC7667",
-      "extended_signed_metadata_key_hmac_key": "8CA9AA70936AFBCBABF150D6E5B27AB91974D4B039CCE5C1F3AD7CD09B124F08",
-      "extended_signed_section_aes_key": "9B148D55787368596D67A8536F89214E",
-      "extended_unsigned_metadata_key_hmac_key": "2497B036129D2F25F43E6838495CA54DA3E39DFBD80906C945F1C536CA8B54F3",
-      "extended_unsigned_section_aes_key": "680EB9CE125DA2B614C1768023E69C8D",
-      "extended_unsigned_section_mic_hmac_key": "4FDA73846885210F5E42B57D32C559C5C176B4FC17A5DE755E9832F7473A6615",
-      "key_seed": "1C21EF77FDCFE2F77ADC1687CCB48A85C71A6D53E8BBF9C445F9A81E44C96010",
-      "legacy_ldt_key": "65D36DD12CFA1D5985EBD4547CEE55D85A439A6879A390A114E8265B52DDC19071E346DE0F8AF67D8D91B506399043DE945F3B61F0698D58E80543BCD6FB57AF",
-      "legacy_metadata_key_hmac_key": "7D96F6A20DDE772C03F269C6B5D1851A569C300D5B0277B7AAEF6EB9C71C884A",
-      "legacy_metadata_nonce": "3E4300FD8F982F491BD335EC"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "2CA6D8809D9CC54B426A5D631F5DD5D2",
+      "v0_identity_token": "C17929AECBB954C19B7D9FFAB41E"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "9CC4",
-      "expanded_salt": "61B568A967985A5611EF9256E29E9894"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "F2BD",
+      "short_salt_nonce": "760FE743C18D50BC5915DEAA"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "D477CF6201CC1CADBA21370D16760F8F",
-      "legacy_metadata_key": "50841355894155E171A91BD507A7"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "66980E111A8AA401E3B96E7E0EA44FF5",
+      "derived_salt_nonce": "51074A20BC98469D5E302D454A661CF1",
+      "derived_salt_third_de": "0A7723EEBC848E10D5573084A5AA9326",
+      "section_extended_salt": "3CDABDF68AAB76BA4AD395BEB668D405"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "DCA35224242C81D062E2D7806C58D0B2",
-      "derived_salt_first_section_no_de": "54EF44DB16594004E554658EFA470231",
-      "derived_salt_first_section_third_de": "001814C42510FCD65262A3B4CC145933",
-      "section_salt": "B601766283CBAE3A4CE5C7DFF2A0DA74"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "A15525217C62F032E6D54151",
-      "extended_signed_metadata_key_hmac_key": "298E19377343EA2CA328F5BFA39DC8517F1C426BDA2171E0A09208C39BA50145",
-      "extended_signed_section_aes_key": "2167A0F9007EB1A8EFED8341491C5B68",
-      "extended_unsigned_metadata_key_hmac_key": "2219E955FC16E04FEF31B79E0C620133CC4BFE2CD2CB6FDFB756DD88FDBC0799",
-      "extended_unsigned_section_aes_key": "1F7AE997F2B6D9AE904158DDF91C7A62",
-      "extended_unsigned_section_mic_hmac_key": "4D3627566109F030079FC771DED6D567F556A1FB703A89C51FB76C2B59FC5443",
-      "key_seed": "121462ED1B16BC8682D880878916CDB81CD7E7556FEEBA1FE22210D2949F0931",
-      "legacy_ldt_key": "0EF6436C70F2797E08DCAE6D368C1C3EE7B8578572EA873C8B7C185D8CD1616FB0F33F4632618E4A60B9B6F9FBFD7D34655A50C444995C7059C93DD28AF58176",
-      "legacy_metadata_key_hmac_key": "184A3999BCF8CC12D37D4013D7822C64A11CE426267A0271C1ED53E7E8DB1000",
-      "legacy_metadata_nonce": "78C1769887FDE04453C81658"
+      "key_seed": "6BE4BD51107BCB90669934994487DFFD384C30A08A130DCF058DFA9CAE77D7B2",
+      "v0_identity_token_hmac_key": "36DB5DF09F5706F43F19A6A6A6DB9EFACE7C0945BE5CCD6FD9B41E514D3ADF6E",
+      "v0_ldt_key": "6B1D1F8670DF12FAE850EFB556A52E80AF256644C657442BFF5B6A8535C0532CA642511E2EF35C9BBF583F051594656F4D9EA1C8639710F51033600488B3C5D7",
+      "v0_metadata_nonce": "E64647D6D6428CA9356F587B",
+      "v1_metadata_nonce": "9446C5C7C84F3592D1BBDB63",
+      "v1_mic_extended_salt_aes_key": "A466870E3E012FD1D47AD9AD1A70BB00",
+      "v1_mic_extended_salt_identity_token_hmac_key": "303A9851761A21D1B8F8428FA5EEFE4311C10CE0BB62E0C846D336C829B626B1",
+      "v1_mic_extended_salt_mic_hmac_key": "4EFE91D05602871EF06FEE7A9B2DB2ABF8FDA8093467E2B7E5437176D24885AF",
+      "v1_mic_short_salt_aes_key": "6465CF64ACF1F01D2D5D9D1441B2E4B9",
+      "v1_mic_short_salt_identity_token_hmac_key": "F5129A6C3923C228366A93A12BA8287B1C2CDA1A6CFDEC305C7FB224D92EDE7B",
+      "v1_mic_short_salt_mic_hmac_key": "30876F2D82145F3754EBD7CAF699A3BA64458F0846C9E6047485B5C209F65C1F",
+      "v1_signature_identity_token_hmac_key": "AD8B825C5A010150B5F2AF5A3FF073A3EE199654BBBB073E4D902FE4D0AC5A48",
+      "v1_signature_section_aes_key": "611B58E120CDE3E26F834932C36566DD"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "296A",
-      "expanded_salt": "F44EA8B30FD9F999BC7E31C8130892C3"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "3CA1",
+      "expanded_salt": "8DDD8654D5EF2D402D1BD89677B3B2F7"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "33A6954AA51AC176229053887D832EB7",
+      "v0_identity_token": "45694EE6AB26F4B895349E87F727"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "D982",
+      "short_salt_nonce": "72163DAF1F557C67B89BA7B0"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "4299BCBB2412A71215EDFBFE1F8176E7",
-      "legacy_metadata_key": "07C4A089310EACCA1B3BA2D4EA94"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "84D7B207BC3AC861E317C4427A2F3301",
+      "derived_salt_nonce": "498C9B6F78B4E5F68FCBDFA5FAD87C0B",
+      "derived_salt_third_de": "363C6DADFC2F7B20DB94885929867F1F",
+      "section_extended_salt": "9435272D1BBC6EA6FB74B94D6AEB5AF9"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "F3CAEB25DC97A317A38C99E7F1438E1F",
-      "derived_salt_first_section_no_de": "EC4686E3C2F9A5AF8934C8EADAFAEB6D",
-      "derived_salt_first_section_third_de": "921DBFBA6B75D60A97443F20A6AE5779",
-      "section_salt": "F3A5EA6D8CC24F111F84B17EA038A2FC"
+    "key_seed_hkdf": {
+      "key_seed": "9A5DF1DDC5B966E46275F11196A5FD20AC8C785438892A20AF9CC19D1F024234",
+      "v0_identity_token_hmac_key": "0CC4D92F3CB8D258086F2FFAE0D3C00CA5D091666B506967365A20BAFDC8075D",
+      "v0_ldt_key": "B002ED5E3E3A89138132968C513DCCCCE3505AC181845BF28BED504BCAEAD1B9992A869C9FFE5869AD40AD9088B6B3B3C0C84152A15A65861B9AA5E1C15B0A48",
+      "v0_metadata_nonce": "A71E83B5E9FAE0461B560352",
+      "v1_metadata_nonce": "D68843EB7043254F1F786CB9",
+      "v1_mic_extended_salt_aes_key": "4A32421BC387873521C5DAC5DD9BB2EC",
+      "v1_mic_extended_salt_identity_token_hmac_key": "1043596317963E0FA7B386319B95A23EC6E2B8BDFC510F98C2D55472FD479CA5",
+      "v1_mic_extended_salt_mic_hmac_key": "D619E81CC454D9988BAEE3126A279B2E06E91555B79E736D3A148BC5562B7640",
+      "v1_mic_short_salt_aes_key": "7F710EBA3B7926655AB09CBE0B907506",
+      "v1_mic_short_salt_identity_token_hmac_key": "CD64A6D97487DFA1AB1893D5E00117807F598A2AE118654996BC1E3DE8F8B61A",
+      "v1_mic_short_salt_mic_hmac_key": "3C5F8ED49E7B095E21F3DFC00E92C34A6291E6E445FB181EB5612FB45389BC9B",
+      "v1_signature_identity_token_hmac_key": "FD265D72D87FBB7503D2B532DF0F03F916FB41D82758D1A6BD1E71AF1C3C99E8",
+      "v1_signature_section_aes_key": "C715D287A1C5131332DE38C844EC32D9"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "38BA",
+      "expanded_salt": "BE558E77E1C1CB6D3B412593604D212E"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "944F676AE58D1738571A6988",
-      "extended_signed_metadata_key_hmac_key": "D5642BD297591472FEDD16557F86D9FC98701FC19BB83E24A87EE5D31314CCF7",
-      "extended_signed_section_aes_key": "B2FE576CBF190EFF36B7DDF428D08806",
-      "extended_unsigned_metadata_key_hmac_key": "80E4277B7C98DE76783580F99D7AFFBD0C939069E129FCD2A451DE2A4DFB347C",
-      "extended_unsigned_section_aes_key": "84AF72363AC42837703E0233DA58AF15",
-      "extended_unsigned_section_mic_hmac_key": "309CEADE18269614513CC150680E07E79A395BFD00966021D074AA51D5DBB0B3",
-      "key_seed": "174C6D54B1B0CC84774E8FB9339AC57933CFEC2D78A57F66268A2ED648C79D54",
-      "legacy_ldt_key": "FEFDC3138EC8C0F7CC34C171A43E9AEEBB4A0FE5EAADC607A9D6600D8B9B54D207A2BC90D0B38DD989EB57AEE3A21F11DFA1C22F8DEEFCBB84F14DE16706CB5E",
-      "legacy_metadata_key_hmac_key": "181967500164E896A23C22E206E00208E0A82CBF97B839DA87F9C3EC5759F1EB",
-      "legacy_metadata_nonce": "0688E75E1B921F995491EB07"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "3249439E945688E53E2D09CECF669801",
+      "v0_identity_token": "262E2E4C531A2756323900C354C5"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "BEB5",
-      "expanded_salt": "C401AFC676735287424E9F7F0DF237B2"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "1C05",
+      "short_salt_nonce": "F93616C2AEC28E78FF0B5D0C"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "8AB09C04AC42D4F4E123FC884885C595",
-      "legacy_metadata_key": "191530A2A7F75BBDFD7777601AE1"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "8F505CBB29A4CC1CD037D1864D15141C",
+      "derived_salt_nonce": "03A12AB647E4834E3E507F1F6EF20F1A",
+      "derived_salt_third_de": "654F8A7CD26F23E82163007DD25029B9",
+      "section_extended_salt": "AD43FF79BFF583BED9ECAA14CDDA4B4A"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "99D022786E9CC9AB870AC2FF57DA80EB",
-      "derived_salt_first_section_no_de": "01E17F91C55F51BD79A72463BC8493F8",
-      "derived_salt_first_section_third_de": "AE2DCB3E34AB81BF22F460D7546376F0",
-      "section_salt": "3E1F652AE28F8335D7B15864E9F7CCF5"
+    "key_seed_hkdf": {
+      "key_seed": "FD4AA335CD6FE01E9BDD45FE11B306293BBECB92F1D2CAD39CBD1AF43ABCDA60",
+      "v0_identity_token_hmac_key": "406B8E5E105F55217B0687BA83B5DDA39C541DA4D53015D7E9CB83DD8A6A307F",
+      "v0_ldt_key": "F5F2E863B32E3B216EB59D37C1CAF3761D6BBE6A089ADE286390690C0C1DA5AD5CB3B647D9CD210ED63267C98C2893D17988B8A1D03845AE170E737CC20D0185",
+      "v0_metadata_nonce": "64FB1137553FADBA3814DD0D",
+      "v1_metadata_nonce": "1DA66804A1577592B5D88534",
+      "v1_mic_extended_salt_aes_key": "204CE842E93EDBEA97BBF662EF60C006",
+      "v1_mic_extended_salt_identity_token_hmac_key": "FE3DE77C3C83956B52F85C4A6284B97BC69E2D238A185F2AC7AD7E3A042D7883",
+      "v1_mic_extended_salt_mic_hmac_key": "208EA31DA316646310DAFE2A4036E5A1556E80020D8EE582DCD9CB6DC475B625",
+      "v1_mic_short_salt_aes_key": "0B308555FC29F102CF5AF79AA5C5D85C",
+      "v1_mic_short_salt_identity_token_hmac_key": "5F89875055E105BD96055508E440A6A6B44AC0F867D642FF95EBF249D2A28BD8",
+      "v1_mic_short_salt_mic_hmac_key": "54F0E228A0E4761F1694BC2717794E2C79BC9CCA99A0B368A370A83750468223",
+      "v1_signature_identity_token_hmac_key": "5838D84FFD60A23225B059EFE52D7FFE71CDFDB599826D8BBCF2E254FA5B72A7",
+      "v1_signature_section_aes_key": "0BA6F0BE32E0E8AF932B27A38BFF678C"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "1D76",
+      "expanded_salt": "03BD30CAD853D018A141F8DEF1F37112"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "F3ABE0F91D65A95957CF2AD7",
-      "extended_signed_metadata_key_hmac_key": "0F4E56038ABF9ACAA73EC95395B337124A89ACC20399E1DBAB7F8F73182BC107",
-      "extended_signed_section_aes_key": "13B7FEAF3CD5ABF9905CDA55AFDDEFF4",
-      "extended_unsigned_metadata_key_hmac_key": "B42A14360C1128B0B4AA1D99E89B32F4D13D2C8A191EC5E58DB841EDDC4ABF76",
-      "extended_unsigned_section_aes_key": "3AE1FBA492CE6D8854923EEAC2AB8A17",
-      "extended_unsigned_section_mic_hmac_key": "9FF77BD8BF275AB41652950CE40976102A641CE5CBCF346A9E755281FC0259B3",
-      "key_seed": "9864ECFD656E7B608A63883FBEC4F62E8E6F7A3590114C39E036AEC87BECBE41",
-      "legacy_ldt_key": "8FC7B079C4225368931B03C4814EAB6DC02175EF1873B1A224CFD39B9CD99814417A884A2FDB9D324747F55A9BF7758B22C2C3113D06ADC373B0A47E925FADF3",
-      "legacy_metadata_key_hmac_key": "493B6E99413B5E07771053F536EEB8DBF169B4DE6E78EE341E1627C5055200B9",
-      "legacy_metadata_nonce": "B4245B6A65DA3B698B64B9B1"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "9355E90CA1E607F24358C2898E658346",
+      "v0_identity_token": "636CFD6764F2586AAD40ADD23774"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "8C5F",
-      "expanded_salt": "1E634D3633DB16B5698B53B1FAC7318A"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "DAC9",
+      "short_salt_nonce": "ACF0BFA6CF9267C79BF71814"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "ABACBBEA70871DA67332DAD622F9877E",
-      "legacy_metadata_key": "711C81AF55689C9F41F26EC0B3D9"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "588B14F40986244A6E6E11B59DBE6004",
+      "derived_salt_nonce": "8F4780C654ACC3FDC454F59459EE4806",
+      "derived_salt_third_de": "433266A654D8821576954A6122E18ED5",
+      "section_extended_salt": "5D5394D0EF11A3C4C83A1B3F4DD1340D"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "D42F8611248F16803F100880FD2C40A8",
-      "derived_salt_first_section_no_de": "0AFD085C634D64812B22120E6EA087A2",
-      "derived_salt_first_section_third_de": "0530E299A7CECDA05EF000FCBD461AE6",
-      "section_salt": "1025402C2B74145C70E2CA2E518A83C4"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "37CAFFB8E8BC64931F3190F3",
-      "extended_signed_metadata_key_hmac_key": "15D89B2AEEBF854A0BE65C186885E4AAAB903E312CF27B67F8881FACB082B7D6",
-      "extended_signed_section_aes_key": "640271AF293235DD4592A0FD12EDCDBB",
-      "extended_unsigned_metadata_key_hmac_key": "3E5D5FBA4B63F41DD8B5F42C1A285DA8C0AACD5AE96891BF9BE94BA7F2A3422E",
-      "extended_unsigned_section_aes_key": "573C4B8052EF1CB1BFCE2EE02A21FDDB",
-      "extended_unsigned_section_mic_hmac_key": "269360FAD76012F1BFB9B46E48CCB4A4484C959B46AAF666E7E99BB571D02DBF",
-      "key_seed": "AED5B53B0E0A4A8E7A00B1DFCC03428D99E7B07CC6B90A2A226DAC3C51C53300",
-      "legacy_ldt_key": "80B93319EEC4CB0E43E07CE1DCC54AFA5143E3E0688297837BE19921550E3903AC441802CF7816C959C8BB5B5F650C294379767E8A13821F0BD4CD0164748355",
-      "legacy_metadata_key_hmac_key": "AD90B2CF579CA71E41CE8EED3E8D2EDBE74BC6B3E090C298B3027E2071E1D707",
-      "legacy_metadata_nonce": "28C25CA1937E414689922A89"
+      "key_seed": "8385E64B2E3EF21FAA2E01F9A7E3681B17C7DBE0498B3A9DB7CB25E9D62EB6ED",
+      "v0_identity_token_hmac_key": "AB4C142CE9F73CAB064D3533B1980B1B4B88B98AED58171986D28966A127F45E",
+      "v0_ldt_key": "8978107F05DBC75203A1D8B09C2B3C3508207EAB51AB43312FA2D6AA912DA366F050FB8EE7A1AEC265D08F47EE1C299B4B8E526A62EE1CD529294A0F9166B135",
+      "v0_metadata_nonce": "48551A9BF3E51223B4FA83A2",
+      "v1_metadata_nonce": "DEEED21180C62518A729ECFA",
+      "v1_mic_extended_salt_aes_key": "CDD92FF2299C64640DD4DDE767B8447C",
+      "v1_mic_extended_salt_identity_token_hmac_key": "475944CC40A29812841D147B2F1B8E6FA3B1960083DB50C373F1D032C05C32D7",
+      "v1_mic_extended_salt_mic_hmac_key": "2ED15F9F067294394BA5F7EB2F554553CB932E33A8C10E6AC3434FF1A7F8DD2F",
+      "v1_mic_short_salt_aes_key": "CA73989C086C73B114AC605A79A0F8F2",
+      "v1_mic_short_salt_identity_token_hmac_key": "63269C17F98AA0029E06EA1B749D8DB6C28ED916906DD28F1A3A8C94E31DACF9",
+      "v1_mic_short_salt_mic_hmac_key": "AD1C3A30332FA34408CA62A9C6B9A5263932EEE57829C1195DC74127B1C88671",
+      "v1_signature_identity_token_hmac_key": "99B0BDB55C04EAEBA66E40815CA346C596D848044A16F8A8CD1E8F22E4B11F97",
+      "v1_signature_section_aes_key": "5F2B10AD3A8C1803B080A1F99239B66C"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "5CD7",
-      "expanded_salt": "3479EE965DA3C29D12C79B83F87DE36A"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "D451",
+      "expanded_salt": "B73C8091A1D606893FFCE4BA8DD96D80"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "EBB365F56DD11C290CF18ABAAA208F68",
-      "legacy_metadata_key": "798BB2405B1E4C58708885C8C64C"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "3A1461B8D59E8F9AAC4DE97877E97A56",
+      "v0_identity_token": "2377F0BE5EACC04FA6679BFD6DC0"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "7A24",
+      "short_salt_nonce": "D13C30E6D309831E754A8B5D"
+    },
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "22A24B673F12E4066B1058549ACB431A",
+      "derived_salt_nonce": "EEF802FFAEBD8AF451F5275934E2F8ED",
+      "derived_salt_third_de": "136F8970074C895F2195791B5636A000",
+      "section_extended_salt": "18B6A2DB75BA6F6C2CF40473B7673D48"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "E06D761A8CA571BF0B0F44BECE8D84E5",
-      "derived_salt_first_section_no_de": "F4FA7DA6B4BEB32FB8D61AD5529221DB",
-      "derived_salt_first_section_third_de": "C8B3BAE8D808A3E4CACFDF99B9006801",
-      "section_salt": "44829BD9245724BB422CDF85FE06575A"
+    "key_seed_hkdf": {
+      "key_seed": "B5F5FD3809B1E2287363B843688B8F263B36F22A818CE6D653AD2E202D4C2029",
+      "v0_identity_token_hmac_key": "8FE854C9FCB565B6E74C28EE13B9740B3F0A76F1BEB869A6EBBB7DE4999F69D2",
+      "v0_ldt_key": "F3B0B3A99C8AE9E6C111C2378C15396404E32B7278E5B3B5514251EAB2FECE631528F1EFE06CFFCD192C2D3483D00760C4D5F3809D32B29ECF4E1A9D9422F6AD",
+      "v0_metadata_nonce": "820F6CC121CECB5D770CBE5D",
+      "v1_metadata_nonce": "371063E1C363EE10DD07C754",
+      "v1_mic_extended_salt_aes_key": "78C14BE8C5AC8D740024768A2B59F13D",
+      "v1_mic_extended_salt_identity_token_hmac_key": "B0CD0B70103C233E66D37E621EE40A04873D917A70D2E647FBA106399AFF720A",
+      "v1_mic_extended_salt_mic_hmac_key": "1FDF6A45C585A0F1B30CB767AD84410A2B95628635124F63C05F092918768D6E",
+      "v1_mic_short_salt_aes_key": "2C30551B0B248E7AA0C567A9F1F588A2",
+      "v1_mic_short_salt_identity_token_hmac_key": "CE516307270755B243FF272BB9A38EDA69BA0E04771E1675B5CFF1218637CC36",
+      "v1_mic_short_salt_mic_hmac_key": "BA808DDB430868DEF3BAA9FA53D9FF19BDA2559876360F87DEC0B4D04E6B482B",
+      "v1_signature_identity_token_hmac_key": "B90E66D1CC33DB3DEED3F867C42569514764A2E4E26D6E65A5009D3C39EF0BB0",
+      "v1_signature_section_aes_key": "5E4075E092E92885446D7D362B52D788"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "3E8E5771EC037DFE9655CF21",
-      "extended_signed_metadata_key_hmac_key": "FB1E172D8BDC30C0686494050F0545AA14AC4CEB4B80178D1A60BA91F52E4220",
-      "extended_signed_section_aes_key": "CCCCAB2218464982259AC1F9E1C883C9",
-      "extended_unsigned_metadata_key_hmac_key": "A165FBA7AE751FB806EB71B517C107108ADE8AD83CAF2270CCBB500A16D6E8B8",
-      "extended_unsigned_section_aes_key": "939BC65E829C365A53FA76DEC5CCAF72",
-      "extended_unsigned_section_mic_hmac_key": "F517D42A409DECE9A73326D2D83073F5354D5FA6E18E361C8F5C322DF0DE2074",
-      "key_seed": "AFBF0717AAE34C12160C6699E875BC497324201A00F28BB689E72FF81EA02083",
-      "legacy_ldt_key": "64701C13AAFD18BD719961E5168794D660F2B303265D8164B3F42DA11DC1689AF643525BD5674FD39E4EDA98217D04C0F72188D4290C1820C3E7DD999E5C26A2",
-      "legacy_metadata_key_hmac_key": "3228A4757D856906247B16A2E70ACBA3E2B3A556B48C3F34188605EF76C36EEC",
-      "legacy_metadata_nonce": "5E989D42F47971FBB6D02CA2"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "E077",
+      "expanded_salt": "A6B67A4794E7CE03C296D02ABC551D86"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "108E348B0D134F66511A75D575F3A1D7",
+      "v0_identity_token": "E79958E8FF15C1F5B7857F2BFD5D"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "B7BF",
-      "expanded_salt": "B08300567B79FF302A5C7E88AA9FD54B"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "735B",
+      "short_salt_nonce": "808310562C7E7A9F797FE241"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "D98863233D54488599DDB89774AEE330",
-      "legacy_metadata_key": "0077F1879D872A287071D462AF20"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "D063EED8AE6DA992FACCD9BEAEFF9D60",
+      "derived_salt_nonce": "49FB1F75B6A395722D25D76BF3B5CA35",
+      "derived_salt_third_de": "86575CCD43CFD910F9103697669DFE08",
+      "section_extended_salt": "AB63F5E51D36B4C5CC9C302876BB2C37"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "CAB00A6159E6DE851D7E64435197DBFA",
-      "derived_salt_first_section_no_de": "9D33AA17D4672886D95B8040F714E99A",
-      "derived_salt_first_section_third_de": "F000BFD919B6C082652650B4A5C5E1A1",
-      "section_salt": "11D3A895308F443C74723FE45C9AD464"
+    "key_seed_hkdf": {
+      "key_seed": "11BC6308D6F1B051F733F9ADD041E90BEE2B19725633988B416F1EEED3C1EA8E",
+      "v0_identity_token_hmac_key": "7DFBD6E8C3D38FFA86D57F9984A91D79F0092F15DDB133C99A35C23C1EB7FF6E",
+      "v0_ldt_key": "15B76645533D06841647A3079A81C60F862CDBF49BD259DB3AAEFC7909B8CC14E44E63E04410434E541C99A00017A1D91A1D48F3960CD19460B17FF720252727",
+      "v0_metadata_nonce": "134D3F0E1599C745191BB287",
+      "v1_metadata_nonce": "7E2895642ECB18F17843FFF5",
+      "v1_mic_extended_salt_aes_key": "E75A52D878873874712BF3D7BB5419E7",
+      "v1_mic_extended_salt_identity_token_hmac_key": "A61F230BA907B4C754FBB42BCEADAA92221444FC88FCCB9B1ADF04AF88035D27",
+      "v1_mic_extended_salt_mic_hmac_key": "57EEBAEC158DD1DC90F3B1D8884CACB5FA32322E4E7C4EE21AA0EB175232498E",
+      "v1_mic_short_salt_aes_key": "CB4C2ECD3F7058A3FF10FBA90939B090",
+      "v1_mic_short_salt_identity_token_hmac_key": "A09E42551B882B1E6FDC075FB2A379332C66F91C3FD2DE496E16AC3C57FB2F88",
+      "v1_mic_short_salt_mic_hmac_key": "4746E3679A6D724E53E049DB091AF90619DAB91369DBD18204E346B2BD69FD6E",
+      "v1_signature_identity_token_hmac_key": "6944BE9FD3E6F9FE05843758B8185862A8112941E10615767A7E41A76E6B1136",
+      "v1_signature_section_aes_key": "4A4A874A9EA2E43F5A9F76E7B2C20283"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "9594966607D85F389214AE63",
-      "extended_signed_metadata_key_hmac_key": "FA1A0A53C819972F4E4F9DC3557D7E48EF74FD16FD1B4E6E343EC2DB2CAC2812",
-      "extended_signed_section_aes_key": "03570780D11AF5A85038B03FF4DD0700",
-      "extended_unsigned_metadata_key_hmac_key": "7379F3362DF6C3204A4B334B8AE60369493FF4E5296C5A7AF2485881940F6170",
-      "extended_unsigned_section_aes_key": "2DF64D6C5F0843468AA52DFAA6A587CD",
-      "extended_unsigned_section_mic_hmac_key": "6259F5119F8B9D77E2DEB23DC41A54DDE67742AD520F225E83E67E98C44DE6A0",
-      "key_seed": "5EA0EF5C2E1E60B1741DAD421FC11EE78391D9598D143B5DAC4FB07091847EA3",
-      "legacy_ldt_key": "A2D8CD41763E0C7F5A24C05FEA4BBF9C05B50338FAD3B877F69A81768FA01BB0CC9704C1A8DCA929025CCC8F6E80507FD74D140BC8D56EFE9E201F2C34077DDB",
-      "legacy_metadata_key_hmac_key": "B5CCC3B2437D3D659C4CDFDF7B0EF920A5ECABFBF75DC6C36CE47E946F35CB9E",
-      "legacy_metadata_nonce": "745E3B9CDE832DE4EE67847F"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "4B85",
+      "expanded_salt": "7E6520C25AD0706CF1B053B159BAB155"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "356B301E5F432F5AC986BD0E0803812D",
+      "v0_identity_token": "05038424718B35F01CEDDA0AE654"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "81EB",
-      "expanded_salt": "400E2A436293377A368189866BF5A615"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "E1E7",
+      "short_salt_nonce": "224F4CDE51A4C3379B542B1E"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "C0B5A3D9B7E8EB4D8A68F6AA158D0C90",
-      "legacy_metadata_key": "8E9165673AEFCC49B33E4535E0AF"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "4CED096908CA26C394EC58083D07668B",
+      "derived_salt_nonce": "0676D12F882BBE0106CA0BB3138F0BB9",
+      "derived_salt_third_de": "5A1A19BD6F8C55822C6B51538206B166",
+      "section_extended_salt": "59D1E9BDA1B9BBC808157A8EFA1D6BC2"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "0FCF0C13F89FFAFF9CF2EBB9C74238B2",
-      "derived_salt_first_section_no_de": "D1375663E1B57A43DD17E6FFAFC17279",
-      "derived_salt_first_section_third_de": "2EFF6AC282F726CA937B8B9AEED2B9FA",
-      "section_salt": "76B2FF4BF7A1754AA61E2FDD6C88FC30"
+    "key_seed_hkdf": {
+      "key_seed": "32E24295D7ED86DD0F048F5AFB62251E5BBA5FF34E2C191F8E89277E93DB4200",
+      "v0_identity_token_hmac_key": "4056DFA3904F3DB84F85315807A1ED8AC4B03A0FB300BB64C244FD377F8B8350",
+      "v0_ldt_key": "D4F8368C22558EE72891F005C5DD4B62C2AE562147C7E7A6B1041E2B8387160CE1DDACFFC1EE0725DBACACF79A6C1AB7F16DD15DCAC3EE96DA0EF70C03CFB261",
+      "v0_metadata_nonce": "5BD37F029FDDCF34D94D9723",
+      "v1_metadata_nonce": "9AEA4508424F0408F12D3F83",
+      "v1_mic_extended_salt_aes_key": "4FA46CB81F16D1973C228D94A26B316F",
+      "v1_mic_extended_salt_identity_token_hmac_key": "A0D1CED918D2A3D702E2D1E81211247FABC8E74E28614FF12B86DA38FE49804E",
+      "v1_mic_extended_salt_mic_hmac_key": "6C6BB48C73B078F00191FCD9E5A6F9E51540963C3B11B242DBC6B8C4EF5DC396",
+      "v1_mic_short_salt_aes_key": "A75EB826BBE44DA0B11FB0E93B06F661",
+      "v1_mic_short_salt_identity_token_hmac_key": "3848F69057BE12FF81A53754431C32DFAE36155AB7069ADB0C59A441F3459C68",
+      "v1_mic_short_salt_mic_hmac_key": "96EF80B80CB868F76C623029EBBC45793B78D584F43D955930C74EACF1256885",
+      "v1_signature_identity_token_hmac_key": "1FC9D1ECDBC54491BE8F60F7663EF74B551FC5734620463D55E6A561A2AF6945",
+      "v1_signature_section_aes_key": "72EEC64698FBD42D3A53F84EFB88EC3A"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "B65D",
+      "expanded_salt": "ED9F26FC67AD4BEDFCA31BE3DD4E2173"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "5706AFC8EE2B6341CC4D3B05",
-      "extended_signed_metadata_key_hmac_key": "D790D59CCEEEA5A0C804429344D35C3AC9A515A9CFF7E10043125388CF5D326C",
-      "extended_signed_section_aes_key": "E23DF3CEBC1F83A569DDC05DE27DCA19",
-      "extended_unsigned_metadata_key_hmac_key": "A396B34DBF66E482B53D2B79DABD00F098E1E5240D45C3805F2386A1237B06ED",
-      "extended_unsigned_section_aes_key": "03D99FA28322A9506C3CB7B9F51F4542",
-      "extended_unsigned_section_mic_hmac_key": "E495765950E61B023CB6E2686ED4DC4189CA56F915CB3AF877B15ADE845EF31A",
-      "key_seed": "206C196A03EB7DE2525B6C27E478552CD3B1904D4D89D3785D51C02B8F1D9B36",
-      "legacy_ldt_key": "71534CC9368B8B599431DDAE5F62200DDFBDF8009248436F92636CF214F43E85CE80EC8703B78FC26503F6DD3634526B7301D8DDF45339A3B63C64838A09734A",
-      "legacy_metadata_key_hmac_key": "3158EF75327E4F117A5ABAB3EF29954CF76B7249C74DC50CB8D1EE4027F70CD2",
-      "legacy_metadata_nonce": "704A450B927CC83483CB5210"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "F432735228DB941C89598E37EC4ECF1A",
+      "v0_identity_token": "70AB53CC636AF05080929153F78F"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "4E36",
-      "expanded_salt": "D96E799C1F673C3AB5621DE38FB7D9FE"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "3309",
+      "short_salt_nonce": "3701F88A64084BBC60178848"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "DC4C0824F5965E110BE7C9036B6043D2",
-      "legacy_metadata_key": "D2DE6C51A1B2F0CC740A0433BA0C"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "7A718CFDEDF904C7A72BD13FD5DFDF47",
+      "derived_salt_nonce": "53C9C943AFE5BE0EF3EEE3859B536A4B",
+      "derived_salt_third_de": "42726C30FDF62DBDA0074E6FAE969CEA",
+      "section_extended_salt": "45E80FFC36670147E8D2CFBA526EFD42"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "D735F933C91F70E6703FA277DD9D6B07",
-      "derived_salt_first_section_no_de": "A1216FDE48D4E7CF716EBA6304B0E002",
-      "derived_salt_first_section_third_de": "6C6F84F7B0ADB3DC95733154462DC464",
-      "section_salt": "EE077330D38FA4E741EEB2A423C44E16"
+    "key_seed_hkdf": {
+      "key_seed": "229032F519800B9A2527068EDE2BB34852628ADCAAC3F20CD72E93B01D4AD9C3",
+      "v0_identity_token_hmac_key": "0D3D137BE4B1478340EF1FDB8FD01B917B72FD783151505E1A7D8782F9A53342",
+      "v0_ldt_key": "AA6E607B24892A16693EEB60F05AA306B2513F8D65E8A43BE6B9C135C2E6E8EFDDE759A13EBE077CC2D84785514D6DC55FD8D3BE9D075003C7BE9D7299ED9F8C",
+      "v0_metadata_nonce": "026E8C93F32AF7A8BEC2E014",
+      "v1_metadata_nonce": "E3A33A83F8DDF01A20423453",
+      "v1_mic_extended_salt_aes_key": "E4F8998F78966160243FFC98073E27EE",
+      "v1_mic_extended_salt_identity_token_hmac_key": "47BC89710BF706CED760412959675300B62B997402FA5BA1969251B22903874A",
+      "v1_mic_extended_salt_mic_hmac_key": "A0C758A7EF5B92B5CB0EB64F5406F63C03CD395DF2D2806661023CDD29A92735",
+      "v1_mic_short_salt_aes_key": "85F922D62800B1716710D7347534F076",
+      "v1_mic_short_salt_identity_token_hmac_key": "734B82605001AA6C8380F8D020D3B66D6CC5BF6D841CB54417FF633566EE42E4",
+      "v1_mic_short_salt_mic_hmac_key": "97851B6E5D32EDF9858D3261BEC3E9B6D821475F73C248EE1C82C5482C780A1D",
+      "v1_signature_identity_token_hmac_key": "0A209406D33340C3CB0052E913F1E9790029DA31184175D8D8E18795251EA194",
+      "v1_signature_section_aes_key": "C9385776AA1D74BC77CD1A7985329E16"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "7A7D",
+      "expanded_salt": "30C981ECC7042DDBD123DB6271F25438"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "E8591F5F7A4A9BEEFD11704A",
-      "extended_signed_metadata_key_hmac_key": "0D06ED71FA2DAF112A89EBA86E42961C0CCF5F114C9D5414221ACAB16493615A",
-      "extended_signed_section_aes_key": "5DCDDA469454FEB3C2EA71021F93C392",
-      "extended_unsigned_metadata_key_hmac_key": "770A74886ECB2E13C09DA386048A1FFDD32F184821CFABF97C591F2F1AF593D9",
-      "extended_unsigned_section_aes_key": "74093EBAD21C75C9FB6496AB55D71D10",
-      "extended_unsigned_section_mic_hmac_key": "528D1D684E3DBB6A55B3C74B76F12E3BF88F622796E111436F8436F9C68BD7AB",
-      "key_seed": "CB62878D769D49414CD07AB82DD01A567BD320BCDFE120E137EA6D24FD754A31",
-      "legacy_ldt_key": "526A62E198AF94F8E1C97AD51BCAD021ADE8BE68CBD16DA8FEDA9186141099AB4D1FB09B3C45E06217A50F20143C6669E024A5FF127311B46024E981A8F79C80",
-      "legacy_metadata_key_hmac_key": "C3FEF82D19D4198A12825C7B81D92F92A27E04C0D512FC07D2C144C48DF39A9A",
-      "legacy_metadata_nonce": "6A2FC44C5DCC8ECE690470BB"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "4975149498F7972EE91550E2838FAA5C",
+      "v0_identity_token": "64357B751F939F46CB00C2E34301"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "4B93",
-      "expanded_salt": "0618681E330AA95ABCF5908C7D80266E"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "0595",
+      "short_salt_nonce": "0C3874DC89E7793DA6562C3B"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "176853A104A7E84FB244044ADB23E60B",
-      "legacy_metadata_key": "AD1ADA1E3F33D44DBAD9D68546F8"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "854BB53F1FFAA03F8E4A4F89B36FC75A",
+      "derived_salt_nonce": "2B61315A2A80409D20DD3567EC8612E6",
+      "derived_salt_third_de": "68CFF39B8B60522BB864F55909F6A07B",
+      "section_extended_salt": "BE6DFEB3BC734140F9F2E7A2C514782A"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "1A605E7282BBB65E768BA802674316E4",
-      "derived_salt_first_section_no_de": "46EDD6B928BC45D5349843C3DA660A46",
-      "derived_salt_first_section_third_de": "23866A17B31C30E4B6BFCE65FF9EF2DD",
-      "section_salt": "CF0811E9E51806ED2EC869D2DCF7C3DD"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "D1031370C1491071F4D7C54A",
-      "extended_signed_metadata_key_hmac_key": "E27E04EEE22E36C87CA16C6AD7D5ADD10776EF643CE1D9CE30EB22D00A30FD59",
-      "extended_signed_section_aes_key": "15D907F25E7808AB08BECD25DC12BB17",
-      "extended_unsigned_metadata_key_hmac_key": "F3F714D219EEE49D4F2BBDEA6245570148A513989BDB07C72D940C8F6174A6C5",
-      "extended_unsigned_section_aes_key": "3449B21771EA3B2B1B246A73B031C67C",
-      "extended_unsigned_section_mic_hmac_key": "7AA8030A85FF64635D6B913A1E96D58EE649BD9E29A27F24A2EE429AADF5B6C5",
-      "key_seed": "FF255729FEA933CDB4EFEC5E38D382C2B8166FC1F61099FAB8E0DD472A1EA192",
-      "legacy_ldt_key": "95B5891E32CDD742FEFDF510C32937F19926BA8A2FF7568AA3A2049E4AC468620FB72DD2E0720EEDC69ABE700AAAD9077345CBC40B410C01F03D7B0B34EBF852",
-      "legacy_metadata_key_hmac_key": "B76F11E187F71D17D2A8038BBDA38DA7F6090E2CEDCF324D4DEBA899F9D39985",
-      "legacy_metadata_nonce": "72FE7AB45734E2C87AE34B96"
+      "key_seed": "926F979CD91828BAD536CFF2B68D1F78CA1D888D07A16717CFBDFB2A4305AF16",
+      "v0_identity_token_hmac_key": "7FEFAC357C80BE62792DDB094471D7595330063D23661E657B48A65185877295",
+      "v0_ldt_key": "CD2E40F2D80EFA65F2C34DB94F64FE799CCB5FEBAE1696FC8141E379FAC4E829F2EAE5D3E4512A03DFA0F00130117CE5A95EE9BF1AC6419618E8B023A2C65575",
+      "v0_metadata_nonce": "826FF1F5574134B44DB2629D",
+      "v1_metadata_nonce": "5524D245AEACC28113B5B37B",
+      "v1_mic_extended_salt_aes_key": "DA21A963D42360531123F21E0B1009D5",
+      "v1_mic_extended_salt_identity_token_hmac_key": "99E686E750515E72D124F70E05EC0A1191378885CCC98D827A8707457DAE7B62",
+      "v1_mic_extended_salt_mic_hmac_key": "BAEDA9CCDD8780DA6D122D777249B462D43E267A60C8AC821B6E49E52FF6E497",
+      "v1_mic_short_salt_aes_key": "B078CE6C132D1D5F03C7011CD1D1B739",
+      "v1_mic_short_salt_identity_token_hmac_key": "B2A42460F29197AA8F08367283175BDB97DDCF5716EE7F011EF7D7765336425A",
+      "v1_mic_short_salt_mic_hmac_key": "C774F2182514364F7FD446D14E545C6B94DAA8292BD09286B5A06320DF0E6E31",
+      "v1_signature_identity_token_hmac_key": "C41617580CDF1D4C81AE49774535BA6A622C319F6F02466905A13F6DFC34D354",
+      "v1_signature_section_aes_key": "F438355B1434789B8BA63BFA2FDB4DF4"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "3CF9",
-      "expanded_salt": "469C89FF0AFE6B7AEA5A94E8E5D413EF"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "F67E",
+      "expanded_salt": "67407A62A7D1A972E818EA6D50A4B82C"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "6BB8FA3FCB7F68A676918B7E274282CE",
+      "v0_identity_token": "A5520A470B231F19B9459DEAC5AD"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "B6FC",
+      "short_salt_nonce": "9A22E33E298A3D2F813E947A"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "34C8733D5598224EBE7E0F468AC42AE0",
-      "legacy_metadata_key": "B4EBC9A698A9F67B3360AB444B65"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "BB326463B805B3B120DF3A3C1EA440E5",
+      "derived_salt_nonce": "6D533671722212A5B388B94F462B66B0",
+      "derived_salt_third_de": "FCCD3C04B6AD3FBB94F5196EE5E5CB39",
+      "section_extended_salt": "E9E85B2DD811EFE01DB44C34D96245D7"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "6C0B7CAAB6B77D7E69927BEC4CF9857D",
-      "derived_salt_first_section_no_de": "E85B82E53864343FE2E11C3BBA921BE8",
-      "derived_salt_first_section_third_de": "B5C9E52B81072C158A44DA3D0F1B863C",
-      "section_salt": "55F816AB16A494DA8DB45539F30C615E"
+    "key_seed_hkdf": {
+      "key_seed": "1E886D86767B2D1CA915B5FC93342EBC806AD6514B0A6FE3BA86837E7C9BB251",
+      "v0_identity_token_hmac_key": "78C9838980927982C90BD31EBBFAEB6437E8C80E9815601BA8DB01AEB8D5C7D5",
+      "v0_ldt_key": "FB4EDFC50836341EF3902AB6838562C0AF614BA3D1D1DA817E78A98EE70517F060C059EB79126CD944E1DA5E9F577A3F82A096769D58A28909EF177697D31106",
+      "v0_metadata_nonce": "FBF4B4DBF353419687387277",
+      "v1_metadata_nonce": "08E00B2CCC92CE1C0E6E3384",
+      "v1_mic_extended_salt_aes_key": "00EE827633E2295F9CBF0C880DFE4CB2",
+      "v1_mic_extended_salt_identity_token_hmac_key": "985DC19EB9D9B8F3921B82AEB1F324C0DEDFCCE99871D5A65D1978C05B2DA1EB",
+      "v1_mic_extended_salt_mic_hmac_key": "DDF3D0BF099CD0A4AFC96E682BA82414775B21F69F7604CC4EEC3A70AD9F1909",
+      "v1_mic_short_salt_aes_key": "10D98E2BA375142D53279A7C8BAA18C9",
+      "v1_mic_short_salt_identity_token_hmac_key": "A3DC4E64B719FC69E60F600ECD6A6C61A1E2DBB03C936A716CDD23BA486BE5DD",
+      "v1_mic_short_salt_mic_hmac_key": "56FBCE7F9800AE9985C591692672ADDD87F9E97DAD409DC33A428C2DAE66D4B1",
+      "v1_signature_identity_token_hmac_key": "F5E639C319E632569E882D9426505E71A1E283584F4DA0CB23BE6792F4708F19",
+      "v1_signature_section_aes_key": "74554872D23253B364539E0DF37FD1B5"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "2C27",
+      "expanded_salt": "FFEC190578D4675D3E8F3B255B665B09"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "9F3A841BC948F67E6F6C1394",
-      "extended_signed_metadata_key_hmac_key": "1741C9FEFE5F3C189A06E18FF55693BAD6F850740AE5EE91CF0CE7E1A6A48D65",
-      "extended_signed_section_aes_key": "6624413BF4F1665423E6AFC7E48F6DBA",
-      "extended_unsigned_metadata_key_hmac_key": "79009162ED60569C88E211D09543E5612C1D5A590B8277C44F5F86A3C1ED26FD",
-      "extended_unsigned_section_aes_key": "4AF8134B5774E0DA661D4D74424BDC8D",
-      "extended_unsigned_section_mic_hmac_key": "FC55078A316070CD61B23304D7E0A076490E3425F2257503D1FA3B13F806B8DD",
-      "key_seed": "17D52F8EE6C17DAA338290F9E0D08F91C9BE1AB1DEA910D487309BBB09F67F0F",
-      "legacy_ldt_key": "F01C06D770236831C55B3A10BC648812125B0A8F65367DE7AD83E48F6EB6C2151F09F83B933B2339BD08E36DB74780BE1F435F035DE643D28CE067AF88D4E30A",
-      "legacy_metadata_key_hmac_key": "4BC9A35B25DA286883594B8258037CCBA46F26A76CF3D16DA66B79A5AA645FBF",
-      "legacy_metadata_nonce": "BAC048C167A5890EA8D5D060"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "1AF203A4134F84A95EC2A465E61E6FC3",
+      "v0_identity_token": "B43A2C7B5909178A9FA4A81A3AF4"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "651A",
-      "expanded_salt": "D9C1AB2EA41BDCC8839A83CF4EFFBEA8"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "EBB5",
+      "short_salt_nonce": "9967714E945DD4F451DCD5D2"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "ECB322FC63A5B136F19A39403AB4A3FF",
-      "legacy_metadata_key": "0224F2C3F01DF3961A9DDB33D33F"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "263AC32CE6FA4D0E5F00671D461C4082",
+      "derived_salt_nonce": "D5216F638FB5E3148C23E3568B02DED0",
+      "derived_salt_third_de": "F5E3DBC571628A18F0832B3A78BB144D",
+      "section_extended_salt": "50AAE9055970B5F6A79554148AFF1C67"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "9BCC1DE1FB74CC1CD16D0B4200B04D30",
-      "derived_salt_first_section_no_de": "9ECDB554F3EF801381DB9373B9886BFB",
-      "derived_salt_first_section_third_de": "B898A171766659C0B6CFD5A54D7D3ED6",
-      "section_salt": "12120586C731008F58E2EBE90F2D9FD7"
+    "key_seed_hkdf": {
+      "key_seed": "E6B40DA9CA1C23164262097414042F425448D4A63997DF8AF14FD97078365506",
+      "v0_identity_token_hmac_key": "9A47EAB582881AAA20B3E09F318008A01C2978ACD715BE8470399821D44B22A3",
+      "v0_ldt_key": "A83EE2141E4896FDF7E0E50F47162919E6837B02A778BE6C77EF84F06425231C7E6F66A403BDE3AB706D394392F8B215E2E1B6F8B135E7FFE487F8EEA553D5C3",
+      "v0_metadata_nonce": "78FA2208BF4A129F35882DA6",
+      "v1_metadata_nonce": "74E88D1A4199CAB29AFD006E",
+      "v1_mic_extended_salt_aes_key": "0B080F62AE2FBAA92991B9381E5BFBCE",
+      "v1_mic_extended_salt_identity_token_hmac_key": "B3AD5C680CB808670B08AE7B3136A329CA755AA266086020CB13F5445D3AB90F",
+      "v1_mic_extended_salt_mic_hmac_key": "3D035169BBBADA723E634DCF0E203D7D0F85ABAA4DADC4C33D1DB14908C31D1B",
+      "v1_mic_short_salt_aes_key": "54AF93FD026428575900009294AF21DA",
+      "v1_mic_short_salt_identity_token_hmac_key": "B4F6A45A822954C251F04E5F54CCAC07FAD0826B5C323F4A9D2755E0E184893F",
+      "v1_mic_short_salt_mic_hmac_key": "6A8B6CAA0F30C013A40BE4535FA727FE47A832C4BAE0878E72ECE9DAC538807D",
+      "v1_signature_identity_token_hmac_key": "2EBBD9FA35E24808B06361017D2ED29263F9BD857595AEFC4336740485D3AA41",
+      "v1_signature_section_aes_key": "44C1F00F21E21DE548332224F191B56A"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "8C48",
+      "expanded_salt": "92DA955A90A7A181E9A744C7F64FCFB1"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "43EC170E0B76202475557760",
-      "extended_signed_metadata_key_hmac_key": "99B0F78F43A68D4F6367ED11338BD73DA00CAD40BCC917DC0E10CD1D3B8225FC",
-      "extended_signed_section_aes_key": "FDB4A65AC4DB43107994369895BB7340",
-      "extended_unsigned_metadata_key_hmac_key": "A4E32E714DBCC3AF4BB2054620ED0E26B05A00235AA46DE36E87DD1C33ED4B02",
-      "extended_unsigned_section_aes_key": "3A50203C921E126E8D1B0A31660E025D",
-      "extended_unsigned_section_mic_hmac_key": "F722C6EA33D7F8257B668A6A48AD451E90292DA2B5C5A2565383AABB3B75B158",
-      "key_seed": "DB33ED53F748F16D4C44A0D7256951E46737473A2ABEA4FF1A66166072D198A4",
-      "legacy_ldt_key": "4679142868B7A72EAE5D275C6077F84BF4A2AEF5A6B638925752B30C9A43F356CEBD1000C540CD5103F5FCF620E11FEE9900D951667026D68770E1CC9F37F9B7",
-      "legacy_metadata_key_hmac_key": "B81CE82EC7918F849BD209F9CBE4F6232FCC22FE773D96A7823468BD9FE6F6BF",
-      "legacy_metadata_nonce": "4DEC4087D83773E0A57AD6DC"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "C60CB21C78BAFD9ED8A08E18C3DC9C1C",
+      "v0_identity_token": "B08BC761A0A1F9C72DCAC3168AF6"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "C854",
-      "expanded_salt": "7F509331897315561AE87E8E465731D5"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "8561",
+      "short_salt_nonce": "A8A5A0D023F982987C6C2912"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "445F1AC997B6F6258C04D6612730F576",
-      "legacy_metadata_key": "5A0395FCA63385D863B94C161830"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "731880CCE113A561F779D98EC3BED041",
+      "derived_salt_nonce": "1C3A582B0B0E6461B150A1559F122B76",
+      "derived_salt_third_de": "374E30A969CBB37DFDDE376E217E4427",
+      "section_extended_salt": "FD6B1023E870A6908EDA3CD8067004BC"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "C5D4701E3941FA12EBDFE7EAF8D1103D",
-      "derived_salt_first_section_no_de": "CA9740A690C83B28BB8EB6C99A28471B",
-      "derived_salt_first_section_third_de": "0C2ACC54B8EB3433574E87CC03090508",
-      "section_salt": "1420CFC23918397B5B0743A7BD1D33DE"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "2AE39BFE66BFF7F3685B7FFC",
-      "extended_signed_metadata_key_hmac_key": "1E5B00AADD20BEF97BB6CA33F97C20661F041FF73DB72D33787C374028FBDAB5",
-      "extended_signed_section_aes_key": "59A55BB848376BB2A1F2212E954743C4",
-      "extended_unsigned_metadata_key_hmac_key": "A33A337FC8BF0C5C435B46121BE28F5D119617D044C990ECDC61A36F8EF60808",
-      "extended_unsigned_section_aes_key": "4B6F0EA5C9C9A1DBC1203B0B5AA1D81C",
-      "extended_unsigned_section_mic_hmac_key": "D5B603436CA006324D83AE5020DAC7BD98F9E05EF3E94BA41F63558E2282FED2",
-      "key_seed": "0982F189CB2F763A17D3D68430CA8DE5C22D6B44C21881CF76473C21F4CF573F",
-      "legacy_ldt_key": "C61EEED8BF13DC405DFB8C57232F66381B7FF78CDFD4DA1FFB6737565E5419E98EB38C237064656DB9BC31611F4C47864D54DEB96CEE29F4BE9BFA8CA257D036",
-      "legacy_metadata_key_hmac_key": "DB9E4BF67F914548B813C071B42B37A486B1564DEB98D90CF8B4A020DB9A2702",
-      "legacy_metadata_nonce": "3BBD17F0F4C43E279C69A7E0"
+      "key_seed": "B800FA4783D4EC4D80AAD9A805AD0F9CD1C26BAD38FC5655A8755A7768505014",
+      "v0_identity_token_hmac_key": "F1DD45F4F5C19D4344F7CB0C04F4EF5E5BF8B417A11E3140006A4F4CA2060652",
+      "v0_ldt_key": "B185F3EF5EDFE2F2B722A0DCB9904AB533A12585B6A3AA897FBCBD3B435BDA5EAA9E5A76C95D64C993C36687807C1DBC5846E9A9C2C386129FE9DA39AB237788",
+      "v0_metadata_nonce": "6EB8B910DC2F0094CAA873C3",
+      "v1_metadata_nonce": "600080C27BA115C7317BD754",
+      "v1_mic_extended_salt_aes_key": "B16E94AC636F789C62A35DE4F974319B",
+      "v1_mic_extended_salt_identity_token_hmac_key": "47E5B25A33520F10E5FEFD1DA06E0908B671CAE8CAB54C5B40BE9541C5E18E97",
+      "v1_mic_extended_salt_mic_hmac_key": "3CED276A31CCFB412EF3E22B0E048907CD73A2BF39955B7C73EFB295689469A5",
+      "v1_mic_short_salt_aes_key": "9DCD2E8874EE2E6A092618AFAF397D20",
+      "v1_mic_short_salt_identity_token_hmac_key": "284F83827CAC37A22DC0CC2A3F10BDAC8A0E3859218C5DD5FF1F6277268FAD3D",
+      "v1_mic_short_salt_mic_hmac_key": "77FAA24269AE6A5A4B61569DCAF8AA75B155C8D483F32AA26ADDAC49C3AFB3EC",
+      "v1_signature_identity_token_hmac_key": "FF533E6454F471C4D5AE1CBE51A085082147480E06881A193CA0375D30305247",
+      "v1_signature_section_aes_key": "E0AA39A3E2D60041A8956A6B8A3FE4E4"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "065F",
-      "expanded_salt": "1360903389867A3F865080EE99218A93"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "BB3E",
+      "expanded_salt": "930D14EDDC1B681C49C2B315D3E89E16"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "F498977D3CD6C519A4BF36C1C10A2272",
+      "v0_identity_token": "3B0DA1390CC2F8F53F5812FE80B2"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "468A",
+      "short_salt_nonce": "147C2526D096E43BFB972E05"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "13D9CD7AB4481EC78AAE713AC7F7A534",
-      "legacy_metadata_key": "13A49EE19095CFD061978BE2DA07"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "0E396FA0163DD119AF3097362E838146",
+      "derived_salt_nonce": "6B87889DA7443CA9F8D9526ED9C86B14",
+      "derived_salt_third_de": "9E91F27B911BF1CF6CBE13CF1BF43338",
+      "section_extended_salt": "72B71CE5E6A3534C17A0A3BDE29FBE0B"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "5809826FE4C41311D0DF8490071E4BA5",
-      "derived_salt_first_section_no_de": "B10FE9FE08BB71AAEF17701E3805E337",
-      "derived_salt_first_section_third_de": "5C8C4CFD85E659D2CCC0A735E4C14C44",
-      "section_salt": "C1290354344488D39DD88998567BFA1E"
+    "key_seed_hkdf": {
+      "key_seed": "3E847398512F2295877A52FC6693C509A77D284B831AC46D17A35463141BB380",
+      "v0_identity_token_hmac_key": "9608CC1C06C3CF935FA9AD9576F7B0699C01190CA821A8BAEB3C196168DB7282",
+      "v0_ldt_key": "68803069E994A0549B92D48777B90B4ACAA691CB67EFD7407273A731C15E8DAD6705780FEB3F9776F22AE3313277E8A393EDAE23802EF457506152A082F9B2DE",
+      "v0_metadata_nonce": "575FF3DD58EABC3013DC3FDB",
+      "v1_metadata_nonce": "DF4C249AEAF7B8651989EE33",
+      "v1_mic_extended_salt_aes_key": "E7AD78F689F894E6415BA5A7538A6B8F",
+      "v1_mic_extended_salt_identity_token_hmac_key": "BB2FA7C1AF13564D37339BF793F69588259FE5F5744165C9DA3A2DCC31BC4DDF",
+      "v1_mic_extended_salt_mic_hmac_key": "A2B891CA3E764C78518ECB419C76EB9696F5B5489B7DAA59246A775C175AE8CB",
+      "v1_mic_short_salt_aes_key": "F511DCF5E5239F6F001CCEA3582BC561",
+      "v1_mic_short_salt_identity_token_hmac_key": "22C4B6CB1135E483F7703A06BC9F71BE65CC7901215DD30047F86ADAAFFF2216",
+      "v1_mic_short_salt_mic_hmac_key": "14D1B893A3947846911168A2AD7BF475D5B10B3AED3E16EB13B82A6AE9E3D5ED",
+      "v1_signature_identity_token_hmac_key": "2818C76B7C6BBC90069219FD3C9B3630A35582813A71627B00D6C255DE831655",
+      "v1_signature_section_aes_key": "AC76472156646E7D1F1E7DEB26409B8F"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "08A7",
+      "expanded_salt": "F848BC5C8A9B08E012A03B3A142F90E4"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "A0A936CA136E8ECA670708EB",
-      "extended_signed_metadata_key_hmac_key": "27200867B9446FAEC18E4B472C0C2A43D8180753607B42B678184CC5CA3B1AB1",
-      "extended_signed_section_aes_key": "2214D4050FEBA4E4C18543FD920D15C4",
-      "extended_unsigned_metadata_key_hmac_key": "51EE6B49F353568E055B06CFB717A8A467DEE6D90A6D5B325052DFFA15564C6E",
-      "extended_unsigned_section_aes_key": "6F162714F3BDA65324EBBC4B328CD0CE",
-      "extended_unsigned_section_mic_hmac_key": "DA51358ED2AA0916325399E9A2B0244D1791A1C5D600AFD8A351BBCADBE616EA",
-      "key_seed": "8EDCDDAE0F2742D381A7E56284E6804AC68080F872F6A587DE4DADC5B589A4A5",
-      "legacy_ldt_key": "6AC852D94DAFB8C18917A6406D3E8364125F01B03D1EC583FD20E5F9B71DD579B807D3874C4B3AB0FD9582F505E558C98F3E96F609636AAEAB90E9DE51703CA8",
-      "legacy_metadata_key_hmac_key": "89D14E7C13E4B33BDA2B0A70AFC4376FC82BC5E8B6D028F1C91D0E9159F9D549",
-      "legacy_metadata_nonce": "CDD93F6D02D83D0685DF0729"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "395560A60B904BDE2A963527473696C2",
+      "v0_identity_token": "8C8B58E7FACCD45E79BF08405433"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "5E81",
-      "expanded_salt": "A1D53BE4EC3D0418587BD370BEFC413A"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "D46A",
+      "short_salt_nonce": "E74FA992910E3A0F20733446"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "39B757F61E136408F7D8CD58DBAE1A89",
-      "legacy_metadata_key": "F58C1AE45F74112200878EB16AD2"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "BE63ECA3139C3A520E562C416E4BD42A",
+      "derived_salt_nonce": "77699741250F48BC8D6C3C275C7129B9",
+      "derived_salt_third_de": "2D57D9556AF4D0076136D20FDBBBE600",
+      "section_extended_salt": "CF0DCBFE60B56E6199A39F86BA47CCEE"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "4B9D669E994848CF832475821E5BA9A0",
-      "derived_salt_first_section_no_de": "B0DD4242F201361D92B775C5F192C979",
-      "derived_salt_first_section_third_de": "E5A6AE615AE988C06CEAB18846B5F659",
-      "section_salt": "33CC65E1EC1CE33B169733DC3096B450"
+    "key_seed_hkdf": {
+      "key_seed": "F4FE47350546180E8E5343E508933EAE003797A3CDE161DEC844E9BD0EBE8361",
+      "v0_identity_token_hmac_key": "472974F3E94D84F6C1BF3D2F805DA381AA033B333B012F16F8EBCE6264E8B028",
+      "v0_ldt_key": "515D9B38BF19EBAD10149CC42317B96796B74284F7EA8C15E4F163A37C8FE16FF756F76AC6C460A513CDC0738886B0D2E472525762810A86D84D0CB8CAA23226",
+      "v0_metadata_nonce": "C7DD8B64D78078D2F303EA8C",
+      "v1_metadata_nonce": "817FBD157903FEB877409931",
+      "v1_mic_extended_salt_aes_key": "A9CAB585445FE1CEDBC7A2B8F1C83C25",
+      "v1_mic_extended_salt_identity_token_hmac_key": "D5BC4A745CBEDC33F297CFDA6D1BD14C8DCE74F9AC6CBC8D5EB1D96A5DFEDA3C",
+      "v1_mic_extended_salt_mic_hmac_key": "8E377E6E535A84940C721E088559D3AABA4F448464DCE43227B446E8DE373E01",
+      "v1_mic_short_salt_aes_key": "0CDBB34EE71BF6F99ED10A8DDB0A3D2C",
+      "v1_mic_short_salt_identity_token_hmac_key": "C3A22D602232DF672FEC71BA616D49C882E9F6140379629A38DCA2DDD8BFB394",
+      "v1_mic_short_salt_mic_hmac_key": "CE5169D9EC16AF2D039574A47BD1B892A57AB874A214BDE3C9F078CC527F9EA1",
+      "v1_signature_identity_token_hmac_key": "4DF918930A3D2DC8871481B4FEB538331D57759E6F0A9407A06D15CF05809EE2",
+      "v1_signature_section_aes_key": "756B3BC24AB2B332F46A02620630465D"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "8F23",
+      "expanded_salt": "473F3FA8970C09B7EDBBF7736DED37D8"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "DDBF503438D36104DA4B319D",
-      "extended_signed_metadata_key_hmac_key": "DB2C6A4EEBCA496FE06C311A8A26AB0C208E2785ACC33A02AEB4ABC1FFC8E5BB",
-      "extended_signed_section_aes_key": "8E5E7C2C79A489BA786B7FBA6B85383B",
-      "extended_unsigned_metadata_key_hmac_key": "803D250E88446724C7192F9BBBB5F1E4EBA342D5391DC8CB8FF53349D4E8DB67",
-      "extended_unsigned_section_aes_key": "58C6E7AFDBC57C0482C6E611EF1A346E",
-      "extended_unsigned_section_mic_hmac_key": "D704D0EC34F6D775C575D3C09AE34A5CC1926C88E59A4DDE6A8B5E0FAC2AD3F0",
-      "key_seed": "EFBE104969A3580E48A63285BFA56DB95CAF3C81137B5DEDA8BBC0ACC12C658E",
-      "legacy_ldt_key": "382BC725B508E73E5616B110C92CD8F55CEAF87EB9E0E5BC372150AD9BEE1F1647F3944E3171519B94249A3A789D472C9961FD675653927D1DFE345FF3266358",
-      "legacy_metadata_key_hmac_key": "8D6251503286E357F6EC394CC68446A36BD0C62D50F20FAC6C258B3DF1FACC4D",
-      "legacy_metadata_nonce": "A19B0D2A97B7F3B3CC296F97"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "DB6746C617AAD634961C3A864D996DEF",
+      "v0_identity_token": "EB699C1CF261CA6323DA7797BBE2"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "5FC9",
-      "expanded_salt": "C69D7BCD94B23FBD8E2AC68B96330BF5"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "BF2E",
+      "short_salt_nonce": "EC8FC7EE9EF97564C0478CFD"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "6AFDFA46631667385F2C5D764E5301C7",
-      "legacy_metadata_key": "062743EDE5C56F5130998BADF2D0"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "5DC4750A1046E2318D30B4B2612F4DDA",
+      "derived_salt_nonce": "AC3C59C2FB8EBABD7150FD5B35F87323",
+      "derived_salt_third_de": "12C1E5CB114FA19F099E4E49C729B365",
+      "section_extended_salt": "2F130A2F720D4DA91B4C0AAC8B3F2F1F"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "58552F903E39FAD1B6FF06B7E55835B0",
-      "derived_salt_first_section_no_de": "586199F442C8473B9690A6AEAC36A405",
-      "derived_salt_first_section_third_de": "B69365658EE47B79809645ACBD160173",
-      "section_salt": "D08A6947309047818E4529E4CC80F9E2"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "1C2BD072C9330CB6D3807B9F",
-      "extended_signed_metadata_key_hmac_key": "7EB76208F558DB04B0ED9C741F7F4146AD323ED5A9676708F3C2718FCF61DD5D",
-      "extended_signed_section_aes_key": "9E043B7A0F1D242100D98F88D77180D1",
-      "extended_unsigned_metadata_key_hmac_key": "4D6397EEC391828BA27EA7E0F01DCB8350A081577CC0DF9FA3C244CACA923EC5",
-      "extended_unsigned_section_aes_key": "13D8B05613887623BFC72CB4BD57D0DD",
-      "extended_unsigned_section_mic_hmac_key": "249A8EFEEF21C7597AB5EAAABA00536ADD7F2D57CDB1C7F81914946748922B30",
-      "key_seed": "C952629EA0AAF16EDF450AB792C0FDF454B3748F14F8228CA39B3AC3D733BCC8",
-      "legacy_ldt_key": "7D90190372B6EB72DE892F159FD90C723D0B30CD1F97D7A6C15536C8766335AF62DD0BFD8FCB33CF808EA9A8565EA8AEE60D03B9A21849C7473B5E653DD58E94",
-      "legacy_metadata_key_hmac_key": "C02E17C17A1E010FB5F8B34E104B6F4B83C3697CE3878896D9C1D8E12128F28A",
-      "legacy_metadata_nonce": "D2834CF3DD852C5D65EF47AF"
+      "key_seed": "C299CC5A114DD4BA0600008349117F85F67333E87C01E772726CA97BA744365A",
+      "v0_identity_token_hmac_key": "86E6D0B6D73CFF4FC2EE2E16D4807D9BE50279BD23514BE123CD273C514451C3",
+      "v0_ldt_key": "1F8AB48896AB2E840310BE0A100EF4CCC8C54576C9C78EC9F433E152CF15D763F766E235198AA702E4BE714D605B773B4E1A6877EC85C2623CAA01C5A9BF8F61",
+      "v0_metadata_nonce": "440BC42EEED871404EAC99D5",
+      "v1_metadata_nonce": "7D8330F7E71F245D8C020753",
+      "v1_mic_extended_salt_aes_key": "45AD1594A7C622CE770C2D754E5F57B0",
+      "v1_mic_extended_salt_identity_token_hmac_key": "1C7636E375A0BD3ED90FFE231977CDFAA8E392C6A77890BE59C4D9877719194B",
+      "v1_mic_extended_salt_mic_hmac_key": "F4C3B085E1887428A07A2EF526B4C54AABDC6A596864674297BD7225FC2BA7AA",
+      "v1_mic_short_salt_aes_key": "19FFFA58D00FE48FC03D4D5D6ABC1D4E",
+      "v1_mic_short_salt_identity_token_hmac_key": "31D79DD4EDB7BE05562955557ADD32358760D9AE327656F563DF31311AD4D2DA",
+      "v1_mic_short_salt_mic_hmac_key": "38846B9D18CEF86540E5F2D2C94E791B1756EB5BB3EEB9D28EF5E60B32D33C8F",
+      "v1_signature_identity_token_hmac_key": "2C78F12CB4F9F423FD2143027E04BEFC493FBE189AC118EC4E5BF6E2E2BC6328",
+      "v1_signature_section_aes_key": "B8E3B89FE6CB6F9983469499A29084BA"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "79A5",
-      "expanded_salt": "18FB4E698A2B0F8FEEAE8D6571217051"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "7AF8",
+      "expanded_salt": "D672F8F7DC19AE5A881214BC736A76D7"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "359493D5139411922367E5C050162264",
-      "legacy_metadata_key": "579718BBEB70558182C85652E2FB"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "BEB747A470A9DA803D1BAF126C3EC943",
+      "v0_identity_token": "5A0CEE752584C7D960B4992E05F4"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "56AB",
+      "short_salt_nonce": "D61316F899E53429A2D50DB3"
+    },
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "70972284567C448B649644F5B6EAB7EB",
+      "derived_salt_nonce": "CBF23EE5038C1D5C1D3CB7B1BF408DDF",
+      "derived_salt_third_de": "D99B02CA713BA66DC867CC60D2500875",
+      "section_extended_salt": "4C5CD144852DF461550C19312FDE138C"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "81083ED8ADE4D6E6EBFEC991A837AC97",
-      "derived_salt_first_section_no_de": "8BDCE6C08C41DDB5EF3990EE1962965F",
-      "derived_salt_first_section_third_de": "6451CAC1C77CDD02F6339AC8F5011935",
-      "section_salt": "19E1A6466DF15274E28FD2AA96C01BB0"
+    "key_seed_hkdf": {
+      "key_seed": "BF2CD3D625C9ABEB44B0789D4BBF5A7BD4BE3A4B5A19DF04E103369C8FBD4F2D",
+      "v0_identity_token_hmac_key": "81950F013B4A13F7EF5B5D0B291C6EE4B1C62F911F3AD01F30B37699AE21BAE7",
+      "v0_ldt_key": "64489F1C234BBFE2217683F96A98B35E5BA4F4515DC6449EC5A711F4248D637F79AF342661904FA8B60DFF457BC879ADA4AC1CF5E73646F201E07594E58C316B",
+      "v0_metadata_nonce": "E48B58BEAD817E906CB7BE7F",
+      "v1_metadata_nonce": "89D0D8C13576F46B74195B51",
+      "v1_mic_extended_salt_aes_key": "CB093E649CF4ED1BA952819C983B0A49",
+      "v1_mic_extended_salt_identity_token_hmac_key": "F8D27C142C1879957C7B0FEABA93097D1AB2F1FE6CE2B4A780EF3B7E12A7A270",
+      "v1_mic_extended_salt_mic_hmac_key": "726C6770FD205CD85F732FED55421A4DB1D7F861E028AE144E883FA88128E099",
+      "v1_mic_short_salt_aes_key": "0CF33F93B242A4095821CFF75ECB9A95",
+      "v1_mic_short_salt_identity_token_hmac_key": "A0E1E2FADA968696B8A421D348021783D41BF5B92A8A61CBABAA3A605706FD70",
+      "v1_mic_short_salt_mic_hmac_key": "32774F6FC589A6A0F3C57D3A3FBDEAAA8C94CE31BC1BE2E71A770C29A365E8C1",
+      "v1_signature_identity_token_hmac_key": "3FA4A5EE8D7CB441D33519D371B615733E82CD99CE5DF704B4BEFFF525B5D484",
+      "v1_signature_section_aes_key": "A69439EEE9E503DC46CDDB7220E6C1BF"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "C47EAAC92D0E2BC76D5B7168",
-      "extended_signed_metadata_key_hmac_key": "8666FC7A47A63BAB78216CE4225E819D8680DB8C6F79F3FFBFB8B573C0A35C95",
-      "extended_signed_section_aes_key": "E4CFAEF9DAA97426A0F6776ABBDD0615",
-      "extended_unsigned_metadata_key_hmac_key": "18B3DA3C8736739FBD9D203493BC7697E47EC2072DE351475F332A989CCE2600",
-      "extended_unsigned_section_aes_key": "3732E014F15539C1999F6486B7933A43",
-      "extended_unsigned_section_mic_hmac_key": "F0ABA5CC299B1D3C210774DA1B065BCBB877A0991EED7C34E1DF5B318D1D5C25",
-      "key_seed": "E49335220345649BE4C6AD75B3EDE2A49A9CD22D291764E90510826835CA3528",
-      "legacy_ldt_key": "637CA1A46B169B80D0833678505E3FBEBDDCD42C7D297E78EC5DD6CE1F8007914742F09396A5439592568445117436BBF0B8A4075BE65843259B28A49C8C4A3F",
-      "legacy_metadata_key_hmac_key": "1780831E70950E84CEDBB6D08591E5867DB33D64B9AEFC60C1D29D3F4BD0DB6A",
-      "legacy_metadata_nonce": "4FE56B42C34913314149EAD3"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "DBA9",
+      "expanded_salt": "D67693A7826F8BD42315C68A72C5FF35"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "EAAB13D180BC7816E235F2D5CCE25C5F",
+      "v0_identity_token": "953C70D3F668A9BF305EA11FA5A5"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "7369",
-      "expanded_salt": "7B7D59EA9882AC8D8E5B95F11ED6773B"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "9443",
+      "short_salt_nonce": "EFDA8286D9A7A9B08A098C9E"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "20D2CA28DAD44F40276871C7B8AD6D1A",
-      "legacy_metadata_key": "AD5DBAB68501DADA871E6CE6EFF0"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "932952F0056579BD3532A369D60A6F70",
+      "derived_salt_nonce": "0F8B66EE0EF87D7B6BE39BF707378448",
+      "derived_salt_third_de": "DED92B09B5BC920A8AECE3DB8C7AA0C1",
+      "section_extended_salt": "CDA0859A2285EADBE17556BD28F0D67F"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "C27C4FA03E43A46EADAC3A827A6ABDA1",
-      "derived_salt_first_section_no_de": "28B6CEF526584124B03720FC4A65188B",
-      "derived_salt_first_section_third_de": "76B42830B3813795F19CF07B6EFD17C8",
-      "section_salt": "3DAC056F62D259B969B815D08DA79507"
+    "key_seed_hkdf": {
+      "key_seed": "C759E0D5EFFC5ACB780EE1FF77B2B82808A4418F5103FCDE125ACBDA1CE010C4",
+      "v0_identity_token_hmac_key": "894B0D42B56405F75E28594CAA888EFFE173A6399B892C4AD6DBE49A197E3522",
+      "v0_ldt_key": "8DC248F5B78A21E5F1178157EFA2933ED5A57FAD965560FCB039FB41271AB5CC26FE160E3FB179E5DC04C15E51EFD2A13F4E861F3E85889457C8ACB0CE8FA4E0",
+      "v0_metadata_nonce": "988562C19FAF889DA9390D3A",
+      "v1_metadata_nonce": "1EDCB2F7F9D00FCCE80BEBE6",
+      "v1_mic_extended_salt_aes_key": "159A387D0884959C1E2EB955EBCE035C",
+      "v1_mic_extended_salt_identity_token_hmac_key": "5646920E0104AB7CB77AB3391A61FF420129C1F499B13CA122DA6C77A3BA7728",
+      "v1_mic_extended_salt_mic_hmac_key": "2B2F8575D91F2B3964DAB5721DDED08B8C12AE5C0B984DEF3363994729661130",
+      "v1_mic_short_salt_aes_key": "EDE131CD4C8D0F1251E92C3A3BFF7BE0",
+      "v1_mic_short_salt_identity_token_hmac_key": "DF5823CB7AFD4442A6BC99342B13B9A94343570D487F47F99B15B41131FDC291",
+      "v1_mic_short_salt_mic_hmac_key": "729CDAF9889B1465960C519EA7C5A2D93ECDCF08A252047125284A6A4AFB8423",
+      "v1_signature_identity_token_hmac_key": "DFD3C298F8D8AA5091667EA7DA56C1749259021B5D046E18FCF11515606605EC",
+      "v1_signature_section_aes_key": "1F0821DA6799721FF225950245DC62A2"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "8BC4D291417DA2637FDCA948",
-      "extended_signed_metadata_key_hmac_key": "69305759C5CF4AD06490F51BF9305543E835A219F53EA84F39ABF714F6B2F855",
-      "extended_signed_section_aes_key": "69E4C6301882A5494C10231DD82FD9AB",
-      "extended_unsigned_metadata_key_hmac_key": "F6F45381D06AD669756F4BC421495F60F02FAA2FFD3ACF9FAE6755CE54AEEA0C",
-      "extended_unsigned_section_aes_key": "744A7950D65D03AEEF5AF17C36AC3954",
-      "extended_unsigned_section_mic_hmac_key": "AF012F292628BFCB7421B4A846361AD4829FB93FD766A014A5D846B7F471F5A8",
-      "key_seed": "B46750D2A75BDF9960E825289F24D8A1C777C4481FDE9E1E38F72A3DF7B29AEA",
-      "legacy_ldt_key": "7C689AF4CE7D769DEC4BE41DE5651B9C87F76D988C90DF4A5263859159CF855DCEFEA5B0047F450AE9033FE5E3C4D96D0B81DA5D52C94487C481C11BC5204A71",
-      "legacy_metadata_key_hmac_key": "4EFEB0DFFB5E37BF0BE1373A4917078FE2F54EAAC0E22CD049D3A84F79CD5BCA",
-      "legacy_metadata_nonce": "0185B0580338EA6BD57D801E"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "4BF9",
+      "expanded_salt": "F27CC32AEDDAFE2FC02FC67785977931"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "F857C81818AD95DAA7A218B71493D27D",
+      "v0_identity_token": "1CC5F7109208CCEF51918F37A789"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "4456",
-      "expanded_salt": "4C0B15F195F4E9506FD3BF692C8A81A9"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "0E95",
+      "short_salt_nonce": "8D12E132EB3558CB5A2A1A0F"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "F7B19B1F77FDDDA49AD637F92F559434",
-      "legacy_metadata_key": "71C96FDFE7539BAA0A52B6F4C1ED"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "A568C7960F41860E4CD438B7E09DA45C",
+      "derived_salt_nonce": "82003458C32B5E1408B98668BAFCC9C4",
+      "derived_salt_third_de": "BC2E3F5E799FF967924D4C31E7A8A0C7",
+      "section_extended_salt": "CCC4AE71E0147BB16F2EBCCDB9FE31A1"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "DB0238E42DD58FFCEF52697A6B60EEAC",
-      "derived_salt_first_section_no_de": "76B8797D8220B4F7D3F2994F57AF40F6",
-      "derived_salt_first_section_third_de": "8FEFF73B163E9ECBF0B75743237146C2",
-      "section_salt": "3FF3074E6E32C3A1282DA7E7486FD5D2"
+    "key_seed_hkdf": {
+      "key_seed": "398915BD66C14A84D4FDCD43C8F1DE6C8CF2BC38022B46C3628F3180D0D9C479",
+      "v0_identity_token_hmac_key": "91447A689F29B11D432DB5A28780ADEA084956BCA2F39040C5DC583036935EDA",
+      "v0_ldt_key": "B7DCF4BDF2590C365200462F2740B1A25108D174F2DD489D0B3AD0D60DA354E884A5FF038167D780A855C04B5991F37554BEB0D5D199C35F929601B38ECE4C0D",
+      "v0_metadata_nonce": "BEFC1162FFC2D7B05AE4721C",
+      "v1_metadata_nonce": "ABBEE58AA1A5C5902A66CDEA",
+      "v1_mic_extended_salt_aes_key": "49D90757E91DE84386B7606327779E1E",
+      "v1_mic_extended_salt_identity_token_hmac_key": "F2E067C282FAA0C1EB289976ADF992BC5C4774E0830E9D90F195259A859FD06B",
+      "v1_mic_extended_salt_mic_hmac_key": "905E498FA9444EFC1C23D87B1A48DFF08CD0F71199F5D4A33B0A8D9C03B687FA",
+      "v1_mic_short_salt_aes_key": "D023099AA0859DBB1547E3736DA4D22F",
+      "v1_mic_short_salt_identity_token_hmac_key": "9446AD8F40DE7801AE8C05F4198221C4545836FAFADCFB6A9F6B6811C17320C8",
+      "v1_mic_short_salt_mic_hmac_key": "2F437832F6F6055F3D9FC22E1FF89071C579F9261B200B1289E40BD1987FCE62",
+      "v1_signature_identity_token_hmac_key": "08DEBDA02FDB8250E3B6DD38CB14E6423F414733847634154DA243A0A48709CF",
+      "v1_signature_section_aes_key": "5AE2258B089A53E623FDE10A849660B9"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "45BD",
+      "expanded_salt": "3028028A24AC94FF0DB3D424F09D8E32"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "8E1A231A33610A9B3F78D046",
-      "extended_signed_metadata_key_hmac_key": "724CBBC4F7B44408327A62B1EF12BBC451732406FE77B1F7E6F5EB125D47F69D",
-      "extended_signed_section_aes_key": "EADDEDCCE98E6DC2145C48C5648052A8",
-      "extended_unsigned_metadata_key_hmac_key": "9519CC0F24A2DB8D534B9233E863D1105711E79E84DFBE17E13DC3D6C1332D2A",
-      "extended_unsigned_section_aes_key": "80C1210F4F306D881C7ED5568A78D643",
-      "extended_unsigned_section_mic_hmac_key": "590362F05927596F04EEE3633FD526DA7C17FBA4A4489DC1B09F33FA2A84DACC",
-      "key_seed": "D83D79192D3A1490BB2DF4F72686E8E351F5F354BBEDB83B389DBC94D0DECC6F",
-      "legacy_ldt_key": "F6CC44D2FB8A497FDEF4D8505BF8954C1673F7AA74279938FF60D74F2D56E5E590B1EA7E71EEE7A14782FB6E5DDF856402DE2F388BC7A6BE7808C6B02D6AF3CF",
-      "legacy_metadata_key_hmac_key": "8EC62082A556856D8D9622063340E8B7A649CBB82193EE3FF1980DB7918A74C8",
-      "legacy_metadata_nonce": "0AE81376D41C4A66A58C9E15"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "FA406BC38B6852DBA58B830C9669BA49",
+      "v0_identity_token": "D2E0463D56277A2CF9CC5BA5F85B"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "7538",
-      "expanded_salt": "9C0EAC987D64D64324502964CE4260AD"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "9C48",
+      "short_salt_nonce": "DBC275D67654EFB6ECE3CB2A"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "809FF61B6778348B260BFB91DB789913",
-      "legacy_metadata_key": "BBCFBE39DF3BDF239B25E14976A5"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "F1C91B43A17919A92B1A663CD5214050",
+      "derived_salt_nonce": "07D78BAAB0539B291E17C946F94AD5A3",
+      "derived_salt_third_de": "9E1D2E450EAA994518580BE3F95163D4",
+      "section_extended_salt": "98646141DF3AFB3FFBD4DA56C170AC50"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "86485E5604FA2EF6F8EAF42DF4CE0CF1",
-      "derived_salt_first_section_no_de": "5506D594D6DEBDF6AA3FF103EF018765",
-      "derived_salt_first_section_third_de": "DE2468D8B660E2757D9D80315450D573",
-      "section_salt": "C7321D6A2F9877CB0FF99B9B11D34309"
+    "key_seed_hkdf": {
+      "key_seed": "935ED1A8B2E5DA1FF777F1F0245CCE37F8BC20FF306A0A82C3F0F318E953DE3F",
+      "v0_identity_token_hmac_key": "5E33B312FFE3E016F7D8B319B7ED4CA72CA25755D32773210E5B758F75732BAE",
+      "v0_ldt_key": "B9BC506239E428193E0ADAE2705D1C5F3F75E502C45DEB393A07D095C2E1E58616548BE47F73AE0056007FEECF9D19DF78F8CBD0F0B4012BCD1E8CF45FE79506",
+      "v0_metadata_nonce": "F225F715CB7E592925BDFFBE",
+      "v1_metadata_nonce": "C07B2E089AEDCFE7E94CBEC0",
+      "v1_mic_extended_salt_aes_key": "E07FDEA905AAD3D0579B42F50705BFD1",
+      "v1_mic_extended_salt_identity_token_hmac_key": "B016136199E5127F95390815C77AAC735A585A8861CB2805543352971D62951B",
+      "v1_mic_extended_salt_mic_hmac_key": "87FA6DA8F450D40085241D55E17EE0F1FAB9B4C51841663CF13DF7D84A03684A",
+      "v1_mic_short_salt_aes_key": "603CDFF82688718D13120372589636A4",
+      "v1_mic_short_salt_identity_token_hmac_key": "743FE98375D1C4D41B02AA9A636A92837AB4C5A588664CECF00E216ABDA17DBE",
+      "v1_mic_short_salt_mic_hmac_key": "1C67FC0FE2E06FE973DD46D4DB0D72A651376F61C8CEF15002F2FB511C05C303",
+      "v1_signature_identity_token_hmac_key": "58B127F990B55B164990917F1C23B485F3EC847DA2E27E0D541FC6F7AD437BBC",
+      "v1_signature_section_aes_key": "33F90FC36D254B78F3D0A1CCE47CDF2F"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "CEF6",
+      "expanded_salt": "889CF5AB8E18277DE8AB9CD2FEBA35D0"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "D7D49880F809AFFDCF2AB7BF",
-      "extended_signed_metadata_key_hmac_key": "89280461B4D2102D3F829CBBDA32CD75F01579E2B25E8C2F01F3983804A7B5D1",
-      "extended_signed_section_aes_key": "9B42E858D2ED7439F951403533CAF3F0",
-      "extended_unsigned_metadata_key_hmac_key": "509A8BF8096E9A547D14F27284C20D7EAFDA6C9D8D0946EF81D4611DE00F22DC",
-      "extended_unsigned_section_aes_key": "88F058A2EAD74A4BAED5F99136508DAD",
-      "extended_unsigned_section_mic_hmac_key": "3729770DD90FEFA7253F53F87244C928CDE14CF5D2D5513D8EB3C1889C34AAA0",
-      "key_seed": "96B3E4E23374C3162E94B1701F6720E0D0918AAAF10FA0E7FCBC4D7059C824D7",
-      "legacy_ldt_key": "4B0AB9E1C8CDE2F1B55EE0EA4A331B8B923E34AF5DADC5F18971111F45D41BDDC7E9A9F7D28F94AC71F50DFE5AAD7FA7B85884169FE0099175073100CE1DA9C7",
-      "legacy_metadata_key_hmac_key": "5FE8C268D4DFCFCC91F4B7820BA679D26A54C8FF0F3063EB856BE84FF4AED8F9",
-      "legacy_metadata_nonce": "D885F819DB614904DEF77E09"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "59B3F16B05BBE9202E78B12ACBB65F9E",
+      "v0_identity_token": "51B2C41F6AF7AEE74DCE977FFA71"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "3881",
-      "expanded_salt": "839E06D9CAFECE9EB910ECA73E7B7041"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "E664",
+      "short_salt_nonce": "5DE9BC53D47908F83224673A"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "C4ABC2194181D14624AFE1B545E87B95",
-      "legacy_metadata_key": "2F8C7737B5E98216C6358DFD1163"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "4AEE99434AD2D226A375264CBAE0497D",
+      "derived_salt_nonce": "0F201B1E244712F3AE2B7C5DE056DE63",
+      "derived_salt_third_de": "282638440E3212C29F5E8654C6B8B9FA",
+      "section_extended_salt": "1A80A34BAC43864CB5BCFF4547F1B88A"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "A662F512F68A331133401EA5EB2A2A66",
-      "derived_salt_first_section_no_de": "BFE2EA19A0984DEE6BE7EB90DC2FC58E",
-      "derived_salt_first_section_third_de": "92E3744D86B5ED21D50C726A66ED36B5",
-      "section_salt": "55DA08E873B5664E175650BAAA9A8021"
+    "key_seed_hkdf": {
+      "key_seed": "C0459E6013DD6B77DAC1CAEF84BC43BE04C7E171D5725375C5EC7BFD0A37DC68",
+      "v0_identity_token_hmac_key": "97B9B1371244C00038A4E32022D32B3A550BFA6611BACD1D56030D8B2EA37452",
+      "v0_ldt_key": "A23B8AE89920C1ABFC2A0994DDD4E3E8EA83B9FEA70F81461E5D51D5BB372C674E9D3CD59D5273D9D719EDEB749BF6CD1AD077DCC1E969D5E1715F91E23E8310",
+      "v0_metadata_nonce": "8A33A7C387BBCC7505C32AEA",
+      "v1_metadata_nonce": "A4DC2C766352C27F845EFAE5",
+      "v1_mic_extended_salt_aes_key": "90A5BFF37C46AC0B5CFDEB75FE20980F",
+      "v1_mic_extended_salt_identity_token_hmac_key": "99E8E1414155D0B5B79390615E2F9F80B2F519C00944C01C19450B4AFAA90601",
+      "v1_mic_extended_salt_mic_hmac_key": "20C74AAE9D32359A0C9353267458581B4AFE60606060B16060591CB96AC68F97",
+      "v1_mic_short_salt_aes_key": "11381F1734226C3C2911C7D1280E55C9",
+      "v1_mic_short_salt_identity_token_hmac_key": "A4E6D4F2475E98ACB5E95D518DEA87E90B1EB7D9D2F5AA1456C609CD5B5351AF",
+      "v1_mic_short_salt_mic_hmac_key": "AF6150F86F8AC3AE7BA32219E7B92B158E866EF8CEB36BCEDF6DB456CBBC94BD",
+      "v1_signature_identity_token_hmac_key": "6D27EB4BE29E556CE5A41BB89648D91314A73D60FE884CAE79BDC811115CF08B",
+      "v1_signature_section_aes_key": "E5C1080FA68F5EBDD6F3D063BBE0EFD7"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "8E7098BA5E6A64E0F97746AD",
-      "extended_signed_metadata_key_hmac_key": "D8C3614062B4B68DFDEC328EE588C257CB5D8378F33F514181FFBAABF9C18A2C",
-      "extended_signed_section_aes_key": "9215527DA83060DC2F32E39048ECD4FB",
-      "extended_unsigned_metadata_key_hmac_key": "345516B0AFF7928137B9AF940CB13EBC734F9015540C713C0C813AD136BCAA09",
-      "extended_unsigned_section_aes_key": "C563004B3236B38E9C02BC6817B9B199",
-      "extended_unsigned_section_mic_hmac_key": "2F4A9499095C511768D57BEC00A3EC265E9384BC9A12B0F9956791D8617BBE3C",
-      "key_seed": "4678C5DAC152DF06265640453E53177D237E72FD58E1B784C692D95363B3DB30",
-      "legacy_ldt_key": "08D4D021EC845DCE579783FAA377F1B8E20A75CD4768D18DF0326F314A181C17CBB821F0033C970B8A7448E208FA06025A855D25DC38775E44917DC51CF267CE",
-      "legacy_metadata_key_hmac_key": "0D31EE249C7FFA7A827E90EA8C20D70CF22F5DD8DE8BA331A5B05B3831435C36",
-      "legacy_metadata_nonce": "DA5011061FF4017D38381053"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "DAD4",
+      "expanded_salt": "122911634C8C923FDF8CBCAFEEB31E6D"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "7B8A759E2B26F88A93F80FF05C5E5793",
+      "v0_identity_token": "F01DFBEDB49F8E2DF56B2A9FF832"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "7C72",
-      "expanded_salt": "7F599F5A3FA8CA8046C80BD2F8D43377"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "31AB",
+      "short_salt_nonce": "D7A09F5405418C63649804EA"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "77B1D1B130E78CD88DB0FB2D439722A5",
-      "legacy_metadata_key": "0FA76D2A99B2E693B6D358C41D42"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "C94F9B608026089CA2BF81CE363295F7",
+      "derived_salt_nonce": "302455E6682F71E2A813211487A3026B",
+      "derived_salt_third_de": "C289C64BE651B24E7365D79679EFCC46",
+      "section_extended_salt": "BFB748C26DD77BA9F8C82E7572B14608"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "532346C01C3D5C6488233523F1239853",
-      "derived_salt_first_section_no_de": "00FA8A3468172AA618AA22518A04AB90",
-      "derived_salt_first_section_third_de": "1B2BBA5F893D87824BD4D36EB568ADE3",
-      "section_salt": "F3BADB4D93D44B2C83651C4D977BDA85"
+    "key_seed_hkdf": {
+      "key_seed": "FFD07DB5B2DC2ADB24E0EDABBF6089E579704E7935A4D1B185F6AB6F4ED93B71",
+      "v0_identity_token_hmac_key": "263FFE0A653F56E30861B534D9826FA459972D6142542CAE3B67B6CB478EBAF8",
+      "v0_ldt_key": "1D85D884D12A065DD61C71B3BE3818E116346E0C9619B89960ACC88F9B8875544B682771AA2BC5ACAB33F0BFAB45F8449A236FCB9EC64688A5F7F00BB7799C3B",
+      "v0_metadata_nonce": "B8421CB3EE4D1BA5FB006128",
+      "v1_metadata_nonce": "E5CD8BD69CB8D261B5AACA5E",
+      "v1_mic_extended_salt_aes_key": "9C22E81DEA2F0837A050CB2A78907AE5",
+      "v1_mic_extended_salt_identity_token_hmac_key": "6EB2EAE0D24668E4FAAD66C88E59CAF50ACD2F932EB11DB1DD2E5FBF93C04FC5",
+      "v1_mic_extended_salt_mic_hmac_key": "EF9D7AC607E5B37BB21B16409556162C6EBD3DD89144F19365698FB34E06A2B1",
+      "v1_mic_short_salt_aes_key": "1DFE937349A0968CEA77801B8B51E743",
+      "v1_mic_short_salt_identity_token_hmac_key": "9D1C138B75F9D255E62CC8806D36727DF5877CD831CF6CBFA9AA1D631D032F32",
+      "v1_mic_short_salt_mic_hmac_key": "9D1EFDC00D17243D29DF77EAD25DD34EE1E10E420B047D7C147769A1EC149AF7",
+      "v1_signature_identity_token_hmac_key": "E07194FE1E11686C87A325E511C7DB7DABA3B631878CBBC2018B70F5D75B843F",
+      "v1_signature_section_aes_key": "34EF3FADD1EA265EA5DB45B3335862B5"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "D65B",
+      "expanded_salt": "47443B86F1FEAEEEA75EC5C4CCED0A59"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "AC7F3D2E8F0CA67D9E3286FB",
-      "extended_signed_metadata_key_hmac_key": "09F574FCD463AC6C36B8E3444B5C205D4E7F34FEE2C50F4C9D8F4E21F3F08E8D",
-      "extended_signed_section_aes_key": "03929B8C9D55DE8A2DB331F3A458C5A0",
-      "extended_unsigned_metadata_key_hmac_key": "41CB06A6ACA499582A97E506954CF3346E10922C6816893088E920400C15724A",
-      "extended_unsigned_section_aes_key": "8586ADD0A300F594781EC6A91F7525DC",
-      "extended_unsigned_section_mic_hmac_key": "0A104999429CBD6CC254840DEC399C70F9A64F4BD4B9EA572861F44280311D1C",
-      "key_seed": "5EA22F37C2895A5ECBEF49AA18F9BEF14E2221E8AD6B73C48B500059C0920563",
-      "legacy_ldt_key": "2CF083240697097DB14CCE7EC06542F2D8CA93DBF4404BF733A67BE123A7D577A4ACEAA6EAFF9BE5B73FAB9E48AAA93D63779D8FAA7044C24D1DF6E6415540D4",
-      "legacy_metadata_key_hmac_key": "5DF6ED321FFE805FD33A7A17CE8CB8348532798C604F3878892D3A6B1D12E4D0",
-      "legacy_metadata_nonce": "E9872F6F6C5595E82DE87409"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "BF1DA5BA126DF4B125488BBEB78B025F",
+      "v0_identity_token": "A684A924CCF85EAE5A0C7FA404D0"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "2D24",
-      "expanded_salt": "09935DB3B8781070715F440E27D5CD90"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "D4C2",
+      "short_salt_nonce": "14E6E257738D5C3F8EA61A99"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "7B9525E023AE6563A1938D713276A250",
-      "legacy_metadata_key": "E95212B84FBB8BD181401FB77BCE"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "37DF3ED46159C7B50B7648454A8E3F6E",
+      "derived_salt_nonce": "B19C63BCBC2C5466B7CDD1DD160BC813",
+      "derived_salt_third_de": "7A52A770AC31BA84A31ABFA410398E39",
+      "section_extended_salt": "02017844CC8940E3F95FE27ABF392FDA"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "F4A7A1A0A42EE2C34BB44EE7CB7FDF50",
-      "derived_salt_first_section_no_de": "F13440BC0A2B7911039742C57654D2B6",
-      "derived_salt_first_section_third_de": "AE1A8F016C73B5BA485C27D9F21C4379",
-      "section_salt": "B7C7FD8E1C40228B71165C00DF490496"
+    "key_seed_hkdf": {
+      "key_seed": "5D8DED0DBEF3082DF7666EE9C6413D7099C5D57CBCB7572519AD55596351058B",
+      "v0_identity_token_hmac_key": "1787E7BC8CD40D4E2156FDEB481F2D100EBE29515F26D88E7C5B47E4B4CE3398",
+      "v0_ldt_key": "907FE054670C2DDE42021BA1CD61ED49199E6A532044A55A3C842F0761A1A7EE4D9EC192745D3117D05D6BA4199C38435678A38F09FB91A28B80012A2E494299",
+      "v0_metadata_nonce": "149C72A3CFC258042CBC296C",
+      "v1_metadata_nonce": "61D13728AAD32435EA7F2C17",
+      "v1_mic_extended_salt_aes_key": "8F7D7FBB86A7B76027622311BD4FC4FB",
+      "v1_mic_extended_salt_identity_token_hmac_key": "E6D3AF59926AE7CA978F7CE621BBEFC1830CF8E20DB29B4748F97936CBF9DE32",
+      "v1_mic_extended_salt_mic_hmac_key": "23736395522DF94359771CA21DE0EAD1E27AE0833A8C02ED4407DA7BC71BB971",
+      "v1_mic_short_salt_aes_key": "917D6DC510048504D748A1251EAC8BBC",
+      "v1_mic_short_salt_identity_token_hmac_key": "D247C289A7F7C16473E7A8E5F737B2F60F1F281359A2E0E5976CA13E33D0BE48",
+      "v1_mic_short_salt_mic_hmac_key": "D917D8FE9CE653BC8AB3109DB07377B95CD9A0E02028432ECF3B71F933AFB548",
+      "v1_signature_identity_token_hmac_key": "A9E65F92433276507487CCFB1318EA841496C7C1DED9B963D843F56035BF2889",
+      "v1_signature_section_aes_key": "714014241942077D480CBEBA5C7FDA16"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "41D7",
+      "expanded_salt": "D7715923CE771A66E2F806CCB655CE28"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "62E90AEC398397F92486725B",
-      "extended_signed_metadata_key_hmac_key": "512490244C6AFB22B8E1FF0AE15B1BD9BF61B02C20FDF18AFCC28704FAA04B11",
-      "extended_signed_section_aes_key": "C650084EAA3E35460C65A6D5A3BC056F",
-      "extended_unsigned_metadata_key_hmac_key": "969259ED1CC21D9DC198921204BD98F1B60D51E52758EF03EA42A088FC33AA99",
-      "extended_unsigned_section_aes_key": "A12E741FBEE73C341789F4A2E217E568",
-      "extended_unsigned_section_mic_hmac_key": "C11625C0E356394FA499EA6E7606CD7ED016364FC8913C2590CD04E6B7101C33",
-      "key_seed": "9FE70D5318CEACFFBC1C0BF66168E2404D67AC342B36AFAC5306F65AB75D4BD1",
-      "legacy_ldt_key": "07DB2360A6FF579827C73C33947D3432F0D2D44282C3055447C79C732A3DA4CFCA3423D87D080259F2056D9EEF0AD03D874AA6D3767019608486CCCE1CB34CAD",
-      "legacy_metadata_key_hmac_key": "B8EF43182F6E8159601E63E474DA30B6A0A18FAC8D37BA25207395990F75CA70",
-      "legacy_metadata_nonce": "B8E730D17A2B71DFD2113E80"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "3E342AEB8310CBDAB633C0FFC9B0ECB7",
+      "v0_identity_token": "E3D4CE4C1ED84B51283AEED98978"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "7141",
-      "expanded_salt": "9E8C5C44E412C2E5D54FB423AB48102F"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "E61A",
+      "short_salt_nonce": "2B7D6083221050162AD11C2B"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "1FC5EA826AA0C33800219591A2856184",
-      "legacy_metadata_key": "DBEE14EB39323EE9CC4B341C3A00"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "1479B6AE3A8F99CC6A421B7654E82B65",
+      "derived_salt_nonce": "DE3F081731C6B7E3B2155922FCFBA033",
+      "derived_salt_third_de": "B0BF305855ED8EE892A1574BBC35F1CE",
+      "section_extended_salt": "4949EB4D92049664AA78BA61AD0A159F"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "BA4492E8755975045094D06AC8930F50",
-      "derived_salt_first_section_no_de": "20B8815C2C7788BE334D8D945AFD59DC",
-      "derived_salt_first_section_third_de": "2E567F8F25DF8D06C7DF61A05811F7F1",
-      "section_salt": "627BA43CFCB6B9FD6C4676C1102635CF"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "D1FE7EF66BC90F8B53E64538",
-      "extended_signed_metadata_key_hmac_key": "1484C6E96375DD5B4314600D12BCADC5063E1C865D64C8390CFF60E52E6FC0A1",
-      "extended_signed_section_aes_key": "CF4880570B17863BCC0B17D263BD563F",
-      "extended_unsigned_metadata_key_hmac_key": "E3CF564D084FEC04FC96B6CD5A23B288D49F360B326005698B6B1CBD8B8860A9",
-      "extended_unsigned_section_aes_key": "6193B4A238BE3240C7E4D53E7708EE8B",
-      "extended_unsigned_section_mic_hmac_key": "45CC2AFD5BB72392874172AC5C059E871BD6E9C46D977E6D0F7510B817B180B4",
-      "key_seed": "B237A0FC008DF51F15EA3E1789FC834923556B61A7BD30664EA06BE6C40169D2",
-      "legacy_ldt_key": "FEE50CF8496A24EB1A216C0FDBED6CB9559099E8650BEC22C2DEF7ED4E490A9CFF5E21C40572A40AA9F5FC3E9C60F60CA60C257D08155849CADCEE76FC869202",
-      "legacy_metadata_key_hmac_key": "8E854A8DB7B847B84862757EDC73881609F0CF90A99DBD82571C204B040B5D54",
-      "legacy_metadata_nonce": "F58512DCB899E1F857C3394D"
+      "key_seed": "529A341BF82608D457E6225E80308FBA0A33945C52B8C711AE8D114F2719090A",
+      "v0_identity_token_hmac_key": "BDF73B8718898BB90871292D9819BDE09EC1C7039512896A7168500FD62B7E32",
+      "v0_ldt_key": "3A0805FC96D98923886D637A955144BA4018728882E7D8B47059A2D7C87FDC0E5F36536B7C494EE81D0857FF5ABEC9FEA4737ADF10F87B5A93618379B3A33CB6",
+      "v0_metadata_nonce": "2F655577FE85379259570525",
+      "v1_metadata_nonce": "589FA32CE6FF284F20D05590",
+      "v1_mic_extended_salt_aes_key": "E7CE9469B47ABF36531D71015AED7CA7",
+      "v1_mic_extended_salt_identity_token_hmac_key": "37433BA485D9F7F18D29122C494E7049754ABACAF6BEA52CED98716B8AA737B8",
+      "v1_mic_extended_salt_mic_hmac_key": "8A46B4B1543041B78B3EB07A68F4228B439E5AB139DAD332660482C7FE902FB4",
+      "v1_mic_short_salt_aes_key": "C5613A5B5FE8DC12550878B35E58E2DA",
+      "v1_mic_short_salt_identity_token_hmac_key": "A6C66389A16F81596893F988F31B3C3EDC871378DF687B6F92C069FEC3763C56",
+      "v1_mic_short_salt_mic_hmac_key": "A309E0EBD1838289BE98D58062298A7BF81743511FFE526162CD57C1A5DA7CD4",
+      "v1_signature_identity_token_hmac_key": "2727662B707BBB86574B63B085950181C10A66E95E365BD5E4BE2BD42CFFCD23",
+      "v1_signature_section_aes_key": "61539D6CB1E73FC4C5234E1F505EFE83"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "770E",
-      "expanded_salt": "7A1A3782A82F1E40CCBD5330A84750E7"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "C590",
+      "expanded_salt": "9155D976B94C493E5068E87CE6DC2B07"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "EF3AFA04327F12137EC7ED24952AE0C6",
+      "v0_identity_token": "E78783E55CF00CB3C377D47D739D"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "FEC4",
+      "short_salt_nonce": "A58A3AB41FB65068E3B2BE23"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "C8C682033DA60D93756D23338D6427BC",
-      "legacy_metadata_key": "E12C6EE466A1ED61753B30E6BD6C"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "982466B94926A293CAFAD0D8DDB5E2A0",
+      "derived_salt_nonce": "9C14D11F87D3E49D71426882A36B793E",
+      "derived_salt_third_de": "53024AF2F7BEFA7D22D9F057CB75B285",
+      "section_extended_salt": "DCA57C5D334B5501EFA2C6C6CA66AC4E"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "B20A3B57F64EFC41D079734A14A3D576",
-      "derived_salt_first_section_no_de": "09B542C88D08F276B29C0E1A2A424246",
-      "derived_salt_first_section_third_de": "962AC562D09F20F714EB885E729D4C3E",
-      "section_salt": "53F5CD677966C5D1E3EC25D1089BDFF2"
+    "key_seed_hkdf": {
+      "key_seed": "77E25A3C9C6D94331B883F22A24EBE4009485DF2EB38AD4D3AD3EE445035AE92",
+      "v0_identity_token_hmac_key": "814E06DF65361B28EEA39EEEA07163115D3015BCA846A3897604B932A07DE31D",
+      "v0_ldt_key": "0559FC378F7A5013C179B9F39D31D5FF8E2E3DD8885228CFC0867D8661D89D035B8D787CC943CBC72AD6E0AA1E215290757A79E3C46D1CA406868F088022F14C",
+      "v0_metadata_nonce": "A636A4C45168D316F30E5416",
+      "v1_metadata_nonce": "EF9A34E9881450F2E1CE9D69",
+      "v1_mic_extended_salt_aes_key": "20E1180F0D29E99EA3461791BCDA9C57",
+      "v1_mic_extended_salt_identity_token_hmac_key": "76967879840F98B5327C103B6E439BB538F0D219F61EF65C7140C8008ED69F10",
+      "v1_mic_extended_salt_mic_hmac_key": "7B8ACCDD365A02C8E2DC4D3DA7CCE6D9B7F5C4EBD65A6513DC9992B9CBD1BEAA",
+      "v1_mic_short_salt_aes_key": "531F84D2E14CC3266E44ECBD6D6DFD96",
+      "v1_mic_short_salt_identity_token_hmac_key": "03B61A2222358A41D08AFD8F242C1905994D315F544DBE92DE9243EA46F0CDEC",
+      "v1_mic_short_salt_mic_hmac_key": "362E7C04F2606012DE5DC59BB8DD1F3B5F5EFE66F6C9427FABD1E5C8F4D2F76A",
+      "v1_signature_identity_token_hmac_key": "C8F25EC50826357C319A9AE60C043945C9A8013FAC0E2F2C623E22977E9B76C3",
+      "v1_signature_section_aes_key": "E2BDA03CF3781A80500F8ECA73C1A84E"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "A7B3",
+      "expanded_salt": "D041484B84E924130D68F058E9FB45C6"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "BE6106BCD5D7388E53C26ECA",
-      "extended_signed_metadata_key_hmac_key": "0D98105D990B688F91C10BC27885E635B1DBDBF2DAE215EE49E04C06E33DAFEE",
-      "extended_signed_section_aes_key": "7A3A2341DB494F1D5AB346E54EA49729",
-      "extended_unsigned_metadata_key_hmac_key": "C6C0836CC438960704071E2EC71EC18219A7CA336E314C9DF1FD5F5848803C07",
-      "extended_unsigned_section_aes_key": "C00032E500DD04C16176DE5EF45466F9",
-      "extended_unsigned_section_mic_hmac_key": "2B813176336A7A645826F95F3763A536ED2EDC15EF25C87FAE32C3766114323F",
-      "key_seed": "BFF3192023AF42A5D4B3D12DC34350E7F41E214B25501A7FE0ADD19497A48276",
-      "legacy_ldt_key": "37E2DFCFDEFECB1FC486B6B26670AA37D3BE7E106E0C2CB5AFD9CCB1130EE7899A3D38CAF15142CD3A06DFC2A39D34503F7826EE517F9232DCC7CB1931855028",
-      "legacy_metadata_key_hmac_key": "5771D06F65AFD121908E4750EF2EBA0E9C385839E6987F30161FBCF88F49A974",
-      "legacy_metadata_nonce": "20448FEFB69E2643D7932CAA"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "C3B32F82777B1C5EB860364BA7248ED1",
+      "v0_identity_token": "B809A4E6EFB05572D6474DBFF0E2"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "524B",
-      "expanded_salt": "F0B23D5EB99A02DF9E8A7D6F8B8A1A3D"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "BCC4",
+      "short_salt_nonce": "C5FBF8210A55A6176527E35A"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "C6B9DB92F2818C6B57A3E386BD44B236",
-      "legacy_metadata_key": "A4C003398DEF995E0CF5457DF3A0"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "4F8C73FA3C662FC6516B4E7D3333B663",
+      "derived_salt_nonce": "01D90834B89356931A7C575478271A43",
+      "derived_salt_third_de": "DF71CC3039A2C9EBE8792C639573245B",
+      "section_extended_salt": "E667569C7C35342D78598CE967DDD6EF"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "EB8E676E9E53A4828200A25942EBA2CF",
-      "derived_salt_first_section_no_de": "A342A8CFFBD87DA2F95D1C72CB0EEA36",
-      "derived_salt_first_section_third_de": "6F049F5664239C49D76286B8218D64B1",
-      "section_salt": "8096BBCFC6AE0C6CA7A17D5623929518"
+    "key_seed_hkdf": {
+      "key_seed": "629137005F03D848FBC9A663FD4545698B82FC1A2A894DF9C41541A1B1B5B3D4",
+      "v0_identity_token_hmac_key": "46F120A4DF532721806D5D778D2C9E9178BD14D80F458D24001DCA4A22401CAC",
+      "v0_ldt_key": "07BF125D6472987365CF8DE0A31DAE3465F2005BED97016523AD98141CF4D4F68F6E5C193CE86F391474C6597827C371656D4339266E84202A174C5402C49386",
+      "v0_metadata_nonce": "ABF5B2E7D958BFF79882AEB8",
+      "v1_metadata_nonce": "86216A34350E9C1E045AD632",
+      "v1_mic_extended_salt_aes_key": "18B12EBCF7ECB1DFC20C7867D9B6207A",
+      "v1_mic_extended_salt_identity_token_hmac_key": "468A41138CBC76A60F8E1D472BB33152A67B9EDA3B7E79B9EADA3A3720FB3C8D",
+      "v1_mic_extended_salt_mic_hmac_key": "D980A878C750F1AE2C25A21C6045C8EECD81A0B45F400F10EEFB4222DCAE79BC",
+      "v1_mic_short_salt_aes_key": "EC807E84AF935FDCF6210EAFC261FC8F",
+      "v1_mic_short_salt_identity_token_hmac_key": "3683F211CF4FF6728E509372C732404FFD7A49EB6E2A9E011D8A2730A2766488",
+      "v1_mic_short_salt_mic_hmac_key": "ED63CB5202AEA05D6A666A9CF66AA76A0D10E6803A7F0A13C6303FC5D149B326",
+      "v1_signature_identity_token_hmac_key": "B1CB9EEB37699A13A225BDF36102A6E80610AA9A97B8A6B320E772FFDC6B5972",
+      "v1_signature_section_aes_key": "58C40319B83E496188B699111FB9A659"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "B8C1",
+      "expanded_salt": "1E42061F10934A6380306B3CE306CB35"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "586425C6E1CAAE439F588912",
-      "extended_signed_metadata_key_hmac_key": "FD0CA08040BB854BDF7414DA7ECF4733A094644B4FE3473C04F787DBEF6E53E1",
-      "extended_signed_section_aes_key": "E4FA3E811B0C7263CCE2CD60500587A4",
-      "extended_unsigned_metadata_key_hmac_key": "6E6DA7DC5B6F17C7B6C8B37E9B8E82D09DA5C4300DB8C601CDBB258161A8701C",
-      "extended_unsigned_section_aes_key": "3E0E93D5EA50FC6420B0331F2D30EE07",
-      "extended_unsigned_section_mic_hmac_key": "78F44EA95AED2D816672A0EB7BC16B8DA13DC285CC903E63BB844BF6499949D4",
-      "key_seed": "66E4BFB8633C10BDD7B8C2E5653E014BC1E652D809487B1810DBF79515ED66F9",
-      "legacy_ldt_key": "D53EDC7BDDEF9CD6FFA5C7DCE7FCFC6A4653412B7F690454201BB6536FCD2EB04A090D3C252375FD703D87869AF7F6CC60EB7F381896396662C472C5B1626A81",
-      "legacy_metadata_key_hmac_key": "466B32DC5CCF02949C092E5AA5AB321CA35E339CA8F6483FB1F9F6F0C7E6A438",
-      "legacy_metadata_nonce": "BFF9144266F9BC3527CB8C46"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "5E342014C14C891E73C9AE86FD46D131",
+      "v0_identity_token": "AE279658E230EA4A84493249656C"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "CCB6",
-      "expanded_salt": "0F9B6162C8CDBC3092573D85B26DBC37"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "3780",
+      "short_salt_nonce": "1BEE1C6648B348133DCF4E1F"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "AEB39B7A005D27F8DA9FAB9AD80D220E",
-      "legacy_metadata_key": "A52FF0A8837236A5D3DDA03BA23E"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "E4EC66F0E365A49CAC1266A28F307798",
+      "derived_salt_nonce": "7478C715C11452249E821EA57A9832A4",
+      "derived_salt_third_de": "1A1F76CB9C0358FB86E217487B860855",
+      "section_extended_salt": "3E351E1A8DEEF7C30011566EF72CCF33"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "FC6812BA03F06BEFD59BF0F949D8F449",
-      "derived_salt_first_section_no_de": "2DCB709ACE9617A6119BD2CC960EED9E",
-      "derived_salt_first_section_third_de": "FE06376C4AE96023D51C6C8475072558",
-      "section_salt": "2F808BAF3FE99E6E84B565038CA90BC9"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "D8C89D6886078AF21EE678DB",
-      "extended_signed_metadata_key_hmac_key": "FCE0422748BD4D4082B66F25B06DD3056E9D37C135CF212CE1C8631FEA94F864",
-      "extended_signed_section_aes_key": "9EB35E1816D04630AECA453BC64A8FE8",
-      "extended_unsigned_metadata_key_hmac_key": "D161E19CB3638E925E028602B7928D6889C776062DF66A33C97F2C45890C0581",
-      "extended_unsigned_section_aes_key": "A7AB3B735821FB8F59F63A8E8DE3F3F8",
-      "extended_unsigned_section_mic_hmac_key": "CEAD2B2095603CC58A1C5DDE0A5ECBC2FC960840DE6A463E9893C97409B0437A",
-      "key_seed": "3538A99DC75533C5A301539C166BD8A6476AF6F5F52A73382441681781283A4B",
-      "legacy_ldt_key": "32360EC7E4FE0B3C5FE27E236639D62C5C81BEE8397B320688087B4D4009B10E6C15B6057CCE1BC1B8F8732606E730BA172746D8CBD34DEECF911CBFB9712B15",
-      "legacy_metadata_key_hmac_key": "90A819E29083F6DF7FD2C7E9EC4A872CF4887CA8D964984E49C4CCC292EB8938",
-      "legacy_metadata_nonce": "0982939640FCC0CEE8551409"
+      "key_seed": "AB3AA3128FFAD716B58E523B678189AA0FC39C9D84F627EBF5D1A742E6B35918",
+      "v0_identity_token_hmac_key": "C24032686F78556724A111E743306F7B583E9369B80AF08B1A680A04FF46B3D4",
+      "v0_ldt_key": "65DD7D59DBF3F853296D96F35BF0C350740D0223DD2629A952FCC2B5349B49A3D5E6D41EC120AFDA492F331FB573F122930A98F506B1F9E34AB7D1F38F6E9461",
+      "v0_metadata_nonce": "9432AAEA580486B80C2CEE67",
+      "v1_metadata_nonce": "9682BC001611E3C0271E96CC",
+      "v1_mic_extended_salt_aes_key": "AA2EC2476363A9127ADF96E354A3694B",
+      "v1_mic_extended_salt_identity_token_hmac_key": "05BE6AB4AD1FCC92C52883DB0FFD4369D472910C0E8417C551AB834962059472",
+      "v1_mic_extended_salt_mic_hmac_key": "378010DBE79B40985A0AEA35F7BDF9791E87ECEE34DB04D82084DEFA57ADB601",
+      "v1_mic_short_salt_aes_key": "15EAD484945F798DA57C65107009E5CA",
+      "v1_mic_short_salt_identity_token_hmac_key": "8B9F442893494B0D663B669AEEB2DB66D489B58FACDCFAE8D583019C16E72BEC",
+      "v1_mic_short_salt_mic_hmac_key": "B4EE0EBCBE390C849D864EFB7FBAEAC67932631186C729F76F3CA91BBC7680E9",
+      "v1_signature_identity_token_hmac_key": "A1673B9103B2253E455B8AAA8C2AE84DDD02BFEA7D69E68FE5D120E0B5E57B80",
+      "v1_signature_section_aes_key": "F7CABAA4EBF642DB0BCFA3C0C3EEA99B"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "7975",
-      "expanded_salt": "AE096CF053C4F4A17ADB41DF4AF79D01"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "B20E",
+      "expanded_salt": "0E708DF23FF58C2D7F3F2D38017496BE"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "23472C27E2F659A4AB1A428AF3018C91",
+      "v0_identity_token": "9F3043710CA07FA18EB47719FEAF"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "1FEA",
+      "short_salt_nonce": "9DFE98ABABD5693077D90FFF"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "EA6B4A31500F98886521EBCEC6ECAAC5",
-      "legacy_metadata_key": "70A44BF295A6510C039A031BCB04"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "013B34285047C393204A70E0056B0308",
+      "derived_salt_nonce": "BF792D32CBA8F57028F7EB5BF225CB6F",
+      "derived_salt_third_de": "208228C6462B7671161768D2DE2FF375",
+      "section_extended_salt": "973ACE22CDA6F8F49B74ED9878CF3437"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "0818451F633193982BFEBBD61988FF62",
-      "derived_salt_first_section_no_de": "48ED1DF2C5BBF94DB0DADE30C0BE21C6",
-      "derived_salt_first_section_third_de": "633611AFE0697A6F36BC5B76E685391E",
-      "section_salt": "AAFCF56E0A0F6A8F76E2BC44E54F2B78"
+    "key_seed_hkdf": {
+      "key_seed": "96DC23CC175FA4AD9D019AD2697C766174387A431AAC413879F1E58A990EDF3F",
+      "v0_identity_token_hmac_key": "B816783CD9584B43744F9090F8190EFB73C065D80C55D42C5B0FB3E703BF3A3B",
+      "v0_ldt_key": "9A9E7488F70951A6BB0E9E73D4671A1B12B0C5EBB1A59D81258FE4FE80987B67AF2679B36873998CD5236433AA9FB12EFC52E66BC013B86583255FBD10C5C7D2",
+      "v0_metadata_nonce": "1ABAE096CF985C4732EE035D",
+      "v1_metadata_nonce": "7E7DB680E724225C9D1F1C26",
+      "v1_mic_extended_salt_aes_key": "A8C561D529E333D9B72002E0BEE43630",
+      "v1_mic_extended_salt_identity_token_hmac_key": "F45A244E0C64DF9AFF0CDACC2871DA35EE197C565CB47314A2158E944E0DBFAA",
+      "v1_mic_extended_salt_mic_hmac_key": "43950758403417002A184DDBC4991FFC5F0B2BC9CC8D2302784D6B59163546BF",
+      "v1_mic_short_salt_aes_key": "DC18AEDB7EF362A00A8A050D44860877",
+      "v1_mic_short_salt_identity_token_hmac_key": "CFB49BD705D54AC75EC449E1BC2D84B405FA970C728BCB004884DB70AB4E9C0D",
+      "v1_mic_short_salt_mic_hmac_key": "2D9C3E637E1E387EDDD22E586AC029A5FE0FE380502E1F9B1070869806EDA0F9",
+      "v1_signature_identity_token_hmac_key": "D8AC46D57D8F97D333705D9E37582923CC64EB9269692E044CB6F42B6EC7A1D3",
+      "v1_signature_section_aes_key": "3408666AFBFBA0F7C6B1956FF696A7BE"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "697D",
+      "expanded_salt": "87DD6D19A23B02C55F35861FD900852E"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "30696C2FFD88091165B93256",
-      "extended_signed_metadata_key_hmac_key": "346B7958CA1259BB7197FDCA84B718A41AF9049D2DE6B472B64B19102D4240CF",
-      "extended_signed_section_aes_key": "854ADA9753D9A82E94E5D6ADBB22873E",
-      "extended_unsigned_metadata_key_hmac_key": "29D905607B3ABF4E2CD261EE4E67C19EF36F41311A61CAADB60C95FC468FB72D",
-      "extended_unsigned_section_aes_key": "DD5329454006EC3BD37FE3520810CD98",
-      "extended_unsigned_section_mic_hmac_key": "E4077D45CDDD895F5577A11D2FE9CB3BE65242D5DFEDAC87471C76038605741C",
-      "key_seed": "3719ACB4884750708757D20A58648804B8B7A614C176AEF7DFA6E0A5BB9CD5A0",
-      "legacy_ldt_key": "88CAEB0E79177042908D16AF2ED4025D95448A76E901A4B6195F405E5210A6889324B3DB121508D411A012499EDAEFF35A280BFCD32B35B2DC773AAFFAC9DC35",
-      "legacy_metadata_key_hmac_key": "A358646C1D338EC123E72FBCAF4BF615844764F91F8F2BD9811CDFCEF5FC2548",
-      "legacy_metadata_nonce": "EB2B57119D7662AFA40BC448"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "B893C7D62C8D5D6C4D27F2F2E89A9388",
+      "v0_identity_token": "1173E9D7D4E23D4FA3AE5FED5C4B"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "7BC6",
-      "expanded_salt": "F81E9EC1C91B1007FF2A32D72FB6483C"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "8D74",
+      "short_salt_nonce": "A07118DCA162260AE3431760"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "36BB3E66C0820FC418EB2F1B873BDDA5",
-      "legacy_metadata_key": "89981C312BA536E82EC3B1090CA5"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "E64BA2437291F00276F06A9686145AA7",
+      "derived_salt_nonce": "0CAC865313B2A15A5080ADFB9217E5C8",
+      "derived_salt_third_de": "6FD2414155FFA0177A9F6417B607C3FD",
+      "section_extended_salt": "AC9AD659EA00A4265C647D5B46D4EB70"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "218AB6D163ADEB31634B1DED52E56326",
-      "derived_salt_first_section_no_de": "0F4F42636DBC8ADE1DF3383E6E8C80F1",
-      "derived_salt_first_section_third_de": "B667CEF8B176732FD56AFE83BFAA7611",
-      "section_salt": "D884E95FCD3A29F4E951A3C64D43C020"
+    "key_seed_hkdf": {
+      "key_seed": "1658D40CA1FAC8A4C15A8EE96A25434C631C2E3D2D710C74DBEDFF18D12C08A7",
+      "v0_identity_token_hmac_key": "396915CF17C3C5A9369928620FC934DF1A64641D9C46449026D74E47E3AF559B",
+      "v0_ldt_key": "6214EFCAA3542FEA0E81A3A5BFBF077E1CF90222DAB3DA4826218DDF8A41A28A2F09C2D2F75E50C24C0DD402BDB343D4B647A94819D3455A69D5B613EE36B90E",
+      "v0_metadata_nonce": "0DAB2F8A9B3C8741A7B7B27C",
+      "v1_metadata_nonce": "C22D7E7354F6A505678AA3B7",
+      "v1_mic_extended_salt_aes_key": "C6A465F79147E737D969FC5873DBF96B",
+      "v1_mic_extended_salt_identity_token_hmac_key": "32379AE42B10574F8D7934F0D75E7CB0EC82A6C7E5F626BD8DE737C3F8964F8A",
+      "v1_mic_extended_salt_mic_hmac_key": "9B25D3914B0CDDBDF5B73F2F95BEDBCFAAE65289D6649696D91C0BCC8926B619",
+      "v1_mic_short_salt_aes_key": "39C321D60F6E9871C6F0E5F08021894B",
+      "v1_mic_short_salt_identity_token_hmac_key": "67F7D65051D4339DCFD118B55233214FDF6A4D22104C123490267C0A68029A37",
+      "v1_mic_short_salt_mic_hmac_key": "97B7E56E79C9F0E05ADE70F6E85B0104B08150A1BB253451FEE37A02AB2F607E",
+      "v1_signature_identity_token_hmac_key": "019A2FF53B587F6B45139C744973D4BE7F6BAD418625181AC3F0404CE042F125",
+      "v1_signature_section_aes_key": "FBAF3E389EB39C82A87E36BF26459FEE"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "C489",
+      "expanded_salt": "830448B54A2F8FE3CF891DC67A61FE6C"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "497081ADD0EAAD948AC3D743",
-      "extended_signed_metadata_key_hmac_key": "4CF4E05DA1B0EF538CA84515202E1AFC845E94A236C672F091D7998A1AFD0D24",
-      "extended_signed_section_aes_key": "7DF8CEF6BFDA7D1944BC5F5105BD003D",
-      "extended_unsigned_metadata_key_hmac_key": "4892A9FD8A1080F06D299D8DF2FBD1382E5F31E32FDE1B974ED1958AD7348C08",
-      "extended_unsigned_section_aes_key": "608194400C0D4390A12891645FEE4EA1",
-      "extended_unsigned_section_mic_hmac_key": "8B3B932C61B0B9E2FF883B13791EAF9EBBE382E4539242DEEFAF03C6930FEE9C",
-      "key_seed": "871B0FD2D15F8C5EA49B610C544D0F957884876C614228083A414F8A441A2D30",
-      "legacy_ldt_key": "D41A6CF8C5BB99BB8FE8B33F341FAD33AF603E980B6C2FF70B6F0BC0080F655141D0421932F573706664085752947678D4F106E1FA71E8E584445BCE7ABBA35D",
-      "legacy_metadata_key_hmac_key": "99D2C4B908B8FF9151F30A4B7ADD9590BF85EBD4D086560B32A20AFB529CC524",
-      "legacy_metadata_nonce": "60B88352445ED074EA447690"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "C97A235E30AD867BD583C5465BAE98C6",
+      "v0_identity_token": "C9F646361843A0AA277C1786D452"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "BFDF",
-      "expanded_salt": "AFDBB3A928B41B9C72F3CC9C21437B63"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "0043",
+      "short_salt_nonce": "5E4D5C6D9A148F2E2695D39F"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "4F832379A70733EB481C98C287573D51",
-      "legacy_metadata_key": "9C8893A6DB94328439157550279E"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "EC8C3C7B3A563015AD914D065F065C42",
+      "derived_salt_nonce": "40842C94F4F5046882D39A1F353F5174",
+      "derived_salt_third_de": "CF99D9895D39ED5AEB35E4596C63B4FE",
+      "section_extended_salt": "FC4A1004C457DF7D85788332C3ABF9FE"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "1F3AC1292A3E34943F65ACBD5A5D5F71",
-      "derived_salt_first_section_no_de": "6162875F35A6B9B20C0F81C8CC9BC0D6",
-      "derived_salt_first_section_third_de": "EF5A3849BB1526EFEF254214AE714CD9",
-      "section_salt": "4058BBC28C1E874801AB570EAA0B1EB6"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "213546D6654AD76E62DDABF6",
-      "extended_signed_metadata_key_hmac_key": "359FCC4FB2576B773E11B61937E190FD0D9249EFE348AAABF18EEB331373BF66",
-      "extended_signed_section_aes_key": "48FEA76F4BA226E1880B3D8245125AEE",
-      "extended_unsigned_metadata_key_hmac_key": "59118D794BC59C9D1F4A6008E3AFB9FDBBC9F874C1F6C25812ADB75AD7744314",
-      "extended_unsigned_section_aes_key": "F55C149FB252D0744AAC781C04E87251",
-      "extended_unsigned_section_mic_hmac_key": "6A6C6DD45298CE923C01E0CCE68519E1C6E2C494F0E9476E596539DCB30423FA",
-      "key_seed": "7B65E45190EA159343E5562D63AE83ECF4B15DA7506706FBFAE25DC607D88720",
-      "legacy_ldt_key": "7801EA9A9FE16C5554116B72463EF63DE1565CA8AAF6DC1025FE6AF53C4B798AB15609774385FC25FDDDC4D28E57A2661ABDD074638A10C7C9CD7E852CCBB9A8",
-      "legacy_metadata_key_hmac_key": "3787DCAE0CC30E049935477D8562D380B9DB021BA494FD1ADD9925DBC8864A85",
-      "legacy_metadata_nonce": "902CCFD130ACF873A78F9481"
+      "key_seed": "8F5CACEFF8E195DC29A6418D15E42C22D93BC3893EC740FBE9DF3BC36A6E87C5",
+      "v0_identity_token_hmac_key": "B74E9A4B1A1967E5F37362FA6109362D3A93F2EB4D4488D938EBEAA0519B82F7",
+      "v0_ldt_key": "2961475C355C92A682E1AD7A0B545BA5F91B8D1C97396C77DFFE2CEFB7DFC564A8816469472B28611021D03424545C54FD68C87BB3513EB5B1E7B96614965D25",
+      "v0_metadata_nonce": "060B128C71924E5F35403978",
+      "v1_metadata_nonce": "0CAC86A9E31724738CB16769",
+      "v1_mic_extended_salt_aes_key": "A16666440709AF7D91C3FC11C0F40BF5",
+      "v1_mic_extended_salt_identity_token_hmac_key": "B54A57DC9F832214FB383DA4123ED089B16C28480D4097DC57F108272C0E53E4",
+      "v1_mic_extended_salt_mic_hmac_key": "73766408361E3A670635EEB7C9FF70A72116819F94576737C06FE78910063502",
+      "v1_mic_short_salt_aes_key": "0C8A3C2D081449F4F263E11730F59385",
+      "v1_mic_short_salt_identity_token_hmac_key": "E46EEBE36262EF589672F67E20DBBE24DC18B863DDF4CF9D8330DCFA3CFAA3B2",
+      "v1_mic_short_salt_mic_hmac_key": "5CE78E09F37386D4554DFC39B22E4AAF28F812D335BD372583B7C3F1A1552B9C",
+      "v1_signature_identity_token_hmac_key": "B947D986D221E4C06CF27FD3352674D5DE51FF5B14885E36074D363B286B28F1",
+      "v1_signature_section_aes_key": "49A0920DE44EA33D0E2FDD6217492837"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "9A70",
-      "expanded_salt": "4ECBEE3BBC2B85F39E379254D7163227"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "28F7",
+      "expanded_salt": "6CF0800012002D2E48A3A5A8AC857378"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "AD4B09305929D4A277BBB2D3227B54BF",
-      "legacy_metadata_key": "ACA208D4C92030B19114C73F8399"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "A0BE87B5DEAEC03D93A1BC9C10DD28AA",
+      "v0_identity_token": "412DACDC38349059C26766C5F8AA"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "29B9",
+      "short_salt_nonce": "17F243E45941156F9CB40E66"
+    },
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "A2D63A55BABCFB0F636B0D3621C4CF0E",
+      "derived_salt_nonce": "7DA7DEB96787A854B3B02CD1D3D90BBC",
+      "derived_salt_third_de": "4A653154FF6C376DD0EB5CE64FEA70A1",
+      "section_extended_salt": "8B2303C4EE99348E3B5B34EAF99E29E6"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "34BCFCB74C32AAE99B97D24A839C4533",
-      "derived_salt_first_section_no_de": "C6C751B4257933CCE05AD7C336B3F17B",
-      "derived_salt_first_section_third_de": "292892273B46FB6C99FA4BC3C44708BD",
-      "section_salt": "74F5AC8F69F601C07AB4AC92E9F51C3F"
+    "key_seed_hkdf": {
+      "key_seed": "CB59F22A81FE55D0C5BC1B5D22D4212A811CD1138477D21FEBE17C5E217C87A5",
+      "v0_identity_token_hmac_key": "3BE67C6ED8D821E5C74A7E68A7C5B570362975613E1F974DB855546D0151E3D6",
+      "v0_ldt_key": "9F6C8A4BCB91447656C00ECAEFF72A01A2EA4B66450A3F70B0D1FBBFE70ADCCB4F69D8DF956F44CA877515D21ECADCDB425DFEBF5A3D7025F1D7603E05750A29",
+      "v0_metadata_nonce": "D7E7D92A0A0A19575DAAFB88",
+      "v1_metadata_nonce": "B51766263DA243F02A43D0D5",
+      "v1_mic_extended_salt_aes_key": "37DE33735A25248A6557BF1A68031161",
+      "v1_mic_extended_salt_identity_token_hmac_key": "1AF94FDE53770179094074B778B0F0F9C4F093C33FBE149C6D597CB20762B36E",
+      "v1_mic_extended_salt_mic_hmac_key": "CAB44B647CEF4A189CF3B89428DFCF4D8D7EF74AA05252D4E2D1473472B17E29",
+      "v1_mic_short_salt_aes_key": "480B8E97EC00901A06850E0746BADF25",
+      "v1_mic_short_salt_identity_token_hmac_key": "6CDF2DF6C49DDB32051C172679F731F040549742B92758445712E5380DB052EE",
+      "v1_mic_short_salt_mic_hmac_key": "751622D2D36222D292DB1FA835FAC4676610C27FE961AC28B6F19EDE4FE6D233",
+      "v1_signature_identity_token_hmac_key": "DB49365A1148EFA93B856ADE848B15DC81143FFED26E29811ED4B400D3C3D880",
+      "v1_signature_section_aes_key": "8376773D764F42C0C02209207DE4AE80"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "3564E475886853F7BF5EC55A",
-      "extended_signed_metadata_key_hmac_key": "35A5FF3F932BDC655A9FD8AAEA36E25ED917C509A42F3337E3E3E89F2DDBCD0D",
-      "extended_signed_section_aes_key": "470C528EF53B704C515C58A17EDF6C86",
-      "extended_unsigned_metadata_key_hmac_key": "4144AFA9B5D527A9D1E05AA282A7C1D00DF51E79CFE93D7578043A5191C8D1A7",
-      "extended_unsigned_section_aes_key": "C32F8F9706900F0FCFEE149D120CAAB5",
-      "extended_unsigned_section_mic_hmac_key": "7D1D483580DA73C3322F088E3D2A24FE640A9F818CBD72842D288364A1937286",
-      "key_seed": "A8590C79753059E9873CB4F0BEEECD1B43071D76BDD4A1E07809FFA0B89AD5E6",
-      "legacy_ldt_key": "82D25B8271ABE1D6262F09DA005D99BB6046EC6BB387800B4E2E01993998867FBBD487C65E0BCDCECBDD89E1EA23ACDE697862E2D9B644963625BE8BAC4E3F99",
-      "legacy_metadata_key_hmac_key": "C574697B87A56EA447BD202BFD3D960E2393119A5AA4BD270CEB0E37B719BF16",
-      "legacy_metadata_nonce": "1CFD6A666E8F405C39D9E1AA"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "FF69",
+      "expanded_salt": "C45C95D99B373A3ACE8CD7D1889F0013"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "47F9BAB879C0FA2AA98428A7FDDC7463",
+      "v0_identity_token": "CD1F7F15A691D8D7C30C42CAF69C"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "5685",
-      "expanded_salt": "8AF7ACB0C156A5882E64105535A8521F"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "C115",
+      "short_salt_nonce": "78A4DF6F4645BF6868D3B8C0"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "9B9FEE02950DC453BFB4EF3C1CCF4811",
-      "legacy_metadata_key": "B2C2A05B519CF20021C9996DF808"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "557438A8D86570CB0179CC69F8B1653F",
+      "derived_salt_nonce": "4BB982A676DCF6715DBA55A5DA00D25A",
+      "derived_salt_third_de": "F250FD05CF2307636F60750CE5E9B323",
+      "section_extended_salt": "941E0C341EB666240EDAAD47239DABD6"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "89CF340356F2E40A23A033F98DDF7D28",
-      "derived_salt_first_section_no_de": "853D5F3E410E67A666A9E41F86BD4EFD",
-      "derived_salt_first_section_third_de": "DE0B861151059E218B754C32B123C147",
-      "section_salt": "226836AA61CC797529A81307D64EA301"
+    "key_seed_hkdf": {
+      "key_seed": "ED908C5A4DCEE6429570F2DF17607E087EAD24F213D1A11EBBE62128AD2CBE07",
+      "v0_identity_token_hmac_key": "6C7B5EBBDD2DA8F1C411A367D3049C91B8C3B1CA6BEE58BD5979CC4A7AD3CC55",
+      "v0_ldt_key": "AF41A2C1CF19A7036CBDD5BE73C1F4C6D035A3FD205588931348B680B8826EE4A9915E6697DB8F4EDE0751DD746CD52A4A1A3C682B515A736F8407717A0F8ECA",
+      "v0_metadata_nonce": "AC353D32F3A9AA176DDA755C",
+      "v1_metadata_nonce": "F5D1D9471B14EA7BF02C3E61",
+      "v1_mic_extended_salt_aes_key": "9094F93C7C363EC83C62938E55F34A45",
+      "v1_mic_extended_salt_identity_token_hmac_key": "CBC3A7ED9BD82335329FC83EAEBC6C57A17D41A94C9E57F5C53EAFA29E3E6FBB",
+      "v1_mic_extended_salt_mic_hmac_key": "C8C2EBA26F834A9E3C96C2B7E4F697D1A180FC8F854A6169710120DB29BF0C0F",
+      "v1_mic_short_salt_aes_key": "CA6577851C9BC850A88F485732FB4C52",
+      "v1_mic_short_salt_identity_token_hmac_key": "EB2B6628523226BE853EC1A2A23C8B8F210B1C4B40EDA72435F65BFEE3AA066D",
+      "v1_mic_short_salt_mic_hmac_key": "D17C34BC36306CB1AEC52A7125B0E7D6C9A50F4B9DAC6015BBB9D6E933E7A2DF",
+      "v1_signature_identity_token_hmac_key": "1527905BF57561A93D2EE2509172BFB08BEB8D1456D17B0227C3EC621766B850",
+      "v1_signature_section_aes_key": "4EACAFCBAEC4739EC7726C78BC27BEA1"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "285561F74E853C3208B51748",
-      "extended_signed_metadata_key_hmac_key": "32C03F14E9AA9082E4A87375B53543103A6B8B041922778BFA9C083D5E1F39B2",
-      "extended_signed_section_aes_key": "A0BE8DF66519E26A80CA4C1D84958150",
-      "extended_unsigned_metadata_key_hmac_key": "98CB9A09F93AB43D229C1D1699E998BB21DCC535128B4F3EBA93939CB2806199",
-      "extended_unsigned_section_aes_key": "C579BE805B3801B4680EC72CF6F4C888",
-      "extended_unsigned_section_mic_hmac_key": "DAE01D0E3D8A08A8B3BB00A48BC81E8C6B730133EC90A03326189F2E43A1B0A2",
-      "key_seed": "82B0C4B354909A4A05C43CFD728804FD58C9A339362576865378D1B92A3DC3FF",
-      "legacy_ldt_key": "A939DFBDC50FBF27637C0B2AE4357D4403A5E3956373BD17CB8C589657517E79D1EB900F634E3F41092E4E25787C6161D2D18EE09A13A35A354A449AB8292BCC",
-      "legacy_metadata_key_hmac_key": "4D32558AF034F2AC2E63AB36256B842852598D49ADC8E2F244C8AC13DA1E9D04",
-      "legacy_metadata_nonce": "F00120FCD5FFEC079D2CA173"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "581F",
+      "expanded_salt": "A5DD61D8ECA1EBCE4C322C8E080C9AE8"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "8EEA2717A9052AFE66226349118182DA",
+      "v0_identity_token": "9675B523AB5F81A7ABF9903D9C7D"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "6C58",
-      "expanded_salt": "92EC631F08ABD78DC8552A2484D568F2"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "BA45",
+      "short_salt_nonce": "BDAD50E1174C4F1A4011335B"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "04443FC782143B5B62BFFB6F4E0EFE8C",
-      "legacy_metadata_key": "F011C17EDC3FB4095C9ED4BFCEF6"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "C92C5055A19F1B8A0FD1F07705B454DA",
+      "derived_salt_nonce": "C2603BFA63695C6D3CF9F8C294217640",
+      "derived_salt_third_de": "C3509C601D0F373B0875B6B1F7FC7D9D",
+      "section_extended_salt": "782DBF528A022F527A00408A045CEDE2"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "7104EDAF3D39ACA85F0DE66FB8A0E6A4",
-      "derived_salt_first_section_no_de": "297FC2DCC22C9793610FD3504DBAAA3B",
-      "derived_salt_first_section_third_de": "CF455558C8C67E5AB573C79AC356B02D",
-      "section_salt": "19421ECBB444B94A369F1684DA6E87F6"
+    "key_seed_hkdf": {
+      "key_seed": "3F7B6F2BBEA10535F2EB1830A466A58D1976838B2F56929C5632DB1D35DB9FA5",
+      "v0_identity_token_hmac_key": "84CAA04B2BEACAC2481CB59E58F2F8D2750130791DB02F906ABBE8AA778F8DF1",
+      "v0_ldt_key": "EFBA37A2FD32D2AD2C89489B2ADD958DC689B71CB85FDC132708BEAA5E450FD34F585CF0C40386715ECC9B1F334EABB5C6E9D071A0FD98DD752170F898DB0DCC",
+      "v0_metadata_nonce": "FAAAE5DFA198AF0DB586D4F0",
+      "v1_metadata_nonce": "1200358F9DF02A61AD1B3720",
+      "v1_mic_extended_salt_aes_key": "444C1136B55AFB20194F8485CDBAB199",
+      "v1_mic_extended_salt_identity_token_hmac_key": "DADAC9D5D6272025A43765625864D677B563A0EC550ACBF1D42D1F1C89C3ACF4",
+      "v1_mic_extended_salt_mic_hmac_key": "15F1121625072AE546BE7E9A17CAF4C7D85F084CB0BEA894495F1CC37E97E60F",
+      "v1_mic_short_salt_aes_key": "6452C325F1DF8A9E0D4B6D4D9EA59A7C",
+      "v1_mic_short_salt_identity_token_hmac_key": "4397BF6738AC22CFF3C4272C046F488139107476E2566FE66C789E240A624803",
+      "v1_mic_short_salt_mic_hmac_key": "A7FD1087175D5FD000D3FB744CA35120E232C54DC167B776F42B9EB5A8453079",
+      "v1_signature_identity_token_hmac_key": "368BFF1160B108CE21133C215812D25D36FF6669B72C4B88BACA2D31A597C8A7",
+      "v1_signature_section_aes_key": "351F043D8B6435E5E73684B10F79471B"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "DE49",
+      "expanded_salt": "FECD5D5E1E04B1C52AE646C5D47B1B26"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "C1BCED6F35A78F0FB40F698B",
-      "extended_signed_metadata_key_hmac_key": "30BB3C9AA11570317B0551F667CAB16554C72400B318F99E70FCDBE24138CBB9",
-      "extended_signed_section_aes_key": "A32C24E09D4ADAAE24CA1944697B961A",
-      "extended_unsigned_metadata_key_hmac_key": "4B5F583F19BB6D221E6105E55B45F9A2CEA17CCE179B03BEB99A9C50147CD2FD",
-      "extended_unsigned_section_aes_key": "5F97AE24166CBFAF9BFE96EC213142C9",
-      "extended_unsigned_section_mic_hmac_key": "C7AE5C3B45B8FA9AD2EB92D39CCF3D67C9F10BCFB44BDC747EEFF84FDEAFF921",
-      "key_seed": "ADBFB02FB9DFC75BD74516C628285096F2C4350DEA65D4616BB4DD30DF2C8907",
-      "legacy_ldt_key": "9CD5AFBB5CDB94FBF0A51B07C7B8C0CDE7EC9E8985D40CCB6E2EA21EBBA409B9D5E2123BA5EFB45000FB0660EC063274F4E112324037D7548F4CC9DD2B00A9EC",
-      "legacy_metadata_key_hmac_key": "CE65891EF8F79F286A62705AAB9FFDB3CC49FDDC42F084BC06D11A4B7C254403",
-      "legacy_metadata_nonce": "5630BBB459F3C2FF04480607"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "573FB84EA56E4DF16BB17C0281D245AD",
+      "v0_identity_token": "EFD9C7521556995E1E091848A8B0"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "6716",
-      "expanded_salt": "7EC07D3176C5ABCB3731A38D7CACE699"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "3FC4",
+      "short_salt_nonce": "68CB8DB099FC125FE4E2AAC3"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "97CCAE1E7A2BB5319834306D940B4798",
-      "legacy_metadata_key": "736A5584FB8EF29D722171B0E133"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "9DDDB28462DBFCA74F63FC476FFE3D46",
+      "derived_salt_nonce": "85989FA93A8C9A85A7DA598529479045",
+      "derived_salt_third_de": "FF1A21F20BCB2A86AFF9A29CB42A3C46",
+      "section_extended_salt": "FA9DB8C9A1376BA0265420D4B0588C21"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "AD388BD4C9CA419A983A50DE70B96E87",
-      "derived_salt_first_section_no_de": "ABACFA97695D2FBA00E7D5F2BB322165",
-      "derived_salt_first_section_third_de": "C3BDDC19E7A1D453F8BE2B85958C2B6A",
-      "section_salt": "522FF617F4877D7FCCB569597903DD05"
+    "key_seed_hkdf": {
+      "key_seed": "E21B543EBF98DEE5174CD576F4F719ECDBF30F15BF7635A11749DD647EAB06C0",
+      "v0_identity_token_hmac_key": "6889E19D6A9AEEFA4E19A699E0FC8A813B709CE5BBA105C8FD6C6100833F8985",
+      "v0_ldt_key": "2EF57271E0BD7D977086265C78C8D0A529D5E70A3855DC650EBF338DF0F914394CF7DC5368B46E8F8E921D9D69A00BC04B2ADC01CBA954190669D1D4DD3B4C03",
+      "v0_metadata_nonce": "9D1D2B472358E5E253B9A79C",
+      "v1_metadata_nonce": "6F6E74240EC260B896F73EED",
+      "v1_mic_extended_salt_aes_key": "10A906B13EFDF85CA05741262EDAD70B",
+      "v1_mic_extended_salt_identity_token_hmac_key": "B77608559C639713B7E58B631DA3706B1D1E90F95871D4CDAC3F65436195946D",
+      "v1_mic_extended_salt_mic_hmac_key": "3A3BDEEC0AC9D04A2EC9E03D8B32BB48BF7EFACEBC4D946D8FF6B814D5661234",
+      "v1_mic_short_salt_aes_key": "6914FF089524DD20681F6A250A3C2BC0",
+      "v1_mic_short_salt_identity_token_hmac_key": "0B75CD46D539DECA8A7D14F4F7ECF2F1A2618FE449722CECC2816B5D674EACB2",
+      "v1_mic_short_salt_mic_hmac_key": "19D33DE899A50CE211E3BA3727669A6470EE3F52B17AC200E5FCD6FDFD000D5B",
+      "v1_signature_identity_token_hmac_key": "324954DBD6C28C2DB4C1918C3C1964FADFCD0DC2CD5F3E1F8EF870DE57059F5A",
+      "v1_signature_section_aes_key": "78F98654143E5EA976DC7EF3080C4E99"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "42AC",
+      "expanded_salt": "1D31BB4D6407E99C1009B34ECA9488B8"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "2D02434C5257433FC62E157D",
-      "extended_signed_metadata_key_hmac_key": "BC06E2ECDF44C4E749F07AA9FFDAE6786156382DEB38214C23ADF9676273F058",
-      "extended_signed_section_aes_key": "277CC3A4FBD6FA8A0433813C35AE03E0",
-      "extended_unsigned_metadata_key_hmac_key": "A93DCADBFDB56157B57CE0FB27845AB3B14072BB8F51AD430251D5FE431E4862",
-      "extended_unsigned_section_aes_key": "01AACC054CCC1E4C69A30F5BDB083DF6",
-      "extended_unsigned_section_mic_hmac_key": "ABD3E647D57721A8AFD6CE354E65FA9717FE273B3D3002C9EF116E3060EE49CA",
-      "key_seed": "04A09B2901EA8F73BF4503081DA2B2F1C701C8FB2E6D0727229AAF18E618D6B2",
-      "legacy_ldt_key": "F728279808DAA6957480F08D9A71EAFCDCA22F6D8BB646A1B55174997DF3C161D95E50BDC84F2AF897D97319D86D6CF1F4D29755B2A4BD4C896188B493579F69",
-      "legacy_metadata_key_hmac_key": "93F7B7662855DC419AEA47D99180AABAA42DE340259E5875C60E90DF9600F419",
-      "legacy_metadata_nonce": "D451E840855973EBCD5CB21E"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "96D5FFEA8F177301FB6C714E41DC89CF",
+      "v0_identity_token": "66F176D3B94C02ECD0D4FFB70273"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "4BE1",
-      "expanded_salt": "76209392CCAC5FB22D8EEAFAEDD20ECC"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "3884",
+      "short_salt_nonce": "6C06CC0653B5BFD0AF6DEEC2"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "2EAFE79FB0028DEEAAABCC1ACE7F7B08",
-      "legacy_metadata_key": "1B9CE163FB544E4B189C5F2B72E1"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "F7684DEFAC951359A02D3C3478964CA4",
+      "derived_salt_nonce": "89DD24B2821B57501E07BF8913BA5B23",
+      "derived_salt_third_de": "381D3B6413BE8703AFFB0D17B38D372B",
+      "section_extended_salt": "58A07B28332F5BEE07B5B2B6501DE060"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "2F0D5E19F15AD52B54C40021FEE0F9E1",
-      "derived_salt_first_section_no_de": "7826CEB8C46C4FE75DFAEB56973BC3BD",
-      "derived_salt_first_section_third_de": "CAC8343515178DAE36FD923FEBD8452D",
-      "section_salt": "CDBB37DFF788530779D089D6EDD732F5"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "71177ECD2A4AF37C9608B3A8",
-      "extended_signed_metadata_key_hmac_key": "B1ABA8C85AFC6DF1FA37250E164F40BD88ACBEA2E74A12437E13C3E0640C1FD6",
-      "extended_signed_section_aes_key": "FD479476C02CE97482D3AC90876BBD75",
-      "extended_unsigned_metadata_key_hmac_key": "01D6F430855496FE82A98334CEBD234A31BEBEFC900AEBE86C89ADDA184F1510",
-      "extended_unsigned_section_aes_key": "7B66F349E92928431F7499B1609BD60B",
-      "extended_unsigned_section_mic_hmac_key": "AB9676BE62D30988CB70A5A7F958E73B2DB6765CB2B06F06F37D462A930FCE4C",
-      "key_seed": "7C6A118C1331C9276169A367E049962899C7A2E20444F9F964D83FEE4B0C46A8",
-      "legacy_ldt_key": "A0C190B05BC945FE38E1C117D1AB06FE7DA9C46D74F76974CFD558344964054B05EE748E0F8F17D6A3AD995B129EC83F5A803F9FDE0098551020356E59CBE674",
-      "legacy_metadata_key_hmac_key": "C12FF27071B70A96145919151789D0C4311B5CE896C010D49431A78FF933393A",
-      "legacy_metadata_nonce": "F388B3284F9C4983BAC29498"
+      "key_seed": "37FDF4CA1BB248C94ED307E5E1764EC0165EA1E981CDC24D6EB13DAC7605CF9F",
+      "v0_identity_token_hmac_key": "07BE3BF8E93F634BCC23B83B7B417DDCB274E250306265BF7FC4A968273B5FEB",
+      "v0_ldt_key": "60FFD2A67E1C4ADCE2D76A3DC22C0C019DE651E563B3DB348BC297BCBA807FC85647F2769B793C77DCE750B36B20070A8C3F9AD83B5A2387C4EB56A9E3FAA6B0",
+      "v0_metadata_nonce": "29107E6A280BFBEAEA342EE9",
+      "v1_metadata_nonce": "FF7F2C8E68DE7F6D85C495AB",
+      "v1_mic_extended_salt_aes_key": "8B784FB1DED40B601E99C37E0D5AF873",
+      "v1_mic_extended_salt_identity_token_hmac_key": "DB2CAF59508F29C763E7C9474B976D98146DE1A82043081750B96E64D8D54DD7",
+      "v1_mic_extended_salt_mic_hmac_key": "FAD94CC0F43729C896A4B38B6FC314A30A6235336FB9144857F74DBA62446C43",
+      "v1_mic_short_salt_aes_key": "DA65AAFCBBF298770D7E4C20E8ED4015",
+      "v1_mic_short_salt_identity_token_hmac_key": "A67A0798A0395495824110A9ADE3520DC3E80110046C46C31AE59B133A31D8AD",
+      "v1_mic_short_salt_mic_hmac_key": "A2BDDA2BF237949C6F6C25D8A5138966D2E2778FDA5692C61CC8DD0B5CE06CCF",
+      "v1_signature_identity_token_hmac_key": "E046453AB6F68FEDB7A960170A2827B28037DDC8DA1E7F73D3CC8C83A15EE813",
+      "v1_signature_section_aes_key": "445F1E3B3AA663DDEFE1E5C0F2AB972C"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "B1F7",
-      "expanded_salt": "7DD107544AA52412D36D5AC0C65E8A86"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "9EA1",
+      "expanded_salt": "5A3B5015E1D58841D0A30728A25F4604"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "830783B4EC92685F1E440371840B7E1B",
+      "v0_identity_token": "AB8F48A84D5F7B02FCA0E0D197C9"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "AAC4",
+      "short_salt_nonce": "BAB0729453AC44CB60695F7A"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "B6A84A4591AA5C4115D0D47E9D475DD1",
-      "legacy_metadata_key": "66D7DAC60B00990F01D618D929E7"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "FF510969FFE54C346C277BCD12314A23",
+      "derived_salt_nonce": "C858D33D065BE6BE321F09A182E4E0B5",
+      "derived_salt_third_de": "8C972C1345CF4B88552917A6C0AA0132",
+      "section_extended_salt": "947F9A9E1AAC92228E70B97E686092B2"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "C03F4007F1E7180EC871FE12519F98B2",
-      "derived_salt_first_section_no_de": "A76C0BBEFDC804F829DEF65EA3F51C90",
-      "derived_salt_first_section_third_de": "A4DF26763162B19617B24D357C4E7EC3",
-      "section_salt": "4D555178782275479BAEBFCD147C9C61"
+    "key_seed_hkdf": {
+      "key_seed": "BF78AE44C8BFD059E8A47A00B4678650360D0BFDD8D58821722ABC1B78FA6E5C",
+      "v0_identity_token_hmac_key": "E185FF7397CBB44FA99048A46EBB22C624BC1E3A21F2ECEDED91686440919752",
+      "v0_ldt_key": "6FF4D69DC4CC5D5CF17589ED7EA6FF8843D4785FB5AAF89B8989E5C9EADF8B915CF0F2A2974240C8A7C49B571986E4BFC93F3B9F9D809A69FD89DA0C470E91E6",
+      "v0_metadata_nonce": "F25EBDA155405BEA86941F45",
+      "v1_metadata_nonce": "7787AB19C65497386A035A57",
+      "v1_mic_extended_salt_aes_key": "68A1F6E9E2E703728795CF51AB018597",
+      "v1_mic_extended_salt_identity_token_hmac_key": "A93B60B9DB23C48E2128EE8A0C982FE2256BAFE463F51C99E1FF0D6A5036EAF3",
+      "v1_mic_extended_salt_mic_hmac_key": "BF3E481EEB971717B448D4EDB765F2C9291C3165A8C021742E3B33D84474772C",
+      "v1_mic_short_salt_aes_key": "C30DBBFFD2A16E98895BD9EC2D7E724C",
+      "v1_mic_short_salt_identity_token_hmac_key": "B0B97E8EF4DCD33E40356CB2D6D7359517FCE3FFC7B8DBD4D73FAE87CE0D0CB5",
+      "v1_mic_short_salt_mic_hmac_key": "7AA526E8ACBE888A47E7F042DA3751FBF8988294D8286208122C55C2322D5F25",
+      "v1_signature_identity_token_hmac_key": "5EA50122D84E384D7C4B2105A182BEEC7A4D8CBE266B54C51D70F2186CC821C4",
+      "v1_signature_section_aes_key": "E9E31EFF10C7F9F2634BDB89CBB407CF"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "C543",
+      "expanded_salt": "4C4AC44FEA185B78FB54A3A2749DC65D"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "E4E6C206BDD5477A4894F465",
-      "extended_signed_metadata_key_hmac_key": "9274B9E3806FFB99E2D096597B8716E0949486FF30BA7A1FAB01FB5D143E5E30",
-      "extended_signed_section_aes_key": "5E09FB429564270B1E01C0C12064457F",
-      "extended_unsigned_metadata_key_hmac_key": "AE2B21B4DD683EEA3815374C1E606EFE81B3012A20C244F3EF44E4B126C1B060",
-      "extended_unsigned_section_aes_key": "4FC383A9EB6FC92D4E550C0869BA7424",
-      "extended_unsigned_section_mic_hmac_key": "A2D2F07EE970FFA7030BC81DB05D87AD7A1CF60E45A0F607F7F7D6CE64E7FE1E",
-      "key_seed": "06E34312B7BA188C16BF9EC455B7C3DB7BFF273ED7E671DCA6218923F6315EAC",
-      "legacy_ldt_key": "9A779C0695146D84379F839C1B01410A77838D4627A16D697AD8BA062BAF892C0518FF10250348929DD369E8A6B0D41076A485B48287E01F9D28F76A7AA93C25",
-      "legacy_metadata_key_hmac_key": "802F06CF894D4A44E91C384CD4CA11282E443D9E44D9A038715A3CC889DDCB19",
-      "legacy_metadata_nonce": "0918753240B432ADA508D2A0"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "D5E805DA2CFD12C67BBC380449E48324",
+      "v0_identity_token": "095409BFC1116FCCCD80F1ED602B"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "B28E",
-      "expanded_salt": "27122881802D23D4B730928B317AD649"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "9D8B",
+      "short_salt_nonce": "FC8A1484820906156C195098"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "A60426318C060F541DED5C1F7AFA6E87",
-      "legacy_metadata_key": "4EE9FB0C014E5C8DEC62CDCE3A54"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "E4CC4A82D88EA39A5187A28FBDD40F3D",
+      "derived_salt_nonce": "B5807E2B08DE99DC9EA01E9478282DDF",
+      "derived_salt_third_de": "E3CC102DB32458B2EAFEEBB6113DCEC2",
+      "section_extended_salt": "2E250B90A61C0F513340D930EA6571E4"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "C9264AB98A5CBEC98A59B436301C9456",
-      "derived_salt_first_section_no_de": "75AA3B31D990FA958C0BAB07C316137C",
-      "derived_salt_first_section_third_de": "20F8C6AA4C641C3EBAC513410853A637",
-      "section_salt": "63056BE924D9AF7A4E1EDFB78F8FFD42"
+    "key_seed_hkdf": {
+      "key_seed": "C458B31EB8502A483EEE8D5A804249BEFC113FA503018A2E65E91AE51F661839",
+      "v0_identity_token_hmac_key": "7681A1EA463DDD1FF1B10F87A8FBFB4C2069594DDD3B552C097154307BABD2FC",
+      "v0_ldt_key": "39C6E02DAA80BA8207A150BDA0241C3650F128706F413B9094997C661F0B722AEBD00042675F9BBF574A105505866B688EB115AEC0DA430BBB3AF5AA2B3B082E",
+      "v0_metadata_nonce": "6CDDAB688C8E27055A4C7735",
+      "v1_metadata_nonce": "E6B46B1329265F0BC3647652",
+      "v1_mic_extended_salt_aes_key": "5EEE9AB4B0A14122DA37583240569EEA",
+      "v1_mic_extended_salt_identity_token_hmac_key": "F0721E6BFC42BC07755E5838705139246B39637F18E9D6DDF8EB76E8E7BA64C5",
+      "v1_mic_extended_salt_mic_hmac_key": "D115FE4B3ED3D6FF3C457411F89EEC573FB6B4FF074844250F10D923416A951F",
+      "v1_mic_short_salt_aes_key": "10F209765DD1E2848DC419C4268771A6",
+      "v1_mic_short_salt_identity_token_hmac_key": "9FD5A74F6EC544AB29AF7CDF130FEAB9D22B37A4178E7A4C7703DCCE6AEAF4C7",
+      "v1_mic_short_salt_mic_hmac_key": "DB1E2E8F93CB7D9891CBC951DDF02E89B0E77F646EDB9BB4A33F936F7A04F693",
+      "v1_signature_identity_token_hmac_key": "419EA078DEA8DE15D57ED34A497920F671D83425B204F2C364CA506B2A4FFAE9",
+      "v1_signature_section_aes_key": "519EA77C6FFACDF488BC05BD1E45C56D"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "C2F8",
+      "expanded_salt": "D20BB79365E7FCB7B41D40D18B29D56C"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "641A9E897828B9CAD7C16FF8",
-      "extended_signed_metadata_key_hmac_key": "690679062F05D8BAC47BA64D13F91029084631B9FD39696B64702B533283C8F1",
-      "extended_signed_section_aes_key": "A521430F5CF09B5073A2EE5DCA704962",
-      "extended_unsigned_metadata_key_hmac_key": "2685C8CB006301455AA48F317D812AFA9F36FC071A692A7E21F411CA4B203E26",
-      "extended_unsigned_section_aes_key": "ADE5DC26FA3ECD51A6B4BE7B2EA0C70A",
-      "extended_unsigned_section_mic_hmac_key": "D8AE4269C81599401F6DE7805A5DD401BBEAFCE68BCF3245E315940130C041C5",
-      "key_seed": "41ECB16B981741F3FE1EC685A601EA8FB5F3B26ABAEC70294805339C90A8AD71",
-      "legacy_ldt_key": "8A6B4B8EFAF5DE8701684F7470183A4046FBFEE965E61D4219E09F95280D43B895BC9FE4B39499C927FF0541F067C95D98377D139CA23D19AD7B729F75207BD2",
-      "legacy_metadata_key_hmac_key": "D9A2131A2E99553E598BAACC299C5E40E3051A4E4244F3FB8705FEACE0CAB3AB",
-      "legacy_metadata_nonce": "862DF488A461D0EFA780FC19"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "5436E96BB1FA5BEA3F61CCF4D1334D7B",
+      "v0_identity_token": "01395F4809126DDA6EDB05CBF274"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "7CE1",
-      "expanded_salt": "72F031CD4FB7AB7A03423FBF04BA2A9E"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "D382",
+      "short_salt_nonce": "AE407828507152FA369D8AF9"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "07EDA657E63FC8F6737B5B3F4A0A8C81",
-      "legacy_metadata_key": "F141D580CA3325C5FC485E552438"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "93A5A6F16A869EFF9F4147B3BFEE2779",
+      "derived_salt_nonce": "51BA87FE075BF338688B780493445A1F",
+      "derived_salt_third_de": "534680E817F3E8366D19AA564ECC2ECC",
+      "section_extended_salt": "58FE62A2B35E250EC7A5F7885357E659"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "D9498F72764DFB0266F773788284BCC9",
-      "derived_salt_first_section_no_de": "2736F330B0FC8CE3AC39558F48513039",
-      "derived_salt_first_section_third_de": "2B485FFF58AC337F216B01885F0A1FEB",
-      "section_salt": "482DFB90D8B4501C8089AD97750EC132"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "F1FA2EBDE91BEFEFDFC7CF00",
-      "extended_signed_metadata_key_hmac_key": "B1DF6DF11C98D5BE667BC5CE8B91321CC5498F9149B0217D48BA9C6E81BE9074",
-      "extended_signed_section_aes_key": "D63F841B59C4398735CCE79146AADC3F",
-      "extended_unsigned_metadata_key_hmac_key": "37A581DC364BB86DDC2AA8F8A1B939A7A92BBA83CA3697408D1CDED5699A4243",
-      "extended_unsigned_section_aes_key": "20A1EA0E7454FEFBFF4DA2BB4DF6CD8D",
-      "extended_unsigned_section_mic_hmac_key": "2842AAA512670388A45EC3DF52356B28AE1F7C3CEA324BE4847E9D8AF578A5DE",
-      "key_seed": "37399B354FF258D23A87941C4E9A348D851C5CAB79E1DB345BB2568841ADE7B7",
-      "legacy_ldt_key": "070759E454B3EBE99755C4C8C6FF67BB9DEA89D49A2CB68E02D3A4E66F12D1F97862F8A8B8664CD80A6F17EE9372170908F47B70681CCD6673019505E4130542",
-      "legacy_metadata_key_hmac_key": "535D5A54192D3E58EFB23C815A099924B2602137D8E12F6279A24077E232C3EA",
-      "legacy_metadata_nonce": "04B9108D0FB4A41EB17D754A"
+      "key_seed": "CAD145B4C4E7B40281586C90415BCE0B1D259E400C12A4558F848D4DB3FF6022",
+      "v0_identity_token_hmac_key": "094878A6B8D94529F33F759B6470003F5EC5D9E9D12D76C245E2E839E98C855B",
+      "v0_ldt_key": "23B0AA6A58D05FA25ADAB4395B948F2718CE48537875D2E68DDDFB38690D96B9A36913007ECF4A030BA7E00E96A7DA7BCE138FEB92FA1A4569F867EC2D881E18",
+      "v0_metadata_nonce": "CBB1C6591A06FBEF5D13C1BC",
+      "v1_metadata_nonce": "B3406DFBE1D1320184D9721C",
+      "v1_mic_extended_salt_aes_key": "0A47274588CCF8ED3772C654A76F7558",
+      "v1_mic_extended_salt_identity_token_hmac_key": "70FCDED0EA67404EF350C5167978234B681BA624C27334896364FDD77C4CCCBD",
+      "v1_mic_extended_salt_mic_hmac_key": "4EA92D6148AA2F61D283E1DB8AE6EE1066893C4A898BA503145CE493F2AD5400",
+      "v1_mic_short_salt_aes_key": "83F56E946E80C574D9A6C89975DEF2DB",
+      "v1_mic_short_salt_identity_token_hmac_key": "5CA60D709F88F9D07556B4A6E6A4CDE9DF1DBD280BABA39BE47A13FE90E5E27D",
+      "v1_mic_short_salt_mic_hmac_key": "33E1065D5BA0349E161E41D873B194379EEF0D2DEE466805D72BBE01F6349F2E",
+      "v1_signature_identity_token_hmac_key": "58A6E794B4593108761562A3C74CA4EDD345EFAA08F35E661B97FF113C24B6A6",
+      "v1_signature_section_aes_key": "1DCC7FBD666A58BD52FB5EB65A63251D"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "D6EB",
-      "expanded_salt": "83EB2FECC0F1C19A6EF7FB30E945C103"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "94D8",
+      "expanded_salt": "0F9CFE35B1EB9C140FD004A84FF23100"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "FAB1FAE5BB91A22DAE1792117AAAF36A",
+      "v0_identity_token": "2A341B1B61F1ACFB5D74DA044867"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "7A1C",
+      "short_salt_nonce": "E006CD94A4539C0185D8A480"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "60C52A4D9245D6E8D49DB7FE7FDA5C43",
-      "legacy_metadata_key": "4E2117A0CED10CE233750FBAB835"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "28AFE5F7B2249029B8C64188C46E8EA6",
+      "derived_salt_nonce": "72B2C14A669C5CA1684D7E4751793E2F",
+      "derived_salt_third_de": "93DA04162B18AEE23A3056DD6B53B447",
+      "section_extended_salt": "E3D5BA66BFF1542BB9E864F1718972EE"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "849B406AC1383E3333C4526823350AC8",
-      "derived_salt_first_section_no_de": "F2657BBB7FB8879E98F9D4EE55343AA4",
-      "derived_salt_first_section_third_de": "9025C1E15E5668F1144640D7C2CB7F63",
-      "section_salt": "971929C83FBFB8552FAC4316895F82EF"
+    "key_seed_hkdf": {
+      "key_seed": "4ACA5648232134F2F67CAF1F73A747C79F05C5433006D6B0499E41C99CC7EDC5",
+      "v0_identity_token_hmac_key": "E5BFAB30F0CDC766E0A2CCAF2748F1C2D3A4C61F807EF3065AA632C1E031849C",
+      "v0_ldt_key": "8E8A67DCD76DB26CA1F35044302978C4981B6934A03F8431C737D66529E88C752B6B85C8585C3C4F5D321DA0C8297BB6112CC63878C69ED84C97EDAB95AA5BD5",
+      "v0_metadata_nonce": "6457431AEFBF8D2360A0892F",
+      "v1_metadata_nonce": "2307C5893275080E5C6B7A5F",
+      "v1_mic_extended_salt_aes_key": "49F7F9AF2E1AAE200CA362CCB2D8DA5C",
+      "v1_mic_extended_salt_identity_token_hmac_key": "DDF91ACE824007F30BD268501F411F2926B578F675DA58158B12ED67C29DA6DF",
+      "v1_mic_extended_salt_mic_hmac_key": "5D68D65D0C1926E664FB9DABDEC88B84A1151479045FF66BD520E496DA11BDE1",
+      "v1_mic_short_salt_aes_key": "4201A2259722BA7647492CD28EEC1AEF",
+      "v1_mic_short_salt_identity_token_hmac_key": "B827390AAFDFFE4121E501DD5AB0F63FE99812B0AA19EE5D3CCE8371BA8D8565",
+      "v1_mic_short_salt_mic_hmac_key": "14E7069440F3314D316424A4C05B14781FB8057BF555056B4DCC1CCC01CEA5F6",
+      "v1_signature_identity_token_hmac_key": "CEF4CC2BC66B6DA8CDE0BDC33CDB0544A482E693D0AE8E4776D2C50EB3141EDA",
+      "v1_signature_section_aes_key": "26D03E938464532CBED815DE327C0E53"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "5883",
+      "expanded_salt": "107D3AF4F533B1415860BB88500FF30E"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "BAB6E78DEE99392F09D6A045",
-      "extended_signed_metadata_key_hmac_key": "FE88421ABA3A3E0DA9E519243749AB2B28586480DB9B59C41F7E4AB17A9459CF",
-      "extended_signed_section_aes_key": "EB1F668BACAD4DE4555036C8B9F21D9F",
-      "extended_unsigned_metadata_key_hmac_key": "F7016A22618D636539DD37221EB7BA8BE8764707AB9DEBA695FAE93546E1E777",
-      "extended_unsigned_section_aes_key": "7AF856E9EA5F3AC6EBF2DA80EC065050",
-      "extended_unsigned_section_mic_hmac_key": "17738046ED5E72F0C379D08FE6A815CDF72CB1B8BA2C675DD013872E7B0DD185",
-      "key_seed": "185AF89CD890AD6F7C0F9DE8228DB8A92AE25DEF3BE510289663D4C196FC2CDD",
-      "legacy_ldt_key": "3659A65B725059278CB5ACD34B73F32EC5D8D267C15E567BD4EEE489618EFA62D03DF9A8C77D3B407F107927AEB771EC613320B1DAE7A295FC49AC8E1B274F8A",
-      "legacy_metadata_key_hmac_key": "5F10B1A7CCA2F40E32B00738583CA2604312F1D9648EB375995EF725438B6B5B",
-      "legacy_metadata_nonce": "EE05CC8759A360B9699FFD9F"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "B5D43495932EA4AAC04D597D47F3E1A5",
+      "v0_identity_token": "CDE1002866DA5EB5F1B93AE8604E"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "7FEA",
-      "expanded_salt": "C7E9D2070465A796CB27EA5E81D7B5C9"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "3F19",
+      "short_salt_nonce": "725F8B5D5A48F5BF15805851"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "E4AB233100D9D776DEBA1949426DC104",
-      "legacy_metadata_key": "C5E5885E8041F9A0E290D0174FF0"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "ECE9232AEB13F080BBA9110F115C1C06",
+      "derived_salt_nonce": "B067DB848C67AB62F7D9647AEF6350DD",
+      "derived_salt_third_de": "0E171CFAF26BE107633093C5FBE09795",
+      "section_extended_salt": "98564150CD7F85177E2FBCD32D1991AB"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "582878C554E5BFF92A4C26C50BBC2B27",
-      "derived_salt_first_section_no_de": "C9FA9274CB4C3F6F03BB7F2AA5D70A11",
-      "derived_salt_first_section_third_de": "582276B5F71F65E87B883C86DEC83349",
-      "section_salt": "2DA2B6676B05EADBBA2AC2C7A1B91E5B"
+    "key_seed_hkdf": {
+      "key_seed": "1BE63378AB9D94C9624BF8AA13D32159AE60185DE029F75CC5ED057DA75B5859",
+      "v0_identity_token_hmac_key": "ABD803C6A6A5FC17BE165B565C5258A7B696BACA67F122314BAC5133D26927D4",
+      "v0_ldt_key": "458D845951F3C72999AD0D892B41AB1ADC8F1D257085F5305EB498CDB702BB32272353D4BF359DC7C0BC7348DE5CF83F92F099854742015D1703501620249349",
+      "v0_metadata_nonce": "DC8B506C111B985C7CE1A61E",
+      "v1_metadata_nonce": "D086A5209230EB1289F25BEB",
+      "v1_mic_extended_salt_aes_key": "8A8CD0757997B9A09368E004C13A5907",
+      "v1_mic_extended_salt_identity_token_hmac_key": "9F5E33BB0AADE8FA3B3EFC1C0ED59446D9F5D7078CA6B5A268B4FCCC24F7EEB8",
+      "v1_mic_extended_salt_mic_hmac_key": "7175D3D814EC85A2D0425C56E3AE38B7380FFF95C51104BAA187095753BA574C",
+      "v1_mic_short_salt_aes_key": "50628611E98D88D30CF256444B776072",
+      "v1_mic_short_salt_identity_token_hmac_key": "D8347CF2051CCCE1900812130C256F539172A7937BB9F024C101C61BD11C0767",
+      "v1_mic_short_salt_mic_hmac_key": "46D08AF025C2B6D0B27D54ACC927D0FB0CE944D38A5611BB15DC7FB71864D468",
+      "v1_signature_identity_token_hmac_key": "8B4C710326841E386DB89A9E34140A3129F740253121E150E59F86AC03B7BE9A",
+      "v1_signature_section_aes_key": "F72965CB64E9365256119F29F5355A04"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "D584",
+      "expanded_salt": "93356D355D4FE32E21F4945687F768BA"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "1516355E155E481A2C1BC292",
-      "extended_signed_metadata_key_hmac_key": "A96AB799DBBF000DBD6429124DF1AB94AD26BA8EA3CFA2BD3685D18F74AB95B5",
-      "extended_signed_section_aes_key": "2EA7AA6081C6055A50D2BA83F7650ECE",
-      "extended_unsigned_metadata_key_hmac_key": "54D6DE3AB30E06638AF867C79CA8F291499A6A912964EDAE3FFF06D75D6734FE",
-      "extended_unsigned_section_aes_key": "FCF1C0C04D383C2B440E91142D5F8DF3",
-      "extended_unsigned_section_mic_hmac_key": "617A0B559B6C31312C6DB59956560DB2200B0DA19132085BB51EEC4662365BEC",
-      "key_seed": "BEE55110B49523CA92D9CD1921A3A3556D3DAE9E7D2DC1DCF809A7F744D744FB",
-      "legacy_ldt_key": "3050E135E4A609B600FA94D673AD60C6800F860CEB26E0C080669174F4967FC877241054D94C945CFC4F511404AAB3941EF5337E49A3C0357DDCFBEE6D0FF178",
-      "legacy_metadata_key_hmac_key": "D4CD2F5513409E9C08796B367D68ED86E7F8AA1D73B3BC80E310A6A89FBB5AF1",
-      "legacy_metadata_nonce": "69BBF34C4C06F9098ED5822C"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "087F0BC622E59911C61FEC7AC6D5CE8B",
+      "v0_identity_token": "D8D6DE28153FB1F87CD75FBA7F61"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "E41C",
-      "expanded_salt": "F90A3A5A7E184026B2412B7981E6E4EA"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "B578",
+      "short_salt_nonce": "011AB27EBDDCF76BECAC83E1"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "7CBB4317E53C77A6EAF04270340C8E74",
-      "legacy_metadata_key": "290406B8E4F3FDD69F77100C25F8"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "9E43D77377463D53AC57DFA860AC64F2",
+      "derived_salt_nonce": "CDABCA09C3FE20F0BFD2F0855C16709B",
+      "derived_salt_third_de": "2A7BB2161625A9FA4C944A03780FE0F7",
+      "section_extended_salt": "C3AD1D6CF13D0ED5D4B52933E4BE9923"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "09C85DC366B0A127E44BC3EB55F1B69F",
-      "derived_salt_first_section_no_de": "8E806978646803E71CF5A89A993EBD15",
-      "derived_salt_first_section_third_de": "A79CB0BE50F815A706B75686E8AF95A8",
-      "section_salt": "BDC5C3A2FDEFF52154DBEE9B2DC99272"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "38347176312E7BF0CFC150F5",
-      "extended_signed_metadata_key_hmac_key": "AF37F8DDF621E95E9DF9E960D7C3C3B204CF5F660E33015C0F76E1FFBF793255",
-      "extended_signed_section_aes_key": "DDCE8B724FCB835C5769FC12FBBBCFA1",
-      "extended_unsigned_metadata_key_hmac_key": "2A31672AF767AD1C20F69A40FC0C39E82760A88CF7FC32AC562B4334A3BB6FE1",
-      "extended_unsigned_section_aes_key": "A6DA0A55C2DF68D9A268E2E7D5DFA910",
-      "extended_unsigned_section_mic_hmac_key": "842AC63178D49573C9A294B11B23053A8D7D032E2092A7B090B18C3B4B1B0D41",
-      "key_seed": "5507520DCBC7FB26D474314F10DE9CA600A29F7EA2B4EE3FCBA75A79812410EB",
-      "legacy_ldt_key": "13FAD5A042A94E5BB18ACB097E8D9AC9BC3212ECECCAE8B3F0877B28BE475A00F055E916510D5E37B70EBFFC6E20281A98E960BB875B6CA04DC4A4D791D728D4",
-      "legacy_metadata_key_hmac_key": "D208CCF899D7738EFA94AF2F50D71E96A850ABD8DEA1E731513BF959E52245D8",
-      "legacy_metadata_nonce": "55869FF688A156A9AD67F4B0"
+      "key_seed": "EF9F19EA27A76CE0EEE32D179274DFC21FF9ADB2DF7A94905F796FC6AB976867",
+      "v0_identity_token_hmac_key": "9B83E478395DEC9B396443937B6E51290E28FFDE2449CC2987C2C75AA91E8BF9",
+      "v0_ldt_key": "FE74DCBB424B6F9D9D31F31CB047176E2CE8B3565149201F01ABA127DA935BA97351D6536ECF0AE1C5E1A73B5E651338E7290BEDC98056E369372A5AB036C8B4",
+      "v0_metadata_nonce": "238DC3B985343E54213B4F84",
+      "v1_metadata_nonce": "830717981E766A8D5EF618DC",
+      "v1_mic_extended_salt_aes_key": "D99AE5B345134E9EB7FD31344806ABC5",
+      "v1_mic_extended_salt_identity_token_hmac_key": "FBDDFC1B6102418E55B29C6B1F5C2A6D5E7CF796272E8D4CDB8A212FC5844487",
+      "v1_mic_extended_salt_mic_hmac_key": "279FD3C9459F9752565AC221A02805DCB2860DEE54FC5FB6787F3ED26C327463",
+      "v1_mic_short_salt_aes_key": "9A62AE0DDB8B45D2E6B1A5C34576D29B",
+      "v1_mic_short_salt_identity_token_hmac_key": "D0FBCE82CFCE356C430CB45A87CAB367C0F43F2D512F996854A1E0CF1AA230F8",
+      "v1_mic_short_salt_mic_hmac_key": "35DD70B190738EE311AD7A9169E9831C6DE663A038BC30F744B73686264D868E",
+      "v1_signature_identity_token_hmac_key": "57683A7ECCAF59E1233B5972B29A681EFB5C5CCC7841A59F200562F43B604955",
+      "v1_signature_section_aes_key": "3992116A5B3CB5E2909EBA148CA3D0C0"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "5D87",
-      "expanded_salt": "6CD22F42846D18DEB8B16AEC28055646"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "3484",
+      "expanded_salt": "0D2B068736CA05A30FB77F4CB162D6E5"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "67C50D4BE0C807CC777A451E55290706",
-      "legacy_metadata_key": "CC56E75A8BB54C3CE1EE9DAC6855"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "0FA4D93D5FB8710DD7B74B82030EFAC3",
+      "v0_identity_token": "DD024CDC610F1298061F59286313"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "A665",
+      "short_salt_nonce": "D060E783D2A0CE06655119EC"
+    },
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "2B2E1D1168C638E5F96A01EFE95E9198",
+      "derived_salt_nonce": "42F31D89F5FF91B8CC141B2555261E94",
+      "derived_salt_third_de": "9ECC9D55DFF824E87A7C10427EDB7D26",
+      "section_extended_salt": "9CF4B88393E26CA09669F2A24F0893F2"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "724B63781CDFD0DE57D682E929C0F1AC",
-      "derived_salt_first_section_no_de": "DA6C22EBBD2E4FC58307D725E7001E18",
-      "derived_salt_first_section_third_de": "70E96C64C5013DDA87D8750BFC734CCF",
-      "section_salt": "60922665EAA6734D3AC84D0BC6B8173E"
+    "key_seed_hkdf": {
+      "key_seed": "81BE1F6A11DF23604652C9BE0BB40A5082107EFCD943A13F4804E11C55B0EE76",
+      "v0_identity_token_hmac_key": "B7C2F66D0712E43B3D7A791F1D6B33AEB5FDED682B54F2FDD1E616C5D6ED1AFC",
+      "v0_ldt_key": "6D3F8AA49C7749B49D6F62AC6AF4A9EE6BE43A50C64D7D9C3C17A21F67BAB89B4CCDA8BDE1AB1E5A287DA1427E1F92F43B78BA2CF12F151EEF68B5FDA962C89F",
+      "v0_metadata_nonce": "1E59D995D9364D1F4F601502",
+      "v1_metadata_nonce": "B55540E50C46A2FD3AA6EE54",
+      "v1_mic_extended_salt_aes_key": "C99342BBECF0C67F095FC1ED24C4C4C3",
+      "v1_mic_extended_salt_identity_token_hmac_key": "98A3EB8EE5B99369FDA984BE76A1F05FD7D51DF6D0983C572437CB40B87B5C48",
+      "v1_mic_extended_salt_mic_hmac_key": "1FFDBECE1FB5B98AC89B7AC112608F8E7705C1C98F2434DEE5529232CFA16BC7",
+      "v1_mic_short_salt_aes_key": "3A7608329B686BEEA3B22FE3358B1310",
+      "v1_mic_short_salt_identity_token_hmac_key": "4B4C530D8FD05FB35D04618172CC0CB522F1DB01D91665941DD62B9179642D3D",
+      "v1_mic_short_salt_mic_hmac_key": "386F86CA17343F3A66426BD8B626682D9F4AFDF77AB93AB964FAA3708E9100F2",
+      "v1_signature_identity_token_hmac_key": "9D9FBDEAEFA4D07C54302BDC107B47530E461D428A4DCBA4C20B4339053FFBF1",
+      "v1_signature_section_aes_key": "D8BB6D91F80D467B55F348501483688A"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "6195C05F850120907C37E671",
-      "extended_signed_metadata_key_hmac_key": "C197E6E017FDECD561EA88AD8CBF7F9826B17CC993E15803C2B770DB98803F71",
-      "extended_signed_section_aes_key": "0400A8E1E5D2A6D93FE2DA13E109D10A",
-      "extended_unsigned_metadata_key_hmac_key": "DA4F587EA32EE460D0605AEFB2A3D4916137A0D27FE142D2BA315CFE40A7CF7E",
-      "extended_unsigned_section_aes_key": "97F44ECF5B72FCAA7730A06BC7E21466",
-      "extended_unsigned_section_mic_hmac_key": "1B2815943A5DAE1D4823E8B146AA3CABB59017B2D01E9DDA7731933E77354459",
-      "key_seed": "933CC9ED79D768475F2C50D65DD35E354AEC0ACDDBB1DB7A406B16A967262F95",
-      "legacy_ldt_key": "B3C8DB885F6949E585C9B9CFB8292A2E1D16DFAEB0D8B4CB859362AF85EEB78F55C676BAED4ED82FE690EB5A52DC194E3C09037367E0AF6C012DC33AFE024750",
-      "legacy_metadata_key_hmac_key": "68B1BDFFCEC2A954A0C61C07BA65A3028BB117945F0D405BFF859719E90C603F",
-      "legacy_metadata_nonce": "9368E6A9BFD9CA79ED0FCC4D"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "1CE3",
+      "expanded_salt": "25EAC881DD2DFD7EFA2973BDB0A3FB23"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "1B3976E6D6E9297D465A6E9EE67559E2",
+      "v0_identity_token": "4CAE44FCA940D2B49B1CDF558260"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "2C1F",
-      "expanded_salt": "764B370D8ED153B4D9AF7326FD7E3131"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "0891",
+      "short_salt_nonce": "F4BD7CB2CE01511C1F18A53E"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "6B947A303D99A9874028683C7663E5AE",
-      "legacy_metadata_key": "95E95ADB8AAC112E502641BB3974"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "541E5A0A4D7EBD30C3366C4628971F0A",
+      "derived_salt_nonce": "6D0DE80696F619D2B4CBA1D9DD0B7DE2",
+      "derived_salt_third_de": "DC761244E373DB2F195ADA9CCB849BF0",
+      "section_extended_salt": "829E9AC22DC10ACB10F1FE7EFE014CDF"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "699F64EEEE0A421370D20D1E656D8EF1",
-      "derived_salt_first_section_no_de": "0B9CC994DEB1CBEE31334AAE82BB01C6",
-      "derived_salt_first_section_third_de": "E883BA14BB1E9C1CF2CA544507A8E26D",
-      "section_salt": "93EC27FD11A0710A12D837043C9CB895"
+    "key_seed_hkdf": {
+      "key_seed": "C33B62DDC31FF779AD1F6D2383226D28789EB3B9BC4F28290975ED6587DA0B4D",
+      "v0_identity_token_hmac_key": "36C816C3AB161BC5691D657590CC8E7D4CED85CFE8A075357B94A00F30E3ED47",
+      "v0_ldt_key": "57E32F4A953427F5D8E6F1DDB9BC9F8A7A770D439E2D9B793033EC74211A8F34938E1FC524EA70C9895DD9872742F13FC22A817FADE748B3F5F0D6D28867098E",
+      "v0_metadata_nonce": "2A4C6A38F60B328CB449D269",
+      "v1_metadata_nonce": "4823300500BAB300C14A290C",
+      "v1_mic_extended_salt_aes_key": "30306339F33123C69725052DBC3DF985",
+      "v1_mic_extended_salt_identity_token_hmac_key": "50B0F5BE869B43E70BC61D249AC48A64E1FDBED45F239E545952CBBACA13171C",
+      "v1_mic_extended_salt_mic_hmac_key": "EBB43E51498E6753A1877E2C996330A2DF880E41887DDDED4FE7FF6D144CF4DA",
+      "v1_mic_short_salt_aes_key": "40119DA2ADBBF3E491019863938942E3",
+      "v1_mic_short_salt_identity_token_hmac_key": "9E82B5C1E723F1B578FE9A8519819B4A5B0D0F6DDAD0147E78BF48F778A38CA0",
+      "v1_mic_short_salt_mic_hmac_key": "2EA118DF0241382FDA2AA66599346986048260DACC8B1CA8EB6AF745224DB3FE",
+      "v1_signature_identity_token_hmac_key": "F31EB8FBF7E0170E352059413B935E7ADC5C8262D97DE0D81CEBF1176D270352",
+      "v1_signature_section_aes_key": "E780DDAB066C5FC692736EBE821C6FF8"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "8C118284D7A96BF2A60C1239",
-      "extended_signed_metadata_key_hmac_key": "C6CE919C79E1CB055C77D1213E9266D95C8B828D32E93F77A8EA73EA48C6CD20",
-      "extended_signed_section_aes_key": "6E770DC0F1C276CE1DCFB138E8005EC3",
-      "extended_unsigned_metadata_key_hmac_key": "2582C714334AAEE935486705696B9196C27A4EC495CB0E59C8601CD60831022D",
-      "extended_unsigned_section_aes_key": "255034A404E961637FCF4CE1CA72B44F",
-      "extended_unsigned_section_mic_hmac_key": "4216782A51DCB1997224685E06185109FEF99FA3354038A49FAE4F0E0076FEE7",
-      "key_seed": "562111357842DD6AD71E95C6B9DC635ADE5E2EC3A2E29A242117147A0148705A",
-      "legacy_ldt_key": "D258BC177A33B5E2256475FDC9F84AD78F8B4886EB8781B4F1926716A6BA6D527D75D2953CC4037044B14107A91B292A45DD46ECEA653A1D086283EAB5607FC1",
-      "legacy_metadata_key_hmac_key": "8BBC6480F67E0BB2AE62AEFBB16D97BFD4C37618E34B1982B29BA56B34FDB466",
-      "legacy_metadata_nonce": "3EA11851CAC5F5608CCCE44F"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "8118",
+      "expanded_salt": "B259AFC30A1526F524AB7D176239D332"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "FD4028D3275272ABF95084312992FC6D",
+      "v0_identity_token": "95C1A38B5345AF2731967010E3D5"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "1C18",
-      "expanded_salt": "A58037650FF49441923278D15FE312D6"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "8F6D",
+      "short_salt_nonce": "E9582F01D2CBE7A7F61C39E8"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "96B7834CD34C6EE7027864BC6CC410FF",
-      "legacy_metadata_key": "B8CD0EC6CBB33A4601A522200869"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "47A9E3DDC054A4E93C11018129263A61",
+      "derived_salt_nonce": "04023B2ED6EDFD2654E91BB8604CFCB9",
+      "derived_salt_third_de": "FE7BEE522D169D527312F2EA7DCDCC1A",
+      "section_extended_salt": "F9696D41F224F85BF8A5CDB62C171144"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "B091260FEC73EE7A8D70201F9D464F83",
-      "derived_salt_first_section_no_de": "EE1D6CE31F19649C24841A7F6533AF34",
-      "derived_salt_first_section_third_de": "14FD75BF7AD5B802CE6725D9A4C2EAC9",
-      "section_salt": "CC587F388F406FC90B64C0AF1000419C"
+    "key_seed_hkdf": {
+      "key_seed": "57C3D798D37A8A013CFA9E360748AFBFDAE3BA89F545869122CD051E58331DD4",
+      "v0_identity_token_hmac_key": "7A85D2EB46CD2EA1373599BC94D8CBC6C6678296E3173CA3DDE74D4A80B1ED3B",
+      "v0_ldt_key": "86EDCA697E60EC4CA150BEC73AE0E8CD8F0A0E76F481E409CBEC6BDD93540BFE80C610C0782CE4600CA45E7DAE40FFE3143A18AC11124C233C484C92C8AEAA36",
+      "v0_metadata_nonce": "5068C44E8025E74D61C784E5",
+      "v1_metadata_nonce": "4228B89FA232A8DB0B5B0C25",
+      "v1_mic_extended_salt_aes_key": "743E02E60C3F633F37B073684C5A42A8",
+      "v1_mic_extended_salt_identity_token_hmac_key": "6A38862A238FEA6BBBB4FB72CE602404A26EA0F64D50FDD1D8C088B50AAD16A2",
+      "v1_mic_extended_salt_mic_hmac_key": "B87017DB360AD3F66B74E6F8779C485573CECE93A9B058E9197E08DE19DC233E",
+      "v1_mic_short_salt_aes_key": "C1802BB67FA9966A675459E93BB18E7C",
+      "v1_mic_short_salt_identity_token_hmac_key": "B9BF21CAEA29FDC085FBDD4D26DF28FE548C1B945952FCA25799D13999C1CD30",
+      "v1_mic_short_salt_mic_hmac_key": "9CBC02F56E1DE42FA95DABD085E014E5B1FAC3282A37B23D497F49DA81EF42A2",
+      "v1_signature_identity_token_hmac_key": "F556B76B0E1EE952A87246820380A53ADFC7928AE9A497F3CDEDCC72CB15F2F3",
+      "v1_signature_section_aes_key": "419C27C2E8899D21456C8586BB234079"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "218D",
+      "expanded_salt": "05D34EDFC849A67164C828BBDC95248B"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "0F1F6DDCDD98D829428F0551",
-      "extended_signed_metadata_key_hmac_key": "6BDD30B0682F92D8E01A1483C3B21887F0053B3E99435388C6DD41037EB51AEB",
-      "extended_signed_section_aes_key": "3F4604AD3B2C106C10BA72C3ECE5AE49",
-      "extended_unsigned_metadata_key_hmac_key": "874627B363F91A534891A9285E0DF0EDD15DC79E3E48A165B0984D4521ACAD7B",
-      "extended_unsigned_section_aes_key": "B103A43F35DC81696985B85943FC9F7C",
-      "extended_unsigned_section_mic_hmac_key": "4983A837EC95679DFBB7BBC1EDA6839C351059CF76E3465FCB7150E2C9DAA555",
-      "key_seed": "67E041F864141E3F2D046910D9F966739B5C393ACC320E68C5DA6017D4599DC7",
-      "legacy_ldt_key": "D8BB8E5985E39A4E64C54FCBDE74D1D5D3E27781C34AC260999F4FF0C06B0E1AE7A0E6D4099F9BF71BCEF921B24C85751314574F394891E49C8001FF56806572",
-      "legacy_metadata_key_hmac_key": "2BE0A55DA15AA8323C56AB17C946C6A4E539560BA87A85C656BD1EF2326300BF",
-      "legacy_metadata_nonce": "6E488DABE0EF2C8F5EDEE07B"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "ABFE5A0FCD29C9FAC689AF6254F6A4AF",
+      "v0_identity_token": "9C4C49A50D448BB7C98961277E16"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "246C",
-      "expanded_salt": "A04B4F4D54258A3481BC4E119A6A2B62"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "614C",
+      "short_salt_nonce": "B726D470FB3C62E5AEE6F3AB"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "9DF4EB64B8D5D2959CFD9D092BC37B17",
-      "legacy_metadata_key": "5505114A304DFA404B9258AB6AF7"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "AFBAA77C37E2F2E4C4BEBD7A7A40E8E8",
+      "derived_salt_nonce": "666C9A35AA6DA090BEE448E331D2201D",
+      "derived_salt_third_de": "89EBB1D894E43156911A709CE348F664",
+      "section_extended_salt": "606E4773F79F85943AD21CE692D8F5BC"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "92F14175B1DFC4CE170902636E42713E",
-      "derived_salt_first_section_no_de": "4156C04BB9AE6EFA9DEC3233CD5A305A",
-      "derived_salt_first_section_third_de": "3C58FB419F0BD045E235ADC4352676DA",
-      "section_salt": "FCC5C8C0A905123991DB1963CA187342"
+    "key_seed_hkdf": {
+      "key_seed": "28F45CDB8E3C5D060AEB3B400E9CADDBE8BC5635375D1B847A0CBCF3F6959E36",
+      "v0_identity_token_hmac_key": "DA584FB27301FAAFA8DBC3A3055B7DB704AA5FA842C4BFE9DCC8402CCBD6B230",
+      "v0_ldt_key": "382BE0817D6EFE8CEFFE2AC75029ED0FB52EA917CD74DEC4146FDCEC8AAB0E40B9D7E540D4E6808C4FB6D3F7A4470D9E9C276746A9BD0E7B7A64D5E7483EE281",
+      "v0_metadata_nonce": "4250F2C00B4540833D26C980",
+      "v1_metadata_nonce": "57EFEB1654A4C124C9B1EF4C",
+      "v1_mic_extended_salt_aes_key": "52562FE1226DFFB4025D2D34C17CABD6",
+      "v1_mic_extended_salt_identity_token_hmac_key": "63DCF0E160424B71FA8769F159F20224F2C36DB2C285EA191EFE891784526290",
+      "v1_mic_extended_salt_mic_hmac_key": "1EF4654DD267EF8ABF0C7B6401F0A108AB7FF39D11593C1CB836C1345312F92B",
+      "v1_mic_short_salt_aes_key": "F7906C1B9382A8AA9E30D58ED937126F",
+      "v1_mic_short_salt_identity_token_hmac_key": "F26237F030DABB04BCCDAB8B927C86C6C5853C2608C39FC574283140E7CD3D65",
+      "v1_mic_short_salt_mic_hmac_key": "387BAD7E4C92B13271493360484B2AD24C8C1DE146CB4AAFD8CB2F9046E57058",
+      "v1_signature_identity_token_hmac_key": "E31153728A02DCA8CA2D2F697F0772E77A92C4483B7CB99E61542B79FCDDA123",
+      "v1_signature_section_aes_key": "AA93AC9530BC4E822430C4A79A898D03"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "2914",
+      "expanded_salt": "8AD51A4D7D8C14145E398607CAFD6008"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "E6CB1D645A1C322DD231C530",
-      "extended_signed_metadata_key_hmac_key": "6541D917FFD4EBE53C09BF098B18DADAAA8DFF5ACBB1356BB3E1439EAE91E399",
-      "extended_signed_section_aes_key": "A5C69E0BB040A4ED7AEA8D202D1AC1B6",
-      "extended_unsigned_metadata_key_hmac_key": "0F3C39ABFB77F95C5B015829511A821DC453306953B33D12A1D0BA5F2E48F3DE",
-      "extended_unsigned_section_aes_key": "F73B279C87F7D8145D53BCE3FBE49FA2",
-      "extended_unsigned_section_mic_hmac_key": "C386709CED7BF9873F36D764C601B3DB06FFF096993FE59E2C2E7A2E06C83ECE",
-      "key_seed": "66BD6221C1E30871D422B67F060C9E22800A834DF098843BFC648B76E54ED908",
-      "legacy_ldt_key": "79204103FD0DC36158434265D60D9D4A7CDF2569D1A855CC1C0A5616298C41E07A25577E028C13A4C517D2BEBCFC6F2198710BE7573B7C0C70E73BFF28C9710F",
-      "legacy_metadata_key_hmac_key": "FDE5E6233F81A1DA995A94969207DA3CF835C672B2C4A5F65D36AE45B2A5D2A1",
-      "legacy_metadata_nonce": "57EA5DBE1D8D34591922A866"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "4D70AC76CAF34E091C8D4A6C59C7E2C6",
+      "v0_identity_token": "DEDA6A2C50578DF5DC97C211B9D4"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "AE61",
-      "expanded_salt": "EE04115499CE6C19FB6BB2610257A5FC"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "9051",
+      "short_salt_nonce": "9B0F55835CCBB056C630C3D6"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "45FE6BCBC3E86F2B10458F44DBC73647",
-      "legacy_metadata_key": "D9B336A4E52CB722943FFD9CF784"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "5B7A957680DBE5A18F6D47490EBF2839",
+      "derived_salt_nonce": "FA772F27245390D3125821597FB8D0F9",
+      "derived_salt_third_de": "62B187FEFED14F6E4B292E06BC94A19B",
+      "section_extended_salt": "157D4AD5BC9A70486F2E15EFFBA52500"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "6919D48644FFCB351097E0A7F63BAD7C",
-      "derived_salt_first_section_no_de": "B40EEC7CA7711C443BC6131AE1209711",
-      "derived_salt_first_section_third_de": "EE87CC5CE451FACAC62DD63C70F53BB3",
-      "section_salt": "E9C2AB1A142AAD88D5B3E8ECE861F46C"
+    "key_seed_hkdf": {
+      "key_seed": "39518BAA6A95173EE46FF390762FCB1F60E9B6A2B8C44DF4133289F2984E1033",
+      "v0_identity_token_hmac_key": "964C1DFEA2AAB348F63FA5CEFA9D994DE2C16C5F0EE6D684F2144436331055E6",
+      "v0_ldt_key": "697FE42DCA3F0390137D789BA209E5D2D31EEA10CDB038D2D17445738293359543EE36DAFDCC214738B0C4976144CAF1C885CB26C2C749CACEC8E0A2B9B558A3",
+      "v0_metadata_nonce": "0D22B7BD15D4EDB6CBFD899F",
+      "v1_metadata_nonce": "BF4DCF73602F6ED1AC6E3B0A",
+      "v1_mic_extended_salt_aes_key": "DDF87B5AD37C73133F051E19D73958BE",
+      "v1_mic_extended_salt_identity_token_hmac_key": "8C1943E117152BD3550B99EEF815EF9B533501B7CEA6277BF6BEE4A3A76DC7F9",
+      "v1_mic_extended_salt_mic_hmac_key": "5CE6F847C64DEAA632CBDA32769F18763E757E80B1746AAB191F638E0833C32E",
+      "v1_mic_short_salt_aes_key": "0544AFBF786E3F4C01EC50AB2AD04DA2",
+      "v1_mic_short_salt_identity_token_hmac_key": "A1DB27DB83EE9FD2C756C67CC8FB993286CDAA5895E7C3012E540F32D46EC046",
+      "v1_mic_short_salt_mic_hmac_key": "000B1436329CFB45BFAEF8BD5A0DB872D2951E54FF56BEAEFE4063407BB02F5C",
+      "v1_signature_identity_token_hmac_key": "A768835197F34ED548D340389920D4128507BE77BC0E13556EF41E95C34CEC3F",
+      "v1_signature_section_aes_key": "D23318460ABBDFF8B79B7B7977564BCB"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "1F7415759E6AF70ED5ECFB8B",
-      "extended_signed_metadata_key_hmac_key": "4C52B8B27F4405B995784DA1A19800280D7790F02E1A4ED718AFF3B9D2CFB632",
-      "extended_signed_section_aes_key": "E39A23C3DE620D184AD6717B9441B453",
-      "extended_unsigned_metadata_key_hmac_key": "D97C790467145FC66B8962922BE7163D01E72398E58A7381E94E52205A99C49B",
-      "extended_unsigned_section_aes_key": "FB9F25B845153CD75A97C8FCDA32A6B9",
-      "extended_unsigned_section_mic_hmac_key": "7F291513A23A7AA803A1CD8DBA7A9DE24180EE1580B31E3AE689EF51851EF266",
-      "key_seed": "C76E4012BA6DAE4B2CC7DE0FD6A60790E7E69183D4C894EEB2C14F55F691D200",
-      "legacy_ldt_key": "02D0233B56EB07765A584139A7791E9E338698C5C16A32B89973A921DCE26B483E7322D354F8D597A48A67818E7697686C9416CE10A2FD5B5A153A80EF0C3DD7",
-      "legacy_metadata_key_hmac_key": "D3BFBF4AEF2B0EAA29B8C0A4A5A8E140C9861C55AE77939A9100863458F73A48",
-      "legacy_metadata_nonce": "3DB25905270EA45BDE8B2C78"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "5EC8",
+      "expanded_salt": "DBB2415837D9360B31C4C27B45D98643"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "48D5A14CE83B030F33DE6D9767913D1E",
+      "v0_identity_token": "5A4832D02E177FF842083118FFC5"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "C0DC",
-      "expanded_salt": "51AFDF06C286189BE2A38DF2AC5C157F"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "3F81",
+      "short_salt_nonce": "0D5A7220B52241C711F8A7B7"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "DBFD6CB8BEF948E30034942C217AB49F",
-      "legacy_metadata_key": "A6572C02A89BFC53CE24AFE726A5"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "7A4EC90B70FA9ACC4A37F691C8A62363",
+      "derived_salt_nonce": "90646E267796BB4D06D9DA6332CFC0B8",
+      "derived_salt_third_de": "C76CC4B946155D059264B51412240813",
+      "section_extended_salt": "8B7B2570D21C35A39687CB7725B1D8DC"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "F0C2D07232D5E955E8C82CD835B56E1C",
-      "derived_salt_first_section_no_de": "1F34BDC3807F158859A6FACC33B76BA6",
-      "derived_salt_first_section_third_de": "8B547F100FE1EAD817E8DD7AC7FA6D7D",
-      "section_salt": "E887AFFFEBC6CAEA000CD7B2843AD21E"
+    "key_seed_hkdf": {
+      "key_seed": "AA2716AF54865437571682DC3C6FE92AA8722F322CA961BBBC7459CE841F960E",
+      "v0_identity_token_hmac_key": "4AD019A61785FEB251708EE927933E062B348CCA84968642EE881FD2D7B5B69B",
+      "v0_ldt_key": "BC65180931BE080985DE03CB0FA3604413E3660D30399A600F5A723A97FFBB64FAE25464CEE6D153A4572EF1F8AE483C95E1159CDFDB9BC338C29D0F7E28B1FF",
+      "v0_metadata_nonce": "262FEDCEAB01B2888C2F284E",
+      "v1_metadata_nonce": "D948EED446410C58F2B5473E",
+      "v1_mic_extended_salt_aes_key": "4D9FB04B417FE0C515441BBB5A24D4DD",
+      "v1_mic_extended_salt_identity_token_hmac_key": "B8CACDDA4B647401AFFA0E64C79A3D60E321CAF15EE406665740F9C4DD5CD6AB",
+      "v1_mic_extended_salt_mic_hmac_key": "D6BF2E0C532A2297799A495F52F5830C47E8D121B07A7600A12ED6DBB6CD1167",
+      "v1_mic_short_salt_aes_key": "8C05F37E25BBE33BFAD40E3BFFB4EFC1",
+      "v1_mic_short_salt_identity_token_hmac_key": "5E3DC31AF514DBE21286E86009435844BD96CC31F501B48E0CDCFCE0032D42AA",
+      "v1_mic_short_salt_mic_hmac_key": "41C70F6FE4E862553196A69B4B66E32B15384159A6865464EB50824E1AC399DB",
+      "v1_signature_identity_token_hmac_key": "7885A9B7E84FB130334AD9AB8BFF1643B3D33D68DE2CD208977656EB5CADA2EE",
+      "v1_signature_section_aes_key": "D559EC95E7D9D905C43582B528204FE6"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "D1C8",
+      "expanded_salt": "ECCA8A1F9E8E75DCF570C5D713300889"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "817052592ED2AC889E53F3DB",
-      "extended_signed_metadata_key_hmac_key": "ED836D0F732D1051DD4E7DAE505FFB670CA6717F7BC287E0C8957EEAA6AAB9EC",
-      "extended_signed_section_aes_key": "03D31EED691F220ED807A0A3699D01E5",
-      "extended_unsigned_metadata_key_hmac_key": "0C304D6EA35B6602646FC0E6951EFF09249712E42E8AA6BD995666AC29E774B6",
-      "extended_unsigned_section_aes_key": "8507DF1A8D34390EE03C10C29C16E20C",
-      "extended_unsigned_section_mic_hmac_key": "857FA3CEE51F9018367961FF82F30041D43DB1F1916BF067396EE7DEEE08B063",
-      "key_seed": "F7E47754ED974913367ECC4B4DE2B1E66D9C8EF36BC36875111231A421A1F892",
-      "legacy_ldt_key": "DF739460333EED047E4700C6D43763208D6F7B8994CD36AFA6C3B725124BD68F50A5DF542D16780F8FAA64C919F4E1321752C2473BF44843012F49CCD2BDC43F",
-      "legacy_metadata_key_hmac_key": "7BC61FACD5C01C6F0B24B38004BD8CC5BD00215D9270D8621919266CFC7CC1F5",
-      "legacy_metadata_nonce": "23DE6EA7BB73D686CA04432D"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "4A7142998A8FC059C0EAFB18E6939785",
+      "v0_identity_token": "BFB0CE297F64B3AB7557113C54AF"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "FD97",
-      "expanded_salt": "AB621F612EC9F1DBF995C1CE7610A3F4"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "EE43",
+      "short_salt_nonce": "FC424C642B26FDAE820AF1AB"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "7E59495C2E31ACF37A8A82914D67A3C2",
-      "legacy_metadata_key": "FD95D8D7F1234F29C61B73586E50"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "F352BAC3D04AAB1663A78964ACD27F3D",
+      "derived_salt_nonce": "BEB93BCF36574D428781A04841770194",
+      "derived_salt_third_de": "21E4F9FB37F092DD846235297297FBED",
+      "section_extended_salt": "A20F1A436E3CD07AF356935EA412F986"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "B1FFFDB5149EB2C1E55818C9AC278F22",
-      "derived_salt_first_section_no_de": "9FF7BA7CA2D7A64907CAD503C9D9E931",
-      "derived_salt_first_section_third_de": "E05CE47FEAAB471BDAAEEFE326144F87",
-      "section_salt": "05FD6AD5FB6ECEF67187AB22628D83F5"
+    "key_seed_hkdf": {
+      "key_seed": "EE6C12A309C3D379106949857A2CB8736DBF31E30D519B8CA103DE0C67D702BB",
+      "v0_identity_token_hmac_key": "FC9E1DCF166C57CEA1D88205FA013353A8A5C92919224E4CC4C05E4EFD81BF78",
+      "v0_ldt_key": "7898406C1F2E86046C179E417C18B2C78414A95C5E1993271CB5E731F13D0A05B8F31E71A81B9A0A323C4841EACEAEFBE026CF0BEA6DE5661A2B671E4F89F786",
+      "v0_metadata_nonce": "4DB6DBED980D2BE700FC04FC",
+      "v1_metadata_nonce": "78FC55E3F54D576A0EA7DFE6",
+      "v1_mic_extended_salt_aes_key": "45C0F33938366922940EFE7E7097C261",
+      "v1_mic_extended_salt_identity_token_hmac_key": "7697143041920B2E9561C1414E72CC0E55FE65C46E13952EA0BECD995799D52B",
+      "v1_mic_extended_salt_mic_hmac_key": "DD85470E8F3EDAF23FE25157B8FBD1A7C241E7B1C31EC2A198F345D750D57A32",
+      "v1_mic_short_salt_aes_key": "88A8BC6F73692B9E4BA3D932F34A1C12",
+      "v1_mic_short_salt_identity_token_hmac_key": "06E05118607B678C75BE26876B469D29C7E039EA5BF642B2339CA4D30EFF432F",
+      "v1_mic_short_salt_mic_hmac_key": "B24E520B572C5A64C652D5F7E95A58FD937569779213CD79DD435503B05F8678",
+      "v1_signature_identity_token_hmac_key": "F75DDF545736C151BD76F0AF6D952DF525D7F53F0393F42346B6DE51D410F6F8",
+      "v1_signature_section_aes_key": "972451D672CCDB9E7CA5D68C7CA291CD"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "AA55",
+      "expanded_salt": "A2A7A96222BBB587E385B4B0438837C3"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "656ECAFAD9D02F5C53D1499C",
-      "extended_signed_metadata_key_hmac_key": "3C07733D5EADB798C0F0A085711E7980D5437BC8D9003F2E6D32EB1E997D8D08",
-      "extended_signed_section_aes_key": "D49EF1EFFE0236EA082EF79C6BEF496E",
-      "extended_unsigned_metadata_key_hmac_key": "BBAF91442DABA83EAEA9AA3C6312972672AB20FE962605FDD01FD709F832D562",
-      "extended_unsigned_section_aes_key": "D472A600CD6F77F233D6274917328EDE",
-      "extended_unsigned_section_mic_hmac_key": "62A45AFFBCAE257E682BCB364B77F8FFFEF9088BB8DE260FAA6E622088E44A43",
-      "key_seed": "744EE5E68214D686A678E2F964A33C054EBF0155354CEEEC0EE77448F6E156F1",
-      "legacy_ldt_key": "A3F256081813BAEF903C9DC155AAAA8F7D8443726E1DB42695632D36BA9932E0A70D568A10CC1631D1CCB86AABA44B82CE08A65EB31C466C4E41728A11429BA0",
-      "legacy_metadata_key_hmac_key": "33EADD4558505A6805581DC7E514C624670C824B489D63F54500E7AE348184E4",
-      "legacy_metadata_nonce": "33283F73B1E3EBA32C5D862D"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "D181D62BE2814F8A4515CEE9F98CB16D",
+      "v0_identity_token": "82AAD91558602B7E975108E4C1A8"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "5987",
-      "expanded_salt": "BDA17AF3C8D8E8E7627742B3AB2075B6"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "1692",
+      "short_salt_nonce": "41FC0F8BC4B6ADD56E767CA0"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "F9BFA683C1B856991A2C5CA893D71050",
-      "legacy_metadata_key": "DCE4D724E5161B46AC54E4C18A08"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "8CA3B5B9AD5A244C7C81A4CECDFBA881",
+      "derived_salt_nonce": "B19D427E9CB92F7F2D1F433BBFC1C484",
+      "derived_salt_third_de": "5EE54CE1EE6359617F847ACF515237C4",
+      "section_extended_salt": "05FE726BE045EDBE53C924C94DCC01FF"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "DB22BC19A27CCC15E51A0A1B950857F7",
-      "derived_salt_first_section_no_de": "547CC56DC0A5507125082C68C053D5D5",
-      "derived_salt_first_section_third_de": "DFBAFDA1CF3AA59B26FE011EA450B823",
-      "section_salt": "078A2C255BB03030F52965D13B0AA53D"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "1020F8C375AC5BF5D3DAC380",
-      "extended_signed_metadata_key_hmac_key": "98F6FD73D248D52E1338E6F37EF4BE53160EB68C57687BBD5F4C4AE7F2C5A6CD",
-      "extended_signed_section_aes_key": "63B0CB478D4E5F38F454521A785B5DA1",
-      "extended_unsigned_metadata_key_hmac_key": "77A743C542941356A1DB8D8F3031439401B82F1B7A6C5BDA68152C1232603CE9",
-      "extended_unsigned_section_aes_key": "B743EB7D63164D30EEA5F452F4ECB6EF",
-      "extended_unsigned_section_mic_hmac_key": "F34F5FED159362C0CFCE08702216F77B7BC9BD6739D7B7A6BE022F69E119A011",
-      "key_seed": "8D8B909F903746E317D2FAD7B81C44E14C2BC911BF57505FD21E657BDE7B9448",
-      "legacy_ldt_key": "B95B352CC7A616CFCEF94F140DB1FFF6C96699456B53E22EF2A92C189C8ACF71B15996E5592651FDB0B9EF482E3D3A77F69C43381BFA3FBEB77EF3FF3508C4F6",
-      "legacy_metadata_key_hmac_key": "0FA59F92E4D45EA6F7CE221654D40FD7F04723C271B02CF2E6FE7D2F38B1F9C8",
-      "legacy_metadata_nonce": "B8CE49F04135E2EFADD82878"
+      "key_seed": "04CB72E32403D848909CD752FB1728D044B0B922E11933AF485BA6667BDA8095",
+      "v0_identity_token_hmac_key": "AE509065FAB5D2F92D5879A79367774D0DD1E299592700BAEEFF59BB38EF0F7C",
+      "v0_ldt_key": "EB2A03CFB458D8EFDC34F5A1C295CE89A7E4C16EB0A310EB29FFB22C058BC8EDC12679CBB59184A1DAC8914A3266B7DA4A9A18DBC463DDF0F1188E6A1D937317",
+      "v0_metadata_nonce": "F8C6BD75CBFD3563E67C738A",
+      "v1_metadata_nonce": "868F99953CEB467B28C252A1",
+      "v1_mic_extended_salt_aes_key": "8DCD6EB583FC8D6A09D0A6E4887C0CB7",
+      "v1_mic_extended_salt_identity_token_hmac_key": "D379FCA209D85EE044A08DC891E4F0E54A46346D5E6E47B28C213ADF81246750",
+      "v1_mic_extended_salt_mic_hmac_key": "F7F9A514455AE831234376BDFBC64A3639D7E2C4274851EBCB7EFB14D3392FD7",
+      "v1_mic_short_salt_aes_key": "2385AEC92C92345F560C4D292D3C4C98",
+      "v1_mic_short_salt_identity_token_hmac_key": "45B7159DC874A72B0F232DB37EEABFC00B801453BBE78B57A5C08B2FB8AE626A",
+      "v1_mic_short_salt_mic_hmac_key": "0352B8CEE18F5D2A75B5D055806611BEF436A2F2172447236E6C929B41C00FF5",
+      "v1_signature_identity_token_hmac_key": "789B7C2BBEA40F1E9D8C35CA4F6A80734F4A0EC11973F49EEA988DAC0EF10877",
+      "v1_signature_section_aes_key": "9AE1BC3E1532FC968486276BE96AEB6C"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "E04C",
-      "expanded_salt": "C09ED0239B32A0C2BE3A0EE440CCBE42"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "0733",
+      "expanded_salt": "5F072B4BB16C2439CF35176A4E208D43"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "DAE265F7391FF6871E5BF1A60EE33F1B",
+      "v0_identity_token": "29E46351DACECB436710E7BE3CFF"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "89B6",
+      "short_salt_nonce": "4B223F4BE10514D1B8407002"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "3FEE720404CE1EA1B1D761262F6E2C09",
-      "legacy_metadata_key": "5D81EB7355CC5ED9A8979ACB3DC3"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "4BF6F87B0CD12A0AE66AB6C80C976E33",
+      "derived_salt_nonce": "3AFAF9158C6986EC11F22D14ECD21D29",
+      "derived_salt_third_de": "EF28C13335BB6321FAB46EF2A859DD58",
+      "section_extended_salt": "0DB9FC8CF99C093927DFE9CC092C008D"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "0062A464AD7B46EC51A8083B2F34410E",
-      "derived_salt_first_section_no_de": "08E8417B389B48DF971A62A738A28A1A",
-      "derived_salt_first_section_third_de": "A475206446E694B5751C5E3CAD225454",
-      "section_salt": "F2F39B46ED2763C2D2471485E6EAE97F"
+    "key_seed_hkdf": {
+      "key_seed": "278BA457B88F328447ACC1E6A4E14BE7A62A44CABF28330D6DC044050DF55085",
+      "v0_identity_token_hmac_key": "6503711E003223721BC7A3412B7387FA2BBA963EB581D1D60B5A17BA0E7C4B8E",
+      "v0_ldt_key": "B6FC809AA5F0DBD46A75F6639594D74B5831293824AB4A9922997A3B3A1E6B12111084BE521B3A486E16DBE1AC0FCB346AE16FEC6B80E3560419AA616CE4984F",
+      "v0_metadata_nonce": "67CABDAF41792AED8A72A868",
+      "v1_metadata_nonce": "8B6B23B4E69766931BE22DEC",
+      "v1_mic_extended_salt_aes_key": "9C86645E9BCE74A1EC73AADCD665154B",
+      "v1_mic_extended_salt_identity_token_hmac_key": "1632B567AC6144C41081C91C3F5BAD0B560F59602E39FD0B6207BB5DECF7D734",
+      "v1_mic_extended_salt_mic_hmac_key": "8CB11E1B229A46BF2620483B959201465A44A31F6FB9E675539B7F27439C8B0E",
+      "v1_mic_short_salt_aes_key": "2C9851D5CD39BB2E1FEC593F79F23085",
+      "v1_mic_short_salt_identity_token_hmac_key": "70315F0D044A3F478184A76892BB4204A5CC1E4542CDA99595196323C0770B31",
+      "v1_mic_short_salt_mic_hmac_key": "FCAD5AB2AF31EC2C2649B9DEFFCCA9865B74E332E81474E8FBD7356D33F88DE6",
+      "v1_signature_identity_token_hmac_key": "80F9B9285D8DBF0666839F92E2D2752EF249FCB127503BFF41A629DFFAA1CB45",
+      "v1_signature_section_aes_key": "B519FDA43EE7B072ACB8A855A0820016"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "0F09",
+      "expanded_salt": "8ADD04F894BD7DED3AA47EC0A5AA2F98"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "9D465DB8703FD644B14B99E1",
-      "extended_signed_metadata_key_hmac_key": "14BA9DEE1626741059F8B6D426CF83FCE010C26124E5AC9D66925D3B89EE2BB1",
-      "extended_signed_section_aes_key": "591995C12F75A6D28673E9BA651BF099",
-      "extended_unsigned_metadata_key_hmac_key": "7BCCE36CAE2D834C397513EA6C8F90026A1108A856285F9106B38319EFC9FF9C",
-      "extended_unsigned_section_aes_key": "A5A156C9EC17E50D3D4D5DABB63CCBA7",
-      "extended_unsigned_section_mic_hmac_key": "EBCEE2B687E58DD608CA3BE824C5465BB325FF602E30DAEBB2F2544DAABEF580",
-      "key_seed": "6EF7F1684C5C353B4ECDB1EEFDC10C34CF78B9E60D067EC7DB769595F8914CD8",
-      "legacy_ldt_key": "8251C9BC73D421AE9345E27335D84A03BB706EE9A2ED41A90D3E7BE66B3EEE9D98ADDF2958DD85B21FF1BF5CB06DD1B7180781BC89EA0AC6B31E0DFA8B6F49BA",
-      "legacy_metadata_key_hmac_key": "177B5056A2BF6ADE707EC448CE3825FC7F4EEFBF6118F58BAFB9504B461FB3C0",
-      "legacy_metadata_nonce": "989E60F047E8D4BD15075FDA"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "CFEBF4005115348238072AB6881A36EA",
+      "v0_identity_token": "818BBE9BBAD25F7622319DA42806"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "0E4A",
-      "expanded_salt": "45841BEF518D55BC872449D35B69D3D3"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "EE7F",
+      "short_salt_nonce": "C2DA1EC8766C69498CB6EB47"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "07BE6AE936B1ED68B3823A6893702467",
-      "legacy_metadata_key": "0C15537A85CACDC26F26019ED439"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "E732FDC269D45C4213635D2D35B4A8C1",
+      "derived_salt_nonce": "FF25222D3D493186CBEA2C203733BA86",
+      "derived_salt_third_de": "02BD9E3B04444BF78D059BB4336D1D14",
+      "section_extended_salt": "BF79E23A91CD05105EDB13CCE3CB6B16"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "3DE9D56F37147C866D25DF4D1EC6E854",
-      "derived_salt_first_section_no_de": "2FDE5B60AFED59C1CF366A514FBBACA3",
-      "derived_salt_first_section_third_de": "AF10E5F3A5DF61DE19420E75E55D12BD",
-      "section_salt": "ED4025D9A2CBE10CAA5B8A1ABA910BE4"
+    "key_seed_hkdf": {
+      "key_seed": "477C882082527F23F504FBEFD21A679EF0409EA198BC392C5113DEA2755DF924",
+      "v0_identity_token_hmac_key": "196C59DDFB9139C0DDBC7FAB56AA600DFDA39EFE683DB66529002A7E383EFC64",
+      "v0_ldt_key": "A1F7AFF1BC0D85F14DB34C40A82A0DBD75179F7F164958602E6859D281A872EB7C979F1D09C29D1D5F0C89805473B7FD28B5269E52B220AFFAE81C77619FFC7A",
+      "v0_metadata_nonce": "FA10FEE70CFECEF59CC0E0E0",
+      "v1_metadata_nonce": "1E02CDDF1094116A322052F1",
+      "v1_mic_extended_salt_aes_key": "3FC29BAF9D68FBF0312A6642360E7BD1",
+      "v1_mic_extended_salt_identity_token_hmac_key": "C4F0AEC099DEEB16FCAA31731CE8C3C4B480EADEE65EA59DADA666BF558DD945",
+      "v1_mic_extended_salt_mic_hmac_key": "73494C1A3BF1D355985E61D009736941FFB39A3251D915D7EB3E555E887117BA",
+      "v1_mic_short_salt_aes_key": "622187A9DC8AF81E033888DDFE504F77",
+      "v1_mic_short_salt_identity_token_hmac_key": "0BD43C55C16C95BDA2A6DFF39A9FCAE21CDF87DE842C35694698B2B53D1FDFAE",
+      "v1_mic_short_salt_mic_hmac_key": "9900746A6567048FAC292563E40333B948F5D3EDA89BB8D8E5B159441D5EECE6",
+      "v1_signature_identity_token_hmac_key": "9A2793927FD494FB1AFB2EB7F7B91319A231EC88B1A22B3E42293FC1EE81E388",
+      "v1_signature_section_aes_key": "0F5A12959E5210EC994C97724D3E2A96"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "2B8A",
+      "expanded_salt": "F4FA10F6983E9EC4A7F916E5FE7191FD"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "6F59318B12B620600E40892E",
-      "extended_signed_metadata_key_hmac_key": "383DEB0C8AA51D119AF01606930A19F42C253BA8AB6A6CA730F7F0DA67F6526B",
-      "extended_signed_section_aes_key": "8E2158CCE463C86814C3A923EB66BAAC",
-      "extended_unsigned_metadata_key_hmac_key": "551F2045C860E1A7B7B0E190ABB6A51447E9DBE9009AFC46FECD25331E4AA414",
-      "extended_unsigned_section_aes_key": "8AEF9D42C27067B232BEA8242FE93F3D",
-      "extended_unsigned_section_mic_hmac_key": "788130EA49F3BC5C2D9FEBA9C9758A4B268A0FB16C2B844A4F28E03E7549D602",
-      "key_seed": "0C692A79213891396CD3B92C3FAC2DA33EB0491272E59411E1CCBAD01F1CC663",
-      "legacy_ldt_key": "AEBD9A429EF5E38A23C12F825C269950D231BBC7548115E198C7BE64B096B6FA6C11CB120803241FFDE8A78BACF27CE5545DFE3E896F598CB5EA715115B9EE56",
-      "legacy_metadata_key_hmac_key": "D68DC293B640E8E6E0E60DAA01B744FB59FC2EF5868D8856EBDB39772A510E5A",
-      "legacy_metadata_nonce": "EC688179F89D775E64394EC6"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "427F95E0A764088384200A097E6AC5CF",
+      "v0_identity_token": "FC6827BE9D955A04B5D4DF33849C"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "BD8B",
-      "expanded_salt": "F3078BA520F2080B927ADF89CF273A18"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "8EBE",
+      "short_salt_nonce": "2183C1252F128A480A53CCDF"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "DDACF0C5D556F78DE46253E77A85ACFF",
-      "legacy_metadata_key": "9732F406F870342BEE8B0A914078"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "1033FF7BE57DFD280D3F1AAF407831C0",
+      "derived_salt_nonce": "E6D9C880D2182F448A6615F7FE10BE69",
+      "derived_salt_third_de": "63A9C49B9F470EA354637612725729A0",
+      "section_extended_salt": "9AFB8618127F357D0060618E781CA599"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "EB86DD1685C8017EAACF542B7A32C154",
-      "derived_salt_first_section_no_de": "2E1D91452B6BE755CA6D1A510B786F95",
-      "derived_salt_first_section_third_de": "3F6795CB4E05C03486FBAD12D98543FF",
-      "section_salt": "700111649D792ABCC1E004978C637E0E"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "E3A2F71B15300A7232927D5B",
-      "extended_signed_metadata_key_hmac_key": "688536A171F65ED7AF6DBE841968025720997891374D0E30EAD96C2DBA1C6CD8",
-      "extended_signed_section_aes_key": "72B7CD1147CAD43C239D0637C9192967",
-      "extended_unsigned_metadata_key_hmac_key": "22EFB24D7DEE84A07643AFF3D11A0EF56D818466C43882E3170027DE0CBEF920",
-      "extended_unsigned_section_aes_key": "C38BC169874FB5551DEA99A853A3242C",
-      "extended_unsigned_section_mic_hmac_key": "28F8743A05F77BB067BE29BB714EF5208532383125EB35317F6C9B464E65159B",
-      "key_seed": "8062D40BA07D2331C15DB4F4992D45A4C500350EB2B4DE00BF5EBBCE396A96E7",
-      "legacy_ldt_key": "4ABC6C92EEAAA16D277812A851D8A1334846D9FDA1DCCAEEA24927F74765F37C48FB6297F88982234DA5B576A6FA5EA69D11972104350CC3CD20932799E3EFDC",
-      "legacy_metadata_key_hmac_key": "30121B3A3B523BCD071DECE2F469E96D24EBD5B83CA941EAF7F038010A70A1B1",
-      "legacy_metadata_nonce": "873248D5319D71CF756CD8B6"
+      "key_seed": "CCB8E6B8AD20D8B150E8FC97A098B6DDD7C1997701EA879CA64182B22FA55503",
+      "v0_identity_token_hmac_key": "E1BE3B3F12B2A2182B49983B8068BA6FA9FB3DE509D671816BBE008523818F8D",
+      "v0_ldt_key": "0F2B30FB50E701F0DB08D85F0CD7328E577CFF43EDB6B223928CB2258BCF1E394E58FECB9596D2AC6B8D43947C4D175BEB95548F21301E1F1F733934393A7360",
+      "v0_metadata_nonce": "942ADD1379EE90A6DED3B65D",
+      "v1_metadata_nonce": "4CEE030F946DF65BBB885DDE",
+      "v1_mic_extended_salt_aes_key": "A1BED5A13B44D10460EEAFB4412EE475",
+      "v1_mic_extended_salt_identity_token_hmac_key": "19675B5C73515ED298ECA658901F187D321A83C935A15C4D4A4844DC48BF077F",
+      "v1_mic_extended_salt_mic_hmac_key": "5D193196A34E4437DB7034795266C7C62DA3605D9F5818C2D433292E610A3762",
+      "v1_mic_short_salt_aes_key": "B29FFF8F001C4726749A2FE7047A82D1",
+      "v1_mic_short_salt_identity_token_hmac_key": "F1A90E829ED67B49C445D7DEBDAFC88E3BCF1974F0ED1EB9643730BF6EB3B90D",
+      "v1_mic_short_salt_mic_hmac_key": "BAB6DA7601191FDEB4344FAA163E818C9BF9B1D5B0AB1EE8D9F4A1C1D30A86E7",
+      "v1_signature_identity_token_hmac_key": "8C6832DB6955803AB934B98378B7F81F3710C65947A8B2C6132525B163F62CD6",
+      "v1_signature_section_aes_key": "8916CE7332C1F924DCC17E68D999EE7D"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "2D7F",
-      "expanded_salt": "3C48F27B1F13EE4BD87F817785EF7703"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "C270",
+      "expanded_salt": "3CD8235D88B0B4D1A0236DCCD0F5629F"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "8DBCA7616260D0E347D3984DCAD03372",
+      "v0_identity_token": "D02797D4E963A7B9806E5EFE0FA0"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "C61C",
+      "short_salt_nonce": "7803F0E069FF172CCF47AA05"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "50979493AC77E7C48BABD922BBBF9775",
-      "legacy_metadata_key": "EC8D649AF82B50E56F7E5F2C1426"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "6D93C32275239B6A82508FEFDFEC112A",
+      "derived_salt_nonce": "D2C45BD5079FE6B2D69E4F5EA192CE07",
+      "derived_salt_third_de": "2CF3FE0C1E6689361B50D2B558FCA09B",
+      "section_extended_salt": "23FF70E4526E9961792744D2C7534BFE"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "D262A02E3A58DF486E345BEC4A2B5D4F",
-      "derived_salt_first_section_no_de": "9E106096FF44973E527F2F7CBBB68275",
-      "derived_salt_first_section_third_de": "F9B9F4D9CBCB69C7A00DA254229D4BF3",
-      "section_salt": "4496838ABE23AA21C0CA736114A37BD6"
+    "key_seed_hkdf": {
+      "key_seed": "9617CCA86C2DE55751298C2F0652DA17D9C461672AF0B25F4440ED17F9A17768",
+      "v0_identity_token_hmac_key": "C4FF34211E8D5C43FBAF8D6653A8AF5FA760F13459000F70082B9D143F3975BD",
+      "v0_ldt_key": "9A118827A019C5EFD3FC97CCC3B10F74C15D56126669EABE07317EC7A02B7B0BF2021383D3F48DDAFC1AEEF5AC930304BE2C85128AE2DF0F6F679B8D48E5CCCD",
+      "v0_metadata_nonce": "A9A2B6059169FBBB81991A3D",
+      "v1_metadata_nonce": "F6B42D5761F30BA937AB8B7C",
+      "v1_mic_extended_salt_aes_key": "328DB3BB74879515324035839561552B",
+      "v1_mic_extended_salt_identity_token_hmac_key": "36243F4E25B1548560477EF30CEB9A785C49D244832564BA262596735ED2DC1D",
+      "v1_mic_extended_salt_mic_hmac_key": "693EB813C40F33650699205BD038EC0D09989F8BA0A6B50741EC8FE1211DFDFE",
+      "v1_mic_short_salt_aes_key": "ADCB2F8D667D5DBABAACD28F72C2E11D",
+      "v1_mic_short_salt_identity_token_hmac_key": "0501458FF851D1C3C83C1BB65F55495E75C24778AA9B8FF0488FFB7F119B574E",
+      "v1_mic_short_salt_mic_hmac_key": "2ABF2E7A68FC09E88AC1122710BC7DD9F57FB8E150F699D5DC0DA5A560C76044",
+      "v1_signature_identity_token_hmac_key": "A267DB5025D3F87D12966C3853FB9449AE1FC4B654428CFAD44AB3AF6020D572",
+      "v1_signature_section_aes_key": "9E6F27E4E0118B8507146871CCF84E2B"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "81AB",
+      "expanded_salt": "F19E33C05E6D989BAB7EDE336BDC01BC"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "786176FCFF3231D1572CA808",
-      "extended_signed_metadata_key_hmac_key": "18CE44A7087E1E3FEDAA6248262F697E1BAC68F878815DDF44A330BFF19A43ED",
-      "extended_signed_section_aes_key": "AC9B94AC20F1D81E1D1DEC0684E36297",
-      "extended_unsigned_metadata_key_hmac_key": "47C8F715E8E2E9F67B1C376E2D726A60D30D1B8033E2D78B7EA3D1DD55BA23FE",
-      "extended_unsigned_section_aes_key": "0C40C4BA4105CE4F2CBE9C8DE5F01123",
-      "extended_unsigned_section_mic_hmac_key": "6D9373A7A34B9072FA386B06725B4BEF851EEEE601907991C5D2D51500356F2E",
-      "key_seed": "EA366AAA629102AB6B62305B92BFF85C878A1E7536C2057444C989AA68370138",
-      "legacy_ldt_key": "4FEC3FF94C7E7BBB8A7ADB496EE01056B702AD1EFE5458E94FD0D9436EE976CB367F12FF19A31166D2E7541C7C71C1B3BF25E532B08D80A456A1FF04A6C79503",
-      "legacy_metadata_key_hmac_key": "EF0679E429D9B23036DBE7B8AB08AD5B02C236DC8C2D526EF3E0E840E1FAE6FE",
-      "legacy_metadata_nonce": "B90108EF65B54923459A2B9F"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "68B06A0E1A66F6B996474EE0B282718A",
+      "v0_identity_token": "F60E36A8E0605B46D6A05871C90C"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "5203",
-      "expanded_salt": "A3A47B0B38AB2899441F905548251625"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "A80C",
+      "short_salt_nonce": "8F039D3FA934BC8560918D45"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "BB0BAE62AFF2FE5A6E20B8D581A272DE",
-      "legacy_metadata_key": "DF9B54563110C3D424C36C7A0B12"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "FC34E69B7FA34B4341D2D33D5D591D46",
+      "derived_salt_nonce": "9BF73251EFD5E11020390188ADFF72C6",
+      "derived_salt_third_de": "C80634B6E96C0D7EA25C5C47F7389B01",
+      "section_extended_salt": "DF9DAD95D8384321FED7873DFC750B66"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "9794C1A08CF2F8E1E64FC53A89C81798",
-      "derived_salt_first_section_no_de": "899E09BEA7E181371FC7DB3E064B93BC",
-      "derived_salt_first_section_third_de": "BDA3B1732F5E0E7F2D0E8F3EA79402FD",
-      "section_salt": "82CF94C9A74170CDB84C8132E0A9AA25"
+    "key_seed_hkdf": {
+      "key_seed": "E465DD783C41093B0A855D11D3D65C141C01A27D0522D7049AC8D92CBEB9D232",
+      "v0_identity_token_hmac_key": "9700E177127FBFC6271A4E7ADE829E5D6BC19901DE311C66F0D5283475241C23",
+      "v0_ldt_key": "FF929D036F9D8713CCAFD13A9A8E3EB1368F904BB28C3A5B16155D440245F2F736A885306539CE9DAE5E8E1E6915738FE2300523831F2FB284A858D968ACF2EB",
+      "v0_metadata_nonce": "113FB8326CCC81FE306E3722",
+      "v1_metadata_nonce": "A1B0C6917739A95F4E36EF34",
+      "v1_mic_extended_salt_aes_key": "FD52721D70BAA377D20F679583CBB776",
+      "v1_mic_extended_salt_identity_token_hmac_key": "C384F651302B7D322F5E3F8A2694A49DF03034C0A13C0A827250DE10E9708B01",
+      "v1_mic_extended_salt_mic_hmac_key": "DD1077C4A05C4701B7EE34561CCC9D4D46C80E522414CC1053A2A3C3F918E24E",
+      "v1_mic_short_salt_aes_key": "EBDF2DEB57C82EC7633A337346495321",
+      "v1_mic_short_salt_identity_token_hmac_key": "B5A4831B58E71C5EDF3206D25CD378D2E569EC32E8E10E173C82527AF7DE59E5",
+      "v1_mic_short_salt_mic_hmac_key": "FD94C8A62DD00C251DAD6ED9DC459E1C06C2E625C24D3EA902122BE1B1EAFC13",
+      "v1_signature_identity_token_hmac_key": "40BACB42A697D9B7322FFC8196CCE5ADCA84758196F9211572C51D1E96683536",
+      "v1_signature_section_aes_key": "ECC4297E2036A147E17C48550B32C9C6"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "D77E",
+      "expanded_salt": "7224B61774E1B0F193E6E2B9C6828D47"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "9E2E62CFF064E5688C2B785E",
-      "extended_signed_metadata_key_hmac_key": "397D11E6EE958656F2674EF69001146BEE0F972C497C92C71914B90F908228F7",
-      "extended_signed_section_aes_key": "C5AB5145752AA16E3C65C6EFE561EC9C",
-      "extended_unsigned_metadata_key_hmac_key": "EAB5C3F841C81AE3394987D3AE876D5043778BB711A191C36BBCCEF42CA3FF6A",
-      "extended_unsigned_section_aes_key": "E9B70118BF8A4D58A758F1B3FA1661AC",
-      "extended_unsigned_section_mic_hmac_key": "B3E6D61C78A8867BDD3F33404218279D13F6F89059671DED0E8D4EE34C7FB5D2",
-      "key_seed": "F61539C310341BFDA73F7D9504502B785A27BD555E2A4B80BCE329AB213522D0",
-      "legacy_ldt_key": "2D427E218733944B5F784EBE48E0DA05A515BE4D4D6C56FF79E36661F2DDDD8D6ACEB11FBB0F7C9DFA18EABF0BB2FA96F093F0D599DBFE357265DC5A6C860ECB",
-      "legacy_metadata_key_hmac_key": "53DB1506A87A258744C9B065C1B6D0C39395EFA8F0E6C543415506BFD6252FE0",
-      "legacy_metadata_nonce": "D3B81B8D52920BB3D47EC5BD"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "29AF30ED417C4F170B58DABA018BCB99",
+      "v0_identity_token": "F0663FB846121698C69430BACA77"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "2037",
-      "expanded_salt": "E1430F688FBB46C8F6067E50A1F4B296"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "0C83",
+      "short_salt_nonce": "DFDD3C550621ED62C9452D36"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "1D4C9F7C2CE767CDAAF1F2E2CC9E56D4",
-      "legacy_metadata_key": "8A521A8E6FD14285498B5DEC2A79"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "CF6F2269AE6601323D59347A6B698B6E",
+      "derived_salt_nonce": "8E899705639BD06B26439FA131EE2A2C",
+      "derived_salt_third_de": "B00A28308187B862E7F971387CA44CC4",
+      "section_extended_salt": "B543154433E370E6D7A97B46A73F5652"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "4363CB3C7259246F4B123095550EE704",
-      "derived_salt_first_section_no_de": "6E95B9CFB044108DE5EABDDD098667A4",
-      "derived_salt_first_section_third_de": "F4441684E3A1C584AE218220C5823D63",
-      "section_salt": "4FB82CF0A6F46347DE465EF01BEF9DC7"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "7F8B9AF9663AE92297910F2F",
-      "extended_signed_metadata_key_hmac_key": "12328EBBAC73837BAC2D4B82D2EB0943C41F11DEA46B6562C1992B49F91D78F4",
-      "extended_signed_section_aes_key": "DB11AC0C3FBD6B105EB4ECF65440F2BF",
-      "extended_unsigned_metadata_key_hmac_key": "D8A37BEB7EC0DB54ABEA9C4A05616090ADE42C3C255949698690C5D38D683627",
-      "extended_unsigned_section_aes_key": "AF42DDA012ADF8A795EE4A7B15DAA081",
-      "extended_unsigned_section_mic_hmac_key": "48EA765212AD49D3527D56554149629C969AE888F1D681DEAFE78CB0F0EAF658",
-      "key_seed": "E615EDD2FD82BB47855C4BE869BFC649E0BD0EF7A15EB0C8F99E5884156711DB",
-      "legacy_ldt_key": "EBC006F8343DAC70202A8ACA714C41EDCFBC6B5A5E6756DE2687A2A2B02068CF3317F3083E0DC3D626BCA08DFC188F1FA131B90679899618782C58CE2D3416E5",
-      "legacy_metadata_key_hmac_key": "F50BB9D2761D9FF880911989A82C2B844E21D7F4AFEAEA1CDB5F8BEB417F1EE0",
-      "legacy_metadata_nonce": "9152D3FCAB6D25F3EB847E41"
+      "key_seed": "882D400EB39A85F33D277BF925D7ED0E552D94A12BD15AD17F7151FE69E775F0",
+      "v0_identity_token_hmac_key": "1A0D3CFA718CBBDB2B330D38803A1BF03DD759B1EF1C9C0FDEA869012F89958A",
+      "v0_ldt_key": "9AF4D1867CF679CC66E3E874AF8056FAC8173B2287DB6CA871D6C3AADA33F936E281AFC6C09E0DBD9E16B067623763A66F0E5584028C36300D972D39E0C1C71E",
+      "v0_metadata_nonce": "96CCAE0B699F6A861C2FD9ED",
+      "v1_metadata_nonce": "475822C913CF03C67FDB558D",
+      "v1_mic_extended_salt_aes_key": "566FC52F26306B98BD4F702D783CFE18",
+      "v1_mic_extended_salt_identity_token_hmac_key": "B9E518A687FB1E222F2131F9D730493C7BB78E92002CF782FD30C75272E24B92",
+      "v1_mic_extended_salt_mic_hmac_key": "893E8AFB307BF4C011CDA36140152CF892F2356DCB8B87AB8D3B0CEFAE7D23CB",
+      "v1_mic_short_salt_aes_key": "33EB408FAD37D1FF76A27B2CF7D421F8",
+      "v1_mic_short_salt_identity_token_hmac_key": "9EEE04B64C468770682AF45F96D7DCA1D6EF53D4C592A2AE7A172A0DEBF10E21",
+      "v1_mic_short_salt_mic_hmac_key": "74655FA0D30D1E2DF0DA9747A3966C9111693B8E00704DC61D2156009D9F26BD",
+      "v1_signature_identity_token_hmac_key": "801294C7CA501C3BF20F57BA2B0E2FA09F13722B3655F625AE8E17C8A79B1154",
+      "v1_signature_section_aes_key": "44D3F5A168DD048901D7FDA07952634C"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "889B",
-      "expanded_salt": "7FBF8613F21C47797F4814D1939629E4"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "ADEB",
+      "expanded_salt": "E005DF91854CE6AB6186D3C976110C49"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "B10610105C6FB841987ED5DA28B1A8FA",
-      "legacy_metadata_key": "82558AF0C2A123C0577AD47FDDD9"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "8032E7A696CC593BA7863159E0C0EA10",
+      "v0_identity_token": "460240DEB4C8813AA5815BB6E5A9"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "4039",
+      "short_salt_nonce": "A4E6F88E3225FACE356BFDA4"
+    },
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "3885509BA1045121AE5CED396AA039EA",
+      "derived_salt_nonce": "679DF301EEF3F51C43FFC1701A184685",
+      "derived_salt_third_de": "441CDFC937345A037284C3E35698448A",
+      "section_extended_salt": "B43C581ACFC8A74540852A7DC924867C"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "BF63189D3C53D46BC6D15EA10B4AB4E0",
-      "derived_salt_first_section_no_de": "C2D485579DD258D6806CF6E345E0EC22",
-      "derived_salt_first_section_third_de": "1CCCCF350FD11768168AC0C94F598931",
-      "section_salt": "3EE85997BA02AB4A6620B2CE05469FE2"
+    "key_seed_hkdf": {
+      "key_seed": "408039A7989CE0B9DF3D3BF66AC9400458A14B524EA03CE24C783D7CEEA6130E",
+      "v0_identity_token_hmac_key": "09DB35D01A5476F41E66061C7420136EA7100245C979987C91111DAFDF5122E3",
+      "v0_ldt_key": "24A79BBB1033112612B3F710B6A9052548CB75772ED0AB03613B84EA77989879032C8026AE28C45D46E71E19261F454C7E0C2A48C8813CBA0D00A986340B31ED",
+      "v0_metadata_nonce": "7D489586FA49BE4CA0B5A333",
+      "v1_metadata_nonce": "F89E50CC26C5A4185A97A407",
+      "v1_mic_extended_salt_aes_key": "4A8884EFFAEFC7981A95DF8A76C68D0C",
+      "v1_mic_extended_salt_identity_token_hmac_key": "5712CAD3673FCB0E83E0BB52AF7D23E84C89D79F9526757F2B5070CA9D9DF81C",
+      "v1_mic_extended_salt_mic_hmac_key": "DB771C4FBA0936936876181EDB642A4C3C018561845F35B3D0DC07D7EE4840F6",
+      "v1_mic_short_salt_aes_key": "AF688DBBE5AE004BAD0D5E49B6F7D234",
+      "v1_mic_short_salt_identity_token_hmac_key": "F09E67D34142371B649BC5A06BF027125CAC81971B962F469FB2BAAF1F077B1C",
+      "v1_mic_short_salt_mic_hmac_key": "DCE2001156BB5C30969FA012D8FE16DB5C599574CC46B61FBA232AE2B184C1F9",
+      "v1_signature_identity_token_hmac_key": "9E7CA4743B0670FB181CDA233640CBADE4A81E38D4EB4F4BE6188B5548EDC765",
+      "v1_signature_section_aes_key": "2523471EBF1ED605399CD39AF0B624B0"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "6743665EF1F2534A7EB969AF",
-      "extended_signed_metadata_key_hmac_key": "355F49A98438F06E21D1501BF501ECCE624C206CC9F9430B0AFBC30041DA605A",
-      "extended_signed_section_aes_key": "CDD0694A18EF582345EA7C785F928EBD",
-      "extended_unsigned_metadata_key_hmac_key": "C82654B0FE5E9E50821806DDDD7F8114A79E9C2DA232C557A4B0AC98A514CEC3",
-      "extended_unsigned_section_aes_key": "E45A101D48BEBF02D446F97609EAFEC2",
-      "extended_unsigned_section_mic_hmac_key": "CE08A1DD793049E727DA51A8E6BA3B0AF1563D9DA40474FB335199037BFA7CAB",
-      "key_seed": "E396273E0432224E384CF4CA199547E73E985A3E7886102E0B9ABE911ADBDECC",
-      "legacy_ldt_key": "78E0EF38C80C61C0B907FF322D047C00664B19B5C704E3698E30B4860EED1814AA25097D7437E972007C5B923DB8D3708B312420EB1708817353D5D3345D40B6",
-      "legacy_metadata_key_hmac_key": "7266C9A1D0B38E92E975FE7E9E16436F7C596C62A99DCEDC01B26D65651917B9",
-      "legacy_metadata_nonce": "9CB20BBE3E11C360C90F4433"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "16D1",
+      "expanded_salt": "24288A2ADBC8574BB7225B2CADBA1F3D"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "B21803E7F32A51D47B439095F1229E51",
+      "v0_identity_token": "6D2145A353E43D90B1487ECD9D20"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "F44E",
-      "expanded_salt": "CC8D7F5AAE254573F1775BD42817E483"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "C1E9",
+      "short_salt_nonce": "483CBFACEC1D1468DBCD4FDA"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "A5CEB053C9960AF550E753C41D78D146",
-      "legacy_metadata_key": "E9E844761B0DA4549C8136A68534"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "03E5070187884CD433FBD176EA5E4A63",
+      "derived_salt_nonce": "D116BF3B37136C23AB77038C81C6FD87",
+      "derived_salt_third_de": "2ADBC8DF751DBB23172BBC87F1E6E77E",
+      "section_extended_salt": "87CA5497D5CE54D75C0B0DBD6CF07B39"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "73E81E7AD1FD560A8492C57B58F0EF4B",
-      "derived_salt_first_section_no_de": "52C7E3EED057E22BEE798D1EF0AA09F8",
-      "derived_salt_first_section_third_de": "075B795823F03E6D0877E58125F3BA2B",
-      "section_salt": "600FE29E6B140E51AFEFA00BB3C72EAF"
+    "key_seed_hkdf": {
+      "key_seed": "1AC9F5AC2ED4C99D2B535AC5304F8D481EC6147B39605BFA2B6E21D18006000B",
+      "v0_identity_token_hmac_key": "B2AB4B284B6CC602FB0403C154FBD19C18FBAC377AB971A35577010F8B0A3A97",
+      "v0_ldt_key": "4A4DAA2BB754E3E1ED2F473A3B31BA19A2165F0720AFE06EB6B712EB1A2940B8733E71DFFC3854A831CE0E9B8A74A19DF080B11A1FD42FBE7215D1797DBC2736",
+      "v0_metadata_nonce": "E5ADB3574A0A35ABE1027CF9",
+      "v1_metadata_nonce": "8F9855477699C38B115C7A54",
+      "v1_mic_extended_salt_aes_key": "D3B77010859F05271F05F26D1251E8D0",
+      "v1_mic_extended_salt_identity_token_hmac_key": "5379903CC277C6CA08E17E7EA2A91185F7FB5F9CB75960271AD3089E9F6F4F97",
+      "v1_mic_extended_salt_mic_hmac_key": "4FDE5BCD4A8CD9B8B0212BCA23A7A9EA49D7C039EBC90AABFECD864F594C5203",
+      "v1_mic_short_salt_aes_key": "8D5372501B32F7A568349C722CB0F695",
+      "v1_mic_short_salt_identity_token_hmac_key": "596A1BE90D3806E242B88A5CCE32BC8A2C4FAE2E17BF48B06AEAAD2C5E122032",
+      "v1_mic_short_salt_mic_hmac_key": "B00D35A3F49F08FBDAFDABB7ECA0A04297850A433A6D11C9FF8F6819F8C39C2B",
+      "v1_signature_identity_token_hmac_key": "7509D7C29027BC944390DD51AD6F3804D429C3EFBBFE24AD1AFF25FF40C6A8DE",
+      "v1_signature_section_aes_key": "EC1FA7F03A5E90DBABFFF5EC2495AB5E"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "34D8405F3BEFA5DA663ABD38",
-      "extended_signed_metadata_key_hmac_key": "A773F7699265B7BAC958E1EBFBA32B1C7B20B10F09DE32FE9F57ADD3190D6037",
-      "extended_signed_section_aes_key": "3648115AE3CE58377A3CBEF0B3BB2911",
-      "extended_unsigned_metadata_key_hmac_key": "64CBB1F7D08A526D5FF00C9C55C3B3F050B64DFEA5D2EEA4B1B5978632552635",
-      "extended_unsigned_section_aes_key": "380C378C2656B11DDBE2DB9BC188C2CB",
-      "extended_unsigned_section_mic_hmac_key": "2606D0A2E28E00E248C1A72D6441575A6737ED0E291F510F3C683E333F6E7860",
-      "key_seed": "FF0CA47221553F08490BF23140D052CEC0055B8B4BF7F1217F20E3325C1A14CD",
-      "legacy_ldt_key": "6CF901B6EF11F104077A3C8FC9DFCD406FFC0A9DEDA5BD1F373D61DF23C020BE603D604C67E2D6C8050AE464EC3F7DC3D6D90732E2E3935C339C5C4BCCD2EB0F",
-      "legacy_metadata_key_hmac_key": "42AB1CF389132FF5895D75FC9695AE07CEB1C228FD9590FDB49A07F612F11F56",
-      "legacy_metadata_nonce": "2F0439B0E875A3BFBC685D90"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "9F21",
+      "expanded_salt": "349E3311FFFF93BE6AC7D0D53D1E36DE"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "212FD419A877090C52821F501D62FF97",
+      "v0_identity_token": "A9ED25A283E0E58F7B127E2B62D6"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "355E",
-      "expanded_salt": "E6423E380E5D5970BE91AE9CCFCF0636"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "BD70",
+      "short_salt_nonce": "02C4AC240A3874E831DF38DB"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "2E0A709E2856F0807AE68B785B51F0D1",
-      "legacy_metadata_key": "26A21171AC7983950B900C2F2979"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "FB3EA084403FA66ECB1C15F9D0AC8396",
+      "derived_salt_nonce": "4443A4454CF2F491410423CD5F2AE9D0",
+      "derived_salt_third_de": "90A8D37E587090B7E6D636C6F8D27114",
+      "section_extended_salt": "38DA154EAF77D994EBBF3CD6FFDE825C"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "40D2F105E8C926AB9D7F7DA89596F555",
-      "derived_salt_first_section_no_de": "33440FBB64C6BAB6453F7F9A050FFEB5",
-      "derived_salt_first_section_third_de": "21899A6B8657F7ACB1048C1F683BAC29",
-      "section_salt": "7B7751AF729D27E3E4C2384D7DB8BEB7"
+    "key_seed_hkdf": {
+      "key_seed": "2D9D38BCDA9C2958227BE553C14CEFCF378D5CDCAB880EBF3C43635E07AAFF5A",
+      "v0_identity_token_hmac_key": "EBD73117325BEECE3A05DB2A1D6323AF7F499E652C61C9008A87A4E47F50EFCE",
+      "v0_ldt_key": "E28854F65892C5A1D6B92902F6142BA14CD3A74AB95356AA70DA22B3A9193E092B2FA17EBA2B76A08307491C7244789B00075B721AF6C8494D9DA1B3C9CA8F8B",
+      "v0_metadata_nonce": "9C3813ADD5746412C3214F51",
+      "v1_metadata_nonce": "6FBC21A602E1E1FB10B5F4EB",
+      "v1_mic_extended_salt_aes_key": "6EB09E856A0915BA60B3869F321981B4",
+      "v1_mic_extended_salt_identity_token_hmac_key": "35A04FCBA5B752C97D20158C3C55761C1E6723629BD40C2CD5F28007B14936C5",
+      "v1_mic_extended_salt_mic_hmac_key": "5F5436261C25D3E5FA446D513B409D5A0C4C27297CBE9F4EE741749154019E21",
+      "v1_mic_short_salt_aes_key": "B03494A82C3848A67453FB367532112C",
+      "v1_mic_short_salt_identity_token_hmac_key": "59D6749A1D6AFA4DB973AEAF088FCC0E7BE7D859A2814D641799BB1590496924",
+      "v1_mic_short_salt_mic_hmac_key": "D3B73C61E28AB288C60D7240B501A074C29A5E328D9BBD8716566B83AB1217A3",
+      "v1_signature_identity_token_hmac_key": "F85D6CA0DA816F073831FDDF3A0E0FCA9C888FB820762CCD6477DAFB90547E8F",
+      "v1_signature_section_aes_key": "807F93D9C345DC3AB3D98D43D6FBEAB9"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "765B",
+      "expanded_salt": "5866D7FC7A27D3A92AB155D7F28348EA"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "0155A616ECEC864B00282442",
-      "extended_signed_metadata_key_hmac_key": "A824088645C02D59479DC7A74318CC5E07654B61F06478C67CF046E7B85BB252",
-      "extended_signed_section_aes_key": "D8D511E9F1CBC39FEA821CEC36BF13CF",
-      "extended_unsigned_metadata_key_hmac_key": "7AA177254B5A06EA9EBAF174490B4117DB213D894B3CFF4F877E4CD83BFAF8C4",
-      "extended_unsigned_section_aes_key": "E902E759E6DEC479968A81B77B94257A",
-      "extended_unsigned_section_mic_hmac_key": "CB6FEBD1421C381E3110CF4D3AB26B66DD581D0E0D56675314B389BF26D80231",
-      "key_seed": "476578AA15A168B1AA7A31A35118008730FB1557A71CB1FB4B49A71103F8900F",
-      "legacy_ldt_key": "EE0CC526127D2D552A052880C0903B85898507C0247F4635F50B0E4A7F00DF2B96CF572DFF31ADB922932C4A119DE3EC06FCCA823709A090BCD3E4F6E3C89B8D",
-      "legacy_metadata_key_hmac_key": "65FA3562C23D80DAA78FF2CAC8C6D283D75C674079FAA21E9381E66DDB977AB4",
-      "legacy_metadata_nonce": "9CC3630D7C3686BE3ACFB38E"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "20C0754D69BC2EA57AE634C03D92D592",
+      "v0_identity_token": "1A1CAE87957F48BB52D07E0172B6"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "9909",
-      "expanded_salt": "AB6B101F206D124E8B55520349C8F4DC"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "607E",
+      "short_salt_nonce": "8B511172378BCB1BF0CB674C"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "DF7EC5214955F4A2666598DBCBC1337B",
-      "legacy_metadata_key": "B37A8ED8D672D20361D8F3D51AAB"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "1E962EC586FE218A8F3A736BA5677B7E",
+      "derived_salt_nonce": "7399E4535A366989EF412F3EA0F8CE45",
+      "derived_salt_third_de": "46F2B7D99127C01B68C8B7C8A45CD479",
+      "section_extended_salt": "6BC0DFA6EBFA267952CB7305F05AD8F2"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "CAF806EE61C9F98C033B6CE648B0A9AA",
-      "derived_salt_first_section_no_de": "7656E7E7B214432B15B30F17901A592C",
-      "derived_salt_first_section_third_de": "DAAF85C2FF8DCA2EEAB6524A94EB001A",
-      "section_salt": "DB0B46A3A5A38945744BC413851D8452"
+    "key_seed_hkdf": {
+      "key_seed": "34F2CDC71D38A430F36FA88F159AC2B053B1F339189D0570B68B238E86173239",
+      "v0_identity_token_hmac_key": "A7724517771962916DFCAAB84400F05865EA15F998EAC81740AE037A413C5425",
+      "v0_ldt_key": "6F267EC51E8018E9D3CEBF01617BA6E893F01E474BD9710272CA31FF1F61237270BCA8F39E6EEB155E95F2BFE393840EEB26079AB1E03C83FCE12CE52719BAB9",
+      "v0_metadata_nonce": "CE68A8C49765C41C38B3F0A5",
+      "v1_metadata_nonce": "27710531902EA355D240D622",
+      "v1_mic_extended_salt_aes_key": "79E4DBA1E056E59BDCB9BD65A7A805B6",
+      "v1_mic_extended_salt_identity_token_hmac_key": "8968D34F626250221E226A66FB2E76A4216E3F825745B3605B3F053863EDBFE1",
+      "v1_mic_extended_salt_mic_hmac_key": "CDD5D25349B3AB7D4885AD124A050704116A5911CE5F379BE998CC7E92DE01AB",
+      "v1_mic_short_salt_aes_key": "405DCC958551CDD1EC90C4A91D1B87AE",
+      "v1_mic_short_salt_identity_token_hmac_key": "D06923425C4A028710861654BBFACA9F2FB6B3488B70E9C3C1898152A8DAFA6E",
+      "v1_mic_short_salt_mic_hmac_key": "421A413608C4344BF7A0E250324337C9ED374636D38D2B35969B6EF434D0D30F",
+      "v1_signature_identity_token_hmac_key": "D4FADAACB47CC260DEEDB1BBD56131D4FAB54270ACBBFAE975D77079A8554E53",
+      "v1_signature_section_aes_key": "1F5F9803F64C4A6DCF4BFB0C4FDC800A"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "CDFF",
+      "expanded_salt": "9F61F76DC1CE3B702DC66772A87A2774"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "0598BCB940F7E46AEB02391A",
-      "extended_signed_metadata_key_hmac_key": "4B37B033B3911F061486FDD312F423269F345C5EC9D6EE4C9E427B2E2D584159",
-      "extended_signed_section_aes_key": "E28193D56E706173061F6890E557F96A",
-      "extended_unsigned_metadata_key_hmac_key": "4817CC85BC549519BEDA10D2257EDADE178B2AA9A710C462DD1190727B9E2E14",
-      "extended_unsigned_section_aes_key": "197FE5273519A250224BC5E582F599AE",
-      "extended_unsigned_section_mic_hmac_key": "E2B6BD73DD9018FC0D73C63574CDB10E27D5B84386958EB961B30AED26BD5303",
-      "key_seed": "2E84B9175D2AF64DCF5592A6CE6A600430F36D1E326268C8628A0790CB832637",
-      "legacy_ldt_key": "78BF29E2890F20C2359FFD32E20DD90972C660DEE7626DFB2FD89DA4DF2AD34B146973BF7FBC08E477F18199F3861A089BC32F247BDE835A7E34D4242241830C",
-      "legacy_metadata_key_hmac_key": "6F73D2A56EC7A166FF4965BD10FF5BD53225F9E598E9835120D4D3ECE29839B1",
-      "legacy_metadata_nonce": "6643257603952C875B40D527"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "B6A7CF0DA707E09E2A9FD05FB28802D6",
+      "v0_identity_token": "028D2AD4E48B79A63C3D6038F453"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "0518",
-      "expanded_salt": "F5CB5C30716D50337E3597A008774306"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "9D69",
+      "short_salt_nonce": "1B68E2C018314F5C61591D06"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "C7437CB6E1910745E1D5F85217DE6C3B",
-      "legacy_metadata_key": "836B4F0F57011DEE3EF79471A08A"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "443DBEF6EA8FBB7E1AED17C651902D8F",
+      "derived_salt_nonce": "C832DD69A51C4466CC7E4C8FF2C8D830",
+      "derived_salt_third_de": "996A7355DADC94B04ED4DD5BFF713D10",
+      "section_extended_salt": "86042432C5C8BFA2ADFEF07E52F76CE1"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "509E424CBCC5964DC827CD736417FDA6",
-      "derived_salt_first_section_no_de": "8269B8D8C65B9F7519F17EA1B501DCF2",
-      "derived_salt_first_section_third_de": "313C4472E5BF2EC6F08F8E407433E2D5",
-      "section_salt": "0C98C48D64771BD607BEE3CFA873E048"
-    },
     "key_seed_hkdf": {
-      "extended_metadata_nonce": "44F203FA2E7FC2E7DE0985A6",
-      "extended_signed_metadata_key_hmac_key": "077688F727BAADA52A42D5F4C5DE0FB10028190F68A2950BD1B622A8810A4F84",
-      "extended_signed_section_aes_key": "74959A95D5FE867B35646442EE2367F0",
-      "extended_unsigned_metadata_key_hmac_key": "6F5E34C56135FDACC90EDD77AB27D40C583689FDA915AD2B3E053FB287A9B7C9",
-      "extended_unsigned_section_aes_key": "BFBA8DDD3768E1475F6C2C8167DE2ED7",
-      "extended_unsigned_section_mic_hmac_key": "7723F9AA8F4694412A19AAF981149B03F332C2CA84EE7A7EA30F0A6CFFE8DF5F",
-      "key_seed": "C2F7D2B6C8A814B76198CC2838B787E70BE404B7F30C2D3300F00657FF15D4D7",
-      "legacy_ldt_key": "A675CA45BBA982D8027F96AB8E65B2B267F317805A8E92547DE9B59FF68D1D5D0464197908115D84F1699DF1321671D201AD42918AEDB938A32D4C9145505949",
-      "legacy_metadata_key_hmac_key": "C8B1A0EA176953817E25A5C2DD984AAA997A0841FD5795EBD596F8954490995D",
-      "legacy_metadata_nonce": "811CF06428436C54CFFBF02F"
+      "key_seed": "41CAB11A50B05B768F94CB737A55F0431C37058077CAC227BA33DEAFB13AC126",
+      "v0_identity_token_hmac_key": "225746B890970F0BA658FD31807704E4792B04142B643292E73897700DF3F7B2",
+      "v0_ldt_key": "83A452F3DB32B0C30BBCE0188F8674597B16F074155312C4AA50D3543E4F5CBD5F38A3742D0626B532DA9BAABA6976CEFF3F95BD8DC6D30AA945DF8A2ABAEF9B",
+      "v0_metadata_nonce": "FD6F822707B0AA6F1DA28329",
+      "v1_metadata_nonce": "EBC62567E6EDFBDA6FB864E1",
+      "v1_mic_extended_salt_aes_key": "4605C732B31CA709A146D42D49F56964",
+      "v1_mic_extended_salt_identity_token_hmac_key": "D67C18D39A016F3578F5B36CEDF74FF42AB2D33063004D81223709D34B35B1B3",
+      "v1_mic_extended_salt_mic_hmac_key": "2F7EFAC31BC19C6414DBFE1E151CA5A9BB2D7FF23816E50DB8D3DBCEF0F651D6",
+      "v1_mic_short_salt_aes_key": "72EED4197BDD9429F3B227EEEF8D9003",
+      "v1_mic_short_salt_identity_token_hmac_key": "060C6B4E277154EF8E2AACD29FA7FD561D89EE1B55FE75804AC900B2A14FC6AC",
+      "v1_mic_short_salt_mic_hmac_key": "9E5E30433992ECC1D0E897740DE7380195AD9D393B172626A04EB4EC1540441D",
+      "v1_signature_identity_token_hmac_key": "6F8D65A2FDF4A76C612A1CE055862D4E1F3BC758FA9DEA8171281F387C89E89F",
+      "v1_signature_section_aes_key": "9E1A2442E091482C1AD358CB46EBB449"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "FB05",
-      "expanded_salt": "5E11580A3CE991235E9CC3C7F2ADF285"
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "6BC4",
+      "expanded_salt": "BCDD2CBC57578E5CE0B7D53841FFC877"
+    },
+    "v0_identity_token_hkdf": {
+      "expanded_key": "0D231BE24C14382ED04D1AD9E6C9C876",
+      "v0_identity_token": "A8027285D444310F6180141C6DAF"
+    },
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "1304",
+      "short_salt_nonce": "B1B266BC940E1CB4C0DDB7C6"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "3226C766C11C315F46A2173286D56880",
-      "legacy_metadata_key": "A3B88B75A089E3D23A9DEE562FCC"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "EC10AA256E69F3BFC61E03E02DAC5572",
+      "derived_salt_nonce": "C5CD7C8AF453AA0A2E0CFB92B4DEC297",
+      "derived_salt_third_de": "E72061FB97353A6B46800EAEA02E86A2",
+      "section_extended_salt": "DC5E1856188626F6FCAF7DEBCD91F83B"
     }
   },
   {
-    "extended_section_salt_hkdf": {
-      "derived_salt_first_section_first_de": "BD4BA99CDFEEFD741D662D819B4D14F8",
-      "derived_salt_first_section_no_de": "64FB9A93030B0365D875001B3E96C350",
-      "derived_salt_first_section_third_de": "6A3C091F49BF86800AE97AE1E7E45328",
-      "section_salt": "E48DDB9A0AB7F195FC4CE1EECE95C905"
+    "key_seed_hkdf": {
+      "key_seed": "F9929BC72706329A1D56A0DDE98F41F0C23A4F3316703FC756E1035BD146961E",
+      "v0_identity_token_hmac_key": "C312A8A15CBF165D135B8934881F7F717ECD9F3EDE6592A73550F7EA63C14083",
+      "v0_ldt_key": "4BF1CFF058BEED01FA792506A058C94F0FCAEEC2559D16E7E0EB7C458EFD3E11D6BF3E549227B264AFEF830BD7924C54249B25BCC59B4CDD207122936033FDA0",
+      "v0_metadata_nonce": "B75EDD27FB344C7096F27C89",
+      "v1_metadata_nonce": "90EDA9020339C51BA2763873",
+      "v1_mic_extended_salt_aes_key": "1FA685333412CFBE2D673D839419BDA8",
+      "v1_mic_extended_salt_identity_token_hmac_key": "ED7A20A29073B5B95209AD0CCEC01A27BDD3A5A12DA9489518AB24E2307D2632",
+      "v1_mic_extended_salt_mic_hmac_key": "2F8F91E46219709D1389613489A418FA8D806A1FB9D9519D0C5D677D55883E5F",
+      "v1_mic_short_salt_aes_key": "B8A40538DB14E9CCE0157DF49441CDAC",
+      "v1_mic_short_salt_identity_token_hmac_key": "08569465F0C66A011A7D4FFF67B0FC97690ECE6868E62DF94767E7E9436467D1",
+      "v1_mic_short_salt_mic_hmac_key": "19D0469CCCA27EE200930615C3D35C27ABE9AAB808752DA5D205E4003A617543",
+      "v1_signature_identity_token_hmac_key": "FCBBD61A230D9BE84D5E57E4DE4C641F3F9733867290614BE54F33E381EAF517",
+      "v1_signature_section_aes_key": "39C03BE4FCCB838D975F0B10F2F27FA6"
+    },
+    "v0_adv_salt_hkdf": {
+      "adv_salt": "DF27",
+      "expanded_salt": "BB3B8A09918621D2F9AE81B329D4F096"
     },
-    "key_seed_hkdf": {
-      "extended_metadata_nonce": "E3B03E7EC1C59A97088C54F1",
-      "extended_signed_metadata_key_hmac_key": "827D30C971E64E75A26047C6288B4709D814C9BE9CC7F46572BB9B343D49A1FC",
-      "extended_signed_section_aes_key": "31F40B5580260656F90D47F58C6FFE3E",
-      "extended_unsigned_metadata_key_hmac_key": "EEF0C2830B30035F6EC56904F132AEFB4098A88951CC33286939CB9789AD1638",
-      "extended_unsigned_section_aes_key": "A60216A1AC950D161D157EDD08C98073",
-      "extended_unsigned_section_mic_hmac_key": "8AADFDEEA0DF6A71D95D3570846F53A4FEC594274282DD4BD227C4BD64FF98AF",
-      "key_seed": "0BE1EE39610085A3BA277EBBEAD2872369707174526F23FFC67AA8BA962ABB1F",
-      "legacy_ldt_key": "8D4922931521FB95569442172FAC2118E396C92702DAFC1DC3CB9E205C0E3ED7896179B06D1A0B9528B936E2F85B6FFC741F8654016FD47107333BD247E96EA5",
-      "legacy_metadata_key_hmac_key": "FAE1B3B936C6756EFD9AEBCA751389891DACDEE3EAFD3A808D0797F0C128F544",
-      "legacy_metadata_nonce": "094573CD360778668C292C9D"
+    "v0_identity_token_hkdf": {
+      "expanded_key": "72DE095B1A0271E0042F668244A1255B",
+      "v0_identity_token": "ADFE0051B256B9778548300C4F17"
     },
-    "legacy_adv_salt_hkdf": {
-      "adv_salt": "3E9E",
-      "expanded_salt": "D7A319F27E248AB5CA95967784D6AF77"
+    "v1_mic_section_short_salt_hkdf": {
+      "section_short_salt": "2816",
+      "short_salt_nonce": "7DB3F25CD050360CEB4DC7D0"
     },
-    "legacy_metadata_key_hkdf": {
-      "expanded_key": "15896E896566D60C3810FAADC94A05CE",
-      "legacy_metadata_key": "C08895650105CE9C165A9BCA08D8"
+    "v1_section_extended_salt_hkdf": {
+      "derived_salt_first_de": "ADACCCB5226C9A33368DCA3CDB62BBA1",
+      "derived_salt_nonce": "00AA84F4608746BEC8A17749584F7229",
+      "derived_salt_third_de": "C925E34CF796B07300BFDA0B91172D86",
+      "section_extended_salt": "65D6AA69B72031C537A1EBD7D1F16B7F"
     }
   }
-]
\ No newline at end of file
+]
diff --git a/nearby/presence/np_hkdf/src/lib.rs b/nearby/presence/np_hkdf/src/lib.rs
index 8201f38..bf82169 100644
--- a/nearby/presence/np_hkdf/src/lib.rs
+++ b/nearby/presence/np_hkdf/src/lib.rs
@@ -22,30 +22,29 @@
 #[cfg(feature = "std")]
 extern crate std;
 
-use core::marker;
-use crypto_provider::{aes::Aes128Key, hkdf::Hkdf, hmac::Hmac, CryptoProvider};
+use crypto_provider::aead::Aead;
+use crypto_provider::{aes, aes::Aes128Key, hkdf::Hkdf, hmac::Hmac, CryptoProvider};
 
 pub mod v1_salt;
 
 /// A wrapper around the common NP usage of HMAC-SHA256.
 ///
 /// These are generally derived via HKDF, but could be used for any HMAC-SHA256 key.
-#[derive(Debug)]
-pub struct NpHmacSha256Key<C: CryptoProvider> {
+#[derive(Clone, Debug)]
+pub struct NpHmacSha256Key {
     /// Nearby Presence uses 32-byte HMAC keys.
     ///
     /// Inside the HMAC algorithm they will be padded to 64 bytes.
     key: [u8; 32],
-    c_phantom: marker::PhantomData<C>,
 }
 
-impl<C: CryptoProvider> NpHmacSha256Key<C> {
+impl NpHmacSha256Key {
     /// Build a fresh HMAC instance.
     ///
     /// Since each HMAC is modified as data is fed to it, HMACs should not be reused.
     ///
     /// See also [Self::calculate_hmac] for simple use cases.
-    pub fn build_hmac(&self) -> C::HmacSha256 {
+    pub fn build_hmac<C: CryptoProvider>(&self) -> C::HmacSha256 {
         C::HmacSha256::new_from_key(self.key)
     }
 
@@ -57,8 +56,8 @@
     /// Build an HMAC, update it with the provided `data`, and finalize it, returning the resulting
     /// MAC. This is convenient for one-and-done HMAC usage rather than incrementally accumulating
     /// the final MAC.
-    pub fn calculate_hmac(&self, data: &[u8]) -> [u8; 32] {
-        let mut hmac = self.build_hmac();
+    pub fn calculate_hmac<C: CryptoProvider>(&self, data: &[u8]) -> [u8; 32] {
+        let mut hmac = self.build_hmac::<C>();
         hmac.update(data);
         hmac.finalize()
     }
@@ -67,26 +66,20 @@
     ///
     /// This is convenient for one-and-done HMAC usage rather than incrementally accumulating
     /// the final MAC.
-    pub fn verify_hmac(
+    pub fn verify_hmac<C: CryptoProvider>(
         &self,
         data: &[u8],
         expected_mac: [u8; 32],
     ) -> Result<(), crypto_provider::hmac::MacError> {
-        let mut hmac = self.build_hmac();
+        let mut hmac = self.build_hmac::<C>();
         hmac.update(data);
         hmac.verify(expected_mac)
     }
 }
 
-impl<C: CryptoProvider> From<[u8; 32]> for NpHmacSha256Key<C> {
+impl From<[u8; 32]> for NpHmacSha256Key {
     fn from(key: [u8; 32]) -> Self {
-        Self { key, c_phantom: Default::default() }
-    }
-}
-
-impl<C: CryptoProvider> Clone for NpHmacSha256Key<C> {
-    fn clone(&self) -> Self {
-        Self { key: self.key, c_phantom: Default::default() }
+        Self { key }
     }
 }
 
@@ -106,84 +99,142 @@
 
     /// LDT key used to decrypt a legacy advertisement
     #[allow(clippy::expect_used)]
-    pub fn legacy_ldt_key(&self) -> ldt::LdtKey<xts_aes::XtsAes128Key> {
+    pub fn v0_ldt_key(&self) -> ldt::LdtKey<xts_aes::XtsAes128Key> {
         ldt::LdtKey::from_concatenated(
-            &self.hkdf.derive_array(b"Legacy LDT key").expect("LDT key is a valid length"),
+            &self.hkdf.derive_array(b"V0 LDT key").expect("LDT key is a valid length"),
         )
     }
 
-    /// HMAC key used when verifying the raw metadata key extracted from an advertisement
-    pub fn legacy_metadata_key_hmac_key(&self) -> NpHmacSha256Key<C> {
-        self.hkdf.derive_hmac_sha256_key(b"Legacy metadata key verification HMAC key")
+    /// HMAC key used when verifying the raw identity token extracted from an advertisement
+    pub fn v0_identity_token_hmac_key(&self) -> NpHmacSha256Key {
+        self.hkdf.derive_hmac_sha256_key(b"V0 Identity token verification HMAC key")
     }
 
     /// AES-GCM nonce used when decrypting metadata
     #[allow(clippy::expect_used)]
-    pub fn legacy_metadata_nonce(&self) -> [u8; 12] {
-        self.hkdf.derive_array(b"Legacy Metadata Nonce").expect("Nonce is a valid length")
+    pub fn v0_metadata_nonce(&self) -> <C::Aes128Gcm as Aead>::Nonce {
+        self.hkdf.derive_array(b"V0 Metadata nonce").expect("Nonce is a valid length")
     }
 
     /// AES-GCM nonce used when decrypting metadata.
     ///
     /// Shared between signed and unsigned since they use the same credential.
     #[allow(clippy::expect_used)]
-    pub fn extended_metadata_nonce(&self) -> [u8; 12] {
-        self.hkdf.derive_array(b"Metadata Nonce").expect("Nonce is a valid length")
+    pub fn v1_metadata_nonce(&self) -> <C::Aes128Gcm as Aead>::Nonce {
+        self.hkdf.derive_array(b"V1 Metadata nonce").expect("Nonce is a valid length")
     }
 
-    /// HMAC key used when verifying the raw metadata key extracted from an advertisement
-    pub fn extended_unsigned_metadata_key_hmac_key(&self) -> NpHmacSha256Key<C> {
-        self.hkdf.derive_hmac_sha256_key(b"Unsigned Section metadata key HMAC key")
+    /// Derived keys for MIC short salt sections
+    pub fn v1_mic_short_salt_keys(&self) -> MicShortSaltSectionKeys<'_, C> {
+        MicShortSaltSectionKeys { hkdf: &self.hkdf }
     }
 
-    /// HMAC key used when verifying the raw metadata key extracted from an extended signed advertisement
-    #[allow(clippy::expect_used)]
-    pub fn extended_signed_metadata_key_hmac_key(&self) -> NpHmacSha256Key<C> {
-        self.hkdf.derive_hmac_sha256_key(b"Signed Section metadata key HMAC key")
+    /// Derived keys for MIC extended salt sections
+    pub fn v1_mic_extended_salt_keys(&self) -> MicExtendedSaltSectionKeys<'_, C> {
+        MicExtendedSaltSectionKeys { hkdf: &self.hkdf }
     }
 
-    /// AES128 key used when decrypting an extended signed section
-    #[allow(clippy::expect_used)]
-    pub fn extended_signed_section_aes_key(&self) -> Aes128Key {
-        self.hkdf.derive_aes128_key(b"Signed Section AES key")
+    /// Derived keys for MIC signature sections
+    pub fn v1_signature_keys(&self) -> SignatureSectionKeys<'_, C> {
+        SignatureSectionKeys { hkdf: &self.hkdf }
     }
 }
 
-impl<C: CryptoProvider> UnsignedSectionKeys<C> for NpKeySeedHkdf<C> {
+/// Derived keys for MIC short salt sections
+pub struct MicShortSaltSectionKeys<'a, C: CryptoProvider> {
+    hkdf: &'a NpHkdf<C>,
+}
+
+impl<'a, C: CryptoProvider> MicShortSaltSectionKeys<'a, C> {
+    /// HMAC-SHA256 key used when verifying a section's ciphertext
+    pub fn mic_hmac_key(&self) -> NpHmacSha256Key {
+        self.hkdf.derive_hmac_sha256_key(b"MIC Section short salt HMAC key")
+    }
+}
+
+impl<'a, C: CryptoProvider> DerivedSectionKeys<C> for MicShortSaltSectionKeys<'a, C> {
     fn aes_key(&self) -> Aes128Key {
-        self.hkdf.derive_aes128_key(b"Unsigned Section AES key")
+        self.hkdf.derive_aes128_key(b"MIC Section short salt AES key")
     }
 
-    fn hmac_key(&self) -> NpHmacSha256Key<C> {
-        self.hkdf.derive_hmac_sha256_key(b"Unsigned Section HMAC key")
+    fn identity_token_hmac_key(&self) -> NpHmacSha256Key {
+        self.hkdf.derive_hmac_sha256_key(b"MIC Section short salt identity token HMAC key")
     }
 }
 
-/// Derived keys for V1 MIC (unsigned) sections
-pub trait UnsignedSectionKeys<C: CryptoProvider> {
-    /// AES128 key used when decrypting an extended unsigned section
+/// Derived keys for MIC extended salt sections
+pub struct MicExtendedSaltSectionKeys<'a, C: CryptoProvider> {
+    hkdf: &'a NpHkdf<C>,
+}
+
+impl<'a, C: CryptoProvider> MicExtendedSaltSectionKeys<'a, C> {
+    /// HMAC-SHA256 key used when verifying a section's ciphertext
+    pub fn mic_hmac_key(&self) -> NpHmacSha256Key {
+        self.hkdf.derive_hmac_sha256_key(b"MIC Section extended salt HMAC key")
+    }
+}
+
+impl<'a, C: CryptoProvider> DerivedSectionKeys<C> for MicExtendedSaltSectionKeys<'a, C> {
+    fn aes_key(&self) -> Aes128Key {
+        self.hkdf.derive_aes128_key(b"MIC Section extended salt AES key")
+    }
+
+    fn identity_token_hmac_key(&self) -> NpHmacSha256Key {
+        self.hkdf.derive_hmac_sha256_key(b"MIC Section extended salt identity token HMAC key")
+    }
+}
+
+/// Derived keys for Signature sections
+pub struct SignatureSectionKeys<'a, C: CryptoProvider> {
+    hkdf: &'a NpHkdf<C>,
+}
+
+impl<'a, C: CryptoProvider> DerivedSectionKeys<C> for SignatureSectionKeys<'a, C> {
+    fn aes_key(&self) -> Aes128Key {
+        self.hkdf.derive_aes128_key(b"Signature Section AES key")
+    }
+
+    fn identity_token_hmac_key(&self) -> NpHmacSha256Key {
+        self.hkdf.derive_hmac_sha256_key(b"Signature Section identity token HMAC key")
+    }
+}
+
+/// Derived keys for encrypted V1 sections
+pub trait DerivedSectionKeys<C: CryptoProvider> {
+    /// AES128 key used when encrypting a section's ciphertext
     fn aes_key(&self) -> Aes128Key;
 
-    /// HMAC-SHA256 key used when verifying an extended unsigned section
-    fn hmac_key(&self) -> NpHmacSha256Key<C>;
+    /// HMAC-SHA256 key used when verifying a section's plaintext identity token
+    fn identity_token_hmac_key(&self) -> NpHmacSha256Key;
 }
 
 /// Expand a legacy salt into the expanded salt used with XOR padding in LDT.
 #[allow(clippy::expect_used)]
-pub fn legacy_ldt_expanded_salt<const B: usize, C: CryptoProvider>(salt: &[u8; 2]) -> [u8; B] {
-    simple_np_hkdf_expand::<B, C>(salt, b"Legacy LDT salt pad")
-        // the padded salt is the tweak size of a tweakable block cipher, which shouldn't be
-        // anywhere close to the max HKDF size (255 * 32)
+pub fn v0_ldt_expanded_salt<C: CryptoProvider>(salt: &[u8; 2]) -> [u8; aes::BLOCK_SIZE] {
+    simple_np_hkdf_expand::<{ aes::BLOCK_SIZE }, C>(salt, b"V0 LDT salt pad")
+        // XTS tweak size is the block cipher's block size
         .expect("Tweak size is a valid HKDF size")
 }
 
-/// Expand a legacy (short) raw metadata key into an AES128 key.
+/// Expand a v0 identity token into an AES128 metadata key.
 #[allow(clippy::expect_used)]
-pub fn legacy_metadata_expanded_key<C: CryptoProvider>(raw_metadata_key: &[u8; 14]) -> [u8; 16] {
-    simple_np_hkdf_expand::<16, C>(raw_metadata_key, b"Legacy metadata key expansion")
+pub fn v0_metadata_expanded_key<C: CryptoProvider>(identity_token: &[u8; 14]) -> [u8; 16] {
+    simple_np_hkdf_expand::<16, C>(identity_token, b"V0 Metadata key expansion")
         .expect("AES128 key is a valid HKDF size")
 }
 
+/// Expand an extended MIC section's short salt into an AES-CTR nonce.
+#[allow(clippy::expect_used)]
+pub fn extended_mic_section_short_salt_nonce<C: CryptoProvider>(
+    salt: [u8; 2],
+) -> aes::ctr::AesCtrNonce {
+    simple_np_hkdf_expand::<{ aes::ctr::AES_CTR_NONCE_LEN }, C>(
+        salt.as_slice(),
+        b"MIC Section short salt nonce",
+    )
+    .expect("AES-CTR nonce is a valid HKDF size")
+}
+
 /// Build an HKDF using the NP HKDF salt, calculate output, and discard the HKDF.
 /// If using the NP key seed as IKM, see [NpKeySeedHkdf] instead.
 ///
@@ -198,12 +249,12 @@
 }
 
 /// Construct an HKDF with the Nearby Presence salt and provided `ikm`
-pub fn np_salt_hkdf<C: CryptoProvider>(ikm: &[u8]) -> C::HkdfSha256 {
+fn np_salt_hkdf<C: CryptoProvider>(ikm: &[u8]) -> C::HkdfSha256 {
     C::HkdfSha256::new(Some(NP_HKDF_SALT), ikm)
 }
 
 /// NP-flavored HKDF operations for common derived output types
-pub struct NpHkdf<C: CryptoProvider> {
+struct NpHkdf<C: CryptoProvider> {
     hkdf: C::HkdfSha256,
 }
 
@@ -222,7 +273,7 @@
 
     /// Derive an HMAC-SHA256 key using the provided `info`
     #[allow(clippy::expect_used)]
-    pub fn derive_hmac_sha256_key(&self, info: &[u8]) -> NpHmacSha256Key<C> {
+    pub fn derive_hmac_sha256_key(&self, info: &[u8]) -> NpHmacSha256Key {
         self.derive_array(info).expect("HMAC-SHA256 keys are a valid length").into()
     }
     /// Derive an AES-128 key using the provided `info`
diff --git a/nearby/presence/np_hkdf/src/v1_salt.rs b/nearby/presence/np_hkdf/src/v1_salt.rs
index 94e6855..cfa1f51 100644
--- a/nearby/presence/np_hkdf/src/v1_salt.rs
+++ b/nearby/presence/np_hkdf/src/v1_salt.rs
@@ -14,45 +14,44 @@
 
 //! Salt used in a V1 advertisement.
 use crate::np_salt_hkdf;
-use core::fmt;
-use crypto_provider::hkdf::Hkdf;
-use crypto_provider::CryptoProvider;
+use crypto_provider::{hkdf::Hkdf, CryptoProvider, CryptoRng, FromCryptoRng};
+
+/// Length of a V1 extended salt
+pub const EXTENDED_SALT_LEN: usize = 16;
 
 /// Salt optionally included in V1 advertisement header.
 ///
 /// The salt is never used directly; rather, a derived salt should be extracted as needed for any
 /// section or DE that requires it.
-#[derive(Clone)]
-pub struct V1Salt<C>
-where
-    C: CryptoProvider,
-{
-    // kept around for Eq and Debug impl, should not be exposed
-    data: [u8; 16],
-    hkdf: C::HkdfSha256,
+#[derive(Clone, Copy, PartialEq, Debug, Eq)]
+pub struct ExtendedV1Salt {
+    data: [u8; EXTENDED_SALT_LEN],
 }
 
-impl<C: CryptoProvider> V1Salt<C> {
-    /// Derive a salt for a particular section and DE, if applicable.
+impl ExtendedV1Salt {
+    /// Derive a salt for a particular DE, if applicable.
     ///
     /// Returns none if the requested size is larger than HKDF allows or if offset arithmetic
     /// overflows.
-    pub fn derive<const N: usize>(&self, de: Option<DataElementOffset>) -> Option<[u8; N]> {
+    pub fn derive<const N: usize, C: CryptoProvider>(
+        &self,
+        de: Option<DataElementOffset>,
+    ) -> Option<[u8; N]> {
+        let hkdf = np_salt_hkdf::<C>(&self.data);
         let mut arr = [0_u8; N];
         // 0-based offsets -> 1-based indices w/ 0 indicating not present
-        self.hkdf
-            .expand_multi_info(
-                &[
-                    b"V1 derived salt",
-                    &de.and_then(|d| d.offset.checked_add(1))
-                        .map(|o| o.into())
-                        .unwrap_or(0_u32)
-                        .to_be_bytes(),
-                ],
-                &mut arr,
-            )
-            .map(|_| arr)
-            .ok()
+        hkdf.expand_multi_info(
+            &[
+                b"V1 derived salt",
+                &de.and_then(|d| d.offset.checked_add(1))
+                    .map(|o| o.into())
+                    .unwrap_or(0_u32)
+                    .to_be_bytes(),
+            ],
+            &mut arr,
+        )
+        .map(|_| arr)
+        .ok()
     }
 
     /// Returns the salt bytes as a slice
@@ -61,38 +60,29 @@
     }
 
     /// Returns the salt bytes as an array
-    pub fn into_array(self) -> [u8; 16] {
+    pub fn into_array(self) -> [u8; EXTENDED_SALT_LEN] {
         self.data
     }
 
     /// Returns the salt bytes as a reference to an array
-    pub fn as_array_ref(&self) -> &[u8; 16] {
+    pub fn bytes(&self) -> &[u8; EXTENDED_SALT_LEN] {
         &self.data
     }
 }
 
-impl<C: CryptoProvider> From<[u8; 16]> for V1Salt<C> {
-    fn from(arr: [u8; 16]) -> Self {
-        Self { data: arr, hkdf: np_salt_hkdf::<C>(&arr) }
+impl From<[u8; EXTENDED_SALT_LEN]> for ExtendedV1Salt {
+    fn from(arr: [u8; EXTENDED_SALT_LEN]) -> Self {
+        Self { data: arr }
     }
 }
 
-impl<C: CryptoProvider> PartialEq<Self> for V1Salt<C> {
-    fn eq(&self, other: &Self) -> bool {
-        // no need to compare hkdf (which it doesn't allow anyway)
-        self.data == other.data
+impl FromCryptoRng for ExtendedV1Salt {
+    fn new_random<R: CryptoRng>(rng: &mut R) -> Self {
+        rng.gen::<[u8; EXTENDED_SALT_LEN]>().into()
     }
 }
 
-impl<C: CryptoProvider> Eq for V1Salt<C> {}
-
-impl<C: CryptoProvider> fmt::Debug for V1Salt<C> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.data.fmt(f)
-    }
-}
-
-/// Offset of a data element in its containing section, used with [V1Salt].
+/// Offset of a data element in its containing section, used with [ExtendedV1Salt].
 #[derive(PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
 pub struct DataElementOffset {
     /// 0-based offset of the DE in the advertisement
diff --git a/nearby/presence/np_hkdf/tests/hmac.rs b/nearby/presence/np_hkdf/tests/hmac.rs
index 4ebef5a..9cb0357 100644
--- a/nearby/presence/np_hkdf/tests/hmac.rs
+++ b/nearby/presence/np_hkdf/tests/hmac.rs
@@ -21,11 +21,11 @@
     let data = &[1_u8; 32];
     let hmac_key = [2; 32];
 
-    let hmac = NpHmacSha256Key::<CryptoProviderImpl>::from(hmac_key);
+    let hmac = NpHmacSha256Key::from(hmac_key);
 
-    let mac = hmac.calculate_hmac(data);
+    let mac = hmac.calculate_hmac::<CryptoProviderImpl>(data);
 
-    assert_eq!(Ok(()), hmac.verify_hmac(data, mac));
+    assert_eq!(Ok(()), hmac.verify_hmac::<CryptoProviderImpl>(data, mac));
 }
 
 #[test]
@@ -33,10 +33,10 @@
     let data = &[1_u8; 32];
     let hmac_key = [2; 32];
 
-    let hmac = NpHmacSha256Key::<CryptoProviderImpl>::from(hmac_key);
+    let hmac = NpHmacSha256Key::from(hmac_key);
 
-    let _mac = hmac.calculate_hmac(data);
+    let _mac = hmac.calculate_hmac::<CryptoProviderImpl>(data);
 
     // wrong mac
-    assert_eq!(Err(MacError), hmac.verify_hmac(data, [0xFF; 32]));
+    assert_eq!(Err(MacError), hmac.verify_hmac::<CryptoProviderImpl>(data, [0xFF; 32]));
 }
diff --git a/nearby/presence/np_hkdf/tests/test_vectors.rs b/nearby/presence/np_hkdf/tests/test_vectors.rs
index 175e73e..a00e747 100644
--- a/nearby/presence/np_hkdf/tests/test_vectors.rs
+++ b/nearby/presence/np_hkdf/tests/test_vectors.rs
@@ -17,12 +17,11 @@
 use anyhow::anyhow;
 use crypto_provider::aes::AesKey;
 use crypto_provider_default::CryptoProviderImpl;
-use np_hkdf::{v1_salt::V1Salt, *};
-use rand::Rng as _;
-use rand_ext::seeded_rng;
+use np_hkdf::{v1_salt::ExtendedV1Salt, *};
 use serde_json::json;
 use std::{fs, io::Read as _};
 use test_helper::extract_key_array;
+use test_vector_hkdf::TestVectorHkdf;
 
 #[test]
 fn hkdf_test_vectors() -> Result<(), anyhow::Error> {
@@ -42,76 +41,97 @@
             let key_seed = extract_key_array::<32>(group, "key_seed");
             let hkdf = NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
             assert_eq!(
-                extract_key_array::<64>(group, "legacy_ldt_key"),
-                hkdf.legacy_ldt_key().as_concatenated()
+                extract_key_array::<64>(group, "v0_ldt_key"),
+                hkdf.v0_ldt_key().as_concatenated()
             );
             assert_eq!(
-                &extract_key_array::<32>(group, "legacy_metadata_key_hmac_key"),
-                hkdf.legacy_metadata_key_hmac_key().as_bytes()
+                &extract_key_array::<32>(group, "v0_identity_token_hmac_key"),
+                hkdf.v0_identity_token_hmac_key().as_bytes()
             );
             assert_eq!(
-                extract_key_array::<12>(group, "legacy_metadata_nonce"),
-                hkdf.legacy_metadata_nonce()
+                extract_key_array::<12>(group, "v0_metadata_nonce"),
+                hkdf.v0_metadata_nonce()
             );
             assert_eq!(
-                extract_key_array::<12>(group, "extended_metadata_nonce"),
-                hkdf.extended_metadata_nonce()
+                extract_key_array::<12>(group, "v1_metadata_nonce"),
+                hkdf.v1_metadata_nonce()
             );
             assert_eq!(
-                &extract_key_array::<32>(group, "extended_unsigned_metadata_key_hmac_key"),
-                hkdf.extended_unsigned_metadata_key_hmac_key().as_bytes()
+                &extract_key_array::<32>(group, "v1_mic_short_salt_identity_token_hmac_key"),
+                hkdf.v1_mic_short_salt_keys().identity_token_hmac_key().as_bytes()
             );
             assert_eq!(
-                extract_key_array::<16>(group, "extended_unsigned_section_aes_key"),
-                *UnsignedSectionKeys::aes_key(&hkdf).as_array()
+                extract_key_array::<16>(group, "v1_mic_short_salt_aes_key"),
+                *hkdf.v1_mic_short_salt_keys().aes_key().as_array()
             );
             assert_eq!(
-                &extract_key_array::<32>(group, "extended_unsigned_section_mic_hmac_key"),
-                UnsignedSectionKeys::hmac_key(&hkdf).as_bytes()
+                &extract_key_array::<32>(group, "v1_mic_short_salt_mic_hmac_key"),
+                hkdf.v1_mic_short_salt_keys().mic_hmac_key().as_bytes()
             );
             assert_eq!(
-                &extract_key_array::<32>(group, "extended_signed_metadata_key_hmac_key"),
-                hkdf.extended_signed_metadata_key_hmac_key().as_bytes()
+                &extract_key_array::<32>(group, "v1_mic_extended_salt_identity_token_hmac_key"),
+                hkdf.v1_mic_extended_salt_keys().identity_token_hmac_key().as_bytes()
             );
             assert_eq!(
-                extract_key_array::<16>(group, "extended_signed_section_aes_key"),
-                *hkdf.extended_signed_section_aes_key().as_array()
+                extract_key_array::<16>(group, "v1_mic_extended_salt_aes_key"),
+                *hkdf.v1_mic_extended_salt_keys().aes_key().as_array()
+            );
+            assert_eq!(
+                &extract_key_array::<32>(group, "v1_mic_extended_salt_mic_hmac_key"),
+                hkdf.v1_mic_extended_salt_keys().mic_hmac_key().as_bytes()
+            );
+            assert_eq!(
+                &extract_key_array::<32>(group, "v1_signature_identity_token_hmac_key"),
+                hkdf.v1_signature_keys().identity_token_hmac_key().as_bytes()
+            );
+            assert_eq!(
+                extract_key_array::<16>(group, "v1_signature_section_aes_key"),
+                *hkdf.v1_signature_keys().aes_key().as_array()
             );
         }
 
         {
-            let group = &tc["legacy_adv_salt_hkdf"];
+            let group = &tc["v0_adv_salt_hkdf"];
             let ikm = extract_key_array::<2>(group, "adv_salt");
             assert_eq!(
                 extract_key_array::<16>(group, "expanded_salt"),
-                legacy_ldt_expanded_salt::<16, CryptoProviderImpl>(&ikm)
+                v0_ldt_expanded_salt::<CryptoProviderImpl>(&ikm)
             )
         }
 
         {
-            let group = &tc["legacy_metadata_key_hkdf"];
-            let ikm = extract_key_array::<14>(group, "legacy_metadata_key");
+            let group = &tc["v0_identity_token_hkdf"];
+            let ikm = extract_key_array::<14>(group, "v0_identity_token");
             assert_eq!(
                 extract_key_array::<16>(group, "expanded_key"),
-                legacy_metadata_expanded_key::<CryptoProviderImpl>(&ikm)
+                v0_metadata_expanded_key::<CryptoProviderImpl>(&ikm)
             )
         }
 
         {
-            let group = &tc["extended_section_salt_hkdf"];
-            let ikm = extract_key_array::<16>(group, "section_salt");
-            let salt = V1Salt::<CryptoProviderImpl>::from(ikm);
+            let group = &tc["v1_section_extended_salt_hkdf"];
+            let ikm = extract_key_array::<16>(group, "section_extended_salt");
+            let salt = ExtendedV1Salt::from(ikm);
             assert_eq!(
-                extract_key_array::<16>(group, "derived_salt_first_section_no_de"),
-                salt.derive(None).unwrap(),
+                extract_key_array::<16>(group, "derived_salt_nonce"),
+                salt.derive::<16, CryptoProviderImpl>(None).unwrap(),
             );
             assert_eq!(
-                extract_key_array::<16>(group, "derived_salt_first_section_first_de"),
-                salt.derive(Some(0.into())).unwrap(),
+                extract_key_array::<16>(group, "derived_salt_first_de"),
+                salt.derive::<16, CryptoProviderImpl>(Some(0.into())).unwrap(),
             );
             assert_eq!(
-                extract_key_array::<16>(group, "derived_salt_first_section_third_de"),
-                salt.derive(Some(2.into())).unwrap(),
+                extract_key_array::<16>(group, "derived_salt_third_de"),
+                salt.derive::<16, CryptoProviderImpl>(Some(2.into())).unwrap(),
+            );
+        }
+
+        {
+            let group = &tc["v1_mic_section_short_salt_hkdf"];
+            let ikm = extract_key_array::<2>(group, "section_short_salt");
+            assert_eq!(
+                extract_key_array::<12>(group, "short_salt_nonce"),
+                extended_mic_section_short_salt_nonce::<CryptoProviderImpl>(ikm),
             );
         }
     }
@@ -121,53 +141,70 @@
 
 // disable unless you want to print out a new set of test vectors
 #[ignore]
+#[allow(clippy::panic)]
 #[test]
 fn gen_test_vectors() {
-    let mut rng = seeded_rng();
-
     let mut array = Vec::<serde_json::Value>::new();
 
-    for _ in 0..100 {
-        let key_seed: [u8; 32] = rng.gen();
-        let legacy_adv_salt: [u8; 2] = rng.gen();
-        let legacy_metadata_key: [u8; 14] = rng.gen();
-        let adv_salt_bytes: [u8; 16] = rng.gen();
-        let extended_adv_salt = V1Salt::<CryptoProviderImpl>::from(adv_salt_bytes);
+    for i in 0_u32..100 {
+        // build "random" things in a repeatable way so future changes don't
+        // rebuild unrelated things, with some /dev/random thrown in for good measure
+        let test_vector_seed_hkdf = TestVectorHkdf::<CryptoProviderImpl>::new(
+            "NP HKDF test vectors pb4qoNqM9aL/ezSC2FU5EQzu8JJoJ25B+rLqbU5kVN8",
+            &i.to_be_bytes(),
+        );
+
+        let key_seed: [u8; 32] = test_vector_seed_hkdf.derive_array("key seed");
+        let v0_adv_salt: [u8; 2] = test_vector_seed_hkdf.derive_array("legacy adv salt");
+        let v0_identity_token: [u8; 14] = test_vector_seed_hkdf.derive_array("v0 identity token");
+        let extended_salt_bytes: [u8; 16] =
+            test_vector_seed_hkdf.derive_array("v1 section extended salt");
+        let v1_mic_short_salt: [u8; 2] =
+            test_vector_seed_hkdf.derive_array("v1 mic section short salt");
+        let v1_extended_salt = ExtendedV1Salt::from(extended_salt_bytes);
 
         let key_seed_hkdf = NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
         array
             .push(json!({
                 "key_seed_hkdf": {
                     "key_seed": hex::encode_upper(key_seed),
-                    "legacy_ldt_key": hex::encode_upper(key_seed_hkdf.legacy_ldt_key().as_concatenated()),
-                    "legacy_metadata_key_hmac_key":
-                        hex::encode_upper(key_seed_hkdf.legacy_metadata_key_hmac_key().as_bytes()),
-                    "legacy_metadata_nonce": hex::encode_upper(key_seed_hkdf.legacy_metadata_nonce()),
-                    "extended_metadata_nonce": hex::encode_upper(key_seed_hkdf.extended_metadata_nonce()),
-                    "extended_unsigned_metadata_key_hmac_key": hex::encode_upper(key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().as_bytes()),
-                    "extended_unsigned_section_aes_key": hex::encode_upper(UnsignedSectionKeys::<CryptoProviderImpl>::aes_key(&key_seed_hkdf).as_array()),
-                    "extended_unsigned_section_mic_hmac_key": hex::encode_upper(UnsignedSectionKeys::<CryptoProviderImpl>::hmac_key(&key_seed_hkdf).as_bytes()),
-                    "extended_signed_metadata_key_hmac_key": hex::encode_upper(key_seed_hkdf.extended_signed_metadata_key_hmac_key().as_bytes()),
-                    "extended_signed_section_aes_key": hex::encode_upper(key_seed_hkdf.extended_signed_section_aes_key().as_array()),
+                    "v0_ldt_key": hex::encode_upper(key_seed_hkdf.v0_ldt_key().as_concatenated()),
+                    "v0_identity_token_hmac_key":
+                        hex::encode_upper(key_seed_hkdf.v0_identity_token_hmac_key().as_bytes()),
+                    "v0_metadata_nonce": hex::encode_upper(key_seed_hkdf.v0_metadata_nonce()),
+                    "v1_metadata_nonce": hex::encode_upper(key_seed_hkdf.v1_metadata_nonce()),
+                    "v1_mic_short_salt_identity_token_hmac_key": hex::encode_upper(key_seed_hkdf.v1_mic_short_salt_keys().identity_token_hmac_key().as_bytes()),
+                    "v1_mic_short_salt_aes_key": hex::encode_upper(key_seed_hkdf.v1_mic_short_salt_keys().aes_key().as_array()),
+                    "v1_mic_short_salt_mic_hmac_key": hex::encode_upper(key_seed_hkdf.v1_mic_short_salt_keys().mic_hmac_key().as_bytes()),
+                    "v1_mic_extended_salt_identity_token_hmac_key": hex::encode_upper(key_seed_hkdf.v1_mic_extended_salt_keys().identity_token_hmac_key().as_bytes()),
+                    "v1_mic_extended_salt_aes_key": hex::encode_upper(key_seed_hkdf.v1_mic_extended_salt_keys().aes_key().as_array()),
+                    "v1_mic_extended_salt_mic_hmac_key": hex::encode_upper(key_seed_hkdf.v1_mic_extended_salt_keys().mic_hmac_key().as_bytes()),
+                    "v1_signature_identity_token_hmac_key": hex::encode_upper(key_seed_hkdf.v1_signature_keys().identity_token_hmac_key().as_bytes()),
+                    "v1_signature_section_aes_key": hex::encode_upper(key_seed_hkdf.v1_signature_keys().aes_key().as_array()),
                 },
-                "legacy_adv_salt_hkdf": {
-                    "adv_salt": hex::encode_upper(legacy_adv_salt),
-                    "expanded_salt": hex::encode_upper(legacy_ldt_expanded_salt::<16, CryptoProviderImpl>(&legacy_adv_salt))
+                "v0_adv_salt_hkdf": {
+                    "adv_salt": hex::encode_upper(v0_adv_salt),
+                    "expanded_salt": hex::encode_upper(v0_ldt_expanded_salt::<CryptoProviderImpl>(&v0_adv_salt))
                 },
-                "legacy_metadata_key_hkdf": {
-                    "legacy_metadata_key": hex::encode_upper(legacy_metadata_key),
+                "v0_identity_token_hkdf": {
+                    "v0_identity_token": hex::encode_upper(v0_identity_token),
                     "expanded_key":
-                        hex::encode_upper(legacy_metadata_expanded_key::<CryptoProviderImpl>(&legacy_metadata_key))
+                        hex::encode_upper(v0_metadata_expanded_key::<CryptoProviderImpl>(&v0_identity_token))
                 },
-                "extended_section_salt_hkdf": {
-                    "section_salt": hex::encode_upper(adv_salt_bytes),
+                "v1_section_extended_salt_hkdf": {
+                    "section_extended_salt": hex::encode_upper(v1_extended_salt.bytes()),
                     // 0-based offsets -> 1-based indexing
-                    "derived_salt_first_section_no_de": hex::encode_upper(extended_adv_salt.derive::<16>(None).unwrap()),
-                    "derived_salt_first_section_first_de": hex::encode_upper(extended_adv_salt.derive::<16>(Some(0.into())).unwrap()),
-                    "derived_salt_first_section_third_de": hex::encode_upper(extended_adv_salt.derive::<16>(Some(2.into())).unwrap()),
+                    "derived_salt_nonce": hex::encode_upper(v1_extended_salt.derive::<16, CryptoProviderImpl>(None).unwrap()),
+                    "derived_salt_first_de": hex::encode_upper(v1_extended_salt.derive::<16, CryptoProviderImpl>(Some(0.into())).unwrap()),
+                    "derived_salt_third_de": hex::encode_upper(v1_extended_salt.derive::<16, CryptoProviderImpl>(Some(2.into())).unwrap()),
+                },
+                "v1_mic_section_short_salt_hkdf": {
+                    "section_short_salt": hex::encode_upper(v1_mic_short_salt),
+                    "short_salt_nonce": hex::encode_upper(extended_mic_section_short_salt_nonce::<CryptoProviderImpl>(v1_mic_short_salt)),
                 }
             }));
     }
 
     println!("{}", serde_json::ser::to_string_pretty(&array).unwrap());
+    panic!("Don't leave this test enabled. Meanwhile, enjoy the text output above.");
 }
diff --git a/nearby/presence/np_java_ffi/.gitignore b/nearby/presence/np_java_ffi/.gitignore
new file mode 100644
index 0000000..cafbf51
--- /dev/null
+++ b/nearby/presence/np_java_ffi/.gitignore
@@ -0,0 +1,8 @@
+# Ignore Gradle project-specific cache directory
+/.gradle
+
+# Ignore Gradle build output directory
+/build
+
+# Ignore IDEA dir
+/.idea
diff --git a/nearby/presence/np_java_ffi/Cargo.toml b/nearby/presence/np_java_ffi/Cargo.toml
new file mode 100644
index 0000000..556b481
--- /dev/null
+++ b/nearby/presence/np_java_ffi/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "np_java_ffi"
+version.workspace = true
+edition.workspace = true
+publish.workspace = true
+
+[lints]
+workspace = true
+
+[dependencies]
+handle_map.workspace = true
+np_adv.workspace = true
+np_ffi_core.workspace = true
+pourover.workspace = true
+pourover_macro.workspace = true
+crypto_provider_default.workspace = true # for setting features from cmdline
+
+jni.workspace = true
+
+[lib]
+# JNI wants a .so or equivalent
+crate-type = ["cdylib"]
diff --git a/nearby/presence/np_java_ffi/build.gradle.kts b/nearby/presence/np_java_ffi/build.gradle.kts
new file mode 100644
index 0000000..69d73c4
--- /dev/null
+++ b/nearby/presence/np_java_ffi/build.gradle.kts
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+plugins {
+  `java-library`
+}
+
+repositories {
+  mavenCentral()
+  google()
+}
+
+dependencies {
+  implementation("androidx.annotation:annotation:1.6.0")
+
+  // JUnit Test Support
+  testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
+  testImplementation("com.google.truth:truth:1.1.4")
+  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
+sourceSets {
+  main {
+    java {
+      setSrcDirs(listOf("java"))
+    }
+  }
+  test {
+    java {
+      setSrcDirs(listOf("test"))
+    }
+  }
+}
+
+tasks.test {
+  useJUnitPlatform()
+  jvmArgs = mutableListOf("-Djava.library.path=$projectDir/../../target/debug")
+}
diff --git a/nearby/presence/np_java_ffi/gradle/wrapper/gradle-wrapper.jar b/nearby/presence/np_java_ffi/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..943f0cb
--- /dev/null
+++ b/nearby/presence/np_java_ffi/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/nearby/presence/np_java_ffi/gradle/wrapper/gradle-wrapper.properties b/nearby/presence/np_java_ffi/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..f398c33
--- /dev/null
+++ b/nearby/presence/np_java_ffi/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+networkTimeout=10000
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/nearby/presence/np_java_ffi/gradlew b/nearby/presence/np_java_ffi/gradlew
new file mode 100755
index 0000000..79a61d4
--- /dev/null
+++ b/nearby/presence/np_java_ffi/gradlew
@@ -0,0 +1,244 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# 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
+#
+#      https://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.
+#
+
+##############################################################################
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+    echo "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD=$JAVA_HOME/jre/sh/java
+    else
+        JAVACMD=$JAVA_HOME/bin/java
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD=java
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC3045
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC3045
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+# Collect all arguments for the java command;
+#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+#     shell script including quotes and variable substitutions, so put them in
+#     double quotes to make sure that they get re-expanded; and
+#   * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializationException.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializationException.java
new file mode 100644
index 0000000..68db2c9
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializationException.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+/** Base class for exceptions that can occur during deserialization. */
+public abstract class DeserializationException extends Exception {
+
+  private DeserializationException(String message) {
+    super(message);
+  }
+
+  public static final class InvalidHeaderException extends DeserializationException {
+    public InvalidHeaderException() {
+      super("Invalid advertisement header");
+    }
+  }
+
+  public static final class InvalidFormatException extends DeserializationException {
+    public InvalidFormatException() {
+      super("Invalid advertisement format");
+    }
+  }
+}
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
new file mode 100644
index 0000000..865df41
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializeResult.java
@@ -0,0 +1,122 @@
+/*
+ * 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 java.lang.annotation.RetentionPolicy.SOURCE;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+import java.lang.annotation.Retention;
+
+/**
+ * A result of calling {@link NpAdv#deserializeAdvertisement}. This represents either an error
+ * condition or successfully deserialized advertisement.
+ *
+ * <p>Note: A successfully deserialized advertisement may still not be legible for cryptographic
+ * reasons. That condition is reported on the advertisement object itself since a V1 advertisement
+ * may be partially legible (some sections are legible, not all).
+ */
+public final class DeserializeResult implements AutoCloseable {
+
+  @IntDef({
+    Kind.UNKNOWN_ERROR,
+    Kind.V0_ADVERTISEMENT,
+    Kind.V1_ADVERTISEMENT,
+  })
+  @Retention(SOURCE)
+  public @interface Kind {
+    public static final int UNKNOWN_ERROR = -1;
+
+    public static final int V0_ADVERTISEMENT = 1;
+    public static final int V1_ADVERTISEMENT = 2;
+  }
+
+  /** Checks if a {@link Kind} represents and error or not. */
+  public static boolean isErrorKind(@Kind int kind) {
+    return kind <= 0;
+  }
+
+  private final @Kind int kind;
+  private final @Nullable DeserializedAdvertisement advertisement;
+
+  /** Create a DeserializeResult containing an error code */
+  /* package */ DeserializeResult(@Kind int errorKind) {
+    if (!isErrorKind(errorKind)) {
+      throw new IllegalArgumentException(
+          "Cannot create empty DeserializeResult with non-error kind");
+    }
+    this.kind = errorKind;
+    this.advertisement = null;
+  }
+
+  /** Create a DeserializeResult containing a V0 advertisement */
+  /* package */ DeserializeResult(DeserializedV0Advertisement advertisement) {
+    this.kind = Kind.V0_ADVERTISEMENT;
+    this.advertisement = advertisement;
+  }
+
+  /** Create a DeserializeResult containing a V1 advertisement */
+  /* package */ DeserializeResult(DeserializedV1Advertisement advertisement) {
+    this.kind = Kind.V1_ADVERTISEMENT;
+    this.advertisement = advertisement;
+  }
+
+  /** Gets the kind of this result. */
+  @Kind
+  public int getKind() {
+    return kind;
+  }
+
+  /** Check if this result is an error result. */
+  public boolean isError() {
+    return isErrorKind(kind);
+  }
+
+  /**
+   * Gets the contained V0 advertisement if present.
+   *
+   * @return the contained V0 advertisement or {@code null} if not present
+   */
+  @Nullable
+  public DeserializedV0Advertisement getAsV0() {
+    if (this.kind != Kind.V0_ADVERTISEMENT) {
+      return null;
+    }
+    return (DeserializedV0Advertisement) this.advertisement;
+  }
+
+  /**
+   * Gets the contained V1 advertisement if present.
+   *
+   * @return the contained V1 advertisement or {@code null} if not present
+   */
+  @Nullable
+  public DeserializedV1Advertisement getAsV1() {
+    if (this.kind != Kind.V1_ADVERTISEMENT) {
+      return null;
+    }
+    return (DeserializedV1Advertisement) this.advertisement;
+  }
+
+  /** Closes the contained advertisement if it exists. */
+  @Override
+  public void close() {
+    if (this.advertisement != null) {
+      this.advertisement.close();
+    }
+  }
+}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedAdvertisement.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedAdvertisement.java
new file mode 100644
index 0000000..2058d8c
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedAdvertisement.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+/** A base class to provide a common type to V0 and V1 advertisements. */
+public abstract class DeserializedAdvertisement implements AutoCloseable {
+
+  /* package */ DeserializedAdvertisement() {}
+
+  // Assert that {@code close()} will not throw checked exceptions.
+  @Override
+  public abstract void close();
+}
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
new file mode 100644
index 0000000..e4a0688
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV0Advertisement.java
@@ -0,0 +1,155 @@
+/*
+ * 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.Nullable;
+import java.util.Iterator;
+
+/**
+ * A deserialized V0 advertisement. This class is backed by native data behind the {@link V0Payload}
+ * 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.
+ */
+public final class DeserializedV0Advertisement extends DeserializedAdvertisement {
+
+  public static boolean isLegibleIdentity(@IdentityKind int identity) {
+    return identity > 0;
+  }
+
+  private final int numDataElements;
+  private final @Nullable V0Payload payload;
+  private final @IdentityKind int identity;
+
+  /** Create an illegible instance with the given error identity. */
+  /* package */ DeserializedV0Advertisement(@IdentityKind int illegibleIdentity) {
+    if (isLegibleIdentity(illegibleIdentity)) {
+      throw new IllegalArgumentException(
+          "Cannot create empty DeserializedV0Advertisement with a legible identity");
+    }
+    this.numDataElements = 0;
+    this.payload = null;
+    this.identity = illegibleIdentity;
+  }
+
+  /** Create a legible instance with the given information. */
+  /* package */ DeserializedV0Advertisement(
+      int numDataElements, V0Payload payload, @IdentityKind int identity) {
+    this.numDataElements = numDataElements;
+    this.payload = payload;
+    this.identity = identity;
+  }
+
+  /**
+   * Create a legible instance with the given information. Payload is specified as a raw handle id.
+   * This is a helper to be called from native code to avoid needing to construct {@code V0Payload}
+   * on the native side.
+   */
+  /* package */ DeserializedV0Advertisement(
+      int numDataElements, long payload, @IdentityKind int identity) {
+    this(numDataElements, new V0Payload(payload), identity);
+  }
+
+  /** Check if this advertisement is legible */
+  public boolean isLegible() {
+    return isLegibleIdentity(this.identity);
+  }
+
+  /** Throws {@code IllegalStateException} if this advertisement is not legible. */
+  private void ensureLegible(String action) {
+    if (!isLegible()) {
+      throw new IllegalStateException(
+          String.format("Cannot %s for non-legible advertisement", action));
+    }
+  }
+
+  /**
+   * Gets the identity for this advertisement.
+   *
+   * @throws IllegalStateException if the advertisement is not legible ({@link #isLegible()}).
+   */
+  @IdentityKind
+  public int getIdentity() {
+    ensureLegible("get identity");
+    return this.identity;
+  }
+
+  /**
+   * Gets the number of data elements in this advertisement.
+   *
+   * @throws IllegalStateException if the advertisement is not legible ({@link #isLegible()}).
+   */
+  public int getDataElementCount() {
+    ensureLegible("get data element count");
+    return this.numDataElements;
+  }
+
+  /**
+   * Gets the data element at the given {@code index} in this advertisement.
+   *
+   * @param index The data element's index in the advertisement
+   * @throws IllegalStateException if the advertisement is not legible ({@link #isLegible()}).
+   * @throws IndexOutOfBoundsException if the index is invalid
+   * @return The data element at {@code index}
+   */
+  public V0DataElement getDataElement(int index) {
+    ensureLegible("get data element");
+    return payload.getDataElement(index);
+  }
+
+  /** Gets all the data elements for iteration. */
+  public Iterable<V0DataElement> getDataElements() {
+    return () -> new DataElementIterator(payload, numDataElements);
+  }
+
+  /** Visits all the data elements with the given visitor. */
+  public void visitDataElements(V0DataElement.Visitor v) {
+    for (V0DataElement de : getDataElements()) {
+      de.visit(v);
+    }
+  }
+
+  /** Iterator instance for data elements in DeserializedV0Advertisement. */
+  private static final class DataElementIterator implements Iterator<V0DataElement> {
+    private final V0Payload payload;
+    private final int numDataElements;
+
+    private int position = 0;
+
+    public DataElementIterator(V0Payload payload, int numDataElements) {
+      this.payload = payload;
+      this.numDataElements = numDataElements;
+    }
+
+    @Override
+    public boolean hasNext() {
+      return position < (numDataElements - 1);
+    }
+
+    @Override
+    public V0DataElement next() {
+      return payload.getDataElement(position++);
+    }
+  }
+
+  /** Closes the payload handle if this advertisement is legible. */
+  @Override
+  public void close() {
+    if (this.payload != null) {
+      this.payload.close();
+    }
+  }
+}
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
new file mode 100644
index 0000000..d1ecdcd
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Advertisement.java
@@ -0,0 +1,97 @@
+/*
+ * 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 java.util.Iterator;
+
+/**
+ * A deserialized V0 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.
+ */
+public final class DeserializedV1Advertisement extends DeserializedAdvertisement {
+
+  private final int numLegibleSections;
+  private final int numUndecryptableSections;
+  private final LegibleV1Sections legibleSections;
+
+  /** Create a legible instance with the given information. */
+  /* package */ DeserializedV1Advertisement(
+      int numLegibleSections, int numUndecryptableSections, LegibleV1Sections legibleSections) {
+    this.numLegibleSections = numLegibleSections;
+    this.numUndecryptableSections = numUndecryptableSections;
+    this.legibleSections = legibleSections;
+  }
+
+  /** Get the number of legible sections in this advertisement */
+  public int getNumLegibleSections() {
+    return numLegibleSections;
+  }
+
+  /** Get the number of undecryptable sections in this advertisement */
+  public int getNumUndecryptableSections() {
+    return numUndecryptableSections;
+  }
+
+  /**
+   * Gets the section at the given {@code index} in this advertisement. {@code index} only counts
+   * legible sections.
+   *
+   * @param index The section's index in the advertisement
+   * @throws IndexOutOfBoundsException if the index is invalid
+   * @return The section at {@code index}
+   */
+  public DeserializedV1Section getSection(int index) {
+    return legibleSections.getSection(index);
+  }
+
+  /** Get an iterable of this advertisement's legible sections. */
+  public Iterable<DeserializedV1Section> getSections() {
+    return () -> new SectionIterator(numLegibleSections, legibleSections);
+  }
+
+  /** Iterator instance for sections in DeserializedV1Advertisement. */
+  private static final class SectionIterator implements Iterator<DeserializedV1Section> {
+    private final LegibleV1Sections legibleSections;
+    private final int numSections;
+
+    private int position = 0;
+
+    public SectionIterator(int numSections, LegibleV1Sections legibleSections) {
+      this.numSections = numSections;
+      this.legibleSections = legibleSections;
+    }
+
+    @Override
+    public boolean hasNext() {
+      return position < (numSections - 1);
+    }
+
+    @Override
+    public DeserializedV1Section next() {
+      return legibleSections.getSection(position++);
+    }
+  }
+
+  /** Closes the legible sections handle if it exists. */
+  @Override
+  public void close() {
+    if (this.legibleSections != null) {
+      this.legibleSections.close();
+    }
+  }
+}
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
new file mode 100644
index 0000000..1667366
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Section.java
@@ -0,0 +1,95 @@
+/*
+ * 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 java.util.Iterator;
+
+public final class DeserializedV1Section {
+  private final LegibleV1Sections legibleSections;
+  private final int legibleSectionsIndex;
+  private final int numDataElements;
+  private final @IdentityKind int identityTag;
+
+  /* package */ DeserializedV1Section(
+      LegibleV1Sections legibleSections,
+      int legibleSectionsIndex,
+      int numDataElements,
+      @IdentityKind int identityTag) {
+    this.legibleSections = legibleSections;
+    this.legibleSectionsIndex = legibleSectionsIndex;
+    this.numDataElements = numDataElements;
+    this.identityTag = identityTag;
+  }
+
+  /** Gets the identity kind for this section. */
+  @IdentityKind
+  public int getIdentityKind() {
+    return this.identityTag;
+  }
+
+  /** Gets the number of data elements in this section. */
+  public int getDataElementCount() {
+    return this.numDataElements;
+  }
+
+  /**
+   * 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) {
+    return legibleSections.getSectionDataElement(this.legibleSectionsIndex, index);
+  }
+
+  /** Gets all the data elements for iteration. */
+  public Iterable<V1DataElement> getDataElements() {
+    return () -> new DataElementIterator(legibleSections, legibleSectionsIndex, numDataElements);
+  }
+
+  /** Visits all the data elements with the given visitor. */
+  public void visitDataElements(V1DataElement.Visitor v) {
+    for (V1DataElement de : getDataElements()) {
+      de.visit(v);
+    }
+  }
+
+  private static final class DataElementIterator implements Iterator<V1DataElement> {
+    private final LegibleV1Sections legibleSections;
+    private final int legibleSectionsIndex;
+    private final int numDataElements;
+
+    private int position = 0;
+
+    public DataElementIterator(
+        LegibleV1Sections legibleSections, int legibleSectionsIndex, int numDataElements) {
+      this.legibleSections = legibleSections;
+      this.legibleSectionsIndex = legibleSectionsIndex;
+      this.numDataElements = numDataElements;
+    }
+
+    @Override
+    public boolean hasNext() {
+      return position < (numDataElements - 1);
+    }
+
+    @Override
+    public V1DataElement next() {
+      return legibleSections.getSectionDataElement(legibleSectionsIndex, position++);
+    }
+  }
+}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/Handle.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/Handle.java
new file mode 100644
index 0000000..a200358
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/Handle.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+/**
+ * A handle to a natively-allocated object. This class should be subclassed in order to provide a
+ * type for the native object. This class does not control the lifetime of the native object. See
+ * {@link OwnedHandle} for a variant that allows Java to deallocate the native object.
+ *
+ * <p>This may be a handle to an object that has already been deallocated. In that case, any native
+ * uses of this handle should throw {@link Handle.InvalidHandleException}.
+ */
+public abstract class Handle {
+
+  /** Thrown when an invalid handle is used */
+  public static final class InvalidHandleException extends Exception {
+    public InvalidHandleException() {
+      super("The given handle is no longer valid");
+    }
+  }
+
+  protected final long handleId;
+
+  protected Handle(long handleId) {
+    this.handleId = handleId;
+  }
+
+  public long getId() {
+    return handleId;
+  }
+}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/IdentityKind.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/IdentityKind.java
new file mode 100644
index 0000000..9b69851
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/IdentityKind.java
@@ -0,0 +1,43 @@
+/*
+ * 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 java.lang.annotation.RetentionPolicy.SOURCE;
+
+import androidx.annotation.IntDef;
+import java.lang.annotation.Retention;
+
+/**
+ * The kind of identity that was discovered for a decrypted advertisement. Values greater than
+ * {@code 0} are legible. Values less than {@code 0} are not.
+ */
+@IntDef({
+  IdentityKind.NO_MATCHING_CREDENTIALS,
+  IdentityKind.PLAINTEXT,
+  IdentityKind.DECRYPTED,
+})
+@Retention(SOURCE)
+public @interface IdentityKind {
+  /** An encrypted identity that we do not have a credential for. */
+  public static final int NO_MATCHING_CREDENTIALS = -1;
+
+  /** A plaintext identity. */
+  public static final int PLAINTEXT = 1;
+
+  /** An encrypted identity that we have a credential for. */
+  public static final int DECRYPTED = 2;
+}
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
new file mode 100644
index 0000000..5175ce6
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/LegibleV1Sections.java
@@ -0,0 +1,80 @@
+/*
+ * 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.Nullable;
+import java.lang.ref.Cleaner;
+
+/** Internal handle for a V1 deserialized advertisement. */
+public final class LegibleV1Sections extends OwnedHandle {
+
+  static {
+    System.loadLibrary(NpAdv.LIBRARY_NAME);
+  }
+
+  /**
+   * Create a LegibleV1Sections handle from the raw handle id. This will use the default cleaner
+   * form {@code NpAdv#getCleaner()}. This is expected to be called from native code.
+   */
+  /* package-visible */ LegibleV1Sections(long handleId) {
+    this(handleId, NpAdv.getCleaner());
+  }
+
+  /** Create a LegibleV1Sections handle from the raw handle id. */
+  /* package-visible */ LegibleV1Sections(long handleId, Cleaner cleaner) {
+    super(handleId, cleaner, LegibleV1Sections::deallocate);
+  }
+
+  /**
+   * Get the section at the given index.
+   *
+   * @param index The section's index in the advertisement
+   * @throws IndexOutOfBoundsException if the given index is out of range for this advertisement
+   * @return The section at that index
+   */
+  public DeserializedV1Section getSection(int index) {
+    DeserializedV1Section section = nativeGetSection(index);
+    if (section == null) {
+      throw new IndexOutOfBoundsException();
+    }
+    return section;
+  }
+
+  /**
+   * Get the data element from a specific section.
+   *
+   * @param sectionIndex The section's index in the advertisement. This only counts legible sections
+   * @param deIndex The data element's index in the section
+   * @throws IndexOutOfBoundsException if either index is out of range for this advertisement
+   * @return The data element found at {@code deIndex} in the section at {@code sectionIndex}
+   */
+  public V1DataElement getSectionDataElement(int sectionIndex, int deIndex) {
+    V1DataElement de = nativeGetSectionDataElement(sectionIndex, deIndex);
+    if (de == null) {
+      throw new IndexOutOfBoundsException();
+    }
+    return de;
+  }
+
+  @Nullable
+  private native DeserializedV1Section nativeGetSection(int index);
+
+  @Nullable
+  private native V1DataElement nativeGetSectionDataElement(int sectionIndex, int deIndex);
+
+  private static native void deallocate(long handleId);
+}
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
new file mode 100644
index 0000000..21abb89
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/NpAdv.java
@@ -0,0 +1,91 @@
+/*
+ * 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.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>
+ *   <li>Deserialize advertisements: {@link #deserializeAdvertisement}
+ * </ul>
+ */
+public final class NpAdv {
+
+  public static final String LIBRARY_NAME = "np_java_ffi";
+
+  static {
+    System.loadLibrary(LIBRARY_NAME);
+  }
+
+  private static @Nullable Cleaner CLEANER = null;
+
+  /**
+   * Deserialize a Nearby Presence advertisement from its service data bytes.
+   *
+   * @param serviceData The service data bytes. Must have length&lt;256.
+   * @param credentialBook The credential book to use to decrypt.
+   * @return A result containing the advertisement if it was able to be deserialized.
+   */
+  public static <M extends CredentialBook.MatchedMetadata>
+      DeserializeResult deserializeAdvertisement(
+          byte[] serviceData, CredentialBook<M> credentialBook) {
+    DeserializeResult result = nativeDeserializeAdvertisement(serviceData, credentialBook.getId());
+    if (result == null) {
+      result = new DeserializeResult(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.
+   */
+  public static synchronized Cleaner getCleaner() {
+    if (CLEANER == null) {
+      CLEANER = Cleaner.create();
+    }
+    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;
+  }
+
+  @Nullable
+  private static native DeserializeResult nativeDeserializeAdvertisement(
+      byte[] serviceData, long 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
new file mode 100644
index 0000000..c505f81
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/OwnedHandle.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.nearby.presence.rust;
+
+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
+ * supports deallocating the native object.
+ *
+ * <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.
+ */
+public abstract class OwnedHandle extends Handle implements AutoCloseable {
+
+  /**
+   * A destructor to be called when this {@link OwnedHandle} is no longer used.
+   *
+   * <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.
+   */
+  public interface Destructor {
+    void deallocate(long handleId);
+  }
+
+  /** Thrown when a new handle cannot be allocated due to lack of space */
+  public static final class NoSpaceLeftException extends RuntimeException {
+    public NoSpaceLeftException() {
+      super("No space remaining in the associated HandleMap");
+    }
+  }
+
+  private final CleanupAction cleanupAction;
+
+  /**
+   * Create a new instance and register it with the given cleaner.
+   *
+   * @param handleId The handle's id
+   * @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) {
+    super(handleId);
+    this.cleanupAction = new CleanupAction(handleId, destructor);
+
+    cleaner.register(this, this.cleanupAction);
+  }
+
+  /** Leak this handle. The associated native object will not be deallocated. */
+  protected final void leak() {
+    this.cleanupAction.leak();
+  }
+
+  /** Implement AutoCloseable for try-with-resources support */
+  @Override
+  public final void close() {
+    this.cleanupAction.cleanupFromCloseable();
+  }
+
+  /**
+   * A {@link Runnable} to be given to the associated {@link Cleaner} to clean up a handle. This
+   * MUST not hold a reference to the {@link OwnedHandle} that it is associated with.
+   */
+  private static final class CleanupAction implements Runnable {
+    private final long handleId;
+    private @Nullable Destructor destructor;
+    private boolean freed = false;
+
+    public CleanupAction(long handleId, Destructor destructor) {
+      this.handleId = handleId;
+      this.destructor = destructor;
+    }
+
+    /** Skip performing cleanup and leak the object instead */
+    private void leak() {
+      this.destructor = null;
+    }
+
+    /**
+     * Deallocate the handle using the given {@link Destructor}.
+     *
+     * <p>The Destructor will only be called once, and future calls to this method will return
+     * {@code false}.
+     *
+     * @return {@code true} if the destructor was called.
+     */
+    private boolean deallocate() {
+      if (this.destructor != null) {
+        this.destructor.deallocate(this.handleId);
+        this.destructor = null;
+        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.
+      }
+    }
+  }
+}
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
new file mode 100644
index 0000000..4575f5a
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0DataElement.java
@@ -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.
+ */
+
+package com.google.android.nearby.presence.rust;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import androidx.annotation.IntDef;
+import java.lang.annotation.Retention;
+
+/** Base class for V0 data element types. */
+public abstract class V0DataElement {
+
+  /**
+   * A visitor interface that can be used while iterating data elements in an advertisement to avoid
+   * checking {@code instanceof} on every one.
+   */
+  public interface Visitor {
+    default void visitTxPower(TxPower txPower) {}
+
+    default void visitV0Actions(V0Actions v0Actions) {}
+  }
+
+  // All subclasses should be in this file
+  private V0DataElement() {}
+
+  /** Visit this advertisement with the given visitor. */
+  public abstract void visit(Visitor v);
+
+  /** Contains the TxPower information. See the spec for more information. */
+  public static final class TxPower extends V0DataElement {
+    private final int txPower;
+
+    public TxPower(int txPower) {
+      this.txPower = txPower;
+    }
+
+    public int getTxPower() {
+      return txPower;
+    }
+
+    public void visit(Visitor v) {
+      v.visitTxPower(this);
+    }
+  }
+
+  /** Marker annotation/enum for V0 action values. */
+  @IntDef({
+    V0ActionType.CROSS_DEV_SDK,
+    V0ActionType.CALL_TRANSFER,
+    V0ActionType.ACTIVE_UNLOCK,
+    V0ActionType.NEARBY_SHARE,
+    V0ActionType.INSTANT_TETHERING,
+    V0ActionType.PHONE_HUB,
+  })
+  @Retention(SOURCE)
+  public @interface V0ActionType {
+    // NOTE: Copied from `np_ffi_core::v0::BooleanActionType`.
+    public static final int CROSS_DEV_SDK = 1;
+    public static final int CALL_TRANSFER = 4;
+    public static final int ACTIVE_UNLOCK = 8;
+    public static final int NEARBY_SHARE = 9;
+    public static final int INSTANT_TETHERING = 10;
+    public static final int PHONE_HUB = 11;
+  }
+
+  /** The Actions data element. See the spec for more information. */
+  public static final class V0Actions extends V0DataElement {
+    static {
+      System.loadLibrary(NpAdv.LIBRARY_NAME);
+    }
+
+    private final @IdentityKind int identityKind;
+    private final int actionBits;
+
+    public V0Actions(@IdentityKind int identityKind, int actionBits) {
+      this.identityKind = identityKind;
+      this.actionBits = actionBits;
+    }
+
+    @IdentityKind
+    public int getIdentityKind() {
+      return identityKind;
+    }
+
+    public int getActionBits() {
+      return actionBits;
+    }
+
+    /** Checks if this Actions DE contains the given action. */
+    public boolean hasAction(@V0ActionType int action) {
+      return nativeHasAction(identityKind, actionBits, action);
+    }
+
+    public void visit(Visitor v) {
+      v.visitV0Actions(this);
+    }
+
+    private static native boolean nativeHasAction(int identityKind, int actionBits, int action);
+  }
+}
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
new file mode 100644
index 0000000..722f37c
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0Payload.java
@@ -0,0 +1,64 @@
+/*
+ * 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.Nullable;
+import java.lang.ref.Cleaner;
+
+/**
+ * Internal handle type for deserialized V0 advertisements. It provides access to the native data
+ * and allows that data to be deallocated.
+ */
+public final class V0Payload extends OwnedHandle {
+
+  static {
+    System.loadLibrary(NpAdv.LIBRARY_NAME);
+  }
+
+  /**
+   * Create a V0Payload handle from the raw handle id. This will use the default cleaner form {@code
+   * NpAdv#getCleaner()}. This is expected to be called from native code.
+   */
+  /* package-visible */ V0Payload(long handleId) {
+    this(handleId, NpAdv.getCleaner());
+  }
+
+  /** Create a V0Payload handle from the raw handle id. */
+  /* package-visible */ V0Payload(long handleId, Cleaner cleaner) {
+    super(handleId, cleaner, V0Payload::deallocate);
+  }
+
+  /**
+   * Get the data element at the given index.
+   *
+   * @param index The data element's index in the advertisement
+   * @throws IndexOutOfBoundsException if the given index is out of range for this advertisement
+   * @return The data element at that index
+   */
+  public V0DataElement getDataElement(int index) {
+    V0DataElement de = nativeGetDataElement(this.handleId, index);
+    if (de == null) {
+      throw new IndexOutOfBoundsException();
+    }
+    return de;
+  }
+
+  @Nullable
+  private static native V0DataElement nativeGetDataElement(long handleId, int index);
+
+  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
new file mode 100644
index 0000000..b52c7bb
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1DataElement.java
@@ -0,0 +1,63 @@
+/*
+ * 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 java.util.Arrays;
+
+/** Base class for V1 data element types. */
+public abstract class V1DataElement {
+
+  /**
+   * A visitor interface that can be used while iterating data elements in an advertisement to avoid
+   * checking {@code instanceof} on every one.
+   */
+  public interface Visitor {
+    void visitGeneric(Generic generic);
+  }
+
+  // All subclasses should be in this file
+  private V1DataElement() {}
+
+  /** Visit this advertisement with the given visitor. */
+  public abstract void visit(Visitor v);
+
+  /**
+   * A generic data element. This is a data element which has a type that is not known by this
+   * library (e.g. a vendor-specific data element).
+   */
+  public static final class Generic extends V1DataElement {
+    private final long type;
+    private final byte[] data;
+
+    public Generic(long type, byte[] data) {
+      this.type = type;
+      this.data = Arrays.copyOf(data, data.length);
+    }
+
+    public long getType() {
+      return type;
+    }
+
+    public byte[] getData() {
+      return Arrays.copyOf(data, data.length);
+    }
+
+    public void visit(Visitor v) {
+      v.visitGeneric(this);
+    }
+  }
+}
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
new file mode 100644
index 0000000..cdcc50b
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialBook.java
@@ -0,0 +1,140 @@
+/*
+ * 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 com.google.android.nearby.presence.rust.NpAdv;
+import com.google.android.nearby.presence.rust.OwnedHandle;
+import java.lang.ref.Cleaner;
+import java.util.ArrayList;
+
+public final class CredentialBook<M extends CredentialBook.MatchedMetadata> extends OwnedHandle {
+
+  static {
+    System.loadLibrary(NpAdv.LIBRARY_NAME);
+  }
+
+  /** Metadata that is associated with a Credential. */
+  public interface MatchedMetadata {
+    /**
+     * Get the bytes for encrypted metadata. This byte array may be empty if there is no encrypted
+     * metadata.
+     */
+    byte[] getEncryptedMetadataBytes();
+  }
+
+  /** {@link MatchedMetadata} implementation for cases where there is no associated metadata. */
+  public static class NoMetadata implements MatchedMetadata {
+
+    /** An instance to avoid needing additional instances of this class. */
+    public static final NoMetadata INSTANCE = new NoMetadata();
+
+    @Override
+    public byte[] getEncryptedMetadataBytes() {
+      return new byte[0];
+    }
+  }
+
+  /** Thrown when a cryptographic key is given and those key bytes are not valid. */
+  public static final class InvalidPublicKeyException extends RuntimeException {
+    public InvalidPublicKeyException() {
+      super(
+          "The provided public key bytes do not actually represent a valid \"edwards y\" format or"
+              + " that said compressed point is not actually a point on the curve.");
+    }
+  }
+
+  /** Builder for {@code CredentialBook} */
+  public static final class Builder<M extends MatchedMetadata> {
+    private Cleaner cleaner;
+    private CredentialSlab slab;
+
+    // Each credential should be given an id of its metadata index so that this array is an
+    // id-to-metadata map.
+    private ArrayList<M> matchDataList;
+
+    /**
+     * 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.
+     *
+     * @param cleaner The cleaner instance to use for the {@link CredentialSlab} and {@code
+     *     CredentialBook}.
+     */
+    public Builder(Cleaner cleaner) {
+      this.cleaner = cleaner;
+      this.slab = new CredentialSlab(cleaner);
+      this.matchDataList = new ArrayList<>();
+    }
+
+    /** Add a {@link V0DiscoveryCredential} to the book. */
+    public Builder<M> addDiscoveryCredential(V0DiscoveryCredential credential, M matchData) {
+      int credIdx = matchDataList.size();
+      matchDataList.add(matchData);
+      slab.addDiscoveryCredential(credential, credIdx, matchData.getEncryptedMetadataBytes());
+      return this;
+    }
+
+    /**
+     * Add a {@link V1DiscoveryCredential} to the book. May throw {@link
+     * CredentialBook.InvalidPublicKeyException} if the key inside {@code credential} is improperly
+     * formatted.
+     */
+    public Builder<M> addDiscoveryCredential(V1DiscoveryCredential credential, M matchData) {
+      int credIdx = matchDataList.size();
+      matchDataList.add(matchData);
+      slab.addDiscoveryCredential(credential, credIdx, matchData.getEncryptedMetadataBytes());
+      return this;
+    }
+
+    /**
+     * Create the {@code CredentialBook}. This can fail if there isn't room to create a new {@code
+     * CredentialBook} handle.
+     */
+    public CredentialBook<M> build() {
+      return new CredentialBook(slab, matchDataList, cleaner);
+    }
+  }
+
+  /** Create a credential book builder with the default cleaner from {@link NpAdv#getCleaner()}. */
+  public static <M extends MatchedMetadata> Builder<M> builder() {
+    return new Builder<M>(NpAdv.getCleaner());
+  }
+
+  /**
+   * Create an empty credential book. This is useful for when only plaintext advertisements are
+   * being deserialized.
+   */
+  public static CredentialBook<NoMetadata> empty() {
+    return new Builder<NoMetadata>(NpAdv.getCleaner()).build();
+  }
+
+  private final ArrayList<M> matchData;
+
+  /**
+   * Create a new credential book. This always consumes the slab handle. This should only be called
+   * 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) {
+    super(allocate(slab.move()), cleaner, CredentialBook::deallocate);
+    this.matchData = matchData;
+  }
+
+  private static native long allocate(long slabHandleId);
+
+  private static native void deallocate(long handleId);
+}
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
new file mode 100644
index 0000000..35fc610
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialSlab.java
@@ -0,0 +1,71 @@
+/*
+ * 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 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
+ * CredentialBook}. Clients should use {@link CredentialSlab.Builder} instead of this class.
+ */
+final class CredentialSlab extends OwnedHandle {
+  static {
+    System.loadLibrary(NpAdv.LIBRARY_NAME);
+  }
+
+  /** Create a new {@code CredentialSlab} with the given {@code cleaner}. */
+  public CredentialSlab(Cleaner 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);
+  }
+
+  /**
+   * Add a V1 discovery credential to this slab. May throw {@link
+   * CredentialBook.InvalidKeyException} if the key inside {@code credential} is improperly
+   * formatted.
+   */
+  public void addDiscoveryCredential(
+      V1DiscoveryCredential credential, int credId, byte[] encryptedMetadataBytes) {
+    nativeAddV1DiscoveryCredential(handleId, credential, credId, encryptedMetadataBytes);
+  }
+
+  /**
+   * Mark this slab handle as moved and return the handle id. This will leak the handle. This should
+   * be done when the handle is moved to the Rust side to avoid freeing it early.
+   */
+  public long move() {
+    leak();
+    return handleId;
+  }
+
+  private static native long allocate();
+
+  private static native boolean nativeAddV0DiscoveryCredential(
+      long handleId, V0DiscoveryCredential cred, int credId, byte[] encryptedMetadataBytes);
+
+  private static native boolean nativeAddV1DiscoveryCredential(
+      long handleId, V1DiscoveryCredential cred, int credId, byte[] encryptedMetadataBytes);
+
+  private static native void deallocate(long handleId);
+}
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
new file mode 100644
index 0000000..c38bbcf
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/Utils.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.nearby.presence.rust.credential;
+
+import java.util.Arrays;
+
+/** Util functions used by multiple files. */
+final class Utils {
+
+  /**
+   * Create a copy of a 32-byte array of key data. Will throw {@code IllegalArgumentException} if
+   * the array is not exactly 32 bytes.
+   */
+  public static byte[] copyKeyBytes(byte[] key) {
+    if (key.length != 32) {
+      throw new IllegalArgumentException(
+          String.format("Expected key length to be 32 bytes, got %s bytes", key.length));
+    }
+    return Arrays.copyOf(key, key.length);
+  }
+}
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
new file mode 100644
index 0000000..be8734b
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0DiscoveryCredential.java
@@ -0,0 +1,31 @@
+/*
+ * 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.android.nearby.presence.rust.credential.Utils.copyKeyBytes;
+
+/** A V0 discovery credential in a format that is ready to be passed to native code. */
+public final class V0DiscoveryCredential {
+  private final byte[] keySeed;
+  private final byte[] identityTokenHmac;
+
+  /** Create the credential. Each array is exactly 32 bytes. */
+  public V0DiscoveryCredential(byte[] keySeed, byte[] identityTokenHmac) {
+    this.keySeed = copyKeyBytes(keySeed);
+    this.identityTokenHmac = copyKeyBytes(identityTokenHmac);
+  }
+}
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
new file mode 100644
index 0000000..c2afefa
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1DiscoveryCredential.java
@@ -0,0 +1,44 @@
+/*
+ * 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.android.nearby.presence.rust.credential.Utils.copyKeyBytes;
+
+/** A V1 discovery credential in a format that is ready to be passed to native code. */
+public final class V1DiscoveryCredential {
+  private final byte[] keySeed;
+  private final byte[] expectedMicShortSaltIdentityTokenHmac;
+  private final byte[] expectedMicExtendedSaltIdentityTokenHmac;
+  private final byte[] expectedSignatureIdentityTokenHmac;
+  private final byte[] pubKey;
+
+  /** Create the credential. Each array is exactly 32 bytes. */
+  public V1DiscoveryCredential(
+      byte[] keySeed,
+      byte[] expectedMicShortSaltIdentityTokenHmac,
+      byte[] expectedMicExtendedSaltIdentityTokenHmac,
+      byte[] expectedSignatureIdentityTokenHmac,
+      byte[] pubKey) {
+    this.keySeed = copyKeyBytes(keySeed);
+    this.expectedMicShortSaltIdentityTokenHmac =
+        copyKeyBytes(expectedMicShortSaltIdentityTokenHmac);
+    this.expectedMicExtendedSaltIdentityTokenHmac =
+        copyKeyBytes(expectedMicExtendedSaltIdentityTokenHmac);
+    this.expectedSignatureIdentityTokenHmac = copyKeyBytes(expectedSignatureIdentityTokenHmac);
+    this.pubKey = copyKeyBytes(pubKey);
+  }
+}
diff --git a/nearby/presence/np_java_ffi/settings.gradle.kts b/nearby/presence/np_java_ffi/settings.gradle.kts
new file mode 100644
index 0000000..0c9915a
--- /dev/null
+++ b/nearby/presence/np_java_ffi/settings.gradle.kts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+rootProject.name = "np-java-ffi"
diff --git a/nearby/presence/np_java_ffi/src/class.rs b/nearby/presence/np_java_ffi/src/class.rs
new file mode 100644
index 0000000..9c07137
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class.rs
@@ -0,0 +1,62 @@
+// 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.
+
+//! Rust wrappers for Java classes.
+//!
+//! The pattern being used here it to create a new wrapper type for each Java class being used by
+//! this library. Each wrapper type will implement the required accessors for its members so that
+//! JNI code using a member can be easily found in case that member is changed. Native methods will
+//! also be implemented along side the wrapper of the class they are implementing.
+//!
+//! This library is primarily meant to be used from Java. The Java entrypoint to this library is
+//! `class NpAdv`.
+
+use jni::JNIEnv;
+
+/// Trait to allow Java exceptions to be thrown from rust Errors
+pub trait ToJavaException {
+    /// Convert this error to a Java exception and throw it.
+    fn throw_java_exception<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()>;
+}
+
+mod credential_book;
+mod credential_slab;
+mod deserialization_exception;
+mod deserialize_result;
+mod deserialized_v0_advertisement;
+mod deserialized_v1_advertisement;
+mod deserialized_v1_section;
+mod handle;
+mod identity_kind;
+mod legible_v1_sections;
+mod np_adv;
+mod owned_handle;
+mod v0_discovery_credential;
+mod v0_payload;
+mod v1_discovery_credential;
+
+pub mod v0_data_element;
+pub mod v1_data_element;
+
+pub use deserialization_exception::{InvalidFormatException, InvalidHeaderException};
+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;
+pub use handle::InvalidHandleException;
+pub use identity_kind::IdentityKind;
+pub use legible_v1_sections::LegibleV1Sections;
+pub use owned_handle::NoSpaceLeftException;
+pub use v0_discovery_credential::V0DiscoveryCredential;
+pub use v1_discovery_credential::V1DiscoveryCredential;
diff --git a/nearby/presence/np_java_ffi/src/class/credential_book.rs b/nearby/presence/np_java_ffi/src/class/credential_book.rs
new file mode 100644
index 0000000..ea371da
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/credential_book.rs
@@ -0,0 +1,65 @@
+// 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 jni::{objects::JClass, sys::jlong, JNIEnv};
+
+use crate::class::{InvalidHandleException, NoSpaceLeftException};
+use handle_map::{Handle, HandleLike};
+use np_ffi_core::credentials::{
+    create_credential_book_from_slab, deallocate_credential_book, CreateCredentialBookResult,
+    CredentialBook, CredentialSlab,
+};
+use pourover::jni_method;
+
+#[jni_method(
+    package = "com.google.android.nearby.presence.rust.credential",
+    class = "CredentialBook",
+    method_name = "allocate"
+)]
+extern "system" fn allocate_book<'local>(
+    mut env: JNIEnv<'local>,
+    _cls: JClass<'local>,
+    slab_handle_id: jlong,
+) -> jlong {
+    let slab = CredentialSlab::from_handle(Handle::from_id(slab_handle_id as u64));
+
+    match create_credential_book_from_slab(slab) {
+        CreateCredentialBookResult::Success(handle) => handle.get_as_handle().get_id() as jlong,
+        CreateCredentialBookResult::InvalidSlabHandle => {
+            let _ = InvalidHandleException::throw_new(&mut env);
+            0
+        }
+        CreateCredentialBookResult::NoSpaceLeft => {
+            // Make sure slab is consumed.
+            let _ = slab.deallocate();
+            let _ = NoSpaceLeftException::throw_new(&mut env);
+            0
+        }
+    }
+}
+
+#[jni_method(
+    package = "com.google.android.nearby.presence.rust.credential",
+    class = "CredentialBook",
+    method_name = "deallocate"
+)]
+extern "system" fn deallocate_book<'local>(
+    _env: JNIEnv<'local>,
+    _cls: JClass<'local>,
+    handle_id: jlong,
+) {
+    // Swallow errors here since there's nothing meaningful to do.
+    let _ =
+        deallocate_credential_book(CredentialBook::from_handle(Handle::from_id(handle_id as u64)));
+}
diff --git a/nearby/presence/np_java_ffi/src/class/credential_slab.rs b/nearby/presence/np_java_ffi/src/class/credential_slab.rs
new file mode 100644
index 0000000..7e8f37d
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/credential_slab.rs
@@ -0,0 +1,160 @@
+// 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 jni::{
+    objects::{JByteArray, JClass, JObject, JThrowable},
+    sys::{jboolean, jint, jlong, JNI_FALSE, JNI_TRUE},
+    JNIEnv,
+};
+
+use crate::class::{
+    InvalidHandleException, NoSpaceLeftException, V0DiscoveryCredential, V1DiscoveryCredential,
+};
+use handle_map::Handle;
+use np_ffi_core::credentials::{
+    create_credential_slab, deallocate_credential_slab, AddV0CredentialToSlabResult,
+    AddV1CredentialToSlabResult, CreateCredentialSlabResult, CredentialSlab, MatchedCredential,
+};
+use pourover::{desc::ClassDesc, jni_method};
+
+static INVALID_KEY_EXCEPTION_CLASS: ClassDesc = ClassDesc::new(
+    "com/google/android/nearby/presence/rust/credential/CredentialBook$InvalidPublicKeyException",
+);
+
+/// Rust representation of `class InvalidPublicKeyException`.
+#[repr(transparent)]
+pub struct InvalidPublicKeyException<Obj>(pub Obj);
+
+impl<'local> InvalidPublicKeyException<JObject<'local>> {
+    /// Create a new instance.
+    pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
+        pourover::call_constructor!(env, &INVALID_KEY_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>>> InvalidPublicKeyException<Obj> {
+    /// Throw this exception.
+    pub fn throw<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
+        env.throw(<&JThrowable>::from(self.0.as_ref()))
+    }
+}
+
+#[jni_method(
+    package = "com.google.android.nearby.presence.rust.credential",
+    class = "CredentialSlab",
+    method_name = "allocate"
+)]
+extern "system" fn allocate_slab<'local>(mut env: JNIEnv<'local>, _cls: JClass<'local>) -> jlong {
+    let CreateCredentialSlabResult::Success(slab) = create_credential_slab() else {
+        let _ = NoSpaceLeftException::throw_new(&mut env);
+        return 0;
+    };
+
+    slab.get_as_handle().get_id() as jlong
+}
+
+#[jni_method(
+    package = "com.google.android.nearby.presence.rust.credential",
+    class = "CredentialSlab"
+)]
+extern "system" fn nativeAddV0DiscoveryCredential<'local>(
+    mut env: JNIEnv<'local>,
+    _cls: JClass<'local>,
+    handle_id: jlong,
+    credential: V0DiscoveryCredential<JObject<'local>>,
+    cred_id: jint,
+    encrypted_metadata_bytes: JByteArray<'local>,
+) -> jboolean {
+    let mut add_cred = move || {
+        let slab = CredentialSlab::from_handle(Handle::from_id(handle_id as u64));
+
+        let core_cred = credential.get_as_core(&mut env)?;
+        let match_data = MatchedCredential::from_arc_bytes(
+            cred_id as u32,
+            env.convert_byte_array(&encrypted_metadata_bytes)?.into(),
+        );
+
+        Ok::<_, jni::errors::Error>(match slab.add_v0(core_cred, match_data) {
+            AddV0CredentialToSlabResult::Success => JNI_TRUE,
+            AddV0CredentialToSlabResult::InvalidHandle => {
+                InvalidHandleException::throw_new(&mut env)?;
+                JNI_FALSE
+            }
+        })
+    };
+
+    match add_cred() {
+        Ok(ret) => ret,
+        Err(_) => JNI_FALSE,
+    }
+}
+
+#[jni_method(
+    package = "com.google.android.nearby.presence.rust.credential",
+    class = "CredentialSlab"
+)]
+extern "system" fn nativeAddV1DiscoveryCredential<'local>(
+    mut env: JNIEnv<'local>,
+    _cls: JClass<'local>,
+    handle_id: jlong,
+    credential: V1DiscoveryCredential<JObject<'local>>,
+    cred_id: jint,
+    encrypted_metadata_bytes: JByteArray<'local>,
+) -> jboolean {
+    let mut add_cred = move || {
+        let slab = CredentialSlab::from_handle(Handle::from_id(handle_id as u64));
+
+        let core_cred = credential.get_as_core(&mut env)?;
+        let match_data = MatchedCredential::from_arc_bytes(
+            cred_id as u32,
+            env.convert_byte_array(&encrypted_metadata_bytes)?.into(),
+        );
+
+        Ok::<_, jni::errors::Error>(match slab.add_v1(core_cred, match_data) {
+            AddV1CredentialToSlabResult::Success => JNI_TRUE,
+            AddV1CredentialToSlabResult::InvalidHandle => {
+                InvalidHandleException::throw_new(&mut env)?;
+                JNI_FALSE
+            }
+            AddV1CredentialToSlabResult::InvalidPublicKeyBytes => {
+                InvalidPublicKeyException::throw_new(&mut env)?;
+                JNI_FALSE
+            }
+        })
+    };
+
+    match add_cred() {
+        Ok(ret) => ret,
+        Err(_) => JNI_FALSE,
+    }
+}
+
+#[jni_method(
+    package = "com.google.android.nearby.presence.rust.credential",
+    class = "CredentialSlab",
+    method_name = "deallocate"
+)]
+extern "system" fn deallocate_slab<'local>(
+    _env: JNIEnv<'local>,
+    _cls: JClass<'local>,
+    handle_id: jlong,
+) {
+    let slab = CredentialSlab::from_handle(Handle::from_id(handle_id as u64));
+    let _ = deallocate_credential_slab(slab);
+}
diff --git a/nearby/presence/np_java_ffi/src/class/deserialization_exception.rs b/nearby/presence/np_java_ffi/src/class/deserialization_exception.rs
new file mode 100644
index 0000000..a48c515
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/deserialization_exception.rs
@@ -0,0 +1,86 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use jni::{
+    objects::{JObject, JThrowable},
+    JNIEnv,
+};
+use np_adv::AdvDeserializationError;
+use pourover::desc::ClassDesc;
+
+static INVALID_HEADER_CLASS: ClassDesc = ClassDesc::new(
+    "com/google/android/nearby/presence/rust/DeserializationException$InvalidHeaderException",
+);
+
+/// Rust representation of `class DeserializationException.InvalidHeaderException`.
+#[repr(transparent)]
+pub struct InvalidHeaderException<Obj>(pub Obj);
+
+impl<'local> InvalidHeaderException<JObject<'local>> {
+    /// Create a new instance.
+    pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
+        pourover::call_constructor!(env, &INVALID_HEADER_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>>> InvalidHeaderException<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 INVALID_FORMAT_CLASS: ClassDesc = ClassDesc::new(
+    "com/google/android/nearby/presence/rust/DeserializationException$InvalidFormatException",
+);
+
+/// Rust representation of `class DeserializationException.InvalidFormatException`.
+#[repr(transparent)]
+pub struct InvalidFormatException<Obj>(pub Obj);
+
+impl<'local> InvalidFormatException<JObject<'local>> {
+    /// Create a new instance.
+    pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
+        pourover::call_constructor!(env, &INVALID_FORMAT_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>>> InvalidFormatException<Obj> {
+    /// Throw this exception.
+    pub fn throw<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
+        env.throw(<&JThrowable>::from(self.0.as_ref()))
+    }
+}
+
+/// Allow AdvDeserializationError to be thrown as a Java exception.
+impl super::ToJavaException for AdvDeserializationError {
+    fn throw_java_exception<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
+        match *self {
+            AdvDeserializationError::VersionHeaderParseError => {
+                InvalidHeaderException::throw_new(env)
+            }
+            AdvDeserializationError::ParseError { .. } => InvalidFormatException::throw_new(env),
+        }
+    }
+}
diff --git a/nearby/presence/np_java_ffi/src/class/deserialize_result.rs b/nearby/presence/np_java_ffi/src/class/deserialize_result.rs
new file mode 100644
index 0000000..a38df0f
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/deserialize_result.rs
@@ -0,0 +1,103 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use jni::{
+    objects::JObject,
+    signature::{JavaType, Primitive},
+    sys::jint,
+    JNIEnv,
+};
+use pourover::desc::{ClassDesc, StaticFieldDesc};
+
+use crate::class::{DeserializedV0Advertisement, DeserializedV1Advertisement};
+
+static DESERIALIZE_RESULT_CLASS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/DeserializeResult");
+
+/// Rust representation of `class DeserializeResult`.
+#[repr(transparent)]
+pub struct DeserializeResult<Obj>(pub Obj);
+
+impl<'local> DeserializeResult<JObject<'local>> {
+    /// Create a result representing the given error.
+    pub fn construct_from_error(
+        env: &mut JNIEnv<'local>,
+        error: DeserializeResultError,
+    ) -> jni::errors::Result<Self> {
+        let error = error.lookup_java_value(env)?;
+        pourover::call_constructor!(env, &DESERIALIZE_RESULT_CLASS, "(I)V", error).map(Self)
+    }
+
+    /// Create a result containing the given advertisement.
+    pub fn from_v0_advertisement(
+        env: &mut JNIEnv<'local>,
+        adv: DeserializedV0Advertisement<impl AsRef<JObject<'local>>>,
+    ) -> jni::errors::Result<Self> {
+        pourover::call_constructor!(
+            env,
+            &DESERIALIZE_RESULT_CLASS,
+            "(Lcom/google/android/nearby/presence/rust/DeserializedV0Advertisement;)V",
+            adv.as_obj()
+        )
+        .map(Self)
+    }
+
+    /// Create a result containing the given advertisement.
+    pub fn from_v1_advertisement(
+        env: &mut JNIEnv<'local>,
+        adv: DeserializedV1Advertisement<impl AsRef<JObject<'local>>>,
+    ) -> jni::errors::Result<Self> {
+        pourover::call_constructor!(
+            env,
+            &DESERIALIZE_RESULT_CLASS,
+            "(Lcom/google/android/nearby/presence/rust/DeserializedV1Advertisement;)V",
+            adv.as_obj()
+        )
+        .map(Self)
+    }
+}
+
+impl<'local, Obj: AsRef<JObject<'local>>> DeserializeResult<Obj> {
+    /// Get a reference to the inner `jni` crate [`JObject`].
+    pub fn as_obj(&self) -> &JObject<'local> {
+        self.0.as_ref()
+    }
+}
+
+static DESERIALIZE_RESULT_KIND_CLASS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/DeserializeResult$Kind");
+
+/// An error that occurs during deserialization
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub enum DeserializeResultError {
+    /// An unspecified error has occurred.
+    UnknownError,
+}
+
+impl DeserializeResultError {
+    /// Fetch the Java `@IntDef` value for this error.
+    fn lookup_java_value(&self, env: &mut JNIEnv<'_>) -> jni::errors::Result<jint> {
+        static UNKNOWN_ERROR_STATIC_FIELD: StaticFieldDesc =
+            DESERIALIZE_RESULT_KIND_CLASS.static_field("UNKNOWN_ERROR", "I");
+        match self {
+            DeserializeResultError::UnknownError => env
+                .get_static_field_unchecked(
+                    UNKNOWN_ERROR_STATIC_FIELD.cls(),
+                    &UNKNOWN_ERROR_STATIC_FIELD,
+                    JavaType::Primitive(Primitive::Int),
+                )
+                .and_then(|ret| ret.i()),
+        }
+    }
+}
diff --git a/nearby/presence/np_java_ffi/src/class/deserialized_v0_advertisement.rs b/nearby/presence/np_java_ffi/src/class/deserialized_v0_advertisement.rs
new file mode 100644
index 0000000..1d91ae9
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/deserialized_v0_advertisement.rs
@@ -0,0 +1,79 @@
+// 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 jni::{
+    objects::JObject,
+    sys::{jint, jlong},
+    JNIEnv,
+};
+use np_ffi_core::deserialize::v0::{DeserializedV0IdentityKind, V0Payload};
+use pourover::desc::ClassDesc;
+
+use crate::class::IdentityKind;
+
+static DESERIALIZED_V0_ADVERTISEMENT_CLASS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/DeserializedV0Advertisement");
+
+/// Rust representation of `class DeserializedV0Advertisement`.
+#[repr(transparent)]
+pub struct DeserializedV0Advertisement<Obj>(pub Obj);
+
+impl<'local> DeserializedV0Advertisement<JObject<'local>> {
+    /// Create an illegible advertisment with the given error.
+    pub fn construct_from_error(
+        env: &mut JNIEnv<'local>,
+        error: V0AdvertisementError,
+    ) -> jni::errors::Result<Self> {
+        let error = IdentityKind::error_for_v0(env, error)?;
+
+        pourover::call_constructor!(env, &DESERIALIZED_V0_ADVERTISEMENT_CLASS, "(I)V", error)
+            .map(Self)
+    }
+
+    /// Create a legible advertisment.
+    pub fn construct(
+        env: &mut JNIEnv<'local>,
+        num_des: u8,
+        v0_payload: V0Payload,
+        identity: DeserializedV0IdentityKind,
+    ) -> jni::errors::Result<Self> {
+        let num_des = jint::from(num_des);
+        let payload_handle = v0_payload.get_as_handle().get_id() as jlong;
+        let identity = IdentityKind::value_for_v0(env, identity)?;
+
+        pourover::call_constructor!(
+            env,
+            &DESERIALIZED_V0_ADVERTISEMENT_CLASS,
+            "(IJI)V",
+            num_des,
+            payload_handle,
+            identity
+        )
+        .map(Self)
+    }
+}
+
+impl<'local, Obj: AsRef<JObject<'local>>> DeserializedV0Advertisement<Obj> {
+    /// Get a reference to the inner `jni` crate [`JObject`].
+    pub fn as_obj(&self) -> &JObject<'local> {
+        self.0.as_ref()
+    }
+}
+
+/// A reason for an advertisment being illegible
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub enum V0AdvertisementError {
+    /// There is no matching credential in the credential book.
+    NoMatchingCredentials,
+}
diff --git a/nearby/presence/np_java_ffi/src/class/deserialized_v1_advertisement.rs b/nearby/presence/np_java_ffi/src/class/deserialized_v1_advertisement.rs
new file mode 100644
index 0000000..e537052
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/deserialized_v1_advertisement.rs
@@ -0,0 +1,50 @@
+// 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 jni::{objects::JObject, sys::jint, JNIEnv};
+use pourover::desc::ClassDesc;
+
+static DESERIALIZED_V1_ADVERTISEMENT_CLASS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/DeserializedV1Advertisement");
+
+/// Rust representation of `class DeserializedV1Advertisement`.
+#[repr(transparent)]
+pub struct DeserializedV1Advertisement<Obj>(pub Obj);
+
+impl<'local> DeserializedV1Advertisement<JObject<'local>> {
+    /// Create a new advertisement.
+    pub fn construct(
+        env: &mut JNIEnv<'local>,
+        num_legible_sections: jint,
+        num_undecryptable_sections: jint,
+        legible_sections: super::LegibleV1Sections<impl AsRef<JObject<'local>>>,
+    ) -> jni::errors::Result<Self> {
+        pourover::call_constructor!(
+            env,
+            &DESERIALIZED_V1_ADVERTISEMENT_CLASS,
+            "(IILcom/google/android/nearby/presence/rust/LegibleV1Sections;)V",
+            num_legible_sections,
+            num_undecryptable_sections,
+            legible_sections.as_obj()
+        )
+        .map(Self)
+    }
+}
+
+impl<'local, Obj: AsRef<JObject<'local>>> DeserializedV1Advertisement<Obj> {
+    /// Get a reference to the inner `jni` crate [`JObject`].
+    pub fn as_obj(&self) -> &JObject<'local> {
+        self.0.as_ref()
+    }
+}
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
new file mode 100644
index 0000000..b3f6dba
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/deserialized_v1_section.rs
@@ -0,0 +1,50 @@
+// 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 jni::{objects::JObject, sys::jint, JNIEnv};
+use np_ffi_core::deserialize::v1::DeserializedV1IdentityKind;
+use pourover::desc::ClassDesc;
+
+use crate::class::{IdentityKind, LegibleV1Sections};
+
+static DESERIALIZED_V1_SECTION_CLASS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/DeserializedV1Section");
+
+/// Rust representation of `class DeserializedV1Section`.
+#[repr(transparent)]
+pub struct DeserializedV1Section<Obj>(pub Obj);
+
+impl<'local> DeserializedV1Section<JObject<'local>> {
+    /// Create a new deserialized section
+    pub fn construct<'a>(
+        env: &mut JNIEnv<'local>,
+        legible_sections_handle: LegibleV1Sections<impl AsRef<JObject<'a>>>,
+        legible_section_index: u8,
+        num_des: u8,
+        identity_kind: DeserializedV1IdentityKind,
+    ) -> jni::errors::Result<Self> {
+        let identity = IdentityKind::value_for_v1(env, identity_kind)?;
+
+        pourover::call_constructor!(
+            env,
+            &DESERIALIZED_V1_SECTION_CLASS,
+            "(Lcom/google/android/nearby/presence/rust/LegibleV1Sections;III)V",
+            legible_sections_handle.as_obj(),
+            jint::from(legible_section_index),
+            jint::from(num_des),
+            identity
+        )
+        .map(Self)
+    }
+}
diff --git a/nearby/presence/np_java_ffi/src/class/handle.rs b/nearby/presence/np_java_ffi/src/class/handle.rs
new file mode 100644
index 0000000..7505507
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/handle.rs
@@ -0,0 +1,45 @@
+// 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 jni::{
+    objects::{JObject, JThrowable},
+    JNIEnv,
+};
+use pourover::desc::ClassDesc;
+
+static INVALID_HANDLE_EXCEPTION_CLASS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/Handle$InvalidHandleException");
+
+/// Rust representation of `class InvalidHandleException`.
+#[repr(transparent)]
+pub struct InvalidHandleException<Obj>(pub Obj);
+
+impl<'local> InvalidHandleException<JObject<'local>> {
+    /// Create a new instance.
+    pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
+        pourover::call_constructor!(env, &INVALID_HANDLE_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>>> InvalidHandleException<Obj> {
+    /// Throw this exception.
+    pub fn throw<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
+        env.throw(<&JThrowable>::from(self.0.as_ref()))
+    }
+}
diff --git a/nearby/presence/np_java_ffi/src/class/identity_kind.rs b/nearby/presence/np_java_ffi/src/class/identity_kind.rs
new file mode 100644
index 0000000..8fce071
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/identity_kind.rs
@@ -0,0 +1,117 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use jni::{
+    signature::{JavaType, Primitive},
+    sys::jint,
+    JNIEnv,
+};
+use np_ffi_core::deserialize::{v0::DeserializedV0IdentityKind, v1::DeserializedV1IdentityKind};
+use pourover::desc::{ClassDesc, StaticFieldDesc};
+use std::sync::RwLock;
+
+use crate::class::V0AdvertisementError;
+
+static IDENTITY_KIND_CLASS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/IdentityKind");
+
+/// Rust representation of `@IdentityKind`. These are `jints` on the Java side, so this type can't
+/// be instantiated.
+pub enum IdentityKind {}
+
+impl IdentityKind {
+    /// Fetch the `NO_MATCHING_CREDENTIALS` constant
+    pub fn no_matching_credentials<'local>(env: &mut JNIEnv<'local>) -> jni::errors::Result<jint> {
+        static NO_MATCHING_CREDENTIALS: StaticFieldDesc =
+            IDENTITY_KIND_CLASS.static_field("NO_MATCHING_CREDENTIALS", "I");
+        static VALUE: RwLock<Option<jint>> = RwLock::new(None);
+        Self::lookup_static_value(env, &NO_MATCHING_CREDENTIALS, &VALUE)
+    }
+
+    /// Fetch the `PLAINTEXT` constant
+    pub fn plaintext<'local>(env: &mut JNIEnv<'local>) -> jni::errors::Result<jint> {
+        static PLAINTEXT: StaticFieldDesc = IDENTITY_KIND_CLASS.static_field("PLAINTEXT", "I");
+        static VALUE: RwLock<Option<jint>> = RwLock::new(None);
+        Self::lookup_static_value(env, &PLAINTEXT, &VALUE)
+    }
+
+    /// Fetch the `DECRYPTED` constant
+    pub fn decrypted<'local>(env: &mut JNIEnv<'local>) -> jni::errors::Result<jint> {
+        static DECRYPTED: StaticFieldDesc = IDENTITY_KIND_CLASS.static_field("DECRYPTED", "I");
+        static VALUE: RwLock<Option<jint>> = RwLock::new(None);
+        Self::lookup_static_value(env, &DECRYPTED, &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> {
+        // 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)
+    }
+
+    /// Get the Java representation of [`V0AdvertisementError`].
+    pub fn error_for_v0<'local>(
+        env: &mut JNIEnv<'local>,
+        identity: V0AdvertisementError,
+    ) -> jni::errors::Result<jint> {
+        match identity {
+            V0AdvertisementError::NoMatchingCredentials => Self::no_matching_credentials(env),
+        }
+    }
+
+    /// Get the Java representation of [`DeserializedV0IdentityKind`].
+    pub fn value_for_v0<'local>(
+        env: &mut JNIEnv<'local>,
+        identity: DeserializedV0IdentityKind,
+    ) -> jni::errors::Result<jint> {
+        match identity {
+            DeserializedV0IdentityKind::Plaintext => Self::plaintext(env),
+            DeserializedV0IdentityKind::Decrypted => Self::decrypted(env),
+        }
+    }
+
+    /// Get the Java representation of [`DeserializedV1IdentityKind`].
+    pub fn value_for_v1<'local>(
+        env: &mut JNIEnv<'local>,
+        identity: DeserializedV1IdentityKind,
+    ) -> jni::errors::Result<jint> {
+        match identity {
+            DeserializedV1IdentityKind::Plaintext => Self::plaintext(env),
+            DeserializedV1IdentityKind::Decrypted => Self::decrypted(env),
+        }
+    }
+}
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
new file mode 100644
index 0000000..911c78d
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/legible_v1_sections.rs
@@ -0,0 +1,159 @@
+// 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 jni::{
+    objects::{JClass, JObject},
+    sys::{jint, jlong},
+    JNIEnv,
+};
+
+use crate::class::{v1_data_element::Generic, DeserializedV1Section};
+use handle_map::{Handle, HandleLike};
+use np_ffi_core::deserialize::v1::{
+    GetV1DEResult, GetV1SectionResult, LegibleV1Sections as LegibleSectionsHandle, V1DataElement,
+};
+use pourover::{desc::ClassDesc, jni_method};
+
+static LEGIBLE_V1_SECTIONS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/LegibleV1Sections");
+
+/// Rust representation for `class LegibleV1Sections`.
+#[repr(transparent)]
+pub struct LegibleV1Sections<Obj>(pub Obj);
+
+impl<'local> LegibleV1Sections<JObject<'local>> {
+    /// Create new Java instance for the given handle. On the Java side this an `OwnedHandle` and
+    /// Java will be responsible to deallocating it.
+    pub fn construct(
+        env: &mut JNIEnv<'local>,
+        handle: LegibleSectionsHandle,
+    ) -> jni::errors::Result<Self> {
+        let handle_id = handle.get_as_handle().get_id() as jlong;
+
+        pourover::call_constructor!(env, &LEGIBLE_V1_SECTIONS, "(J)V", handle_id,).map(Self)
+    }
+}
+
+impl<'local, Obj: AsRef<JObject<'local>>> LegibleV1Sections<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<LegibleSectionsHandle> {
+        let handle_id = self.get_handle_id(env)?;
+        Ok(LegibleSectionsHandle::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 jni::signature::{Primitive, ReturnType};
+        use pourover::desc::FieldDesc;
+
+        static HANDLE_ID_FIELD: FieldDesc = LEGIBLE_V1_SECTIONS.field("handleId", "J");
+
+        env.get_field_unchecked(
+            self.0.as_ref(),
+            &HANDLE_ID_FIELD,
+            ReturnType::Primitive(Primitive::Long),
+        )
+        .and_then(|val| val.j())
+    }
+}
+
+// Native method implementations
+
+#[jni_method(package = "com.google.android.nearby.presence.rust", class = "LegibleV1Sections")]
+extern "system" fn nativeGetSection<'local>(
+    mut env: JNIEnv<'local>,
+    legible_sections_obj: LegibleV1Sections<JObject<'local>>,
+    index: jint,
+) -> JObject<'local> {
+    let Ok(legible_sections) = legible_sections_obj.as_rust_handle(&mut env) else {
+        return JObject::null();
+    };
+    let Ok(index) = u8::try_from(index) else {
+        return JObject::null();
+    };
+
+    let GetV1SectionResult::Success(section) = legible_sections.get_section(index) else {
+        return JObject::null();
+    };
+
+    match DeserializedV1Section::construct(
+        &mut env,
+        legible_sections_obj,
+        index,
+        section.num_des(),
+        section.identity_kind(),
+    ) {
+        Ok(section) => section.0,
+        Err(_) => JObject::null(),
+    }
+}
+
+#[jni_method(package = "com.google.android.nearby.presence.rust", class = "LegibleV1Sections")]
+extern "system" fn nativeGetSectionDataElement<'local>(
+    mut env: JNIEnv<'local>,
+    legible_sections_obj: LegibleV1Sections<JObject<'local>>,
+    section_index: jint,
+    de_index: jint,
+) -> JObject<'local> {
+    let Ok(legible_sections) = legible_sections_obj.as_rust_handle(&mut env) else {
+        return JObject::null();
+    };
+    let Ok(section_index) = u8::try_from(section_index) else {
+        return JObject::null();
+    };
+    let Ok(de_index) = u8::try_from(de_index) else {
+        return JObject::null();
+    };
+
+    let GetV1DEResult::Success(de) = legible_sections.get_section_de(section_index, de_index)
+    else {
+        return JObject::null();
+    };
+
+    let ret = match de {
+        V1DataElement::Generic(generic) => {
+            let de_type = jlong::from(generic.de_type().to_u32());
+            let Some(slice) = generic.payload.as_slice() else {
+                return JObject::null();
+            };
+
+            env.byte_array_from_slice(slice)
+                .and_then(|data| Generic::construct(&mut env, de_type, data))
+                .map(|obj| obj.0)
+        }
+    };
+
+    ret.unwrap_or_else(|_err| JObject::null())
+}
+
+#[jni_method(package = "com.google.android.nearby.presence.rust", class = "LegibleV1Sections")]
+extern "system" fn deallocate<'local>(
+    _env: JNIEnv<'local>,
+    _cls: JClass<'local>,
+    handle_id: jlong,
+) {
+    // Swallow errors here since there's nothing meaningful to do.
+    let _ = LegibleSectionsHandle::from_handle(Handle::from_id(handle_id as u64)).deallocate();
+}
diff --git a/nearby/presence/np_java_ffi/src/class/np_adv.rs b/nearby/presence/np_java_ffi/src/class/np_adv.rs
new file mode 100644
index 0000000..88513cc
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/np_adv.rs
@@ -0,0 +1,95 @@
+// 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 jni::{
+    objects::{JByteArray, JClass, JObject},
+    sys::{jint, jlong},
+    JNIEnv,
+};
+
+use crate::class::{
+    DeserializeResult, DeserializeResultError, DeserializedV0Advertisement,
+    DeserializedV1Advertisement, LegibleV1Sections, V0AdvertisementError,
+};
+use handle_map::Handle;
+use np_ffi_core::{credentials::CredentialBook, deserialize::deserialize_advertisement_from_slice};
+use pourover::{jni_method, ToUnsigned};
+
+#[jni_method(package = "com.google.android.nearby.presence.rust", class = "NpAdv")]
+extern "system" fn nativeDeserializeAdvertisement<'local>(
+    mut env: JNIEnv<'local>,
+    _cls: JClass<'local>,
+    service_data: JByteArray<'local>,
+    credential_book: jlong,
+) -> JObject<'local> {
+    let credential_book = CredentialBook::from_handle(Handle::from_id(credential_book as u64));
+
+    // Unpack the service data
+    let mut service_data_buf = [0i8; 256];
+    let Some(service_data) = env.get_array_length(&service_data).ok().and_then(|len| {
+        let len = usize::try_from(len).ok()?;
+        let region = service_data_buf.get_mut(0..len)?;
+        env.get_byte_array_region(&service_data, 0, region).ok()?;
+        Some(region.to_unsigned())
+    }) else {
+        return DeserializeResult::construct_from_error(
+            &mut env,
+            DeserializeResultError::UnknownError,
+        )
+        .map(|obj| obj.0)
+        .unwrap_or(JObject::null());
+    };
+
+    use np_ffi_core::deserialize::{
+        v0::DeserializedV0Advertisement::{Legible, NoMatchingCredentials},
+        DeserializeAdvertisementResult::{Error, V0, V1},
+    };
+
+    let res = match deserialize_advertisement_from_slice(service_data, credential_book) {
+        Error => {
+            DeserializeResult::construct_from_error(&mut env, DeserializeResultError::UnknownError)
+                .map(|obj| obj.0)
+        }
+
+        V0(NoMatchingCredentials) => DeserializedV0Advertisement::construct_from_error(
+            &mut env,
+            V0AdvertisementError::NoMatchingCredentials,
+        )
+        .and_then(|adv| DeserializeResult::from_v0_advertisement(&mut env, adv))
+        .map(|obj| obj.0),
+
+        V0(Legible(adv)) => DeserializedV0Advertisement::construct(
+            &mut env,
+            adv.num_des(),
+            adv.payload(),
+            adv.identity_kind(),
+        )
+        .and_then(|adv| DeserializeResult::from_v0_advertisement(&mut env, adv))
+        .map(|obj| obj.0),
+
+        V1(adv) => LegibleV1Sections::construct(&mut env, adv.legible_sections)
+            .and_then(|sections| {
+                DeserializedV1Advertisement::construct(
+                    &mut env,
+                    jint::from(adv.num_legible_sections),
+                    jint::from(adv.num_undecryptable_sections),
+                    sections,
+                )
+            })
+            .and_then(|adv| DeserializeResult::from_v1_advertisement(&mut env, adv))
+            .map(|res| res.0),
+    };
+
+    res.unwrap_or(JObject::null())
+}
diff --git a/nearby/presence/np_java_ffi/src/class/owned_handle.rs b/nearby/presence/np_java_ffi/src/class/owned_handle.rs
new file mode 100644
index 0000000..8dc6e11
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/owned_handle.rs
@@ -0,0 +1,45 @@
+// 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 jni::{
+    objects::{JObject, JThrowable},
+    JNIEnv,
+};
+use pourover::desc::ClassDesc;
+
+static NO_SPACE_LEFT_EXCEPTION_CLASS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/OwnedHandle$NoSpaceLeftException");
+
+/// Rust representation of `class OwnedHandle.NoSpaceLeftException`.
+#[repr(transparent)]
+pub struct NoSpaceLeftException<Obj>(pub Obj);
+
+impl<'local> NoSpaceLeftException<JObject<'local>> {
+    /// Create a new instance.
+    pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
+        pourover::call_constructor!(env, &NO_SPACE_LEFT_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>>> NoSpaceLeftException<Obj> {
+    /// Throw this exception.
+    pub fn throw<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
+        env.throw(<&JThrowable>::from(self.0.as_ref()))
+    }
+}
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
new file mode 100644
index 0000000..759a486
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/v0_data_element.rs
@@ -0,0 +1,172 @@
+// 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.
+
+//! Data Elementes for v0 advertisements. See `class V0DataElement`.
+
+use crate::class::IdentityKind;
+use jni::{
+    objects::{JClass, JObject},
+    signature::{Primitive, ReturnType},
+    sys::{jboolean, jint, JNI_FALSE, JNI_TRUE},
+    JNIEnv,
+};
+use np_ffi_core::{deserialize::v0::DeserializedV0IdentityKind, v0};
+use pourover::desc::{ClassDesc, FieldDesc};
+
+static TX_POWER_CLASS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/V0DataElement$TxPower");
+
+/// Rust representation of `class V0DataElement.TxPower`.
+#[repr(transparent)]
+pub struct TxPower<Obj>(pub Obj);
+
+impl<'local> TxPower<JObject<'local>> {
+    /// Create a new TxPower date element with the given `tx_power`.
+    pub fn construct(env: &mut JNIEnv<'local>, tx_power: jint) -> jni::errors::Result<Self> {
+        pourover::call_constructor!(env, &TX_POWER_CLASS, "(I)V", tx_power).map(Self)
+    }
+}
+
+impl<'local, Obj: AsRef<JObject<'local>>> TxPower<Obj> {
+    /// Cast the given Java object to `TxPower` if it is an instance of the type. Returns `None` if
+    /// the object's type does not match.
+    pub fn checked_cast<'other_local>(
+        env: &mut JNIEnv<'other_local>,
+        obj: Obj,
+    ) -> jni::errors::Result<Option<Self>> {
+        Ok(env.is_instance_of(obj.as_ref(), &TX_POWER_CLASS)?.then(|| Self(obj)))
+    }
+
+    /// Gets the value of the `int txPower` field.
+    pub fn get_tx_power<'env_local>(
+        &self,
+        env: &mut JNIEnv<'env_local>,
+    ) -> jni::errors::Result<jint> {
+        static TX_POWER_FIELD: FieldDesc = TX_POWER_CLASS.field("txPower", "I");
+        env.get_field_unchecked(
+            self.0.as_ref(),
+            &TX_POWER_FIELD,
+            ReturnType::Primitive(Primitive::Int),
+        )
+        .and_then(|ret| ret.i())
+    }
+}
+
+static V0_ACTIONS_CLASS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/V0DataElement$V0Actions");
+
+/// Rust representation of `class V0DataElement.V0Actions`.
+#[repr(transparent)]
+pub struct V0Actions<Obj>(pub Obj);
+
+impl<'local> V0Actions<JObject<'local>> {
+    /// Create a new TxPower date element with the given identity and action bits.
+    pub fn construct(
+        env: &mut JNIEnv<'local>,
+        identity_kind: DeserializedV0IdentityKind,
+        action_bits: jint,
+    ) -> jni::errors::Result<Self> {
+        let identity_kind = IdentityKind::value_for_v0(env, identity_kind)?;
+
+        pourover::call_constructor!(env, &V0_ACTIONS_CLASS, "(II)V", identity_kind, action_bits)
+            .map(Self)
+    }
+}
+
+impl<'local, Obj: AsRef<JObject<'local>>> V0Actions<Obj> {
+    /// Cast the given Java object to `V0Actions` if it is an instance of the type. Returns `None` if
+    /// the object's type does not match.
+    pub fn checked_cast<'other_local>(
+        env: &mut JNIEnv<'other_local>,
+        obj: Obj,
+    ) -> jni::errors::Result<Option<Self>> {
+        Ok(env.is_instance_of(obj.as_ref(), &V0_ACTIONS_CLASS)?.then(|| Self(obj)))
+    }
+
+    /// Get the `int identityKind` field from the Java object.
+    pub fn get_identity_kind<'env_local>(
+        &self,
+        env: &mut JNIEnv<'env_local>,
+    ) -> jni::errors::Result<jint> {
+        static IDENTITY_KIND: FieldDesc = V0_ACTIONS_CLASS.field("identityKind", "I");
+
+        env.get_field_unchecked(
+            self.0.as_ref(),
+            &IDENTITY_KIND,
+            ReturnType::Primitive(Primitive::Int),
+        )
+        .and_then(|ret| ret.i())
+    }
+
+    /// Get the `int actionBits` field from the Java object.
+    pub fn get_action_bits<'env_local>(
+        &self,
+        env: &mut JNIEnv<'env_local>,
+    ) -> jni::errors::Result<jint> {
+        static ACTION_BITS_FIELD: FieldDesc = V0_ACTIONS_CLASS.field("actionBits", "I");
+
+        env.get_field_unchecked(
+            self.0.as_ref(),
+            &ACTION_BITS_FIELD,
+            ReturnType::Primitive(Primitive::Int),
+        )
+        .and_then(|ret| ret.i())
+    }
+}
+
+/// Helper to build a [`V0Actions`][v0::V0Actions] instance from raw Java fields.
+fn construct_actions_from_ints(
+    env: &mut JNIEnv<'_>,
+    identity_kind: jint,
+    action_bits: jint,
+) -> Option<v0::V0Actions> {
+    let wrapper = if identity_kind == IdentityKind::plaintext(env).ok()? {
+        v0::V0Actions::Plaintext
+    } else if identity_kind == IdentityKind::decrypted(env).ok()? {
+        v0::V0Actions::Encrypted
+    } else {
+        return None;
+    };
+
+    let bits = v0::V0ActionBits::from(action_bits as u32);
+
+    Some(wrapper(bits))
+}
+
+#[pourover::jni_method(
+    package = "com.google.android.nearby.presence.rust",
+    class = "V0DataElement.V0Actions"
+)]
+extern "system" fn nativeHasAction<'local>(
+    mut env: JNIEnv<'local>,
+    _cls: JClass<'local>,
+    identity_kind: jint,
+    action_bits: jint,
+    action: jint,
+) -> jboolean {
+    let Some(actions) = construct_actions_from_ints(&mut env, identity_kind, action_bits) else {
+        return JNI_FALSE;
+    };
+
+    let Ok(action) = u8::try_from(action).map_err(From::from).and_then(v0::ActionType::try_from)
+    else {
+        return JNI_FALSE;
+    };
+
+    if actions.has_action(action).unwrap_or(false) {
+        JNI_TRUE
+    } else {
+        JNI_FALSE
+    }
+}
diff --git a/nearby/presence/np_java_ffi/src/class/v0_discovery_credential.rs b/nearby/presence/np_java_ffi/src/class/v0_discovery_credential.rs
new file mode 100644
index 0000000..d8f21cf
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/v0_discovery_credential.rs
@@ -0,0 +1,72 @@
+// 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 jni::{
+    objects::{JByteArray, JObject},
+    signature::ReturnType,
+    JNIEnv,
+};
+
+use np_ffi_core::credentials::V0DiscoveryCredential as CoreV0DiscoveryCredential;
+use pourover::desc::{ClassDesc, FieldDesc};
+
+static V0_DISCOVERY_CREDENTIAL_CLS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/credential/V0DiscoveryCredential");
+
+/// Rust representation of `class V0DiscoveryCredential`.
+#[repr(transparent)]
+pub struct V0DiscoveryCredential<Obj>(pub Obj);
+
+impl<'local, Obj: AsRef<JObject<'local>>> V0DiscoveryCredential<Obj> {
+    /// Get an array field as a Rust array.
+    fn get_array<'env>(
+        &self,
+        env: &mut JNIEnv<'env>,
+        field: &FieldDesc,
+    ) -> jni::errors::Result<[u8; 32]> {
+        let arr: JByteArray<'env> =
+            env.get_field_unchecked(self.0.as_ref(), field, ReturnType::Array)?.l()?.into();
+
+        let mut buf = [0; 32];
+        env.get_byte_array_region(arr, 0, &mut buf[..])?;
+        Ok(buf.map(|byte| byte as u8))
+    }
+
+    /// Get the key seed.
+    pub fn get_key_seed<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<[u8; 32]> {
+        static KEY_SEED: FieldDesc = V0_DISCOVERY_CREDENTIAL_CLS.field("keySeed", "[B");
+        self.get_array(env, &KEY_SEED)
+    }
+
+    /// Get the identity token hmac.
+    pub fn get_identity_token_hmac<'env>(
+        &self,
+        env: &mut JNIEnv<'env>,
+    ) -> jni::errors::Result<[u8; 32]> {
+        static IDENTITY_TOKEN_HMAC: FieldDesc =
+            V0_DISCOVERY_CREDENTIAL_CLS.field("identityTokenHmac", "[B");
+        self.get_array(env, &IDENTITY_TOKEN_HMAC)
+    }
+
+    /// Convert this to the `np_ffi_core` representation.
+    pub fn get_as_core<'env>(
+        &self,
+        env: &mut JNIEnv<'env>,
+    ) -> jni::errors::Result<CoreV0DiscoveryCredential> {
+        Ok(CoreV0DiscoveryCredential::new(
+            self.get_key_seed(env)?,
+            self.get_identity_token_hmac(env)?,
+        ))
+    }
+}
diff --git a/nearby/presence/np_java_ffi/src/class/v0_payload.rs b/nearby/presence/np_java_ffi/src/class/v0_payload.rs
new file mode 100644
index 0000000..9b5e05b
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/v0_payload.rs
@@ -0,0 +1,73 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::class::v0_data_element::{TxPower, V0Actions};
+use handle_map::{Handle, HandleLike};
+use jni::{
+    objects::{JClass, JObject},
+    sys::{jint, jlong},
+    JNIEnv,
+};
+use np_ffi_core::deserialize::v0::{DeserializedV0IdentityKind, V0Payload};
+use np_ffi_core::v0::V0Actions as CoreV0Actions;
+use pourover::jni_method;
+
+#[jni_method(package = "com.google.android.nearby.presence.rust", class = "V0Payload")]
+extern "system" fn nativeGetDataElement<'local>(
+    mut env: JNIEnv<'local>,
+    _cls: JClass<'local>,
+    handle_id: jlong,
+    index: jint,
+) -> JObject<'local> {
+    let v0_payload = V0Payload::from_handle(Handle::from_id(handle_id as u64));
+    let Ok(index) = u8::try_from(index) else {
+        return JObject::null();
+    };
+
+    use np_ffi_core::{
+        deserialize::v0::GetV0DEResult::{Error, Success},
+        v0::V0DataElement::{Actions, TxPower as TxPow},
+    };
+    let ret = match v0_payload.get_de(index) {
+        Success(TxPow(tx_power)) => {
+            TxPower::construct(&mut env, jint::from(tx_power.as_i8())).map(|obj| obj.0)
+        }
+        Success(Actions(actions)) => {
+            let identity_kind = match &actions {
+                CoreV0Actions::Plaintext(_) => DeserializedV0IdentityKind::Plaintext,
+                CoreV0Actions::Encrypted(_) => DeserializedV0IdentityKind::Decrypted,
+            };
+
+            V0Actions::construct(&mut env, identity_kind, actions.as_u32() as jint).map(|obj| obj.0)
+        }
+        Error => {
+            return JObject::null();
+        }
+    };
+
+    match ret {
+        Ok(de) => de,
+        Err(_jni_err) => JObject::null(),
+    }
+}
+
+#[jni_method(package = "com.google.android.nearby.presence.rust", class = "V0Payload")]
+extern "system" fn deallocate<'local>(
+    _env: JNIEnv<'local>,
+    _cls: JClass<'local>,
+    handle_id: jlong,
+) {
+    // Swallow errors here since there's nothing meaningful to do.
+    let _ = V0Payload::from_handle(Handle::from_id(handle_id as u64)).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
new file mode 100644
index 0000000..6401165
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/v1_data_element.rs
@@ -0,0 +1,51 @@
+// 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.
+
+//! Data Elements for v1 advertisements. See `class V1DataElement`.
+
+use jni::{
+    objects::{JByteArray, JObject},
+    sys::jlong,
+    JNIEnv,
+};
+use pourover::desc::ClassDesc;
+
+static GENERIC_CLASS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$Generic");
+
+/// Rust representation of `class V1DataElement.Generic`.
+#[repr(transparent)]
+pub struct Generic<Obj>(pub Obj);
+
+impl<'local> Generic<JObject<'local>> {
+    /// Create a new Java instance from the given data element info.
+    pub fn construct<'data>(
+        env: &mut JNIEnv<'local>,
+        de_type: jlong,
+        data: JByteArray<'data>,
+    ) -> jni::errors::Result<Self> {
+        pourover::call_constructor!(env, &GENERIC_CLASS, "(J[B)V", de_type, data).map(Self)
+    }
+}
+
+impl<'local, Obj: AsRef<JObject<'local>>> Generic<Obj> {
+    /// Cast the given Java object to `Generic` if it is an instance of the type. Returns `None` if
+    /// the object's type does not match.
+    pub fn checked_cast<'other_local>(
+        env: &mut JNIEnv<'other_local>,
+        obj: Obj,
+    ) -> jni::errors::Result<Option<Self>> {
+        Ok(env.is_instance_of(obj.as_ref(), &GENERIC_CLASS)?.then(|| Self(obj)))
+    }
+}
diff --git a/nearby/presence/np_java_ffi/src/class/v1_discovery_credential.rs b/nearby/presence/np_java_ffi/src/class/v1_discovery_credential.rs
new file mode 100644
index 0000000..923446a
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/v1_discovery_credential.rs
@@ -0,0 +1,101 @@
+// 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 jni::{
+    objects::{JByteArray, JObject},
+    signature::ReturnType,
+    JNIEnv,
+};
+
+use np_ffi_core::credentials::V1DiscoveryCredential as CoreV1DiscoveryCredential;
+use pourover::desc::{ClassDesc, FieldDesc};
+
+static V1_DISCOVERY_CREDENTIAL_CLS: ClassDesc =
+    ClassDesc::new("com/google/android/nearby/presence/rust/credential/V1DiscoveryCredential");
+
+/// Rust representation of `class V1DiscoveryCredential`.
+#[repr(transparent)]
+pub struct V1DiscoveryCredential<Obj>(pub Obj);
+
+impl<'local, Obj: AsRef<JObject<'local>>> V1DiscoveryCredential<Obj> {
+    /// Get an array field as a Rust array.
+    fn get_array<'env>(
+        &self,
+        env: &mut JNIEnv<'env>,
+        field: &FieldDesc,
+    ) -> jni::errors::Result<[u8; 32]> {
+        let arr: JByteArray<'env> =
+            env.get_field_unchecked(self.0.as_ref(), field, ReturnType::Array)?.l()?.into();
+
+        let mut buf = [0; 32];
+        env.get_byte_array_region(arr, 0, &mut buf[..])?;
+        Ok(buf.map(|byte| byte as u8))
+    }
+
+    /// Get the key seed.
+    pub fn get_key_seed<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<[u8; 32]> {
+        static KEY_SEED: FieldDesc = V1_DISCOVERY_CREDENTIAL_CLS.field("keySeed", "[B");
+        self.get_array(env, &KEY_SEED)
+    }
+
+    /// Get the expected mic short salt identity token hmac.
+    pub fn get_expected_mic_short_salt_identity_token_hmac<'env>(
+        &self,
+        env: &mut JNIEnv<'env>,
+    ) -> jni::errors::Result<[u8; 32]> {
+        static EXPECTED_MIC_SHORT_SALT_IDENTITY_TOKEN_HMAC: FieldDesc =
+            V1_DISCOVERY_CREDENTIAL_CLS.field("expectedMicShortSaltIdentityTokenHmac", "[B");
+        self.get_array(env, &EXPECTED_MIC_SHORT_SALT_IDENTITY_TOKEN_HMAC)
+    }
+
+    /// Get the expected mic extended salt identity token hmac.
+    pub fn get_expected_mic_extended_salt_identity_token_hmac<'env>(
+        &self,
+        env: &mut JNIEnv<'env>,
+    ) -> jni::errors::Result<[u8; 32]> {
+        static EXPECTED_MIC_EXTENDED_SALT_IDENTITY_TOKEN_HMAC: FieldDesc =
+            V1_DISCOVERY_CREDENTIAL_CLS.field("expectedMicExtendedSaltIdentityTokenHmac", "[B");
+        self.get_array(env, &EXPECTED_MIC_EXTENDED_SALT_IDENTITY_TOKEN_HMAC)
+    }
+
+    /// Get the expected signature identity token hmac.
+    pub fn get_expected_signature_identity_token_hmac<'env>(
+        &self,
+        env: &mut JNIEnv<'env>,
+    ) -> jni::errors::Result<[u8; 32]> {
+        static EXPECTED_SIGNATURE_IDENTITY_TOKEN_HMAC: FieldDesc =
+            V1_DISCOVERY_CREDENTIAL_CLS.field("expectedSignatureIdentityTokenHmac", "[B");
+        self.get_array(env, &EXPECTED_SIGNATURE_IDENTITY_TOKEN_HMAC)
+    }
+
+    /// Get the pub key.
+    pub fn get_pub_key<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<[u8; 32]> {
+        static PUB_KEY: FieldDesc = V1_DISCOVERY_CREDENTIAL_CLS.field("pubKey", "[B");
+        self.get_array(env, &PUB_KEY)
+    }
+
+    /// Convert this to the `np_ffi_core` representation.
+    pub fn get_as_core<'env>(
+        &self,
+        env: &mut JNIEnv<'env>,
+    ) -> jni::errors::Result<CoreV1DiscoveryCredential> {
+        Ok(CoreV1DiscoveryCredential::new(
+            self.get_key_seed(env)?,
+            self.get_expected_mic_short_salt_identity_token_hmac(env)?,
+            self.get_expected_mic_extended_salt_identity_token_hmac(env)?,
+            self.get_expected_signature_identity_token_hmac(env)?,
+            self.get_pub_key(env)?,
+        ))
+    }
+}
diff --git a/nearby/util/pourover/src/lib.rs b/nearby/presence/np_java_ffi/src/lib.rs
similarity index 71%
copy from nearby/util/pourover/src/lib.rs
copy to nearby/presence/np_java_ffi/src/lib.rs
index 51495de..7a688e8 100644
--- a/nearby/util/pourover/src/lib.rs
+++ b/nearby/presence/np_java_ffi/src/lib.rs
@@ -12,15 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Utilties for JNI interactions.
+//! Java bindings for np_ffi_core.
 
-// Enforce documentation
-#![deny(missing_docs)]
-#![deny(unsafe_op_in_unsafe_fn)]
+// Named lifetimes are being used to match jni crate conventions.
+// See: https://docs.rs/jni/latest/jni/struct.JNIEnv.html#lifetime-names
+#![allow(clippy::needless_lifetimes)]
 
-pub use pourover_macro::jni_method;
-
-pub mod desc;
-
-mod conversions;
-pub use conversions::{ToSigned, ToUnsigned};
+pub mod class;
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
new file mode 100644
index 0000000..d4f146e
--- /dev/null
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DeserializeTests.java
@@ -0,0 +1,199 @@
+/*
+ * 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.android.nearby.presence.rust.TestData.*;
+import static com.google.android.nearby.presence.rust.credential.CredentialBook.NoMetadata;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.android.nearby.presence.rust.credential.CredentialBook;
+import org.junit.jupiter.api.Test;
+
+public class DeserializeTests {
+
+  DeserializeResult parsePublicAdv(byte[] bytes) {
+    // Call parse with an empty CredentialBook
+    try (CredentialBook<NoMetadata> book = CredentialBook.empty()) {
+      return NpAdv.deserializeAdvertisement(bytes, book);
+    }
+  }
+
+  DeserializeResult parsePrivateAdv(byte[] bytes) {
+    try (CredentialBook<NoMetadata> book =
+        CredentialBook.<NoMetadata>builder()
+            .addDiscoveryCredential(V0_CRED, NoMetadata.INSTANCE)
+            .addDiscoveryCredential(V1_CRED, NoMetadata.INSTANCE)
+            .build()) {
+      return NpAdv.deserializeAdvertisement(bytes, book);
+    }
+  }
+
+  @Test
+  void deserializeAdvertisement_v0_canParsePublic() {
+    try (DeserializeResult result = parsePublicAdv(V0_PUBLIC)) {
+      assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V0_ADVERTISEMENT);
+
+      DeserializedV0Advertisement adv = result.getAsV0();
+
+      assertThat(adv).isNotNull();
+      assertThat(adv.isLegible()).isTrue();
+      assertThat(adv.getIdentity()).isEqualTo(IdentityKind.PLAINTEXT);
+      assertThat(adv.getDataElementCount()).isEqualTo(2);
+    }
+  }
+
+  @Test
+  void deserializeAdvertisement_v0_canParsePublicWithCreds() {
+    try (DeserializeResult result = parsePrivateAdv(V0_PUBLIC)) {
+      assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V0_ADVERTISEMENT);
+
+      DeserializedV0Advertisement adv = result.getAsV0();
+
+      assertThat(adv).isNotNull();
+      assertThat(adv.isLegible()).isTrue();
+      assertThat(adv.getIdentity()).isEqualTo(IdentityKind.PLAINTEXT);
+      assertThat(adv.getDataElementCount()).isEqualTo(2);
+    }
+  }
+
+  @Test
+  void deserializeAdvertisement_v0_canParsePrivate() {
+    try (DeserializeResult result = parsePrivateAdv(V0_PRIVATE)) {
+      assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V0_ADVERTISEMENT);
+
+      DeserializedV0Advertisement adv = result.getAsV0();
+
+      assertThat(adv).isNotNull();
+      assertThat(adv.isLegible()).isTrue();
+      assertThat(adv.getIdentity()).isEqualTo(IdentityKind.DECRYPTED);
+      assertThat(adv.getDataElementCount()).isEqualTo(1);
+      assertThat(adv.getDataElement(0)).isInstanceOf(V0DataElement.TxPower.class);
+    }
+  }
+
+  @Test
+  void deserializeAdvertisement_v0_cannotParsePrivateWithoutCreds() {
+    try (DeserializeResult result = parsePublicAdv(V0_PRIVATE)) {
+      assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V0_ADVERTISEMENT);
+
+      DeserializedV0Advertisement adv = result.getAsV0();
+
+      assertThat(adv).isNotNull();
+      assertThat(adv.isLegible()).isFalse();
+    }
+  }
+
+  @Test
+  void deserializeAdvertisement_v0_canReadTxPowerDe() {
+    try (DeserializeResult result = parsePublicAdv(V0_PUBLIC)) {
+      DeserializedV0Advertisement adv = result.getAsV0();
+
+      V0DataElement de = adv.getDataElement(0);
+
+      assertThat(de).isInstanceOf(V0DataElement.TxPower.class);
+      V0DataElement.TxPower txPower = (V0DataElement.TxPower) de;
+      assertThat(txPower.getTxPower()).isEqualTo(20);
+    }
+  }
+
+  @Test
+  void deserializeAdvertisement_v0_canReadActionsDe() {
+    try (DeserializeResult result = parsePublicAdv(V0_PUBLIC)) {
+      DeserializedV0Advertisement adv = result.getAsV0();
+
+      V0DataElement de = adv.getDataElement(1);
+
+      assertThat(de).isInstanceOf(V0DataElement.V0Actions.class);
+      V0DataElement.V0Actions actions = (V0DataElement.V0Actions) de;
+      assertThat(actions.getIdentityKind()).isEqualTo(IdentityKind.PLAINTEXT);
+      assertThat(actions.hasAction(V0DataElement.V0ActionType.NEARBY_SHARE)).isTrue();
+      assertThat(actions.hasAction(V0DataElement.V0ActionType.CROSS_DEV_SDK)).isFalse();
+    }
+  }
+
+  @Test
+  void deserializeAdvertisement_v1_canParsePublic() {
+    try (DeserializeResult result = parsePublicAdv(V1_PUBLIC)) {
+      assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V1_ADVERTISEMENT);
+
+      DeserializedV1Advertisement adv = result.getAsV1();
+
+      assertThat(adv).isNotNull();
+      assertThat(adv.getNumLegibleSections()).isEqualTo(1);
+      assertThat(adv.getNumUndecryptableSections()).isEqualTo(0);
+    }
+  }
+
+  @Test
+  void deserializeAdvertisement_v1_canParsePrivate() {
+    try (DeserializeResult result = parsePrivateAdv(V1_PRIVATE)) {
+      assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V1_ADVERTISEMENT);
+
+      DeserializedV1Advertisement adv = result.getAsV1();
+
+      assertThat(adv).isNotNull();
+      assertThat(adv.getNumLegibleSections()).isEqualTo(1);
+      assertThat(adv.getNumUndecryptableSections()).isEqualTo(0);
+
+      DeserializedV1Section section = adv.getSection(0);
+      assertThat(section.getIdentityKind()).isEqualTo(IdentityKind.DECRYPTED);
+      assertThat(section.getDataElementCount()).isEqualTo(1);
+    }
+  }
+
+  @Test
+  void deserializeAdvertisement_v1_canParsePrivateWithoutCreds() {
+    try (DeserializeResult result = parsePublicAdv(V1_PRIVATE)) {
+      assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V1_ADVERTISEMENT);
+
+      DeserializedV1Advertisement adv = result.getAsV1();
+
+      assertThat(adv).isNotNull();
+      assertThat(adv.getNumLegibleSections()).isEqualTo(0);
+      assertThat(adv.getNumUndecryptableSections()).isEqualTo(1);
+    }
+  }
+
+  @Test
+  void deserializeAdvertisement_v1_canParsePublicSection() {
+    try (DeserializeResult result = parsePublicAdv(V1_PUBLIC)) {
+      DeserializedV1Advertisement adv = result.getAsV1();
+
+      DeserializedV1Section section = adv.getSection(0);
+
+      assertThat(section).isNotNull();
+      assertThat(section.getIdentityKind()).isEqualTo(IdentityKind.PLAINTEXT);
+      assertThat(section.getDataElementCount()).isEqualTo(1);
+    }
+  }
+
+  @Test
+  void deserializeAdvertisement_v1_canReadGenericDe() {
+    try (DeserializeResult result = parsePublicAdv(V1_PUBLIC)) {
+      DeserializedV1Advertisement adv = result.getAsV1();
+      DeserializedV1Section section = adv.getSection(0);
+
+      V1DataElement de = section.getDataElement(0);
+
+      assertThat(de).isNotNull();
+      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);
+    }
+  }
+}
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
new file mode 100644
index 0000000..8ee26a1
--- /dev/null
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestData.java
@@ -0,0 +1,410 @@
+/*
+ * 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 com.google.android.nearby.presence.rust.credential.V0DiscoveryCredential;
+import com.google.android.nearby.presence.rust.credential.V1DiscoveryCredential;
+
+public class TestData {
+
+  public static final byte[] V0_PUBLIC = {
+    0x00, // adv header
+    0x15, 20, // tx power
+    0x26, 0x00, 0x40, // actions
+  };
+
+  public static final byte[] V1_PUBLIC = {
+    0x20, // NP Version Header
+    0x00, // Section format
+    0x02, // section len
+    0x15, 0x06, // tx power value 6
+  };
+
+  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,
+    0x11, 0x11, 0x11, 0x11
+  };
+
+  public static final byte[] V0_IDENTITY_TOKEN = {
+    0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+    0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+    0x33, 0x33, 0x33, 0x33
+  };
+
+  public static final byte[] V0_IDENTITY_TOKEN_HMAC = {
+    (byte) 0x09,
+    (byte) 0xFE,
+    (byte) 0x9E,
+    (byte) 0x81,
+    (byte) 0xB7,
+    (byte) 0x3E,
+    (byte) 0x5E,
+    (byte) 0xCC,
+    (byte) 0x76,
+    (byte) 0x59,
+    (byte) 0x57,
+    (byte) 0x71,
+    (byte) 0xE0,
+    (byte) 0x1F,
+    (byte) 0xFB,
+    (byte) 0x34,
+    (byte) 0x38,
+    (byte) 0xE7,
+    (byte) 0x5F,
+    (byte) 0x24,
+    (byte) 0xA7,
+    (byte) 0x69,
+    (byte) 0x56,
+    (byte) 0xA0,
+    (byte) 0xB8,
+    (byte) 0xEA,
+    (byte) 0x67,
+    (byte) 0xD1,
+    (byte) 0x1C,
+    (byte) 0x3E,
+    (byte) 0x36,
+    (byte) 0xFD
+  };
+
+  public static final V0DiscoveryCredential V0_CRED =
+      new V0DiscoveryCredential(V0_KEY_SEED, V0_IDENTITY_TOKEN_HMAC);
+
+  public static final byte[] V0_PRIVATE = {
+    0x04, // adv header
+    0x22,
+    0x22, // salt
+    (byte) 0xD8,
+    (byte) 0x22,
+    (byte) 0x12,
+    (byte) 0xEF,
+    (byte) 0x16,
+    (byte) 0xDB,
+    (byte) 0xF8,
+    (byte) 0x72,
+    (byte) 0xF2,
+    (byte) 0xA3,
+    (byte) 0xA7,
+    (byte) 0xC0,
+    (byte) 0xFA,
+    (byte) 0x52,
+    (byte) 0x48,
+    (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 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) 0x39,
+    (byte) 0xdc,
+    (byte) 0x16,
+    (byte) 0x27,
+    (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) 0x32,
+    (byte) 0xb1,
+    (byte) 0xf2,
+    (byte) 0x1a,
+    (byte) 0xf7,
+    (byte) 0x7d,
+    (byte) 0x95,
+    (byte) 0x8f,
+    (byte) 0xeb,
+    (byte) 0x5f,
+    (byte) 0xbe,
+    (byte) 0xfd,
+    (byte) 0x62,
+    (byte) 0xa7,
+    (byte) 0xc0,
+    (byte) 0x16,
+    (byte) 0x66
+  };
+
+  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) 0x54,
+    (byte) 0xca,
+    (byte) 0xc9,
+    (byte) 0xcb,
+    (byte) 0xca,
+    (byte) 0x72,
+    (byte) 0x94,
+    (byte) 0x24,
+    (byte) 0xd8,
+    (byte) 0xf5,
+    (byte) 0xa6,
+    (byte) 0x1e,
+    (byte) 0xcf,
+    (byte) 0x04,
+    (byte) 0x3e,
+    (byte) 0x8f,
+    (byte) 0x91,
+    (byte) 0x81,
+    (byte) 0x6d,
+    (byte) 0x19,
+    (byte) 0x74
+  };
+
+  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
+  };
+}
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
new file mode 100644
index 0000000..12302d3
--- /dev/null
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/credential/CredentialBookTests.java
@@ -0,0 +1,40 @@
+/*
+ * 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/src/lib.rs b/nearby/presence/rand_ext/src/lib.rs
index d767143..587279d 100644
--- a/nearby/presence/rand_ext/src/lib.rs
+++ b/nearby/presence/rand_ext/src/lib.rs
@@ -20,8 +20,8 @@
 use alloc::vec::Vec;
 use crypto_provider::{CryptoProvider, CryptoRng};
 use log::info;
-pub use rand;
 use rand::{Rng as _, SeedableRng};
+pub use {rand, rand_pcg};
 
 /// Returns a random Vec with the provided length.
 pub fn random_vec<C: CryptoProvider>(rng: &mut C::CryptoRng, len: usize) -> Vec<u8> {
@@ -52,7 +52,7 @@
 }
 
 /// Returns a fast rng seeded with the thread rng (which is itself seeded from the OS).
-pub fn seeded_rng() -> impl rand::Rng {
+pub fn seeded_rng() -> rand_pcg::Pcg64 {
     let mut seed: <rand_pcg::Pcg64 as rand::SeedableRng>::Seed = Default::default();
     rand::thread_rng().fill(&mut seed);
     // print it out so if a test fails, the seed will be visible for further investigation
diff --git a/nearby/presence/test_helper/Cargo.toml b/nearby/presence/test_helper/Cargo.toml
index c52d6bf..340a843 100644
--- a/nearby/presence/test_helper/Cargo.toml
+++ b/nearby/presence/test_helper/Cargo.toml
@@ -10,3 +10,4 @@
 [dependencies]
 hex.workspace = true
 serde_json.workspace = true
+itertools.workspace = true
diff --git a/nearby/presence/test_helper/src/lib.rs b/nearby/presence/test_helper/src/lib.rs
index 8b42623..76dccf4 100644
--- a/nearby/presence/test_helper/src/lib.rs
+++ b/nearby/presence/test_helper/src/lib.rs
@@ -16,6 +16,7 @@
 
 #![allow(clippy::unwrap_used, clippy::expect_used)]
 
+use itertools::Itertools;
 use std::fs;
 use std::io::Read;
 
@@ -62,3 +63,16 @@
 pub fn string_to_hex(str: &str) -> Vec<u8> {
     hex::decode(str).unwrap()
 }
+
+/// Format data as hex bytes for the convenience of test data in FFI tests.
+///
+/// # Examples
+///
+/// ```
+/// use test_helper::hex_bytes;
+///
+/// assert_eq!("0x12, 0x34", hex_bytes(&[0x12, 0x34]));
+/// ```
+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
new file mode 100644
index 0000000..cf5d1a2
--- /dev/null
+++ b/nearby/presence/test_vector_hkdf/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "test_vector_hkdf"
+version.workspace = true
+edition.workspace = true
+publish.workspace = true
+
+[lints]
+workspace = true
+
+[dependencies]
+crypto_provider.workspace = true
+crypto_provider_default = { workspace = true, features = ["rustcrypto"] }
+
+[dev-dependencies]
+
+
+
diff --git a/nearby/presence/test_vector_hkdf/src/lib.rs b/nearby/presence/test_vector_hkdf/src/lib.rs
new file mode 100644
index 0000000..d067eec
--- /dev/null
+++ b/nearby/presence/test_vector_hkdf/src/lib.rs
@@ -0,0 +1,91 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Tools for generating test vector data in a repeatable way.
+//!
+//! The common approach of just using an RNG means that even a small change to how test vectors are
+//! created will regenerate everything. Instead, by using a predictable seed and deriving data from
+//! that with per-datum names, incremental changes can be made without regenerating everything.
+//!
+//! # Examples
+//!
+//! Generating 100 groups of data:
+//!
+//! ```
+//! use crypto_provider_default::CryptoProviderImpl;
+//! use test_vector_hkdf::TestVectorHkdf;
+//!
+//! for i in 0_u32..100 {
+//!     let hkdf = TestVectorHkdf::<CryptoProviderImpl>::new(
+//!         "Spiffy test vectors - random seed: hunter2",
+//!          i.to_be_bytes().as_slice());
+//!
+//!     let vec = hkdf.derive_vec(
+//!         "fun data",
+//!         hkdf.derive_range_element("fun data length", 3..=18).try_into().unwrap());
+//!     let array = hkdf.derive_array::<16>("array data");
+//!     // store the generated data somewhere
+//! }
+//! ```
+
+#![allow(clippy::unwrap_used)]
+
+use crypto_provider::hkdf::Hkdf;
+use crypto_provider::CryptoProvider;
+use crypto_provider_default::CryptoProviderImpl;
+use std::ops;
+
+/// Typical usage would generate one instance per loop, and derive all data needed for that loop
+/// iteration.
+///
+/// The `description` passed to all `derive_` calls should be distinct, unless identical data is
+/// desired.
+pub struct TestVectorHkdf<C: CryptoProvider = CryptoProviderImpl> {
+    hkdf: C::HkdfSha256,
+}
+
+impl<C: CryptoProvider> TestVectorHkdf<C> {
+    /// Create a new instance for the provided namespace and iteration.
+    ///
+    /// The namespace should contain a blob of randomly generated data. If reshuffling the generated
+    /// data is desired, just change the blob of random data.
+    pub fn new(namespace: &str, iteration: &[u8]) -> Self {
+        Self { hkdf: C::HkdfSha256::new(Some(namespace.as_bytes()), iteration) }
+    }
+
+    /// Derive an array of `N` bytes.
+    pub fn derive_array<const N: usize>(&self, description: &str) -> [u8; N] {
+        let mut arr = [0; N];
+        self.hkdf.expand(description.as_bytes(), &mut arr).unwrap();
+        arr
+    }
+
+    /// Derive a Vec of the specified `len`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `len` is too long for an HKDF output.
+    pub fn derive_vec(&self, description: &str, len: usize) -> Vec<u8> {
+        let mut vec = vec![0; len];
+        self.hkdf.expand(description.as_bytes(), &mut vec).unwrap();
+        vec
+    }
+
+    /// Generated a biased element in a range using a sloppy sampling technique.
+    /// Will be increasingly biased as the range gets bigger.
+    pub fn derive_range_element(&self, description: &str, range: ops::RangeInclusive<u64>) -> u64 {
+        let num = u64::from_be_bytes(self.derive_array(description));
+        num % (range.end() - range.start() + 1) + range.start()
+    }
+}
diff --git a/nearby/presence/xts_aes/fuzz/Cargo.toml b/nearby/presence/xts_aes/fuzz/Cargo.toml
index 4d00c49..5d99173 100644
--- a/nearby/presence/xts_aes/fuzz/Cargo.toml
+++ b/nearby/presence/xts_aes/fuzz/Cargo.toml
@@ -9,27 +9,17 @@
 cargo-fuzz = true
 
 [dependencies]
-libfuzzer-sys = "0.4"
-arbitrary = { version = "1.1.7", features = ["derive"] }
+arbitrary = { workspace = true, features = ["derive"] }
+crypto_provider.workspace = true
+crypto_provider_rustcrypto.workspace = true
+derive_fuzztest.workspace = true
+ldt_tbc.workspace = true
+xts_aes.workspace = true
 
-[dependencies.xts_aes]
-path = ".."
-
-[dependencies.crypto_provider]
-path = "../../../crypto/crypto_provider"
-
-[dependencies.crypto_provider_rustcrypto]
-path = "../../../crypto/crypto_provider_rustcrypto"
-
-[dependencies.ldt_tbc]
-path = "../../../presence/ldt_tbc"
-
-# Prevent this from interfering with workspaces
-[workspace]
-members = ["."]
+[target.'cfg(fuzzing)'.dependencies]
+libfuzzer-sys.workspace = true
 
 [[bin]]
-name = "xts-roundtrip"
-path = "fuzz_targets/xts_roundtrip.rs"
-test = false
+name = "xts_roundtrip"
+path = "src/bin/xts_roundtrip.rs"
 doc = false
diff --git a/nearby/presence/xts_aes/fuzz/fuzz_targets/xts_roundtrip.rs b/nearby/presence/xts_aes/fuzz/src/bin/xts_roundtrip.rs
similarity index 86%
rename from nearby/presence/xts_aes/fuzz/fuzz_targets/xts_roundtrip.rs
rename to nearby/presence/xts_aes/fuzz/src/bin/xts_roundtrip.rs
index b7133de..f135c3b 100644
--- a/nearby/presence/xts_aes/fuzz/fuzz_targets/xts_roundtrip.rs
+++ b/nearby/presence/xts_aes/fuzz/src/bin/xts_roundtrip.rs
@@ -1,4 +1,4 @@
-#![no_main]
+#![cfg_attr(fuzzing, no_main)]
 // Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,11 +14,12 @@
 // limitations under the License.
 
 use crypto_provider_rustcrypto::RustCrypto;
+use derive_fuzztest::fuzztest;
 use ldt_tbc::{TweakableBlockCipherDecrypter, TweakableBlockCipherEncrypter};
-use libfuzzer_sys::fuzz_target;
 use xts_aes::*;
 
-fuzz_target!(|data: XtsFuzzInput| {
+#[fuzztest]
+fn test(data: XtsFuzzInput) {
     // XTS requires at least one block
     if data.plaintext.len() < 16 {
         return;
@@ -37,14 +38,12 @@
 
     let mut buffer = data.plaintext.clone();
 
-    xts_enc
-        .encrypt_data_unit(tweak.clone(), &mut buffer[..])
-        .unwrap();
+    xts_enc.encrypt_data_unit(tweak.clone(), &mut buffer[..]).unwrap();
     xts_dec.decrypt_data_unit(tweak, &mut buffer[..]).unwrap();
     assert_eq!(data.plaintext, buffer);
-});
+}
 
-#[derive(Debug, arbitrary::Arbitrary)]
+#[derive(Clone, Debug, arbitrary::Arbitrary)]
 struct XtsFuzzInput {
     key: [u8; 32],
     tweak: [u8; 16],
diff --git a/nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs b/nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs
index abe1077..10b8c7d 100644
--- a/nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs
+++ b/nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs
@@ -15,16 +15,14 @@
 #![allow(clippy::unwrap_used)]
 
 use aes::{cipher, cipher::KeyInit as _};
-use alloc::vec::Vec;
 use crypto_provider::aes::*;
 use crypto_provider::CryptoProvider;
 use crypto_provider_default::CryptoProviderImpl;
 use ldt_tbc::TweakableBlockCipherDecrypter;
 use ldt_tbc::TweakableBlockCipherEncrypter;
-use rand::{self, distributions, Rng as _};
+use rand::{distributions, Rng as _};
 use rand_ext::seeded_rng;
 use xts_aes::{Tweak, XtsAes128Key, XtsAes256Key, XtsDecrypter, XtsEncrypter, XtsKey};
-extern crate alloc;
 
 #[test]
 fn identical_to_xtsmode_crate() {
diff --git a/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs b/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs
index 1f92f89..ede231d 100644
--- a/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs
+++ b/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs
@@ -20,7 +20,7 @@
 use ldt_tbc::TweakableBlockCipherEncrypter;
 use ldt_tbc::TweakableBlockCipherKey;
 use wycheproof::cipher::TestGroup;
-use xts_aes::{self, XtsAes128Key, XtsAes256Key, XtsDecrypter, XtsEncrypter, XtsKey};
+use xts_aes::{XtsAes128Key, XtsAes256Key, XtsDecrypter, XtsEncrypter, XtsKey};
 
 #[test]
 fn run_wycheproof_vectors() {
diff --git a/nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs b/nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs
index 6ceb8b9..6661e96 100644
--- a/nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs
+++ b/nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs
@@ -21,7 +21,7 @@
 use ldt_tbc::TweakableBlockCipherEncrypter;
 use ldt_tbc::TweakableBlockCipherKey;
 use std::{collections::hash_map, fs, io, io::BufRead as _};
-use xts_aes::{self, XtsAes128Key, XtsAes256Key, XtsDecrypter, XtsEncrypter, XtsKey};
+use xts_aes::{XtsAes128Key, XtsAes256Key, XtsDecrypter, XtsEncrypter, XtsKey};
 
 #[test]
 fn nist_test_vectors_data_unit_seq_128() -> Result<(), anyhow::Error> {
diff --git a/nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs b/nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs
index 924b665..ab967dc 100644
--- a/nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs
+++ b/nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs
@@ -14,16 +14,14 @@
 
 #![allow(clippy::unwrap_used)]
 
-use alloc::vec::Vec;
 use crypto_provider::aes::*;
 use crypto_provider::CryptoProvider;
 use crypto_provider_default::CryptoProviderImpl;
 use ldt_tbc::TweakableBlockCipherDecrypter;
 use ldt_tbc::TweakableBlockCipherEncrypter;
-use rand::{self, distributions, Rng as _};
+use rand::{distributions, Rng as _};
 use rand_ext::seeded_rng;
 use xts_aes::{Tweak, XtsAes128Key, XtsAes256Key, XtsDecrypter, XtsEncrypter, XtsKey};
-extern crate alloc;
 
 #[test]
 fn roundtrip_self() {
diff --git a/nearby/util/pourover_macro_core/src/lib.rs b/nearby/src/coverage.rs
similarity index 91%
rename from nearby/util/pourover_macro_core/src/lib.rs
rename to nearby/src/coverage.rs
index ae06345..329e11c 100644
--- a/nearby/util/pourover_macro_core/src/lib.rs
+++ b/nearby/src/coverage.rs
@@ -12,5 +12,3 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-mod jni_method;
-pub use jni_method::jni_method;
diff --git a/nearby/src/crypto_ffi.rs b/nearby/src/crypto_ffi.rs
index 42ac5ab..b1e08e9 100644
--- a/nearby/src/crypto_ffi.rs
+++ b/nearby/src/crypto_ffi.rs
@@ -19,13 +19,8 @@
 
 use crate::CargoOptions;
 
-pub fn boringssl_check_everything(root: &Path, cargo_options: &CargoOptions) -> anyhow::Result<()> {
-    check_boringssl(root, cargo_options)?;
-    Ok(())
-}
-
 pub fn build_boringssl(root: &Path) -> anyhow::Result<()> {
-    let bindgen_version_req = VersionReq::parse(">=0.61.0")?;
+    let bindgen_version_req = VersionReq::parse(">=0.69.4")?;
     let bindgen_version = get_bindgen_version()?;
 
     if !bindgen_version_req.matches(&bindgen_version) {
@@ -77,11 +72,42 @@
     run_cmd_shell(&bssl_dir, format!("cargo check {locked_arg}"))?;
     run_cmd_shell(&bssl_dir, "cargo fmt --check")?;
     run_cmd_shell(&bssl_dir, "cargo clippy --all-targets")?;
-    run_cmd_shell(&bssl_dir, format!("cargo test {locked_arg} -- --color=always"))?;
+    run_cmd_shell(&bssl_dir, cargo_options.test("check_boringssl", ""))?;
     run_cmd_shell(&bssl_dir, "cargo doc --no-deps")?;
+    run_cmd_shell(
+        root,
+        cargo_options.test(
+            "check_boringssl_ukey2",
+            "-p ukey2_connections -p ukey2_rs --no-default-features --features test_boringssl",
+        ),
+    )?;
+    Ok(())
+}
 
-    run_cmd_shell(root, "cargo test -p ukey2_connections -p ukey2_rs --no-default-features --features test_boringssl")?;
+/// Checks out latest boringssl commit and runs our crypto provider tests against it
+pub fn check_boringssl_at_head(root: &Path, cargo_options: &CargoOptions) -> anyhow::Result<()> {
+    // TODO: find a better way, a kokoro implemented auto-roller?
+    build_boringssl_at_latest(root)?;
 
+    let bssl_dir = root.join("crypto/crypto_provider_boringssl");
+    run_cmd_shell(&bssl_dir, "cargo check")?;
+    run_cmd_shell(&bssl_dir, cargo_options.test("check_boringssl_latest", ""))?;
+    Ok(())
+}
+
+fn build_boringssl_at_latest(root: &Path) -> anyhow::Result<()> {
+    // Now check boringssl against HEAD. Kokoro does not allow us to directly update the git submodule
+    // so we must use manual hackery instead :/
+    run_cmd_shell(root.parent().unwrap(), "rm -Rf third_party/boringssl")?;
+    run_cmd_shell(
+        &root.parent().unwrap().join("third_party"),
+        "git clone https://boringssl.googlesource.com/boringssl",
+    )?;
+    run_cmd_shell(
+        &root.parent().unwrap().join("third_party/boringssl"),
+        "git checkout origin/master",
+    )?;
+    build_boringssl(root)?;
     Ok(())
 }
 
diff --git a/nearby/src/ffi.rs b/nearby/src/ffi.rs
index 68e6e8c..ac0c585 100644
--- a/nearby/src/ffi.rs
+++ b/nearby/src/ffi.rs
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::CargoOptions;
+use crate::{crypto_ffi, CargoOptions};
 use cmd_runner::{run_cmd_shell, run_cmd_shell_with_color, YellowStderr};
 use std::{fs, path};
 
 // wrapper for checking all ffi related things
-pub fn check_everything(root: &path::Path, cargo_options: &CargoOptions) -> anyhow::Result<()> {
+pub fn check_ffi(root: &path::Path, cargo_options: &CargoOptions) -> anyhow::Result<()> {
+    crypto_ffi::check_boringssl(root, cargo_options)?;
     check_np_ffi_cmake(root, cargo_options)?;
     check_ldt_cmake(root, cargo_options)?;
 
@@ -33,7 +34,7 @@
 
     run_cmd_shell_with_color::<YellowStderr>(
         &build_dir,
-        "cmake -G Ninja -DENABLE_TESTS=true -DCMAKE_BUILD_TYPE=Release ..",
+        "cmake -G Ninja -DENABLE_TESTS=true -DCMAKE_BUILD_TYPE=Release -DENABLE_FUZZ=false ..",
     )?;
 
     // verify sample and benchmarks build
@@ -74,10 +75,10 @@
 
     run_cmd_shell_with_color::<YellowStderr>(
         &build_dir,
-        "cmake -G Ninja -DENABLE_TESTS=true -DCMAKE_BUILD_TYPE=Release ..",
+        "cmake -G Ninja -DENABLE_TESTS=true -DCMAKE_BUILD_TYPE=Release -DENABLE_FUZZ=false ..",
     )?;
 
-    // verify sample and benchmarks build
+    run_cmd_shell(root, format!("cargo build {locked_arg} -p ldt_np_adv_ffi --quiet --release"))?;
     run_cmd_shell_with_color::<YellowStderr>(&build_dir, "cmake --build . --target ldt_c_sample")?;
     run_cmd_shell_with_color::<YellowStderr>(
         &build_dir,
diff --git a/nearby/src/fuzzers.rs b/nearby/src/fuzzers.rs
index aba01b3..fa4f21e 100644
--- a/nearby/src/fuzzers.rs
+++ b/nearby/src/fuzzers.rs
@@ -19,19 +19,19 @@
     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",
+        "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",
+        "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",
+        "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",
+        "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"),
@@ -61,7 +61,7 @@
 }
 
 // Runs the fuzztest fuzzers as short lived unit tests, compatible with gtest
-pub(crate) fn build_fuzztest_uts(root: &path::Path) -> anyhow::Result<()> {
+pub(crate) fn build_fuzztest_unit_tests(root: &path::Path) -> anyhow::Result<()> {
     log::info!("Checking fuzztest targets in unit test mode");
 
     // first build the rust static libs to link against
diff --git a/nearby/src/jni.rs b/nearby/src/jni.rs
index 0b000fb..54aafdc 100644
--- a/nearby/src/jni.rs
+++ b/nearby/src/jni.rs
@@ -33,3 +33,10 @@
     run_cmd_shell(&ukey2_jni_path, "./gradlew :test")?;
     Ok(())
 }
+
+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")?;
+    let ukey2_jni_path = root.to_path_buf().join("presence/np_java_ffi");
+    run_cmd_shell(&ukey2_jni_path, "./gradlew :test --info --rerun")?;
+    Ok(())
+}
diff --git a/nearby/src/license.rs b/nearby/src/license.rs
index d9491b2..2229d04 100644
--- a/nearby/src/license.rs
+++ b/nearby/src/license.rs
@@ -12,68 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use chrono::Datelike;
-use file_header::{check_headers_recursively, license::spdx::*};
-use std::path;
+use cmd_runner::license_checker::LicenseChecker;
 
-pub(crate) fn check_license_headers(root: &path::Path) -> anyhow::Result<()> {
-    log::info!("Checking license headers");
-    let ignore = license_ignore()?;
-    let results = check_headers_recursively(
-        root,
-        |p| !ignore.is_match(p),
-        APACHE_2_0.build_header(YearCopyrightOwnerValue::new(
-            u32::try_from(chrono::Utc::now().year())?,
-            "Google LLC".to_string(),
-        )),
-        4,
-    )?;
-
-    for path in results.no_header_files.iter() {
-        eprintln!("Header not present: {path:?}");
-    }
-
-    for path in results.binary_files.iter() {
-        eprintln!("Binary file: {path:?}");
-    }
-    if !results.binary_files.is_empty() {
-        eprintln!("Consider adding binary files to the ignore list in src/licence.rs.");
-    }
-
-    if results.has_failure() {
-        Err(anyhow::anyhow!("License header check failed"))
-    } else {
-        Ok(())
-    }
-}
-
-pub(crate) fn add_license_headers(root: &path::Path) -> anyhow::Result<()> {
-    let ignore = license_ignore()?;
-    for p in file_header::add_headers_recursively(
-        root,
-        |p| !ignore.is_match(p),
-        APACHE_2_0.build_header(YearCopyrightOwnerValue::new(
-            u32::try_from(chrono::Utc::now().year())?,
-            "Google LLC".to_string(),
-        )),
-    )? {
-        println!("Added header: {:?}", p);
-    }
-
-    Ok(())
-}
-
-fn license_ignore() -> Result<globset::GlobSet, globset::Error> {
-    let mut builder = globset::GlobSetBuilder::new();
-    for lic in license_ignore_dirs() {
-        builder.add(globset::Glob::new(lic)?);
-    }
-    builder.build()
-}
-
-fn license_ignore_dirs() -> Vec<&'static str> {
+pub const LICENSE_CHECKER: LicenseChecker = LicenseChecker {
     // These will be checked against the absolute path of each file.
-    vec![
+    ignore: &[
         "**/android/build/**",
         "**/target/**",
         "**/.idea/**",
@@ -81,6 +24,7 @@
         "**/java/build/**",
         "**/java/*/build/**",
         "**/ukey2_c_ffi/cpp/build/**",
+        "**/np_java_ffi/build/**",
         "**/*.toml",
         "**/*.md",
         "**/*.lock",
@@ -110,15 +54,18 @@
         "**/fuzz/artifacts/**",
         "**/cmake-build-debug/**",
         "**/tags",
-    ]
-}
+        "**/MODULE.bazel",
+        "**/WORKSPACE",
+        "**/.bazelrc",
+    ],
+};
 
 #[cfg(test)]
 mod tests {
+    use super::LICENSE_CHECKER;
+
     #[test]
     fn new_ignore_is_likely_buggy() {
-        for dir in super::license_ignore_dirs() {
-            assert!(dir.starts_with("**/"), "Matching on the root filesystem is likely unintended");
-        }
+        LICENSE_CHECKER.check_new_ignore_is_likely_buggy()
     }
 }
diff --git a/nearby/src/main.rs b/nearby/src/main.rs
index a38676f..25fe348 100644
--- a/nearby/src/main.rs
+++ b/nearby/src/main.rs
@@ -15,8 +15,9 @@
 extern crate core;
 
 use clap::Parser as _;
-use cmd_runner::{run_cmd, run_cmd_shell, YellowStderr};
+use cmd_runner::{license_checker::LicenseSubcommand, run_cmd, run_cmd_shell, YellowStderr};
 use env_logger::Env;
+use license::LICENSE_CHECKER;
 use std::{env, ffi::OsString, path};
 
 mod crypto_ffi;
@@ -35,23 +36,33 @@
     );
 
     match cli.subcommand {
-        Subcommand::CheckEverything { ref check_options } => {
-            check_everything(&root_dir, check_options)?
+        Subcommand::RunDefaultChecks(ref 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",
+                "    cargo run -- run-rust-fuzzers\n",
+                "    cargo run -- check-stack-usage\n",
+            ));
         }
+        Subcommand::VerifyCi { ref check_options } => verify_ci(&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::FfiCheckEverything(ref options) => ffi::check_everything(&root_dir, options)?,
-        Subcommand::BoringsslCheckEverything(ref options) => {
-            crypto_ffi::boringssl_check_everything(&root_dir, options)?
-        }
+        Subcommand::CheckAllFfi(ref options) => ffi::check_ffi(&root_dir, options)?,
         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)?
+        }
         Subcommand::RunRustFuzzers => fuzzers::run_rust_fuzzers(&root_dir)?,
-        Subcommand::CheckFuzztest => fuzzers::build_fuzztest_uts(&root_dir)?,
-        Subcommand::CheckLicenseHeaders => license::check_license_headers(&root_dir)?,
-        Subcommand::AddLicenseHeaders => license::add_license_headers(&root_dir)?,
+        Subcommand::CheckFuzztest => fuzzers::build_fuzztest_unit_tests(&root_dir)?,
+        Subcommand::License(license_subcommand) => {
+            license_subcommand.run(&LICENSE_CHECKER, &root_dir)?
+        }
         Subcommand::CheckUkey2Ffi(ref options) => ukey2::check_ukey2_ffi(&root_dir, options)?,
         Subcommand::RunUkey2JniTests => jni::run_ukey2_jni_tests(&root_dir)?,
+        Subcommand::RunNpJavaFfiTests => jni::run_np_java_ffi_tests(&root_dir)?,
         Subcommand::CheckLdtJni => jni::check_ldt_jni(&root_dir)?,
         Subcommand::CheckLdtCmake(ref options) => ffi::check_ldt_cmake(&root_dir, options)?,
         Subcommand::CheckNpFfiCmake(ref options) => ffi::check_np_ffi_cmake(&root_dir, options)?,
@@ -61,7 +72,7 @@
     Ok(())
 }
 
-fn check_format(root: &path::Path, options: &CheckOptions) -> anyhow::Result<()> {
+fn check_format(root: &path::Path, options: &FormatterOptions) -> anyhow::Result<()> {
     // Rust format
     {
         let fmt_command = if options.reformat { "cargo fmt" } else { "cargo fmt --check" };
@@ -112,15 +123,18 @@
     log::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)?;
+    check_format(root, &options.formatter_options)?;
 
     for cargo_cmd in [
         // make sure everything compiles
         "cargo check --workspace --all-targets --quiet",
         // run all the tests
-        "cargo test --workspace --quiet -- --color=always",
+        &options.cargo_options.test("check_workspace", "--workspace --quiet"),
         // Test ukey2 builds with different crypto providers
-        "cargo test -p ukey2_connections -p ukey2_rs --no-default-features --features test_rustcrypto",
+        &options.cargo_options.test(
+            "check_workspace_ukey2",
+            "-p ukey2_connections -p ukey2_rs --no-default-features --features test_rustcrypto",
+        ),
         // ensure the docs are valid (cross-references to other code, etc)
         concat!(
             "RUSTDOCFLAGS='--deny warnings' ",
@@ -136,19 +150,28 @@
     Ok(())
 }
 
-/// Runs checks to ensure lints are passing and all targets are building
-pub fn check_everything(root: &path::Path, check_options: &CheckOptions) -> anyhow::Result<()> {
-    license::check_license_headers(root)?;
+/// Runs default checks that are suiable for verifying a local change.
+pub fn run_default_checks(root: &path::Path, check_options: &CheckOptions) -> anyhow::Result<()> {
+    license::LICENSE_CHECKER.check(root)?;
     check_workspace(root, check_options)?;
     crypto_ffi::check_boringssl(root, &check_options.cargo_options)?;
-    ffi::check_everything(root, &check_options.cargo_options)?;
-    jni::check_ldt_jni(root)?;
-    jni::run_kotlin_tests(root)?;
-    jni::run_ukey2_jni_tests(root)?;
+    ffi::check_ffi(root, &check_options.cargo_options)?;
+    if !cfg!(target_os = "windows") {
+        fuzzers::build_fuzztest_unit_tests(root)?;
+    }
+    crypto_ffi::check_boringssl_at_head(root, &check_options.cargo_options)?;
     ukey2::check_ukey2_ffi(root, &check_options.cargo_options)?;
-    fuzzers::run_rust_fuzzers(root)?;
-    fuzzers::build_fuzztest_uts(root)?;
+    if !cfg!(target_os = "windows") {
+        // Test below requires Java SE 9, but on Windows we only have Java SE 8 installed
+        jni::run_np_java_ffi_tests(root)?;
+    }
+    jni::run_ukey2_jni_tests(root)?;
+    Ok(())
+}
 
+/// Runs checks to ensure lints are passing and all targets are building
+pub fn verify_ci(root: &path::Path, check_options: &CheckOptions) -> anyhow::Result<()> {
+    run_default_checks(root, check_options)?;
     Ok(())
 }
 
@@ -157,6 +180,7 @@
     run_cmd_shell(&root.join("presence/np_c_ffi"), "cargo clean")?;
     run_cmd_shell(&root.join("crypto/crypto_provider_boringssl"), "cargo clean")?;
     run_cmd_shell(&root.join("connections/ukey2/ukey2_c_ffi"), "cargo clean")?;
+    run_cmd_shell(&root.join("presence/np_java_ffi"), "./gradlew :clean")?;
     Ok(())
 }
 
@@ -168,37 +192,39 @@
 
 #[derive(clap::Subcommand, Debug, Clone)]
 enum Subcommand {
-    /// Checks everything in beto-rust
-    CheckEverything {
+    /// Runs all of the checks that CI runs
+    VerifyCi {
         #[command(flatten)]
         check_options: CheckOptions,
     },
+    /// Runs the default set of checks suitable for verifying local changes.
+    RunDefaultChecks(CheckOptions),
     /// Cleans the main workspace and all sub projects - useful if upgrading rust compiler version
     /// and need dependencies to be compiled with the same version
     CleanEverything,
+    /// Checks code formatting
+    CheckFormat(FormatterOptions),
     /// Checks everything included in the top level workspace
     CheckWorkspace(CheckOptions),
-    /// Checks everything related to the boringssl version
-    BoringsslCheckEverything(CargoOptions),
     /// Clones boringssl and uses bindgen to generate the rust crate
     BuildBoringssl,
     /// Run crypto provider tests using boringssl backend
     CheckBoringssl(CargoOptions),
+    /// Checks out latest boringssl commit and runs our tests against it
+    CheckBoringsslAtLatest(CargoOptions),
     /// Build and run pure Rust fuzzers for 10000 runs
     RunRustFuzzers,
     /// Builds and runs fuzztest property based unit tests
     CheckFuzztest,
     /// Builds and runs tests for all C/C++ projects. This is a combination of CheckNpFfi,
     /// CheckLdtFfi, and CheckCmakeBuildAndTests
-    FfiCheckEverything(CargoOptions),
+    CheckAllFfi(CargoOptions),
     /// Checks the CMake build and runs all of the C/C++ tests
     CheckLdtCmake(CargoOptions),
     /// Checks the CMake build and runs all of the C/C++ tests
     CheckNpFfiCmake(CargoOptions),
-    /// Checks the workspace 3rd party crates and makes sure they have a valid license
-    CheckLicenseHeaders,
-    /// Generate new headers for any files that are missing them
-    AddLicenseHeaders,
+    #[command(flatten)]
+    License(LicenseSubcommand),
     /// Builds and runs tests for the UKEY2 FFI
     CheckUkey2Ffi(CargoOptions),
     /// Checks the build of ldt_jni wrapper with non default features, ie boringssl
@@ -207,12 +233,20 @@
     RunKotlinTests,
     /// Checks the build of the ukey2_jni wrapper and runs tests
     RunUkey2JniTests,
+    /// Checks the build of the np_java_ffi wrapper and runs tests
+    RunNpJavaFfiTests,
+}
+
+#[derive(clap::Args, Debug, Clone, Default)]
+pub struct FormatterOptions {
+    #[arg(long, help = "reformat files files in the workspace with the code formatter")]
+    reformat: bool,
 }
 
 #[derive(clap::Args, Debug, Clone, Default)]
 pub struct CheckOptions {
-    #[arg(long, help = "reformat files with cargo fmt")]
-    reformat: bool,
+    #[command(flatten)]
+    formatter_options: FormatterOptions,
     #[command(flatten)]
     cargo_options: CargoOptions,
 }
@@ -221,4 +255,23 @@
 pub struct CargoOptions {
     #[arg(long, help = "whether to run cargo with --locked")]
     locked: bool,
+    #[arg(long, help = "gather coverage metrics")]
+    coverage: bool,
+}
+
+impl CargoOptions {
+    /// Run `cargo test` or `cargo llvm-cov` depending on the configured options.
+    pub fn test(&self, tag: &str, args: impl AsRef<str>) -> String {
+        format!(
+            "cargo {subcommand} {locked} {args} {cov_args} -- --color=always",
+            subcommand = if self.coverage { "llvm-cov" } else { "test" },
+            locked = if self.locked { "--locked" } else { "" },
+            args = args.as_ref(),
+            cov_args = if self.coverage {
+                format!("--lcov --output-path \"target/{tag}.info\"")
+            } else {
+                String::default()
+            },
+        )
+    }
 }
diff --git a/nearby/util/pourover_macro/Cargo.toml b/nearby/util/pourover_macro/Cargo.toml
deleted file mode 100644
index ac230b1..0000000
--- a/nearby/util/pourover_macro/Cargo.toml
+++ /dev/null
@@ -1,11 +0,0 @@
-[package]
-name = "pourover_macro"
-version.workspace = true
-edition.workspace = true
-publish.workspace = true
-
-[lib]
-proc-macro = true
-
-[dependencies]
-pourover_macro_core.workspace = true
diff --git a/nearby/util/pourover_macro/src/lib.rs b/nearby/util/pourover_macro/src/lib.rs
deleted file mode 100644
index 9f62354..0000000
--- a/nearby/util/pourover_macro/src/lib.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-extern crate proc_macro;
-
-use pourover_macro_core as core;
-use proc_macro::TokenStream;
-
-/// Export a function as a JNI native method. This will attach a `#[export_name = "..."]` attribute that
-/// is formatted with the given parameters.
-///
-/// # Parameters
-/// - `package` (lit str): the Java package for the class being implemented
-/// - `class` (lit str): the Java class being implemented. Use `Foo.Inner` syntax for inner
-/// classes.
-/// - `panic_returns` (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.
-///
-/// 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
-/// arguments and return values are passed by pointer or value and not by Rust reference.
-///
-/// # Example
-/// ```
-/// # use pourover_macro::jni_method;
-/// # #[allow(non_camel_case_types)] type jint = i32; // avoid jni dep for test
-/// # type JNIEnv<'local> = *mut ();
-/// # type JObject<'local> = *mut ();
-///
-/// #[jni_method(package = "my.package", class = "Foo", panic_returns = -1)]
-/// extern "system" fn getFoo<'local>(
-///     mut env: JNIEnv<'local>,
-///     this: JObject<'local>,
-/// ) -> jint {
-///     // ...
-///     0
-/// }
-/// ```
-///
-/// This function will be exported with `#[export_name = "Java_my_package_Foo_getFoo"]`.
-#[proc_macro_attribute]
-pub fn jni_method(meta: TokenStream, item: TokenStream) -> TokenStream {
-    core::jni_method(meta.into(), item.into()).into()
-}
diff --git a/nearby/util/pourover_macro_core/Cargo.toml b/nearby/util/pourover_macro_core/Cargo.toml
deleted file mode 100644
index 04882d1..0000000
--- a/nearby/util/pourover_macro_core/Cargo.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-[package]
-name = "pourover_macro_core"
-version.workspace = true
-edition.workspace = true
-publish.workspace = true
-
-[dependencies]
-proc-macro2.workspace = true
-syn.workspace = true
-quote.workspace = true
diff --git a/remoteauth/Cargo.lock b/remoteauth/Cargo.lock
index 55841b9..1123c44 100644
--- a/remoteauth/Cargo.lock
+++ b/remoteauth/Cargo.lock
@@ -4,18 +4,33 @@
 
 [[package]]
 name = "aho-corasick"
-version = "1.0.5"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
-name = "anstream"
-version = "0.5.0"
+name = "android-tzdata"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -46,17 +61,17 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
 dependencies = [
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "2.1.0"
+version = "3.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
 dependencies = [
  "anstyle",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -66,23 +81,45 @@
 checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
 
 [[package]]
+name = "autocfg"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+
+[[package]]
 name = "bitflags"
 version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
 
 [[package]]
+name = "bstr"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
 name = "build-scripts"
 version = "0.1.0"
 dependencies = [
  "anyhow",
  "clap",
- "cmd-runner",
+ "cmd_runner",
  "env_logger",
  "log",
 ]
 
 [[package]]
+name = "bumpalo"
+version = "3.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
+
+[[package]]
 name = "cc"
 version = "1.0.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -92,21 +129,40 @@
 ]
 
 [[package]]
-name = "clap"
-version = "4.4.1"
+name = "cfg-if"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c8d502cbaec4595d2e7d5f61e318f05417bd2b66fdc3809498f0d3fdf0bea27"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
 dependencies = [
  "clap_builder",
  "clap_derive",
- "once_cell",
 ]
 
 [[package]]
 name = "clap_builder"
-version = "4.4.1"
+version = "4.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5891c7bc0edb3e1c2204fc5e94009affabeb1821c9e5fdc3959536c5c0bb984d"
+checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
 dependencies = [
  "anstream",
  "anstyle",
@@ -116,9 +172,9 @@
 
 [[package]]
 name = "clap_derive"
-version = "4.4.0"
+version = "4.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9fd1a5729c4548118d7d70ff234a44868d00489a4b6597b0b020918a0e91a1a"
+checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -128,17 +184,23 @@
 
 [[package]]
 name = "clap_lex"
-version = "0.5.1"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
+checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
 
 [[package]]
-name = "cmd-runner"
+name = "cmd_runner"
 version = "0.1.0"
 dependencies = [
  "anyhow",
+ "chrono",
+ "clap",
+ "file-header",
+ "globset",
+ "log",
  "owo-colors",
  "shell-escape",
+ "xshell",
 ]
 
 [[package]]
@@ -148,6 +210,68 @@
 checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
 
 [[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "crossbeam"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-epoch",
+ "crossbeam-queue",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+
+[[package]]
 name = "ctap_protocol"
 version = "0.1.0"
 dependencies = [
@@ -175,7 +299,7 @@
 dependencies = [
  "errno-dragonfly",
  "libc",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -189,10 +313,36 @@
 ]
 
 [[package]]
-name = "heck"
-version = "0.4.1"
+name = "file-header"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+checksum = "b5568149106e77ae33bc3a2c3ef3839cbe63ffa4a8dd4a81612a6f9dfdbc2e9f"
+dependencies = [
+ "crossbeam",
+ "lazy_static",
+ "license",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "globset"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "log",
+ "regex-automata 0.4.6",
+ "regex-syntax 0.8.3",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
 [[package]]
 name = "hermit-abi"
@@ -207,6 +357,29 @@
 checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
 name = "is-terminal"
 version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -214,16 +387,48 @@
 dependencies = [
  "hermit-abi",
  "rustix",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
 name = "libc"
 version = "0.2.147"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
 
 [[package]]
+name = "license"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "778718185117620a06e95d2b1e57d50166b1d6bfad93c8abfc1b3344c863ad8c"
+dependencies = [
+ "reword",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
 name = "linux-raw-sys"
 version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -231,21 +436,30 @@
 
 [[package]]
 name = "log"
-version = "0.4.20"
+version = "0.4.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
 
 [[package]]
 name = "memchr"
-version = "2.6.1"
+version = "2.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f478948fd84d9f8e86967bf432640e46adfb5a4bd4f14ef7e864ab38220534ae"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
 
 [[package]]
 name = "once_cell"
-version = "1.18.0"
+version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
 [[package]]
 name = "owo-colors"
@@ -262,18 +476,18 @@
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.66"
+version = "1.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.32"
+version = "1.0.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
 dependencies = [
  "proc-macro2",
 ]
@@ -286,8 +500,8 @@
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-automata",
- "regex-syntax",
+ "regex-automata 0.3.7",
+ "regex-syntax 0.7.5",
 ]
 
 [[package]]
@@ -298,7 +512,18 @@
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-syntax",
+ "regex-syntax 0.7.5",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.3",
 ]
 
 [[package]]
@@ -308,6 +533,12 @@
 checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
 
 [[package]]
+name = "regex-syntax"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+
+[[package]]
 name = "remote_auth_protocol"
 version = "0.1.0"
 dependencies = [
@@ -315,6 +546,15 @@
 ]
 
 [[package]]
+name = "reword"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe272098dce9ed76b479995953f748d1851261390b08f8a0ff619c885a1f0765"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
 name = "rustix"
 version = "0.38.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -324,7 +564,53 @@
  "errno",
  "libc",
  "linux-raw-sys",
- "windows-sys",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.115"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
 ]
 
 [[package]]
@@ -335,15 +621,15 @@
 
 [[package]]
 name = "strsim"
-version = "0.10.0"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "syn"
-version = "2.0.28"
+version = "2.0.58"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
+checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -360,18 +646,108 @@
 ]
 
 [[package]]
+name = "thiserror"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "unicode-ident"
 version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
 
 [[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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 
 [[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
 name = "winapi"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -403,12 +779,30 @@
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.4",
+]
+
+[[package]]
 name = "windows-sys"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets",
+ "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.4",
 ]
 
 [[package]]
@@ -417,13 +811,28 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "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.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.4",
+ "windows_aarch64_msvc 0.52.4",
+ "windows_i686_gnu 0.52.4",
+ "windows_i686_msvc 0.52.4",
+ "windows_x86_64_gnu 0.52.4",
+ "windows_x86_64_gnullvm 0.52.4",
+ "windows_x86_64_msvc 0.52.4",
 ]
 
 [[package]]
@@ -433,37 +842,94 @@
 checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
 [[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
+
+[[package]]
 name = "windows_aarch64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
 [[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
+
+[[package]]
 name = "windows_i686_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
 [[package]]
+name = "windows_i686_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
+
+[[package]]
 name = "windows_i686_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
 [[package]]
+name = "windows_i686_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
+
+[[package]]
 name = "windows_x86_64_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
 [[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
+
+[[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
 [[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
+
+[[package]]
 name = "windows_x86_64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
+
+[[package]]
+name = "xshell"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437"
+dependencies = [
+ "xshell-macros",
+]
+
+[[package]]
+name = "xshell-macros"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852"
diff --git a/remoteauth/Cargo.toml b/remoteauth/Cargo.toml
index 20b2ec3..d86adc0 100644
--- a/remoteauth/Cargo.toml
+++ b/remoteauth/Cargo.toml
@@ -22,6 +22,6 @@
 [dependencies]
 anyhow.workspace = true
 clap = { version = "4.0.25", features = ["derive"] }
-cmd-runner = { path = "../cmd-runner"  }
+cmd_runner = { path = "../common/cmd_runner"  }
 env_logger = "0.10.0"
 log = "0.4.17"
\ No newline at end of file