Snap for 10453563 from 9c513b301c0cc94c5a29375fccbe0b727c088e0b to mainline-adservices-release

Change-Id: I0b8107ae34e69d173c27c444b80ec328a165f5ae
diff --git a/.gitignore b/.gitignore
index 7aeb491..a9837dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
 .clangd
+.cache
 compile_commands.json
 out
+build_overrides/pigweed_environment.gni
diff --git a/Android.bp b/Android.bp
index 37745e4..a0043a0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -9,30 +9,45 @@
     license_text: ["LICENSE"],
 }
 
-cc_library_headers {
-    name: "libopen_dice_headers",
+cc_defaults {
+    name: "libopen_dice.cc_defaults",
+    defaults_visibility: ["//visibility:private"],
     host_supported: true,
     vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.compos",
+        "com.android.virt",
+    ],
+}
+
+cc_library_headers {
+    name: "libopen_dice_headers",
+    defaults: ["libopen_dice.cc_defaults"],
     export_include_dirs: ["include"],
 }
 
 cc_library_headers {
     name: "libopen_dice_boringssl_ed25519_headers",
-    host_supported: true,
-    vendor_available: true,
+    defaults: ["libopen_dice.cc_defaults"],
     export_include_dirs: ["include/dice/config/boringssl_ed25519"],
 }
 
-cc_library_static {
-    name: "libopen_dice_cbor",
-    host_supported: true,
-    vendor_available: true,
+cc_library_headers {
+    name: "libopen_dice_boringssl_ecdsa_p384_headers",
+    defaults: ["libopen_dice.cc_defaults"],
+    export_include_dirs: ["include/dice/config/boringssl_ecdsa_p384"],
+}
+
+cc_defaults {
+    name: "libopen_dice_cbor.cc_defaults",
+    defaults: ["libopen_dice.cc_defaults"],
     srcs: [
         "src/boringssl_hash_kdf_ops.c",
         "src/boringssl_ed25519_ops.c",
         "src/cbor_cert_op.c",
+        "src/cbor_ed25519_cert_op.c",
         "src/cbor_writer.c",
-        "src/clear_memory.c",
         "src/dice.c",
         "src/utils.c",
     ],
@@ -44,19 +59,88 @@
         "libopen_dice_boringssl_ed25519_headers",
         "libopen_dice_headers",
     ],
+}
+
+cc_library {
+    name: "libopen_dice_cbor",
+    defaults: ["libopen_dice_cbor.cc_defaults"],
+    srcs: [
+        "src/clear_memory.c",
+    ],
     shared_libs: ["libcrypto"],
 }
 
 cc_library_static {
-    name: "libopen_dice_bcc",
-    host_supported: true,
-    vendor_available: true,
+    name: "libopen_dice_cbor_p384",
+    defaults: ["libopen_dice.cc_defaults"],
+    srcs: [
+        "src/boringssl_hash_kdf_ops.c",
+        "src/boringssl_ecdsa_utils.c",
+        "src/boringssl_p384_ops.c",
+        "src/cbor_cert_op.c",
+        "src/cbor_p384_cert_op.c",
+        "src/cbor_writer.c",
+        "src/clear_memory.c",
+        "src/dice.c",
+        "src/utils.c",
+    ],
+    header_libs: [
+        "libopen_dice_boringssl_ecdsa_p384_headers",
+        "libopen_dice_headers",
+    ],
+    export_header_lib_headers: [
+        "libopen_dice_boringssl_ecdsa_p384_headers",
+        "libopen_dice_headers",
+    ],
+    shared_libs: ["libcrypto"],
+}
+
+// Version of the library missing DiceClearMemory, for baremetal client code.
+cc_library_static {
+    name: "libopen_dice_cbor_baremetal",
+    defaults: ["libopen_dice_cbor.cc_defaults"],
+    allow_undefined_symbols: true,
+    static_libs: ["libcrypto_baremetal"],
+}
+
+cc_defaults {
+    name: "libopen_dice_bcc.cc_defaults",
+    defaults: ["libopen_dice.cc_defaults"],
     srcs: [
         "src/cbor_reader.c",
         "src/android/bcc.c",
     ],
-    static_libs: ["libopen_dice_cbor"],
-    export_static_lib_headers: ["libopen_dice_cbor"],
+}
+
+cc_library {
+    name: "libopen_dice_bcc",
+    defaults: ["libopen_dice_bcc.cc_defaults"],
+    export_shared_lib_headers: ["libopen_dice_cbor"],
+    shared_libs: [
+        "libcrypto",
+        "libopen_dice_cbor",
+    ],
+}
+
+cc_library_static {
+    name: "libopen_dice_bcc_baremetal",
+    defaults: ["libopen_dice_bcc.cc_defaults"],
+    export_static_lib_headers: ["libopen_dice_cbor_baremetal"],
+    static_libs: [
+        "libcrypto_baremetal",
+        "libopen_dice_cbor_baremetal",
+    ],
+}
+
+cc_library_static {
+    name: "libopen_dice_bcc_p384",
+    defaults: ["libopen_dice.cc_defaults"],
+    srcs: [
+        "src/cbor_reader.c",
+        "src/android/bcc.c",
+    ],
+    static_libs: ["libopen_dice_cbor_p384"],
+    export_static_lib_headers: ["libopen_dice_cbor_p384"],
     shared_libs: ["libcrypto"],
 }
 
@@ -78,6 +162,23 @@
 }
 
 cc_test {
+    name: "libopen_dice_p384_test",
+    host_supported: true,
+    cflags: ["-DDICE_USE_GTEST"],
+    srcs: [
+        "src/clear_memory.c",
+        "src/dice_test.cc",
+        "src/dice.c",
+        "src/utils.c",
+    ],
+    header_libs: [
+        "libopen_dice_boringssl_ecdsa_p384_headers",
+        "libopen_dice_headers",
+    ],
+    shared_libs: ["libcrypto"],
+}
+
+cc_test {
     name: "libopen_dice_cbor_writer_test",
     host_supported: true,
     cflags: ["-DDICE_USE_GTEST"],
@@ -86,6 +187,14 @@
 }
 
 cc_test {
+    name: "libopen_dice_cbor_writer_p384_test",
+    host_supported: true,
+    cflags: ["-DDICE_USE_GTEST"],
+    srcs: ["src/cbor_writer_test.cc"],
+    static_libs: ["libopen_dice_cbor_p384"],
+}
+
+cc_test {
     name: "libopen_dice_cbor_reader_test",
     host_supported: true,
     cflags: ["-DDICE_USE_GTEST"],
@@ -94,6 +203,14 @@
 }
 
 cc_test {
+    name: "libopen_dice_cbor_reader_p384_test",
+    host_supported: true,
+    cflags: ["-DDICE_USE_GTEST"],
+    srcs: ["src/cbor_reader_test.cc"],
+    static_libs: ["libopen_dice_bcc_p384"],
+}
+
+cc_test {
     name: "libopen_dice_bcc_test",
     host_supported: true,
     cflags: ["-DDICE_USE_GTEST"],
@@ -115,6 +232,13 @@
 }
 
 cc_fuzz {
+    name: "libopen_dice_cbor_writer_p384_fuzzer",
+    host_supported: true,
+    srcs: ["src/cbor_writer_fuzzer.cc"],
+    static_libs: ["libopen_dice_cbor_p384"],
+}
+
+cc_fuzz {
     name: "libopen_dice_cbor_reader_fuzzer",
     host_supported: true,
     srcs: ["src/cbor_reader_fuzzer.cc"],
@@ -122,6 +246,13 @@
 }
 
 cc_fuzz {
+    name: "libopen_dice_cbor_reader_p384_fuzzer",
+    host_supported: true,
+    srcs: ["src/cbor_reader_fuzzer.cc"],
+    static_libs: ["libopen_dice_bcc_p384"],
+}
+
+cc_fuzz {
     name: "libopen_dice_cbor_fuzzer",
     host_supported: true,
     srcs: ["src/fuzzer.cc"],
@@ -130,6 +261,14 @@
 }
 
 cc_fuzz {
+    name: "libopen_dice_cbor_p384_fuzzer",
+    host_supported: true,
+    srcs: ["src/fuzzer.cc"],
+    static_libs: ["libopen_dice_cbor_p384"],
+    shared_libs: ["libcrypto"],
+}
+
+cc_fuzz {
     name: "libopen_dice_bcc_fuzzer",
     host_supported: true,
     srcs: ["src/android/bcc_fuzzer.cc"],
@@ -140,115 +279,13 @@
     shared_libs: ["libcrypto"],
 }
 
-rust_bindgen {
-    name: "libopen_dice_cbor_bindgen",
-    wrapper_src: "rust/dice.h",
-    crate_name: "open_dice_cbor_bindgen",
-    source_stem: "bindings",
-    header_libs: [
-        "libopen_dice_boringssl_ed25519_headers",
-        "libopen_dice_headers",
-    ],
-
-    // Generate bindings only for the symbols that are actually exported (see exported.map.txt).
-    // This makes the generated bindings much more concise and improves compilation
-    // time.
-    bindgen_flags: [
-        "--size_t-is-usize",
-
-        "--allowlist-function=DiceDeriveCdiPrivateKeySeed",
-        "--allowlist-function=DiceDeriveCdiCertificateId",
-        "--allowlist-function=DiceMainFlow",
-        "--allowlist-function=DiceHash",
-        "--allowlist-function=DiceKdf",
-        "--allowlist-function=DiceKeypairFromSeed",
-        "--allowlist-function=DiceSign",
-        "--allowlist-function=DiceVerify",
-        "--allowlist-function=DiceGenerateCertificate",
-
-        // We also need some constants in addition to the functions.
-        "--allowlist-var=DICE_CDI_SIZE",
-        "--allowlist-var=DICE_HASH_SIZE",
-        "--allowlist-var=DICE_HIDDEN_SIZE",
-        "--allowlist-var=DICE_INLINE_CONFIG_SIZE",
-        "--allowlist-var=DICE_PRIVATE_KEY_SEED_SIZE",
-        "--allowlist-var=DICE_ID_SIZE",
-        "--allowlist-var=DICE_PUBLIC_KEY_SIZE",
-        "--allowlist-var=DICE_PRIVATE_KEY_SIZE",
-        "--allowlist-var=DICE_SIGNATURE_SIZE",
-    ],
-
-    // This is mainly to run layout tests for generated bindings on the host.
+cc_fuzz {
+    name: "libopen_dice_bcc_p384_fuzzer",
     host_supported: true,
-    vendor_available: true,
-}
-
-rust_bindgen {
-    name: "libopen_dice_bcc_bindgen",
-    wrapper_src: "rust/android/bcc.h",
-    crate_name: "open_dice_bcc_bindgen",
-    source_stem: "bindings",
-    header_libs: [
-        "libopen_dice_headers",
+    srcs: ["src/android/bcc_fuzzer.cc"],
+    static_libs: [
+        "libopen_dice_bcc_p384",
+        "libopen_dice_cbor_p384",
     ],
-
-    // Generate bindings only for the symbols that are actually exported (see exported.map.txt).
-    // This makes the generated bindings much more concise and improves compilation
-    // time.
-    bindgen_flags: [
-        "--size_t-is-usize",
-
-        "--allowlist-function=BccFormatConfigDescriptor",
-        "--allowlist-function=BccMainFlow",
-        "--allowlist-function=BccHandoverMainFlow",
-
-        // We also need some constants in addition to the functions.
-        "--allowlist-var=BCC_INPUT_COMPONENT_NAME",
-        "--allowlist-var=BCC_INPUT_COMPONENT_VERSION",
-        "--allowlist-var=BCC_INPUT_RESETTABLE",
-
-        // Prevent DiceInputValues from being generated a second time and
-        // import it instead from open_dice_cbor_bindgen.
-        "--blocklist-type=DiceInputValues_",
-        "--blocklist-type=DiceInputValues",
-        "--raw-line",
-        "pub use open_dice_cbor_bindgen::DiceInputValues;",
-    ],
-
-    rustlibs: [
-        "libopen_dice_cbor_bindgen",
-    ],
-
-    // This is mainly to run layout tests for generated bindings on the host.
-    host_supported: true,
-    vendor_available: true,
-}
-
-rust_test {
-    name: "libopen_dice_cbor_bindgen_test",
-    srcs: [
-        ":libopen_dice_cbor_bindgen",
-    ],
-    crate_name: "open_dice_cbor_bindgen_test",
-    test_suites: ["general-tests"],
-    auto_gen_config: true,
-    clippy_lints: "none",
-    lints: "none",
-}
-
-rust_test {
-    name: "libopen_dice_bcc_bindgen_test",
-    srcs: [
-        ":libopen_dice_bcc_bindgen",
-    ],
-    crate_name: "open_dice_bcc_bindgen_test",
-
-    rustlibs: [
-        "libopen_dice_cbor_bindgen",
-    ],
-
-    test_suites: ["general-tests"],
-    auto_gen_config: true,
-    clippy_lints: "none",
-    lints: "none",
+    shared_libs: ["libcrypto"],
 }
diff --git a/BUILD.gn b/BUILD.gn
index f2c68a9..4787252 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -26,16 +26,12 @@
 }
 
 pw_source_set("cbor_writer") {
-  public = [
-    "include/dice/cbor_writer.h",
-  ]
+  public = [ "include/dice/cbor_writer.h" ]
   sources = [ "src/cbor_writer.c" ]
 }
 
 pw_source_set("cbor_reader") {
-  public = [
-    "include/dice/cbor_reader.h",
-  ]
+  public = [ "include/dice/cbor_reader.h" ]
   sources = [ "src/cbor_reader.c" ]
 }
 
@@ -50,7 +46,7 @@
   ]
   sources = [
     "src/clear_memory.c",
-    "src/dice.c"
+    "src/dice.c",
   ]
   all_dependent_configs = [ ":standalone_ops_config" ]
 }
@@ -59,27 +55,47 @@
   include_dirs = [ "//include/dice/config/boringssl_ed25519" ]
 }
 
-pw_static_library("dice_with_boringssl_ops") {
+config("boringssl_ecdsa_p384_ops_config") {
+  include_dirs = [ "//include/dice/config/boringssl_ecdsa_p384" ]
+}
+
+pw_static_library("dice_with_boringssl_ed25519_ops") {
   public = [
     "include/dice/dice.h",
     "include/dice/utils.h",
   ]
   sources = [
     "src/boringssl_cert_op.c",
-    "src/boringssl_hash_kdf_ops.c",
     "src/boringssl_ed25519_ops.c",
+    "src/boringssl_hash_kdf_ops.c",
     "src/clear_memory.c",
     "src/dice.c",
     "src/utils.c",
   ]
-  deps = [
-    "//third_party/boringssl:crypto",
-  ]
+  deps = [ "//third_party/boringssl:crypto" ]
   all_dependent_configs = [ ":boringssl_ed25519_ops_config" ]
 }
 
+pw_static_library("dice_with_boringssl_p384_ops") {
+  public = [
+    "include/dice/dice.h",
+    "include/dice/utils.h",
+  ]
+  sources = [
+    "src/boringssl_cert_op.c",
+    "src/boringssl_p384_ops.c",
+    "src/boringssl_hash_kdf_ops.c",
+    "src/clear_memory.c",
+    "src/dice.c",
+    "src/utils.c",
+  ]
+  deps = [ "//third_party/boringssl:crypto" ]
+  all_dependent_configs = [ ":boringssl_ecdsa_p384_ops_config" ]
+}
+
 config("mbedtls_ops_config") {
   include_dirs = [ "//include//dice/config/mbedtls_ecdsa_p256" ]
+  defines = [ "MBEDTLS_ALLOW_PRIVATE_ACCESS" ]
 }
 
 pw_static_library("dice_with_mbedtls_ops") {
@@ -93,21 +109,20 @@
     "src/mbedtls_ops.c",
     "src/utils.c",
   ]
-  deps = [
-    "//third_party/mbedtls:mbedcrypto",
-  ]
+  deps = [ "//third_party/mbedtls:mbedcrypto" ]
   all_dependent_configs = [ ":mbedtls_ops_config" ]
 }
 
-pw_static_library("dice_with_cbor_cert") {
+pw_static_library("dice_with_cbor_ed25519_cert") {
   public = [
     "include/dice/dice.h",
     "include/dice/utils.h",
   ]
   sources = [
-    "src/boringssl_hash_kdf_ops.c",
     "src/boringssl_ed25519_ops.c",
+    "src/boringssl_hash_kdf_ops.c",
     "src/cbor_cert_op.c",
+    "src/cbor_ed25519_cert_op.c",
     "src/clear_memory.c",
     "src/dice.c",
     "src/utils.c",
@@ -119,22 +134,58 @@
   all_dependent_configs = [ ":boringssl_ed25519_ops_config" ]
 }
 
-pw_static_library("dice_with_cbor_template_cert") {
+
+pw_static_library("boringssl_ecdsa_utils") {
+  public = [
+    "include/dice/boringssl_ecdsa_utils.h",
+  ]
+  sources = [
+    "src/boringssl_ecdsa_utils.c",
+  ]
+  deps = [
+    "//third_party/boringssl:crypto",
+  ]
+}
+
+pw_static_library("dice_with_cbor_p384_cert") {
   public = [
     "include/dice/dice.h",
     "include/dice/utils.h",
   ]
   sources = [
     "src/boringssl_hash_kdf_ops.c",
+    "src/boringssl_p384_ops.c",
+    "src/cbor_cert_op.c",
+    "src/cbor_p384_cert_op.c",
+    "src/clear_memory.c",
+    "src/dice.c",
+    "src/utils.c",
+    "src/boringssl_ecdsa_utils.c",
+  ]
+  deps = [
+    ":cbor_writer",
+    ":boringssl_ecdsa_utils",
+    "//third_party/boringssl:crypto",
+  ]
+  all_dependent_configs = [
+    ":boringssl_ecdsa_p384_ops_config",
+  ]
+}
+
+pw_static_library("dice_with_cbor_template_ed25519_cert") {
+  public = [
+    "include/dice/dice.h",
+    "include/dice/utils.h",
+  ]
+  sources = [
     "src/boringssl_ed25519_ops.c",
+    "src/boringssl_hash_kdf_ops.c",
     "src/clear_memory.c",
     "src/dice.c",
     "src/template_cbor_cert_op.c",
     "src/utils.c",
   ]
-  deps = [
-    "//third_party/boringssl:crypto",
-  ]
+  deps = [ "//third_party/boringssl:crypto" ]
   all_dependent_configs = [ ":boringssl_ed25519_ops_config" ]
 }
 
@@ -144,16 +195,14 @@
     "include/dice/utils.h",
   ]
   sources = [
-    "src/boringssl_hash_kdf_ops.c",
     "src/boringssl_ed25519_ops.c",
+    "src/boringssl_hash_kdf_ops.c",
     "src/clear_memory.c",
     "src/dice.c",
     "src/template_cert_op.c",
     "src/utils.c",
   ]
-  deps = [
-    "//third_party/boringssl:crypto",
-  ]
+  deps = [ "//third_party/boringssl:crypto" ]
   all_dependent_configs = [ ":boringssl_ed25519_ops_config" ]
 }
 
@@ -163,30 +212,22 @@
 
 pw_test("cbor_writer_test") {
   sources = [ "src/cbor_writer_test.cc" ]
-  deps = [
-    ":cbor_writer",
-  ]
+  deps = [ ":cbor_writer" ]
 }
 
 pw_test("cbor_reader_test") {
   sources = [ "src/cbor_reader_test.cc" ]
-  deps = [
-    ":cbor_reader",
-  ]
+  deps = [ ":cbor_reader" ]
 }
 
 pw_executable("cbor_writer_fuzzer") {
   sources = [ "src/cbor_writer_fuzzer.cc" ]
-  deps = [
-    ":cbor_writer",
-  ]
+  deps = [ ":cbor_writer" ]
 }
 
 pw_executable("cbor_reader_fuzzer") {
   sources = [ "src/cbor_reader_fuzzer.cc" ]
-  deps = [
-    ":cbor_reader",
-  ]
+  deps = [ ":cbor_reader" ]
 }
 
 pw_test("dice_test") {
@@ -197,22 +238,30 @@
   ]
 }
 
-pw_test("boringssl_ops_test") {
+pw_test("boringssl_ed25519_ops_test") {
   sources = [
-    "src/boringssl_ops_test.cc",
+    "src/boringssl_ed25519_ops_test.cc",
     "src/test_utils.cc",
   ]
   deps = [
-    ":dice_with_boringssl_ops",
-    "//third_party/boringssl:crypto",
-    "//third_party/cose-c:cose-c",
+    ":dice_with_boringssl_ed25519_ops",
+    ":boringssl_ecdsa_utils",
     "$dir_pw_string:pw_string",
+    "//third_party/boringssl:crypto",
+    "//third_party/cose-c:cose-c_ed25519",
   ]
 }
 
-pw_executable("boringssl_ops_fuzzer") {
+pw_executable("boringssl_ed25519_ops_fuzzer") {
   deps = [
-    ":dice_with_boringssl_ops",
+    ":dice_with_boringssl_ed25519_ops",
+    ":fuzzer",
+  ]
+}
+
+pw_executable("boringssl_p384_ops_fuzzer") {
+  deps = [
+    ":dice_with_boringssl_p384_ops",
     ":fuzzer",
   ]
 }
@@ -224,9 +273,10 @@
   ]
   deps = [
     ":dice_with_x509_template_cert",
-    "//third_party/boringssl:crypto",
-    "//third_party/cose-c:cose-c",
+    ":boringssl_ecdsa_utils",
     "$dir_pw_string:pw_string",
+    "//third_party/boringssl:crypto",
+    "//third_party/cose-c:cose-c_ed25519",
   ]
 }
 
@@ -237,42 +287,58 @@
   ]
 }
 
-pw_test("cbor_cert_op_test") {
+pw_test("cbor_ed25519_cert_op_test") {
   sources = [
     "src/cbor_cert_op_test.cc",
     "src/test_utils.cc",
   ]
   deps = [
-    ":dice_with_cbor_cert",
+    ":dice_with_cbor_ed25519_cert",
+    ":boringssl_ecdsa_utils",
+    "$dir_pw_string:pw_string",
     "//third_party/boringssl:crypto",
-    "//third_party/cose-c:cose-c",
+    "//third_party/cose-c:cose-c_ed25519",
+  ]
+}
+
+pw_test("cbor_p384_cert_op_test") {
+  sources = [
+    "src/cbor_p384_cert_op_test.cc",
+    "src/test_utils.cc",
+  ]
+  deps = [
+    ":dice_with_cbor_p384_cert",
+    ":boringssl_ecdsa_utils",
+    "//third_party/boringssl:crypto",
+    "//third_party/cose-c:cose-c_p384",
     "$dir_pw_string:pw_string",
   ]
 }
 
-pw_executable("cbor_cert_op_fuzzer") {
+pw_executable("cbor_ed25519_cert_op_fuzzer") {
   deps = [
-    ":dice_with_cbor_cert",
+    ":dice_with_cbor_ed25519_cert",
     ":fuzzer",
   ]
 }
 
-pw_test("template_cbor_cert_op_test") {
+pw_test("template_cbor_ed25519_cert_op_test") {
   sources = [
     "src/template_cbor_cert_op_test.cc",
     "src/test_utils.cc",
   ]
   deps = [
-    ":dice_with_cbor_template_cert",
-    "//third_party/boringssl:crypto",
-    "//third_party/cose-c:cose-c",
+    ":dice_with_cbor_template_ed25519_cert",
+    ":boringssl_ecdsa_utils",
     "$dir_pw_string:pw_string",
+    "//third_party/boringssl:crypto",
+    "//third_party/cose-c:cose-c_ed25519",
   ]
 }
 
-pw_executable("template_cbor_cert_op_fuzzer") {
+pw_executable("template_cbor_ed25519_cert_op_fuzzer") {
   deps = [
-    ":dice_with_cbor_template_cert",
+    ":dice_with_cbor_template_ed25519_cert",
     ":fuzzer",
   ]
 }
@@ -284,9 +350,10 @@
   ]
   deps = [
     ":dice_with_mbedtls_ops",
-    "//third_party/boringssl:crypto",
-    "//third_party/cose-c:cose-c",
+    ":boringssl_ecdsa_utils",
     "$dir_pw_string:pw_string",
+    "//third_party/boringssl:crypto",
+    "//third_party/cose-c:cose-c_ed25519",
   ]
 }
 
@@ -299,13 +366,14 @@
 
 pw_test_group("tests") {
   tests = [
-    ":boringssl_ops_test",
-    ":cbor_cert_op_test",
+    ":boringssl_ed25519_ops_test",
+    ":cbor_ed25519_cert_op_test",
+    ":cbor_p384_cert_op_test",
     ":cbor_reader_test",
     ":cbor_writer_test",
     ":dice_test",
     ":mbedtls_ops_test",
-    ":template_cbor_cert_op_test",
+    ":template_cbor_ed25519_cert_op_test",
     ":template_cert_op_test",
     "//src/android:bcc_test",
   ]
@@ -313,12 +381,13 @@
 
 group("fuzzers") {
   deps = [
-    ":boringssl_ops_fuzzer",
-    ":cbor_cert_op_fuzzer",
+    ":boringssl_ed25519_ops_fuzzer",
+    ":boringssl_p384_ops_fuzzer",
+    ":cbor_ed25519_cert_op_fuzzer",
     ":cbor_reader_fuzzer",
     ":cbor_writer_fuzzer",
     ":mbedtls_ops_fuzzer",
-    ":template_cbor_cert_op_fuzzer",
+    ":template_cbor_ed25519_cert_op_fuzzer",
     ":template_cert_op_fuzzer",
     "//src/android:bcc_fuzzer",
   ]
@@ -340,10 +409,10 @@
   sources = [ "src/dice_main.c" ]
 }
 
-pw_executable("dice_with_boringssl_ops_main") {
+pw_executable("dice_with_boringssl_ed25519_ops_main") {
   deps = [
     ":dice_main",
-    ":dice_with_boringssl_ops",
+    ":dice_with_boringssl_ed25519_ops",
   ]
 }
 
@@ -357,14 +426,14 @@
 pw_executable("dice_with_cbor_cert_main") {
   deps = [
     ":dice_main",
-    ":dice_with_cbor_cert",
+    ":dice_with_cbor_ed25519_cert",
   ]
 }
 
-pw_executable("dice_with_cbor_template_cert_main") {
+pw_executable("dice_with_cbor_template_ed25519_cert_main") {
   deps = [
     ":dice_main",
-    ":dice_with_cbor_template_cert",
+    ":dice_with_cbor_template_ed25519_cert",
   ]
 }
 
@@ -375,7 +444,7 @@
   ]
 }
 
-pw_size_report("executable_size_report") {
+pw_size_diff("executable_size_report") {
   title = "Executable sizes (includes thirdparty deps)"
   base = ":empty_main"
   binaries = [
@@ -384,8 +453,8 @@
       label = "DiceMainFlow only (No Ops)"
     },
     {
-      target = ":dice_with_boringssl_ops_main"
-      label = "Boringssl Ops"
+      target = ":dice_with_boringssl_ed25519_ops_main"
+      label = "Boringssl Ed25519 Ops"
       base = ":dice_standalone_main"
     },
     {
@@ -396,22 +465,22 @@
     {
       target = ":dice_with_cbor_cert_main"
       label = "Boringssl with CBOR Cert"
-      base = ":dice_with_boringssl_ops_main"
+      base = ":dice_with_boringssl_ed25519_ops_main"
     },
     {
-      target = ":dice_with_cbor_template_cert_main"
-      label = "Boringssl with CBOR Template Cert"
-      base = ":dice_with_boringssl_ops_main"
+      target = ":dice_with_cbor_template_ed25519_cert_main"
+      label = "Boringssl with CBOR Template Ed25519 Cert"
+      base = ":dice_with_boringssl_ed25519_ops_main"
     },
     {
       target = ":dice_with_x509_template_cert_main"
       label = "Boringssl with X.509 Template Cert"
-      base = ":dice_with_boringssl_ops_main"
+      base = ":dice_with_boringssl_ed25519_ops_main"
     },
   ]
 }
 
-pw_size_report("library_size_report") {
+pw_size_diff("library_size_report") {
   title = "Library sizes (excludes thirdparty deps)"
   base = ":empty_lib"
   binaries = [
@@ -420,7 +489,7 @@
       label = "DICE Standalone (No Ops)"
     },
     {
-      target = ":dice_with_boringssl_ops"
+      target = ":dice_with_boringssl_ed25519_ops"
       label = "Boringssl Ops"
       base = ":dice_standalone"
     },
@@ -430,12 +499,17 @@
       base = ":dice_standalone"
     },
     {
-      target = ":dice_with_cbor_cert"
+      target = ":dice_with_cbor_ed25519_cert"
       label = "CBOR Cert"
       base = ":dice_standalone"
     },
     {
-      target = ":dice_with_cbor_template_cert"
+      target = ":dice_with_cbor_p384_cert"
+      label = "CBOR P384 Cert"
+      base = ":dice_standalone"
+    },
+    {
+      target = ":dice_with_cbor_template_ed25519_cert"
       label = "CBOR Template Cert"
       base = ":dice_standalone"
     },
@@ -450,9 +524,10 @@
 group("optimized_libs") {
   deps = [
     ":dice_standalone",
-    ":dice_with_boringssl_ops",
-    ":dice_with_cbor_cert",
-    ":dice_with_cbor_template_cert",
+    ":dice_with_boringssl_ed25519_ops",
+    ":dice_with_cbor_ed25519_cert",
+    ":dice_with_cbor_p384_cert",
+    ":dice_with_cbor_template_ed25519_cert",
     ":dice_with_mbedtls_ops",
     ":dice_with_x509_template_cert",
     ":executable_size_report",
diff --git a/TEST_MAPPING b/TEST_MAPPING
deleted file mode 100644
index 507c7f5..0000000
--- a/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "libopen_dice_cbor_bindgen_test"
-    },
-    {
-      "name": "libopen_dice_bcc_bindgen_test"
-    }
-  ]
-}
diff --git a/bootstrap.sh b/bootstrap.sh
index 3404d07..181daec 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -25,11 +25,14 @@
 # manually.
 git submodule update --init
 
-# Apply local submodule patches.
-git -C third_party/mbedtls/src am ../0001-Mark-basic-constraints-critical-as-appropriate.patch
-
 # Bootstrap the pigweed environment.
 . third_party/pigweed/src/bootstrap.sh
 
+# Copy the pigweed environment config with a path fixup.
+sed s/environment/third_party\\\/pigweed\\\/src\\\/environment/g \
+  < third_party/pigweed/src/build_overrides/pigweed_environment.gni \
+  > build_overrides/pigweed_environment.gni
+gn format build_overrides/pigweed_environment.gni
+
 # Setup the build.
 gn gen --export-compile-commands out
diff --git a/docs/specification.md b/docs/specification.md
index 5c746f2..60206b2 100644
--- a/docs/specification.md
+++ b/docs/specification.md
@@ -1,6 +1,6 @@
 # Open Profile for DICE
 
-v2.3
+v2.4
 
 [TOC]
 
@@ -9,17 +9,17 @@
 The Trusted Computing Group (TCG) specifies
 [Hardware Requirements for a Device Identifier Composition Engine](https://trustedcomputinggroup.org/wp-content/uploads/Hardware-Requirements-for-Device-Identifier-Composition-Engine-r78_For-Publication.pdf)
 (DICE) which provides the context for this document. We'll call this TCG
-document the _TCG DICE specification_. Concepts like a Unique Device Secret
+document the *TCG DICE specification*. Concepts like a Unique Device Secret
 (UDS) and a Compound Device Identifier (CDI) are used as defined in the TCG DICE
 specification.
 
 #### A Note on Nomenclature
 
-This document uses the term _hardware_ to refer to anything that is immutable by
-design after manufacturing. Code in mask ROM, for example, is _hardware_. The
-terms _firmware_, _software_ and _program_ are all interchangeable; they all
-refer to mutable code. Often we say _firmware_ for code that runs early in boot,
-and _program_ for a particular unit of code, but it's really all _software_.
+This document uses the term *hardware* to refer to anything that is immutable by
+design after manufacturing. Code in mask ROM, for example, is *hardware*. The
+terms *firmware*, *software* and *program* are all interchangeable; they all
+refer to mutable code. Often we say *firmware* for code that runs early in boot,
+and *program* for a particular unit of code, but it's really all *software*.
 
 #### DICE Primer
 
@@ -87,17 +87,18 @@
 software, and uses the UDS in the derivation of both the Attestation CDI and
 Sealing CDI. Subsequent DICE transitions would use the current CDI values in
 place of the UDS to compute the subsequent CDI values. See
-[Layering Details](#layering-details).
+[Layering Details](#layering-details). See the [Cryptography](#cryptography)
+section for details on the primitives referenced in the diagram.
 
 ![Architecture Diagram](../images/architecture.png)
 
 ## Use Cases
 
 This design is motivated by two use cases: **attestation** and **sealing**.
-_Attestation_ allows a computing device or program to provide verifiable
+*Attestation* allows a computing device or program to provide verifiable
 evidence of its identity and operating state, including hardware identity,
 software image, security-relevant configuration, operating environment, etc.
-_Sealing_ allows a computing device or program to encrypt data in such a way
+*Sealing* allows a computing device or program to encrypt data in such a way
 that it can only be decrypted by the same device or program operating in the
 same state as at the time of encryption.
 
@@ -131,7 +132,7 @@
     authority selection, device mode, boot location, chip status information,
     instance identifiers, etc. This value may or may not be a hash of the actual
     configuration data. When it is a hash, the original data must also be
-    included in certificates. It's ok for this input to be _not stable_, it may
+    included in certificates. It's ok for this input to be *not stable*, it may
     change from one boot to the next.
 3.  **Authority Data (64 bytes)** - This input is computed by hashing a
     representation of the verified boot trusted authority. For example, this may
@@ -153,7 +154,7 @@
     device makes at runtime. In the sealing use case, this enables data to be
     sealed separately under each mode. See
     [Mode Value Details](#mode-value-details).
-5.  **Hidden Inputs (64 bytes)** - This optional input value is _hidden_ in the
+5.  **Hidden Inputs (64 bytes)** - This optional input value is *hidden* in the
     sense that it does not appear in any certificate. It is used for both
     attestation and sealing CDI derivation so it is expected to be stable; it
     should not change under normal operation except when that change is an
@@ -178,12 +179,12 @@
 
 1.  **Attestation CDI** - This CDI is derived from the combination of all input
     values and will change across software updates or configuration changes.
-    This CDI is appropriate for attestation and is _mandatory_ for
+    This CDI is appropriate for attestation and is *mandatory* for
     implementations of this profile.
 2.  **Sealing CDI** - This CDI is derived from only the authority data, mode
     decision, and hidden inputs because these are stable. It will reflect this
     stability and will remain the same across software updates and some
-    configuration changes. This CDI is appropriate for sealing and is _optional_
+    configuration changes. This CDI is appropriate for sealing and is *optional*
     for implementations of this profile.
 
 ### CDI Certificates
@@ -340,13 +341,19 @@
 algorithm. The KDF inputs map exactly to HKDF parameters, by design. This is the
 recommended default.
 
+Per the HKDF
+[specification](https://datatracker.ietf.org/doc/html/rfc5869#section-3.3) the
+extract step can be skipped in some cases, and since all KDFs used in this
+specification use cryptographically strong ikm values, doing so is acceptable
+here.
+
 ##### DRBG
 
 A
 [DRBG](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf)
 can be used to implement the KDF operation. Depending on the DRBG implementation
-this may require UDS and CDI values larger than 256 bits to provide both _nonce_
-and _entropy_ inputs when instantiating the DRBG. The DRBG should be
+this may require UDS and CDI values larger than 256 bits to provide both *nonce*
+and *entropy* inputs when instantiating the DRBG. The DRBG should be
 instantiated with a security strength of 256 bits. The sequence of DRBG
 functions {instantiate, generate, uninstantiate}, are used as a KDF operation.
 The mapping of inputs is as shown in the following table.
@@ -363,6 +370,12 @@
 [OpenTitan Key Manager](https://docs.opentitan.org/hw/ip/keymgr/doc/index.html)
 can be used as a KDF. See the OpenTitan documentation for details.
 
+##### SP800-108
+
+The KDFs described in NIST's
+[SP800-108](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf)
+can be used.
+
 #### Digital Signatures
 
 ##### Ed25519
@@ -370,10 +383,13 @@
 [Ed25519](https://en.wikipedia.org/wiki/EdDSA#Ed25519) is the recommended
 default.
 
+When deriving Ed25519 key pairs, using the output of ASYM_KDF directly as the
+private key is acceptable.
+
 ##### ECDSA
 
 [ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm)
-can be used instead of Ed25519. When signing the CDI certificate, the random _k_
+can be used instead of Ed25519. When signing the CDI certificate, the random *k*
 required by ECDSA may be generated deterministically per
 [RFC6979](https://tools.ietf.org/html/rfc6979). One weakness of Ed25519 is that
 implementations may be susceptible to error injection
@@ -387,6 +403,13 @@
 *   P-256
 *   P-384
 
+When deriving ECDSA key pairs the output of ASYM_KDF cannot be used directly.
+Following the process described in
+[RFC 6979](https://datatracker.ietf.org/doc/html/rfc6979#section-3.3) is
+recommended. In this process the seed, in this case the output of ASYM_KDF, is
+used to seed an HMAC_DRBG instance and then the private key is generated from
+the DRBG. See the RFC for details.
+
 ## Layering Details
 
 This DICE profile is designed to be layered. That is, software that receives CDI
@@ -477,7 +500,7 @@
 UDS is derived on-chip from internal and external entropy, at least 256 bits
 each. Internal entropy may be generated using a
 [PUF](https://en.wikipedia.org/wiki/Physical_unclonable_function), or generated
-once using an internal hardware RNG and stored, for example, in OTP memory.
+once using an internal hardware TRNG and stored, for example, in OTP memory.
 External entropy is injected once during manufacturing and stored, for example,
 in OTP memory. The UDS is derived at runtime on every boot from the combined
 entropy. The UDS derivation (i.e. conditioning) from internal and external
@@ -534,7 +557,8 @@
     inject to multiple devices
 2.  [Manufacturing] Run the DICE flow and read the UDS\_Public key
 3.  [Manufacturing] Retain H(UDS\_Public) in a secure database
-4.  [On-Demand] Send UDS\_Public to the CA (no proof-of-possession necessary)
+4.  [On-Demand] Send UDS\_Public from the device to the CA (no
+    proof-of-possession necessary)
 5.  [CA] Check that H(UDS\_Public) is approved by the manufacturer
 6.  [CA] Issue a certificate for UDS\_Public
 
@@ -574,14 +598,14 @@
 infrastructure may place additional constraints on certificate type, but this
 profile does not.
 
-Regardless of type, UDS and CDI certificates are always semantically _CA
-certificates_ to enable use cases for certifying subsequent DICE
+Regardless of type, UDS and CDI certificates are always semantically *CA
+certificates* to enable use cases for certifying subsequent DICE
 [layers](#layering-details) or certifying attestation keys of some kind; the
 UDS\_Private and CDI\_Private keys are not intended to be used for any purpose
 other than signing certificates. In particular, this means CDI\_Private should
 not participate directly in attestation protocols, but should rather certify an
 attestation key. If a target software component does not launch additional
-software, the _pathLenConstraint_ field can be set to zero so certification of a
+software, the *pathLenConstraint* field can be set to zero so certification of a
 subsequent CDI\_Public is not possible.
 
 When UDS and CDI certificates are standard X.509 certificates, they follow the
@@ -593,10 +617,11 @@
 
 ### X.509 UDS Certificates
 
-The following table describes all standard fields of a UDS certificate's
-tbsCertificate field that this profile requires. Fields omitted are
-implementation dependent, but must not break the ability to chain to a CDI
-Certificate.
+X.509 UDS certificates generally follow
+[RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280). The following table
+describes all standard fields of a UDS certificate's tbsCertificate field that
+this profile requires. Fields omitted are implementation dependent, but must not
+break the ability to chain to a CDI Certificate.
 
 Field                | Description
 -------------------- | -----------
@@ -615,20 +640,22 @@
 
 ### X.509 CDI Certificates
 
-All standard fields of a CDI certificate and the tbsCertificate field are
-described in the following table. Notably, this certificate can be generated
-deterministically given a CDI\_Public key and the DICE input value details.
+X.509 CDI certificates generally follow
+[RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280). All standard fields
+of a CDI certificate and the tbsCertificate field are described in the following
+table. Notably, this certificate can be generated deterministically given a
+CDI\_Public key and the DICE input value details.
 
 Field                | Description
 -------------------- | -----------
-signatureAlgorithm   | id-Ed25519 per [RFC 8410](https://tools.ietf.org/html/rfc8410)
-signatureValue       | 64 byte Ed25519 signature per [RFC 8032](https://tools.ietf.org/html/rfc8032), using UDS\_Private or a previous CDI\_Private as the signing key
+signatureAlgorithm   | When using Ed25519, id-Ed25519 per [RFC 8410](https://tools.ietf.org/html/rfc8410)
+signatureValue       | When using Ed25519, 64 byte Ed25519 signature per [RFC 8032](https://tools.ietf.org/html/rfc8032), using UDS\_Private or the current CDI\_Private as the signing key
 version              | v3
 serialNumber         | CDI\_ID in ASN.1 INTEGER form
-signature            | id-Ed25519 per [RFC 8410](https://tools.ietf.org/html/rfc8410)
-issuer               | "SERIALNUMBER=\<UDS\_ID\>" where UDS\_ID is hex encoded lower case
-validity             | The DICE is not expected to have a reliable source of time when generating a certificate. The validity values are populated as follows: _notBefore_ can be any time known to be in the past; in the absence of a better value, "180322235959Z" can be used which is the date of publication of the [TCG DICE specification](#background), and _notAfter_ is set to the standard value used to indicate no well-known expiry date, "99991231235959Z".
-subject              | "SERIALNUMBER=\<CDI\_ID\>" where CDI\_ID is hex encoded lower case
+signature            | When using Ed25519, id-Ed25519 per [RFC 8410](https://tools.ietf.org/html/rfc8410)
+issuer               | "SERIALNUMBER=\<UDS\_ID\>" where UDS\_ID is hex encoded lower case. When layering, UDS\_ID becomes CDI\_ID of the current layer.
+validity             | The DICE is not expected to have a reliable source of time when generating a certificate. The validity values are populated as follows: *notBefore* can be any time known to be in the past; in the absence of a better value, "180322235959Z" can be used which is the date of publication of the [TCG DICE specification](#background), and *notAfter* is set to the standard value used to indicate no well-known expiry date, "99991231235959Z".
+subject              | "SERIALNUMBER=\<CDI\_ID\>" where CDI\_ID is hex encoded lower case. When layering this is the CDI\_ID of the next layer.
 subjectPublicKeyInfo | When using Ed25519, the info per [RFC 8410](https://tools.ietf.org/html/rfc8410) and [RFC 8032](https://tools.ietf.org/html/rfc8032)
 issuerUniqueID       | Omitted
 subjectUniqueID      | Omitted
@@ -648,7 +675,7 @@
 Field     | Value
 --------- | -----
 extnID    | 1.3.6.1.4.1.11129.2.1.24 (The 1.3.6.1.4.1 is the [enterprise number](https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers), the 11129.2.1 is google.googleSecurity.certificateExtensions, and 24 is diceAttestationData assigned for this profile).
-critical  | FALSE
+critical  | TRUE
 extnValue | A [OpenDiceInput](#custom-extension-format) sequence
 
 #### Custom Extension Format
@@ -699,13 +726,12 @@
 
 A CBOR UDS certificate is a standard signed CWT. The following table lists all
 field constraints required by this profile in addition to the standard. The
-certificate is _untagged_, and it must be a _COSE\_Sign1_ message.
+certificate is *untagged*, and it must be a *COSE\_Sign1* message.
 
 Field | Description
 ----- | -----------
 iss   | Required: The value is implementation dependent.
 sub   | Required: The value must be "\<UDS\_ID\>" where UDS\_ID is hex encoded lower case.
-aud   | Omitted
 
 #### Additional Fields
 
@@ -718,10 +744,10 @@
 subjectPublicKey | -4670552
 keyUsage         | -4670553
 
-The _subjectPublicKey_ field contains the public key associated with the subject
+The *subjectPublicKey* field contains the public key associated with the subject
 in the form of a COSE\_Key structure encoded to a CBOR byte string.
 
-The _keyUsage_ field contains a CBOR byte string the bits of which correspond to
+The *keyUsage* field contains a CBOR byte string the bits of which correspond to
 the [X.509 KeyUsage bits](https://tools.ietf.org/html/rfc5280#section-4.2.1.3)
 in little-endian byte order (i.e. bit 0 is the low-order bit of the first byte).
 For UDS certificates this should have only the keyCertSign bit set.
@@ -729,18 +755,16 @@
 ### CBOR CDI Certificates
 
 A CBOR CDI certificate is a standard signed CWT with additional fields. The
-certificate is _untagged_, and it must be a _COSE\_Sign1_ message. The following
+certificate is *untagged*, and it must be a *COSE\_Sign1* message. The following
 table lists all constraints on standard fields required by this profile.
 
 Field | Description
 ----- | -----------
-iss   | Required: The value must be "\<UDS\_ID\>" where UDS\_ID is hex encoded lower case.
-sub   | Required: The value must be "\<CDI\_ID\>" where CDI\_ID is hex encoded lower case.
-aud   | Omitted
-exp   | Omitted
+iss   | Required: The value must be "\<UDS\_ID\>" where UDS\_ID is hex encoded lower case. When layering, UDS\_ID becomes CDI\_ID of the current layer.
+sub   | Required: The value must be "\<CDI\_ID\>" where CDI\_ID is hex encoded lower case. When layering this is the CDI\_ID of the next layer.
+exp   | Omitted when a reliable time source is not available
 nbf   | Omitted when a reliable time source is not available
 iat   | Omitted when a reliable time source is not available
-cti   | Omitted
 
 #### Additional Fields
 
@@ -760,17 +784,17 @@
 subjectPublicKey        | -4670552
 keyUsage                | -4670553
 
-The _subjectPublicKey_ field contains the public key associated with the subject
+The *subjectPublicKey* field contains the public key associated with the subject
 in the form of a COSE\_Key structure encoded to a CBOR byte string.
 
-The _keyUsage_ field contains a CBOR byte string the bits of which correspond to
+The *keyUsage* field contains a CBOR byte string the bits of which correspond to
 the [X.509 KeyUsage bits](https://tools.ietf.org/html/rfc5280#section-4.2.1.3)
 in little-endian byte order (i.e. bit 0 is the low-order bit of the first byte).
 For CDI certificates this should have only the keyCertSign bit set.
 
 All other fields have identical semantics to their counterparts in the
 [X.509 custom extension](#custom-extension-format). The encoding for each is a
-CBOR byte string including _mode_ which is a CBOR byte string holding a single
+CBOR byte string including *mode* which is a CBOR byte string holding a single
 byte (the advantage to using a byte string here is a consistent encoding size
 regardless of the value of mode).
 
@@ -789,7 +813,7 @@
 cover both the hardware and the firmware implementing this profile.
 
 However, this only works if the firmware that implements this profile is
-unmodified during normal operation. It becomes a _ROM extension_ in the sense
+unmodified during normal operation. It becomes a *ROM extension* in the sense
 that if it is modified, the firmware CDI changes, and the certificate chain
 provisioned for the device is no longer valid. In an ARM Trusted Firmware
 architecture, it would likely be BL2 firmware that implements this profile.
@@ -826,7 +850,7 @@
 implementations as long as (1) A UDS can be made available by some means early
 in boot, and (2) that UDS can be made subsequently unavailable until the next
 boot. These implementations meet the requirements of the TCG DICE specification
-as an _updatable DICE_ per section 6.2.
+as an *updatable DICE* per section 6.2.
 
 # Appendix B: Hardware Implementation Checklist
 
@@ -835,7 +859,7 @@
 intended to add any additional requirements or constraints.
 
 1.  Provide a UDS capability as required by this profile and the TCG DICE
-    specification. Usually this _cannot_ be implemented in mask ROM but requires
+    specification. Usually this *cannot* be implemented in mask ROM but requires
     additional hardware capabilities. See [UDS Details](#uds-details).
 1.  Reserve on the order of 8KB of mask ROM for DICE, not including crypto
     primitives. The rest of this list can usually be implemented entirely in
@@ -906,8 +930,8 @@
 *   Destroy any copy of the V-KDF seed, so it's only available to the V-KDF
 *   Run the DICE flow as usual
 
-Note that the V-KDF seed is derived from the _current_ sealing CDI; this value
-is _not_ passed to target code but is locked / destroyed as part of the DICE
+Note that the V-KDF seed is derived from the *current* sealing CDI; this value
+is *not* passed to target code but is locked / destroyed as part of the DICE
 flow. As a result the target code can only generate versioned keys as seeded by
 the previous layer.
 
diff --git a/generate_test_values.py b/generate_test_values.py
index 953ef82..f4afd4f 100644
--- a/generate_test_values.py
+++ b/generate_test_values.py
@@ -115,13 +115,13 @@
     content += _generate_array('kExpectedCdiSeal_%s' % _to_camel_case(name),
                                seal_cdi_data)
     for cert_type in ('X509', 'CBOR'):
-        for key_type in ('Ed25519', 'P256'):
+        for key_type in ('Ed25519', 'P256', 'P384'):
             var_name = 'kExpected%s%sCert_%s' % (_to_camel_case(cert_type),
                                                  _to_camel_case(key_type),
                                                  _to_camel_case(name))
             cert_data = _read_file('_%s_%s_cert_%s.cert' %
                                    (cert_type, key_type, name))
-            if cert_type == 'X509':
+            if cert_type == 'X509' and key_type != 'P384':
                 content += (
                     '// $ openssl x509 -inform DER -noout -text -certopt '
                     'ext_parse\n')
diff --git a/images/architecture.png b/images/architecture.png
index b9a66f5..7b13787 100644
--- a/images/architecture.png
+++ b/images/architecture.png
Binary files differ
diff --git a/include/dice/android/bcc.h b/include/dice/android/bcc.h
index e3e6c55..06f9fcb 100644
--- a/include/dice/android/bcc.h
+++ b/include/dice/android/bcc.h
@@ -80,6 +80,17 @@
                                size_t buffer_size, uint8_t* buffer,
                                size_t* actual_size);
 
+// Parses a BCC handover to extract the fields.
+//
+// Given a pointer to a BCC handover, pointers to the CDIs and, optionally, the
+// BCC in the buffer are returned. If the BCC is not included in the handover,
+// the pointer is NULL and the size is 0.
+DiceResult BccHandoverParse(const uint8_t* bcc_handover,
+                            size_t bcc_handover_size,
+                            const uint8_t** cdi_attest,
+                            const uint8_t** cdi_seal, const uint8_t** bcc,
+                            size_t* bcc_size);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/include/dice/boringssl_ecdsa_utils.h b/include/dice/boringssl_ecdsa_utils.h
new file mode 100644
index 0000000..229926a
--- /dev/null
+++ b/include/dice/boringssl_ecdsa_utils.h
@@ -0,0 +1,57 @@
+// 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
+//
+//     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.
+
+#ifndef BORINGSSL_ECDSA_UTILS_H_
+#define BORINGSSL_ECDSA_UTILS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "dice/dice.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define P384_PRIVATE_KEY_SIZE 48
+#define P384_PUBLIC_KEY_SIZE 96
+#define P384_SIGNATURE_SIZE 96
+
+// Deterministically generates a public and private key pair from |seed|.
+// Since this is deterministic, |seed| is as sensitive as a private key and can
+// be used directly as the private key. The |private_key| may use an
+// implementation defined format so may only be passed to the |sign| operation.
+int P384KeypairFromSeed(uint8_t public_key[P384_PUBLIC_KEY_SIZE],
+                        uint8_t private_key[P384_PRIVATE_KEY_SIZE],
+                        const uint8_t seed[DICE_PRIVATE_KEY_SEED_SIZE]);
+
+// Calculates a signature of |message_size| bytes from |message| using
+// |private_key|. |private_key| was generated by |keypair_from_seed| to allow
+// an implementation to use their own private key format. |signature| points to
+// the buffer where the calculated signature is written.
+int P384Sign(uint8_t signature[P384_SIGNATURE_SIZE], const uint8_t* message,
+             size_t message_size,
+             const uint8_t private_key[P384_PRIVATE_KEY_SIZE]);
+
+// Verifies, using |public_key|, that |signature| covers |message_size| bytes
+// from |message|.
+int P384Verify(const uint8_t* message, size_t message_size,
+               const uint8_t signature[P384_SIGNATURE_SIZE],
+               const uint8_t public_key[P384_PUBLIC_KEY_SIZE]);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // BORINGSSL_ECDSA_UTILS_H_
diff --git a/include/dice/cbor_reader.h b/include/dice/cbor_reader.h
index 5086d9e..04cabc6 100644
--- a/include/dice/cbor_reader.h
+++ b/include/dice/cbor_reader.h
@@ -72,11 +72,15 @@
 enum CborReadResult CborReadFalse(struct CborIn* in);
 enum CborReadResult CborReadTrue(struct CborIn* in);
 enum CborReadResult CborReadNull(struct CborIn* in);
+// Returns CBOR_READ_RESULT_OK even if the value read does not correspond to
+// a valid tag. See https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
+// for a registry of reserved and invalid tag values.
+enum CborReadResult CborReadTag(struct CborIn* in, uint64_t* tag);
 
 // Skips over the next CBOR item in the input. The item may contain nested
-// items, in the case of an array or map, and this function will attempt to
-// descend and skip all nested items in order to skip the parent item. There is
-// a limit on the level of nesting, after which this function will fail with
+// items, in the case of an array, map, or tag, and this function will attempt
+// to descend and skip all nested items in order to skip the parent item. There
+// is a limit on the level of nesting, after which this function will fail with
 // CBOR_READ_RESULT_MALFORMED.
 #define CBOR_READ_SKIP_STACK_SIZE 10
 enum CborReadResult CborReadSkip(struct CborIn* in);
diff --git a/include/dice/cbor_writer.h b/include/dice/cbor_writer.h
index 8a96afa..d7ed6a6 100644
--- a/include/dice/cbor_writer.h
+++ b/include/dice/cbor_writer.h
@@ -66,6 +66,7 @@
 void CborWriteTstr(const char* str, struct CborOut* out);
 void CborWriteArray(size_t num_elements, struct CborOut* out);
 void CborWriteMap(size_t num_pairs, struct CborOut* out);
+void CborWriteTag(uint64_t tag, struct CborOut* out);
 void CborWriteFalse(struct CborOut* out);
 void CborWriteTrue(struct CborOut* out);
 void CborWriteNull(struct CborOut* out);
diff --git a/rust/dice.h b/include/dice/config/boringssl_ecdsa_p384/dice/config.h
similarity index 65%
rename from rust/dice.h
rename to include/dice/config/boringssl_ecdsa_p384/dice/config.h
index 47fe911..a0f9583 100644
--- a/rust/dice.h
+++ b/include/dice/config/boringssl_ecdsa_p384/dice/config.h
@@ -1,4 +1,4 @@
-// Copyright 2021 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
@@ -12,7 +12,14 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-#pragma once
+#ifndef DICE_CONFIG_H_
+#define DICE_CONFIG_H_
 
-#include <dice/dice.h>
-#include <dice/ops.h>
+// ECDSA P384
+// From table 1 of RFC 9053
+#define DICE_COSE_KEY_ALG_VALUE (-35)
+#define DICE_PUBLIC_KEY_SIZE 96
+#define DICE_PRIVATE_KEY_SIZE 48
+#define DICE_SIGNATURE_SIZE 96
+
+#endif  // DICE_DICE_CONFIG_H_
diff --git a/include/dice/config/boringssl_ed25519/dice/config.h b/include/dice/config/boringssl_ed25519/dice/config.h
index 583ac68..02097c4 100644
--- a/include/dice/config/boringssl_ed25519/dice/config.h
+++ b/include/dice/config/boringssl_ed25519/dice/config.h
@@ -16,6 +16,8 @@
 #define DICE_CONFIG_H_
 
 // Ed25519
+// COSE Key alg value from Table 2 of RFC9053
+#define DICE_COSE_KEY_ALG_VALUE (-8)
 #define DICE_PUBLIC_KEY_SIZE 32
 #define DICE_PRIVATE_KEY_SIZE 64
 #define DICE_SIGNATURE_SIZE 64
diff --git a/include/dice/known_test_values.h b/include/dice/known_test_values.h
index 90c565a..c3a052b 100644
--- a/include/dice/known_test_values.h
+++ b/include/dice/known_test_values.h
@@ -62,7 +62,7 @@
 //                 Certificate Sign
 //             X509v3 Basic Constraints: critical
 //                 CA:TRUE
-//             1.3.6.1.4.1.11129.2.1.24:
+//             1.3.6.1.4.1.11129.2.1.24: critical
 //     0:d=0  hl=3 l= 209 cons: SEQUENCE
 //     3:d=1  hl=2 l=  66 cons:  cont [ 0 ]
 //     5:d=2  hl=2 l=  64 prim:   OCTET STRING
@@ -86,12 +86,12 @@
 //   209:d=2  hl=2 l=   1 prim:   ENUMERATED        :00
 //
 //     Signature Algorithm: ED25519
-//          ba:e1:ef:9f:c5:b6:88:be:4a:67:22:b4:47:44:50:25:b6:37:
-//          68:8b:1a:29:27:9e:46:c6:d0:68:d8:6d:19:ed:50:55:ca:24:
-//          41:71:a5:b5:37:0d:60:2b:85:08:f9:38:6c:95:c6:77:33:11:
-//          a2:28:a5:49:5b:87:05:e4:e8:0d
-constexpr uint8_t kExpectedX509Ed25519Cert_ZeroInput[635] = {
-    0x30, 0x82, 0x02, 0x77, 0x30, 0x82, 0x02, 0x29, 0xa0, 0x03, 0x02, 0x01,
+//          70:23:a8:a3:78:fe:5f:8c:8d:83:49:95:ff:ee:e6:12:39:9c:
+//          4d:a0:49:f6:4b:4f:f7:02:1e:9f:97:0c:bd:3b:7f:94:78:26:
+//          09:fe:f2:91:7c:a4:dd:90:07:cc:95:a8:bc:4a:f4:84:69:ec:
+//          24:a4:9e:b7:01:0e:81:3a:a3:0a
+constexpr uint8_t kExpectedX509Ed25519Cert_ZeroInput[638] = {
+    0x30, 0x82, 0x02, 0x7a, 0x30, 0x82, 0x02, 0x2c, 0xa0, 0x03, 0x02, 0x01,
     0x02, 0x02, 0x14, 0x67, 0xc2, 0x2a, 0x88, 0x59, 0x06, 0x2b, 0x98, 0x68,
     0x18, 0xe8, 0xe7, 0x2b, 0x0b, 0xcd, 0x9f, 0x59, 0x34, 0x9c, 0x89, 0x30,
     0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x30, 0x33, 0x31, 0x31, 0x30, 0x2f,
@@ -110,7 +110,7 @@
     0x21, 0x00, 0x0d, 0x14, 0xe5, 0xde, 0x29, 0x2e, 0xb1, 0xc8, 0xb3, 0x1b,
     0xea, 0xe4, 0x3a, 0xb5, 0x5d, 0x8e, 0x9d, 0xc0, 0x14, 0xb7, 0x3e, 0xaa,
     0x83, 0xb9, 0x25, 0xa0, 0x78, 0x8c, 0xc6, 0x2e, 0x5c, 0x8d, 0xa3, 0x82,
-    0x01, 0x4b, 0x30, 0x82, 0x01, 0x47, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+    0x01, 0x4e, 0x30, 0x82, 0x01, 0x4a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
     0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7a, 0x06, 0xee, 0xe4, 0x1b,
     0x78, 0x9f, 0x48, 0x63, 0xd8, 0x6b, 0x87, 0x78, 0xb1, 0xa2, 0x01, 0xa6,
     0xfe, 0xdd, 0x56, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
@@ -118,32 +118,33 @@
     0xe8, 0xe7, 0x2b, 0x0b, 0xcd, 0x9f, 0x59, 0x34, 0x9c, 0x89, 0x30, 0x0e,
     0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
     0x02, 0x04, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
-    0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x81, 0xe3, 0x06, 0x0a,
-    0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x18, 0x04, 0x81,
-    0xd4, 0x30, 0x81, 0xd1, 0xa0, 0x42, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00,
+    0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x81, 0xe6, 0x06, 0x0a,
+    0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x18, 0x01, 0x01,
+    0xff, 0x04, 0x81, 0xd4, 0x30, 0x81, 0xd1, 0xa0, 0x42, 0x04, 0x40, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0xa3, 0x42, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0xa3, 0x42, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x42, 0x04, 0x40,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4,
+    0x42, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0xa6, 0x03, 0x0a, 0x01, 0x00, 0x30, 0x05, 0x06,
-    0x03, 0x2b, 0x65, 0x70, 0x03, 0x41, 0x00, 0xba, 0xe1, 0xef, 0x9f, 0xc5,
-    0xb6, 0x88, 0xbe, 0x4a, 0x67, 0x22, 0xb4, 0x47, 0x44, 0x50, 0x25, 0xb6,
-    0x37, 0x68, 0x8b, 0x1a, 0x29, 0x27, 0x9e, 0x46, 0xc6, 0xd0, 0x68, 0xd8,
-    0x6d, 0x19, 0xed, 0x50, 0x55, 0xca, 0x24, 0x41, 0x71, 0xa5, 0xb5, 0x37,
-    0x0d, 0x60, 0x2b, 0x85, 0x08, 0xf9, 0x38, 0x6c, 0x95, 0xc6, 0x77, 0x33,
-    0x11, 0xa2, 0x28, 0xa5, 0x49, 0x5b, 0x87, 0x05, 0xe4, 0xe8, 0x0d};
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x03, 0x0a, 0x01, 0x00,
+    0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x41, 0x00, 0x70, 0x23,
+    0xa8, 0xa3, 0x78, 0xfe, 0x5f, 0x8c, 0x8d, 0x83, 0x49, 0x95, 0xff, 0xee,
+    0xe6, 0x12, 0x39, 0x9c, 0x4d, 0xa0, 0x49, 0xf6, 0x4b, 0x4f, 0xf7, 0x02,
+    0x1e, 0x9f, 0x97, 0x0c, 0xbd, 0x3b, 0x7f, 0x94, 0x78, 0x26, 0x09, 0xfe,
+    0xf2, 0x91, 0x7c, 0xa4, 0xdd, 0x90, 0x07, 0xcc, 0x95, 0xa8, 0xbc, 0x4a,
+    0xf4, 0x84, 0x69, 0xec, 0x24, 0xa4, 0x9e, 0xb7, 0x01, 0x0e, 0x81, 0x3a,
+    0xa3, 0x0a};
 
 // $ openssl x509 -inform DER -noout -text -certopt ext_parse
 // Certificate:
@@ -178,7 +179,7 @@
 //                 Certificate Sign
 //             X509v3 Basic Constraints: critical
 //                 CA:TRUE
-//             1.3.6.1.4.1.11129.2.1.24:
+//             1.3.6.1.4.1.11129.2.1.24: critical
 //     0:d=0  hl=3 l= 209 cons: SEQUENCE
 //     3:d=1  hl=2 l=  66 cons:  cont [ 0 ]
 //     5:d=2  hl=2 l=  64 prim:   OCTET STRING
@@ -202,12 +203,12 @@
 //   209:d=2  hl=2 l=   1 prim:   ENUMERATED        :00
 //
 //     Signature Algorithm: ecdsa-with-SHA512
-//          30:46:02:21:00:b9:97:04:aa:8f:7f:65:d6:da:7c:3b:35:d8:
-//          6c:1f:93:c9:e2:ae:48:69:dd:0d:82:b9:0a:e1:f4:42:02:35:
-//          b0:02:21:00:9b:a9:cf:3c:5f:2b:88:fd:4f:e6:97:2b:b4:c8:
-//          6b:28:91:75:c0:49:17:48:df:47:2f:80:fa:8c:5d:3f:a9:82
-constexpr uint8_t kExpectedX509P256Cert_ZeroInput[704] = {
-    0x30, 0x82, 0x02, 0xbc, 0x30, 0x82, 0x02, 0x5f, 0xa0, 0x03, 0x02, 0x01,
+//          30:44:02:20:5c:20:d4:32:af:59:4b:44:fc:49:e5:d4:5d:b6:
+//          fe:9c:ac:55:02:74:8b:66:c8:8c:f5:9a:70:8a:97:3c:bc:e0:
+//          02:20:66:83:d1:78:8c:75:44:83:90:c7:1e:d7:4c:98:1f:10:
+//          7f:14:6a:45:c0:1c:48:99:ce:6e:8f:6d:d3:dc:f3:93
+constexpr uint8_t kExpectedX509P256Cert_ZeroInput[705] = {
+    0x30, 0x82, 0x02, 0xbd, 0x30, 0x82, 0x02, 0x62, 0xa0, 0x03, 0x02, 0x01,
     0x02, 0x02, 0x14, 0x7c, 0x7d, 0xc0, 0xa3, 0xc1, 0xe7, 0x8d, 0x4e, 0x68,
     0xbc, 0xc1, 0xa2, 0x32, 0x9e, 0xf9, 0x1c, 0xa8, 0x12, 0x44, 0x91, 0x30,
     0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04, 0x05,
@@ -230,7 +231,7 @@
     0x06, 0x02, 0xae, 0xc2, 0x69, 0x54, 0x1c, 0x6b, 0xe7, 0xeb, 0x40, 0x19,
     0xab, 0x55, 0xc6, 0x6b, 0xc8, 0x8b, 0xb8, 0xb4, 0x69, 0xad, 0x7e, 0xe8,
     0x58, 0x9e, 0x07, 0xd2, 0xf8, 0xbc, 0x88, 0x8e, 0xb3, 0x11, 0xc2, 0xdf,
-    0x97, 0x3b, 0x1b, 0x4a, 0xa3, 0x82, 0x01, 0x4b, 0x30, 0x82, 0x01, 0x47,
+    0x97, 0x3b, 0x1b, 0x4a, 0xa3, 0x82, 0x01, 0x4e, 0x30, 0x82, 0x01, 0x4a,
     0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
     0x14, 0x4c, 0x51, 0x4d, 0x88, 0xdb, 0x0f, 0x81, 0xd5, 0x7b, 0xeb, 0x96,
     0x17, 0x7e, 0x3d, 0x7e, 0xa4, 0xaa, 0x58, 0x1e, 0x66, 0x30, 0x1d, 0x06,
@@ -239,33 +240,35 @@
     0xa8, 0x12, 0x44, 0x91, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
     0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x0f, 0x06, 0x03,
     0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01,
-    0xff, 0x30, 0x81, 0xe3, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6,
-    0x79, 0x02, 0x01, 0x18, 0x04, 0x81, 0xd4, 0x30, 0x81, 0xd1, 0xa0, 0x42,
-    0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xff, 0x30, 0x81, 0xe6, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6,
+    0x79, 0x02, 0x01, 0x18, 0x01, 0x01, 0xff, 0x04, 0x81, 0xd4, 0x30, 0x81,
+    0xd1, 0xa0, 0x42, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x42, 0x04, 0x40, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x42, 0x04,
+    0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x42, 0x04, 0x40, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0xa4, 0x42, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x03,
-    0x0a, 0x01, 0x00, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
-    0x04, 0x03, 0x04, 0x05, 0x00, 0x03, 0x49, 0x00, 0x30, 0x46, 0x02, 0x21,
-    0x00, 0xb9, 0x97, 0x04, 0xaa, 0x8f, 0x7f, 0x65, 0xd6, 0xda, 0x7c, 0x3b,
-    0x35, 0xd8, 0x6c, 0x1f, 0x93, 0xc9, 0xe2, 0xae, 0x48, 0x69, 0xdd, 0x0d,
-    0x82, 0xb9, 0x0a, 0xe1, 0xf4, 0x42, 0x02, 0x35, 0xb0, 0x02, 0x21, 0x00,
-    0x9b, 0xa9, 0xcf, 0x3c, 0x5f, 0x2b, 0x88, 0xfd, 0x4f, 0xe6, 0x97, 0x2b,
-    0xb4, 0xc8, 0x6b, 0x28, 0x91, 0x75, 0xc0, 0x49, 0x17, 0x48, 0xdf, 0x47,
-    0x2f, 0x80, 0xfa, 0x8c, 0x5d, 0x3f, 0xa9, 0x82};
+    0x00, 0xa6, 0x03, 0x0a, 0x01, 0x00, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86,
+    0x48, 0xce, 0x3d, 0x04, 0x03, 0x04, 0x05, 0x00, 0x03, 0x47, 0x00, 0x30,
+    0x44, 0x02, 0x20, 0x5c, 0x20, 0xd4, 0x32, 0xaf, 0x59, 0x4b, 0x44, 0xfc,
+    0x49, 0xe5, 0xd4, 0x5d, 0xb6, 0xfe, 0x9c, 0xac, 0x55, 0x02, 0x74, 0x8b,
+    0x66, 0xc8, 0x8c, 0xf5, 0x9a, 0x70, 0x8a, 0x97, 0x3c, 0xbc, 0xe0, 0x02,
+    0x20, 0x66, 0x83, 0xd1, 0x78, 0x8c, 0x75, 0x44, 0x83, 0x90, 0xc7, 0x1e,
+    0xd7, 0x4c, 0x98, 0x1f, 0x10, 0x7f, 0x14, 0x6a, 0x45, 0xc0, 0x1c, 0x48,
+    0x99, 0xce, 0x6e, 0x8f, 0x6d, 0xd3, 0xdc, 0xf3, 0x93};
+
+constexpr uint8_t kExpectedX509P384Cert_ZeroInput[0] = {};
 
 constexpr uint8_t kExpectedCborEd25519Cert_ZeroInput[441] = {
     0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x6e, 0xa8, 0x01, 0x78,
@@ -308,6 +311,54 @@
 
 constexpr uint8_t kExpectedCborP256Cert_ZeroInput[0] = {};
 
+constexpr uint8_t kExpectedCborP384Cert_ZeroInput[542] = {
+    0x84, 0x44, 0xa1, 0x01, 0x38, 0x22, 0xa0, 0x59, 0x01, 0xb2, 0xa8, 0x01,
+    0x78, 0x28, 0x30, 0x34, 0x63, 0x32, 0x36, 0x35, 0x66, 0x65, 0x30, 0x36,
+    0x66, 0x66, 0x32, 0x33, 0x30, 0x65, 0x33, 0x39, 0x62, 0x36, 0x33, 0x32,
+    0x32, 0x65, 0x65, 0x61, 0x39, 0x65, 0x30, 0x31, 0x30, 0x37, 0x31, 0x31,
+    0x66, 0x62, 0x36, 0x36, 0x62, 0x34, 0x02, 0x78, 0x28, 0x34, 0x30, 0x63,
+    0x62, 0x34, 0x66, 0x30, 0x36, 0x34, 0x61, 0x36, 0x38, 0x64, 0x34, 0x30,
+    0x37, 0x61, 0x30, 0x62, 0x33, 0x39, 0x30, 0x61, 0x62, 0x63, 0x63, 0x30,
+    0x35, 0x61, 0x33, 0x34, 0x62, 0x66, 0x63, 0x38, 0x61, 0x66, 0x33, 0x33,
+    0x66, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x3a, 0x00, 0x47, 0x44, 0x53, 0x58, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a,
+    0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00,
+    0x47, 0x44, 0x56, 0x41, 0x00, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x71,
+    0xa6, 0x01, 0x02, 0x03, 0x38, 0x22, 0x04, 0x81, 0x02, 0x20, 0x02, 0x21,
+    0x58, 0x30, 0x77, 0x19, 0x6b, 0xa5, 0x84, 0xeb, 0x79, 0x46, 0xd2, 0xfb,
+    0xb0, 0xd5, 0xc8, 0x31, 0xc7, 0xad, 0x91, 0x37, 0x5e, 0x11, 0x28, 0xdb,
+    0x23, 0x8c, 0xc1, 0xc6, 0x7f, 0xae, 0x5e, 0x07, 0x10, 0x95, 0x5b, 0x17,
+    0xb5, 0xd5, 0x08, 0x12, 0x31, 0x06, 0xba, 0x31, 0x31, 0x10, 0x43, 0x71,
+    0x51, 0xde, 0x22, 0x58, 0x30, 0x03, 0x25, 0xa9, 0x76, 0x29, 0x67, 0x9d,
+    0x6b, 0xa9, 0x01, 0xb1, 0x22, 0xa0, 0x4b, 0xee, 0xf7, 0xb3, 0xe1, 0x52,
+    0xfc, 0xe0, 0x3c, 0xdc, 0x5d, 0x1b, 0x58, 0x16, 0x69, 0xdd, 0x44, 0x24,
+    0x67, 0xbf, 0x21, 0xd7, 0x47, 0xf3, 0x13, 0xd1, 0x47, 0x6c, 0x4b, 0xd3,
+    0x05, 0xb5, 0x29, 0xa0, 0xf1, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20,
+    0x58, 0x60, 0xb0, 0xb7, 0x0f, 0x47, 0xfa, 0xba, 0xca, 0x05, 0x16, 0x4f,
+    0x1e, 0xd1, 0x73, 0x15, 0x94, 0x17, 0xcd, 0x6b, 0x3c, 0x0e, 0x18, 0x77,
+    0xc0, 0x6e, 0x53, 0x97, 0xf2, 0x03, 0xb9, 0xbb, 0x82, 0xde, 0xe0, 0x2d,
+    0xb3, 0xc8, 0x0e, 0x7a, 0x5e, 0xb0, 0x74, 0x15, 0xbe, 0x25, 0x6a, 0x7c,
+    0x90, 0x30, 0x80, 0x0c, 0x90, 0x20, 0x00, 0xc5, 0x42, 0xbf, 0xf7, 0x4e,
+    0x1b, 0xd8, 0xe2, 0xe4, 0x32, 0xd5, 0xf3, 0x8e, 0x1e, 0x59, 0x9f, 0x1e,
+    0x71, 0x8e, 0xc3, 0x2e, 0x50, 0x8a, 0xa0, 0x02, 0xa4, 0xd3, 0xe6, 0x8c,
+    0x68, 0xbb, 0x5e, 0x33, 0xfd, 0x81, 0x12, 0xa7, 0xdc, 0x68, 0xc7, 0x7a,
+    0x4b, 0xfa};
+
 constexpr uint8_t kExpectedCdiAttest_HashOnlyInput[32] = {
     0x08, 0x4e, 0xf4, 0x06, 0xc6, 0x9b, 0xa7, 0x4b, 0x1e, 0x24, 0xd0,
     0x62, 0xf9, 0xab, 0x8a, 0x8d, 0x89, 0xda, 0x6e, 0x03, 0xe4, 0xc6,
@@ -347,7 +398,7 @@
 //                 Certificate Sign
 //             X509v3 Basic Constraints: critical
 //                 CA:TRUE
-//             1.3.6.1.4.1.11129.2.1.24:
+//             1.3.6.1.4.1.11129.2.1.24: critical
 //     0:d=0  hl=3 l= 209 cons: SEQUENCE
 //     3:d=1  hl=2 l=  66 cons:  cont [ 0 ]
 //     5:d=2  hl=2 l=  64 prim:   OCTET STRING
@@ -371,12 +422,12 @@
 //   209:d=2  hl=2 l=   1 prim:   ENUMERATED        :00
 //
 //     Signature Algorithm: ED25519
-//          2d:26:2d:e5:ad:8f:4a:d2:78:e3:38:fe:bc:4f:60:67:d8:c3:
-//          20:4b:f1:19:e1:e7:48:c5:9f:51:01:57:62:f1:0d:75:d2:6d:
-//          d0:1b:0b:a4:32:f6:55:5a:55:b0:86:c0:e9:f9:6a:35:7c:5e:
-//          40:68:5f:05:ef:f6:52:a7:4c:05
-constexpr uint8_t kExpectedX509Ed25519Cert_HashOnlyInput[635] = {
-    0x30, 0x82, 0x02, 0x77, 0x30, 0x82, 0x02, 0x29, 0xa0, 0x03, 0x02, 0x01,
+//          27:68:51:a1:be:41:b7:90:03:8d:ce:ce:9c:35:9c:69:ff:f7:
+//          7c:14:fb:8d:59:06:71:cf:39:d8:c9:8e:95:79:70:f2:8f:89:
+//          af:4d:92:a7:40:c8:fb:85:2a:7c:21:3d:09:8a:d4:f2:25:96:
+//          cd:f6:29:06:73:98:db:70:74:08
+constexpr uint8_t kExpectedX509Ed25519Cert_HashOnlyInput[638] = {
+    0x30, 0x82, 0x02, 0x7a, 0x30, 0x82, 0x02, 0x2c, 0xa0, 0x03, 0x02, 0x01,
     0x02, 0x02, 0x14, 0x0d, 0x04, 0x0e, 0x2f, 0x46, 0x00, 0x52, 0xa5, 0x31,
     0x1c, 0x1b, 0x91, 0xdb, 0xf9, 0xb4, 0x40, 0x83, 0x32, 0xec, 0x29, 0x30,
     0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x30, 0x33, 0x31, 0x31, 0x30, 0x2f,
@@ -395,7 +446,7 @@
     0x21, 0x00, 0x5a, 0x39, 0x49, 0x67, 0x8c, 0xd3, 0x0e, 0x88, 0xab, 0x1c,
     0xdd, 0xf7, 0x15, 0x55, 0xd5, 0xbf, 0xd3, 0xf0, 0xb8, 0x47, 0x25, 0xa9,
     0x58, 0xe1, 0xb9, 0xda, 0x4e, 0xb5, 0xf1, 0x38, 0x9a, 0x5a, 0xa3, 0x82,
-    0x01, 0x4b, 0x30, 0x82, 0x01, 0x47, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+    0x01, 0x4e, 0x30, 0x82, 0x01, 0x4a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
     0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x47, 0x57, 0x08, 0xeb, 0x3b,
     0x42, 0x6f, 0x38, 0x6c, 0xfc, 0xe8, 0xf3, 0xba, 0xf5, 0x43, 0x90, 0x46,
     0x27, 0x8d, 0xfa, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
@@ -403,32 +454,33 @@
     0x1b, 0x91, 0xdb, 0xf9, 0xb4, 0x40, 0x83, 0x32, 0xec, 0x29, 0x30, 0x0e,
     0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
     0x02, 0x04, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
-    0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x81, 0xe3, 0x06, 0x0a,
-    0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x18, 0x04, 0x81,
-    0xd4, 0x30, 0x81, 0xd1, 0xa0, 0x42, 0x04, 0x40, 0xb7, 0xd4, 0x0c, 0xcb,
-    0x22, 0x5b, 0xa5, 0x78, 0x8f, 0x98, 0xff, 0x9e, 0x86, 0x93, 0x75, 0xf6,
-    0x90, 0xac, 0x50, 0xcf, 0x9e, 0xbd, 0x0a, 0xfe, 0xb1, 0xd9, 0xc2, 0x4e,
-    0x52, 0x19, 0xe4, 0xde, 0x29, 0xe5, 0x61, 0xf3, 0xf9, 0x29, 0xe8, 0x40,
-    0x87, 0x7a, 0xdd, 0x17, 0x48, 0x05, 0x89, 0x7e, 0x2b, 0xcb, 0x54, 0x79,
-    0xcc, 0x66, 0xf1, 0xb3, 0x13, 0x29, 0x0c, 0x68, 0x96, 0xb2, 0xbb, 0x8f,
-    0xa3, 0x42, 0x04, 0x40, 0xcf, 0x99, 0x7b, 0xea, 0x2e, 0x2c, 0x86, 0xa0,
-    0x7b, 0x52, 0x09, 0xc8, 0xb5, 0x3c, 0x41, 0x12, 0x29, 0x28, 0x1a, 0x82,
-    0x0d, 0x49, 0x9c, 0x95, 0xcb, 0x0b, 0x1b, 0x31, 0x1a, 0x01, 0x9c, 0xf2,
-    0x66, 0x1a, 0xd9, 0xb5, 0xce, 0x52, 0x59, 0xcb, 0xf4, 0x81, 0x9b, 0x21,
-    0xaf, 0x32, 0x5d, 0x07, 0xa0, 0x1e, 0x91, 0x59, 0x6f, 0x06, 0x55, 0x10,
-    0x8e, 0x2e, 0x08, 0x88, 0x52, 0x28, 0x86, 0x7f, 0xa4, 0x42, 0x04, 0x40,
-    0x22, 0x52, 0x60, 0x17, 0xef, 0x2c, 0xa1, 0xf6, 0xcb, 0xed, 0x39, 0xd5,
-    0xe2, 0xaa, 0x65, 0x20, 0xfb, 0xad, 0x82, 0x93, 0xe5, 0x78, 0x23, 0x22,
-    0x97, 0xc1, 0x6e, 0x6a, 0x4e, 0x36, 0xd7, 0x6a, 0x61, 0x39, 0x08, 0x21,
-    0xd4, 0xfe, 0x92, 0x5f, 0x36, 0x2d, 0xeb, 0x5d, 0xbb, 0x32, 0x8b, 0xe3,
-    0x94, 0x4f, 0xbe, 0x1b, 0x21, 0xf9, 0xcc, 0x23, 0x73, 0x41, 0xb6, 0xb9,
-    0xb6, 0x98, 0xd0, 0xbc, 0xa6, 0x03, 0x0a, 0x01, 0x00, 0x30, 0x05, 0x06,
-    0x03, 0x2b, 0x65, 0x70, 0x03, 0x41, 0x00, 0x2d, 0x26, 0x2d, 0xe5, 0xad,
-    0x8f, 0x4a, 0xd2, 0x78, 0xe3, 0x38, 0xfe, 0xbc, 0x4f, 0x60, 0x67, 0xd8,
-    0xc3, 0x20, 0x4b, 0xf1, 0x19, 0xe1, 0xe7, 0x48, 0xc5, 0x9f, 0x51, 0x01,
-    0x57, 0x62, 0xf1, 0x0d, 0x75, 0xd2, 0x6d, 0xd0, 0x1b, 0x0b, 0xa4, 0x32,
-    0xf6, 0x55, 0x5a, 0x55, 0xb0, 0x86, 0xc0, 0xe9, 0xf9, 0x6a, 0x35, 0x7c,
-    0x5e, 0x40, 0x68, 0x5f, 0x05, 0xef, 0xf6, 0x52, 0xa7, 0x4c, 0x05};
+    0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x81, 0xe6, 0x06, 0x0a,
+    0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x18, 0x01, 0x01,
+    0xff, 0x04, 0x81, 0xd4, 0x30, 0x81, 0xd1, 0xa0, 0x42, 0x04, 0x40, 0xb7,
+    0xd4, 0x0c, 0xcb, 0x22, 0x5b, 0xa5, 0x78, 0x8f, 0x98, 0xff, 0x9e, 0x86,
+    0x93, 0x75, 0xf6, 0x90, 0xac, 0x50, 0xcf, 0x9e, 0xbd, 0x0a, 0xfe, 0xb1,
+    0xd9, 0xc2, 0x4e, 0x52, 0x19, 0xe4, 0xde, 0x29, 0xe5, 0x61, 0xf3, 0xf9,
+    0x29, 0xe8, 0x40, 0x87, 0x7a, 0xdd, 0x17, 0x48, 0x05, 0x89, 0x7e, 0x2b,
+    0xcb, 0x54, 0x79, 0xcc, 0x66, 0xf1, 0xb3, 0x13, 0x29, 0x0c, 0x68, 0x96,
+    0xb2, 0xbb, 0x8f, 0xa3, 0x42, 0x04, 0x40, 0xcf, 0x99, 0x7b, 0xea, 0x2e,
+    0x2c, 0x86, 0xa0, 0x7b, 0x52, 0x09, 0xc8, 0xb5, 0x3c, 0x41, 0x12, 0x29,
+    0x28, 0x1a, 0x82, 0x0d, 0x49, 0x9c, 0x95, 0xcb, 0x0b, 0x1b, 0x31, 0x1a,
+    0x01, 0x9c, 0xf2, 0x66, 0x1a, 0xd9, 0xb5, 0xce, 0x52, 0x59, 0xcb, 0xf4,
+    0x81, 0x9b, 0x21, 0xaf, 0x32, 0x5d, 0x07, 0xa0, 0x1e, 0x91, 0x59, 0x6f,
+    0x06, 0x55, 0x10, 0x8e, 0x2e, 0x08, 0x88, 0x52, 0x28, 0x86, 0x7f, 0xa4,
+    0x42, 0x04, 0x40, 0x22, 0x52, 0x60, 0x17, 0xef, 0x2c, 0xa1, 0xf6, 0xcb,
+    0xed, 0x39, 0xd5, 0xe2, 0xaa, 0x65, 0x20, 0xfb, 0xad, 0x82, 0x93, 0xe5,
+    0x78, 0x23, 0x22, 0x97, 0xc1, 0x6e, 0x6a, 0x4e, 0x36, 0xd7, 0x6a, 0x61,
+    0x39, 0x08, 0x21, 0xd4, 0xfe, 0x92, 0x5f, 0x36, 0x2d, 0xeb, 0x5d, 0xbb,
+    0x32, 0x8b, 0xe3, 0x94, 0x4f, 0xbe, 0x1b, 0x21, 0xf9, 0xcc, 0x23, 0x73,
+    0x41, 0xb6, 0xb9, 0xb6, 0x98, 0xd0, 0xbc, 0xa6, 0x03, 0x0a, 0x01, 0x00,
+    0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x41, 0x00, 0x27, 0x68,
+    0x51, 0xa1, 0xbe, 0x41, 0xb7, 0x90, 0x03, 0x8d, 0xce, 0xce, 0x9c, 0x35,
+    0x9c, 0x69, 0xff, 0xf7, 0x7c, 0x14, 0xfb, 0x8d, 0x59, 0x06, 0x71, 0xcf,
+    0x39, 0xd8, 0xc9, 0x8e, 0x95, 0x79, 0x70, 0xf2, 0x8f, 0x89, 0xaf, 0x4d,
+    0x92, 0xa7, 0x40, 0xc8, 0xfb, 0x85, 0x2a, 0x7c, 0x21, 0x3d, 0x09, 0x8a,
+    0xd4, 0xf2, 0x25, 0x96, 0xcd, 0xf6, 0x29, 0x06, 0x73, 0x98, 0xdb, 0x70,
+    0x74, 0x08};
 
 // $ openssl x509 -inform DER -noout -text -certopt ext_parse
 // Certificate:
@@ -463,7 +515,7 @@
 //                 Certificate Sign
 //             X509v3 Basic Constraints: critical
 //                 CA:TRUE
-//             1.3.6.1.4.1.11129.2.1.24:
+//             1.3.6.1.4.1.11129.2.1.24: critical
 //     0:d=0  hl=3 l= 209 cons: SEQUENCE
 //     3:d=1  hl=2 l=  66 cons:  cont [ 0 ]
 //     5:d=2  hl=2 l=  64 prim:   OCTET STRING
@@ -487,12 +539,12 @@
 //   209:d=2  hl=2 l=   1 prim:   ENUMERATED        :00
 //
 //     Signature Algorithm: ecdsa-with-SHA512
-//          30:45:02:21:00:a8:1f:9a:1c:bd:6e:f7:a1:6c:4c:a8:98:b3:
-//          c9:c7:48:8b:2a:aa:29:37:35:83:8a:e3:64:2b:f2:e2:e2:a1:
-//          44:02:20:5e:80:29:7c:a9:7e:8c:44:3d:01:f7:ea:bd:8a:1f:
-//          eb:e8:f6:92:43:03:16:e6:a9:5d:e7:26:42:f4:9b:6e:54
-constexpr uint8_t kExpectedX509P256Cert_HashOnlyInput[703] = {
-    0x30, 0x82, 0x02, 0xbb, 0x30, 0x82, 0x02, 0x5f, 0xa0, 0x03, 0x02, 0x01,
+//          30:46:02:21:00:d0:ff:60:84:d4:74:47:eb:f4:29:86:c5:2a:
+//          12:01:f9:7e:5b:ab:fd:fe:d3:11:75:bc:26:63:71:57:2b:b1:
+//          c8:02:21:00:e0:16:72:65:b0:4a:92:ca:c6:c9:e2:0f:50:f0:
+//          1c:70:01:64:a7:8d:7d:51:e9:59:fe:e6:7f:31:28:30:04:04
+constexpr uint8_t kExpectedX509P256Cert_HashOnlyInput[707] = {
+    0x30, 0x82, 0x02, 0xbf, 0x30, 0x82, 0x02, 0x62, 0xa0, 0x03, 0x02, 0x01,
     0x02, 0x02, 0x14, 0x68, 0x49, 0x58, 0xd9, 0xae, 0xa7, 0x2e, 0xbf, 0x7c,
     0x06, 0xaf, 0x20, 0x03, 0xb6, 0x44, 0x47, 0x82, 0x4a, 0x62, 0x71, 0x30,
     0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04, 0x05,
@@ -515,7 +567,7 @@
     0xfb, 0x6d, 0x57, 0x18, 0xfc, 0x8f, 0x6f, 0x0b, 0x09, 0x1a, 0x19, 0xea,
     0x10, 0x7e, 0xa9, 0x38, 0xf4, 0x45, 0x33, 0xc1, 0x66, 0x5b, 0xbc, 0xfc,
     0x0a, 0x6e, 0x98, 0x99, 0x72, 0x88, 0xc1, 0xad, 0x0e, 0x15, 0xc2, 0x85,
-    0x77, 0x75, 0x00, 0x0b, 0xa3, 0x82, 0x01, 0x4b, 0x30, 0x82, 0x01, 0x47,
+    0x77, 0x75, 0x00, 0x0b, 0xa3, 0x82, 0x01, 0x4e, 0x30, 0x82, 0x01, 0x4a,
     0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
     0x14, 0x1b, 0xe5, 0x68, 0x79, 0x33, 0xdb, 0x3d, 0x9c, 0xd5, 0xfc, 0xa7,
     0x29, 0xe8, 0x1d, 0x66, 0x85, 0x46, 0x5a, 0x7b, 0xf1, 0x30, 0x1d, 0x06,
@@ -524,33 +576,35 @@
     0x82, 0x4a, 0x62, 0x71, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
     0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x0f, 0x06, 0x03,
     0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01,
-    0xff, 0x30, 0x81, 0xe3, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6,
-    0x79, 0x02, 0x01, 0x18, 0x04, 0x81, 0xd4, 0x30, 0x81, 0xd1, 0xa0, 0x42,
-    0x04, 0x40, 0xb7, 0xd4, 0x0c, 0xcb, 0x22, 0x5b, 0xa5, 0x78, 0x8f, 0x98,
-    0xff, 0x9e, 0x86, 0x93, 0x75, 0xf6, 0x90, 0xac, 0x50, 0xcf, 0x9e, 0xbd,
-    0x0a, 0xfe, 0xb1, 0xd9, 0xc2, 0x4e, 0x52, 0x19, 0xe4, 0xde, 0x29, 0xe5,
-    0x61, 0xf3, 0xf9, 0x29, 0xe8, 0x40, 0x87, 0x7a, 0xdd, 0x17, 0x48, 0x05,
-    0x89, 0x7e, 0x2b, 0xcb, 0x54, 0x79, 0xcc, 0x66, 0xf1, 0xb3, 0x13, 0x29,
-    0x0c, 0x68, 0x96, 0xb2, 0xbb, 0x8f, 0xa3, 0x42, 0x04, 0x40, 0xcf, 0x99,
-    0x7b, 0xea, 0x2e, 0x2c, 0x86, 0xa0, 0x7b, 0x52, 0x09, 0xc8, 0xb5, 0x3c,
-    0x41, 0x12, 0x29, 0x28, 0x1a, 0x82, 0x0d, 0x49, 0x9c, 0x95, 0xcb, 0x0b,
-    0x1b, 0x31, 0x1a, 0x01, 0x9c, 0xf2, 0x66, 0x1a, 0xd9, 0xb5, 0xce, 0x52,
-    0x59, 0xcb, 0xf4, 0x81, 0x9b, 0x21, 0xaf, 0x32, 0x5d, 0x07, 0xa0, 0x1e,
-    0x91, 0x59, 0x6f, 0x06, 0x55, 0x10, 0x8e, 0x2e, 0x08, 0x88, 0x52, 0x28,
-    0x86, 0x7f, 0xa4, 0x42, 0x04, 0x40, 0x22, 0x52, 0x60, 0x17, 0xef, 0x2c,
-    0xa1, 0xf6, 0xcb, 0xed, 0x39, 0xd5, 0xe2, 0xaa, 0x65, 0x20, 0xfb, 0xad,
-    0x82, 0x93, 0xe5, 0x78, 0x23, 0x22, 0x97, 0xc1, 0x6e, 0x6a, 0x4e, 0x36,
-    0xd7, 0x6a, 0x61, 0x39, 0x08, 0x21, 0xd4, 0xfe, 0x92, 0x5f, 0x36, 0x2d,
-    0xeb, 0x5d, 0xbb, 0x32, 0x8b, 0xe3, 0x94, 0x4f, 0xbe, 0x1b, 0x21, 0xf9,
-    0xcc, 0x23, 0x73, 0x41, 0xb6, 0xb9, 0xb6, 0x98, 0xd0, 0xbc, 0xa6, 0x03,
-    0x0a, 0x01, 0x00, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
-    0x04, 0x03, 0x04, 0x05, 0x00, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x21,
-    0x00, 0xa8, 0x1f, 0x9a, 0x1c, 0xbd, 0x6e, 0xf7, 0xa1, 0x6c, 0x4c, 0xa8,
-    0x98, 0xb3, 0xc9, 0xc7, 0x48, 0x8b, 0x2a, 0xaa, 0x29, 0x37, 0x35, 0x83,
-    0x8a, 0xe3, 0x64, 0x2b, 0xf2, 0xe2, 0xe2, 0xa1, 0x44, 0x02, 0x20, 0x5e,
-    0x80, 0x29, 0x7c, 0xa9, 0x7e, 0x8c, 0x44, 0x3d, 0x01, 0xf7, 0xea, 0xbd,
-    0x8a, 0x1f, 0xeb, 0xe8, 0xf6, 0x92, 0x43, 0x03, 0x16, 0xe6, 0xa9, 0x5d,
-    0xe7, 0x26, 0x42, 0xf4, 0x9b, 0x6e, 0x54};
+    0xff, 0x30, 0x81, 0xe6, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6,
+    0x79, 0x02, 0x01, 0x18, 0x01, 0x01, 0xff, 0x04, 0x81, 0xd4, 0x30, 0x81,
+    0xd1, 0xa0, 0x42, 0x04, 0x40, 0xb7, 0xd4, 0x0c, 0xcb, 0x22, 0x5b, 0xa5,
+    0x78, 0x8f, 0x98, 0xff, 0x9e, 0x86, 0x93, 0x75, 0xf6, 0x90, 0xac, 0x50,
+    0xcf, 0x9e, 0xbd, 0x0a, 0xfe, 0xb1, 0xd9, 0xc2, 0x4e, 0x52, 0x19, 0xe4,
+    0xde, 0x29, 0xe5, 0x61, 0xf3, 0xf9, 0x29, 0xe8, 0x40, 0x87, 0x7a, 0xdd,
+    0x17, 0x48, 0x05, 0x89, 0x7e, 0x2b, 0xcb, 0x54, 0x79, 0xcc, 0x66, 0xf1,
+    0xb3, 0x13, 0x29, 0x0c, 0x68, 0x96, 0xb2, 0xbb, 0x8f, 0xa3, 0x42, 0x04,
+    0x40, 0xcf, 0x99, 0x7b, 0xea, 0x2e, 0x2c, 0x86, 0xa0, 0x7b, 0x52, 0x09,
+    0xc8, 0xb5, 0x3c, 0x41, 0x12, 0x29, 0x28, 0x1a, 0x82, 0x0d, 0x49, 0x9c,
+    0x95, 0xcb, 0x0b, 0x1b, 0x31, 0x1a, 0x01, 0x9c, 0xf2, 0x66, 0x1a, 0xd9,
+    0xb5, 0xce, 0x52, 0x59, 0xcb, 0xf4, 0x81, 0x9b, 0x21, 0xaf, 0x32, 0x5d,
+    0x07, 0xa0, 0x1e, 0x91, 0x59, 0x6f, 0x06, 0x55, 0x10, 0x8e, 0x2e, 0x08,
+    0x88, 0x52, 0x28, 0x86, 0x7f, 0xa4, 0x42, 0x04, 0x40, 0x22, 0x52, 0x60,
+    0x17, 0xef, 0x2c, 0xa1, 0xf6, 0xcb, 0xed, 0x39, 0xd5, 0xe2, 0xaa, 0x65,
+    0x20, 0xfb, 0xad, 0x82, 0x93, 0xe5, 0x78, 0x23, 0x22, 0x97, 0xc1, 0x6e,
+    0x6a, 0x4e, 0x36, 0xd7, 0x6a, 0x61, 0x39, 0x08, 0x21, 0xd4, 0xfe, 0x92,
+    0x5f, 0x36, 0x2d, 0xeb, 0x5d, 0xbb, 0x32, 0x8b, 0xe3, 0x94, 0x4f, 0xbe,
+    0x1b, 0x21, 0xf9, 0xcc, 0x23, 0x73, 0x41, 0xb6, 0xb9, 0xb6, 0x98, 0xd0,
+    0xbc, 0xa6, 0x03, 0x0a, 0x01, 0x00, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86,
+    0x48, 0xce, 0x3d, 0x04, 0x03, 0x04, 0x05, 0x00, 0x03, 0x49, 0x00, 0x30,
+    0x46, 0x02, 0x21, 0x00, 0xd0, 0xff, 0x60, 0x84, 0xd4, 0x74, 0x47, 0xeb,
+    0xf4, 0x29, 0x86, 0xc5, 0x2a, 0x12, 0x01, 0xf9, 0x7e, 0x5b, 0xab, 0xfd,
+    0xfe, 0xd3, 0x11, 0x75, 0xbc, 0x26, 0x63, 0x71, 0x57, 0x2b, 0xb1, 0xc8,
+    0x02, 0x21, 0x00, 0xe0, 0x16, 0x72, 0x65, 0xb0, 0x4a, 0x92, 0xca, 0xc6,
+    0xc9, 0xe2, 0x0f, 0x50, 0xf0, 0x1c, 0x70, 0x01, 0x64, 0xa7, 0x8d, 0x7d,
+    0x51, 0xe9, 0x59, 0xfe, 0xe6, 0x7f, 0x31, 0x28, 0x30, 0x04, 0x04};
+
+constexpr uint8_t kExpectedX509P384Cert_HashOnlyInput[0] = {};
 
 constexpr uint8_t kExpectedCborEd25519Cert_HashOnlyInput[441] = {
     0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x6e, 0xa8, 0x01, 0x78,
@@ -593,6 +647,54 @@
 
 constexpr uint8_t kExpectedCborP256Cert_HashOnlyInput[0] = {};
 
+constexpr uint8_t kExpectedCborP384Cert_HashOnlyInput[542] = {
+    0x84, 0x44, 0xa1, 0x01, 0x38, 0x22, 0xa0, 0x59, 0x01, 0xb2, 0xa8, 0x01,
+    0x78, 0x28, 0x35, 0x64, 0x38, 0x62, 0x36, 0x62, 0x65, 0x37, 0x63, 0x65,
+    0x33, 0x65, 0x64, 0x65, 0x36, 0x61, 0x32, 0x34, 0x31, 0x38, 0x30, 0x31,
+    0x34, 0x35, 0x32, 0x33, 0x65, 0x36, 0x63, 0x39, 0x64, 0x63, 0x38, 0x37,
+    0x65, 0x39, 0x38, 0x63, 0x63, 0x36, 0x02, 0x78, 0x28, 0x36, 0x35, 0x61,
+    0x63, 0x35, 0x39, 0x36, 0x61, 0x62, 0x39, 0x39, 0x34, 0x30, 0x33, 0x61,
+    0x38, 0x63, 0x37, 0x30, 0x32, 0x37, 0x35, 0x62, 0x31, 0x34, 0x62, 0x30,
+    0x32, 0x33, 0x37, 0x33, 0x66, 0x66, 0x31, 0x34, 0x33, 0x66, 0x39, 0x31,
+    0x65, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0xb7, 0xd4, 0x0c, 0xcb,
+    0x22, 0x5b, 0xa5, 0x78, 0x8f, 0x98, 0xff, 0x9e, 0x86, 0x93, 0x75, 0xf6,
+    0x90, 0xac, 0x50, 0xcf, 0x9e, 0xbd, 0x0a, 0xfe, 0xb1, 0xd9, 0xc2, 0x4e,
+    0x52, 0x19, 0xe4, 0xde, 0x29, 0xe5, 0x61, 0xf3, 0xf9, 0x29, 0xe8, 0x40,
+    0x87, 0x7a, 0xdd, 0x17, 0x48, 0x05, 0x89, 0x7e, 0x2b, 0xcb, 0x54, 0x79,
+    0xcc, 0x66, 0xf1, 0xb3, 0x13, 0x29, 0x0c, 0x68, 0x96, 0xb2, 0xbb, 0x8f,
+    0x3a, 0x00, 0x47, 0x44, 0x53, 0x58, 0x40, 0xcf, 0x99, 0x7b, 0xea, 0x2e,
+    0x2c, 0x86, 0xa0, 0x7b, 0x52, 0x09, 0xc8, 0xb5, 0x3c, 0x41, 0x12, 0x29,
+    0x28, 0x1a, 0x82, 0x0d, 0x49, 0x9c, 0x95, 0xcb, 0x0b, 0x1b, 0x31, 0x1a,
+    0x01, 0x9c, 0xf2, 0x66, 0x1a, 0xd9, 0xb5, 0xce, 0x52, 0x59, 0xcb, 0xf4,
+    0x81, 0x9b, 0x21, 0xaf, 0x32, 0x5d, 0x07, 0xa0, 0x1e, 0x91, 0x59, 0x6f,
+    0x06, 0x55, 0x10, 0x8e, 0x2e, 0x08, 0x88, 0x52, 0x28, 0x86, 0x7f, 0x3a,
+    0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0x22, 0x52, 0x60, 0x17, 0xef, 0x2c,
+    0xa1, 0xf6, 0xcb, 0xed, 0x39, 0xd5, 0xe2, 0xaa, 0x65, 0x20, 0xfb, 0xad,
+    0x82, 0x93, 0xe5, 0x78, 0x23, 0x22, 0x97, 0xc1, 0x6e, 0x6a, 0x4e, 0x36,
+    0xd7, 0x6a, 0x61, 0x39, 0x08, 0x21, 0xd4, 0xfe, 0x92, 0x5f, 0x36, 0x2d,
+    0xeb, 0x5d, 0xbb, 0x32, 0x8b, 0xe3, 0x94, 0x4f, 0xbe, 0x1b, 0x21, 0xf9,
+    0xcc, 0x23, 0x73, 0x41, 0xb6, 0xb9, 0xb6, 0x98, 0xd0, 0xbc, 0x3a, 0x00,
+    0x47, 0x44, 0x56, 0x41, 0x00, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x71,
+    0xa6, 0x01, 0x02, 0x03, 0x38, 0x22, 0x04, 0x81, 0x02, 0x20, 0x02, 0x21,
+    0x58, 0x30, 0x32, 0x81, 0xad, 0x61, 0x1e, 0x50, 0x96, 0x2b, 0x5e, 0xda,
+    0xff, 0xee, 0x14, 0xa6, 0x44, 0x3d, 0xd9, 0xd1, 0x34, 0xf6, 0x64, 0xb7,
+    0x61, 0x58, 0xf4, 0x9a, 0x58, 0xdb, 0xef, 0xa8, 0x87, 0x13, 0x26, 0x08,
+    0x1b, 0xc7, 0xdd, 0xc5, 0x5b, 0x73, 0x42, 0xd6, 0x29, 0x87, 0x3f, 0x85,
+    0xd0, 0xe4, 0x22, 0x58, 0x30, 0x60, 0x85, 0xd8, 0x42, 0x29, 0x1b, 0xc6,
+    0xd9, 0xf6, 0x2f, 0x3a, 0xce, 0xa0, 0xb9, 0x40, 0xb8, 0x18, 0xde, 0xc2,
+    0x5c, 0x90, 0xfb, 0x4b, 0x6d, 0x96, 0x42, 0x77, 0xe2, 0xf6, 0x58, 0x3d,
+    0x37, 0xa7, 0x3d, 0x2e, 0xca, 0xd1, 0x2c, 0xa4, 0xd4, 0xa7, 0xaf, 0x25,
+    0xc3, 0xb2, 0xe7, 0x34, 0xf5, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20,
+    0x58, 0x60, 0x84, 0x9a, 0x2e, 0x89, 0xeb, 0x6d, 0x61, 0x93, 0xe0, 0x0e,
+    0xb9, 0x57, 0xc6, 0x84, 0x08, 0x28, 0x77, 0xeb, 0x9e, 0x39, 0xad, 0x74,
+    0x78, 0xf3, 0x8f, 0xf7, 0xdf, 0xfa, 0xa2, 0xbf, 0x01, 0x4f, 0x94, 0x1a,
+    0xa1, 0x27, 0xaa, 0x93, 0x57, 0xe4, 0x49, 0x07, 0xb9, 0xd7, 0x49, 0xdb,
+    0x73, 0x1e, 0xa1, 0x7e, 0xf1, 0x19, 0x87, 0x8e, 0x5a, 0x89, 0xb7, 0x02,
+    0x19, 0x13, 0xdb, 0x20, 0x3f, 0x5f, 0x49, 0xc9, 0xb7, 0xcf, 0x52, 0xc3,
+    0xd2, 0xf7, 0x90, 0x52, 0xf7, 0xaa, 0x39, 0x66, 0x78, 0x7a, 0x5b, 0xb3,
+    0xa7, 0xe9, 0x33, 0xc6, 0x4f, 0xe6, 0x78, 0xf3, 0x4b, 0x51, 0xf1, 0xed,
+    0x15, 0xeb};
+
 constexpr uint8_t kExpectedCdiAttest_DescriptorInput[32] = {
     0x20, 0xd5, 0x0c, 0x68, 0x5a, 0xd9, 0xe2, 0xdf, 0x77, 0x60, 0x78,
     0x68, 0x19, 0x00, 0x24, 0xc2, 0x04, 0x4f, 0xb8, 0xde, 0x79, 0xaa,
@@ -632,7 +734,7 @@
 //                 Certificate Sign
 //             X509v3 Basic Constraints: critical
 //                 CA:TRUE
-//             1.3.6.1.4.1.11129.2.1.24:
+//             1.3.6.1.4.1.11129.2.1.24: critical
 //     0:d=0  hl=4 l= 426 cons: SEQUENCE
 //     4:d=1  hl=2 l=  66 cons:  cont [ 0 ]
 //     6:d=2  hl=2 l=  64 prim:   OCTET STRING
@@ -677,12 +779,12 @@
 //   427:d=2  hl=2 l=   1 prim:   ENUMERATED        :00
 //
 //     Signature Algorithm: ED25519
-//          70:3c:ba:64:6b:18:2b:3f:26:b3:67:52:3b:3d:08:f5:88:3d:
-//          fe:79:d4:b4:e5:70:71:1d:95:f6:9a:ac:ca:f7:23:8b:fd:e2:
-//          22:5b:3b:39:82:99:02:e8:db:67:70:f2:99:9d:f7:3b:73:12:
-//          6a:b0:a0:ac:49:4b:37:06:21:06
-constexpr uint8_t kExpectedX509Ed25519Cert_DescriptorInput[855] = {
-    0x30, 0x82, 0x03, 0x53, 0x30, 0x82, 0x03, 0x05, 0xa0, 0x03, 0x02, 0x01,
+//          23:23:66:8c:b2:de:08:0a:fa:96:01:3f:5c:f3:87:ac:33:e0:
+//          d0:86:9b:f5:6b:df:2c:6e:9e:63:a5:5c:dd:d4:55:27:df:7e:
+//          6f:ad:6a:95:99:fd:0f:91:a9:44:25:42:c4:02:c5:ed:2a:b3:
+//          72:23:4e:79:76:f9:13:6e:f7:00
+constexpr uint8_t kExpectedX509Ed25519Cert_DescriptorInput[858] = {
+    0x30, 0x82, 0x03, 0x56, 0x30, 0x82, 0x03, 0x08, 0xa0, 0x03, 0x02, 0x01,
     0x02, 0x02, 0x14, 0x52, 0x1f, 0x03, 0x5c, 0x21, 0xe3, 0x2f, 0x16, 0x74,
     0x1c, 0x1e, 0xae, 0x6b, 0xde, 0xd9, 0x3c, 0xe3, 0x21, 0xe0, 0xdf, 0x30,
     0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x30, 0x33, 0x31, 0x31, 0x30, 0x2f,
@@ -701,7 +803,7 @@
     0x21, 0x00, 0x93, 0x7f, 0xd9, 0xc0, 0x4d, 0xc6, 0xbb, 0x2e, 0x1d, 0x11,
     0x62, 0xcd, 0x5c, 0x76, 0x94, 0xc7, 0xdb, 0x02, 0x54, 0x0c, 0x85, 0x01,
     0x3a, 0x01, 0xab, 0x37, 0xfa, 0xce, 0xf9, 0x6e, 0x62, 0x20, 0xa3, 0x82,
-    0x02, 0x27, 0x30, 0x82, 0x02, 0x23, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+    0x02, 0x2a, 0x30, 0x82, 0x02, 0x26, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
     0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x47, 0x57, 0x08, 0xeb, 0x3b,
     0x42, 0x6f, 0x38, 0x6c, 0xfc, 0xe8, 0xf3, 0xba, 0xf5, 0x43, 0x90, 0x46,
     0x27, 0x8d, 0xfa, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
@@ -709,51 +811,51 @@
     0x1e, 0xae, 0x6b, 0xde, 0xd9, 0x3c, 0xe3, 0x21, 0xe0, 0xdf, 0x30, 0x0e,
     0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
     0x02, 0x04, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
-    0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x82, 0x01, 0xbe, 0x06,
-    0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x18, 0x04,
-    0x82, 0x01, 0xae, 0x30, 0x82, 0x01, 0xaa, 0xa0, 0x42, 0x04, 0x40, 0xb7,
-    0xd4, 0x0c, 0xcb, 0x22, 0x5b, 0xa5, 0x78, 0x8f, 0x98, 0xff, 0x9e, 0x86,
-    0x93, 0x75, 0xf6, 0x90, 0xac, 0x50, 0xcf, 0x9e, 0xbd, 0x0a, 0xfe, 0xb1,
-    0xd9, 0xc2, 0x4e, 0x52, 0x19, 0xe4, 0xde, 0x29, 0xe5, 0x61, 0xf3, 0xf9,
-    0x29, 0xe8, 0x40, 0x87, 0x7a, 0xdd, 0x17, 0x48, 0x05, 0x89, 0x7e, 0x2b,
-    0xcb, 0x54, 0x79, 0xcc, 0x66, 0xf1, 0xb3, 0x13, 0x29, 0x0c, 0x68, 0x96,
-    0xb2, 0xbb, 0x8f, 0xa1, 0x66, 0x04, 0x64, 0x6c, 0x46, 0x01, 0x33, 0x26,
-    0x73, 0x4b, 0x22, 0x65, 0xfd, 0xfa, 0x58, 0xd7, 0x57, 0x3e, 0x95, 0x59,
-    0xe0, 0x3a, 0xc3, 0xb9, 0xf7, 0xc8, 0x0e, 0x98, 0x80, 0x8c, 0xf5, 0xc4,
-    0xb8, 0xaf, 0xe3, 0x16, 0x84, 0x25, 0xa5, 0x35, 0x5d, 0x17, 0x72, 0x56,
-    0x8f, 0x8e, 0xec, 0x2f, 0x5a, 0x74, 0x60, 0x77, 0x2a, 0x6e, 0x90, 0xc0,
-    0x4e, 0x9f, 0x87, 0x6b, 0xf4, 0x8d, 0x9c, 0x66, 0xe3, 0x0b, 0xd2, 0x10,
-    0x35, 0x21, 0xa8, 0x1d, 0xa2, 0x31, 0x17, 0xe7, 0x0c, 0xdf, 0x18, 0xf7,
-    0x94, 0xe4, 0xd1, 0xca, 0x32, 0x7d, 0xf2, 0x63, 0x23, 0x1d, 0xbc, 0x84,
-    0x74, 0x61, 0xdb, 0x87, 0xf2, 0xab, 0x72, 0xad, 0xaf, 0x08, 0xf8, 0xa2,
-    0x42, 0x04, 0x40, 0x45, 0x00, 0xe9, 0x5c, 0xbd, 0x00, 0x57, 0x04, 0x55,
-    0x87, 0x6c, 0xbd, 0x2f, 0xea, 0x41, 0x9c, 0x66, 0x42, 0x51, 0x41, 0xbb,
-    0x44, 0xed, 0x0e, 0xe9, 0x66, 0xcf, 0xd5, 0x10, 0x73, 0x0d, 0x4b, 0x48,
-    0xe4, 0x7a, 0x53, 0x35, 0x01, 0x0e, 0x6d, 0x15, 0x55, 0xc5, 0xb7, 0xd2,
-    0xd5, 0x36, 0xb6, 0xbc, 0x7e, 0xb0, 0xf3, 0x3d, 0xe6, 0x19, 0x78, 0x62,
-    0xeb, 0x02, 0x57, 0x39, 0x56, 0x73, 0x4f, 0xa3, 0x2a, 0x04, 0x28, 0x1b,
-    0x40, 0xc1, 0xa9, 0x77, 0x60, 0xeb, 0xc3, 0x67, 0xf0, 0x5f, 0x6a, 0xe1,
-    0x5e, 0x20, 0xc2, 0x51, 0x68, 0x4d, 0x82, 0x48, 0x8b, 0x03, 0x32, 0x16,
-    0x79, 0x88, 0x14, 0x37, 0x78, 0x7f, 0x16, 0x9a, 0x06, 0xfd, 0xc0, 0x8a,
-    0x15, 0x80, 0x62, 0xa4, 0x42, 0x04, 0x40, 0x22, 0x52, 0x60, 0x17, 0xef,
-    0x2c, 0xa1, 0xf6, 0xcb, 0xed, 0x39, 0xd5, 0xe2, 0xaa, 0x65, 0x20, 0xfb,
-    0xad, 0x82, 0x93, 0xe5, 0x78, 0x23, 0x22, 0x97, 0xc1, 0x6e, 0x6a, 0x4e,
-    0x36, 0xd7, 0x6a, 0x61, 0x39, 0x08, 0x21, 0xd4, 0xfe, 0x92, 0x5f, 0x36,
-    0x2d, 0xeb, 0x5d, 0xbb, 0x32, 0x8b, 0xe3, 0x94, 0x4f, 0xbe, 0x1b, 0x21,
-    0xf9, 0xcc, 0x23, 0x73, 0x41, 0xb6, 0xb9, 0xb6, 0x98, 0xd0, 0xbc, 0xa5,
-    0x43, 0x04, 0x41, 0x92, 0xd6, 0x97, 0xb3, 0x83, 0xdf, 0xe7, 0x8c, 0xc7,
-    0xbc, 0x4a, 0xfc, 0xea, 0x76, 0xc0, 0x53, 0x66, 0xbd, 0x2c, 0x1e, 0x10,
-    0x31, 0x90, 0x80, 0x11, 0x2d, 0x08, 0x4d, 0x7c, 0x39, 0x76, 0xdc, 0x73,
-    0xe7, 0x1c, 0x16, 0x62, 0xd5, 0x59, 0xd7, 0x49, 0x2b, 0x6a, 0xa2, 0x36,
-    0x67, 0x57, 0xd1, 0xf2, 0xf9, 0xaf, 0x13, 0xd7, 0xa3, 0xe4, 0xd3, 0x39,
-    0x5b, 0x02, 0x78, 0xb1, 0xe0, 0x09, 0x70, 0xa2, 0xa6, 0x03, 0x0a, 0x01,
-    0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x41, 0x00, 0x70,
-    0x3c, 0xba, 0x64, 0x6b, 0x18, 0x2b, 0x3f, 0x26, 0xb3, 0x67, 0x52, 0x3b,
-    0x3d, 0x08, 0xf5, 0x88, 0x3d, 0xfe, 0x79, 0xd4, 0xb4, 0xe5, 0x70, 0x71,
-    0x1d, 0x95, 0xf6, 0x9a, 0xac, 0xca, 0xf7, 0x23, 0x8b, 0xfd, 0xe2, 0x22,
-    0x5b, 0x3b, 0x39, 0x82, 0x99, 0x02, 0xe8, 0xdb, 0x67, 0x70, 0xf2, 0x99,
-    0x9d, 0xf7, 0x3b, 0x73, 0x12, 0x6a, 0xb0, 0xa0, 0xac, 0x49, 0x4b, 0x37,
-    0x06, 0x21, 0x06};
+    0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x82, 0x01, 0xc1, 0x06,
+    0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x18, 0x01,
+    0x01, 0xff, 0x04, 0x82, 0x01, 0xae, 0x30, 0x82, 0x01, 0xaa, 0xa0, 0x42,
+    0x04, 0x40, 0xb7, 0xd4, 0x0c, 0xcb, 0x22, 0x5b, 0xa5, 0x78, 0x8f, 0x98,
+    0xff, 0x9e, 0x86, 0x93, 0x75, 0xf6, 0x90, 0xac, 0x50, 0xcf, 0x9e, 0xbd,
+    0x0a, 0xfe, 0xb1, 0xd9, 0xc2, 0x4e, 0x52, 0x19, 0xe4, 0xde, 0x29, 0xe5,
+    0x61, 0xf3, 0xf9, 0x29, 0xe8, 0x40, 0x87, 0x7a, 0xdd, 0x17, 0x48, 0x05,
+    0x89, 0x7e, 0x2b, 0xcb, 0x54, 0x79, 0xcc, 0x66, 0xf1, 0xb3, 0x13, 0x29,
+    0x0c, 0x68, 0x96, 0xb2, 0xbb, 0x8f, 0xa1, 0x66, 0x04, 0x64, 0x6c, 0x46,
+    0x01, 0x33, 0x26, 0x73, 0x4b, 0x22, 0x65, 0xfd, 0xfa, 0x58, 0xd7, 0x57,
+    0x3e, 0x95, 0x59, 0xe0, 0x3a, 0xc3, 0xb9, 0xf7, 0xc8, 0x0e, 0x98, 0x80,
+    0x8c, 0xf5, 0xc4, 0xb8, 0xaf, 0xe3, 0x16, 0x84, 0x25, 0xa5, 0x35, 0x5d,
+    0x17, 0x72, 0x56, 0x8f, 0x8e, 0xec, 0x2f, 0x5a, 0x74, 0x60, 0x77, 0x2a,
+    0x6e, 0x90, 0xc0, 0x4e, 0x9f, 0x87, 0x6b, 0xf4, 0x8d, 0x9c, 0x66, 0xe3,
+    0x0b, 0xd2, 0x10, 0x35, 0x21, 0xa8, 0x1d, 0xa2, 0x31, 0x17, 0xe7, 0x0c,
+    0xdf, 0x18, 0xf7, 0x94, 0xe4, 0xd1, 0xca, 0x32, 0x7d, 0xf2, 0x63, 0x23,
+    0x1d, 0xbc, 0x84, 0x74, 0x61, 0xdb, 0x87, 0xf2, 0xab, 0x72, 0xad, 0xaf,
+    0x08, 0xf8, 0xa2, 0x42, 0x04, 0x40, 0x45, 0x00, 0xe9, 0x5c, 0xbd, 0x00,
+    0x57, 0x04, 0x55, 0x87, 0x6c, 0xbd, 0x2f, 0xea, 0x41, 0x9c, 0x66, 0x42,
+    0x51, 0x41, 0xbb, 0x44, 0xed, 0x0e, 0xe9, 0x66, 0xcf, 0xd5, 0x10, 0x73,
+    0x0d, 0x4b, 0x48, 0xe4, 0x7a, 0x53, 0x35, 0x01, 0x0e, 0x6d, 0x15, 0x55,
+    0xc5, 0xb7, 0xd2, 0xd5, 0x36, 0xb6, 0xbc, 0x7e, 0xb0, 0xf3, 0x3d, 0xe6,
+    0x19, 0x78, 0x62, 0xeb, 0x02, 0x57, 0x39, 0x56, 0x73, 0x4f, 0xa3, 0x2a,
+    0x04, 0x28, 0x1b, 0x40, 0xc1, 0xa9, 0x77, 0x60, 0xeb, 0xc3, 0x67, 0xf0,
+    0x5f, 0x6a, 0xe1, 0x5e, 0x20, 0xc2, 0x51, 0x68, 0x4d, 0x82, 0x48, 0x8b,
+    0x03, 0x32, 0x16, 0x79, 0x88, 0x14, 0x37, 0x78, 0x7f, 0x16, 0x9a, 0x06,
+    0xfd, 0xc0, 0x8a, 0x15, 0x80, 0x62, 0xa4, 0x42, 0x04, 0x40, 0x22, 0x52,
+    0x60, 0x17, 0xef, 0x2c, 0xa1, 0xf6, 0xcb, 0xed, 0x39, 0xd5, 0xe2, 0xaa,
+    0x65, 0x20, 0xfb, 0xad, 0x82, 0x93, 0xe5, 0x78, 0x23, 0x22, 0x97, 0xc1,
+    0x6e, 0x6a, 0x4e, 0x36, 0xd7, 0x6a, 0x61, 0x39, 0x08, 0x21, 0xd4, 0xfe,
+    0x92, 0x5f, 0x36, 0x2d, 0xeb, 0x5d, 0xbb, 0x32, 0x8b, 0xe3, 0x94, 0x4f,
+    0xbe, 0x1b, 0x21, 0xf9, 0xcc, 0x23, 0x73, 0x41, 0xb6, 0xb9, 0xb6, 0x98,
+    0xd0, 0xbc, 0xa5, 0x43, 0x04, 0x41, 0x92, 0xd6, 0x97, 0xb3, 0x83, 0xdf,
+    0xe7, 0x8c, 0xc7, 0xbc, 0x4a, 0xfc, 0xea, 0x76, 0xc0, 0x53, 0x66, 0xbd,
+    0x2c, 0x1e, 0x10, 0x31, 0x90, 0x80, 0x11, 0x2d, 0x08, 0x4d, 0x7c, 0x39,
+    0x76, 0xdc, 0x73, 0xe7, 0x1c, 0x16, 0x62, 0xd5, 0x59, 0xd7, 0x49, 0x2b,
+    0x6a, 0xa2, 0x36, 0x67, 0x57, 0xd1, 0xf2, 0xf9, 0xaf, 0x13, 0xd7, 0xa3,
+    0xe4, 0xd3, 0x39, 0x5b, 0x02, 0x78, 0xb1, 0xe0, 0x09, 0x70, 0xa2, 0xa6,
+    0x03, 0x0a, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03,
+    0x41, 0x00, 0x23, 0x23, 0x66, 0x8c, 0xb2, 0xde, 0x08, 0x0a, 0xfa, 0x96,
+    0x01, 0x3f, 0x5c, 0xf3, 0x87, 0xac, 0x33, 0xe0, 0xd0, 0x86, 0x9b, 0xf5,
+    0x6b, 0xdf, 0x2c, 0x6e, 0x9e, 0x63, 0xa5, 0x5c, 0xdd, 0xd4, 0x55, 0x27,
+    0xdf, 0x7e, 0x6f, 0xad, 0x6a, 0x95, 0x99, 0xfd, 0x0f, 0x91, 0xa9, 0x44,
+    0x25, 0x42, 0xc4, 0x02, 0xc5, 0xed, 0x2a, 0xb3, 0x72, 0x23, 0x4e, 0x79,
+    0x76, 0xf9, 0x13, 0x6e, 0xf7, 0x00};
 
 // $ openssl x509 -inform DER -noout -text -certopt ext_parse
 // Certificate:
@@ -788,7 +890,7 @@
 //                 Certificate Sign
 //             X509v3 Basic Constraints: critical
 //                 CA:TRUE
-//             1.3.6.1.4.1.11129.2.1.24:
+//             1.3.6.1.4.1.11129.2.1.24: critical
 //     0:d=0  hl=4 l= 426 cons: SEQUENCE
 //     4:d=1  hl=2 l=  66 cons:  cont [ 0 ]
 //     6:d=2  hl=2 l=  64 prim:   OCTET STRING
@@ -833,12 +935,12 @@
 //   427:d=2  hl=2 l=   1 prim:   ENUMERATED        :00
 //
 //     Signature Algorithm: ecdsa-with-SHA512
-//          30:45:02:21:00:b2:0b:bb:86:04:4e:38:55:db:f5:58:92:e1:
-//          c1:31:4e:79:57:e4:5f:bc:36:68:29:b4:89:a5:63:4e:67:19:
-//          8c:02:20:1b:5d:e6:35:c0:cd:9f:7b:ec:c0:01:52:b8:2f:81:
-//          f0:29:01:a0:17:19:83:ca:84:e2:ec:01:a8:b2:13:8a:b9
-constexpr uint8_t kExpectedX509P256Cert_DescriptorInput[923] = {
-    0x30, 0x82, 0x03, 0x97, 0x30, 0x82, 0x03, 0x3b, 0xa0, 0x03, 0x02, 0x01,
+//          30:46:02:21:00:a9:57:7d:ac:e0:80:8e:1c:7a:e1:4d:7d:2a:
+//          52:d2:5e:fb:5a:7d:45:1e:fa:67:17:04:9e:b3:05:c2:34:f0:
+//          7a:02:21:00:b7:3a:31:25:ae:1d:2b:06:8b:ba:44:d9:3a:bf:
+//          8b:e2:3f:89:e2:a6:14:0c:fa:c2:b1:dd:0c:0a:78:ac:60:19
+constexpr uint8_t kExpectedX509P256Cert_DescriptorInput[927] = {
+    0x30, 0x82, 0x03, 0x9b, 0x30, 0x82, 0x03, 0x3e, 0xa0, 0x03, 0x02, 0x01,
     0x02, 0x02, 0x14, 0x2c, 0x0d, 0xe9, 0x55, 0xc4, 0xfa, 0x08, 0x2c, 0x2c,
     0x3a, 0x0b, 0x40, 0x66, 0x59, 0xaf, 0xa1, 0xc1, 0xc0, 0x84, 0x6c, 0x30,
     0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04, 0x05,
@@ -861,7 +963,7 @@
     0x9b, 0x27, 0xf3, 0x87, 0x97, 0xb3, 0xe7, 0x36, 0xe6, 0x42, 0x87, 0x8c,
     0x72, 0xde, 0xf7, 0xaf, 0x2d, 0xc6, 0x23, 0x00, 0xb1, 0x2b, 0x4e, 0x1c,
     0xf3, 0xaf, 0x67, 0xf0, 0x9b, 0x88, 0x40, 0x79, 0x3b, 0x09, 0x78, 0x30,
-    0x51, 0x65, 0x38, 0x61, 0xa3, 0x82, 0x02, 0x27, 0x30, 0x82, 0x02, 0x23,
+    0x51, 0x65, 0x38, 0x61, 0xa3, 0x82, 0x02, 0x2a, 0x30, 0x82, 0x02, 0x26,
     0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
     0x14, 0x1b, 0xe5, 0x68, 0x79, 0x33, 0xdb, 0x3d, 0x9c, 0xd5, 0xfc, 0xa7,
     0x29, 0xe8, 0x1d, 0x66, 0x85, 0x46, 0x5a, 0x7b, 0xf1, 0x30, 0x1d, 0x06,
@@ -870,51 +972,54 @@
     0xc1, 0xc0, 0x84, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
     0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x0f, 0x06, 0x03,
     0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01,
-    0xff, 0x30, 0x82, 0x01, 0xbe, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01,
-    0xd6, 0x79, 0x02, 0x01, 0x18, 0x04, 0x82, 0x01, 0xae, 0x30, 0x82, 0x01,
-    0xaa, 0xa0, 0x42, 0x04, 0x40, 0xb7, 0xd4, 0x0c, 0xcb, 0x22, 0x5b, 0xa5,
-    0x78, 0x8f, 0x98, 0xff, 0x9e, 0x86, 0x93, 0x75, 0xf6, 0x90, 0xac, 0x50,
-    0xcf, 0x9e, 0xbd, 0x0a, 0xfe, 0xb1, 0xd9, 0xc2, 0x4e, 0x52, 0x19, 0xe4,
-    0xde, 0x29, 0xe5, 0x61, 0xf3, 0xf9, 0x29, 0xe8, 0x40, 0x87, 0x7a, 0xdd,
-    0x17, 0x48, 0x05, 0x89, 0x7e, 0x2b, 0xcb, 0x54, 0x79, 0xcc, 0x66, 0xf1,
-    0xb3, 0x13, 0x29, 0x0c, 0x68, 0x96, 0xb2, 0xbb, 0x8f, 0xa1, 0x66, 0x04,
-    0x64, 0x6c, 0x46, 0x01, 0x33, 0x26, 0x73, 0x4b, 0x22, 0x65, 0xfd, 0xfa,
-    0x58, 0xd7, 0x57, 0x3e, 0x95, 0x59, 0xe0, 0x3a, 0xc3, 0xb9, 0xf7, 0xc8,
-    0x0e, 0x98, 0x80, 0x8c, 0xf5, 0xc4, 0xb8, 0xaf, 0xe3, 0x16, 0x84, 0x25,
-    0xa5, 0x35, 0x5d, 0x17, 0x72, 0x56, 0x8f, 0x8e, 0xec, 0x2f, 0x5a, 0x74,
-    0x60, 0x77, 0x2a, 0x6e, 0x90, 0xc0, 0x4e, 0x9f, 0x87, 0x6b, 0xf4, 0x8d,
-    0x9c, 0x66, 0xe3, 0x0b, 0xd2, 0x10, 0x35, 0x21, 0xa8, 0x1d, 0xa2, 0x31,
-    0x17, 0xe7, 0x0c, 0xdf, 0x18, 0xf7, 0x94, 0xe4, 0xd1, 0xca, 0x32, 0x7d,
-    0xf2, 0x63, 0x23, 0x1d, 0xbc, 0x84, 0x74, 0x61, 0xdb, 0x87, 0xf2, 0xab,
-    0x72, 0xad, 0xaf, 0x08, 0xf8, 0xa2, 0x42, 0x04, 0x40, 0x45, 0x00, 0xe9,
-    0x5c, 0xbd, 0x00, 0x57, 0x04, 0x55, 0x87, 0x6c, 0xbd, 0x2f, 0xea, 0x41,
-    0x9c, 0x66, 0x42, 0x51, 0x41, 0xbb, 0x44, 0xed, 0x0e, 0xe9, 0x66, 0xcf,
-    0xd5, 0x10, 0x73, 0x0d, 0x4b, 0x48, 0xe4, 0x7a, 0x53, 0x35, 0x01, 0x0e,
-    0x6d, 0x15, 0x55, 0xc5, 0xb7, 0xd2, 0xd5, 0x36, 0xb6, 0xbc, 0x7e, 0xb0,
-    0xf3, 0x3d, 0xe6, 0x19, 0x78, 0x62, 0xeb, 0x02, 0x57, 0x39, 0x56, 0x73,
-    0x4f, 0xa3, 0x2a, 0x04, 0x28, 0x1b, 0x40, 0xc1, 0xa9, 0x77, 0x60, 0xeb,
-    0xc3, 0x67, 0xf0, 0x5f, 0x6a, 0xe1, 0x5e, 0x20, 0xc2, 0x51, 0x68, 0x4d,
-    0x82, 0x48, 0x8b, 0x03, 0x32, 0x16, 0x79, 0x88, 0x14, 0x37, 0x78, 0x7f,
-    0x16, 0x9a, 0x06, 0xfd, 0xc0, 0x8a, 0x15, 0x80, 0x62, 0xa4, 0x42, 0x04,
-    0x40, 0x22, 0x52, 0x60, 0x17, 0xef, 0x2c, 0xa1, 0xf6, 0xcb, 0xed, 0x39,
-    0xd5, 0xe2, 0xaa, 0x65, 0x20, 0xfb, 0xad, 0x82, 0x93, 0xe5, 0x78, 0x23,
-    0x22, 0x97, 0xc1, 0x6e, 0x6a, 0x4e, 0x36, 0xd7, 0x6a, 0x61, 0x39, 0x08,
-    0x21, 0xd4, 0xfe, 0x92, 0x5f, 0x36, 0x2d, 0xeb, 0x5d, 0xbb, 0x32, 0x8b,
-    0xe3, 0x94, 0x4f, 0xbe, 0x1b, 0x21, 0xf9, 0xcc, 0x23, 0x73, 0x41, 0xb6,
-    0xb9, 0xb6, 0x98, 0xd0, 0xbc, 0xa5, 0x43, 0x04, 0x41, 0x92, 0xd6, 0x97,
-    0xb3, 0x83, 0xdf, 0xe7, 0x8c, 0xc7, 0xbc, 0x4a, 0xfc, 0xea, 0x76, 0xc0,
-    0x53, 0x66, 0xbd, 0x2c, 0x1e, 0x10, 0x31, 0x90, 0x80, 0x11, 0x2d, 0x08,
-    0x4d, 0x7c, 0x39, 0x76, 0xdc, 0x73, 0xe7, 0x1c, 0x16, 0x62, 0xd5, 0x59,
-    0xd7, 0x49, 0x2b, 0x6a, 0xa2, 0x36, 0x67, 0x57, 0xd1, 0xf2, 0xf9, 0xaf,
-    0x13, 0xd7, 0xa3, 0xe4, 0xd3, 0x39, 0x5b, 0x02, 0x78, 0xb1, 0xe0, 0x09,
-    0x70, 0xa2, 0xa6, 0x03, 0x0a, 0x01, 0x00, 0x30, 0x0c, 0x06, 0x08, 0x2a,
-    0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04, 0x05, 0x00, 0x03, 0x48, 0x00,
-    0x30, 0x45, 0x02, 0x21, 0x00, 0xb2, 0x0b, 0xbb, 0x86, 0x04, 0x4e, 0x38,
-    0x55, 0xdb, 0xf5, 0x58, 0x92, 0xe1, 0xc1, 0x31, 0x4e, 0x79, 0x57, 0xe4,
-    0x5f, 0xbc, 0x36, 0x68, 0x29, 0xb4, 0x89, 0xa5, 0x63, 0x4e, 0x67, 0x19,
-    0x8c, 0x02, 0x20, 0x1b, 0x5d, 0xe6, 0x35, 0xc0, 0xcd, 0x9f, 0x7b, 0xec,
-    0xc0, 0x01, 0x52, 0xb8, 0x2f, 0x81, 0xf0, 0x29, 0x01, 0xa0, 0x17, 0x19,
-    0x83, 0xca, 0x84, 0xe2, 0xec, 0x01, 0xa8, 0xb2, 0x13, 0x8a, 0xb9};
+    0xff, 0x30, 0x82, 0x01, 0xc1, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01,
+    0xd6, 0x79, 0x02, 0x01, 0x18, 0x01, 0x01, 0xff, 0x04, 0x82, 0x01, 0xae,
+    0x30, 0x82, 0x01, 0xaa, 0xa0, 0x42, 0x04, 0x40, 0xb7, 0xd4, 0x0c, 0xcb,
+    0x22, 0x5b, 0xa5, 0x78, 0x8f, 0x98, 0xff, 0x9e, 0x86, 0x93, 0x75, 0xf6,
+    0x90, 0xac, 0x50, 0xcf, 0x9e, 0xbd, 0x0a, 0xfe, 0xb1, 0xd9, 0xc2, 0x4e,
+    0x52, 0x19, 0xe4, 0xde, 0x29, 0xe5, 0x61, 0xf3, 0xf9, 0x29, 0xe8, 0x40,
+    0x87, 0x7a, 0xdd, 0x17, 0x48, 0x05, 0x89, 0x7e, 0x2b, 0xcb, 0x54, 0x79,
+    0xcc, 0x66, 0xf1, 0xb3, 0x13, 0x29, 0x0c, 0x68, 0x96, 0xb2, 0xbb, 0x8f,
+    0xa1, 0x66, 0x04, 0x64, 0x6c, 0x46, 0x01, 0x33, 0x26, 0x73, 0x4b, 0x22,
+    0x65, 0xfd, 0xfa, 0x58, 0xd7, 0x57, 0x3e, 0x95, 0x59, 0xe0, 0x3a, 0xc3,
+    0xb9, 0xf7, 0xc8, 0x0e, 0x98, 0x80, 0x8c, 0xf5, 0xc4, 0xb8, 0xaf, 0xe3,
+    0x16, 0x84, 0x25, 0xa5, 0x35, 0x5d, 0x17, 0x72, 0x56, 0x8f, 0x8e, 0xec,
+    0x2f, 0x5a, 0x74, 0x60, 0x77, 0x2a, 0x6e, 0x90, 0xc0, 0x4e, 0x9f, 0x87,
+    0x6b, 0xf4, 0x8d, 0x9c, 0x66, 0xe3, 0x0b, 0xd2, 0x10, 0x35, 0x21, 0xa8,
+    0x1d, 0xa2, 0x31, 0x17, 0xe7, 0x0c, 0xdf, 0x18, 0xf7, 0x94, 0xe4, 0xd1,
+    0xca, 0x32, 0x7d, 0xf2, 0x63, 0x23, 0x1d, 0xbc, 0x84, 0x74, 0x61, 0xdb,
+    0x87, 0xf2, 0xab, 0x72, 0xad, 0xaf, 0x08, 0xf8, 0xa2, 0x42, 0x04, 0x40,
+    0x45, 0x00, 0xe9, 0x5c, 0xbd, 0x00, 0x57, 0x04, 0x55, 0x87, 0x6c, 0xbd,
+    0x2f, 0xea, 0x41, 0x9c, 0x66, 0x42, 0x51, 0x41, 0xbb, 0x44, 0xed, 0x0e,
+    0xe9, 0x66, 0xcf, 0xd5, 0x10, 0x73, 0x0d, 0x4b, 0x48, 0xe4, 0x7a, 0x53,
+    0x35, 0x01, 0x0e, 0x6d, 0x15, 0x55, 0xc5, 0xb7, 0xd2, 0xd5, 0x36, 0xb6,
+    0xbc, 0x7e, 0xb0, 0xf3, 0x3d, 0xe6, 0x19, 0x78, 0x62, 0xeb, 0x02, 0x57,
+    0x39, 0x56, 0x73, 0x4f, 0xa3, 0x2a, 0x04, 0x28, 0x1b, 0x40, 0xc1, 0xa9,
+    0x77, 0x60, 0xeb, 0xc3, 0x67, 0xf0, 0x5f, 0x6a, 0xe1, 0x5e, 0x20, 0xc2,
+    0x51, 0x68, 0x4d, 0x82, 0x48, 0x8b, 0x03, 0x32, 0x16, 0x79, 0x88, 0x14,
+    0x37, 0x78, 0x7f, 0x16, 0x9a, 0x06, 0xfd, 0xc0, 0x8a, 0x15, 0x80, 0x62,
+    0xa4, 0x42, 0x04, 0x40, 0x22, 0x52, 0x60, 0x17, 0xef, 0x2c, 0xa1, 0xf6,
+    0xcb, 0xed, 0x39, 0xd5, 0xe2, 0xaa, 0x65, 0x20, 0xfb, 0xad, 0x82, 0x93,
+    0xe5, 0x78, 0x23, 0x22, 0x97, 0xc1, 0x6e, 0x6a, 0x4e, 0x36, 0xd7, 0x6a,
+    0x61, 0x39, 0x08, 0x21, 0xd4, 0xfe, 0x92, 0x5f, 0x36, 0x2d, 0xeb, 0x5d,
+    0xbb, 0x32, 0x8b, 0xe3, 0x94, 0x4f, 0xbe, 0x1b, 0x21, 0xf9, 0xcc, 0x23,
+    0x73, 0x41, 0xb6, 0xb9, 0xb6, 0x98, 0xd0, 0xbc, 0xa5, 0x43, 0x04, 0x41,
+    0x92, 0xd6, 0x97, 0xb3, 0x83, 0xdf, 0xe7, 0x8c, 0xc7, 0xbc, 0x4a, 0xfc,
+    0xea, 0x76, 0xc0, 0x53, 0x66, 0xbd, 0x2c, 0x1e, 0x10, 0x31, 0x90, 0x80,
+    0x11, 0x2d, 0x08, 0x4d, 0x7c, 0x39, 0x76, 0xdc, 0x73, 0xe7, 0x1c, 0x16,
+    0x62, 0xd5, 0x59, 0xd7, 0x49, 0x2b, 0x6a, 0xa2, 0x36, 0x67, 0x57, 0xd1,
+    0xf2, 0xf9, 0xaf, 0x13, 0xd7, 0xa3, 0xe4, 0xd3, 0x39, 0x5b, 0x02, 0x78,
+    0xb1, 0xe0, 0x09, 0x70, 0xa2, 0xa6, 0x03, 0x0a, 0x01, 0x00, 0x30, 0x0c,
+    0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04, 0x05, 0x00,
+    0x03, 0x49, 0x00, 0x30, 0x46, 0x02, 0x21, 0x00, 0xa9, 0x57, 0x7d, 0xac,
+    0xe0, 0x80, 0x8e, 0x1c, 0x7a, 0xe1, 0x4d, 0x7d, 0x2a, 0x52, 0xd2, 0x5e,
+    0xfb, 0x5a, 0x7d, 0x45, 0x1e, 0xfa, 0x67, 0x17, 0x04, 0x9e, 0xb3, 0x05,
+    0xc2, 0x34, 0xf0, 0x7a, 0x02, 0x21, 0x00, 0xb7, 0x3a, 0x31, 0x25, 0xae,
+    0x1d, 0x2b, 0x06, 0x8b, 0xba, 0x44, 0xd9, 0x3a, 0xbf, 0x8b, 0xe2, 0x3f,
+    0x89, 0xe2, 0xa6, 0x14, 0x0c, 0xfa, 0xc2, 0xb1, 0xdd, 0x0c, 0x0a, 0x78,
+    0xac, 0x60, 0x19};
+
+constexpr uint8_t kExpectedX509P384Cert_DescriptorInput[0] = {};
 
 constexpr uint8_t kExpectedCborEd25519Cert_DescriptorInput[667] = {
     0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x02, 0x50, 0xab, 0x01, 0x78,
@@ -976,6 +1081,72 @@
 
 constexpr uint8_t kExpectedCborP256Cert_DescriptorInput[0] = {};
 
+constexpr uint8_t kExpectedCborP384Cert_DescriptorInput[768] = {
+    0x84, 0x44, 0xa1, 0x01, 0x38, 0x22, 0xa0, 0x59, 0x02, 0x94, 0xab, 0x01,
+    0x78, 0x28, 0x35, 0x64, 0x38, 0x62, 0x36, 0x62, 0x65, 0x37, 0x63, 0x65,
+    0x33, 0x65, 0x64, 0x65, 0x36, 0x61, 0x32, 0x34, 0x31, 0x38, 0x30, 0x31,
+    0x34, 0x35, 0x32, 0x33, 0x65, 0x36, 0x63, 0x39, 0x64, 0x63, 0x38, 0x37,
+    0x65, 0x39, 0x38, 0x63, 0x63, 0x36, 0x02, 0x78, 0x28, 0x36, 0x66, 0x31,
+    0x33, 0x39, 0x63, 0x37, 0x62, 0x32, 0x62, 0x31, 0x36, 0x61, 0x63, 0x38,
+    0x31, 0x30, 0x32, 0x34, 0x64, 0x35, 0x37, 0x34, 0x39, 0x36, 0x62, 0x31,
+    0x62, 0x37, 0x61, 0x31, 0x33, 0x66, 0x64, 0x33, 0x65, 0x38, 0x30, 0x37,
+    0x66, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0xb7, 0xd4, 0x0c, 0xcb,
+    0x22, 0x5b, 0xa5, 0x78, 0x8f, 0x98, 0xff, 0x9e, 0x86, 0x93, 0x75, 0xf6,
+    0x90, 0xac, 0x50, 0xcf, 0x9e, 0xbd, 0x0a, 0xfe, 0xb1, 0xd9, 0xc2, 0x4e,
+    0x52, 0x19, 0xe4, 0xde, 0x29, 0xe5, 0x61, 0xf3, 0xf9, 0x29, 0xe8, 0x40,
+    0x87, 0x7a, 0xdd, 0x17, 0x48, 0x05, 0x89, 0x7e, 0x2b, 0xcb, 0x54, 0x79,
+    0xcc, 0x66, 0xf1, 0xb3, 0x13, 0x29, 0x0c, 0x68, 0x96, 0xb2, 0xbb, 0x8f,
+    0x3a, 0x00, 0x47, 0x44, 0x51, 0x58, 0x64, 0x6c, 0x46, 0x01, 0x33, 0x26,
+    0x73, 0x4b, 0x22, 0x65, 0xfd, 0xfa, 0x58, 0xd7, 0x57, 0x3e, 0x95, 0x59,
+    0xe0, 0x3a, 0xc3, 0xb9, 0xf7, 0xc8, 0x0e, 0x98, 0x80, 0x8c, 0xf5, 0xc4,
+    0xb8, 0xaf, 0xe3, 0x16, 0x84, 0x25, 0xa5, 0x35, 0x5d, 0x17, 0x72, 0x56,
+    0x8f, 0x8e, 0xec, 0x2f, 0x5a, 0x74, 0x60, 0x77, 0x2a, 0x6e, 0x90, 0xc0,
+    0x4e, 0x9f, 0x87, 0x6b, 0xf4, 0x8d, 0x9c, 0x66, 0xe3, 0x0b, 0xd2, 0x10,
+    0x35, 0x21, 0xa8, 0x1d, 0xa2, 0x31, 0x17, 0xe7, 0x0c, 0xdf, 0x18, 0xf7,
+    0x94, 0xe4, 0xd1, 0xca, 0x32, 0x7d, 0xf2, 0x63, 0x23, 0x1d, 0xbc, 0x84,
+    0x74, 0x61, 0xdb, 0x87, 0xf2, 0xab, 0x72, 0xad, 0xaf, 0x08, 0xf8, 0x3a,
+    0x00, 0x47, 0x44, 0x53, 0x58, 0x28, 0x1b, 0x40, 0xc1, 0xa9, 0x77, 0x60,
+    0xeb, 0xc3, 0x67, 0xf0, 0x5f, 0x6a, 0xe1, 0x5e, 0x20, 0xc2, 0x51, 0x68,
+    0x4d, 0x82, 0x48, 0x8b, 0x03, 0x32, 0x16, 0x79, 0x88, 0x14, 0x37, 0x78,
+    0x7f, 0x16, 0x9a, 0x06, 0xfd, 0xc0, 0x8a, 0x15, 0x80, 0x62, 0x3a, 0x00,
+    0x47, 0x44, 0x52, 0x58, 0x40, 0x45, 0x00, 0xe9, 0x5c, 0xbd, 0x00, 0x57,
+    0x04, 0x55, 0x87, 0x6c, 0xbd, 0x2f, 0xea, 0x41, 0x9c, 0x66, 0x42, 0x51,
+    0x41, 0xbb, 0x44, 0xed, 0x0e, 0xe9, 0x66, 0xcf, 0xd5, 0x10, 0x73, 0x0d,
+    0x4b, 0x48, 0xe4, 0x7a, 0x53, 0x35, 0x01, 0x0e, 0x6d, 0x15, 0x55, 0xc5,
+    0xb7, 0xd2, 0xd5, 0x36, 0xb6, 0xbc, 0x7e, 0xb0, 0xf3, 0x3d, 0xe6, 0x19,
+    0x78, 0x62, 0xeb, 0x02, 0x57, 0x39, 0x56, 0x73, 0x4f, 0x3a, 0x00, 0x47,
+    0x44, 0x54, 0x58, 0x40, 0x22, 0x52, 0x60, 0x17, 0xef, 0x2c, 0xa1, 0xf6,
+    0xcb, 0xed, 0x39, 0xd5, 0xe2, 0xaa, 0x65, 0x20, 0xfb, 0xad, 0x82, 0x93,
+    0xe5, 0x78, 0x23, 0x22, 0x97, 0xc1, 0x6e, 0x6a, 0x4e, 0x36, 0xd7, 0x6a,
+    0x61, 0x39, 0x08, 0x21, 0xd4, 0xfe, 0x92, 0x5f, 0x36, 0x2d, 0xeb, 0x5d,
+    0xbb, 0x32, 0x8b, 0xe3, 0x94, 0x4f, 0xbe, 0x1b, 0x21, 0xf9, 0xcc, 0x23,
+    0x73, 0x41, 0xb6, 0xb9, 0xb6, 0x98, 0xd0, 0xbc, 0x3a, 0x00, 0x47, 0x44,
+    0x55, 0x58, 0x41, 0x92, 0xd6, 0x97, 0xb3, 0x83, 0xdf, 0xe7, 0x8c, 0xc7,
+    0xbc, 0x4a, 0xfc, 0xea, 0x76, 0xc0, 0x53, 0x66, 0xbd, 0x2c, 0x1e, 0x10,
+    0x31, 0x90, 0x80, 0x11, 0x2d, 0x08, 0x4d, 0x7c, 0x39, 0x76, 0xdc, 0x73,
+    0xe7, 0x1c, 0x16, 0x62, 0xd5, 0x59, 0xd7, 0x49, 0x2b, 0x6a, 0xa2, 0x36,
+    0x67, 0x57, 0xd1, 0xf2, 0xf9, 0xaf, 0x13, 0xd7, 0xa3, 0xe4, 0xd3, 0x39,
+    0x5b, 0x02, 0x78, 0xb1, 0xe0, 0x09, 0x70, 0xa2, 0x3a, 0x00, 0x47, 0x44,
+    0x56, 0x41, 0x00, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x71, 0xa6, 0x01,
+    0x02, 0x03, 0x38, 0x22, 0x04, 0x81, 0x02, 0x20, 0x02, 0x21, 0x58, 0x30,
+    0xb4, 0x02, 0x19, 0x48, 0xca, 0xdd, 0x23, 0x4b, 0x92, 0x91, 0x22, 0x8d,
+    0xa8, 0x80, 0x85, 0xc0, 0xf9, 0x23, 0xe4, 0x89, 0xbd, 0x91, 0x8d, 0xf3,
+    0x8f, 0xa3, 0x73, 0x60, 0x70, 0x19, 0xc6, 0x33, 0x76, 0xbf, 0xd4, 0x60,
+    0xfa, 0xdc, 0xde, 0x46, 0x58, 0x51, 0x13, 0x1d, 0x73, 0x81, 0x79, 0xff,
+    0x22, 0x58, 0x30, 0x39, 0x79, 0x1b, 0x49, 0x6a, 0xcf, 0x37, 0x8f, 0xf4,
+    0x1a, 0xc2, 0x29, 0xb5, 0x80, 0x2f, 0x7b, 0x2b, 0x0a, 0x27, 0x96, 0xb3,
+    0xb2, 0xc1, 0xc6, 0x6f, 0xb4, 0x16, 0xa4, 0x78, 0x76, 0x73, 0x6f, 0xcb,
+    0xf5, 0x7d, 0x26, 0xc2, 0x37, 0xe9, 0x58, 0x98, 0xeb, 0xef, 0x11, 0x7c,
+    0x8d, 0x1d, 0x4b, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x60,
+    0x12, 0xa5, 0xb9, 0xc3, 0xbf, 0x06, 0x10, 0x7d, 0x22, 0x4b, 0xc1, 0xd2,
+    0x58, 0xce, 0xd8, 0x4c, 0x42, 0x48, 0x69, 0x58, 0xf0, 0x83, 0x4a, 0x54,
+    0x39, 0x90, 0xec, 0x10, 0xef, 0x76, 0x2e, 0xb0, 0x33, 0x82, 0x6f, 0x93,
+    0x2b, 0xc3, 0xb2, 0xc4, 0x6a, 0xcb, 0xde, 0x4c, 0x21, 0x62, 0x56, 0x07,
+    0xc7, 0xb1, 0xd6, 0xde, 0xff, 0x95, 0x2c, 0xce, 0xbf, 0x3c, 0x0b, 0xdc,
+    0xd0, 0xc5, 0x39, 0x6d, 0xc3, 0x59, 0x23, 0x7b, 0x54, 0x11, 0xf4, 0xb1,
+    0x33, 0x66, 0x94, 0x47, 0x3c, 0x14, 0x99, 0x0a, 0xca, 0xa9, 0x84, 0xf4,
+    0xc9, 0x24, 0xfd, 0x11, 0xe7, 0xcc, 0x37, 0x6f, 0x12, 0x2b, 0x1a, 0xa7};
+
 }  // namespace test
 }  // namespace dice
 
diff --git a/include/dice/test_framework.h b/include/dice/test_framework.h
index 16e1c90..d85ba2b 100644
--- a/include/dice/test_framework.h
+++ b/include/dice/test_framework.h
@@ -15,10 +15,11 @@
 #ifndef DICE_TEST_FRAMEWORK_H_
 #define DICE_TEST_FRAMEWORK_H_
 
-#ifdef DICE_USE_GTEST
 #include "gtest/gtest.h"
-#else
-#include "pw_unit_test/framework.h"
+
+#ifndef DICE_USE_GTEST
+// Use pigweed's pw_unit_test::light framework instead of upstream gtest.
+#include "pw_unit_test/simple_printing_event_handler.h"
 #endif
 
 #endif  // DICE_TEST_FRAMEWORK_H_
diff --git a/include/dice/test_utils.h b/include/dice/test_utils.h
index 73383d4..afc400a 100644
--- a/include/dice/test_utils.h
+++ b/include/dice/test_utils.h
@@ -30,6 +30,7 @@
 enum KeyType {
   KeyType_Ed25519,
   KeyType_P256,
+  KeyType_P384,
 };
 
 struct DiceStateForTest {
@@ -56,7 +57,13 @@
 
 // Verifies a chain of CDI certificates given by |states| against
 // |root_certificate|. If |is_partial_chain| is set, then root_certificate does
-// not need to be self signed.
+// not need to be self signed. For X.509 certificate chains, only the standard
+// certificate fields and extensions are checked, other custom extensions are
+// ignored even if marked critical. For this reason, additional tests are needed
+// to fully verify a certificate chain, this is just useful for checking that a
+// chain is correctly constructed in terms of standard fields. Similarly for
+// CBOR certificate chains the chaining construction is verified but the content
+// of other fields is ignored.
 bool VerifyCertificateChain(CertificateType cert_type,
                             const uint8_t* root_certificate,
                             size_t root_certificate_size,
diff --git a/rules.mk b/rules.mk
index ff5b942..f76d557 100644
--- a/rules.mk
+++ b/rules.mk
@@ -23,6 +23,8 @@
 	$(LOCAL_DIR)/src/boringssl_hash_kdf_ops.c \
 	$(LOCAL_DIR)/src/boringssl_ed25519_ops.c \
 	$(LOCAL_DIR)/src/cbor_cert_op.c \
+	$(LOCAL_DIR)/src/cbor_ed25519_cert_op.c \
+	$(LOCAL_DIR)/src/cbor_reader.c \
 	$(LOCAL_DIR)/src/cbor_writer.c \
 	$(LOCAL_DIR)/src/clear_memory.c \
 	$(LOCAL_DIR)/src/dice.c \
diff --git a/rust/android/bcc.h b/rust/android/bcc.h
deleted file mode 100644
index 4dfc862..0000000
--- a/rust/android/bcc.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2021 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.
-
-#pragma once
-
-#include <dice/android/bcc.h>
diff --git a/src/android/BUILD.gn b/src/android/BUILD.gn
index dd8bdbc..b9b9574 100644
--- a/src/android/BUILD.gn
+++ b/src/android/BUILD.gn
@@ -21,24 +21,16 @@
   deps = [
     "//:cbor_reader",
     "//:cbor_writer",
-    "//:dice_with_cbor_cert",
+    "//:dice_with_cbor_ed25519_cert",
   ]
 }
 
 pw_test("bcc_test") {
-  sources = [
-    "bcc_test.cc",
-  ]
-  deps = [
-    ":bcc",
-  ]
+  sources = [ "bcc_test.cc" ]
+  deps = [ ":bcc" ]
 }
 
 pw_executable("bcc_fuzzer") {
-  sources = [
-    "bcc_fuzzer.cc",
-  ]
-  deps = [
-    ":bcc",
-  ]
+  sources = [ "bcc_fuzzer.cc" ]
+  deps = [ ":bcc" ]
 }
diff --git a/src/android/bcc.c b/src/android/bcc.c
index 8855f71..5dd8620 100644
--- a/src/android/bcc.c
+++ b/src/android/bcc.c
@@ -192,60 +192,28 @@
   return result;
 }
 
+static const int64_t kCdiAttestLabel = 1;
+static const int64_t kCdiSealLabel = 2;
+static const int64_t kBccLabel = 3;
+
 DiceResult BccHandoverMainFlow(void* context, const uint8_t* bcc_handover,
                                size_t bcc_handover_size,
                                const DiceInputValues* input_values,
                                size_t buffer_size, uint8_t* buffer,
                                size_t* actual_size) {
-  static const int64_t kCdiAttestLabel = 1;
-  static const int64_t kCdiSealLabel = 2;
-  static const int64_t kBccLabel = 3;
-
   DiceResult result;
   const uint8_t* current_cdi_attest;
   const uint8_t* current_cdi_seal;
   const uint8_t* bcc;
+  size_t bcc_size;
 
-  // Extract details from the handover data.
-  //
-  // BccHandover = {
-  //   1 : bstr .size 32,     ; CDI_Attest
-  //   2 : bstr .size 32,     ; CDI_Seal
-  //   ? 3 : Bcc,             ; Certificate chain
-  // }
-  struct CborIn in;
-  int64_t label;
-  size_t item_size;
-  CborInInit(bcc_handover, bcc_handover_size, &in);
-  if (CborReadMap(&in, &item_size) != CBOR_READ_RESULT_OK || item_size < 2 ||
-      // Read the attestation CDI.
-      CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
-      label != kCdiAttestLabel ||
-      CborReadBstr(&in, &item_size, &current_cdi_attest) !=
-          CBOR_READ_RESULT_OK ||
-      item_size != DICE_CDI_SIZE ||
-      // Read the sealing CDI.
-      CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
-      label != kCdiSealLabel ||
-      CborReadBstr(&in, &item_size, &current_cdi_seal) != CBOR_READ_RESULT_OK ||
-      item_size != DICE_CDI_SIZE) {
+  result =
+      BccHandoverParse(bcc_handover, bcc_handover_size, &current_cdi_attest,
+                       &current_cdi_seal, &bcc, &bcc_size);
+  if (result != kDiceResultOk) {
     return kDiceResultInvalidInput;
   }
 
-  size_t bcc_size = 0;
-  // Calculate the BCC size, if the BCC is present in the BccHandover.
-  if (CborReadInt(&in, &label) == CBOR_READ_RESULT_OK) {
-    if (label != kBccLabel) {
-      return kDiceResultInvalidInput;
-    }
-    size_t bcc_start = CborInOffset(&in);
-    bcc = bcc_handover + bcc_start;
-    if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
-      return kDiceResultInvalidInput;
-    }
-    bcc_size = CborInOffset(&in) - bcc_start;
-  }
-
   // Write the new handover data.
   struct CborOut out;
   CborOutInit(buffer, buffer_size, &out);
@@ -281,3 +249,51 @@
   *actual_size = CborOutSize(&out) + bcc_size;
   return kDiceResultOk;
 }
+
+DiceResult BccHandoverParse(const uint8_t* bcc_handover,
+                            size_t bcc_handover_size,
+                            const uint8_t** cdi_attest,
+                            const uint8_t** cdi_seal, const uint8_t** bcc,
+                            size_t* bcc_size) {
+  // Extract details from the handover data.
+  //
+  // BccHandover = {
+  //   1 : bstr .size 32,     ; CDI_Attest
+  //   2 : bstr .size 32,     ; CDI_Seal
+  //   ? 3 : Bcc,             ; Certificate chain
+  // }
+  struct CborIn in;
+  int64_t label;
+  size_t num_pairs;
+  size_t item_size;
+  CborInInit(bcc_handover, bcc_handover_size, &in);
+  if (CborReadMap(&in, &num_pairs) != CBOR_READ_RESULT_OK || num_pairs < 2 ||
+      // Read the attestation CDI.
+      CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
+      label != kCdiAttestLabel ||
+      CborReadBstr(&in, &item_size, cdi_attest) != CBOR_READ_RESULT_OK ||
+      item_size != DICE_CDI_SIZE ||
+      // Read the sealing CDI.
+      CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
+      label != kCdiSealLabel ||
+      CborReadBstr(&in, &item_size, cdi_seal) != CBOR_READ_RESULT_OK ||
+      item_size != DICE_CDI_SIZE) {
+    return kDiceResultInvalidInput;
+  }
+
+  *bcc = NULL;
+  *bcc_size = 0;
+  if (num_pairs >= 3 && CborReadInt(&in, &label) == CBOR_READ_RESULT_OK) {
+    if (label == kBccLabel) {
+      // Calculate the BCC size, if the BCC is present in the BccHandover.
+      size_t bcc_start = CborInOffset(&in);
+      if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
+        return kDiceResultInvalidInput;
+      }
+      *bcc = bcc_handover + bcc_start;
+      *bcc_size = CborInOffset(&in) - bcc_start;
+    }
+  }
+
+  return kDiceResultOk;
+}
diff --git a/src/android/bcc_test.cc b/src/android/bcc_test.cc
index 44dce5b..0c45e06 100644
--- a/src/android/bcc_test.cc
+++ b/src/android/bcc_test.cc
@@ -115,7 +115,7 @@
                       sizeof(bcc_handover) - 8 - 73));
 }
 
-TEST(BccHandoverNoCertTest, InHandoverWithoutBccOutHandoverWithBcc) {
+TEST(BccHandoverTest, InHandoverWithoutBccOutHandoverWithBcc) {
   const uint8_t bcc_handover[] = {
       0xa2,
       // CDI attest
@@ -126,7 +126,8 @@
       0x02, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  };
+      // 8-bytes of trailing data that aren't part of the BCC.
+      0x00, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40};
   DiceInputValues input_values = {};
   uint8_t next_bcc_handover[1024] = {};
   size_t next_bcc_handover_size;
@@ -137,6 +138,137 @@
   EXPECT_GT(next_bcc_handover_size, sizeof(bcc_handover));
   EXPECT_EQ(0xa3, next_bcc_handover[0]);
 }
+
+TEST(BccHandoverTest, InHandoverWithoutBccButUnknownFieldOutHandoverWithBcc) {
+  const uint8_t bcc_handover[] = {
+      0xa3,
+      // CDI attest
+      0x01, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // CDI seal
+      0x02, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // Ignored unknown field
+      0x04, 0x01,
+      // 8-bytes of trailing data that aren't part of the BCC.
+      0x00, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40};
+  DiceInputValues input_values = {};
+  uint8_t next_bcc_handover[1024] = {};
+  size_t next_bcc_handover_size;
+  DiceResult result = BccHandoverMainFlow(
+      /*context=*/NULL, bcc_handover, sizeof(bcc_handover), &input_values,
+      sizeof(next_bcc_handover), next_bcc_handover, &next_bcc_handover_size);
+  EXPECT_EQ(kDiceResultOk, result);
+  EXPECT_GT(next_bcc_handover_size, sizeof(bcc_handover));
+  EXPECT_EQ(0xa3, next_bcc_handover[0]);
+}
+
+TEST(BccHandoverTest, ParseHandover) {
+  const uint8_t bcc_handover[] = {
+      0xa3,
+      // CDI attest
+      0x01, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // CDI seal
+      0x02, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // BCC
+      0x03, 0x82, 0xa6, 0x01, 0x02, 0x03, 0x27, 0x04, 0x02, 0x20, 0x01, 0x21,
+      0x40, 0x22, 0x40, 0x84, 0x40, 0xa0, 0x40, 0x40,
+      // 8-bytes of trailing data that aren't part of the BCC.
+      0x00, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40};
+  const uint8_t *cdi_attest;
+  const uint8_t *cdi_seal;
+  const uint8_t *bcc;
+  size_t bcc_size;
+  DiceResult result = BccHandoverParse(bcc_handover, sizeof(bcc_handover),
+                                       &cdi_attest, &cdi_seal, &bcc, &bcc_size);
+  EXPECT_EQ(kDiceResultOk, result);
+  EXPECT_EQ(bcc_handover + 4, cdi_attest);
+  EXPECT_EQ(bcc_handover + 39, cdi_seal);
+  EXPECT_EQ(bcc_handover + 72, bcc);
+  EXPECT_EQ(19u, bcc_size);
+}
+
+TEST(BccHandoverTest, ParseHandoverWithoutBcc) {
+  const uint8_t bcc_handover[] = {
+      0xa2,
+      // CDI attest
+      0x01, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // CDI seal
+      0x02, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // 8-bytes of trailing data that aren't part of the BCC.
+      0x00, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40};
+  const uint8_t *cdi_attest;
+  const uint8_t *cdi_seal;
+  const uint8_t *bcc;
+  size_t bcc_size;
+  DiceResult result = BccHandoverParse(bcc_handover, sizeof(bcc_handover),
+                                       &cdi_attest, &cdi_seal, &bcc, &bcc_size);
+  EXPECT_EQ(kDiceResultOk, result);
+  EXPECT_EQ(bcc_handover + 4, cdi_attest);
+  EXPECT_EQ(bcc_handover + 39, cdi_seal);
+  EXPECT_EQ(nullptr, bcc);
+  EXPECT_EQ(0u, bcc_size);
+}
+
+TEST(BccHandoverTest, ParseHandoverWithoutBccButUnknownField) {
+  const uint8_t bcc_handover[] = {
+      0xa3,
+      // CDI attest
+      0x01, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // CDI seal
+      0x02, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // Ignored unknown field
+      0x04, 0x01,
+      // 8-bytes of trailing data that aren't part of the BCC.
+      0x00, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40};
+  const uint8_t *cdi_attest;
+  const uint8_t *cdi_seal;
+  const uint8_t *bcc;
+  size_t bcc_size;
+  DiceResult result = BccHandoverParse(bcc_handover, sizeof(bcc_handover),
+                                       &cdi_attest, &cdi_seal, &bcc, &bcc_size);
+  EXPECT_EQ(kDiceResultOk, result);
+  EXPECT_EQ(bcc_handover + 4, cdi_attest);
+  EXPECT_EQ(bcc_handover + 39, cdi_seal);
+  EXPECT_EQ(nullptr, bcc);
+  EXPECT_EQ(0u, bcc_size);
+}
+
+TEST(BccHandoverTest, ParseHandoverCdiTooLarge) {
+  const uint8_t bcc_handover[] = {
+      0xa2,
+      // CDI attest
+      0x01, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // CDI seal
+      0x02, 0x58, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // 8-bytes of trailing data that aren't part of the BCC.
+      0x00, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40};
+  const uint8_t *cdi_attest;
+  const uint8_t *cdi_seal;
+  const uint8_t *bcc;
+  size_t bcc_size;
+  DiceResult result = BccHandoverParse(bcc_handover, sizeof(bcc_handover),
+                                       &cdi_attest, &cdi_seal, &bcc, &bcc_size);
+  EXPECT_EQ(kDiceResultInvalidInput, result);
+}
 }
 
 }  // namespace
diff --git a/src/boringssl_cert_op.c b/src/boringssl_cert_op.c
index 08315de..432294c 100644
--- a/src/boringssl_cert_op.c
+++ b/src/boringssl_cert_op.c
@@ -475,7 +475,7 @@
   }
 
   extension =
-      X509_EXTENSION_create_by_OBJ(/*ex=*/NULL, oid, /*crit=*/0, octets);
+      X509_EXTENSION_create_by_OBJ(/*ex=*/NULL, oid, /*crit=*/1, octets);
   if (!extension) {
     result = kDiceResultPlatformError;
     goto out;
diff --git a/src/boringssl_ecdsa_utils.c b/src/boringssl_ecdsa_utils.c
new file mode 100644
index 0000000..9513cd4
--- /dev/null
+++ b/src/boringssl_ecdsa_utils.c
@@ -0,0 +1,297 @@
+// 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
+//
+//     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.
+
+// This is an implementation of the crypto operations that uses boringssl. The
+// algorithms used are SHA512, HKDF-SHA512, and ECDSA P384-SHA384.
+
+#include "dice/boringssl_ecdsa_utils.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "openssl/bn.h"
+#include "openssl/crypto.h"
+#include "openssl/ec.h"
+#include "openssl/ec_key.h"
+#include "openssl/ecdsa.h"
+#include "openssl/evp.h"
+#include "openssl/hkdf.h"
+#include "openssl/hmac.h"
+#include "openssl/is_boringssl.h"
+#include "openssl/sha.h"
+
+static int hmac(uint8_t k[64], uint8_t in[64], uint8_t *out,
+                unsigned int out_len) {
+  int ret = 0;
+
+  if (out_len > 64 || out_len < 0) {
+    goto out;
+  }
+  HMAC_CTX ctx;
+  HMAC_CTX_init(&ctx);
+  if (1 != HMAC_Init_ex(&ctx, k, 64, EVP_sha512(), NULL /* impl */)) {
+    goto out;
+  }
+  if (1 != HMAC_Update(&ctx, in, 64)) {
+    goto out;
+  }
+  ret = HMAC_Final(&ctx, out, &out_len);
+  HMAC_CTX_cleanup(&ctx);
+
+out:
+  return ret;
+}
+
+static int hmac3(uint8_t k[64], uint8_t in1[64], uint8_t in2,
+                  const uint8_t *in3, unsigned int in3_len, uint8_t out[64]) {
+  int ret = 0;
+
+  HMAC_CTX ctx;
+  HMAC_CTX_init(&ctx);
+  if (1 != HMAC_Init_ex(&ctx, k, 64, EVP_sha512(), NULL /* impl */)) {
+    goto out;
+  }
+  if (1 != HMAC_Update(&ctx, in1, 64)) {
+    goto out;
+  }
+  if (1 != HMAC_Update(&ctx, &in2, 1)) {
+    goto out;
+  }
+  if (in3 != NULL && in3_len > 0) {
+    if (1 != HMAC_Update(&ctx, in3, in3_len)) {
+      goto out;
+    }
+  }
+  unsigned int out_len = 64;
+  ret = HMAC_Final(&ctx, out, &out_len);
+  HMAC_CTX_cleanup(&ctx);
+
+out:
+  return ret;
+}
+
+// Algorithm from section 3.2 of IETF RFC6979
+static BIGNUM *derivePrivateKey(const EC_GROUP *group, const uint8_t *seed,
+                                size_t seed_size, uint8_t *private_key,
+                                size_t private_key_len) {
+  BIGNUM *candidate = NULL;
+  uint8_t v[64];
+  uint8_t k[64];
+  memset(v, 1, 64);
+  memset(k, 0, 64);
+
+  if (1 != hmac3(k, v, 0x00, seed, (unsigned int)seed_size, k)) {
+    goto err;
+  }
+  if (1 != hmac(k, v, v, sizeof(v))) {
+    goto err;
+  }
+  if (1 != hmac3(k, v, 0x01, seed, (unsigned int)seed_size, k)) {
+    goto err;
+  }
+  do {
+    if (1 != hmac(k, v, v, sizeof(v))) {
+      goto err;
+    }
+    if (1 != hmac(k, v, private_key, private_key_len)) {
+      goto err;
+    }
+    if (1 != hmac3(k, v, 0x00, NULL, 0, k)) {
+      goto err;
+    }
+    candidate = BN_bin2bn(private_key, private_key_len, NULL);
+    if (!candidate) {
+      goto err;
+    }
+  } while (BN_cmp(candidate, EC_GROUP_get0_order(group)) >= 0 ||
+           BN_is_zero(candidate));
+  goto out;
+
+err:
+  BN_clear_free(candidate);
+  candidate = NULL;
+out:
+  return candidate;
+}
+
+int P384KeypairFromSeed(uint8_t public_key[P384_PUBLIC_KEY_SIZE],
+                        uint8_t private_key[P384_PRIVATE_KEY_SIZE],
+                        const uint8_t seed[DICE_PRIVATE_KEY_SEED_SIZE]) {
+  int ret = 0;
+  EC_POINT *publicKey = NULL;
+  BIGNUM *pD = NULL;
+  BIGNUM *x = NULL;
+  BIGNUM *y = NULL;
+
+  EC_KEY *key = EC_KEY_new_by_curve_name(NID_secp384r1);
+  if (!key) {
+    goto out;
+  }
+  const EC_GROUP *group = EC_KEY_get0_group(key);
+  if (!group) {
+    goto out;
+  }
+  publicKey = EC_POINT_new(group);
+  if (!publicKey) {
+    goto out;
+  }
+
+  pD = derivePrivateKey(group, seed, DICE_PRIVATE_KEY_SEED_SIZE, private_key,
+                                P384_PRIVATE_KEY_SIZE);
+  if (!pD) {
+    goto out;
+  }
+  if (1 != BN_bn2bin_padded(private_key, P384_PRIVATE_KEY_SIZE, pD)) {
+    goto out;
+  }
+  if (1 != EC_KEY_set_private_key(key, pD)) {
+    goto out;
+  }
+  if (1 != EC_POINT_mul(group, publicKey, pD, NULL, NULL, NULL)) {
+    goto out;
+  }
+  x = BN_new();
+  if (!x) {
+    goto out;
+  }
+  y = BN_new();
+  if (!y) {
+    goto out;
+  }
+  if (1 != EC_POINT_get_affine_coordinates_GFp(group, publicKey, x, y, NULL)) {
+    goto out;
+  }
+  if (BN_num_bytes(x) > P384_PRIVATE_KEY_SIZE) {
+    goto out;
+  }
+  BN_bn2bin(x, &public_key[0]);
+  if (BN_num_bytes(y) > P384_PRIVATE_KEY_SIZE) {
+    goto out;
+  }
+  BN_bn2bin(y, &public_key[P384_PRIVATE_KEY_SIZE]);
+  ret = 1;
+
+out:
+  EC_POINT_free(publicKey);
+  BN_clear_free(x);
+  BN_clear_free(y);
+  EC_KEY_free(key);
+  BN_clear_free(pD);
+
+  return ret;
+}
+
+int P384Sign(uint8_t signature[P384_SIGNATURE_SIZE], const uint8_t *message,
+             size_t message_size,
+             const uint8_t private_key[P384_PRIVATE_KEY_SIZE]) {
+  int ret = 0;
+  BIGNUM *pD = NULL;
+  EC_KEY *key = NULL;
+  uint8_t output[48];
+  ECDSA_SIG *sig = NULL;
+
+  pD = BN_bin2bn(private_key, P384_PRIVATE_KEY_SIZE, NULL);
+  if (!pD) {
+    goto out;
+  }
+  key = EC_KEY_new_by_curve_name(NID_secp384r1);
+  if (!key) {
+    goto out;
+  }
+  if (1 != EC_KEY_set_private_key(key, pD)) {
+    goto out;
+  }
+  SHA384(message, message_size, output);
+  sig = ECDSA_do_sign(output, 48, key);
+  if (!sig) {
+    goto out;
+  }
+
+  if (BN_num_bytes(sig->r) > P384_PRIVATE_KEY_SIZE) {
+    goto out;
+  }
+  BN_bn2bin(sig->r, &signature[0]);
+  if (BN_num_bytes(sig->s) > P384_PRIVATE_KEY_SIZE) {
+    goto out;
+  }
+  BN_bn2bin(sig->s, &signature[P384_PRIVATE_KEY_SIZE]);
+  ret = 1;
+
+out:
+  EC_KEY_free(key);
+  BN_clear_free(pD);
+  ECDSA_SIG_free(sig);
+  return ret;
+}
+
+int P384Verify(const uint8_t *message, size_t message_size,
+               const uint8_t signature[P384_SIGNATURE_SIZE],
+               const uint8_t public_key[P384_PUBLIC_KEY_SIZE]) {
+  int ret = 0;
+  uint8_t output[48];
+  EC_KEY *key = NULL;
+  BIGNUM *bn_ret = NULL;
+  BIGNUM *x = NULL;
+  BIGNUM *y = NULL;
+  ECDSA_SIG *sig = NULL;
+
+  SHA384(message, message_size, output);
+  key = EC_KEY_new_by_curve_name(NID_secp384r1);
+  if (!key) {
+    goto out;
+  }
+  x = BN_new();
+  if (!x) {
+    goto out;
+  }
+  bn_ret = BN_bin2bn(&public_key[0], P384_PUBLIC_KEY_SIZE / 2, x);
+  if (!bn_ret) {
+    goto out;
+  }
+  y = BN_new();
+  if (!y) {
+    goto out;
+  }
+  bn_ret = BN_bin2bn(&public_key[P384_PUBLIC_KEY_SIZE / 2], P384_PUBLIC_KEY_SIZE / 2, y);
+  if (!bn_ret) {
+    goto out;
+  }
+  if (1 != EC_KEY_set_public_key_affine_coordinates(key, x, y)) {
+    goto out;
+  }
+
+
+  sig = ECDSA_SIG_new();
+  if (!sig) {
+    goto out;
+  }
+  bn_ret = BN_bin2bn(&signature[0], P384_SIGNATURE_SIZE / 2, sig->r);
+  if (!bn_ret) {
+    goto out;
+  }
+  bn_ret = BN_bin2bn(&signature[P384_SIGNATURE_SIZE / 2], P384_SIGNATURE_SIZE / 2,
+            sig->s);
+  if (!bn_ret) {
+    goto out;
+  }
+  ret = ECDSA_do_verify(output, 48, sig, key);
+
+out:
+  BN_clear_free(y);
+  BN_clear_free(x);
+  EC_KEY_free(key);
+  ECDSA_SIG_free(sig);
+  return ret;
+}
diff --git a/src/boringssl_ops_test.cc b/src/boringssl_ed25519_ops_test.cc
similarity index 99%
rename from src/boringssl_ops_test.cc
rename to src/boringssl_ed25519_ops_test.cc
index 971ad0b..adb6f10 100644
--- a/src/boringssl_ops_test.cc
+++ b/src/boringssl_ed25519_ops_test.cc
@@ -137,7 +137,7 @@
 }
 
 TEST(DiceOpsTest, NonZeroMode) {
-  constexpr size_t kModeOffsetInCert = 0x230;
+  constexpr size_t kModeOffsetInCert = 0x233;
   DiceStateForTest current_state = {};
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
diff --git a/src/boringssl_p384_ops.c b/src/boringssl_p384_ops.c
new file mode 100644
index 0000000..ca5e88b
--- /dev/null
+++ b/src/boringssl_p384_ops.c
@@ -0,0 +1,69 @@
+// 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
+//
+//     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.
+
+// This is an implementation of the crypto operations that uses boringssl. The
+// algorithms used are SHA512, HKDF-SHA512, and Ed25519-SHA512.
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "dice/boringssl_ecdsa_utils.h"
+#include "dice/dice.h"
+#include "dice/ops.h"
+
+#if DICE_PRIVATE_KEY_SEED_SIZE != 32
+#error "Private key seed is expected to be 32 bytes."
+#endif
+#if DICE_PUBLIC_KEY_SIZE != 96
+#error "This P-384 implementation needs 96 bytes to store the public key."
+#endif
+#if DICE_PRIVATE_KEY_SIZE != 48
+#error "P-384 needs 48 bytes for the private key."
+#endif
+#if DICE_SIGNATURE_SIZE != 96
+#error "P-384 needs 96 bytes to store the signature."
+#endif
+
+DiceResult DiceKeypairFromSeed(void* context_not_used,
+                               const uint8_t seed[DICE_PRIVATE_KEY_SEED_SIZE],
+                               uint8_t public_key[DICE_PUBLIC_KEY_SIZE],
+                               uint8_t private_key[DICE_PRIVATE_KEY_SIZE]) {
+  (void)context_not_used;
+  if (1 == P384KeypairFromSeed(public_key, private_key, seed)) {
+    return kDiceResultOk;
+  }
+  return kDiceResultPlatformError;
+}
+
+DiceResult DiceSign(void* context_not_used, const uint8_t* message,
+                    size_t message_size,
+                    const uint8_t private_key[DICE_PRIVATE_KEY_SIZE],
+                    uint8_t signature[DICE_SIGNATURE_SIZE]) {
+  (void)context_not_used;
+  if (1 == P384Sign(signature, message, message_size, private_key)) {
+    return kDiceResultOk;
+  }
+  return kDiceResultPlatformError;
+}
+
+DiceResult DiceVerify(void* context_not_used, const uint8_t* message,
+                      size_t message_size,
+                      const uint8_t signature[DICE_SIGNATURE_SIZE],
+                      const uint8_t public_key[DICE_PUBLIC_KEY_SIZE]) {
+  (void)context_not_used;
+  if (1 == P384Verify(message, message_size, signature, public_key)) {
+    return kDiceResultOk;
+  }
+  return kDiceResultPlatformError;
+}
diff --git a/src/cbor_cert_op.c b/src/cbor_cert_op.c
index 8006294..5df1583 100644
--- a/src/cbor_cert_op.c
+++ b/src/cbor_cert_op.c
@@ -13,7 +13,8 @@
 // the License.
 
 // This is a DiceGenerateCertificate implementation that generates a CWT-style
-// CBOR certificate using the ED25519-SHA512 signature scheme.
+// CBOR certificate. The function DiceCoseEncodePublicKey depends on the
+// signature algorithm type, and must be implemented elsewhere.
 
 #include <stddef.h>
 #include <stdint.h>
@@ -25,74 +26,24 @@
 #include "dice/ops/trait/cose.h"
 #include "dice/utils.h"
 
-#if DICE_PUBLIC_KEY_SIZE != 32
-#error "Only Ed25519 is supported; 32 bytes needed to store the public key."
-#endif
-#if DICE_SIGNATURE_SIZE != 64
-#error "Only Ed25519 is supported; 64 bytes needed to store the signature."
-#endif
-
 // Max size of COSE_Sign1 including payload.
 #define DICE_MAX_CERTIFICATE_SIZE 2048
 // Max size of COSE_Key encoding.
-#define DICE_MAX_PUBLIC_KEY_SIZE 64
+#define DICE_MAX_PUBLIC_KEY_SIZE (DICE_PUBLIC_KEY_SIZE + 32)
 // Max size of the COSE_Sign1 protected attributes.
 #define DICE_MAX_PROTECTED_ATTRIBUTES_SIZE 16
 
-DiceResult DiceCoseEncodePublicKey(
-    void* context_not_used, const uint8_t public_key[DICE_PUBLIC_KEY_SIZE],
-    size_t buffer_size, uint8_t* buffer, size_t* encoded_size) {
-  (void)context_not_used;
-
-  // Constants per RFC 8152.
-  const int64_t kCoseKeyKtyLabel = 1;
-  const int64_t kCoseKeyAlgLabel = 3;
-  const int64_t kCoseKeyOpsLabel = 4;
-  const int64_t kCoseOkpCrvLabel = -1;
-  const int64_t kCoseOkpXLabel = -2;
-  const int64_t kCoseKeyTypeOkp = 1;
-  const int64_t kCoseAlgEdDSA = -8;
-  const int64_t kCoseKeyOpsVerify = 2;
-  const int64_t kCoseCrvEd25519 = 6;
-
-  struct CborOut out;
-  CborOutInit(buffer, buffer_size, &out);
-  CborWriteMap(/*num_pairs=*/5, &out);
-  // Add the key type.
-  CborWriteInt(kCoseKeyKtyLabel, &out);
-  CborWriteInt(kCoseKeyTypeOkp, &out);
-  // Add the algorithm.
-  CborWriteInt(kCoseKeyAlgLabel, &out);
-  CborWriteInt(kCoseAlgEdDSA, &out);
-  // Add the KeyOps.
-  CborWriteInt(kCoseKeyOpsLabel, &out);
-  CborWriteArray(/*num_elements=*/1, &out);
-  CborWriteInt(kCoseKeyOpsVerify, &out);
-  // Add the curve.
-  CborWriteInt(kCoseOkpCrvLabel, &out);
-  CborWriteInt(kCoseCrvEd25519, &out);
-  // Add the public key.
-  CborWriteInt(kCoseOkpXLabel, &out);
-  CborWriteBstr(/*data_size=*/DICE_PUBLIC_KEY_SIZE, public_key, &out);
-  if (CborOutOverflowed(&out)) {
-    return kDiceResultBufferTooSmall;
-  }
-  *encoded_size = CborOutSize(&out);
-  return kDiceResultOk;
-}
-
 static DiceResult EncodeProtectedAttributes(size_t buffer_size, uint8_t* buffer,
                                             size_t* encoded_size) {
   // Constants per RFC 8152.
   const int64_t kCoseHeaderAlgLabel = 1;
-  const int64_t kCoseAlgEdDSA = -8;
 
   struct CborOut out;
   CborOutInit(buffer, buffer_size, &out);
   CborWriteMap(/*num_elements=*/1, &out);
   // Add the algorithm.
   CborWriteInt(kCoseHeaderAlgLabel, &out);
-  CborWriteInt(kCoseAlgEdDSA, &out);
+  CborWriteInt(DICE_COSE_KEY_ALG_VALUE, &out);
   if (CborOutOverflowed(&out)) {
     return kDiceResultBufferTooSmall;
   }
diff --git a/src/cbor_ed25519_cert_op.c b/src/cbor_ed25519_cert_op.c
new file mode 100644
index 0000000..a461ae1
--- /dev/null
+++ b/src/cbor_ed25519_cert_op.c
@@ -0,0 +1,69 @@
+// 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
+//
+//     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.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "dice/cbor_writer.h"
+#include "dice/ops/trait/cose.h"
+
+#if DICE_PUBLIC_KEY_SIZE != 32
+#error "Only Ed25519 is supported; 32 bytes needed to store the public key."
+#endif
+#if DICE_SIGNATURE_SIZE != 64
+#error "Only Ed25519 is supported; 64 bytes needed to store the signature."
+#endif
+
+DiceResult DiceCoseEncodePublicKey(
+    void* context_not_used, const uint8_t public_key[DICE_PUBLIC_KEY_SIZE],
+    size_t buffer_size, uint8_t* buffer, size_t* encoded_size) {
+  (void)context_not_used;
+
+  // Constants per RFC 8152.
+  const int64_t kCoseKeyKtyLabel = 1;
+  const int64_t kCoseKeyAlgLabel = 3;
+  const int64_t kCoseKeyOpsLabel = 4;
+  const int64_t kCoseOkpCrvLabel = -1;
+  const int64_t kCoseOkpXLabel = -2;
+  const int64_t kCoseKeyTypeOkp = 1;
+  const int64_t kCoseAlgEdDSA = DICE_COSE_KEY_ALG_VALUE;
+  const int64_t kCoseKeyOpsVerify = 2;
+  const int64_t kCoseCrvEd25519 = 6;
+
+  struct CborOut out;
+  CborOutInit(buffer, buffer_size, &out);
+  CborWriteMap(/*num_pairs=*/5, &out);
+  // Add the key type.
+  CborWriteInt(kCoseKeyKtyLabel, &out);
+  CborWriteInt(kCoseKeyTypeOkp, &out);
+  // Add the algorithm.
+  CborWriteInt(kCoseKeyAlgLabel, &out);
+  CborWriteInt(kCoseAlgEdDSA, &out);
+  // Add the KeyOps.
+  CborWriteInt(kCoseKeyOpsLabel, &out);
+  CborWriteArray(/*num_elements=*/1, &out);
+  CborWriteInt(kCoseKeyOpsVerify, &out);
+  // Add the curve.
+  CborWriteInt(kCoseOkpCrvLabel, &out);
+  CborWriteInt(kCoseCrvEd25519, &out);
+  // Add the public key.
+  CborWriteInt(kCoseOkpXLabel, &out);
+  CborWriteBstr(/*data_size=*/DICE_PUBLIC_KEY_SIZE, public_key, &out);
+  if (CborOutOverflowed(&out)) {
+    return kDiceResultBufferTooSmall;
+  }
+  *encoded_size = CborOutSize(&out);
+  return kDiceResultOk;
+}
diff --git a/src/cbor_p384_cert_op.c b/src/cbor_p384_cert_op.c
new file mode 100644
index 0000000..de078b6
--- /dev/null
+++ b/src/cbor_p384_cert_op.c
@@ -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
+//
+//     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.
+
+// This is a DiceGenerateCertificate implementation that generates a CWT-style
+// CBOR certificate using the ED25519-SHA512 signature scheme.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "dice/cbor_writer.h"
+#include "dice/dice.h"
+#include "dice/ops.h"
+#include "dice/ops/trait/cose.h"
+#include "dice/utils.h"
+
+#if DICE_PUBLIC_KEY_SIZE != 96
+#error "96 bytes needed to store the public key."
+#endif
+#if DICE_SIGNATURE_SIZE != 96
+#error "96 bytes needed to store the signature."
+#endif
+
+DiceResult DiceCoseEncodePublicKey(
+    void* context_not_used, const uint8_t public_key[DICE_PUBLIC_KEY_SIZE],
+    size_t buffer_size, uint8_t* buffer, size_t* encoded_size) {
+  (void)context_not_used;
+
+  // Constants per RFC 8152.
+  const int64_t kCoseKeyKtyLabel = 1;
+  const int64_t kCoseKeyAlgLabel = 3;
+  const int64_t kCoseKeyAlgValue = DICE_COSE_KEY_ALG_VALUE;
+  const int64_t kCoseKeyOpsLabel = 4;
+  const int64_t kCoseKeyOpsValue = 2;  // Verify
+  const int64_t kCoseKeyKtyValue = 2;  // EC2
+  const int64_t kCoseEc2CrvLabel = -1;
+  const int64_t kCoseEc2CrvValue = 2;  // P-384
+  const int64_t kCoseEc2XLabel = -2;
+  const int64_t kCoseEc2YLabel = -3;
+
+  struct CborOut out;
+  CborOutInit(buffer, buffer_size, &out);
+  CborWriteMap(/*num_pairs=*/6, &out);
+  // Add the key type.
+  CborWriteInt(kCoseKeyKtyLabel, &out);
+  CborWriteInt(kCoseKeyKtyValue, &out);
+  // Add the algorithm.
+  CborWriteInt(kCoseKeyAlgLabel, &out);
+  CborWriteInt(kCoseKeyAlgValue, &out);
+  // Add the KeyOps.
+  CborWriteInt(kCoseKeyOpsLabel, &out);
+  CborWriteArray(/*num_elements=*/1, &out);
+  CborWriteInt(kCoseKeyOpsValue, &out);
+  // Add the curve.
+  CborWriteInt(kCoseEc2CrvLabel, &out);
+  CborWriteInt(kCoseEc2CrvValue, &out);
+  // Add the subject public key x and y coordinates
+  CborWriteInt(kCoseEc2XLabel, &out);
+  CborWriteBstr(/*data_size=*/DICE_PUBLIC_KEY_SIZE / 2, &public_key[0], &out);
+  CborWriteInt(kCoseEc2YLabel, &out);
+  CborWriteBstr(/*data_size=*/DICE_PUBLIC_KEY_SIZE / 2,
+                &public_key[DICE_PUBLIC_KEY_SIZE / 2], &out);
+
+  if (CborOutOverflowed(&out)) {
+    return kDiceResultBufferTooSmall;
+  }
+  *encoded_size = CborOutSize(&out);
+  return kDiceResultOk;
+}
diff --git a/src/boringssl_ops_test.cc b/src/cbor_p384_cert_op_test.cc
similarity index 81%
copy from src/boringssl_ops_test.cc
copy to src/cbor_p384_cert_op_test.cc
index 971ad0b..21d0331 100644
--- a/src/boringssl_ops_test.cc
+++ b/src/cbor_p384_cert_op_test.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 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
@@ -18,6 +18,7 @@
 
 #include <memory>
 
+#include "dice/config.h"
 #include "dice/dice.h"
 #include "dice/known_test_values.h"
 #include "dice/test_framework.h"
@@ -27,11 +28,10 @@
 
 namespace {
 
-using dice::test::CertificateType_X509;
+using dice::test::CertificateType_Cbor;
 using dice::test::DeriveFakeInputValue;
 using dice::test::DiceStateForTest;
-using dice::test::DumpState;
-using dice::test::KeyType_Ed25519;
+using dice::test::KeyType_P384;
 
 TEST(DiceOpsTest, KnownAnswerZeroInput) {
   DiceStateForTest current_state = {};
@@ -42,16 +42,23 @@
       sizeof(next_state.certificate), next_state.certificate,
       &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal);
   EXPECT_EQ(kDiceResultOk, result);
-  DumpState(CertificateType_X509, KeyType_Ed25519, "zero_input", next_state);
-  // Both CDI values and the certificate should be deterministic.
+  DumpState(CertificateType_Cbor, KeyType_P384, "zero_input", next_state);
+  // The CDI values should be deterministic.
+  ASSERT_EQ(sizeof(next_state.cdi_attest),
+            sizeof(dice::test::kExpectedCdiAttest_ZeroInput));
   EXPECT_EQ(0, memcmp(next_state.cdi_attest,
                       dice::test::kExpectedCdiAttest_ZeroInput, DICE_CDI_SIZE));
+  ASSERT_EQ(sizeof(next_state.cdi_seal),
+            sizeof(dice::test::kExpectedCdiSeal_ZeroInput));
   EXPECT_EQ(0, memcmp(next_state.cdi_seal,
                       dice::test::kExpectedCdiSeal_ZeroInput, DICE_CDI_SIZE));
-  ASSERT_EQ(sizeof(dice::test::kExpectedX509Ed25519Cert_ZeroInput),
+  ASSERT_EQ(sizeof(dice::test::kExpectedCborP384Cert_ZeroInput),
             next_state.certificate_size);
-  EXPECT_EQ(0, memcmp(dice::test::kExpectedX509Ed25519Cert_ZeroInput,
-                      next_state.certificate, next_state.certificate_size));
+  // Comparing everything except for the signature, since ECDSA signatures are
+  // not deterministic
+  EXPECT_EQ(0, memcmp(dice::test::kExpectedCborP384Cert_ZeroInput,
+                      next_state.certificate,
+                      next_state.certificate_size - DICE_SIGNATURE_SIZE));
 }
 
 TEST(DiceOpsTest, KnownAnswerHashOnlyInput) {
@@ -72,19 +79,22 @@
       sizeof(next_state.certificate), next_state.certificate,
       &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal);
   EXPECT_EQ(kDiceResultOk, result);
-  DumpState(CertificateType_X509, KeyType_Ed25519, "hash_only_input",
-            next_state);
-  // Both CDI values and the certificate should be deterministic.
+  DumpState(CertificateType_Cbor, KeyType_P384, "hash_only_input", next_state);
+  ASSERT_EQ(sizeof(next_state.cdi_attest),
+            sizeof(dice::test::kExpectedCdiAttest_HashOnlyInput));
   EXPECT_EQ(
       0, memcmp(next_state.cdi_attest,
                 dice::test::kExpectedCdiAttest_HashOnlyInput, DICE_CDI_SIZE));
+  ASSERT_EQ(sizeof(next_state.cdi_seal),
+            sizeof(dice::test::kExpectedCdiSeal_HashOnlyInput));
   EXPECT_EQ(
       0, memcmp(next_state.cdi_seal, dice::test::kExpectedCdiSeal_HashOnlyInput,
                 DICE_CDI_SIZE));
-  ASSERT_EQ(sizeof(dice::test::kExpectedX509Ed25519Cert_HashOnlyInput),
+  ASSERT_EQ(sizeof(dice::test::kExpectedCborP384Cert_HashOnlyInput),
             next_state.certificate_size);
-  EXPECT_EQ(0, memcmp(dice::test::kExpectedX509Ed25519Cert_HashOnlyInput,
-                      next_state.certificate, next_state.certificate_size));
+  EXPECT_EQ(0, memcmp(dice::test::kExpectedCborP384Cert_HashOnlyInput,
+                      next_state.certificate,
+                      next_state.certificate_size - DICE_SIGNATURE_SIZE));
 }
 
 TEST(DiceOpsTest, KnownAnswerDescriptorInput) {
@@ -121,8 +131,7 @@
       sizeof(next_state.certificate), next_state.certificate,
       &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal);
   EXPECT_EQ(kDiceResultOk, result);
-  DumpState(CertificateType_X509, KeyType_Ed25519, "descriptor_input",
-            next_state);
+  DumpState(CertificateType_Cbor, KeyType_P384, "descriptor_input", next_state);
   // Both CDI values and the certificate should be deterministic.
   EXPECT_EQ(
       0, memcmp(next_state.cdi_attest,
@@ -130,14 +139,15 @@
   EXPECT_EQ(
       0, memcmp(next_state.cdi_seal,
                 dice::test::kExpectedCdiSeal_DescriptorInput, DICE_CDI_SIZE));
-  ASSERT_EQ(sizeof(dice::test::kExpectedX509Ed25519Cert_DescriptorInput),
+  ASSERT_EQ(sizeof(dice::test::kExpectedCborP384Cert_DescriptorInput),
             next_state.certificate_size);
-  EXPECT_EQ(0, memcmp(dice::test::kExpectedX509Ed25519Cert_DescriptorInput,
-                      next_state.certificate, next_state.certificate_size));
+  EXPECT_EQ(0, memcmp(dice::test::kExpectedCborP384Cert_DescriptorInput,
+                      next_state.certificate,
+                      next_state.certificate_size - DICE_SIGNATURE_SIZE));
 }
 
 TEST(DiceOpsTest, NonZeroMode) {
-  constexpr size_t kModeOffsetInCert = 0x230;
+  constexpr size_t kModeOffsetInCert = 316;
   DiceStateForTest current_state = {};
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
@@ -198,11 +208,11 @@
                      states[i + 1].cdi_attest, states[i + 1].cdi_seal));
     char suffix[40];
     pw::string::Format(suffix, "part_cert_chain_%zu", i);
-    DumpState(CertificateType_X509, KeyType_Ed25519, suffix, states[i + 1]);
+    DumpState(CertificateType_Cbor, KeyType_P384, suffix, states[i + 1]);
   }
   // Use the first derived CDI cert as the 'root' of partial chain.
   EXPECT_TRUE(dice::test::VerifyCertificateChain(
-      CertificateType_X509, states[1].certificate, states[1].certificate_size,
+      CertificateType_Cbor, states[1].certificate, states[1].certificate_size,
       &states[2], kNumLayers - 1, /*is_partial_chain=*/true));
 }
 
@@ -228,18 +238,17 @@
                      states[i + 1].cdi_attest, states[i + 1].cdi_seal));
     char suffix[40];
     pw::string::Format(suffix, "full_cert_chain_%zu", i);
-    DumpState(CertificateType_X509, KeyType_Ed25519, suffix, states[i + 1]);
+    DumpState(CertificateType_Cbor, KeyType_P384, suffix, states[i + 1]);
   }
   // Use a fake self-signed UDS cert as the 'root'.
   uint8_t root_certificate[dice::test::kTestCertSize];
   size_t root_certificate_size = 0;
   dice::test::CreateFakeUdsCertificate(
-      NULL, states[0].cdi_attest, CertificateType_X509, KeyType_Ed25519,
+      NULL, states[0].cdi_attest, CertificateType_Cbor, KeyType_P384,
       root_certificate, &root_certificate_size);
   EXPECT_TRUE(dice::test::VerifyCertificateChain(
-      CertificateType_X509, root_certificate, root_certificate_size, &states[1],
-      kNumLayers,
-      /*is_partial_chain=*/false));
+      CertificateType_Cbor, root_certificate, root_certificate_size, &states[1],
+      kNumLayers, /*is_partial_chain=*/false));
 }
 
 }  // namespace
diff --git a/src/cbor_reader.c b/src/cbor_reader.c
index 3b0b343..035a0bc 100644
--- a/src/cbor_reader.c
+++ b/src/cbor_reader.c
@@ -21,7 +21,7 @@
   CBOR_TYPE_TSTR = 3,
   CBOR_TYPE_ARRAY = 4,
   CBOR_TYPE_MAP = 5,
-  CBOR_TYPE_TAG_NOT_SUPPORTED = 6,
+  CBOR_TYPE_TAG = 6,
   CBOR_TYPE_SIMPLE = 7,
 };
 
@@ -188,6 +188,21 @@
   return CborReadSize(in, CBOR_TYPE_MAP, num_pairs);
 }
 
+enum CborReadResult CborReadTag(struct CborIn* in, uint64_t* tag) {
+  uint8_t bytes;
+  enum CborType type;
+  enum CborReadResult res =
+      CborPeekIntialValueAndArgument(in, &bytes, &type, tag);
+  if (res != CBOR_READ_RESULT_OK) {
+    return res;
+  }
+  if (type != CBOR_TYPE_TAG) {
+    return CBOR_READ_RESULT_NOT_FOUND;
+  }
+  in->cursor += bytes;
+  return CBOR_READ_RESULT_OK;
+}
+
 enum CborReadResult CborReadFalse(struct CborIn* in) {
   return CborReadSimple(in, /*val=*/20);
 }
@@ -246,6 +261,9 @@
         }
         val *= 2;
         break;
+      case CBOR_TYPE_TAG:
+        val = 1;
+        break;
       case CBOR_TYPE_ARRAY:
         break;
       default:
diff --git a/src/cbor_reader_fuzzer.cc b/src/cbor_reader_fuzzer.cc
index 9dc08fb..3eeb544 100644
--- a/src/cbor_reader_fuzzer.cc
+++ b/src/cbor_reader_fuzzer.cc
@@ -46,6 +46,9 @@
     CborReadMap(&peeker, &sz);
 
     peeker = in;
+    CborReadTag(&peeker, &unsigned_int);
+
+    peeker = in;
     CborReadFalse(&peeker);
 
     peeker = in;
diff --git a/src/cbor_reader_test.cc b/src/cbor_reader_test.cc
index b491025..7b14475 100644
--- a/src/cbor_reader_test.cc
+++ b/src/cbor_reader_test.cc
@@ -249,13 +249,65 @@
   EXPECT_TRUE(CborInAtEnd(&in));
 }
 
+TEST(CborReaderTest, TagEncoding) {
+  const uint8_t buffer[] = {0xcf, 0xd8, 0x18, 0xd9, 0xd9, 0xf8, 0xda, 0x4f,
+                            0x50, 0x53, 0x4e, 0xdb, 0x10, 0x00, 0x00, 0x00,
+                            0x00, 0x00, 0x00, 0x00};
+  CborIn in;
+  uint64_t tag;
+  CborInInit(buffer, sizeof(buffer), &in);
+  EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+  EXPECT_EQ(/* Unassigned */15u, tag);
+  EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+  EXPECT_EQ(/* COSE_Sign1 */24u, tag);
+  EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+  EXPECT_EQ(/* Byte string */0xd9f8u, tag);
+  EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+  EXPECT_EQ(/* Openswan cfg */0x4f50534eu, tag);
+  EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+  EXPECT_EQ(/* Unassigned */0x1000000000000000u, tag);
+  EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, TagInvalid) {
+  // The following tags are always invalid but are treated as any other tag.
+  // Reference https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml.
+  const uint8_t invalid16[] = {0xd9, 0xff, 0xff};
+  const uint8_t invalid32[] = {0xda, 0xff, 0xff, 0xff, 0xff};
+  const uint8_t invalid64[] = {0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                               0xff};
+  CborIn in;
+  uint64_t tag;
+  CborInInit(invalid16, sizeof(invalid16), &in);
+  EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+  EXPECT_TRUE(CborInAtEnd(&in));
+  CborInInit(invalid32, sizeof(invalid32), &in);
+  EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+  EXPECT_TRUE(CborInAtEnd(&in));
+  CborInInit(invalid64, sizeof(invalid64), &in);
+  EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+  EXPECT_TRUE(CborInAtEnd(&in));
+}
+
 TEST(CborReaderTest, Skip) {
   const uint8_t buffer[] = {0x84, 0x03, 0xa2, 0x82, 0x23, 0x05, 0xf4,
                             0x16, 0xf6, 0x61, 0x44, 0x41, 0xaa};
+  const uint8_t tag[] = {0xc4, 0xf5};
+  const uint8_t tagtag[] = {0xc4, 0xc4, 0xf5};
+  const uint8_t nested_tag[] = {0x82, 0xa1, 0x02, 0xc7, 0x04, 0x09};
   CborIn in;
   CborInInit(buffer, sizeof(buffer), &in);
   EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadSkip(&in));
   EXPECT_TRUE(CborInAtEnd(&in));
+  CborInInit(tag, sizeof(tag), &in);
+  EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadSkip(&in));
+  EXPECT_TRUE(CborInAtEnd(&in));
+  CborInInit(tagtag, sizeof(tagtag), &in);
+  EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadSkip(&in));
+  EXPECT_TRUE(CborInAtEnd(&in));
+  CborInInit(nested_tag, sizeof(nested_tag), &in);
+  EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadSkip(&in));
+  EXPECT_TRUE(CborInAtEnd(&in));
 }
 
 TEST(CborReaderTest, SkipTooDeeplyNestedMalformed) {
@@ -273,18 +325,6 @@
   EXPECT_EQ(0u, CborInOffset(&in));
 }
 
-TEST(CborReaderTest, SkipTagMalformed) {
-  const uint8_t tag[] = {0xc4, 0xf5};
-  const uint8_t nested_tag[] = {0x82, 0xa1, 0x02, 0xc7, 0x04, 0x09};
-  CborIn in;
-  CborInInit(tag, sizeof(tag), &in);
-  EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadSkip(&in));
-  EXPECT_EQ(0u, CborInOffset(&in));
-  CborInInit(nested_tag, sizeof(nested_tag), &in);
-  EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadSkip(&in));
-  EXPECT_EQ(0u, CborInOffset(&in));
-}
-
 TEST(CborReaderTest, EmptyBufferAtEnd) {
   int64_t val;
   uint64_t uval;
@@ -303,11 +343,12 @@
   EXPECT_EQ(CBOR_READ_RESULT_END, CborReadFalse(&in));
   EXPECT_EQ(CBOR_READ_RESULT_END, CborReadTrue(&in));
   EXPECT_EQ(CBOR_READ_RESULT_END, CborReadNull(&in));
+  EXPECT_EQ(CBOR_READ_RESULT_END, CborReadTag(&in, &uval));
   EXPECT_EQ(0u, CborInOffset(&in));
 }
 
 TEST(CborReaderTest, NotFound) {
-  const uint8_t buffer[] = {0xc0, 0x08};
+  const uint8_t buffer[] = {0xe0, 0x08};
   int64_t val;
   uint64_t uval;
   size_t size;
@@ -324,6 +365,7 @@
   EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadFalse(&in));
   EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadTrue(&in));
   EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadNull(&in));
+  EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadTag(&in, &uval));
   EXPECT_EQ(0u, CborInOffset(&in));
 }
 
diff --git a/src/cbor_writer.c b/src/cbor_writer.c
index bfa403f..e512931 100644
--- a/src/cbor_writer.c
+++ b/src/cbor_writer.c
@@ -26,7 +26,7 @@
   CBOR_TYPE_TSTR = 3,
   CBOR_TYPE_ARRAY = 4,
   CBOR_TYPE_MAP = 5,
-  // Type 6, tags, are not supported.
+  CBOR_TYPE_TAG = 6,
   CBOR_TYPE_SIMPLE = 7,
 };
 
@@ -142,6 +142,10 @@
   CborWriteType(CBOR_TYPE_MAP, num_pairs, out);
 }
 
+void CborWriteTag(uint64_t tag, struct CborOut* out) {
+  CborWriteType(CBOR_TYPE_TAG, tag, out);
+}
+
 void CborWriteFalse(struct CborOut* out) {
   CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/20, out);
 }
diff --git a/src/cbor_writer_fuzzer.cc b/src/cbor_writer_fuzzer.cc
index ebcb1e3..2119e02 100644
--- a/src/cbor_writer_fuzzer.cc
+++ b/src/cbor_writer_fuzzer.cc
@@ -26,6 +26,7 @@
   AllocTstr,
   WriteArray,
   WriteMap,
+  WriteTag,
   WriteFalse,
   WriteTrue,
   WriteNull,
@@ -97,6 +98,11 @@
         CborWriteMap(num_pairs, &out);
         break;
       }
+      case WriteTag: {
+        auto tag = fdp.ConsumeIntegral<uint64_t>();
+        CborWriteTag(tag, &out);
+        break;
+      }
       case WriteFalse:
         CborWriteNull(&out);
         break;
diff --git a/src/cbor_writer_test.cc b/src/cbor_writer_test.cc
index 2ad72a4..c1f7a9d 100644
--- a/src/cbor_writer_test.cc
+++ b/src/cbor_writer_test.cc
@@ -188,6 +188,22 @@
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
+TEST(CborWriterTest, TagEncoding) {
+  const uint8_t kExpectedEncoding[] = {0xcf, 0xd8, 0x18, 0xd9, 0xd9, 0xf8, 0xda,
+                                       0x4f, 0x50, 0x53, 0x4e, 0xdb, 0x10, 0x00,
+                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+  uint8_t buffer[64];
+  CborOut out;
+  CborOutInit(buffer, sizeof(buffer), &out);
+  CborWriteTag(/*tag=*/15, &out);
+  CborWriteTag(/*tag=*/24, &out);
+  CborWriteTag(/*tag=*/0xd9f8u, &out);
+  CborWriteTag(/*tag=*/0x4f50534eu, &out);
+  CborWriteTag(/*tag=*/0x1000000000000000u, &out);
+  EXPECT_FALSE(CborOutOverflowed(&out));
+  EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
+}
+
 TEST(CborWriterTest, FalseEncoding) {
   const uint8_t kExpectedEncoding[] = {0xf4};
   uint8_t buffer[64];
@@ -230,12 +246,13 @@
   EXPECT_NE(nullptr, CborAllocTstr(6, &out));
   CborWriteArray(/*num_elements=*/16, &out);
   CborWriteMap(/*num_pairs=*/35, &out);
+  CborWriteTag(/*tag=*/15, &out);
   CborWriteFalse(&out);
   CborWriteTrue(&out);
   CborWriteNull(&out);
   EXPECT_FALSE(CborOutOverflowed(&out));
   // Offset is the cumulative size.
-  EXPECT_EQ(3 + 6 + 8 + 9 + 7 + 1 + 2 + 1 + 1 + 1u, CborOutSize(&out));
+  EXPECT_EQ(3 + 6 + 8 + 9 + 7 + 1 + 2 + 1 + 1 + 1 + 1u, CborOutSize(&out));
 }
 
 TEST(CborWriterTest, NullBufferForMeasurement) {
@@ -245,6 +262,7 @@
   CborWriteNull(&out);
   CborWriteTrue(&out);
   CborWriteFalse(&out);
+  CborWriteTag(/*tag=*/15, &out);
   CborWriteMap(/*num_pairs=*/623, &out);
   CborWriteArray(/*num_elements=*/70000, &out);
   EXPECT_EQ(nullptr, CborAllocTstr(8, &out));
@@ -255,7 +273,7 @@
   // Measurement has occurred, but output did not.
   EXPECT_TRUE(CborOutOverflowed(&out));
   // Offset is the cumulative size.
-  EXPECT_EQ(1 + 1 + 1 + 3 + 5 + 9 + 7 + 2 + 8 + 5u, CborOutSize(&out));
+  EXPECT_EQ(1 + 1 + 1 + 1 + 3 + 5 + 9 + 7 + 2 + 8 + 5u, CborOutSize(&out));
 }
 
 TEST(CborWriterTest, BufferTooSmall) {
diff --git a/src/mbedtls_ops.c b/src/mbedtls_ops.c
index 18eb4d4..4dbf2dd 100644
--- a/src/mbedtls_ops.c
+++ b/src/mbedtls_ops.c
@@ -433,17 +433,24 @@
   }
   if (0 != mbedtls_x509write_crt_set_extension(
                &cert_context, kDiceExtensionOid, kDiceExtensionOidLength,
-               /*critical=*/0, dice_extension, dice_extension_size)) {
+               /*critical=*/1, dice_extension, dice_extension_size)) {
     result = kDiceResultPlatformError;
     goto out;
   }
   // This implementation is deterministic and assumes entropy is not available.
   // If this code is run where entropy is available, however, f_rng and p_rng
-  // should be set appropriately.
+  // should be set to use that entropy. As is, we'll provide a DRBG for blinding
+  // but it will be ineffective.
+  mbedtls_hmac_drbg_context drbg;
+  mbedtls_hmac_drbg_init(&drbg);
+  mbedtls_hmac_drbg_seed_buf(&drbg,
+                             mbedtls_md_info_from_type(MBEDTLS_MD_SHA512),
+                             subject_key_id, subject_key_id_size);
   uint8_t tmp_buffer[DICE_MAX_CERTIFICATE_SIZE];
   int length_or_error =
       mbedtls_x509write_crt_der(&cert_context, tmp_buffer, sizeof(tmp_buffer),
-                                /*f_rng=*/NULL, /*p_rng=*/NULL);
+                                mbedtls_hmac_drbg_random, &drbg);
+  mbedtls_hmac_drbg_free(&drbg);
   if (length_or_error < 0) {
     result = kDiceResultPlatformError;
     goto out;
diff --git a/src/mbedtls_ops_test.cc b/src/mbedtls_ops_test.cc
index ff54180..7f97366 100644
--- a/src/mbedtls_ops_test.cc
+++ b/src/mbedtls_ops_test.cc
@@ -135,7 +135,7 @@
 }
 
 TEST(DiceOpsTest, NonZeroMode) {
-  constexpr size_t kModeOffsetInCert = 0x266;
+  constexpr size_t kModeOffsetInCert = 0x269;
   DiceStateForTest current_state = {};
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
diff --git a/src/template_cert_op.c b/src/template_cert_op.c
index e577cd2..daa4317 100644
--- a/src/template_cert_op.c
+++ b/src/template_cert_op.c
@@ -40,12 +40,12 @@
 #include "openssl/is_boringssl.h"
 
 // A well-formed certificate, but with zeros in all fields to be filled.
-static const uint8_t kTemplate[635] = {
+static const uint8_t kTemplate[638] = {
     // Constant encoding.
-    0x30, 0x82, 0x02, 0x77,
+    0x30, 0x82, 0x02, 0x7a,
     // Offset 4: TBS starts here.
     // Constant encoding.
-    0x30, 0x82, 0x02, 0x29, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x14,
+    0x30, 0x82, 0x02, 0x2c, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x14,
     // Offset 15: Serial number, 20 bytes.
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -74,7 +74,7 @@
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     // Constant encoding.
-    0xa3, 0x82, 0x01, 0x4b, 0x30, 0x82, 0x01, 0x47, 0x30, 0x1f, 0x06, 0x03,
+    0xa3, 0x82, 0x01, 0x4e, 0x30, 0x82, 0x01, 0x4a, 0x30, 0x1f, 0x06, 0x03,
     0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
     // Offset 247: Authority key identifier, 20 bytes.
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -87,9 +87,10 @@
     // Constant encoding.
     0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
     0x03, 0x02, 0x02, 0x04, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
-    0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x81, 0xe3,
+    0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x81, 0xe6,
     0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x18,
-    0x04, 0x81, 0xd4, 0x30, 0x81, 0xd1, 0xa0, 0x42, 0x04, 0x40,
+    0x01, 0x01, 0xff, 0x04, 0x81, 0xd4, 0x30, 0x81, 0xd1, 0xa0, 0x42, 0x04,
+    0x40,
     // Offset 356: Code hash, 64 bytes.
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -139,12 +140,12 @@
                    {194, 32},  // Subject public key
                    {247, 20},  // Authority key id
                    {278, 20},  // Subject key id
-                   {356, 64},  // Code hash
-                   {424, 64},  // Config descriptor
-                   {492, 64},  // Authority hash
-                   {560, 1},   // Mode
-                   {571, 64},  // Signature
-                   {4, 557}};  // Entire TBS
+                   {359, 64},  // Code hash
+                   {427, 64},  // Config descriptor
+                   {495, 64},  // Authority hash
+                   {563, 1},   // Mode
+                   {574, 64},  // Signature
+                   {4, 560}};  // Entire TBS
 
 static const size_t kFieldIndexSerial = 0;
 static const size_t kFieldIndexIssuerName = 1;
diff --git a/src/template_cert_op_test.cc b/src/template_cert_op_test.cc
index f522e16..409e8d0 100644
--- a/src/template_cert_op_test.cc
+++ b/src/template_cert_op_test.cc
@@ -125,7 +125,7 @@
 }
 
 TEST(DiceOpsTest, NonZeroMode) {
-  constexpr size_t kModeOffsetInCert = 0x230;
+  constexpr size_t kModeOffsetInCert = 0x233;
   DiceStateForTest current_state = {};
   DiceStateForTest next_state = {};
   DiceInputValues input_values = {};
diff --git a/src/test_utils.cc b/src/test_utils.cc
index e5dbd6f..cb21d3f 100644
--- a/src/test_utils.cc
+++ b/src/test_utils.cc
@@ -21,6 +21,7 @@
 #include <memory>
 
 #include "cose/cose.h"
+#include "dice/boringssl_ecdsa_utils.h"
 #include "dice/dice.h"
 #include "dice/utils.h"
 #include "openssl/asn1.h"
@@ -36,6 +37,9 @@
 #include "openssl/x509v3.h"
 #include "pw_string/format.h"
 
+// The largest possible public key size among ECDSA P-384, P-256, and ED25519
+#define MAX_PUBLIC_KEY_SIZE 96
+
 namespace {
 
 // A scoped pointer for cn_cbor.
@@ -60,6 +64,8 @@
       return "Ed25519";
     case dice::test::KeyType_P256:
       return "P256";
+    case dice::test::KeyType_P384:
+      return "P384";
   }
   return "";
 }
@@ -161,7 +167,7 @@
 
 bssl::UniquePtr<EVP_PKEY> KeyFromRawKey(
     const uint8_t raw_key[DICE_PRIVATE_KEY_SEED_SIZE],
-    dice::test::KeyType key_type, uint8_t raw_public_key[33],
+    dice::test::KeyType key_type, uint8_t raw_public_key[MAX_PUBLIC_KEY_SIZE],
     size_t* raw_public_key_size) {
   if (key_type == dice::test::KeyType_Ed25519) {
     bssl::UniquePtr<EVP_PKEY> key(
@@ -197,7 +203,26 @@
         EC_POINT_point2oct(group, pub.get(), POINT_CONVERSION_COMPRESSED,
                            raw_public_key, 33, /*ctx=*/nullptr);
     return pkey;
+  } else if (key_type == dice::test::KeyType_P384) {
+    const size_t kPublicKeySize = 96;
+    const size_t kPrivateKeySize = 48;
+    uint8_t pk[kPrivateKeySize];
+    P384KeypairFromSeed(raw_public_key, pk, raw_key);
+    *raw_public_key_size = kPublicKeySize;
+
+    bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(NID_secp384r1));
+    BIGNUM* x = BN_new();
+    BN_bin2bn(&raw_public_key[0], kPublicKeySize / 2, x);
+    BIGNUM* y = BN_new();
+    BN_bin2bn(&raw_public_key[kPublicKeySize / 2], kPublicKeySize / 2, y);
+    EC_KEY_set_public_key_affine_coordinates(key.get(), x, y);
+    BN_clear_free(y);
+    BN_clear_free(x);
+    bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+    EVP_PKEY_set1_EC_KEY(pkey.get(), key.get());
+    return pkey;
   }
+
   printf("ERROR: Unsupported key type.\n");
   return nullptr;
 }
@@ -293,11 +318,13 @@
   if (is_partial_chain) {
     X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_PARTIAL_CHAIN);
   }
+  // Boringssl doesn't support custom extensions, so ignore them.
+  X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_IGNORE_CRITICAL);
   X509_STORE_CTX_set0_param(x509_store_ctx.get(), param);
   return (1 == X509_verify_cert(x509_store_ctx.get()));
 }
 
-void CreateCborUdsCertificate(
+void CreateEd25519CborUdsCertificate(
     const uint8_t private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
     const uint8_t id[DICE_ID_SIZE],
     uint8_t certificate[dice::test::kTestCertSize], size_t* certificate_size) {
@@ -381,6 +408,121 @@
       certificate, 0, dice::test::kTestCertSize, sign1.get());
 }
 
+void CreateP384CborUdsCertificate(
+    const uint8_t private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
+    const uint8_t id[DICE_ID_SIZE],
+    uint8_t certificate[dice::test::kTestCertSize], size_t* certificate_size) {
+  const int64_t kCwtIssuerLabel = 1;
+  const int64_t kCwtSubjectLabel = 2;
+  const int64_t kUdsPublicKeyLabel = -4670552;
+  const int64_t kUdsKeyUsageLabel = -4670553;
+  const uint8_t kKeyUsageCertSign = 32;  // Bit 5.
+  const uint8_t kProtectedAttributesCbor[4] = {
+      0xa1 /* map(1) */, 0x01 /* alg(1) */, 0x38, 0x22 /* ES384(-34) */};
+  const size_t kPublicKeySize = 96;
+  const size_t kPrivateKeySize = 48;
+  const size_t kSignatureSize = 96;
+
+  // Public key encoded as a COSE_Key.
+  uint8_t public_key[kPublicKeySize];
+  uint8_t private_key[kPrivateKeySize];
+  P384KeypairFromSeed(public_key, private_key, private_key_seed);
+  cn_cbor_errback error;
+  ScopedCbor public_key_cbor(cn_cbor_map_create(&error));
+  // kty = ec2
+  cn_cbor_mapput_int(public_key_cbor.get(), 1, cn_cbor_int_create(2, &error),
+                     &error);
+  // crv = P-384
+  cn_cbor_mapput_int(public_key_cbor.get(), -1, cn_cbor_int_create(2, &error),
+                     &error);
+  // x = public_key X
+  cn_cbor_mapput_int(
+      public_key_cbor.get(), -2,
+      cn_cbor_data_create(&public_key[0], kPublicKeySize / 2, &error), &error);
+  // y = public_key Y
+  cn_cbor_mapput_int(public_key_cbor.get(), -3,
+                     cn_cbor_data_create(&public_key[kPublicKeySize / 2],
+                                         kPublicKeySize / 2, &error),
+                     &error);
+  uint8_t encoded_public_key[200];
+  size_t encoded_public_key_size =
+      cn_cbor_encoder_write(encoded_public_key, 0, 200, public_key_cbor.get());
+
+  // Simple CWT payload with issuer, subject, and use the same subject public
+  // key field as a CDI certificate to make verification easy.
+  char id_hex[41];
+  DiceHexEncode(id, DICE_ID_SIZE, id_hex, sizeof(id_hex));
+  id_hex[40] = '\0';
+  ScopedCbor cwt(cn_cbor_map_create(&error));
+  cn_cbor_mapput_int(cwt.get(), kCwtIssuerLabel,
+                     cn_cbor_string_create(id_hex, &error), &error);
+  cn_cbor_mapput_int(cwt.get(), kCwtSubjectLabel,
+                     cn_cbor_string_create(id_hex, &error), &error);
+  cn_cbor_mapput_int(
+      cwt.get(), kUdsPublicKeyLabel,
+      cn_cbor_data_create(encoded_public_key, encoded_public_key_size, &error),
+      &error);
+  uint8_t key_usage_byte = kKeyUsageCertSign;
+  cn_cbor_mapput_int(cwt.get(), kUdsKeyUsageLabel,
+                     cn_cbor_data_create(&key_usage_byte, 1, &error), &error);
+  uint8_t payload[dice::test::kTestCertSize];
+  size_t payload_size =
+      cn_cbor_encoder_write(payload, 0, dice::test::kTestCertSize, cwt.get());
+
+  // Signature over COSE Sign1 TBS.
+  ScopedCbor tbs_cbor(cn_cbor_array_create(&error));
+  cn_cbor_array_append(tbs_cbor.get(),
+                       cn_cbor_string_create("Signature1", &error), &error);
+  cn_cbor_array_append(tbs_cbor.get(),
+                       cn_cbor_data_create(kProtectedAttributesCbor, 4, &error),
+                       &error);
+  cn_cbor_array_append(tbs_cbor.get(), cn_cbor_data_create(NULL, 0, &error),
+                       &error);
+  cn_cbor_array_append(tbs_cbor.get(),
+                       cn_cbor_data_create(payload, payload_size, &error),
+                       &error);
+  uint8_t tbs[dice::test::kTestCertSize];
+  size_t tbs_size =
+      cn_cbor_encoder_write(tbs, 0, dice::test::kTestCertSize, tbs_cbor.get());
+  uint8_t signature[kSignatureSize];
+  P384Sign(signature, tbs, tbs_size, private_key);
+
+  // COSE Sign1.
+  ScopedCbor sign1(cn_cbor_array_create(&error));
+  cn_cbor_array_append(sign1.get(),
+                       cn_cbor_data_create(kProtectedAttributesCbor, 4, &error),
+                       &error);
+  cn_cbor_array_append(sign1.get(), cn_cbor_map_create(&error), &error);
+  cn_cbor_array_append(
+      sign1.get(), cn_cbor_data_create(payload, payload_size, &error), &error);
+  cn_cbor_array_append(sign1.get(),
+                       cn_cbor_data_create(signature, kSignatureSize, &error),
+                       &error);
+  *certificate_size = cn_cbor_encoder_write(
+      certificate, 0, dice::test::kTestCertSize, sign1.get());
+}
+
+void CreateCborUdsCertificate(
+    const uint8_t private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
+    dice::test::KeyType key_type, const uint8_t id[DICE_ID_SIZE],
+    uint8_t certificate[dice::test::kTestCertSize], size_t* certificate_size) {
+  switch (key_type) {
+    case dice::test::KeyType_Ed25519:
+      CreateEd25519CborUdsCertificate(private_key_seed, id, certificate,
+                                      certificate_size);
+      break;
+    case dice::test::KeyType_P256:
+      printf(
+          "Error: encountered unsupported KeyType P256 when creating CBOR UDS "
+          "certificate\n");
+      break;
+    case dice::test::KeyType_P384:
+      CreateP384CborUdsCertificate(private_key_seed, id, certificate,
+                                   certificate_size);
+      break;
+  }
+}
+
 ScopedCbor ExtractCwtFromCborCertificate(const uint8_t* certificate,
                                          size_t certificate_size) {
   cn_cbor_errback error;
@@ -622,7 +764,7 @@
   uint8_t raw_key[DICE_PRIVATE_KEY_SEED_SIZE];
   DiceDeriveCdiPrivateKeySeed(context, uds, raw_key);
 
-  uint8_t raw_public_key[33];
+  uint8_t raw_public_key[MAX_PUBLIC_KEY_SIZE];
   size_t raw_public_key_size = 0;
   bssl::UniquePtr<EVP_PKEY> key(
       KeyFromRawKey(raw_key, key_type, raw_public_key, &raw_public_key_size));
@@ -633,7 +775,8 @@
   if (cert_type == CertificateType_X509) {
     CreateX509UdsCertificate(key.get(), id, certificate, certificate_size);
   } else {
-    CreateCborUdsCertificate(raw_key, id, certificate, certificate_size);
+    CreateCborUdsCertificate(raw_key, key_type, id, certificate,
+                             certificate_size);
   }
 
   char filename[100];
diff --git a/third_party/boringssl/.gitignore b/third_party/boringssl/.gitignore
index 656f869..c3389b0 100644
--- a/third_party/boringssl/.gitignore
+++ b/third_party/boringssl/.gitignore
@@ -7,4 +7,5 @@
 linux-*
 mac-*
 win-*
+apple-*
 
diff --git a/third_party/boringssl/BUILD.generated.gni b/third_party/boringssl/BUILD.generated.gni
index fed3f02..194fdbb 100644
--- a/third_party/boringssl/BUILD.generated.gni
+++ b/third_party/boringssl/BUILD.generated.gni
@@ -1,4 +1,4 @@
-# Copyright (c) 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -10,34 +10,30 @@
   "src/crypto/asn1/a_bool.c",
   "src/crypto/asn1/a_d2i_fp.c",
   "src/crypto/asn1/a_dup.c",
-  "src/crypto/asn1/a_enum.c",
   "src/crypto/asn1/a_gentm.c",
   "src/crypto/asn1/a_i2d_fp.c",
   "src/crypto/asn1/a_int.c",
   "src/crypto/asn1/a_mbstr.c",
   "src/crypto/asn1/a_object.c",
   "src/crypto/asn1/a_octet.c",
-  "src/crypto/asn1/a_print.c",
   "src/crypto/asn1/a_strex.c",
   "src/crypto/asn1/a_strnid.c",
   "src/crypto/asn1/a_time.c",
   "src/crypto/asn1/a_type.c",
   "src/crypto/asn1/a_utctm.c",
-  "src/crypto/asn1/a_utf8.c",
   "src/crypto/asn1/asn1_lib.c",
   "src/crypto/asn1/asn1_par.c",
   "src/crypto/asn1/asn_pack.c",
-  "src/crypto/asn1/charmap.h",
   "src/crypto/asn1/f_int.c",
   "src/crypto/asn1/f_string.c",
   "src/crypto/asn1/internal.h",
+  "src/crypto/asn1/posix_time.c",
   "src/crypto/asn1/tasn_dec.c",
   "src/crypto/asn1/tasn_enc.c",
   "src/crypto/asn1/tasn_fre.c",
   "src/crypto/asn1/tasn_new.c",
   "src/crypto/asn1/tasn_typ.c",
   "src/crypto/asn1/tasn_utl.c",
-  "src/crypto/asn1/time_support.c",
   "src/crypto/base64/base64.c",
   "src/crypto/bio/bio.c",
   "src/crypto/bio/bio_mem.c",
@@ -64,33 +60,37 @@
   "src/crypto/chacha/internal.h",
   "src/crypto/cipher_extra/cipher_extra.c",
   "src/crypto/cipher_extra/derive_key.c",
-  "src/crypto/cipher_extra/e_aesccm.c",
   "src/crypto/cipher_extra/e_aesctrhmac.c",
   "src/crypto/cipher_extra/e_aesgcmsiv.c",
   "src/crypto/cipher_extra/e_chacha20poly1305.c",
+  "src/crypto/cipher_extra/e_des.c",
   "src/crypto/cipher_extra/e_null.c",
   "src/crypto/cipher_extra/e_rc2.c",
   "src/crypto/cipher_extra/e_rc4.c",
   "src/crypto/cipher_extra/e_tls.c",
   "src/crypto/cipher_extra/internal.h",
   "src/crypto/cipher_extra/tls_cbc.c",
-  "src/crypto/cmac/cmac.c",
   "src/crypto/conf/conf.c",
   "src/crypto/conf/conf_def.h",
   "src/crypto/conf/internal.h",
-  "src/crypto/cpu-aarch64-fuchsia.c",
-  "src/crypto/cpu-aarch64-linux.c",
-  "src/crypto/cpu-aarch64-win.c",
-  "src/crypto/cpu-arm-linux.c",
-  "src/crypto/cpu-arm-linux.h",
-  "src/crypto/cpu-arm.c",
-  "src/crypto/cpu-intel.c",
-  "src/crypto/cpu-ppc64le.c",
+  "src/crypto/cpu_aarch64_apple.c",
+  "src/crypto/cpu_aarch64_freebsd.c",
+  "src/crypto/cpu_aarch64_fuchsia.c",
+  "src/crypto/cpu_aarch64_linux.c",
+  "src/crypto/cpu_aarch64_win.c",
+  "src/crypto/cpu_arm.c",
+  "src/crypto/cpu_arm_freebsd.c",
+  "src/crypto/cpu_arm_linux.c",
+  "src/crypto/cpu_arm_linux.h",
+  "src/crypto/cpu_intel.c",
+  "src/crypto/cpu_ppc64le.c",
   "src/crypto/crypto.c",
   "src/crypto/curve25519/curve25519.c",
   "src/crypto/curve25519/curve25519_tables.h",
   "src/crypto/curve25519/internal.h",
   "src/crypto/curve25519/spake25519.c",
+  "src/crypto/des/des.c",
+  "src/crypto/des/internal.h",
   "src/crypto/dh_extra/dh_asn1.c",
   "src/crypto/dh_extra/params.c",
   "src/crypto/digest_extra/digest_extra.c",
@@ -106,7 +106,6 @@
   "src/crypto/engine/engine.c",
   "src/crypto/err/err.c",
   "src/crypto/err/internal.h",
-  "src/crypto/evp/digestsign.c",
   "src/crypto/evp/evp.c",
   "src/crypto/evp/evp_asn1.c",
   "src/crypto/evp/evp_ctx.c",
@@ -116,6 +115,7 @@
   "src/crypto/evp/p_ec_asn1.c",
   "src/crypto/evp/p_ed25519.c",
   "src/crypto/evp/p_ed25519_asn1.c",
+  "src/crypto/evp/p_hkdf.c",
   "src/crypto/evp/p_rsa.c",
   "src/crypto/evp/p_rsa_asn1.c",
   "src/crypto/evp/p_x25519.c",
@@ -131,12 +131,12 @@
   "src/crypto/fipsmodule/bn/rsaz_exp.h",
   "src/crypto/fipsmodule/cipher/internal.h",
   "src/crypto/fipsmodule/delocate.h",
-  "src/crypto/fipsmodule/des/internal.h",
+  "src/crypto/fipsmodule/dh/internal.h",
   "src/crypto/fipsmodule/digest/internal.h",
   "src/crypto/fipsmodule/digest/md32_common.h",
   "src/crypto/fipsmodule/ec/internal.h",
-  "src/crypto/fipsmodule/ec/p256-x86_64-table.h",
-  "src/crypto/fipsmodule/ec/p256-x86_64.h",
+  "src/crypto/fipsmodule/ec/p256-nistz-table.h",
+  "src/crypto/fipsmodule/ec/p256-nistz.h",
   "src/crypto/fipsmodule/ec/p256_table.h",
   "src/crypto/fipsmodule/ecdsa/internal.h",
   "src/crypto/fipsmodule/fips_shared_support.c",
@@ -146,6 +146,7 @@
   "src/crypto/fipsmodule/rand/getrandom_fillin.h",
   "src/crypto/fipsmodule/rand/internal.h",
   "src/crypto/fipsmodule/rsa/internal.h",
+  "src/crypto/fipsmodule/service_indicator/internal.h",
   "src/crypto/fipsmodule/sha/internal.h",
   "src/crypto/fipsmodule/tls/internal.h",
   "src/crypto/hkdf/hkdf.c",
@@ -254,7 +255,6 @@
   "src/crypto/x509v3/internal.h",
   "src/crypto/x509v3/pcy_cache.c",
   "src/crypto/x509v3/pcy_data.c",
-  "src/crypto/x509v3/pcy_lib.c",
   "src/crypto/x509v3/pcy_map.c",
   "src/crypto/x509v3/pcy_node.c",
   "src/crypto/x509v3/pcy_tree.c",
@@ -312,6 +312,7 @@
   "src/include/openssl/conf.h",
   "src/include/openssl/cpu.h",
   "src/include/openssl/crypto.h",
+  "src/include/openssl/ctrdrbg.h",
   "src/include/openssl/curve25519.h",
   "src/include/openssl/des.h",
   "src/include/openssl/dh.h",
@@ -332,6 +333,7 @@
   "src/include/openssl/hpke.h",
   "src/include/openssl/hrss.h",
   "src/include/openssl/is_boringssl.h",
+  "src/include/openssl/kdf.h",
   "src/include/openssl/lhash.h",
   "src/include/openssl/md4.h",
   "src/include/openssl/md5.h",
@@ -354,11 +356,13 @@
   "src/include/openssl/ripemd.h",
   "src/include/openssl/rsa.h",
   "src/include/openssl/safestack.h",
+  "src/include/openssl/service_indicator.h",
   "src/include/openssl/sha.h",
   "src/include/openssl/siphash.h",
   "src/include/openssl/span.h",
   "src/include/openssl/stack.h",
   "src/include/openssl/thread.h",
+  "src/include/openssl/time.h",
   "src/include/openssl/trust_token.h",
   "src/include/openssl/type_check.h",
   "src/include/openssl/x509.h",
@@ -437,39 +441,83 @@
   "src/tool/transport_common.h",
 ]
 
-crypto_sources_ios_aarch64 = [
-  "ios-aarch64/crypto/chacha/chacha-armv8.S",
-  "ios-aarch64/crypto/fipsmodule/aesv8-armx64.S",
-  "ios-aarch64/crypto/fipsmodule/armv8-mont.S",
-  "ios-aarch64/crypto/fipsmodule/ghash-neon-armv8.S",
-  "ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S",
-  "ios-aarch64/crypto/fipsmodule/sha1-armv8.S",
-  "ios-aarch64/crypto/fipsmodule/sha256-armv8.S",
-  "ios-aarch64/crypto/fipsmodule/sha512-armv8.S",
-  "ios-aarch64/crypto/fipsmodule/vpaes-armv8.S",
-  "ios-aarch64/crypto/test/trampoline-armv8.S",
+crypto_sources_apple_aarch64 = [
+  "apple-aarch64/crypto/chacha/chacha-armv8.S",
+  "apple-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S",
+  "apple-aarch64/crypto/fipsmodule/aesv8-armx64.S",
+  "apple-aarch64/crypto/fipsmodule/armv8-mont.S",
+  "apple-aarch64/crypto/fipsmodule/ghash-neon-armv8.S",
+  "apple-aarch64/crypto/fipsmodule/ghashv8-armx64.S",
+  "apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S",
+  "apple-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S",
+  "apple-aarch64/crypto/fipsmodule/sha1-armv8.S",
+  "apple-aarch64/crypto/fipsmodule/sha256-armv8.S",
+  "apple-aarch64/crypto/fipsmodule/sha512-armv8.S",
+  "apple-aarch64/crypto/fipsmodule/vpaes-armv8.S",
+  "apple-aarch64/crypto/test/trampoline-armv8.S",
 ]
 
-crypto_sources_ios_arm = [
-  "ios-arm/crypto/chacha/chacha-armv4.S",
-  "ios-arm/crypto/fipsmodule/aesv8-armx32.S",
-  "ios-arm/crypto/fipsmodule/armv4-mont.S",
-  "ios-arm/crypto/fipsmodule/bsaes-armv7.S",
-  "ios-arm/crypto/fipsmodule/ghash-armv4.S",
-  "ios-arm/crypto/fipsmodule/ghashv8-armx32.S",
-  "ios-arm/crypto/fipsmodule/sha1-armv4-large.S",
-  "ios-arm/crypto/fipsmodule/sha256-armv4.S",
-  "ios-arm/crypto/fipsmodule/sha512-armv4.S",
-  "ios-arm/crypto/fipsmodule/vpaes-armv7.S",
-  "ios-arm/crypto/test/trampoline-armv4.S",
+crypto_sources_apple_arm = [
+  "apple-arm/crypto/chacha/chacha-armv4.S",
+  "apple-arm/crypto/fipsmodule/aesv8-armx32.S",
+  "apple-arm/crypto/fipsmodule/armv4-mont.S",
+  "apple-arm/crypto/fipsmodule/bsaes-armv7.S",
+  "apple-arm/crypto/fipsmodule/ghash-armv4.S",
+  "apple-arm/crypto/fipsmodule/ghashv8-armx32.S",
+  "apple-arm/crypto/fipsmodule/sha1-armv4-large.S",
+  "apple-arm/crypto/fipsmodule/sha256-armv4.S",
+  "apple-arm/crypto/fipsmodule/sha512-armv4.S",
+  "apple-arm/crypto/fipsmodule/vpaes-armv7.S",
+  "apple-arm/crypto/test/trampoline-armv4.S",
+]
+
+crypto_sources_apple_x86 = [
+  "apple-x86/crypto/chacha/chacha-x86.S",
+  "apple-x86/crypto/fipsmodule/aesni-x86.S",
+  "apple-x86/crypto/fipsmodule/bn-586.S",
+  "apple-x86/crypto/fipsmodule/co-586.S",
+  "apple-x86/crypto/fipsmodule/ghash-ssse3-x86.S",
+  "apple-x86/crypto/fipsmodule/ghash-x86.S",
+  "apple-x86/crypto/fipsmodule/md5-586.S",
+  "apple-x86/crypto/fipsmodule/sha1-586.S",
+  "apple-x86/crypto/fipsmodule/sha256-586.S",
+  "apple-x86/crypto/fipsmodule/sha512-586.S",
+  "apple-x86/crypto/fipsmodule/vpaes-x86.S",
+  "apple-x86/crypto/fipsmodule/x86-mont.S",
+  "apple-x86/crypto/test/trampoline-x86.S",
+]
+
+crypto_sources_apple_x86_64 = [
+  "apple-x86_64/crypto/chacha/chacha-x86_64.S",
+  "apple-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S",
+  "apple-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S",
+  "apple-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S",
+  "apple-x86_64/crypto/fipsmodule/aesni-x86_64.S",
+  "apple-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S",
+  "apple-x86_64/crypto/fipsmodule/ghash-x86_64.S",
+  "apple-x86_64/crypto/fipsmodule/md5-x86_64.S",
+  "apple-x86_64/crypto/fipsmodule/p256-x86_64-asm.S",
+  "apple-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S",
+  "apple-x86_64/crypto/fipsmodule/rdrand-x86_64.S",
+  "apple-x86_64/crypto/fipsmodule/rsaz-avx2.S",
+  "apple-x86_64/crypto/fipsmodule/sha1-x86_64.S",
+  "apple-x86_64/crypto/fipsmodule/sha256-x86_64.S",
+  "apple-x86_64/crypto/fipsmodule/sha512-x86_64.S",
+  "apple-x86_64/crypto/fipsmodule/vpaes-x86_64.S",
+  "apple-x86_64/crypto/fipsmodule/x86_64-mont.S",
+  "apple-x86_64/crypto/fipsmodule/x86_64-mont5.S",
+  "apple-x86_64/crypto/test/trampoline-x86_64.S",
 ]
 
 crypto_sources_linux_aarch64 = [
   "linux-aarch64/crypto/chacha/chacha-armv8.S",
+  "linux-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S",
   "linux-aarch64/crypto/fipsmodule/aesv8-armx64.S",
   "linux-aarch64/crypto/fipsmodule/armv8-mont.S",
   "linux-aarch64/crypto/fipsmodule/ghash-neon-armv8.S",
   "linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S",
+  "linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S",
+  "linux-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S",
   "linux-aarch64/crypto/fipsmodule/sha1-armv8.S",
   "linux-aarch64/crypto/fipsmodule/sha256-armv8.S",
   "linux-aarch64/crypto/fipsmodule/sha512-armv8.S",
@@ -538,50 +586,15 @@
   "src/crypto/hrss/asm/poly_rq_mul.S",
 ]
 
-crypto_sources_mac_x86 = [
-  "mac-x86/crypto/chacha/chacha-x86.S",
-  "mac-x86/crypto/fipsmodule/aesni-x86.S",
-  "mac-x86/crypto/fipsmodule/bn-586.S",
-  "mac-x86/crypto/fipsmodule/co-586.S",
-  "mac-x86/crypto/fipsmodule/ghash-ssse3-x86.S",
-  "mac-x86/crypto/fipsmodule/ghash-x86.S",
-  "mac-x86/crypto/fipsmodule/md5-586.S",
-  "mac-x86/crypto/fipsmodule/sha1-586.S",
-  "mac-x86/crypto/fipsmodule/sha256-586.S",
-  "mac-x86/crypto/fipsmodule/sha512-586.S",
-  "mac-x86/crypto/fipsmodule/vpaes-x86.S",
-  "mac-x86/crypto/fipsmodule/x86-mont.S",
-  "mac-x86/crypto/test/trampoline-x86.S",
-]
-
-crypto_sources_mac_x86_64 = [
-  "mac-x86_64/crypto/chacha/chacha-x86_64.S",
-  "mac-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S",
-  "mac-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S",
-  "mac-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S",
-  "mac-x86_64/crypto/fipsmodule/aesni-x86_64.S",
-  "mac-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S",
-  "mac-x86_64/crypto/fipsmodule/ghash-x86_64.S",
-  "mac-x86_64/crypto/fipsmodule/md5-x86_64.S",
-  "mac-x86_64/crypto/fipsmodule/p256-x86_64-asm.S",
-  "mac-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S",
-  "mac-x86_64/crypto/fipsmodule/rdrand-x86_64.S",
-  "mac-x86_64/crypto/fipsmodule/rsaz-avx2.S",
-  "mac-x86_64/crypto/fipsmodule/sha1-x86_64.S",
-  "mac-x86_64/crypto/fipsmodule/sha256-x86_64.S",
-  "mac-x86_64/crypto/fipsmodule/sha512-x86_64.S",
-  "mac-x86_64/crypto/fipsmodule/vpaes-x86_64.S",
-  "mac-x86_64/crypto/fipsmodule/x86_64-mont.S",
-  "mac-x86_64/crypto/fipsmodule/x86_64-mont5.S",
-  "mac-x86_64/crypto/test/trampoline-x86_64.S",
-]
-
 crypto_sources_win_aarch64 = [
   "win-aarch64/crypto/chacha/chacha-armv8.S",
+  "win-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S",
   "win-aarch64/crypto/fipsmodule/aesv8-armx64.S",
   "win-aarch64/crypto/fipsmodule/armv8-mont.S",
   "win-aarch64/crypto/fipsmodule/ghash-neon-armv8.S",
   "win-aarch64/crypto/fipsmodule/ghashv8-armx64.S",
+  "win-aarch64/crypto/fipsmodule/p256-armv8-asm.S",
+  "win-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S",
   "win-aarch64/crypto/fipsmodule/sha1-armv8.S",
   "win-aarch64/crypto/fipsmodule/sha256-armv8.S",
   "win-aarch64/crypto/fipsmodule/sha512-armv8.S",
@@ -634,6 +647,7 @@
   "cert",
   "client",
   "decode_client_hello_inner",
+  "der_roundtrip",
   "dtls_client",
   "dtls_server",
   "pkcs12",
diff --git a/third_party/boringssl/README.md b/third_party/boringssl/README.md
index bbeb404..1942162 100644
--- a/third_party/boringssl/README.md
+++ b/third_party/boringssl/README.md
@@ -5,5 +5,5 @@
 
 ```
 cd third_party/boringssl
-python2 src/util/generate_build_files.py gn
+python src/util/generate_build_files.py gn
 ```
diff --git a/third_party/boringssl/err_data.c b/third_party/boringssl/err_data.c
index 98bc22a..7991be6 100644
--- a/third_party/boringssl/err_data.c
+++ b/third_party/boringssl/err_data.c
@@ -16,224 +16,227 @@
 
 #include <openssl/base.h>
 #include <openssl/err.h>
-#include <openssl/type_check.h>
 
+#include <assert.h>
 
-OPENSSL_STATIC_ASSERT(ERR_LIB_NONE == 1, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_SYS == 2, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_BN == 3, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_RSA == 4, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_DH == 5, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_EVP == 6, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_BUF == 7, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_OBJ == 8, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_PEM == 9, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_DSA == 10, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_X509 == 11, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_ASN1 == 12, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_CONF == 13, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_CRYPTO == 14, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_EC == 15, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_SSL == 16, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_BIO == 17, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_PKCS7 == 18, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_PKCS8 == 19, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_X509V3 == 20, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_RAND == 21, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_ENGINE == 22, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_OCSP == 23, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_UI == 24, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_COMP == 25, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_ECDSA == 26, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_ECDH == 27, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_HMAC == 28, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_DIGEST == 29, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_CIPHER == 30, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_HKDF == 31, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_TRUST_TOKEN == 32, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_LIB_USER == 33, "library value changed");
-OPENSSL_STATIC_ASSERT(ERR_NUM_LIBS == 34, "number of libraries changed");
+static_assert(ERR_LIB_NONE == 1, "library value changed");
+static_assert(ERR_LIB_SYS == 2, "library value changed");
+static_assert(ERR_LIB_BN == 3, "library value changed");
+static_assert(ERR_LIB_RSA == 4, "library value changed");
+static_assert(ERR_LIB_DH == 5, "library value changed");
+static_assert(ERR_LIB_EVP == 6, "library value changed");
+static_assert(ERR_LIB_BUF == 7, "library value changed");
+static_assert(ERR_LIB_OBJ == 8, "library value changed");
+static_assert(ERR_LIB_PEM == 9, "library value changed");
+static_assert(ERR_LIB_DSA == 10, "library value changed");
+static_assert(ERR_LIB_X509 == 11, "library value changed");
+static_assert(ERR_LIB_ASN1 == 12, "library value changed");
+static_assert(ERR_LIB_CONF == 13, "library value changed");
+static_assert(ERR_LIB_CRYPTO == 14, "library value changed");
+static_assert(ERR_LIB_EC == 15, "library value changed");
+static_assert(ERR_LIB_SSL == 16, "library value changed");
+static_assert(ERR_LIB_BIO == 17, "library value changed");
+static_assert(ERR_LIB_PKCS7 == 18, "library value changed");
+static_assert(ERR_LIB_PKCS8 == 19, "library value changed");
+static_assert(ERR_LIB_X509V3 == 20, "library value changed");
+static_assert(ERR_LIB_RAND == 21, "library value changed");
+static_assert(ERR_LIB_ENGINE == 22, "library value changed");
+static_assert(ERR_LIB_OCSP == 23, "library value changed");
+static_assert(ERR_LIB_UI == 24, "library value changed");
+static_assert(ERR_LIB_COMP == 25, "library value changed");
+static_assert(ERR_LIB_ECDSA == 26, "library value changed");
+static_assert(ERR_LIB_ECDH == 27, "library value changed");
+static_assert(ERR_LIB_HMAC == 28, "library value changed");
+static_assert(ERR_LIB_DIGEST == 29, "library value changed");
+static_assert(ERR_LIB_CIPHER == 30, "library value changed");
+static_assert(ERR_LIB_HKDF == 31, "library value changed");
+static_assert(ERR_LIB_TRUST_TOKEN == 32, "library value changed");
+static_assert(ERR_LIB_USER == 33, "library value changed");
+static_assert(ERR_NUM_LIBS == 34, "number of libraries changed");
 
 const uint32_t kOpenSSLReasonValues[] = {
-    0xc320862,
-    0xc32887c,
-    0xc33088b,
-    0xc33889b,
-    0xc3408aa,
-    0xc3488c3,
-    0xc3508cf,
-    0xc3588ec,
-    0xc36090c,
-    0xc36891a,
-    0xc37092a,
-    0xc378937,
-    0xc380947,
-    0xc388952,
-    0xc390968,
-    0xc398977,
-    0xc3a098b,
-    0xc3a886f,
+    0xc320885,
+    0xc32889f,
+    0xc3308ae,
+    0xc3388be,
+    0xc3408cd,
+    0xc3488e6,
+    0xc3508f2,
+    0xc35890f,
+    0xc36092f,
+    0xc36893d,
+    0xc37094d,
+    0xc37895a,
+    0xc38096a,
+    0xc388975,
+    0xc39098b,
+    0xc39899a,
+    0xc3a09ae,
+    0xc3a8892,
     0xc3b00f7,
-    0xc3b88fe,
-    0x1032086f,
-    0x103295e5,
-    0x103315f1,
-    0x1033960a,
-    0x1034161d,
-    0x10348f4f,
-    0x10350c88,
-    0x10359630,
-    0x1036165a,
-    0x1036966d,
-    0x1037168c,
-    0x103796a5,
-    0x103816ba,
-    0x103896d8,
-    0x103916e7,
-    0x10399703,
-    0x103a171e,
-    0x103a972d,
-    0x103b1749,
-    0x103b9764,
-    0x103c178a,
+    0xc3b8921,
+    0x10320892,
+    0x10329620,
+    0x1033162c,
+    0x10339645,
+    0x10341658,
+    0x10348f72,
+    0x10350cab,
+    0x1035966b,
+    0x10361695,
+    0x103696a8,
+    0x103716c7,
+    0x103796e0,
+    0x103816f5,
+    0x10389713,
+    0x10391722,
+    0x1039973e,
+    0x103a1759,
+    0x103a9768,
+    0x103b1784,
+    0x103b979f,
+    0x103c17c5,
     0x103c80f7,
-    0x103d179b,
-    0x103d97af,
-    0x103e17ce,
-    0x103e97dd,
-    0x103f17f4,
-    0x103f9807,
-    0x10400c4c,
-    0x1040981a,
-    0x10411838,
-    0x1041984b,
-    0x10421865,
-    0x10429875,
-    0x10431889,
-    0x1043989f,
-    0x104418b7,
-    0x104498cc,
-    0x104518e0,
-    0x104598f2,
-    0x10460625,
-    0x10468977,
-    0x10471907,
-    0x1047991e,
-    0x10481933,
-    0x10489941,
-    0x10490e9b,
-    0x1049977b,
-    0x104a1645,
-    0x14320c2f,
-    0x14328c3d,
-    0x14330c4c,
-    0x14338c5e,
+    0x103d17d6,
+    0x103d97ea,
+    0x103e1809,
+    0x103e9818,
+    0x103f182f,
+    0x103f9842,
+    0x10400c6f,
+    0x10409855,
+    0x10411873,
+    0x10419886,
+    0x104218a0,
+    0x104298b0,
+    0x104318c4,
+    0x104398da,
+    0x104418f2,
+    0x10449907,
+    0x1045191b,
+    0x1045992d,
+    0x10460635,
+    0x1046899a,
+    0x10471942,
+    0x10479959,
+    0x1048196e,
+    0x1048997c,
+    0x10490ebe,
+    0x104997b6,
+    0x104a1680,
+    0x14320c52,
+    0x14328c60,
+    0x14330c6f,
+    0x14338c81,
     0x143400b9,
     0x143480f7,
     0x18320090,
-    0x18328fa5,
+    0x18328fc8,
     0x183300b9,
-    0x18338fbb,
-    0x18340fcf,
+    0x18338fde,
+    0x18340ff2,
     0x183480f7,
-    0x18350fee,
-    0x18359006,
-    0x1836101b,
-    0x1836902f,
-    0x18371067,
-    0x1837907d,
-    0x18381091,
-    0x183890a1,
-    0x18390a9d,
-    0x183990b1,
-    0x183a10d7,
-    0x183a90fd,
-    0x183b0ca7,
-    0x183b914c,
-    0x183c115e,
-    0x183c9169,
-    0x183d1179,
-    0x183d918a,
-    0x183e119b,
-    0x183e91ad,
-    0x183f11d6,
-    0x183f91ef,
-    0x18401207,
-    0x184086fd,
-    0x18411120,
-    0x184190eb,
-    0x1842110a,
-    0x18428c94,
-    0x184310c6,
-    0x18439132,
-    0x18440fe4,
-    0x18449053,
-    0x20321241,
-    0x2032922e,
-    0x2432124d,
-    0x243289bd,
-    0x2433125f,
-    0x2433926c,
-    0x24341279,
-    0x2434928b,
-    0x2435129a,
-    0x243592b7,
-    0x243612c4,
-    0x243692d2,
-    0x243712e0,
-    0x243792ee,
-    0x243812f7,
-    0x24389304,
-    0x24391317,
-    0x28320c7c,
-    0x28328ca7,
-    0x28330c4c,
-    0x28338cba,
-    0x28340c88,
+    0x18351011,
+    0x18359029,
+    0x1836103e,
+    0x18369052,
+    0x1837108a,
+    0x183790a0,
+    0x183810b4,
+    0x183890c4,
+    0x18390ac0,
+    0x183990d4,
+    0x183a10fa,
+    0x183a9120,
+    0x183b0cca,
+    0x183b916f,
+    0x183c1181,
+    0x183c918c,
+    0x183d119c,
+    0x183d91ad,
+    0x183e11be,
+    0x183e91d0,
+    0x183f11f9,
+    0x183f9212,
+    0x1840122a,
+    0x1840870d,
+    0x18411143,
+    0x1841910e,
+    0x1842112d,
+    0x18428cb7,
+    0x184310e9,
+    0x18439155,
+    0x18441007,
+    0x18449076,
+    0x20321264,
+    0x20329251,
+    0x24321270,
+    0x243289e0,
+    0x24331282,
+    0x2433928f,
+    0x2434129c,
+    0x243492ae,
+    0x243512bd,
+    0x243592da,
+    0x243612e7,
+    0x243692f5,
+    0x24371303,
+    0x24379311,
+    0x2438131a,
+    0x24389327,
+    0x2439133a,
+    0x28320c9f,
+    0x28328cca,
+    0x28330c6f,
+    0x28338cdd,
+    0x28340cab,
     0x283480b9,
     0x283500f7,
-    0x28358c94,
-    0x2c323286,
-    0x2c32932e,
-    0x2c333294,
-    0x2c33b2a6,
-    0x2c3432ba,
-    0x2c34b2cc,
-    0x2c3532e7,
-    0x2c35b2f9,
-    0x2c363329,
+    0x28358cb7,
+    0x2c3232bf,
+    0x2c329351,
+    0x2c3332cd,
+    0x2c33b2df,
+    0x2c3432f3,
+    0x2c34b305,
+    0x2c353320,
+    0x2c35b332,
+    0x2c363362,
     0x2c36833a,
-    0x2c373336,
-    0x2c37b362,
-    0x2c383387,
-    0x2c38b39e,
-    0x2c3933bc,
-    0x2c39b3cc,
-    0x2c3a33de,
-    0x2c3ab3f2,
-    0x2c3b3403,
-    0x2c3bb422,
-    0x2c3c1340,
-    0x2c3c9356,
-    0x2c3d3436,
-    0x2c3d936f,
-    0x2c3e3453,
-    0x2c3eb461,
-    0x2c3f3479,
-    0x2c3fb491,
-    0x2c4034bb,
-    0x2c409241,
-    0x2c4134cc,
-    0x2c41b4df,
-    0x2c421207,
-    0x2c42b4f0,
-    0x2c43074a,
-    0x2c43b414,
-    0x2c443375,
-    0x2c44b49e,
-    0x2c45330c,
-    0x2c45b348,
-    0x2c4633ac,
+    0x2c37336f,
+    0x2c37b39b,
+    0x2c3833c0,
+    0x2c38b3d7,
+    0x2c3933f5,
+    0x2c39b405,
+    0x2c3a3417,
+    0x2c3ab42b,
+    0x2c3b343c,
+    0x2c3bb45b,
+    0x2c3c1363,
+    0x2c3c9379,
+    0x2c3d34a0,
+    0x2c3d9392,
+    0x2c3e34ca,
+    0x2c3eb4d8,
+    0x2c3f34f0,
+    0x2c3fb508,
+    0x2c403532,
+    0x2c409264,
+    0x2c413543,
+    0x2c41b556,
+    0x2c42122a,
+    0x2c42b567,
+    0x2c43076d,
+    0x2c43b44d,
+    0x2c4433ae,
+    0x2c44b515,
+    0x2c453345,
+    0x2c45b381,
+    0x2c4633e5,
+    0x2c46b46f,
+    0x2c473484,
+    0x2c47b4bd,
     0x30320000,
     0x30328015,
     0x3033001f,
@@ -278,528 +281,531 @@
     0x3046833a,
     0x30470372,
     0x30478384,
-    0x30480392,
-    0x304883a3,
-    0x304903b2,
-    0x304983ca,
-    0x304a03dc,
-    0x304a83f0,
-    0x304b0408,
-    0x304b841b,
-    0x304c0426,
-    0x304c8437,
-    0x304d0443,
-    0x304d8459,
-    0x304e0467,
-    0x304e847d,
-    0x304f048f,
-    0x304f84a1,
-    0x305004c4,
-    0x305084d7,
-    0x305104e8,
-    0x305184f8,
-    0x30520510,
-    0x30528525,
-    0x3053053d,
-    0x30538551,
-    0x30540569,
-    0x30548582,
-    0x3055059b,
-    0x305585b8,
-    0x305605c3,
-    0x305685db,
-    0x305705eb,
-    0x305785fc,
-    0x3058060f,
-    0x30588625,
-    0x3059062e,
-    0x30598643,
-    0x305a0656,
-    0x305a8665,
-    0x305b0685,
-    0x305b8694,
-    0x305c06b5,
-    0x305c86d1,
-    0x305d06dd,
-    0x305d86fd,
-    0x305e0719,
-    0x305e872a,
-    0x305f0740,
-    0x305f874a,
-    0x306004b4,
+    0x304803a2,
+    0x304883b3,
+    0x304903c2,
+    0x304983da,
+    0x304a03ec,
+    0x304a8400,
+    0x304b0418,
+    0x304b842b,
+    0x304c0436,
+    0x304c8447,
+    0x304d0453,
+    0x304d8469,
+    0x304e0477,
+    0x304e848d,
+    0x304f049f,
+    0x304f84b1,
+    0x305004d4,
+    0x305084e7,
+    0x305104f8,
+    0x30518508,
+    0x30520520,
+    0x30528535,
+    0x3053054d,
+    0x30538561,
+    0x30540579,
+    0x30548592,
+    0x305505ab,
+    0x305585c8,
+    0x305605d3,
+    0x305685eb,
+    0x305705fb,
+    0x3057860c,
+    0x3058061f,
+    0x30588635,
+    0x3059063e,
+    0x30598653,
+    0x305a0666,
+    0x305a8675,
+    0x305b0695,
+    0x305b86a4,
+    0x305c06c5,
+    0x305c86e1,
+    0x305d06ed,
+    0x305d870d,
+    0x305e0729,
+    0x305e874d,
+    0x305f0763,
+    0x305f876d,
+    0x306004c4,
     0x3060804a,
     0x30610357,
-    0x34320b8d,
-    0x34328ba1,
-    0x34330bbe,
-    0x34338bd1,
-    0x34340be0,
-    0x34348c19,
-    0x34350bfd,
+    0x3061873a,
+    0x30620392,
+    0x34320bb0,
+    0x34328bc4,
+    0x34330be1,
+    0x34338bf4,
+    0x34340c03,
+    0x34348c3c,
+    0x34350c20,
     0x3c320090,
-    0x3c328ce4,
-    0x3c330cfd,
-    0x3c338d18,
-    0x3c340d35,
-    0x3c348d5f,
-    0x3c350d7a,
-    0x3c358da0,
-    0x3c360db9,
-    0x3c368dd1,
-    0x3c370de2,
-    0x3c378df0,
-    0x3c380dfd,
-    0x3c388e11,
-    0x3c390ca7,
-    0x3c398e34,
-    0x3c3a0e48,
-    0x3c3a8937,
-    0x3c3b0e58,
-    0x3c3b8e73,
-    0x3c3c0e85,
-    0x3c3c8eb8,
-    0x3c3d0ec2,
-    0x3c3d8ed6,
-    0x3c3e0ee4,
-    0x3c3e8f09,
-    0x3c3f0cd0,
-    0x3c3f8ef2,
+    0x3c328d07,
+    0x3c330d20,
+    0x3c338d3b,
+    0x3c340d58,
+    0x3c348d82,
+    0x3c350d9d,
+    0x3c358dc3,
+    0x3c360ddc,
+    0x3c368df4,
+    0x3c370e05,
+    0x3c378e13,
+    0x3c380e20,
+    0x3c388e34,
+    0x3c390cca,
+    0x3c398e57,
+    0x3c3a0e6b,
+    0x3c3a895a,
+    0x3c3b0e7b,
+    0x3c3b8e96,
+    0x3c3c0ea8,
+    0x3c3c8edb,
+    0x3c3d0ee5,
+    0x3c3d8ef9,
+    0x3c3e0f07,
+    0x3c3e8f2c,
+    0x3c3f0cf3,
+    0x3c3f8f15,
     0x3c4000b9,
     0x3c4080f7,
-    0x3c410d50,
-    0x3c418d8f,
-    0x3c420e9b,
-    0x3c428e25,
-    0x403219d3,
-    0x403299e9,
-    0x40331a17,
-    0x40339a21,
-    0x40341a38,
-    0x40349a56,
-    0x40351a66,
-    0x40359a78,
-    0x40361a85,
-    0x40369a91,
-    0x40371aa6,
-    0x40379ab8,
-    0x40381ac3,
-    0x40389ad5,
-    0x40390f4f,
-    0x40399ae5,
-    0x403a1af8,
-    0x403a9b19,
-    0x403b1b2a,
-    0x403b9b3a,
+    0x3c410d73,
+    0x3c418db2,
+    0x3c420ebe,
+    0x3c428e48,
+    0x40321a0e,
+    0x40329a24,
+    0x40331a52,
+    0x40339a5c,
+    0x40341a73,
+    0x40349a91,
+    0x40351aa1,
+    0x40359ab3,
+    0x40361ac0,
+    0x40369acc,
+    0x40371ae1,
+    0x40379af3,
+    0x40381afe,
+    0x40389b10,
+    0x40390f72,
+    0x40399b20,
+    0x403a1b33,
+    0x403a9b54,
+    0x403b1b65,
+    0x403b9b75,
     0x403c0071,
     0x403c8090,
-    0x403d1b9b,
-    0x403d9bb1,
-    0x403e1bc0,
-    0x403e9bf8,
-    0x403f1c12,
-    0x403f9c3a,
-    0x40401c4f,
-    0x40409c63,
-    0x40411c9e,
-    0x40419cb9,
-    0x40421cd2,
-    0x40429ce5,
-    0x40431cf9,
-    0x40439d27,
-    0x40441d3e,
+    0x403d1bd6,
+    0x403d9bec,
+    0x403e1bfb,
+    0x403e9c33,
+    0x403f1c4d,
+    0x403f9c75,
+    0x40401c8a,
+    0x40409c9e,
+    0x40411cd9,
+    0x40419cf4,
+    0x40421d0d,
+    0x40429d20,
+    0x40431d34,
+    0x40439d62,
+    0x40441d79,
     0x404480b9,
-    0x40451d53,
-    0x40459d65,
-    0x40461d89,
-    0x40469da9,
-    0x40471db7,
-    0x40479dde,
-    0x40481e4f,
-    0x40489f09,
-    0x40491f20,
-    0x40499f3a,
-    0x404a1f51,
-    0x404a9f6f,
-    0x404b1f87,
-    0x404b9fb4,
-    0x404c1fca,
-    0x404c9fdc,
-    0x404d1ffd,
-    0x404da036,
-    0x404e204a,
-    0x404ea057,
-    0x404f20f1,
-    0x404fa167,
-    0x405021be,
-    0x4050a1d2,
-    0x40512205,
-    0x40522215,
-    0x4052a239,
-    0x40532251,
-    0x4053a264,
-    0x40542279,
-    0x4054a29c,
-    0x405522c7,
-    0x4055a304,
-    0x40562329,
-    0x4056a342,
-    0x4057235a,
-    0x4057a36d,
-    0x40582382,
-    0x4058a3a9,
-    0x405923d8,
-    0x4059a405,
-    0x405a2419,
-    0x405aa429,
-    0x405b2441,
-    0x405ba452,
-    0x405c2465,
-    0x405ca4a4,
-    0x405d24b1,
-    0x405da4d6,
-    0x405e2514,
-    0x405e8adb,
-    0x405f254f,
-    0x405fa55c,
-    0x4060256a,
-    0x4060a58c,
-    0x406125ed,
-    0x4061a625,
-    0x4062263c,
-    0x4062a64d,
-    0x4063269a,
-    0x4063a6af,
-    0x406426c6,
-    0x4064a6f2,
-    0x4065270d,
-    0x4065a724,
-    0x4066273c,
-    0x4066a766,
-    0x40672791,
-    0x4067a7d6,
-    0x4068281e,
-    0x4068a83f,
-    0x40692871,
-    0x4069a89f,
-    0x406a28c0,
-    0x406aa8e0,
-    0x406b2a68,
-    0x406baa8b,
-    0x406c2aa1,
-    0x406cadab,
-    0x406d2dda,
-    0x406dae02,
-    0x406e2e30,
-    0x406eae7d,
-    0x406f2ed6,
-    0x406faf0e,
-    0x40702f21,
-    0x4070af3e,
-    0x4071082a,
-    0x4071af50,
-    0x40722f63,
-    0x4072af99,
-    0x40732fb1,
-    0x40739540,
-    0x40742fc5,
-    0x4074afdf,
-    0x40752ff0,
-    0x4075b004,
-    0x40763012,
-    0x40769304,
-    0x40773037,
-    0x4077b077,
-    0x40783092,
-    0x4078b0cb,
-    0x407930e2,
-    0x4079b0f8,
-    0x407a3124,
-    0x407ab137,
-    0x407b314c,
-    0x407bb15e,
-    0x407c318f,
-    0x407cb198,
-    0x407d285a,
-    0x407da177,
-    0x407e30a7,
-    0x407ea3b9,
-    0x407f1dcb,
-    0x407f9f9e,
-    0x40802101,
-    0x40809df3,
-    0x40812227,
-    0x4081a0a5,
-    0x40822e1b,
-    0x40829b46,
-    0x40832394,
-    0x4083a6d7,
-    0x40841e07,
-    0x4084a3f1,
-    0x40852476,
-    0x4085a5b4,
-    0x408624f6,
-    0x4086a191,
-    0x40872e61,
-    0x4087a602,
-    0x40881b84,
-    0x4088a7e9,
-    0x40891bd3,
-    0x40899b60,
-    0x408a2ad9,
-    0x408a9958,
-    0x408b3173,
-    0x408baeeb,
-    0x408c2486,
-    0x408c9990,
-    0x408d1eef,
-    0x408d9e39,
-    0x408e201f,
-    0x408ea2e4,
-    0x408f27fd,
-    0x408fa5d0,
-    0x409027b2,
-    0x4090a4c8,
-    0x40912ac1,
-    0x409199b6,
-    0x40921c20,
-    0x4092ae9c,
-    0x40932f7c,
-    0x4093a1a2,
-    0x40941e1b,
-    0x4094aaf2,
-    0x4095265e,
-    0x4095b104,
-    0x40962e48,
-    0x4096a11a,
-    0x409721ed,
-    0x4097a06e,
-    0x40981c80,
-    0x4098a672,
-    0x40992eb8,
-    0x4099a311,
-    0x409a22aa,
-    0x409a9974,
-    0x409b1e75,
-    0x409b9ea0,
-    0x409c3059,
-    0x409c9ec8,
-    0x409d20d6,
-    0x409da0bb,
-    0x409e1d11,
-    0x409ea14f,
-    0x409f2137,
-    0x409f9e68,
-    0x40a02535,
-    0x40a0a088,
-    0x41f42993,
-    0x41f92a25,
-    0x41fe2918,
-    0x41feabce,
-    0x41ff2cfc,
-    0x420329ac,
-    0x420829ce,
-    0x4208aa0a,
-    0x420928fc,
-    0x4209aa44,
-    0x420a2953,
-    0x420aa933,
-    0x420b2973,
-    0x420ba9ec,
-    0x420c2d18,
-    0x420cab02,
-    0x420d2bb5,
-    0x420dabec,
-    0x42122c1f,
-    0x42172cdf,
-    0x4217ac61,
-    0x421c2c83,
-    0x421f2c3e,
-    0x42212d90,
-    0x42262cc2,
-    0x422b2d6e,
-    0x422bab90,
-    0x422c2d50,
-    0x422cab43,
-    0x422d2b1c,
-    0x422dad2f,
-    0x422e2b6f,
-    0x42302c9e,
-    0x4230ac06,
-    0x44320755,
-    0x44328764,
-    0x44330770,
-    0x4433877e,
-    0x44340791,
-    0x443487a2,
-    0x443507a9,
-    0x443587b3,
-    0x443607c6,
-    0x443687dc,
-    0x443707ee,
-    0x443787fb,
-    0x4438080a,
-    0x44388812,
-    0x4439082a,
-    0x44398838,
-    0x443a084b,
-    0x4832132e,
-    0x48329340,
-    0x48331356,
-    0x4833936f,
-    0x4c321394,
-    0x4c3293a4,
-    0x4c3313b7,
-    0x4c3393d7,
+    0x40451d8e,
+    0x40459da0,
+    0x40461dc4,
+    0x40469de4,
+    0x40471df2,
+    0x40479e19,
+    0x40481e8a,
+    0x40489f44,
+    0x40491f5b,
+    0x40499f75,
+    0x404a1f8c,
+    0x404a9faa,
+    0x404b1fc2,
+    0x404b9fef,
+    0x404c2005,
+    0x404ca017,
+    0x404d2038,
+    0x404da071,
+    0x404e2085,
+    0x404ea092,
+    0x404f212c,
+    0x404fa1a2,
+    0x40502211,
+    0x4050a225,
+    0x40512258,
+    0x40522268,
+    0x4052a28c,
+    0x405322a4,
+    0x4053a2b7,
+    0x405422cc,
+    0x4054a2ef,
+    0x4055231a,
+    0x4055a357,
+    0x4056237c,
+    0x4056a395,
+    0x405723ad,
+    0x4057a3c0,
+    0x405823d5,
+    0x4058a3fc,
+    0x4059242b,
+    0x4059a458,
+    0x405a246c,
+    0x405aa47c,
+    0x405b2494,
+    0x405ba4a5,
+    0x405c24b8,
+    0x405ca4f7,
+    0x405d2504,
+    0x405da529,
+    0x405e2567,
+    0x405e8afe,
+    0x405f2588,
+    0x405fa595,
+    0x406025a3,
+    0x4060a5c5,
+    0x40612626,
+    0x4061a65e,
+    0x40622675,
+    0x4062a686,
+    0x406326d3,
+    0x4063a6e8,
+    0x406426ff,
+    0x4064a72b,
+    0x40652746,
+    0x4065a75d,
+    0x40662775,
+    0x4066a79f,
+    0x406727ca,
+    0x4067a80f,
+    0x40682857,
+    0x4068a878,
+    0x406928aa,
+    0x4069a8d8,
+    0x406a28f9,
+    0x406aa919,
+    0x406b2aa1,
+    0x406baac4,
+    0x406c2ada,
+    0x406cade4,
+    0x406d2e13,
+    0x406dae3b,
+    0x406e2e69,
+    0x406eaeb6,
+    0x406f2f0f,
+    0x406faf47,
+    0x40702f5a,
+    0x4070af77,
+    0x4071084d,
+    0x4071af89,
+    0x40722f9c,
+    0x4072afd2,
+    0x40732fea,
+    0x4073957b,
+    0x40742ffe,
+    0x4074b018,
+    0x40753029,
+    0x4075b03d,
+    0x4076304b,
+    0x40769327,
+    0x40773070,
+    0x4077b0b0,
+    0x407830cb,
+    0x4078b104,
+    0x4079311b,
+    0x4079b131,
+    0x407a315d,
+    0x407ab170,
+    0x407b3185,
+    0x407bb197,
+    0x407c31c8,
+    0x407cb1d1,
+    0x407d2893,
+    0x407da1ca,
+    0x407e30e0,
+    0x407ea40c,
+    0x407f1e06,
+    0x407f9fd9,
+    0x4080213c,
+    0x40809e2e,
+    0x4081227a,
+    0x4081a0e0,
+    0x40822e54,
+    0x40829b81,
+    0x408323e7,
+    0x4083a710,
+    0x40841e42,
+    0x4084a444,
+    0x408524c9,
+    0x4085a5ed,
+    0x40862549,
+    0x4086a1e4,
+    0x40872e9a,
+    0x4087a63b,
+    0x40881bbf,
+    0x4088a822,
+    0x40891c0e,
+    0x40899b9b,
+    0x408a2b12,
+    0x408a9993,
+    0x408b31ac,
+    0x408baf24,
+    0x408c24d9,
+    0x408c99cb,
+    0x408d1f2a,
+    0x408d9e74,
+    0x408e205a,
+    0x408ea337,
+    0x408f2836,
+    0x408fa609,
+    0x409027eb,
+    0x4090a51b,
+    0x40912afa,
+    0x409199f1,
+    0x40921c5b,
+    0x4092aed5,
+    0x40932fb5,
+    0x4093a1f5,
+    0x40941e56,
+    0x4094ab2b,
+    0x40952697,
+    0x4095b13d,
+    0x40962e81,
+    0x4096a155,
+    0x40972240,
+    0x4097a0a9,
+    0x40981cbb,
+    0x4098a6ab,
+    0x40992ef1,
+    0x4099a364,
+    0x409a22fd,
+    0x409a99af,
+    0x409b1eb0,
+    0x409b9edb,
+    0x409c3092,
+    0x409c9f03,
+    0x409d2111,
+    0x409da0f6,
+    0x409e1d4c,
+    0x409ea18a,
+    0x409f2172,
+    0x409f9ea3,
+    0x40a021b2,
+    0x40a0a0c3,
+    0x41f429cc,
+    0x41f92a5e,
+    0x41fe2951,
+    0x41feac07,
+    0x41ff2d35,
+    0x420329e5,
+    0x42082a07,
+    0x4208aa43,
+    0x42092935,
+    0x4209aa7d,
+    0x420a298c,
+    0x420aa96c,
+    0x420b29ac,
+    0x420baa25,
+    0x420c2d51,
+    0x420cab3b,
+    0x420d2bee,
+    0x420dac25,
+    0x42122c58,
+    0x42172d18,
+    0x4217ac9a,
+    0x421c2cbc,
+    0x421f2c77,
+    0x42212dc9,
+    0x42262cfb,
+    0x422b2da7,
+    0x422babc9,
+    0x422c2d89,
+    0x422cab7c,
+    0x422d2b55,
+    0x422dad68,
+    0x422e2ba8,
+    0x42302cd7,
+    0x4230ac3f,
+    0x44320778,
+    0x44328787,
+    0x44330793,
+    0x443387a1,
+    0x443407b4,
+    0x443487c5,
+    0x443507cc,
+    0x443587d6,
+    0x443607e9,
+    0x443687ff,
+    0x44370811,
+    0x4437881e,
+    0x4438082d,
+    0x44388835,
+    0x4439084d,
+    0x4439885b,
+    0x443a086e,
+    0x48321351,
+    0x48329363,
+    0x48331379,
+    0x48339392,
+    0x4c3213cf,
+    0x4c3293df,
+    0x4c3313f2,
+    0x4c339412,
     0x4c3400b9,
     0x4c3480f7,
-    0x4c3513e3,
-    0x4c3593f1,
-    0x4c36140d,
-    0x4c369433,
-    0x4c371442,
-    0x4c379450,
-    0x4c381465,
-    0x4c389471,
-    0x4c391491,
-    0x4c3994bb,
-    0x4c3a14d4,
-    0x4c3a94ed,
-    0x4c3b0625,
-    0x4c3b9506,
-    0x4c3c1518,
-    0x4c3c9527,
-    0x4c3d1540,
-    0x4c3d8c6f,
-    0x4c3e15ad,
-    0x4c3e954f,
-    0x4c3f15cf,
-    0x4c3f9304,
-    0x4c401565,
-    0x4c409380,
-    0x4c41159d,
-    0x4c419420,
-    0x4c421589,
-    0x50323502,
-    0x5032b511,
-    0x5033351c,
-    0x5033b52c,
-    0x50343545,
-    0x5034b55f,
-    0x5035356d,
-    0x5035b583,
-    0x50363595,
-    0x5036b5ab,
-    0x503735c4,
-    0x5037b5d7,
-    0x503835ef,
-    0x5038b600,
-    0x50393615,
-    0x5039b629,
-    0x503a3649,
-    0x503ab65f,
-    0x503b3677,
-    0x503bb689,
-    0x503c36a5,
-    0x503cb6bc,
-    0x503d36d5,
-    0x503db6eb,
-    0x503e36f8,
-    0x503eb70e,
-    0x503f3720,
-    0x503f83a3,
-    0x50403733,
-    0x5040b743,
-    0x5041375d,
-    0x5041b76c,
-    0x50423786,
-    0x5042b7a3,
-    0x504337b3,
-    0x5043b7c3,
-    0x504437e0,
-    0x50448459,
-    0x504537f4,
-    0x5045b812,
-    0x50463825,
-    0x5046b83b,
-    0x5047384d,
-    0x5047b862,
-    0x50483888,
-    0x5048b896,
-    0x504938a9,
-    0x5049b8be,
-    0x504a38d4,
-    0x504ab8e4,
-    0x504b3904,
-    0x504bb917,
-    0x504c393a,
-    0x504cb968,
-    0x504d3995,
-    0x504db9b2,
-    0x504e39cd,
-    0x504eb9e9,
-    0x504f39fb,
-    0x504fba12,
-    0x50503a21,
-    0x50508719,
-    0x50513a34,
-    0x5051b7d2,
-    0x5052397a,
-    0x58320f8d,
-    0x68320f4f,
-    0x68328ca7,
-    0x68330cba,
-    0x68338f5d,
-    0x68340f6d,
+    0x4c35141e,
+    0x4c35942c,
+    0x4c361448,
+    0x4c36946e,
+    0x4c37147d,
+    0x4c37948b,
+    0x4c3814a0,
+    0x4c3894ac,
+    0x4c3914cc,
+    0x4c3994f6,
+    0x4c3a150f,
+    0x4c3a9528,
+    0x4c3b0635,
+    0x4c3b9541,
+    0x4c3c1553,
+    0x4c3c9562,
+    0x4c3d157b,
+    0x4c3d8c92,
+    0x4c3e15e8,
+    0x4c3e958a,
+    0x4c3f160a,
+    0x4c3f9327,
+    0x4c4015a0,
+    0x4c4093bb,
+    0x4c4115d8,
+    0x4c41945b,
+    0x4c4215c4,
+    0x4c4293a3,
+    0x50323579,
+    0x5032b588,
+    0x50333593,
+    0x5033b5a3,
+    0x503435bc,
+    0x5034b5d6,
+    0x503535e4,
+    0x5035b5fa,
+    0x5036360c,
+    0x5036b622,
+    0x5037363b,
+    0x5037b64e,
+    0x50383666,
+    0x5038b677,
+    0x5039368c,
+    0x5039b6a0,
+    0x503a36c0,
+    0x503ab6d6,
+    0x503b36ee,
+    0x503bb700,
+    0x503c371c,
+    0x503cb733,
+    0x503d374c,
+    0x503db762,
+    0x503e376f,
+    0x503eb785,
+    0x503f3797,
+    0x503f83b3,
+    0x504037aa,
+    0x5040b7ba,
+    0x504137d4,
+    0x5041b7e3,
+    0x504237fd,
+    0x5042b81a,
+    0x5043382a,
+    0x5043b83a,
+    0x50443857,
+    0x50448469,
+    0x5045386b,
+    0x5045b889,
+    0x5046389c,
+    0x5046b8b2,
+    0x504738c4,
+    0x5047b8d9,
+    0x504838ff,
+    0x5048b90d,
+    0x50493920,
+    0x5049b935,
+    0x504a394b,
+    0x504ab95b,
+    0x504b397b,
+    0x504bb98e,
+    0x504c39b1,
+    0x504cb9df,
+    0x504d3a0c,
+    0x504dba29,
+    0x504e3a44,
+    0x504eba60,
+    0x504f3a72,
+    0x504fba89,
+    0x50503a98,
+    0x50508729,
+    0x50513aab,
+    0x5051b849,
+    0x505239f1,
+    0x58320fb0,
+    0x68320f72,
+    0x68328cca,
+    0x68330cdd,
+    0x68338f80,
+    0x68340f90,
     0x683480f7,
-    0x6c320f15,
-    0x6c328c5e,
-    0x6c330f20,
-    0x6c338f39,
-    0x74320a43,
+    0x6c320f38,
+    0x6c328c81,
+    0x6c330f43,
+    0x6c338f5c,
+    0x74320a66,
     0x743280b9,
-    0x74330c6f,
-    0x783209a8,
-    0x783289bd,
-    0x783309c9,
+    0x74330c92,
+    0x783209cb,
+    0x783289e0,
+    0x783309ec,
     0x78338090,
-    0x783409d8,
-    0x783489ed,
-    0x78350a0c,
-    0x78358a2e,
-    0x78360a43,
-    0x78368a59,
-    0x78370a69,
-    0x78378a8a,
-    0x78380a9d,
-    0x78388aaf,
-    0x78390abc,
-    0x78398adb,
-    0x783a0af0,
-    0x783a8afe,
-    0x783b0b08,
-    0x783b8b1c,
-    0x783c0b33,
-    0x783c8b48,
-    0x783d0b5f,
-    0x783d8b74,
-    0x783e0aca,
-    0x783e8a7c,
-    0x7c32121d,
-    0x80321433,
+    0x783409fb,
+    0x78348a10,
+    0x78350a2f,
+    0x78358a51,
+    0x78360a66,
+    0x78368a7c,
+    0x78370a8c,
+    0x78378aad,
+    0x78380ac0,
+    0x78388ad2,
+    0x78390adf,
+    0x78398afe,
+    0x783a0b13,
+    0x783a8b21,
+    0x783b0b2b,
+    0x783b8b3f,
+    0x783c0b56,
+    0x783c8b6b,
+    0x783d0b82,
+    0x783d8b97,
+    0x783e0aed,
+    0x783e8a9f,
+    0x7c321240,
+    0x8032146e,
     0x80328090,
-    0x80333255,
+    0x8033328e,
     0x803380b9,
-    0x80343264,
-    0x8034b1cc,
-    0x803531ea,
-    0x8035b278,
-    0x8036322c,
-    0x8036b1db,
-    0x8037321e,
-    0x8037b1b9,
-    0x8038323f,
-    0x8038b1fb,
-    0x80393210,
+    0x8034329d,
+    0x8034b205,
+    0x80353223,
+    0x8035b2b1,
+    0x80363265,
+    0x8036b214,
+    0x80373257,
+    0x8037b1f2,
+    0x80383278,
+    0x8038b234,
+    0x80393249,
 };
 
 const size_t kOpenSSLReasonValuesLen = sizeof(kOpenSSLReasonValues) / sizeof(kOpenSSLReasonValues[0]);
@@ -851,6 +857,7 @@
     "INVALID_BIT_STRING_PADDING\0"
     "INVALID_BMPSTRING\0"
     "INVALID_DIGIT\0"
+    "INVALID_INTEGER\0"
     "INVALID_MODIFIER\0"
     "INVALID_NUMBER\0"
     "INVALID_OBJECT_ENCODING\0"
@@ -897,6 +904,7 @@
     "UNSUPPORTED_ANY_DEFINED_BY_TYPE\0"
     "UNSUPPORTED_PUBLIC_KEY_TYPE\0"
     "UNSUPPORTED_TYPE\0"
+    "WRONG_INTEGER_TYPE\0"
     "WRONG_PUBLIC_KEY_TYPE\0"
     "WRONG_TAG\0"
     "WRONG_TYPE\0"
@@ -1066,6 +1074,7 @@
     "NOT_PKCS7_SIGNED_DATA\0"
     "NO_CERTIFICATES_INCLUDED\0"
     "NO_CRLS_INCLUDED\0"
+    "AMBIGUOUS_FRIENDLY_NAME\0"
     "BAD_ITERATION_COUNT\0"
     "BAD_PKCS12_DATA\0"
     "BAD_PKCS12_VERSION\0"
@@ -1223,6 +1232,7 @@
     "INVALID_ECH_CONFIG_LIST\0"
     "INVALID_ECH_PUBLIC_NAME\0"
     "INVALID_MESSAGE\0"
+    "INVALID_OUTER_EXTENSION\0"
     "INVALID_OUTER_RECORD_TYPE\0"
     "INVALID_SCT_LIST\0"
     "INVALID_SIGNATURE_ALGORITHM\0"
@@ -1266,7 +1276,6 @@
     "OLD_SESSION_CIPHER_NOT_RETURNED\0"
     "OLD_SESSION_PRF_HASH_MISMATCH\0"
     "OLD_SESSION_VERSION_NOT_RETURNED\0"
-    "OUTER_EXTENSION_NOT_FOUND\0"
     "PARSE_TLSEXT\0"
     "PATH_TOO_LONG\0"
     "PEER_DID_NOT_RETURN_A_CERTIFICATE\0"
@@ -1417,7 +1426,10 @@
     "LOADING_DEFAULTS\0"
     "NAME_TOO_LONG\0"
     "NEWER_CRL_NOT_NEWER\0"
+    "NO_CERTIFICATE_FOUND\0"
+    "NO_CERTIFICATE_OR_CRL_FOUND\0"
     "NO_CERT_SET_FOR_US_TO_VERIFY\0"
+    "NO_CRL_FOUND\0"
     "NO_CRL_NUMBER\0"
     "PUBLIC_KEY_DECODE_ERROR\0"
     "PUBLIC_KEY_ENCODE_ERROR\0"
diff --git a/third_party/cose-c/BUILD.gn b/third_party/cose-c/BUILD.gn
index 917e647..e5a46aa 100644
--- a/third_party/cose-c/BUILD.gn
+++ b/third_party/cose-c/BUILD.gn
@@ -15,10 +15,17 @@
 import("//build_overrides/pigweed.gni")
 import("$dir_pw_build/target_types.gni")
 
-config("external_config") {
+config("external_config_ed25519") {
   include_dirs = [
     "src/include",
-    "include",
+    "include/ed25519",
+  ]
+}
+
+config("external_config_p384") {
+  include_dirs = [
+    "src/include",
+    "include/p384",
   ]
 }
 
@@ -28,16 +35,33 @@
   cflags = [ "-Wno-cast-qual" ]
 }
 
-pw_static_library("cose-c") {
+pw_static_library("cose-c_ed25519") {
   public = [ "src/include/cose/cose.h" ]
   sources = [
-    "cose_deps.cc",
+    "cose_ed25519_deps.cc",
     "src/src/Cose.cpp",
     "src/src/CoseKey.cpp",
     "src/src/Sign1.cpp",
     "src/src/cbor.cpp",
   ]
-  public_configs = [ ":external_config" ]
+  public_configs = [ ":external_config_ed25519" ]
+  configs = [ ":internal_config" ]
+  public_deps = [
+    "//third_party/boringssl:crypto",
+    "//third_party/cn-cbor:cn-cbor",
+  ]
+}
+
+pw_static_library("cose-c_p384") {
+  public = [ "src/include/cose/cose.h" ]
+  sources = [
+    "cose_p384_deps.cc",
+    "src/src/Cose.cpp",
+    "src/src/CoseKey.cpp",
+    "src/src/Sign1.cpp",
+    "src/src/cbor.cpp",
+  ]
+  public_configs = [ ":external_config_p384" ]
   configs = [ ":internal_config" ]
   public_deps = [
     "//third_party/boringssl:crypto",
diff --git a/third_party/cose-c/cose_deps.cc b/third_party/cose-c/cose_ed25519_deps.cc
similarity index 94%
rename from third_party/cose-c/cose_deps.cc
rename to third_party/cose-c/cose_ed25519_deps.cc
index 986b2b0..1eb8a15 100644
--- a/third_party/cose-c/cose_deps.cc
+++ b/third_party/cose-c/cose_ed25519_deps.cc
@@ -23,7 +23,7 @@
 
 // Gets the public key from a well-formed Ed25519 COSE_Key. On success populates
 // |public_key| and returns true.
-static bool GetPublicKeyFromCbor(const cn_cbor *key, uint8_t public_key[32]) {
+static bool GetPublicKeyFromCbor(const cn_cbor *key, uint8_t public_key[PUBLIC_KEY_SIZE]) {
   const int64_t kCoseKeyAlgLabel = 3;
   const int64_t kCoseKeyOpsLabel = 4;
   const uint64_t kCoseKeyOpsVerify = 2;
@@ -42,7 +42,7 @@
   if (curve->type != CN_CBOR_UINT || curve->v.uint != COSE_Curve_Ed25519) {
     return false;
   }
-  if (x->type != CN_CBOR_BYTES || x->length != 32) {
+  if (x->type != CN_CBOR_BYTES || x->length != PUBLIC_KEY_SIZE) {
     return false;
   }
   // Optional attributes.
@@ -72,7 +72,7 @@
     }
   }
 
-  memcpy(public_key, x->v.bytes, 32);
+  memcpy(public_key, x->v.bytes, PUBLIC_KEY_SIZE);
   return true;
 }
 
@@ -88,7 +88,7 @@
   if (signature->type != CN_CBOR_BYTES || signature->length != 64) {
     return false;
   }
-  uint8_t public_key[32];
+  uint8_t public_key[PUBLIC_KEY_SIZE];
   if (!GetPublicKeyFromCbor(key, public_key)) {
     return false;
   }
diff --git a/third_party/cose-c/cose_p384_deps.cc b/third_party/cose-c/cose_p384_deps.cc
new file mode 100644
index 0000000..795b080
--- /dev/null
+++ b/third_party/cose-c/cose_p384_deps.cc
@@ -0,0 +1,148 @@
+// 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
+//
+//     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.
+
+#include <stdint.h>
+#include <string.h>
+
+#include "cose/cose.h"
+#include "cose/cose_configure.h"
+#include "cose_int.h"
+#include "openssl/bn.h"
+#include "openssl/ec.h"
+#include "openssl/ec_key.h"
+#include "openssl/ecdsa.h"
+#include "openssl/evp.h"
+#include "openssl/hkdf.h"
+#include "openssl/is_boringssl.h"
+#include "openssl/sha.h"
+
+// Gets the public key from a well-formed ECDSA P-384 COSE_Key. On
+// success populates |public_key| and returns true; public_key must hold 96 bytes
+// (uncompressed format).
+static bool GetPublicKeyFromCbor(const cn_cbor *key, uint8_t *public_key) {
+  const int64_t kCoseKeyAlgLabel = 3;
+  const int64_t kCoseKeyOpsLabel = 4;
+  const uint64_t kCoseKeyOpsVerify = 2;
+  const int64_t kCoseAlgEs384 = -35;
+
+  // Mandatory attributes.
+  cn_cbor *type = cn_cbor_mapget_int(key, COSE_Key_Type);
+  cn_cbor *curve = cn_cbor_mapget_int(key, COSE_Key_OPK_Curve);
+  if (!type || !curve) {
+    return false;
+  }
+  if (type->type != CN_CBOR_UINT || curve->type != CN_CBOR_UINT) {
+    return false;
+  }
+
+  if (type->v.uint != COSE_Key_Type_EC2 || curve->v.uint != COSE_Curve_P384) {
+    return false;
+  }
+
+  cn_cbor *x = cn_cbor_mapget_int(key, COSE_Key_EC2_X);
+  if (!x || x->type != CN_CBOR_BYTES || x->length != (PUBLIC_KEY_SIZE / 2)) {
+    return false;
+  }
+
+  cn_cbor *y = cn_cbor_mapget_int(key, COSE_Key_EC2_Y);
+  if (!y || y->type != CN_CBOR_BYTES || y->length != (PUBLIC_KEY_SIZE / 2)) {
+    return false;
+  }
+
+  cn_cbor *alg = cn_cbor_mapget_int(key, kCoseKeyAlgLabel);
+  if (alg) {
+    if (alg->type != CN_CBOR_INT || alg->v.sint != kCoseAlgEs384) {
+      return false;
+    }
+  }
+
+  cn_cbor *ops = cn_cbor_mapget_int(key, kCoseKeyOpsLabel);
+  if (ops) {
+    if (ops->type != CN_CBOR_ARRAY || ops->length == 0) {
+      return false;
+    }
+    bool found_verify = false;
+    for (size_t i = 0; i < ops->length; ++i) {
+      cn_cbor *item = cn_cbor_index(ops, i);
+      if (!item || item->type != CN_CBOR_UINT) {
+        return false;
+      }
+      if (item->v.uint == kCoseKeyOpsVerify) {
+        found_verify = true;
+      }
+    }
+    if (!found_verify) {
+      return false;
+    }
+  }
+
+  memcpy(&public_key[0], x->v.bytes, PUBLIC_KEY_SIZE / 2);
+  memcpy(&public_key[PUBLIC_KEY_SIZE / 2], y->v.bytes, PUBLIC_KEY_SIZE / 2);
+  return true;
+}
+
+bool ECDSA_Verify(COSE *cose_signer, int signature_index, COSE_KEY *cose_key,
+                  int cbitsDigest, const byte *message, size_t message_size,
+                  cose_errback *) {
+  (void)cbitsDigest;
+  cn_cbor *signature = _COSE_arrayget_int(cose_signer, signature_index);
+  cn_cbor *key = cose_key->m_cborKey;
+  if (!signature || !key) {
+    return false;
+  }
+  if (signature->type != CN_CBOR_BYTES || signature->length != PUBLIC_KEY_SIZE) {
+    return false;
+  }
+  uint8_t public_key[PUBLIC_KEY_SIZE];
+  if (!GetPublicKeyFromCbor(key, public_key)) {
+    return false;
+  }
+
+  // Implementation of ECDSA verification starts here
+  uint8_t output[48];
+  SHA384(message, message_size, output);
+  EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp384r1);
+  BIGNUM *x = BN_new();
+  BN_bin2bn(&public_key[0], 48, x);
+  BIGNUM *y = BN_new();
+  BN_bin2bn(&public_key[48], 48, y);
+  int result = EC_KEY_set_public_key_affine_coordinates(eckey, x, y);
+
+  BN_clear_free(y);
+  BN_clear_free(x);
+
+  if (result == 0) {
+    printf("Setting affine coordinates failed\n");
+    return false;
+  }
+
+  ECDSA_SIG *sig = ECDSA_SIG_new();
+  BN_bin2bn(&(signature->v.bytes[0]), 48, sig->r);
+  BN_bin2bn(&(signature->v.bytes[48]), 48, sig->s);
+  result = ECDSA_do_verify(output, 48, sig, eckey);
+
+  EC_KEY_free(eckey);
+  ECDSA_SIG_free(sig);
+  if (1 != result) {
+    return false;
+  }
+  return true;
+}
+
+// A stub for 'ECDSA_Sign'. This is unused, but helps make linkers happy.
+bool ECDSA_Sign(COSE * /*cose_signer*/, int /*signature_index*/,
+                COSE_KEY * /*cose_key*/, const byte * /*message*/,
+                size_t /*message_size*/, cose_errback *) {
+  return false;
+}
diff --git a/third_party/cose-c/include/cose/cose_configure.h b/third_party/cose-c/include/ed25519/cose/cose_configure.h
similarity index 89%
rename from third_party/cose-c/include/cose/cose_configure.h
rename to third_party/cose-c/include/ed25519/cose/cose_configure.h
index c874231..f6d91a2 100644
--- a/third_party/cose-c/include/cose/cose_configure.h
+++ b/third_party/cose-c/include/ed25519/cose/cose_configure.h
@@ -1,4 +1,5 @@
 #define USE_EDDSA
+#define PUBLIC_KEY_SIZE 32
 
 #define INCLUDE_ENCRYPT 0
 #define INCLUDE_ENCRYPT0 0
diff --git a/third_party/cose-c/include/cose/cose_configure.h b/third_party/cose-c/include/p384/cose/cose_configure.h
similarity index 80%
copy from third_party/cose-c/include/cose/cose_configure.h
copy to third_party/cose-c/include/p384/cose/cose_configure.h
index c874231..ebb4469 100644
--- a/third_party/cose-c/include/cose/cose_configure.h
+++ b/third_party/cose-c/include/p384/cose/cose_configure.h
@@ -1,4 +1,5 @@
-#define USE_EDDSA
+#define USE_ECDSA_SHA_384
+#define PUBLIC_KEY_SIZE 96
 
 #define INCLUDE_ENCRYPT 0
 #define INCLUDE_ENCRYPT0 0
diff --git a/third_party/mbedtls/0001-Mark-basic-constraints-critical-as-appropriate.patch b/third_party/mbedtls/0001-Mark-basic-constraints-critical-as-appropriate.patch
deleted file mode 100644
index 097eb17..0000000
--- a/third_party/mbedtls/0001-Mark-basic-constraints-critical-as-appropriate.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From d5cbe3484248ee5f44543b1b50604bcd5739cc85 Mon Sep 17 00:00:00 2001
-From: Darren Krahn <dkrahn@google.com>
-Date: Fri, 10 Jul 2020 17:03:57 -0700
-Subject: [PATCH] Mark basic constraints critical as appropriate.
-
-Per RFC 5280 4.2.1.9 if the 'cA' field is set to true, the extension
-must be marked critical.
----
- library/x509write_crt.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/library/x509write_crt.c b/library/x509write_crt.c
-index 32c655096..498b8b0a0 100644
---- a/library/x509write_crt.c
-+++ b/library/x509write_crt.c
-@@ -163,7 +163,7 @@ int mbedtls_x509write_crt_set_basic_constraints( mbedtls_x509write_cert *ctx,
-     return(
-         mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_BASIC_CONSTRAINTS,
-                              MBEDTLS_OID_SIZE( MBEDTLS_OID_BASIC_CONSTRAINTS ),
--                             0, buf + sizeof(buf) - len, len ) );
-+                             is_ca, buf + sizeof(buf) - len, len ) );
- }
- 
- #if defined(MBEDTLS_SHA1_C)
--- 
-2.29.0.rc1.297.gfa9743e501-goog
-
diff --git a/third_party/mbedtls/BUILD.gn b/third_party/mbedtls/BUILD.gn
index e77c1b2..7e6d0f7 100644
--- a/third_party/mbedtls/BUILD.gn
+++ b/third_party/mbedtls/BUILD.gn
@@ -31,9 +31,12 @@
     "src/library/asn1parse.c",
     "src/library/asn1write.c",
     "src/library/bignum.c",
+    "src/library/bignum_core.c",
+    "src/library/constant_time.c",
     "src/library/ecdsa.c",
     "src/library/ecp.c",
     "src/library/ecp_curves.c",
+    "src/library/hash_info.c",
     "src/library/hkdf.c",
     "src/library/hmac_drbg.c",
     "src/library/md.c",
diff --git a/third_party/mbedtls/custom_config.h b/third_party/mbedtls/custom_config.h
index ff71785..24d5418 100644
--- a/third_party/mbedtls/custom_config.h
+++ b/third_party/mbedtls/custom_config.h
@@ -32,6 +32,7 @@
 #define MBEDTLS_MD_C
 #define MBEDTLS_OID_C
 #define MBEDTLS_PK_C
+#define MBEDTLS_PK_PARSE_C
 #define MBEDTLS_PK_WRITE_C
 #define MBEDTLS_SHA512_C
 #define MBEDTLS_X509_CREATE_C