Merge branch 'main' into api-summary-dependencies
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index d533149..b93c3f9 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -10,7 +10,7 @@
 tool/update-yaml-test-suite.sh  @dart-lang/dart-pub-team
 
 pkgs/api_summary                @kevmoo
-pkgs/bazel_worker               @dart-lang/dart-bat
+pkgs/bazel_worker               @dart-lang/dart-ecosystem-team
 pkgs/benchmark_harness          @dart-lang/dart-native-runtime-team
 pkgs/boolean_selector           @dart-lang/dart-ecosystem-team
 pkgs/boring                     @dart-lang/dart-ecosystem-team
@@ -26,8 +26,8 @@
 pkgs/file_testing               @dart-lang/dart-ecosystem-team
 pkgs/glob                       @dart-lang/dart-ecosystem-team
 pkgs/graphs                     @natebosch
-pkgs/html                       @HosseinYousefi
-pkgs/io                         @dart-lang/dart-bat
+pkgs/html                       @dart-lang/dart-ecosystem-team
+pkgs/io                         @dart-lang/dart-ecosystem-team
 pkgs/json_rpc_2                 @dart-lang/dart-ecosystem-team
 pkgs/markdown                   @srawlins
 pkgs/mime                       @lrhn
@@ -36,7 +36,7 @@
 pkgs/pool                       @dart-lang/dart-ecosystem-team
 pkgs/process                    @dart-lang/dart-ecosystem-team
 pkgs/pub_semver                 @dart-lang/dart-pub-team
-pkgs/pubspec_parse              @dart-lang/dart-bat
+pkgs/pubspec_parse              @dart-lang/dart-ecosystem-team
 pkgs/source_maps                @dart-lang/dart-ecosystem-team
 pkgs/source_map_stack_trace     @dart-lang/dart-ecosystem-team
 pkgs/source_span                @dart-lang/dart-ecosystem-team
@@ -45,9 +45,9 @@
 pkgs/stream_channel             @natebosch
 pkgs/stream_transform           @natebosch
 pkgs/string_scanner             @dart-lang/dart-ecosystem-team
-pkgs/term_glyph                 @dart-lang/dart-bat
+pkgs/term_glyph                 @dart-lang/dart-ecosystem-team
 pkgs/test_reflective_loader     @scheglov
-pkgs/timing                     @dart-lang/dart-bat
+pkgs/timing                     @dart-lang/dart-ecosystem-team
 pkgs/unified_analytics          @dart-lang/analyzer-team
 pkgs/watcher                    @davidmorgan @dart-lang/dart-model-team
 pkgs/yaml                       @dart-lang/dart-pub-team
diff --git a/pkgs/api_summary/CHANGELOG.md b/pkgs/api_summary/CHANGELOG.md
index 4e98f17..daac9c8 100644
--- a/pkgs/api_summary/CHANGELOG.md
+++ b/pkgs/api_summary/CHANGELOG.md
@@ -1,5 +1,7 @@
 ## 1.3.0-wip
 
+- Add `includeImplicitNonPublicMembers` and `includeReferencedTypes` optional
+  named constructor parameters to `ApiSummaryCustomizer`.
 - Track `pubspec.yaml` runtime `dependencies` constraints for external packages
   referenced in the public API on `ApiSummary.dependencies`, and render them
   inline on referenced `package:...` library headers in `api.txt`
diff --git a/pkgs/api_summary/README.md b/pkgs/api_summary/README.md
index d81dae7..81a95aa 100644
--- a/pkgs/api_summary/README.md
+++ b/pkgs/api_summary/README.md
@@ -108,9 +108,26 @@
 
 ### Customizing the Summary
 
-Extend the `ApiSummaryCustomizer` class to customize what is displayed in the
-API summary. For example, to exclude specific public classes or only display
-details of certain elements:
+Pass `ApiSummaryCustomizer` with optional flags, or extend it to customize what
+is displayed in the API summary. For example, to include member details for
+implicitly exposed non-public types or referenced external types:
+
+```dart
+import 'package:api_summary/api_summary.dart';
+
+void main() async {
+  final summary = await apiSummary(
+    '/path/to/package',
+    customizer: const ApiSummaryCustomizer(
+      includeImplicitNonPublicMembers: true,
+      includeReferencedTypes: true,
+    ),
+  );
+  print(summary);
+}
+```
+
+Or extend `ApiSummaryCustomizer` for custom element filtering:
 
 ```dart
 import 'package:api_summary/api_summary.dart';
diff --git a/pkgs/api_summary/api.json b/pkgs/api_summary/api.json
index 21e910d..0357a2f 100644
--- a/pkgs/api_summary/api.json
+++ b/pkgs/api_summary/api.json
@@ -3271,6 +3271,26 @@
                 "name": "ApiSummaryCustomizer",
                 "libraryUri": "package:api_summary/src/api_summary_customizer.dart"
               },
+              "parameters": [
+                {
+                  "name": "includeImplicitNonPublicMembers",
+                  "type": {
+                    "kind": "interface",
+                    "name": "bool",
+                    "libraryUri": "dart:core"
+                  },
+                  "isNamed": true
+                },
+                {
+                  "name": "includeReferencedTypes",
+                  "type": {
+                    "kind": "interface",
+                    "name": "bool",
+                    "libraryUri": "dart:core"
+                  },
+                  "isNamed": true
+                }
+              ],
               "isConst": true
             }
           ],
diff --git a/pkgs/api_summary/api.txt b/pkgs/api_summary/api.txt
index cec4ef5..41db736 100644
--- a/pkgs/api_summary/api.txt
+++ b/pkgs/api_summary/api.txt
@@ -152,7 +152,7 @@
     publicApiLibraries (getter: List<LibraryElement>)
     topLevelPublicElements (getter: Set<Element>)
   ApiSummaryCustomizer (class extends Object, base):
-    new (const constructor: ApiSummaryCustomizer Function())
+    new (const constructor: ApiSummaryCustomizer Function({bool includeImplicitNonPublicMembers, bool includeReferencedTypes}))
     includeImplicitNonPublicMembers (getter: bool)
     includeReferencedTypes (getter: bool)
     initialScanComplete (method: Future<void> Function(ApiSummaryContext))
diff --git a/pkgs/api_summary/api.yaml b/pkgs/api_summary/api.yaml
index b50ebc0..09dd3c6 100644
--- a/pkgs/api_summary/api.yaml
+++ b/pkgs/api_summary/api.yaml
@@ -2127,6 +2127,19 @@
               kind: interface
               name: ApiSummaryCustomizer
               libraryUri: package:api_summary/src/api_summary_customizer.dart
+            parameters:
+              - name: includeImplicitNonPublicMembers
+                type:
+                  kind: interface
+                  name: bool
+                  libraryUri: dart:core
+                isNamed: true
+              - name: includeReferencedTypes
+                type:
+                  kind: interface
+                  name: bool
+                  libraryUri: dart:core
+                isNamed: true
             isConst: true
         methods:
           - name: includeImplicitNonPublicMembers
diff --git a/pkgs/api_summary/lib/src/api_summary_customizer.dart b/pkgs/api_summary/lib/src/api_summary_customizer.dart
index 17fa9bd..a4139d1 100644
--- a/pkgs/api_summary/lib/src/api_summary_customizer.dart
+++ b/pkgs/api_summary/lib/src/api_summary_customizer.dart
@@ -44,7 +44,21 @@
 /// Clients should not *implement* this class, however, because additional
 /// methods may be added in the future.
 base class ApiSummaryCustomizer {
-  const ApiSummaryCustomizer();
+  /// Whether to include full member signatures (constructors, methods) for
+  /// non-public declarations that are implicitly exposed via public signatures.
+  final bool includeImplicitNonPublicMembers;
+
+  /// Whether to include declarations of referenced types from external packages
+  /// or SDK libraries.
+  ///
+  /// If true, referenced types will be included in the summary with a status of
+  /// [ApiDeclarationStatus.referenced] and without their members.
+  final bool includeReferencedTypes;
+
+  const ApiSummaryCustomizer({
+    this.includeImplicitNonPublicMembers = false,
+    this.includeReferencedTypes = false,
+  });
 
   /// Called after [ApiSummaryContext.packageName] and
   /// [ApiSummaryContext.analysisContext] have been set, but before any analysis
@@ -60,17 +74,6 @@
   /// Further analysis won't be performed until the returned Future completes.
   Future<void> initialScanComplete(ApiSummaryContext context) async {}
 
-  /// Whether to include full member signatures (constructors, methods) for
-  /// non-public declarations that are implicitly exposed via public signatures.
-  bool get includeImplicitNonPublicMembers => false;
-
-  /// Whether to include declarations of referenced types from external packages
-  /// or SDK libraries.
-  ///
-  /// If true, referenced types will be included in the summary with a status of
-  /// [ApiDeclarationStatus.referenced] and without their members.
-  bool get includeReferencedTypes => false;
-
   /// Called after [initialScanComplete] to determine if details about an
   /// element should be shown in the API summary.
   ///
diff --git a/pkgs/api_summary/test/api_builder_test.dart b/pkgs/api_summary/test/api_builder_test.dart
index a1498be..a066cea 100644
--- a/pkgs/api_summary/test/api_builder_test.dart
+++ b/pkgs/api_summary/test/api_builder_test.dart
@@ -258,7 +258,7 @@
       '$testPackageLibPath/src/private.dart': '''
 class PrivateInterface {}
 ''',
-    }, customizer: _IncludeReferencedTypesCustomizer());
+    }, customizer: const ApiSummaryCustomizer(includeReferencedTypes: true));
 
     final decodedMap = jsonDecode(summary) as Map<String, dynamic>;
     final rehydrated = ApiSummary.fromJson(decodedMap);
@@ -380,6 +380,53 @@
     expect(enumValues.isEnumConstant, isFalse);
   }
 
+  Future<void> test_customizerConstructorFlags() async {
+    final summary = await _build(
+      {
+        '$testPackageLibPath/file.dart': '''
+import 'src/private.dart';
+
+class PublicClass extends LeakedBase {}
+''',
+        '$testPackageLibPath/src/private.dart': '''
+class LeakedBase {
+  void leakedMethod(TransitiveLeak arg) {}
+}
+class TransitiveLeak {
+  int get count => 0;
+}
+''',
+      },
+      customizer: const ApiSummaryCustomizer(
+        includeImplicitNonPublicMembers: true,
+      ),
+    );
+
+    final decodedMap = jsonDecode(summary) as Map<String, dynamic>;
+    final rehydrated = ApiSummary.fromJson(decodedMap);
+
+    final privateLib = rehydrated.libraries.firstWhere(
+      (l) => l.uri == 'package:test/src/private.dart',
+    );
+    expect(privateLib.isPublicEntryPoint, isFalse);
+    expect(
+      privateLib.classes.map((c) => c.name),
+      containsAll(['LeakedBase', 'TransitiveLeak']),
+    );
+
+    final leakedBase = privateLib.classes.firstWhere(
+      (c) => c.name == 'LeakedBase',
+    );
+    expect(leakedBase.status, ApiDeclarationStatus.nonPublic);
+    expect(leakedBase.methods.map((m) => m.name), contains('leakedMethod'));
+
+    final transitiveLeak = privateLib.classes.firstWhere(
+      (c) => c.name == 'TransitiveLeak',
+    );
+    expect(transitiveLeak.status, ApiDeclarationStatus.nonPublic);
+    expect(transitiveLeak.methods.map((m) => m.name), contains('count'));
+  }
+
   Future<String> _build(
     Map<String, String> files, {
     ApiSummaryCustomizer? customizer,
@@ -401,8 +448,3 @@
     return jsonEncode(package.toJson());
   }
 }
-
-base class _IncludeReferencedTypesCustomizer extends ApiSummaryCustomizer {
-  @override
-  bool get includeReferencedTypes => true;
-}
diff --git a/pkgs/boring/CHANGELOG.md b/pkgs/boring/CHANGELOG.md
index d539dda..020e1e7 100644
--- a/pkgs/boring/CHANGELOG.md
+++ b/pkgs/boring/CHANGELOG.md
@@ -6,6 +6,7 @@
 - The package is now licensed under the BSD 3-Clause license used by other
   Dart packages.
 - Update BoringSSL to 3fed4f20.
+- Update BoringSSL to 64188f17.
 
 ## 0.4.1
 
diff --git a/pkgs/boring/lib/src/bindings/boringssl.g.dart b/pkgs/boring/lib/src/bindings/boringssl.g.dart
index dd7d95b..40c777d 100644
--- a/pkgs/boring/lib/src/bindings/boringssl.g.dart
+++ b/pkgs/boring/lib/src/bindings/boringssl.g.dart
@@ -1011,7 +1011,7 @@
 /// representation that does not always correspond to the DER encoding of the
 /// type. See the documentation for `ASN1_STRING` for details.
 ///
-/// Prefer `ASN1_STRING_get0_data`.
+/// Prefer `ASN1_STRING_get0_data`. See crbug.com/574019297.
 @meta.RecordUse()
 @ffi.Native<ffi.Pointer<ffi.UnsignedChar> Function(ffi.Pointer<ASN1_STRING>)>(
   symbol: 'bssl_dart_ASN1_STRING_data',
@@ -1060,7 +1060,21 @@
 )
 external int ASN1_STRING_get_default_mask();
 
-/// ASN1_STRING_length returns the length of `str`, in bytes.
+/// ASN1_STRING_get_length returns the length of `str`, in bytes.
+///
+/// The contents of an `ASN1_STRING` encode the value in some type-specific
+/// representation that does not always correspond to the DER encoding of the
+/// type. See the documentation for `ASN1_STRING` for details.
+@meta.RecordUse()
+@ffi.Native<ffi.Size Function(ffi.Pointer<ASN1_STRING>)>(
+  symbol: 'bssl_dart_ASN1_STRING_get_length',
+)
+external int ASN1_STRING_get_length(
+  ffi.Pointer<ASN1_STRING> str,
+);
+
+/// ASN1_STRING_length returns the length of `str`, in bytes. Prefer
+/// `ASN1_STRING_get_length`.
 ///
 /// The contents of an `ASN1_STRING` encode the value in some type-specific
 /// representation that does not always correspond to the DER encoding of the
@@ -2384,6 +2398,16 @@
   ffi.Pointer<BIO> bio,
 );
 
+/// BIO_dup_ref increments the reference count of `bio` and returns the same
+/// handle.
+@meta.RecordUse()
+@ffi.Native<ffi.Pointer<BIO> Function(ffi.Pointer<BIO>)>(
+  symbol: 'bssl_dart_BIO_dup_ref',
+)
+external ffi.Pointer<BIO> BIO_dup_ref(
+  ffi.Pointer<BIO> bio,
+);
+
 /// BIO_eof returns non-zero when `bio` has reached end-of-file. The precise
 /// meaning of which depends on the concrete type of `bio`. Note that in the
 /// case of BIO_pair this always returns non-zero.
@@ -8208,6 +8232,17 @@
   ffi.Pointer<DH> dh,
 );
 
+/// DH_dup_ref increments the reference count of `dh` and returns the same
+/// handle. It does not mutate `dh` for thread-safety purposes and may be used
+/// concurrently.
+@meta.RecordUse()
+@ffi.Native<ffi.Pointer<DH> Function(ffi.Pointer<DH>)>(
+  symbol: 'bssl_dart_DH_dup_ref',
+)
+external ffi.Pointer<DH> DH_dup_ref(
+  ffi.Pointer<DH> dh,
+);
+
 /// DH_free decrements the reference count of `dh` and frees it if the reference
 /// count drops to zero.
 @meta.RecordUse()
@@ -13059,9 +13094,6 @@
 /// Ed25519, `type` should be NULL. The `EVP_MD_CTX` itself is unused but is
 /// present so the API is uniform. See `EVP_DigestSign`.
 ///
-/// This function does not mutate `pkey` for thread-safety purposes and may be
-/// used concurrently with other non-mutating functions on `pkey`.
-///
 /// It returns one on success, or zero on error.
 @meta.RecordUse()
 @ffi.Native<
@@ -13156,9 +13188,6 @@
 /// Ed25519, `type` should be NULL. The `EVP_MD_CTX` itself is unused but is
 /// present so the API is uniform. See `EVP_DigestVerify`.
 ///
-/// This function does not mutate `pkey` for thread-safety purposes and may be
-/// used concurrently with other non-mutating functions on `pkey`.
-///
 /// It returns one on success, or zero on error.
 @meta.RecordUse()
 @ffi.Native<
@@ -15671,11 +15700,6 @@
 /// actual size of the signature is written to `*out_sig_len`.
 ///
 /// It returns one on success and zero otherwise.
-///
-/// It does not modify `ctx`, thus it's possible to continue to use `ctx` in
-/// order to sign a longer message. It also does not mutate `pkey` for
-/// thread-safety purposes and may be used concurrently with other non-mutating
-/// functions on `pkey`.
 @meta.RecordUse()
 @ffi.Native<
   ffi.Int Function(
@@ -15741,11 +15765,6 @@
 /// calls to `EVP_VerifyUpdate`.
 ///
 /// It returns one on success and zero otherwise.
-///
-/// It does not modify `ctx`, thus it's possible to continue to use `ctx` in
-/// order to verify a longer message. It also does not mutate `pkey` for
-/// thread-safety purposes and may be used concurrently with other non-mutating
-/// functions on `pkey`.
 @meta.RecordUse()
 @ffi.Native<
   ffi.Int Function(
@@ -16731,6 +16750,23 @@
 @ffi.Native<ffi.Pointer<EVP_MD> Function()>(symbol: 'bssl_dart_EVP_sha384')
 external ffi.Pointer<EVP_MD> EVP_sha384();
 
+/// SHA-3 family of digest functions.
+///
+/// WARNING: Prefer to use SHA-2 (e.g. `EVP_sha256`) over SHA-3. Despite being a
+/// newer hash function, SHA-3 performs poorly and should only be used when
+/// necessary for interoperability with existing systems.
+@meta.RecordUse()
+@ffi.Native<ffi.Pointer<EVP_MD> Function()>(symbol: 'bssl_dart_EVP_sha3_256')
+external ffi.Pointer<EVP_MD> EVP_sha3_256();
+
+@meta.RecordUse()
+@ffi.Native<ffi.Pointer<EVP_MD> Function()>(symbol: 'bssl_dart_EVP_sha3_384')
+external ffi.Pointer<EVP_MD> EVP_sha3_384();
+
+@meta.RecordUse()
+@ffi.Native<ffi.Pointer<EVP_MD> Function()>(symbol: 'bssl_dart_EVP_sha3_512')
+external ffi.Pointer<EVP_MD> EVP_sha3_512();
+
 @meta.RecordUse()
 @ffi.Native<ffi.Pointer<EVP_MD> Function()>(symbol: 'bssl_dart_EVP_sha512')
 external ffi.Pointer<EVP_MD> EVP_sha512();
@@ -23213,6 +23249,16 @@
   ffi.Pointer<X509_CRL> crl,
 );
 
+/// X509_CRL_dup_ref adds one to the reference count of `crl` and returns the
+/// same.
+@meta.RecordUse()
+@ffi.Native<ffi.Pointer<X509_CRL> Function(ffi.Pointer<X509_CRL>)>(
+  symbol: 'bssl_dart_X509_CRL_dup_ref',
+)
+external ffi.Pointer<X509_CRL> X509_CRL_dup_ref(
+  ffi.Pointer<X509_CRL> crl,
+);
+
 /// X509_CRL_free decrements `crl`'s reference count and, if zero, releases
 /// memory associated with `crl`.
 @meta.RecordUse()
@@ -25546,16 +25592,6 @@
   ffi.Pointer<X509_STORE_CTX> ctx,
 );
 
-/// X509_STORE_CTX_get0_current_crl returns the CRL which caused the error
-/// returned by `X509_STORE_CTX_get_error`.
-@meta.RecordUse()
-@ffi.Native<ffi.Pointer<X509_CRL> Function(ffi.Pointer<X509_STORE_CTX>)>(
-  symbol: 'bssl_dart_X509_STORE_CTX_get0_current_crl',
-)
-external ffi.Pointer<X509_CRL> X509_STORE_CTX_get0_current_crl(
-  ffi.Pointer<X509_STORE_CTX> ctx,
-);
-
 /// X509_STORE_CTX_get0_param returns `ctx`'s verification parameters. This
 /// object is mutable and may be modified by the caller.
 @meta.RecordUse()
@@ -28088,8 +28124,7 @@
 );
 
 /// X509_set_pubkey sets `x509`'s public key to `pkey`. It returns one on success
-/// and zero on error. This function does not take ownership of `pkey` and
-/// internally copies and updates reference counts as needed.
+/// and zero on error.
 @meta.RecordUse()
 @ffi.Native<ffi.Int Function(ffi.Pointer<X509>, ffi.Pointer<EVP_PKEY>)>(
   symbol: 'bssl_dart_X509_set_pubkey',
@@ -32975,9 +33010,9 @@
 
 const int EVP_MAX_KEY_LENGTH = 64;
 
-const int EVP_MAX_MD_BLOCK_SIZE = 128;
+const int EVP_MAX_MD_BLOCK_SIZE = 136;
 
-const int EVP_MAX_MD_DATA_SIZE = 208;
+const int EVP_MAX_MD_DATA_SIZE = 240;
 
 const int EVP_MAX_MD_SIZE = 64;
 
@@ -33276,11 +33311,11 @@
 
 const int NID_Private = 385;
 
-const int NID_SLH_DSA_SHA2_128s = 979;
+const int NID_SLH_DSA_SHA2_128s = 973;
 
-const int NID_SLH_DSA_SHA2_128s_WITH_SHA256 = 981;
+const int NID_SLH_DSA_SHA2_128s_WITH_SHA256 = 975;
 
-const int NID_SLH_DSA_SHAKE_256f = 980;
+const int NID_SLH_DSA_SHAKE_256f = 974;
 
 const int NID_SMIME = 188;
 
@@ -33462,7 +33497,7 @@
 
 const int NID_aes_256_xts = 914;
 
-const int NID_alg_mtcProof_draft = 976;
+const int NID_alg_mtcProof = 976;
 
 const int NID_algorithm = 376;
 
@@ -34532,7 +34567,7 @@
 
 const int NID_pbmac1 = 162;
 
-const int NID_pe_mtcCertificationAuthority_draft = 978;
+const int NID_pe_mtcCertificationAuthority_SHA256 = 978;
 
 const int NID_personalSignature = 499;
 
@@ -34662,7 +34697,7 @@
 
 const int NID_rc5_ofb64 = 123;
 
-const int NID_rdna_trustAnchorID_draft = 977;
+const int NID_rdna_trustAnchorID = 977;
 
 const int NID_registeredAddress = 870;
 
@@ -35771,7 +35806,7 @@
 
 /// md_data contains the hash-specific context.
 final class UnnamedUnion$2 extends ffi.Union {
-  @ffi.Array.multi([208])
+  @ffi.Array.multi([240])
   external ffi.Array<ffi.Uint8> md_data;
 
   @ffi.Uint64()
@@ -36367,7 +36402,7 @@
 
 const int X509_V_FLAG_CB_ISSUER_CHECK = 1;
 
-const int X509_V_FLAG_CHECK_SS_SIGNATURE = 16384;
+const int X509_V_FLAG_CHECK_SS_SIGNATURE = 0;
 
 const int X509_V_FLAG_CRL_CHECK = 4;
 
@@ -36399,7 +36434,7 @@
 
 const int X509_V_FLAG_USE_DELTAS = 8192;
 
-const int X509_V_FLAG_USE_MTC_DRAFT_PLANTS_05 = 4194304;
+const int X509_V_FLAG_USE_MTC_DRAFT_PLANTS_07 = 4194304;
 
 const int X509_V_FLAG_X509_STRICT = 0;
 
diff --git a/pkgs/boring/lib/src/bindings/record_use_mapping.g.dart b/pkgs/boring/lib/src/bindings/record_use_mapping.g.dart
index f61e022..5aa218a 100644
--- a/pkgs/boring/lib/src/bindings/record_use_mapping.g.dart
+++ b/pkgs/boring/lib/src/bindings/record_use_mapping.g.dart
@@ -94,6 +94,7 @@
   'ASN1_STRING_free': 'bssl_dart_ASN1_STRING_free',
   'ASN1_STRING_get0_data': 'bssl_dart_ASN1_STRING_get0_data',
   'ASN1_STRING_get_default_mask': 'bssl_dart_ASN1_STRING_get_default_mask',
+  'ASN1_STRING_get_length': 'bssl_dart_ASN1_STRING_get_length',
   'ASN1_STRING_length': 'bssl_dart_ASN1_STRING_length',
   'ASN1_STRING_new': 'bssl_dart_ASN1_STRING_new',
   'ASN1_STRING_print': 'bssl_dart_ASN1_STRING_print',
@@ -183,6 +184,7 @@
   'BIO_ctrl_get_write_guarantee': 'bssl_dart_BIO_ctrl_get_write_guarantee',
   'BIO_ctrl_pending': 'bssl_dart_BIO_ctrl_pending',
   'BIO_do_connect': 'bssl_dart_BIO_do_connect',
+  'BIO_dup_ref': 'bssl_dart_BIO_dup_ref',
   'BIO_eof': 'bssl_dart_BIO_eof',
   'BIO_find_type': 'bssl_dart_BIO_find_type',
   'BIO_flush': 'bssl_dart_BIO_flush',
@@ -608,6 +610,7 @@
   'DH_compute_key': 'bssl_dart_DH_compute_key',
   'DH_compute_key_hashed': 'bssl_dart_DH_compute_key_hashed',
   'DH_compute_key_padded': 'bssl_dart_DH_compute_key_padded',
+  'DH_dup_ref': 'bssl_dart_DH_dup_ref',
   'DH_free': 'bssl_dart_DH_free',
   'DH_generate_key': 'bssl_dart_DH_generate_key',
   'DH_generate_parameters_ex': 'bssl_dart_DH_generate_parameters_ex',
@@ -1249,6 +1252,9 @@
   'EVP_sha224': 'bssl_dart_EVP_sha224',
   'EVP_sha256': 'bssl_dart_EVP_sha256',
   'EVP_sha384': 'bssl_dart_EVP_sha384',
+  'EVP_sha3_256': 'bssl_dart_EVP_sha3_256',
+  'EVP_sha3_384': 'bssl_dart_EVP_sha3_384',
+  'EVP_sha3_512': 'bssl_dart_EVP_sha3_512',
   'EVP_sha512': 'bssl_dart_EVP_sha512',
   'EVP_sha512_256': 'bssl_dart_EVP_sha512_256',
   'EXTENDED_KEY_USAGE_free': 'bssl_dart_EXTENDED_KEY_USAGE_free',
@@ -1702,6 +1708,7 @@
   'X509_CRL_delete_ext': 'bssl_dart_X509_CRL_delete_ext',
   'X509_CRL_digest': 'bssl_dart_X509_CRL_digest',
   'X509_CRL_dup': 'bssl_dart_X509_CRL_dup',
+  'X509_CRL_dup_ref': 'bssl_dart_X509_CRL_dup_ref',
   'X509_CRL_free': 'bssl_dart_X509_CRL_free',
   'X509_CRL_get0_by_cert': 'bssl_dart_X509_CRL_get0_by_cert',
   'X509_CRL_get0_by_serial': 'bssl_dart_X509_CRL_get0_by_serial',
@@ -1869,8 +1876,6 @@
   'X509_STORE_CTX_free': 'bssl_dart_X509_STORE_CTX_free',
   'X509_STORE_CTX_get0_cert': 'bssl_dart_X509_STORE_CTX_get0_cert',
   'X509_STORE_CTX_get0_chain': 'bssl_dart_X509_STORE_CTX_get0_chain',
-  'X509_STORE_CTX_get0_current_crl':
-      'bssl_dart_X509_STORE_CTX_get0_current_crl',
   'X509_STORE_CTX_get0_param': 'bssl_dart_X509_STORE_CTX_get0_param',
   'X509_STORE_CTX_get0_parent_ctx': 'bssl_dart_X509_STORE_CTX_get0_parent_ctx',
   'X509_STORE_CTX_get0_store': 'bssl_dart_X509_STORE_CTX_get0_store',
diff --git a/pkgs/boring/third_party/boringssl/sources.cmake b/pkgs/boring/third_party/boringssl/sources.cmake
index 2d97387..fcf752d 100644
--- a/pkgs/boring/third_party/boringssl/sources.cmake
+++ b/pkgs/boring/third_party/boringssl/sources.cmake
@@ -112,6 +112,7 @@
   ${BORINGSSL_ROOT}src/crypto/evp/p_ec.cc
   ${BORINGSSL_ROOT}src/crypto/evp/p_ed25519.cc
   ${BORINGSSL_ROOT}src/crypto/evp/p_hkdf.cc
+  ${BORINGSSL_ROOT}src/crypto/evp/p_keccak.cc
   ${BORINGSSL_ROOT}src/crypto/evp/p_mldsa.cc
   ${BORINGSSL_ROOT}src/crypto/evp/p_mlkem.cc
   ${BORINGSSL_ROOT}src/crypto/evp/p_rsa.cc
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/asn1/asn1_lib.cc b/pkgs/boring/third_party/boringssl/src/crypto/asn1/asn1_lib.cc
index e41e43e..960cabb 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/asn1/asn1_lib.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/asn1/asn1_lib.cc
@@ -280,6 +280,7 @@
 }
 
 void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len) {
+  BSSL_CHECK(len >= 0);
   OPENSSL_free(str->data);
   str->data = reinterpret_cast<uint8_t *>(data);
   str->length = len;
@@ -361,6 +362,8 @@
 
 int ASN1_STRING_length(const ASN1_STRING *str) { return str->length; }
 
+size_t ASN1_STRING_get_length(const ASN1_STRING *str) { return str->length; }
+
 int ASN1_STRING_type(const ASN1_STRING *str) { return str->type; }
 
 unsigned char *ASN1_STRING_data(ASN1_STRING *str) { return str->data; }
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/bio/bio.cc b/pkgs/boring/third_party/boringssl/src/crypto/bio/bio.cc
index aabf06d..28c3f4b 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/bio/bio.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/bio/bio.cc
@@ -74,6 +74,13 @@
   return 1;
 }
 
+BIO *BIO_dup_ref(const BIO *bio) {
+  if (bio == nullptr) {
+    return nullptr;
+  }
+  return FromOpaque(bio)->DupRefInternal();
+}
+
 void BIO_vfree(BIO *bio) { BIO_free(bio); }
 
 void BIO_free_all(BIO *bio) { BIO_free(bio); }
@@ -291,9 +298,7 @@
   impl->retry_reason = 0;
 }
 
-int BIO_method_type(const BIO *bio) {
-  return FromOpaque(bio)->method->type;
-}
+int BIO_method_type(const BIO *bio) { return FromOpaque(bio)->method->type; }
 
 void BIO_copy_next_retry(BIO *bio) {
   auto *impl = FromOpaque(bio);
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/cms/cms.cc b/pkgs/boring/third_party/boringssl/src/crypto/cms/cms.cc
index e8ba962..ddd9d60 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/cms/cms.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/cms/cms.cc
@@ -56,8 +56,9 @@
 
 BSSL_NAMESPACE_END
 
-CMS_ContentInfo *CMS_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs,
-                          BIO *data, uint32_t flags) {
+CMS_ContentInfo *CMS_sign(const X509 *signcert, const EVP_PKEY *pkey,
+                          const STACK_OF(X509) *certs, BIO *data,
+                          uint32_t flags) {
   // We only support external signatures and do not support embedding
   // certificates in SignedData.
   if ((flags & CMS_DETACHED) == 0 || sk_X509_num(certs) != 0) {
@@ -88,8 +89,8 @@
 
 void CMS_ContentInfo_free(CMS_ContentInfo *cms) { Delete(FromOpaque(cms)); }
 
-CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms, X509 *signcert,
-                                EVP_PKEY *pkey, const EVP_MD *md,
+CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms, const X509 *signcert,
+                                const EVP_PKEY *pkey, const EVP_MD *md,
                                 uint32_t flags) {
   auto *impl = FromOpaque(cms);
   if (  // Already finalized.
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/dsa/dsa.cc b/pkgs/boring/third_party/boringssl/src/crypto/dsa/dsa.cc
index 2048c44..9bb2df1 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/dsa/dsa.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/dsa/dsa.cc
@@ -76,9 +76,7 @@
   if (dsa == nullptr) {
     return nullptr;
   }
-  auto *ptr = const_cast<DSA *>(dsa);
-  DSA_up_ref(ptr);
-  return ptr;
+  return FromOpaque(dsa)->DupRefInternal();
 }
 
 unsigned DSA_bits(const DSA *dsa) {
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/evp/evp.cc b/pkgs/boring/third_party/boringssl/src/crypto/evp/evp.cc
index f99ee15..f52a967 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/evp/evp.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/evp/evp.cc
@@ -63,10 +63,7 @@
   if (pkey == nullptr) {
     return nullptr;
   }
-  auto pkey_ref = const_cast<EVP_PKEY *>(pkey);
-  // We know that this call always returns one.
-  EVP_PKEY_up_ref(pkey_ref);
-  return pkey_ref;
+  return FromOpaque(pkey)->DupRefInternal();
 }
 
 int EVP_PKEY_is_opaque(const EVP_PKEY *pkey) {
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/evp/evp_ctx.cc b/pkgs/boring/third_party/boringssl/src/crypto/evp/evp_ctx.cc
index a0d7810..e01e92a 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/evp/evp_ctx.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/evp/evp_ctx.cc
@@ -30,7 +30,8 @@
 using namespace bssl;
 
 static UniquePtr<EvpPkeyCtx> evp_pkey_ctx_new(
-    EvpPkey *pkey, const EVP_PKEY_ALG *alg, const EVP_PKEY_CTX_METHOD *pmeth) {
+    const EvpPkey *pkey, const EVP_PKEY_ALG *alg,
+    const EVP_PKEY_CTX_METHOD *pmeth) {
   assert(pkey != nullptr || alg != nullptr);
   UniquePtr<EvpPkeyCtx> ret = MakeUnique<EvpPkeyCtx>();
   if (!ret) {
@@ -49,7 +50,7 @@
   return ret;
 }
 
-EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e) {
+EVP_PKEY_CTX *EVP_PKEY_CTX_new(const EVP_PKEY *pkey, ENGINE *e) {
   auto *pkey_impl = FromOpaque(pkey);
   if (pkey_impl == nullptr || pkey_impl->ameth == nullptr) {
     OPENSSL_PUT_ERROR(EVP, ERR_R_PASSED_NULL_PARAMETER);
@@ -151,7 +152,7 @@
   return ret.release();
 }
 
-EVP_PKEY *EVP_PKEY_CTX_get0_pkey(EVP_PKEY_CTX *ctx) {
+EVP_PKEY *EVP_PKEY_CTX_get0_pkey(const EVP_PKEY_CTX *ctx) {
   auto *impl = FromOpaque(ctx);
   return impl->pkey.get();
 }
@@ -315,7 +316,7 @@
   return 1;
 }
 
-int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer) {
+int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, const EVP_PKEY *peer) {
   auto *impl = FromOpaque(ctx);
   if (!impl || !impl->pmeth ||
       !(impl->pmeth->derive || impl->pmeth->encrypt || impl->pmeth->decrypt) ||
@@ -330,7 +331,8 @@
     return 0;
   }
 
-  int ret = impl->pmeth->ctrl(impl, EVP_PKEY_CTRL_PEER_KEY, 0, peer);
+  int ret = impl->pmeth->ctrl(impl, EVP_PKEY_CTRL_PEER_KEY, 0,
+                              const_cast<EVP_PKEY *>(peer));
 
   if (ret <= 0) {
     return 0;
@@ -357,7 +359,7 @@
   }
 
   impl->peerkey = UpRef(FromOpaque(peer));
-  ret = impl->pmeth->ctrl(impl, EVP_PKEY_CTRL_PEER_KEY, 1, peer);
+  ret = impl->pmeth->ctrl(impl, EVP_PKEY_CTRL_PEER_KEY, 1, impl->peerkey.get());
   if (ret <= 0) {
     impl->peerkey = nullptr;
     return 0;
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/evp/p_keccak.cc b/pkgs/boring/third_party/boringssl/src/crypto/evp/p_keccak.cc
new file mode 100644
index 0000000..99731c0
--- /dev/null
+++ b/pkgs/boring/third_party/boringssl/src/crypto/evp/p_keccak.cc
@@ -0,0 +1,84 @@
+// Copyright 2026 The BoringSSL Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Family of Keccak-derived hash functions including:
+// - SHA3-256
+// - SHA3-512
+
+#include <openssl/evp.h>
+
+#include "../fipsmodule/digest/internal.h"  // IWYU pragma: keep
+#include "../fipsmodule/keccak/internal.h"
+
+BSSL_NAMESPACE_BEGIN
+namespace {
+template <boringssl_keccak_config_t Config>
+void keccak_init(EVP_MD_CTX *ctx) {
+  BORINGSSL_keccak_init(reinterpret_cast<BORINGSSL_keccak_st *>(ctx->md_data),
+                        Config);
+}
+
+void keccak_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
+  BORINGSSL_keccak_absorb(reinterpret_cast<BORINGSSL_keccak_st *>(ctx->md_data),
+                          static_cast<const uint8_t *>(data), count);
+}
+
+template <size_t Size>
+void keccak_final(EVP_MD_CTX *ctx, uint8_t *out) {
+  BORINGSSL_keccak_squeeze(
+      reinterpret_cast<BORINGSSL_keccak_st *>(ctx->md_data),
+      static_cast<uint8_t *>(out), Size);
+}
+
+constexpr EVP_MD evp_md_sha3_256 = {
+    /*type=*/NID_undef,
+    /*md_size=*/SHA3_256_DIGEST_LENGTH,
+    /*flags=*/0,
+    /*init=*/keccak_init<boringssl_sha3_256>,
+    /*update=*/keccak_update,
+    /*final=*/keccak_final<SHA3_256_DIGEST_LENGTH>,
+    /*block_size=*/SHA3_256_CBLOCK,
+    /*ctx_size=*/sizeof(BORINGSSL_keccak_st),
+};
+
+constexpr EVP_MD evp_md_sha3_384 = {
+    /*type=*/NID_undef,
+    /*md_size=*/SHA3_384_DIGEST_LENGTH,
+    /*flags=*/0,
+    /*init=*/keccak_init<boringssl_sha3_384>,
+    /*update=*/keccak_update,
+    /*final=*/keccak_final<SHA3_384_DIGEST_LENGTH>,
+    /*block_size=*/SHA3_384_CBLOCK,
+    /*ctx_size=*/sizeof(BORINGSSL_keccak_st),
+};
+
+constexpr EVP_MD evp_md_sha3_512 = {
+    /*type=*/NID_undef,
+    /*md_size=*/SHA3_512_DIGEST_LENGTH,
+    /*flags=*/0,
+    /*init=*/keccak_init<boringssl_sha3_512>,
+    /*update=*/keccak_update,
+    /*final=*/keccak_final<SHA3_512_DIGEST_LENGTH>,
+    /*block_size=*/SHA3_512_CBLOCK,
+    /*ctx_size=*/sizeof(BORINGSSL_keccak_st),
+};
+
+static_assert(sizeof(BORINGSSL_keccak_st) <= EVP_MAX_MD_DATA_SIZE);
+static_assert(SHA3_256_CBLOCK <= EVP_MAX_MD_BLOCK_SIZE);
+}  // namespace
+BSSL_NAMESPACE_END
+
+const EVP_MD *EVP_sha3_256() { return &bssl::evp_md_sha3_256; }
+const EVP_MD *EVP_sha3_384() { return &bssl::evp_md_sha3_384; }
+const EVP_MD *EVP_sha3_512() { return &bssl::evp_md_sha3_512; }
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/evp/sign.cc b/pkgs/boring/third_party/boringssl/src/crypto/evp/sign.cc
index afccef5..3503c40 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/evp/sign.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/evp/sign.cc
@@ -19,8 +19,6 @@
 #include <openssl/digest.h>
 #include <openssl/err.h>
 
-#include "internal.h"
-
 
 int EVP_SignInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl) {
   return EVP_DigestInit_ex(ctx, type, impl);
@@ -35,7 +33,7 @@
 }
 
 int EVP_SignFinal(const EVP_MD_CTX *ctx, uint8_t *sig, unsigned *out_sig_len,
-                  EVP_PKEY *pkey) {
+                  const EVP_PKEY *pkey) {
   // Ensure the final result will fit in `unsigned`.
   size_t sig_len = EVP_PKEY_size(pkey);
   if (sig_len > UINT_MAX) {
@@ -75,7 +73,7 @@
 }
 
 int EVP_VerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig, size_t sig_len,
-                    EVP_PKEY *pkey) {
+                    const EVP_PKEY *pkey) {
   uint8_t m[EVP_MAX_MD_SIZE];
   unsigned m_len;
   bssl::ScopedEVP_MD_CTX tmp_ctx;
@@ -85,11 +83,9 @@
   }
 
   bssl::UniquePtr<EVP_PKEY_CTX> pkctx(EVP_PKEY_CTX_new(pkey, nullptr));
-  if (!pkctx ||
-      !EVP_PKEY_verify_init(pkctx.get()) ||
+  if (!pkctx || !EVP_PKEY_verify_init(pkctx.get()) ||
       !EVP_PKEY_CTX_set_signature_md(pkctx.get(), ctx->digest)) {
     return 0;
   }
   return EVP_PKEY_verify(pkctx.get(), sig, sig_len, m, m_len);
 }
-
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/dh/dh.cc.inc b/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/dh/dh.cc.inc
index d018f76..8dfcdee 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/dh/dh.cc.inc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/dh/dh.cc.inc
@@ -353,6 +353,13 @@
   return 1;
 }
 
+DH *DH_dup_ref(const DH *dh) {
+  if (dh == nullptr) {
+    return nullptr;
+  }
+  return FromOpaque(dh)->DupRefInternal();
+}
+
 DH *DH_get_rfc7919_2048() {
   // This is the prime from https://tools.ietf.org/html/rfc7919#appendix-A.1,
   // which is specifically approved for FIPS in appendix D of SP 800-56Ar3.
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/digestsign/digestsign.cc.inc b/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/digestsign/digestsign.cc.inc
index 8920546..a886d46 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/digestsign/digestsign.cc.inc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/digestsign/digestsign.cc.inc
@@ -41,7 +41,7 @@
 }
 
 static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **out_pctx,
-                          const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey,
+                          const EVP_MD *type, ENGINE *e, const EVP_PKEY *pkey,
                           enum evp_sign_verify_t op) {
   UniquePtr<EVP_PKEY_CTX> pctx(EVP_PKEY_CTX_new(pkey, e));
   if (pctx == nullptr) {
@@ -84,12 +84,12 @@
 }
 
 int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type,
-                       ENGINE *e, EVP_PKEY *pkey) {
+                       ENGINE *e, const EVP_PKEY *pkey) {
   return do_sigver_init(ctx, pctx, type, e, pkey, evp_sign);
 }
 
 int EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
-                         const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey) {
+                         const EVP_MD *type, ENGINE *e, const EVP_PKEY *pkey) {
   return do_sigver_init(ctx, pctx, type, e, pkey, evp_verify);
 }
 
@@ -143,8 +143,7 @@
   }
 }
 
-int EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig,
-                          size_t sig_len) {
+int EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig, size_t sig_len) {
   if (!uses_prehash(ctx->pctx, evp_verify)) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
     return 0;
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/ec/ec_key.cc.inc b/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/ec/ec_key.cc.inc
index e1e4e4b..078e541 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/ec/ec_key.cc.inc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/ec/ec_key.cc.inc
@@ -150,9 +150,7 @@
   if (key == nullptr) {
     return nullptr;
   }
-  auto *ptr = const_cast<EC_KEY *>(key);
-  EC_KEY_up_ref(ptr);
-  return ptr;
+  return FromOpaque(key)->DupRefInternal();
 }
 
 int EC_KEY_is_opaque(const EC_KEY *key) {
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/keccak/internal.h b/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/keccak/internal.h
index e7d79b0..fa1e2e8 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/keccak/internal.h
+++ b/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/keccak/internal.h
@@ -22,8 +22,18 @@
 
 BSSL_NAMESPACE_BEGIN
 
+#define BITS_TO_BYTES(BITS) ((BITS) / 8)
+#define SHA3_256_DIGEST_LENGTH BITS_TO_BYTES(256)
+#define SHA3_384_DIGEST_LENGTH BITS_TO_BYTES(384)
+#define SHA3_512_DIGEST_LENGTH BITS_TO_BYTES(512)
+#define SHA3_BLOCK_SIZE(BITS) BITS_TO_BYTES(1600 - 2 * (BITS))
+#define SHA3_256_CBLOCK SHA3_BLOCK_SIZE(256)
+#define SHA3_384_CBLOCK SHA3_BLOCK_SIZE(384)
+#define SHA3_512_CBLOCK SHA3_BLOCK_SIZE(512)
+
 enum boringssl_keccak_config_t : int32_t {
   boringssl_sha3_256,
+  boringssl_sha3_384,
   boringssl_sha3_512,
   boringssl_shake128,
   boringssl_shake256,
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/keccak/keccak.cc.inc b/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/keccak/keccak.cc.inc
index adb09a2..e808dd5 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/keccak/keccak.cc.inc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/keccak/keccak.cc.inc
@@ -205,6 +205,10 @@
       capacity_bytes = 512 / 8;
       required_out_len = 32;
       break;
+    case boringssl_sha3_384:
+      capacity_bytes = 768 / 8;
+      required_out_len = 48;
+      break;
     case boringssl_sha3_512:
       capacity_bytes = 1024 / 8;
       required_out_len = 64;
@@ -235,8 +239,9 @@
 
 [[maybe_unused]] static bool keccak_can_parallel(
     enum boringssl_keccak_config_t config) {
-  return config == boringssl_sha3_256 || config == boringssl_sha3_512 ||
-         config == boringssl_shake128 || config == boringssl_shake256;
+  return config == boringssl_sha3_256 || config == boringssl_sha3_384 ||
+         config == boringssl_sha3_512 || config == boringssl_shake128 ||
+         config == boringssl_shake256;
 }
 
 static bool keccak_is_cshake(enum boringssl_keccak_config_t config) {
@@ -430,6 +435,7 @@
 static uint8_t keccak_terminator(struct BORINGSSL_keccak_st *ctx) {
   switch (ctx->config) {
     case boringssl_sha3_256:
+    case boringssl_sha3_384:
     case boringssl_sha3_512:
       return 0x06;
     case boringssl_shake128:
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/rsa/rsa.cc.inc b/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/rsa/rsa.cc.inc
index 34a219f..cf406b2 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/rsa/rsa.cc.inc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/fipsmodule/rsa/rsa.cc.inc
@@ -225,9 +225,7 @@
   if (rsa == nullptr) {
     return nullptr;
   }
-  auto *ptr = const_cast<RSA *>(rsa);
-  RSA_up_ref(ptr);
-  return ptr;
+  return FromOpaque(rsa)->DupRefInternal();
 }
 
 unsigned RSA_bits(const RSA *rsa) {
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/mem_internal.h b/pkgs/boring/third_party/boringssl/src/crypto/mem_internal.h
index 59927e1..488052f 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/mem_internal.h
+++ b/pkgs/boring/third_party/boringssl/src/crypto/mem_internal.h
@@ -135,6 +135,13 @@
     // ref-count variable.
     CRYPTO_refcount_inc(&references_);
   }
+
+  // Only the implementations of `FOO_dup_ref` should call these.
+  Derived *DupRefInternal() const {
+    UpRefInternal();
+    return const_cast<Derived *>(static_cast<const Derived *>(this));
+  }
+
   bool DecRefInternal() {
     if (CRYPTO_refcount_dec_and_test_zero(&references_)) {
       Derived *d = static_cast<Derived *>(this);
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/obj/obj_dat.h b/pkgs/boring/third_party/boringssl/src/crypto/obj/obj_dat.h
index ced3582..0e7d4c2 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/obj/obj_dat.h
+++ b/pkgs/boring/third_party/boringssl/src/crypto/obj/obj_dat.h
@@ -16,7 +16,7 @@
 
 
 BSSL_NAMESPACE_BEGIN
-#define NUM_NID 982
+#define NUM_NID 979
 
 static const uint8_t kObjectData[] = {
     /* NID_rsadsi */
@@ -7146,39 +7146,6 @@
     0x04,
     0x04,
     0x02,
-    /* NID_alg_mtcProof_draft */
-    0x2b,
-    0x06,
-    0x01,
-    0x04,
-    0x01,
-    0x82,
-    0xda,
-    0x4b,
-    0x2f,
-    0x00,
-    /* NID_rdna_trustAnchorID_draft */
-    0x2b,
-    0x06,
-    0x01,
-    0x04,
-    0x01,
-    0x82,
-    0xda,
-    0x4b,
-    0x2f,
-    0x01,
-    /* NID_pe_mtcCertificationAuthority_draft */
-    0x2b,
-    0x06,
-    0x01,
-    0x04,
-    0x01,
-    0x82,
-    0xda,
-    0x4b,
-    0x2f,
-    0x02,
     /* NID_SLH_DSA_SHA2_128s */
     0x60,
     0x86,
@@ -7209,6 +7176,33 @@
     0x04,
     0x03,
     0x23,
+    /* NID_alg_mtcProof */
+    0x2b,
+    0x06,
+    0x01,
+    0x05,
+    0x05,
+    0x07,
+    0x06,
+    0x43,
+    /* NID_rdna_trustAnchorID */
+    0x2b,
+    0x06,
+    0x01,
+    0x05,
+    0x05,
+    0x07,
+    0x19,
+    0x03,
+    /* NID_pe_mtcCertificationAuthority_SHA256 */
+    0x2b,
+    0x06,
+    0x01,
+    0x05,
+    0x05,
+    0x07,
+    0x01,
+    0x26,
 };
 
 static const ASN1_OBJECT kObjects[NUM_NID] = {
@@ -8865,21 +8859,19 @@
      0},
     {NULL, NULL, NID_undef, 0, NULL, 0},
     {"X-Wing", "X-Wing", NID_X_Wing, 0, NULL, 0},
-    {NULL, NULL, NID_undef, 0, NULL, 0},
-    {NULL, NULL, NID_undef, 0, NULL, 0},
-    {NULL, NULL, NID_undef, 0, NULL, 0},
-    {"alg-mtcProof-draft", "alg-mtcProof-draft", NID_alg_mtcProof_draft, 10,
-     &kObjectData[6241], 0},
-    {"rdna-trustAnchorID-draft", "rdna-trustAnchorID-draft",
-     NID_rdna_trustAnchorID_draft, 10, &kObjectData[6251], 0},
-    {"pe-mtcCertificationAuthority-draft", "pe-mtcCertificationAuthority-draft",
-     NID_pe_mtcCertificationAuthority_draft, 10, &kObjectData[6261], 0},
     {"id-slh-dsa-sha2-128s", "SLH-DSA-SHA2-128s", NID_SLH_DSA_SHA2_128s, 9,
-     &kObjectData[6271], 0},
+     &kObjectData[6241], 0},
     {"id-slh-dsa-shake-256f", "SLH-DSA-SHAKE-256f", NID_SLH_DSA_SHAKE_256f, 9,
-     &kObjectData[6280], 0},
+     &kObjectData[6250], 0},
     {"id-hash-slh-dsa-sha2-128s-with-sha256", "SLH-DSA-SHA2-128s-WITH-SHA256",
-     NID_SLH_DSA_SHA2_128s_WITH_SHA256, 9, &kObjectData[6289], 0},
+     NID_SLH_DSA_SHA2_128s_WITH_SHA256, 9, &kObjectData[6259], 0},
+    {"alg-mtcProof", "alg-mtcProof", NID_alg_mtcProof, 8, &kObjectData[6268],
+     0},
+    {"rdna-trustAnchorID", "rdna-trustAnchorID", NID_rdna_trustAnchorID, 8,
+     &kObjectData[6276], 0},
+    {"pe-mtcCertificationAuthority-SHA256",
+     "pe-mtcCertificationAuthority-SHA256",
+     NID_pe_mtcCertificationAuthority_SHA256, 8, &kObjectData[6284], 0},
 };
 
 static const uint16_t kNIDsInShortNameOrder[] = {
@@ -9095,7 +9087,7 @@
     368 /* acceptableResponses */,
     446 /* account */,
     363 /* ad_timestamping */,
-    976 /* alg-mtcProof-draft */,
+    976 /* alg-mtcProof */,
     376 /* algorithm */,
     405 /* ansi-X9-62 */,
     910 /* anyExtendedKeyUsage */,
@@ -9354,7 +9346,7 @@
     331 /* id-cmc-transactionId */,
     787 /* id-ct-asciiTextWithCRLF */,
     408 /* id-ecPublicKey */,
-    981 /* id-hash-slh-dsa-sha2-128s-with-sha256 */,
+    975 /* id-hash-slh-dsa-sha2-128s-with-sha256 */,
     508 /* id-hex-multipart-message */,
     507 /* id-hex-partial-message */,
     260 /* id-it */,
@@ -9426,8 +9418,8 @@
     322 /* id-regInfo-certReq */,
     321 /* id-regInfo-utf8Pairs */,
     512 /* id-set */,
-    979 /* id-slh-dsa-sha2-128s */,
-    980 /* id-slh-dsa-shake-256f */,
+    973 /* id-slh-dsa-sha2-128s */,
+    974 /* id-slh-dsa-shake-256f */,
     191 /* id-smime-aa */,
     215 /* id-smime-aa-contentHint */,
     218 /* id-smime-aa-contentIdentifier */,
@@ -9560,7 +9552,7 @@
     489 /* pagerTelephoneNumber */,
     374 /* path */,
     112 /* pbeWithMD5AndCast5CBC */,
-    978 /* pe-mtcCertificationAuthority-draft */,
+    978 /* pe-mtcCertificationAuthority-SHA256 */,
     499 /* personalSignature */,
     487 /* personalTitle */,
     464 /* photo */,
@@ -9614,7 +9606,7 @@
     286 /* qcStatements */,
     457 /* qualityLabelledData */,
     450 /* rFC822localPart */,
-    977 /* rdna-trustAnchorID-draft */,
+    977 /* rdna-trustAnchorID */,
     870 /* registeredAddress */,
     400 /* role */,
     877 /* roleOccupant */,
@@ -9955,9 +9947,9 @@
     2 /* RSA Data Security, Inc. PKCS */,
     188 /* S/MIME */,
     167 /* S/MIME Capabilities */,
-    979 /* SLH-DSA-SHA2-128s */,
-    981 /* SLH-DSA-SHA2-128s-WITH-SHA256 */,
-    980 /* SLH-DSA-SHAKE-256f */,
+    973 /* SLH-DSA-SHA2-128s */,
+    975 /* SLH-DSA-SHA2-128s-WITH-SHA256 */,
+    974 /* SLH-DSA-SHAKE-256f */,
     387 /* SNMPv2 */,
     512 /* Secure Electronic Transactions */,
     386 /* Security */,
@@ -10041,7 +10033,7 @@
     901 /* aes-256-gcm */,
     428 /* aes-256-ofb */,
     914 /* aes-256-xts */,
-    976 /* alg-mtcProof-draft */,
+    976 /* alg-mtcProof */,
     376 /* algorithm */,
     484 /* associatedDomain */,
     485 /* associatedName */,
@@ -10520,7 +10512,7 @@
     145 /* pbeWithSHA1And40BitRC4 */,
     170 /* pbeWithSHA1AndDES-CBC */,
     68 /* pbeWithSHA1AndRC2-CBC */,
-    978 /* pe-mtcCertificationAuthority-draft */,
+    978 /* pe-mtcCertificationAuthority-SHA256 */,
     499 /* personalSignature */,
     487 /* personalTitle */,
     464 /* photo */,
@@ -10580,7 +10572,7 @@
     122 /* rc5-cfb */,
     121 /* rc5-ecb */,
     123 /* rc5-ofb */,
-    977 /* rdna-trustAnchorID-draft */,
+    977 /* rdna-trustAnchorID */,
     870 /* registeredAddress */,
     460 /* rfc822Mailbox */,
     117 /* ripemd160 */,
@@ -11322,6 +11314,7 @@
     397 /* 1.3.6.1.5.5.7.1.10 (OBJ_ac_proxying) */,
     398 /* 1.3.6.1.5.5.7.1.11 (OBJ_sinfo_access) */,
     663 /* 1.3.6.1.5.5.7.1.14 (OBJ_proxyCertInfo) */,
+    978 /* 1.3.6.1.5.5.7.1.38 (OBJ_pe_mtcCertificationAuthority_SHA256) */,
     164 /* 1.3.6.1.5.5.7.2.1 (OBJ_id_qt_cps) */,
     165 /* 1.3.6.1.5.5.7.2.2 (OBJ_id_qt_unotice) */,
     293 /* 1.3.6.1.5.5.7.2.3 (OBJ_textNotice) */,
@@ -11357,6 +11350,7 @@
     324 /* 1.3.6.1.5.5.7.6.2 (OBJ_id_alg_noSignature) */,
     325 /* 1.3.6.1.5.5.7.6.3 (OBJ_id_alg_dh_sig_hmac_sha1) */,
     326 /* 1.3.6.1.5.5.7.6.4 (OBJ_id_alg_dh_pop) */,
+    976 /* 1.3.6.1.5.5.7.6.67 (OBJ_alg_mtcProof) */,
     327 /* 1.3.6.1.5.5.7.7.1 (OBJ_id_cmc_statusInfo) */,
     328 /* 1.3.6.1.5.5.7.7.2 (OBJ_id_cmc_identification) */,
     329 /* 1.3.6.1.5.5.7.7.3 (OBJ_id_cmc_identityProof) */,
@@ -11397,6 +11391,7 @@
     664 /* 1.3.6.1.5.5.7.21.0 (OBJ_id_ppl_anyLanguage) */,
     665 /* 1.3.6.1.5.5.7.21.1 (OBJ_id_ppl_inheritAll) */,
     667 /* 1.3.6.1.5.5.7.21.2 (OBJ_Independent) */,
+    977 /* 1.3.6.1.5.5.7.25.3 (OBJ_rdna_trustAnchorID) */,
     178 /* 1.3.6.1.5.5.7.48.1 (OBJ_ad_OCSP) */,
     179 /* 1.3.6.1.5.5.7.48.2 (OBJ_ad_ca_issuers) */,
     363 /* 1.3.6.1.5.5.7.48.3 (OBJ_ad_timeStamping) */,
@@ -11535,9 +11530,9 @@
     967 /* 2.16.840.1.101.3.4.3.17 (OBJ_ML_DSA_44) */,
     968 /* 2.16.840.1.101.3.4.3.18 (OBJ_ML_DSA_65) */,
     969 /* 2.16.840.1.101.3.4.3.19 (OBJ_ML_DSA_87) */,
-    979 /* 2.16.840.1.101.3.4.3.20 (OBJ_SLH_DSA_SHA2_128s) */,
-    980 /* 2.16.840.1.101.3.4.3.31 (OBJ_SLH_DSA_SHAKE_256f) */,
-    981 /* 2.16.840.1.101.3.4.3.35 (OBJ_SLH_DSA_SHA2_128s_WITH_SHA256) */,
+    973 /* 2.16.840.1.101.3.4.3.20 (OBJ_SLH_DSA_SHA2_128s) */,
+    974 /* 2.16.840.1.101.3.4.3.31 (OBJ_SLH_DSA_SHAKE_256f) */,
+    975 /* 2.16.840.1.101.3.4.3.35 (OBJ_SLH_DSA_SHA2_128s_WITH_SHA256) */,
     970 /* 2.16.840.1.101.3.4.4.2 (OBJ_ML_KEM_768) */,
     966 /* 2.16.840.1.101.3.4.4.3 (OBJ_ML_KEM_1024) */,
     71 /* 2.16.840.1.113730.1.1 (OBJ_netscape_cert_type) */,
@@ -11637,9 +11632,6 @@
     138 /* 1.3.6.1.4.1.311.10.3.4 (OBJ_ms_efs) */,
     648 /* 1.3.6.1.4.1.311.20.2.2 (OBJ_ms_smartcard_login) */,
     649 /* 1.3.6.1.4.1.311.20.2.3 (OBJ_ms_upn) */,
-    976 /* 1.3.6.1.4.1.44363.47.0 (OBJ_alg_mtcProof_draft) */,
-    977 /* 1.3.6.1.4.1.44363.47.1 (OBJ_rdna_trustAnchorID_draft) */,
-    978 /* 1.3.6.1.4.1.44363.47.2 (OBJ_pe_mtcCertificationAuthority_draft) */,
     751 /* 1.2.392.200011.61.1.1.1.2 (OBJ_camellia_128_cbc) */,
     752 /* 1.2.392.200011.61.1.1.1.3 (OBJ_camellia_192_cbc) */,
     753 /* 1.2.392.200011.61.1.1.1.4 (OBJ_camellia_256_cbc) */,
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/pkcs7/internal.h b/pkgs/boring/third_party/boringssl/src/crypto/pkcs7/internal.h
index a6ab04e..66f8955 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/pkcs7/internal.h
+++ b/pkgs/boring/third_party/boringssl/src/crypto/pkcs7/internal.h
@@ -59,7 +59,7 @@
 //
 // Note: CMS v1 and PKCS#7 v1.5 are not completely compatible, but they overlap
 // in all cases implemented by this function.
-int pkcs7_add_external_signature(CBB *out, X509 *sign_cert, EVP_PKEY *key,
+int pkcs7_add_external_signature(CBB *out, X509 *sign_cert, const EVP_PKEY *key,
                                  const EVP_MD *md, BIO *data, bool use_key_id);
 
 BSSL_NAMESPACE_END
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/pkcs7/pkcs7_x509.cc b/pkgs/boring/third_party/boringssl/src/crypto/pkcs7/pkcs7_x509.cc
index cb9a725..c179e3a 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/pkcs7/pkcs7_x509.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/pkcs7/pkcs7_x509.cc
@@ -472,9 +472,9 @@
   return 1;
 }
 
-int bssl::pkcs7_add_external_signature(CBB *out, X509 *sign_cert, EVP_PKEY *key,
-                                       const EVP_MD *md, BIO *data,
-                                       bool use_key_id) {
+int bssl::pkcs7_add_external_signature(CBB *out, X509 *sign_cert,
+                                       const EVP_PKEY *key, const EVP_MD *md,
+                                       BIO *data, bool use_key_id) {
   signer_info_data si_data;
   si_data.use_key_id = use_key_id;
   si_data.sign_cert = sign_cert;
@@ -494,7 +494,7 @@
       /*cert_crl_cb=*/nullptr, write_signer_info, &si_data);
 }
 
-PKCS7 *PKCS7_sign(X509 *sign_cert, EVP_PKEY *pkey, STACK_OF(X509) *certs,
+PKCS7 *PKCS7_sign(X509 *sign_cert, const EVP_PKEY *pkey, STACK_OF(X509) *certs,
                   BIO *data, int flags) {
   ScopedCBB cbb;
   if (!CBB_init(cbb.get(), 2048)) {
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/pool/pool.cc b/pkgs/boring/third_party/boringssl/src/crypto/pool/pool.cc
index 4bc3a19..21e2203 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/pool/pool.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/pool/pool.cc
@@ -99,9 +99,7 @@
   if (pool == nullptr) {
     return nullptr;
   }
-  auto *ptr = const_cast<CRYPTO_BUFFER_POOL *>(pool);
-  CRYPTO_BUFFER_POOL_up_ref(ptr);
-  return ptr;
+  return FromOpaque(pool)->DupRefInternal();
 }
 
 void CryptoBuffer::UpRefInternal() const {
@@ -273,6 +271,9 @@
 }
 
 CRYPTO_BUFFER *CRYPTO_BUFFER_dup_ref(const CRYPTO_BUFFER *buf) {
+  if (buf == nullptr) {
+    return nullptr;
+  }
   auto *buf_ = const_cast<CRYPTO_BUFFER *>(buf);
   FromOpaque(buf_)->UpRefInternal();
   return buf_;
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/trust_token/trust_token.cc b/pkgs/boring/third_party/boringssl/src/crypto/trust_token/trust_token.cc
index d0e5c24..b295a6b 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/trust_token/trust_token.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/trust_token/trust_token.cc
@@ -279,13 +279,13 @@
   return 1;
 }
 
-int TRUST_TOKEN_CLIENT_set_srr_key(TRUST_TOKEN_CLIENT *ctx, EVP_PKEY *key) {
+int TRUST_TOKEN_CLIENT_set_srr_key(TRUST_TOKEN_CLIENT *ctx,
+                                   const EVP_PKEY *key) {
   if (!ctx->method->has_srr) {
     return 1;
   }
   EVP_PKEY_free(ctx->srr_key);
-  EVP_PKEY_up_ref(key);
-  ctx->srr_key = key;
+  ctx->srr_key = EVP_PKEY_dup_ref(key);
   return 1;
 }
 
@@ -515,10 +515,10 @@
   return 1;
 }
 
-int TRUST_TOKEN_ISSUER_set_srr_key(TRUST_TOKEN_ISSUER *ctx, EVP_PKEY *key) {
+int TRUST_TOKEN_ISSUER_set_srr_key(TRUST_TOKEN_ISSUER *ctx,
+                                   const EVP_PKEY *key) {
   EVP_PKEY_free(ctx->srr_key);
-  EVP_PKEY_up_ref(key);
-  ctx->srr_key = key;
+  ctx->srr_key = EVP_PKEY_dup_ref(key);
   return 1;
 }
 
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/x509/a_sign.cc b/pkgs/boring/third_party/boringssl/src/crypto/x509/a_sign.cc
index 3e313d4..f09e80a 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/x509/a_sign.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/x509/a_sign.cc
@@ -31,7 +31,7 @@
 using namespace bssl;
 
 int ASN1_item_sign(const ASN1_ITEM *it, X509_ALGOR *algor1, X509_ALGOR *algor2,
-                   ASN1_BIT_STRING *signature, void *asn, EVP_PKEY *pkey,
+                   ASN1_BIT_STRING *signature, void *asn, const EVP_PKEY *pkey,
                    const EVP_MD *type) {
   if (signature->type != V_ASN1_BIT_STRING) {
     OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/x509/internal.h b/pkgs/boring/third_party/boringssl/src/crypto/x509/internal.h
index ab07fec..238a2e8 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/x509/internal.h
+++ b/pkgs/boring/third_party/boringssl/src/crypto/x509/internal.h
@@ -61,7 +61,7 @@
 int x509_parse_public_key(CBS *cbs, X509_PUBKEY *out,
                           Span<const EVP_PKEY_ALG *const> algs);
 int x509_marshal_public_key(CBB *cbb, const X509_PUBKEY *in);
-int x509_pubkey_set1(X509_PUBKEY *key, EVP_PKEY *pkey);
+int x509_pubkey_set1(X509_PUBKEY *key, const EVP_PKEY *pkey);
 
 // X509_PUBKEY is an `ASN1_ITEM` whose ASN.1 type is SubjectPublicKeyInfo and C
 // type is `X509_PUBKEY*`.
@@ -170,18 +170,18 @@
   UniquePtr<CRYPTO_BUFFER> buf;
   CRYPTO_EX_DATA ex_data;
   // These contain copies of various extension values
-  long ex_pathlen = -1;
-  uint32_t ex_flags = 0;
-  uint32_t ex_kusage = 0;
-  uint32_t ex_xkusage = 0;
-  UniquePtr<ASN1_OCTET_STRING> skid;
-  UniquePtr<AUTHORITY_KEYID> akid;
-  UniquePtr<STACK_OF(DIST_POINT)> crldp;
-  UniquePtr<STACK_OF(GENERAL_NAME)> altname;
-  UniquePtr<NAME_CONSTRAINTS> nc;
-  unsigned char cert_hash[SHA256_DIGEST_LENGTH] = {};
+  mutable long ex_pathlen = -1;
+  mutable uint32_t ex_flags = 0;
+  mutable uint32_t ex_kusage = 0;
+  mutable uint32_t ex_xkusage = 0;
+  mutable UniquePtr<ASN1_OCTET_STRING> skid;
+  mutable UniquePtr<AUTHORITY_KEYID> akid;
+  mutable UniquePtr<STACK_OF(DIST_POINT)> crldp;
+  mutable UniquePtr<STACK_OF(GENERAL_NAME)> altname;
+  mutable UniquePtr<NAME_CONSTRAINTS> nc;
+  mutable unsigned char cert_hash[SHA256_DIGEST_LENGTH] = {};
   bssl::X509_CERT_AUX *aux = nullptr;
-  Mutex lock;
+  mutable Mutex lock;
 
  private:
   friend RefCounted;
@@ -406,10 +406,6 @@
   int error_depth;
   int error;
   X509 *current_cert;
-  X509_CRL *current_crl;  // current CRL
-
-  X509 *current_crl_issuer;  // issuer of current CRL
-  int current_crl_score;     // score of current CRL
 
   CRYPTO_EX_DATA ex_data;
 } /* X509_STORE_CTX */;
@@ -481,12 +477,11 @@
 // the user-initial-policy-set. If `user_policies` is NULL or empty, it is
 // interpreted as anyPolicy. `flags` is a set of `X509_V_FLAG_*` values to
 // apply. It returns `X509_V_OK` on success and `X509_V_ERR_*` on error. It
-// additionally sets `*out_current_cert` to the certificate where the error
-// occurred. If the function succeeded, or the error applies to the entire
-// chain, it sets `*out_current_cert` to NULL.
+// additionally sets `*out_error_depth` to the index of certificate where the
+// error occurred.
 int X509_policy_check(const STACK_OF(X509) *certs,
                       const STACK_OF(ASN1_OBJECT) *user_policies,
-                      unsigned long flags, X509 **out_current_cert);
+                      unsigned long flags, int *out_error_depth);
 
 // x509_check_issued_with_callback calls `X509_check_issued`, but allows the
 // verify callback to override the result. It returns one on success and zero on
@@ -527,7 +522,7 @@
 // x509v3_cache_extensions fills in a number of fields relating to X.509
 // extensions in `x`. It returns one on success and zero if some extensions were
 // invalid.
-OPENSSL_EXPORT int x509v3_cache_extensions(X509 *x);
+OPENSSL_EXPORT int x509v3_cache_extensions(const X509 *x);
 
 // x509v3_a2i_ipadd decodes `ipasc` as an IPv4 or IPv6 address. IPv6 addresses
 // use colon-separated syntax while IPv4 addresses use dotted decimal syntax. If
@@ -646,7 +641,7 @@
 // Merkle Tree Certificate (MTC) verification functions.
 
 // x509_is_merkle_tree_ca returns whether `x509` contains an extension of type
-// id-pe-mtcCertificationAuthority.
+// id-pe-mtcCertificationAuthority-SHA256.
 bool x509_is_merkle_tree_ca(const X509 *x509);
 
 // x509_evaluate_mtc_subtree_inclusion_proof carries out the procedure in
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/x509/policy.cc b/pkgs/boring/third_party/boringssl/src/crypto/x509/policy.cc
index b2c5fba..194b0b5 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/x509/policy.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/x509/policy.cc
@@ -571,8 +571,8 @@
 
 int X509_policy_check(const STACK_OF(X509) *certs,
                       const STACK_OF(ASN1_OBJECT) *user_policies,
-                      unsigned long flags, X509 **out_current_cert) {
-  *out_current_cert = nullptr;
+                      unsigned long flags, int *out_error_depth) {
+  *out_error_depth = 0;
 
   // Skip policy checking if the chain is just the trust anchor.
   const size_t num_certs = sk_X509_num(certs);
@@ -593,6 +593,7 @@
     X509 *cert = sk_X509_value(certs, i);
     uint32_t ex_flags = X509_get_extension_flags(cert);
     if (ex_flags & EXFLAG_INVALID) {
+      *out_error_depth = static_cast<int>(i);
       return X509_V_ERR_OUT_OF_MEM;
     }
     const bool is_self_issued = (ex_flags & EXFLAG_SI) != 0;
@@ -610,17 +611,19 @@
     const int any_policy_allowed =
         inhibit_any_policy > 0 || (i > 0 && is_self_issued);
     if (!process_certificate_policies(cert, &*level, any_policy_allowed)) {
-      *out_current_cert = cert;
+      *out_error_depth = static_cast<int>(i);
       return X509_V_ERR_INVALID_POLICY_EXTENSION;
     }
 
     // RFC 5280, section 6.1.3, step (f).
     if (explicit_policy == 0 && level->is_empty()) {
+      *out_error_depth = static_cast<int>(i);
       return X509_V_ERR_NO_EXPLICIT_POLICY;
     }
 
     // Insert the completed level into the list.
     if (!levels.Push(*std::exchange(level, std::nullopt))) {
+      *out_error_depth = static_cast<int>(i);
       return X509_V_ERR_OUT_OF_MEM;
     }
     level = std::nullopt;
@@ -631,7 +634,7 @@
       // RFC 5280, section 6.1.4, steps (a) and (b).
       level = process_policy_mappings(cert, &levels.back(), policy_mapping > 0);
       if (!level.has_value()) {
-        *out_current_cert = cert;
+        *out_error_depth = static_cast<int>(i);
         return X509_V_ERR_INVALID_POLICY_EXTENSION;
       }
     }
@@ -653,7 +656,7 @@
     }
     if (!process_policy_constraints(cert, &explicit_policy, &policy_mapping,
                                     &inhibit_any_policy)) {
-      *out_current_cert = cert;
+      *out_error_depth = static_cast<int>(i);
       return X509_V_ERR_INVALID_POLICY_EXTENSION;
     }
   }
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/x509/v3_purp.cc b/pkgs/boring/third_party/boringssl/src/crypto/x509/v3_purp.cc
index 86f23b8..2dba0d0 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/x509/v3_purp.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/x509/v3_purp.cc
@@ -18,6 +18,7 @@
 #include <openssl/digest.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
+#include <openssl/nid.h>
 #include <openssl/obj.h>
 #include <openssl/stack.h>
 #include <openssl/x509.h>
@@ -142,11 +143,11 @@
          nid == NID_policy_constraints ||    //
          nid == NID_name_constraints ||      //
          nid == NID_policy_mappings ||       //
-         nid == NID_inhibit_any_policy ||
-         nid == NID_pe_mtcCertificationAuthority_draft;
+         nid == NID_inhibit_any_policy ||    //
+         nid == NID_pe_mtcCertificationAuthority_SHA256;
 }
 
-static int setup_crldp(X509 *x) {
+static int setup_crldp(const X509 *x) {
   int j;
   auto *impl = FromOpaque(x);
   impl->crldp.reset(reinterpret_cast<STACK_OF(DIST_POINT) *>(
@@ -157,7 +158,7 @@
   return 1;
 }
 
-int bssl::x509v3_cache_extensions(X509 *x) {
+int bssl::x509v3_cache_extensions(const X509 *x) {
   BASIC_CONSTRAINTS *bs;
   ASN1_BIT_STRING *usage;
   EXTENDED_KEY_USAGE *extusage;
@@ -341,7 +342,7 @@
 }
 
 int X509_check_ca(const X509 *x) {
-  if (!x509v3_cache_extensions(const_cast<X509*>(x))) {
+  if (!x509v3_cache_extensions(x)) {
     return 0;
   }
   return check_ca(x);
@@ -450,8 +451,8 @@
                     X509_get_issuer_name(subject))) {
     return X509_V_ERR_SUBJECT_ISSUER_MISMATCH;
   }
-  if (!x509v3_cache_extensions(const_cast<X509 *>(issuer)) ||
-      !x509v3_cache_extensions(const_cast<X509 *>(subject))) {
+  if (!x509v3_cache_extensions(issuer) ||
+      !x509v3_cache_extensions(subject)) {
     return X509_V_ERR_UNSPECIFIED;
   }
 
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_cmp.cc b/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_cmp.cc
index 4dea236..5c78399 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_cmp.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_cmp.cc
@@ -107,8 +107,8 @@
   // comparison and may cause misbehaving sorts by transitivity. For now, we
   // retain the old OpenSSL behavior, which was to ignore the error. See
   // https://crbug.com/boringssl/355.
-  x509v3_cache_extensions((X509Impl *)a_impl);
-  x509v3_cache_extensions((X509Impl *)b_impl);
+  x509v3_cache_extensions(a_impl);
+  x509v3_cache_extensions(b_impl);
 
   return OPENSSL_memcmp(a_impl->cert_hash, b_impl->cert_hash,
                         SHA256_DIGEST_LENGTH);
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_lu.cc b/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_lu.cc
index 91c3b2d..5b60327 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_lu.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_lu.cc
@@ -153,9 +153,7 @@
   if (store == nullptr) {
     return nullptr;
   }
-  auto *ptr = const_cast<X509_STORE *>(store);
-  X509_STORE_up_ref(ptr);
-  return ptr;
+  return FromOpaque(store)->DupRefInternal();
 }
 
 void X509_STORE_free(X509_STORE *vfy) {
@@ -213,7 +211,7 @@
   return 1;
 }
 
-static int x509_store_add(X509Store *ctx, void *x, int is_crl) {
+static int x509_store_add(X509Store *ctx, const void *x, int is_crl) {
   if (x == nullptr) {
     return 0;
   }
@@ -225,12 +223,11 @@
 
   if (is_crl) {
     obj->type = X509_LU_CRL;
-    obj->data.crl = (X509_CRL *)x;
+    obj->data.crl = X509_CRL_dup_ref(static_cast<const X509_CRL *>(x));
   } else {
     obj->type = X509_LU_X509;
-    obj->data.x509 = (X509 *)x;
+    obj->data.x509 = X509_dup_ref(static_cast<const X509 *>(x));
   }
-  X509_OBJECT_up_ref_count(obj);
 
   ctx->objs_lock.LockWrite();
 
@@ -250,11 +247,11 @@
   return ret;
 }
 
-int X509_STORE_add_cert(X509_STORE *ctx, X509 *x) {
+int X509_STORE_add_cert(X509_STORE *ctx, const X509 *x) {
   return x509_store_add(FromOpaque(ctx), x, /*is_crl=*/0);
 }
 
-int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x) {
+int X509_STORE_add_crl(X509_STORE *ctx, const X509_CRL *x) {
   return x509_store_add(FromOpaque(ctx), x, /*is_crl=*/1);
 }
 
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_mtc.cc b/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_mtc.cc
index abbbbb4..1b4a8ae 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_mtc.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_mtc.cc
@@ -38,17 +38,16 @@
 // Prefix for domain separation to denote a Merkle Tree leaf or interior node.
 constexpr uint8_t kMTCLeafDomainSeparator[] = {0x00};
 constexpr uint8_t kMTCNodeDomainSeparator[] = {0x01};
-// DER encoding of AlgorithmIdentifier for alg-mtcProof-draft with absent
-// parameters.
-//   SEQUENCE (12 bytes) {
-//     OBJECT IDENTIFIER 1.3.6.1.4.1.44363.47.0 (10 bytes)
+// DER encoding of AlgorithmIdentifier for alg-mtcProof with absent parameters.
+//   SEQUENCE (10 bytes) {
+//     OBJECT IDENTIFIER 1.3.6.1.5.5.7.6.67 (8 bytes)
 //   }
 constexpr uint8_t kMTCAlgorithmIdentifier[] = {
     0x30,  // SEQUENCE tag
-    12,    // tag + len + OID bytes
+    10,    // tag + len + OID bytes
     0x06,  // OBJECT tag
-    10,    // OID length
-    OBJ_ENC_alg_mtcProof_draft,
+    8,     // OID length
+    OBJ_ENC_alg_mtcProof,
 };
 // MTCLogEntryType enum value for tbs_cert_entry.
 constexpr uint8_t kMTCLogEntryTypeTBSCertEntry[] = {0x00, 0x01};
@@ -64,6 +63,8 @@
                                          '4', '.', '1', '.'};
 // DER encoded OID component found in trust anchor IDs for issuance logs.
 constexpr uint8_t kEncOIDLogs = 0x00;
+// draft-ietf-tls-trust-anchor-ids-06 limits trust anchor IDs to 32 bytes.
+constexpr size_t kMaxTrustAnchorIdBytes = 32;
 
 constexpr CBS_ASN1_TAG kTBSVersionTag =
     CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0;
@@ -88,10 +89,18 @@
 
 // is_mtc_proof returns whether `algor` is an mtcProof AlgorithmIdentifier.
 bool is_mtc_proof(const X509_ALGOR *algor) {
-  return OBJ_obj2nid(algor->algorithm) == NID_alg_mtcProof_draft &&
+  return OBJ_obj2nid(algor->algorithm) == NID_alg_mtcProof &&
          algor->parameter == nullptr;
 }
 
+// is_mtc_serial_valid returns whether `serial` is within
+// (mtcMinSerial..mtcMaxSerial), which implies the log number is > 0.
+bool is_mtc_serial_valid(uint64_t serial) {
+  // mtcMinSerial is 2^48. mtcMaxSerial is 2^64-1, the largest number
+  // representable as uint64_t, so is not checked explicitly.
+  return serial >> 48;
+}
+
 // Returns the first hash value (of size `log_hash_size`) remaining in
 // `inclusion_proof` and advances `inclusion_proof` past the returned value.
 // Returns an empty span if there are no more hash values of the appropriate
@@ -137,6 +146,7 @@
 
 // MTCCACosigner represents a Merkle Tree CA and its corresponding CA cosigner
 // (see draft-ietf-plants-merkle-tree-certs, section 5.5).
+// Only id-pe-mtcCertificationAuthority-SHA256 is supported.
 class MTCCACosigner {
  public:
   MTCCACosigner() = default;
@@ -150,63 +160,59 @@
   bool Init(const X509 *x509, const EVP_PKEY *pkey) {
     // Get and parse the MTCCertificationAuthority extension (see section 5.5).
     int ext_index =
-        X509_get_ext_by_NID(x509, NID_pe_mtcCertificationAuthority_draft, -1);
+        X509_get_ext_by_NID(x509, NID_pe_mtcCertificationAuthority_SHA256, -1);
     const X509_EXTENSION *ext = X509_get_ext(x509, ext_index);
     if (ext == nullptr || !X509_EXTENSION_get_critical(ext)) {
       OPENSSL_PUT_ERROR(X509, X509_R_INVALID_MTC_CA);
       return false;
     }
     const ASN1_STRING *value = X509_EXTENSION_get_data(ext);
-    CBS ext_value;
+    CBS ext_value, seq;
     CBS_init(&ext_value, ASN1_STRING_get0_data(value),
              ASN1_STRING_length(value));
-
-    CBS seq, log_hash;
-    if (!CBS_get_asn1(&ext_value, &seq, CBS_ASN1_SEQUENCE) ||         //
-        CBS_len(&ext_value) != 0 ||                                   //
-        !CBS_get_asn1_element(&seq, &log_hash, CBS_ASN1_SEQUENCE) ||  //
-        !x509_parse_algorithm(&seq, cosign_sigalg_.get()) ||          //
-        !CBS_get_asn1_uint64(&seq, &min_serial_) ||                   //
-        !CBS_get_asn1_uint64(&seq, &max_serial_) ||                   //
+    if (!CBS_get_asn1(&ext_value, &seq, CBS_ASN1_SEQUENCE) ||  //
+        CBS_len(&ext_value) != 0 ||                            //
+        !x509_parse_algorithm(&seq, cosign_sigalg_.get()) ||   //
+        !CBS_get_asn1_uint64(&seq, &min_serial_) ||            //
+        !CBS_get_asn1_uint64(&seq, &max_serial_) ||            //
         CBS_len(&seq) != 0) {
       OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
       return false;
     }
-    log_hash_ = EVP_parse_digest_algorithm(&log_hash);
-    if (log_hash_ == nullptr) {
+    if (!is_mtc_serial_valid(min_serial_) ||
+        !is_mtc_serial_valid(max_serial_)) {
+      OPENSSL_PUT_ERROR(X509, X509_R_INVALID_MTC_CA);
       return false;
     }
-    if (CBS_len(&log_hash) != 0) {
-      OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
-      return false;
-    }
-
     // Extract the CA ID from the subject (see section 5.1).
     const X509_NAME *subject = X509_get_subject_name(x509);
     int tai_index =
-        X509_NAME_get_index_by_NID(subject, NID_rdna_trustAnchorID_draft, -1);
+        X509_NAME_get_index_by_NID(subject, NID_rdna_trustAnchorID, -1);
     if (tai_index < 0 || X509_NAME_entry_count(subject) != 1) {
       OPENSSL_PUT_ERROR(X509, X509_R_INVALID_MTC_CA);
       return false;
     }
-    // For initial experimentation, the attribute's value is a UTF8String
-    // containing the trust anchor ID's ASCII representation.
-    const ASN1_STRING *tai_ascii =
+    const ASN1_STRING *tai =
         X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject, tai_index));
-    if (ASN1_STRING_type(tai_ascii) != V_ASN1_UTF8STRING) {
+    // RELATIVE-OID has no dedicated ASN1_STRING representation, so the value
+    // is stored as V_ASN1_OTHER containing the entire element.
+    if (ASN1_STRING_type(tai) != V_ASN1_OTHER) {
       OPENSSL_PUT_ERROR(X509, X509_R_WRONG_TYPE);
       return false;
     }
-    std::string_view tai_ascii_str = BytesAsStringView(
-        Span(ASN1_STRING_get0_data(tai_ascii), ASN1_STRING_length(tai_ascii)));
-    ScopedCBB ca_id_enc;
-    if (!CBB_init(ca_id_enc.get(), 64) ||
-        !CBB_add_asn1_relative_oid_from_text(
-            ca_id_enc.get(), tai_ascii_str.data(), tai_ascii_str.size())) {
+    CBS tai_elem, tai_relative_oid;
+    CBS_init(&tai_elem, ASN1_STRING_get0_data(tai), ASN1_STRING_length(tai));
+    if (!CBS_get_asn1(&tai_elem, &tai_relative_oid, CBS_ASN1_RELATIVE_OID) ||
+        CBS_len(&tai_elem) != 0 ||
+        !CBS_is_valid_asn1_relative_oid(&tai_relative_oid)) {
       OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_OBJECT_ENCODING);
       return false;
     }
-    if (!CBBFinishArray(ca_id_enc.get(), &ca_id_enc_oid_)) {
+    if (CBS_len(&tai_relative_oid) > kMaxTrustAnchorIdBytes) {
+      OPENSSL_PUT_ERROR(X509, X509_R_INVALID_MTC_CA);
+      return false;
+    }
+    if (!ca_id_enc_oid_.CopyFrom(tai_relative_oid)) {
       OPENSSL_PUT_ERROR(X509, ERR_R_INTERNAL_ERROR);
       return false;
     }
@@ -216,7 +222,9 @@
   }
 
   bool IsSerialInRange(uint64_t serial) const {
-    return min_serial_ <= serial && serial <= max_serial_;
+    bool ok = min_serial_ <= serial && serial <= max_serial_;
+    assert(!ok || is_mtc_serial_valid(serial));
+    return ok;
   }
 
   // Constructs CosignedMessage and computes signature.
@@ -249,7 +257,7 @@
     return trust_anchor_id == ca_id_enc_oid_;
   }
 
-  const EVP_MD *log_hash() const { return log_hash_; }
+  const EVP_MD *log_hash() const { return EVP_sha256(); }
 
  private:
   // WriteTaiAscii concatenates the trust anchor ID prefix with the CA ID as a
@@ -257,9 +265,9 @@
   // OID components from `oid_suffix`, and writes the ASCII string
   // representation of the full trust anchor ID to `out`.
   bool WriteTaiAscii(CBB *out, Span<const uint64_t> oid_suffix = {}) const {
-    // Trust anchor IDs are constrained to 256 bytes by spec (and are typically
-    // much shorter).
-    uint8_t id[256];
+    // Trust anchor IDs such as the CA ID are limited to 32 bytes by spec, but
+    // `oid_suffix` may add additional bytes.
+    uint8_t id[2 * kMaxTrustAnchorIdBytes];
     CBB id_cbb;
     if (!CBB_init_fixed(&id_cbb, id, sizeof(id)) ||
         !CBB_add_bytes(&id_cbb, ca_id_enc_oid_.data(), ca_id_enc_oid_.size())) {
@@ -302,21 +310,20 @@
   // 5.1 and 5.4), in the form of a DER-encoded relative OID. This does not
   // include any framing, only the element's contents.
   Array<uint8_t> ca_id_enc_oid_;
-  // Log hash algorithm.
-  const EVP_MD *log_hash_ = nullptr;
   // Public key of the CA cosigner.
   UniquePtr<EVP_PKEY> cosign_pubkey_;
   // Signature algorithm for CA cosigner signatures.
   ScopedX509Algor cosign_sigalg_;
-  // Min and max allowed serial number, (0..2^64-1).
+  // Min and max allowed serial number, both (2^48 .. 2^64-1).
   uint64_t min_serial_ = 0u;
   uint64_t max_serial_ = 0u;
 };
 
 }  // namespace
 
+// Only id-pe-mtcCertificationAuthority-SHA256 is supported.
 bool x509_is_merkle_tree_ca(const X509 *x509) {
-  return X509_get_ext_by_NID(x509, NID_pe_mtcCertificationAuthority_draft,
+  return X509_get_ext_by_NID(x509, NID_pe_mtcCertificationAuthority_SHA256,
                              -1) >= 0;
 }
 
@@ -421,7 +428,7 @@
       !CBS_get_u48(&mtc_proof, &subtree_start) ||
       !CBS_get_u48(&mtc_proof, &subtree_end) ||
       !CBS_get_u16_length_prefixed(&mtc_proof, &inclusion_proof) ||
-      !CBS_get_u16_length_prefixed(&mtc_proof, &signatures) ||
+      !CBS_get_u24_length_prefixed(&mtc_proof, &signatures) ||
       CBS_len(&mtc_proof) != 0) {
     OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
     return 0;
@@ -497,12 +504,9 @@
     return 0;
   }
   // `index` is the least significant 48 bits of `serial`.
-  uint64_t index = serial & ((uint64_t{1} << 48) - 1);
-  uint64_t log_number = serial >> 48;
-  if (log_number == 0) {
-    OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PARAMETER);
-    return 0;
-  }
+  const uint64_t index = serial & ((uint64_t{1} << 48) - 1);
+  const uint64_t log_number = serial >> 48;
+  assert(is_mtc_serial_valid(serial));
 
   // The TBSCertificate's `signature` field must match the expected MTC
   // signature algorithm identifier.
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_set.cc b/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_set.cc
index ae45063..2ede541 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_set.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_set.cc
@@ -143,7 +143,7 @@
   }
 }
 
-int X509_set_pubkey(X509 *x, EVP_PKEY *pkey) {
+int X509_set_pubkey(X509 *x, const EVP_PKEY *pkey) {
   if (x == nullptr) {
     return 0;
   }
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_vfy.cc b/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_vfy.cc
index 6ceaed3..d291f54 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_vfy.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/x509/x509_vfy.cc
@@ -59,6 +59,14 @@
 // CRL issuer matches CRL AKID
 #define CRL_SCORE_AKID 0x004
 
+namespace {
+struct CRLInfo {
+  UniquePtr<X509_CRL> crl;
+  X509 *issuer = nullptr;
+  int score = 0;
+};
+}  // namespace
+
 static int null_callback(int ok, X509_STORE_CTX *e);
 static X509 *find_issuer(X509_STORE_CTX *ctx, const STACK_OF(X509) *sk,
                          const X509 *x);
@@ -67,20 +75,22 @@
 static int check_id(X509_STORE_CTX *ctx);
 static int check_trust(X509_STORE_CTX *ctx);
 static int check_revocation(X509_STORE_CTX *ctx);
-static int check_cert(X509_STORE_CTX *ctx);
+static int check_revocation_for_cert(X509_STORE_CTX *ctx, int depth);
 static int check_policy(X509_STORE_CTX *ctx);
 
 static UniquePtr<X509> get_trusted_issuer(X509_STORE_CTX *ctx, const X509 *x);
-static int get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer, X509_CRL *crl,
-                         X509 *x);
-static int get_crl(X509_STORE_CTX *ctx, X509_CRL **pcrl, X509 *x);
-static int crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, X509 **pissuer,
-                          int *pcrl_score);
-static int crl_crldp_check(X509 *x, X509_CRL *crl, int crl_score);
-static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl);
-static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x);
+static int get_crl_score(X509_STORE_CTX *ctx, X509 **out_issuer,
+                         const X509_CRL *crl, const X509 *x, int depth);
+static int get_crl(X509_STORE_CTX *ctx, CRLInfo *out_crl_info, const X509 *x,
+                   int depth);
+static int crl_akid_check(X509_STORE_CTX *ctx, const X509_CRL *crl,
+                          X509 **out_issuer, int *out_crl_score, int depth);
+static int crl_crldp_check(const X509 *x, const X509_CRL *crl, int crl_score);
+static int check_crl(X509_STORE_CTX *ctx, const CRLInfo &crl_info, int depth);
+static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, const X509 *x,
+                    int depth);
 
-static int internal_verify(X509_STORE_CTX *ctx);
+static int check_chain_signatures_and_time(X509_STORE_CTX *ctx);
 
 static int null_callback(int ok, X509_STORE_CTX *e) { return ok; }
 
@@ -107,6 +117,14 @@
   return ok;
 }
 
+// TODO(crbug.com/42290549): Switch remaining `call_verify_cb` calls to this
+// function.
+static int call_verify_cb_at_depth(int ok, X509_STORE_CTX *ctx, int depth) {
+  ctx->current_cert = sk_X509_value(ctx->chain, depth);
+  ctx->error_depth = depth;
+  return call_verify_cb(ok, ctx);
+}
+
 // Given a certificate try and find an exact match in the store
 static UniquePtr<X509> lookup_cert_match(X509_STORE_CTX *ctx, const X509 *x) {
   // Lookup all certs with matching subject name
@@ -253,10 +271,8 @@
       UniquePtr<X509> issuer = get_trusted_issuer(ctx, last);
       if (issuer == nullptr || X509_cmp(last, issuer.get()) != 0) {
         ctx->error = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT;
-        ctx->current_cert = last;
-        ctx->error_depth = num - 1;
         bad_chain = 1;
-        if (!call_verify_cb(0, ctx)) {
+        if (!call_verify_cb_at_depth(0, ctx, num - 1)) {
           return 0;
         }
       } else {
@@ -322,7 +338,6 @@
       } else {
         ctx->error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;
       }
-      ctx->current_cert = last;
     } else {
       if (!PushToStack(ctx->chain, std::move(chain_ss))) {
         ctx->error = X509_V_ERR_OUT_OF_MEM;
@@ -330,13 +345,11 @@
       }
       num++;
       ctx->last_untrusted = num;
-      ctx->current_cert = sk_X509_last(ctx->chain);
       ctx->error = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
     }
 
-    ctx->error_depth = num - 1;
     bad_chain = 1;
-    if (!call_verify_cb(0, ctx)) {
+    if (!call_verify_cb_at_depth(0, ctx, num - 1)) {
       return 0;
     }
   }
@@ -346,8 +359,8 @@
       !check_id(ctx) ||
       // We check revocation status after copying parameters because they may
       // be needed for CRL signature verification.
-      !check_revocation(ctx) ||  //
-      !internal_verify(ctx) ||   //
+      !check_revocation(ctx) ||                 //
+      !check_chain_signatures_and_time(ctx) ||  //
       !check_name_constraints(ctx) ||
       // TODO(davidben): Does `check_policy` still need to be conditioned on
       // |!bad_chain|? DoS concerns have been resolved.
@@ -414,9 +427,7 @@
     if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL) &&
         (x->ex_flags & EXFLAG_CRITICAL)) {
       ctx->error = X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION;
-      ctx->error_depth = i;
-      ctx->current_cert = x;
-      if (!call_verify_cb(0, ctx)) {
+      if (!call_verify_cb_at_depth(0, ctx, i)) {
         return 0;
       }
     }
@@ -424,18 +435,14 @@
     int must_be_ca = i > 0;
     if (must_be_ca && !X509_check_ca(x)) {
       ctx->error = X509_V_ERR_INVALID_CA;
-      ctx->error_depth = i;
-      ctx->current_cert = x;
-      if (!call_verify_cb(0, ctx)) {
+      if (!call_verify_cb_at_depth(0, ctx, i)) {
         return 0;
       }
     }
     if (ctx->param->purpose > 0 &&
         X509_check_purpose(x, purpose, must_be_ca) != 1) {
       ctx->error = X509_V_ERR_INVALID_PURPOSE;
-      ctx->error_depth = i;
-      ctx->current_cert = x;
-      if (!call_verify_cb(0, ctx)) {
+      if (!call_verify_cb_at_depth(0, ctx, i)) {
         return 0;
       }
     }
@@ -450,9 +457,7 @@
     // considered - they are just not counted as part of the path length!
     if (i > 1 && x->ex_pathlen != -1 && plen > x->ex_pathlen + 1) {
       ctx->error = X509_V_ERR_PATH_LENGTH_EXCEEDED;
-      ctx->error_depth = i;
-      ctx->current_cert = x;
-      if (!call_verify_cb(0, ctx)) {
+      if (!call_verify_cb_at_depth(0, ctx, i)) {
         return 0;
       }
     }
@@ -521,9 +526,7 @@
             return 0;
           default:
             ctx->error = rv;
-            ctx->error_depth = i;
-            ctx->current_cert = x;
-            if (!call_verify_cb(0, ctx)) {
+            if (!call_verify_cb_at_depth(0, ctx, i)) {
               return 0;
             }
             break;
@@ -553,9 +556,7 @@
         return 0;
       default:
         ctx->error = rv;
-        ctx->error_depth = 0;
-        ctx->current_cert = leaf;
-        if (!call_verify_cb(0, ctx)) {
+        if (!call_verify_cb_at_depth(0, ctx, /*depth=*/0)) {
           return 0;
         }
         break;
@@ -567,9 +568,7 @@
 
 static int check_id_error(X509_STORE_CTX *ctx, int errcode) {
   ctx->error = errcode;
-  ctx->current_cert = ctx->cert;
-  ctx->error_depth = 0;
-  return call_verify_cb(0, ctx);
+  return call_verify_cb_at_depth(0, ctx, /*depth=*/0);
 }
 
 static int check_hosts(X509 *x, X509_VERIFY_PARAM *param) {
@@ -624,10 +623,8 @@
     // If explicitly rejected notify callback and reject if not
     // overridden.
     if (trust == X509_TRUST_REJECTED) {
-      ctx->error_depth = (int)i;
-      ctx->current_cert = x;
       ctx->error = X509_V_ERR_CERT_REJECTED;
-      if (!call_verify_cb(0, ctx)) {
+      if (!call_verify_cb_at_depth(0, ctx, static_cast<int>(i))) {
         return X509_TRUST_REJECTED;
       }
     }
@@ -664,58 +661,36 @@
     last = 0;
   }
   for (int i = 0; i <= last; i++) {
-    ctx->error_depth = i;
-    if (!check_cert(ctx)) {
+    if (!check_revocation_for_cert(ctx, i)) {
       return 0;
     }
   }
   return 1;
 }
 
-static int check_cert(X509_STORE_CTX *ctx) {
-  X509_CRL *crl = nullptr;
-  int ok = 0, cnum = ctx->error_depth;
-  X509 *x = sk_X509_value(ctx->chain, cnum);
-  ctx->current_cert = x;
-  ctx->current_crl_issuer = nullptr;
-  ctx->current_crl_score = 0;
-
-  // Try to retrieve the relevant CRL. Note that `get_crl` sets
-  // `current_crl_issuer` and `current_crl_score`, which `check_crl` then reads.
-  //
-  // TODO(davidben): The awkward internal calling convention is a historical
-  // artifact of when these functions were user-overridable callbacks, even
-  // though there was no way to set them correctly. These callbacks have since
-  // been removed, so we can pass input and output parameters more directly.
-  if (!get_crl(ctx, &crl, x)) {
+static int check_revocation_for_cert(X509_STORE_CTX *ctx, int depth) {
+  X509 *x = sk_X509_value(ctx->chain, depth);
+  CRLInfo crl_info;
+  if (!get_crl(ctx, &crl_info, x, depth)) {
     ctx->error = X509_V_ERR_UNABLE_TO_GET_CRL;
-    ok = call_verify_cb(0, ctx);
-    goto err;
+    return call_verify_cb_at_depth(0, ctx, depth);
   }
 
-  ctx->current_crl = crl;
-  if (!check_crl(ctx, crl) ||  //
-      !cert_crl(ctx, crl, x)) {
-    goto err;
+  if (!check_crl(ctx, crl_info, depth) ||  //
+      !cert_crl(ctx, crl_info.crl.get(), x, depth)) {
+    return 0;
   }
 
-  ok = 1;
-
-err:
-  X509_CRL_free(crl);
-  ctx->current_crl = nullptr;
-  return ok;
+  return 1;
 }
 
 // Check CRL times against values in X509_STORE_CTX
-static int check_crl_time(X509_STORE_CTX *ctx, X509_CRL *crl, int notify) {
+static int check_crl_time(X509_STORE_CTX *ctx, const X509_CRL *crl, bool notify,
+                          int depth) {
   if (ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) {
     return 1;
   }
 
-  if (notify) {
-    ctx->current_crl = crl;
-  }
   int64_t ptime;
   if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) {
     ptime = ctx->param->check_time;
@@ -729,7 +704,7 @@
       return 0;
     }
     ctx->error = X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD;
-    if (!call_verify_cb(0, ctx)) {
+    if (!call_verify_cb_at_depth(0, ctx, depth)) {
       return 0;
     }
   }
@@ -739,7 +714,7 @@
       return 0;
     }
     ctx->error = X509_V_ERR_CRL_NOT_YET_VALID;
-    if (!call_verify_cb(0, ctx)) {
+    if (!call_verify_cb_at_depth(0, ctx, depth)) {
       return 0;
     }
   }
@@ -752,7 +727,7 @@
         return 0;
       }
       ctx->error = X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD;
-      if (!call_verify_cb(0, ctx)) {
+      if (!call_verify_cb_at_depth(0, ctx, depth)) {
         return 0;
       }
     }
@@ -761,36 +736,27 @@
         return 0;
       }
       ctx->error = X509_V_ERR_CRL_HAS_EXPIRED;
-      if (!call_verify_cb(0, ctx)) {
+      if (!call_verify_cb_at_depth(0, ctx, depth)) {
         return 0;
       }
     }
   }
 
-  if (notify) {
-    ctx->current_crl = nullptr;
-  }
-
   return 1;
 }
 
-static int get_crl_sk(X509_STORE_CTX *ctx, X509_CRL **pcrl, X509 **pissuer,
-                      int *pscore, STACK_OF(X509_CRL) *crls) {
-  int crl_score, best_score = *pscore;
-  X509 *x = ctx->current_cert;
-  X509_CRL *best_crl = nullptr;
-  X509 *crl_issuer = nullptr, *best_crl_issuer = nullptr;
-
-  for (size_t i = 0; i < sk_X509_CRL_num(crls); i++) {
-    X509_CRL *crl = sk_X509_CRL_value(crls, i);
-    crl_score = get_crl_score(ctx, &crl_issuer, crl, x);
-    if (crl_score < best_score || crl_score == 0) {
+static int get_crl_sk(X509_STORE_CTX *ctx, CRLInfo *best,
+                      STACK_OF(X509_CRL) *crls, const X509 *x, int depth) {
+  for (X509_CRL *crl : crls) {
+    X509 *crl_issuer = nullptr;
+    int crl_score = get_crl_score(ctx, &crl_issuer, crl, x, depth);
+    if (crl_score < best->score || crl_score == 0) {
       continue;
     }
     // If current CRL is equivalent use it if it is newer
-    if (crl_score == best_score && best_crl != nullptr) {
+    if (crl_score == best->score && best->crl != nullptr) {
       int day, sec;
-      if (ASN1_TIME_diff(&day, &sec, X509_CRL_get0_lastUpdate(best_crl),
+      if (ASN1_TIME_diff(&day, &sec, X509_CRL_get0_lastUpdate(best->crl.get()),
                          X509_CRL_get0_lastUpdate(crl)) == 0) {
         continue;
       }
@@ -800,33 +766,19 @@
         continue;
       }
     }
-    best_crl = crl;
-    best_crl_issuer = crl_issuer;
-    best_score = crl_score;
+    best->crl = UpRef(crl);
+    best->issuer = crl_issuer;
+    best->score = crl_score;
   }
 
-  if (best_crl) {
-    if (*pcrl) {
-      X509_CRL_free(*pcrl);
-    }
-    *pcrl = best_crl;
-    *pissuer = best_crl_issuer;
-    *pscore = best_score;
-    X509_CRL_up_ref(best_crl);
-  }
-
-  if (best_score >= CRL_SCORE_VALID) {
-    return 1;
-  }
-
-  return 0;
+  return best->score >= CRL_SCORE_VALID;
 }
 
 // For a given CRL return how suitable it is for the supplied certificate
 // 'x'. The return value is a mask of several criteria. If the issuer is not
 // the certificate issuer this is returned in *pissuer.
-static int get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer, X509_CRL *crl,
-                         X509 *x) {
+static int get_crl_score(X509_STORE_CTX *ctx, X509 **out_issuer,
+                         const X509_CRL *crl, const X509 *x, int depth) {
   if (crl->idp) {
     // Per RFC 5280, section 5.2.5, at most one of onlyContainsUserCerts,
     // onlyContainsCACerts, and onlyContainsAttributeCerts may be true.
@@ -854,12 +806,12 @@
   }
 
   // Check expiry
-  if (check_crl_time(ctx, crl, 0)) {
+  if (check_crl_time(ctx, crl, /*notify=*/false, depth)) {
     crl_score |= CRL_SCORE_TIME;
   }
 
   // Check authority key ID and locate certificate issuer
-  if (!crl_akid_check(ctx, crl, pissuer, &crl_score)) {
+  if (!crl_akid_check(ctx, crl, out_issuer, &crl_score, depth)) {
     // If we can't locate certificate issuer at this point forget it
     return 0;
   }
@@ -872,32 +824,28 @@
   return crl_score;
 }
 
-static int crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, X509 **pissuer,
-                          int *pcrl_score) {
-  X509 *crl_issuer = nullptr;
-  X509_NAME *cnm = X509_CRL_get_issuer(crl);
-  int cidx = ctx->error_depth;
-
-  if ((size_t)cidx != sk_X509_num(ctx->chain) - 1) {
-    cidx++;
+static int crl_akid_check(X509_STORE_CTX *ctx, const X509_CRL *crl,
+                          X509 **out_issuer, int *out_crl_score, int depth) {
+  const X509_NAME *cnm = X509_CRL_get_issuer(crl);
+  if ((size_t)depth != sk_X509_num(ctx->chain) - 1) {
+    depth++;
   }
 
-  crl_issuer = sk_X509_value(ctx->chain, cidx);
-
+  X509 *crl_issuer = sk_X509_value(ctx->chain, depth);
   if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK) {
-    *pcrl_score |= CRL_SCORE_AKID | CRL_SCORE_ISSUER_CERT;
-    *pissuer = crl_issuer;
+    *out_crl_score |= CRL_SCORE_AKID | CRL_SCORE_ISSUER_CERT;
+    *out_issuer = crl_issuer;
     return 1;
   }
 
-  for (cidx++; cidx < (int)sk_X509_num(ctx->chain); cidx++) {
-    crl_issuer = sk_X509_value(ctx->chain, cidx);
+  for (depth++; depth < (int)sk_X509_num(ctx->chain); depth++) {
+    crl_issuer = sk_X509_value(ctx->chain, depth);
     if (X509_NAME_cmp(X509_get_subject_name(crl_issuer), cnm)) {
       continue;
     }
     if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK) {
-      *pcrl_score |= CRL_SCORE_AKID | CRL_SCORE_SAME_PATH;
-      *pissuer = crl_issuer;
+      *out_crl_score |= CRL_SCORE_AKID | CRL_SCORE_SAME_PATH;
+      *out_issuer = crl_issuer;
       return 1;
     }
   }
@@ -939,7 +887,7 @@
 }
 
 // Check CRLDP and IDP
-static int crl_crldp_check(X509 *x, X509_CRL *crl, int crl_score) {
+static int crl_crldp_check(const X509 *x, const X509_CRL *crl, int crl_score) {
   auto *impl = FromOpaque(x);
   if (impl->ex_flags & EXFLAG_CA) {
     if (crl->idp && crl->idp->onlyuser) {
@@ -979,33 +927,22 @@
 }
 
 // Retrieve CRL corresponding to current certificate.
-static int get_crl(X509_STORE_CTX *ctx, X509_CRL **pcrl, X509 *x) {
-  X509 *issuer = nullptr;
-  int crl_score = 0;
-  X509_CRL *crl = nullptr;
-  STACK_OF(X509_CRL) *skcrl = nullptr;
-  if (get_crl_sk(ctx, &crl, &issuer, &crl_score, ctx->crls)) {
-    goto done;
+static int get_crl(X509_STORE_CTX *ctx, CRLInfo *out_crl_info, const X509 *x,
+                   int depth) {
+  CRLInfo best;
+  if (!get_crl_sk(ctx, &best, ctx->crls, x, depth)) {
+    // If we haven't yet found a CRL with sufficiently high score, lookup CRLs
+    // from the store.
+    UniquePtr<STACK_OF(X509_CRL)> skcrl(
+        X509_STORE_CTX_get1_crls(ctx, X509_get_issuer_name(x)));
+    if (skcrl) {
+      get_crl_sk(ctx, &best, skcrl.get(), x, depth);
+    }
   }
 
-  // Lookup CRLs from store
-  skcrl = X509_STORE_CTX_get1_crls(ctx, X509_get_issuer_name(x));
-
-  // If no CRLs found and a near match from get_crl_sk use that
-  if (!skcrl && crl) {
-    goto done;
-  }
-
-  get_crl_sk(ctx, &crl, &issuer, &crl_score, skcrl);
-
-  sk_X509_CRL_pop_free(skcrl, X509_CRL_free);
-
-done:
   // If we got any kind of CRL use it and return success
-  if (crl) {
-    ctx->current_crl_issuer = issuer;
-    ctx->current_crl_score = crl_score;
-    *pcrl = crl;
+  if (best.crl) {
+    *out_crl_info = std::move(best);
     return 1;
   }
 
@@ -1013,22 +950,22 @@
 }
 
 // Check CRL validity
-static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl) {
-  X509Impl *issuer = nullptr;
-  int cnum = ctx->error_depth;
-  int chnum = (int)sk_X509_num(ctx->chain) - 1;
+static int check_crl(X509_STORE_CTX *ctx, const CRLInfo &crl_info, int depth) {
+  X509_CRL *crl = crl_info.crl.get();
+  int root_depth = (int)sk_X509_num(ctx->chain) - 1;
   // If we have an alternative CRL issuer cert use that. Otherwise, it is the
   // issuer of the current certificate.
-  if (ctx->current_crl_issuer) {
-    issuer = FromOpaque(ctx->current_crl_issuer);
-  } else if (cnum < chnum) {
-    issuer = FromOpaque(sk_X509_value(ctx->chain, cnum + 1));
+  X509Impl *issuer = nullptr;
+  if (crl_info.issuer) {
+    issuer = FromOpaque(crl_info.issuer);
+  } else if (depth < root_depth) {
+    issuer = FromOpaque(sk_X509_value(ctx->chain, depth + 1));
   } else {
-    issuer = FromOpaque(sk_X509_value(ctx->chain, chnum));
+    issuer = FromOpaque(sk_X509_value(ctx->chain, root_depth));
     // If not self signed, can't check signature
     if (!x509_check_issued_with_callback(ctx, issuer, issuer)) {
       ctx->error = X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER;
-      if (!call_verify_cb(0, ctx)) {
+      if (!call_verify_cb_at_depth(0, ctx, depth)) {
         return 0;
       }
     }
@@ -1039,20 +976,20 @@
     if ((issuer->ex_flags & EXFLAG_KUSAGE) &&
         !(issuer->ex_kusage & X509v3_KU_CRL_SIGN)) {
       ctx->error = X509_V_ERR_KEYUSAGE_NO_CRL_SIGN;
-      if (!call_verify_cb(0, ctx)) {
+      if (!call_verify_cb_at_depth(0, ctx, depth)) {
         return 0;
       }
     }
 
-    if (!(ctx->current_crl_score & CRL_SCORE_SCOPE)) {
+    if (!(crl_info.score & CRL_SCORE_SCOPE)) {
       ctx->error = X509_V_ERR_DIFFERENT_CRL_SCOPE;
-      if (!call_verify_cb(0, ctx)) {
+      if (!call_verify_cb_at_depth(0, ctx, depth)) {
         return 0;
       }
     }
 
-    if (!(ctx->current_crl_score & CRL_SCORE_TIME)) {
-      if (!check_crl_time(ctx, crl, 1)) {
+    if (!(crl_info.score & CRL_SCORE_TIME)) {
+      if (!check_crl_time(ctx, crl, /*notify=*/true, depth)) {
         return 0;
       }
     }
@@ -1061,14 +998,14 @@
     EVP_PKEY *ikey = X509_get0_pubkey(issuer);
     if (!ikey) {
       ctx->error = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY;
-      if (!call_verify_cb(0, ctx)) {
+      if (!call_verify_cb_at_depth(0, ctx, depth)) {
         return 0;
       }
     } else {
       // Verify CRL signature
       if (X509_CRL_verify(crl, ikey) <= 0) {
         ctx->error = X509_V_ERR_CRL_SIGNATURE_FAILURE;
-        if (!call_verify_cb(0, ctx)) {
+        if (!call_verify_cb_at_depth(0, ctx, depth)) {
           return 0;
         }
       }
@@ -1079,7 +1016,8 @@
 }
 
 // Check certificate against CRL
-static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x) {
+static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, const X509 *x,
+                    int depth) {
   // The rules changed for this... previously if a CRL contained unhandled
   // critical extensions it could still be used to indicate a certificate
   // was revoked. This has since been changed since critical extension can
@@ -1087,7 +1025,7 @@
   if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL) &&
       (crl->flags & EXFLAG_CRITICAL)) {
     ctx->error = X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION;
-    if (!call_verify_cb(0, ctx)) {
+    if (!call_verify_cb_at_depth(0, ctx, depth)) {
       return 0;
     }
   }
@@ -1095,7 +1033,7 @@
   X509_REVOKED *rev;
   if (X509_CRL_get0_by_cert(crl, &rev, x)) {
     ctx->error = X509_V_ERR_CERT_REVOKED;
-    if (!call_verify_cb(0, ctx)) {
+    if (!call_verify_cb_at_depth(0, ctx, depth)) {
       return 0;
     }
   }
@@ -1104,22 +1042,21 @@
 }
 
 static int check_policy(X509_STORE_CTX *ctx) {
-  X509 *current_cert = nullptr;
+  int error_depth;
   int ret = X509_policy_check(ctx->chain, ctx->param->policies,
-                              ctx->param->flags, &current_cert);
+                              ctx->param->flags, &error_depth);
   if (ret != X509_V_OK) {
-    ctx->current_cert = current_cert;
     ctx->error = ret;
     if (ret == X509_V_ERR_OUT_OF_MEM) {
       return 0;
     }
-    return call_verify_cb(0, ctx);
+    return call_verify_cb_at_depth(0, ctx, error_depth);
   }
 
   return 1;
 }
 
-static int check_cert_time(X509_STORE_CTX *ctx, X509 *x) {
+static int check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth) {
   if (ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) {
     return 1;
   }
@@ -1136,16 +1073,14 @@
               : X509_cmp_time_posix(X509_get_notBefore(x), ptime);
   if (i == 0) {
     ctx->error = X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD;
-    ctx->current_cert = x;
-    if (!call_verify_cb(0, ctx)) {
+    if (!call_verify_cb_at_depth(0, ctx, depth)) {
       return 0;
     }
   }
 
   if (i > 0) {
     ctx->error = X509_V_ERR_CERT_NOT_YET_VALID;
-    ctx->current_cert = x;
-    if (!call_verify_cb(0, ctx)) {
+    if (!call_verify_cb_at_depth(0, ctx, depth)) {
       return 0;
     }
   }
@@ -1155,16 +1090,14 @@
           : X509_cmp_time_posix(X509_get_notAfter(x), ptime);
   if (i == 0) {
     ctx->error = X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD;
-    ctx->current_cert = x;
-    if (!call_verify_cb(0, ctx)) {
+    if (!call_verify_cb_at_depth(0, ctx, depth)) {
       return 0;
     }
   }
 
   if (i < 0) {
     ctx->error = X509_V_ERR_CERT_HAS_EXPIRED;
-    ctx->current_cert = x;
-    if (!call_verify_cb(0, ctx)) {
+    if (!call_verify_cb_at_depth(0, ctx, depth)) {
       return 0;
     }
   }
@@ -1173,9 +1106,14 @@
 }
 
 static int verify_signature(X509_STORE_CTX *ctx, const X509 *x509,
-                            const X509 *issuer, EVP_PKEY *pkey) {
+                            const X509 *issuer) {
+  EVP_PKEY *pkey = X509_get0_pubkey(issuer);
+  if (pkey == nullptr) {
+    return X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY;
+  }
+
   int ret;
-  if ((ctx->param->flags & X509_V_FLAG_USE_MTC_DRAFT_PLANTS_05) &&
+  if ((ctx->param->flags & X509_V_FLAG_USE_MTC_DRAFT_PLANTS_07) &&
       x509_is_merkle_tree_ca(issuer)) {
     ret = x509_verify_mtc(x509, pkey, issuer);
   } else {
@@ -1184,73 +1122,28 @@
   return ret ? X509_V_OK : X509_V_ERR_CERT_SIGNATURE_FAILURE;
 }
 
-static int internal_verify(X509_STORE_CTX *ctx) {
-  // TODO(davidben): This logic is incredibly confusing. Rewrite this:
-  //
-  // First, don't allow the verify callback to suppress
-  // X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY, which will simplify the
-  // signature check. Then replace jumping into the middle of the loop. It's
-  // trying to ensure that all certificates see `check_cert_time`, then checking
-  // the root's self signature when requested, but not breaking partial chains
-  // in the process.
-  int n = (int)sk_X509_num(ctx->chain);
-  ctx->error_depth = n - 1;
-  n--;
-  X509 *xi = sk_X509_value(ctx->chain, n);
-  X509 *xs;
-  if (x509_check_issued_with_callback(ctx, xi, xi)) {
-    xs = xi;
-  } else {
-    if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) {
-      xs = xi;
-      goto check_cert;
-    }
-    if (n <= 0) {
-      ctx->error = X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE;
-      ctx->current_cert = xi;
-      return call_verify_cb(0, ctx);
-    }
-    n--;
-    ctx->error_depth = n;
-    xs = sk_X509_value(ctx->chain, n);
-  }
-
-  //      ctx->error=0;  not needed
-  while (n >= 0) {
-    ctx->error_depth = n;
-
-    // Skip signature check for self signed certificates unless
-    // explicitly asked for. It doesn't add any security and just wastes
-    // time.
-    if (xs != xi || (ctx->param->flags & X509_V_FLAG_CHECK_SS_SIGNATURE)) {
-      EVP_PKEY *pkey = X509_get0_pubkey(xi);
-      int err = pkey == nullptr ? X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY
-                                : verify_signature(ctx, xs, xi, pkey);
-      if (err != X509_V_OK) {
+static int check_chain_signatures_and_time(X509_STORE_CTX *ctx) {
+  size_t num = sk_X509_num(ctx->chain);
+  for (size_t i = num - 1; i < num; i--) {
+    X509 *x509 = sk_X509_value(ctx->chain, i);
+    if (i < num - 1) {
+      X509 *issuer = sk_X509_value(ctx->chain, i + 1);
+      if (int err = verify_signature(ctx, x509, issuer); err != X509_V_OK) {
         ctx->error = err;
-        ctx->current_cert = xs;
-        if (!call_verify_cb(0, ctx)) {
+        if (!call_verify_cb_at_depth(0, ctx, static_cast<int>(i))) {
           return 0;
         }
       }
     }
 
-  check_cert:
-    if (!check_cert_time(ctx, xs)) {
+    if (!check_cert_time(ctx, x509, static_cast<int>(i))) {
       return 0;
     }
 
     // The last error (if any) is still in the error value
-    ctx->current_cert = xs;
-    if (!call_verify_cb(1, ctx)) {
+    if (!call_verify_cb_at_depth(1, ctx, static_cast<int>(i))) {
       return 0;
     }
-
-    n--;
-    if (n >= 0) {
-      xi = xs;
-      xs = sk_X509_value(ctx->chain, n);
-    }
   }
 
   return 1;
@@ -1348,10 +1241,6 @@
   return X509_chain_up_ref(ctx->chain);
 }
 
-X509_CRL *X509_STORE_CTX_get0_current_crl(const X509_STORE_CTX *ctx) {
-  return ctx->current_crl;
-}
-
 X509_STORE_CTX *X509_STORE_CTX_get0_parent_ctx(const X509_STORE_CTX *ctx) {
   // In OpenSSL, an `X509_STORE_CTX` sometimes has a parent context during CRL
   // path validation for indirect CRLs. We require the CRL to be issued
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/x509/x509cset.cc b/pkgs/boring/third_party/boringssl/src/crypto/x509/x509cset.cc
index 19e938a..5ece53d 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/x509/x509cset.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/x509/x509cset.cc
@@ -103,6 +103,15 @@
   return 1;
 }
 
+X509_CRL *X509_CRL_dup_ref(const X509_CRL *crl) {
+  if (crl == nullptr) {
+    return nullptr;
+  }
+  auto *ptr = const_cast<X509_CRL *>(crl);
+  CRYPTO_refcount_inc(&ptr->references);
+  return ptr;
+}
+
 long X509_CRL_get_version(const X509_CRL *crl) {
   return ASN1_INTEGER_get(crl->crl->version);
 }
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/x509/x_pubkey.cc b/pkgs/boring/third_party/boringssl/src/crypto/x509/x_pubkey.cc
index 4862f1f..3d59a1e 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/x509/x_pubkey.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/x509/x_pubkey.cc
@@ -118,7 +118,7 @@
 
 BSSL_NAMESPACE_END
 
-int bssl::x509_pubkey_set1(X509_PUBKEY *key, EVP_PKEY *pkey) {
+int bssl::x509_pubkey_set1(X509_PUBKEY *key, const EVP_PKEY *pkey) {
   ScopedCBB cbb;
   if (!CBB_init(cbb.get(), 64) || !EVP_marshal_public_key(cbb.get(), pkey)) {
     OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_ENCODE_ERROR);
diff --git a/pkgs/boring/third_party/boringssl/src/crypto/x509/x_x509.cc b/pkgs/boring/third_party/boringssl/src/crypto/x509/x_x509.cc
index 413c40f..a2f1f1d 100644
--- a/pkgs/boring/third_party/boringssl/src/crypto/x509/x_x509.cc
+++ b/pkgs/boring/third_party/boringssl/src/crypto/x509/x_x509.cc
@@ -328,10 +328,10 @@
 }
 
 X509 *X509_dup_ref(const X509 *x) {
-  auto *x_ref = const_cast<X509 *>(x);
-  // We know that this call always returns one.
-  X509_up_ref(x_ref);
-  return x_ref;
+  if (x == nullptr) {
+    return nullptr;
+  }
+  return FromOpaque(x)->DupRefInternal();
 }
 
 int X509_get_ex_new_index(long argl, void *argp, CRYPTO_EX_unused *unused,
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/asn1.h b/pkgs/boring/third_party/boringssl/src/include/openssl/asn1.h
index f92153d..2882a25 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/asn1.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/asn1.h
@@ -517,23 +517,12 @@
 OPENSSL_EXPORT const unsigned char *ASN1_STRING_get0_data(
     const ASN1_STRING *str);
 
-// ASN1_STRING_data returns a mutable pointer to `str`'s contents. Callers
-// should use `ASN1_STRING_length` to determine the length of the string. The
-// string may have embedded NUL bytes and may not be NUL-terminated.
+// ASN1_STRING_get_length returns the length of `str`, in bytes.
 //
 // The contents of an `ASN1_STRING` encode the value in some type-specific
 // representation that does not always correspond to the DER encoding of the
 // type. See the documentation for `ASN1_STRING` for details.
-//
-// Prefer `ASN1_STRING_get0_data`.
-OPENSSL_EXPORT unsigned char *ASN1_STRING_data(ASN1_STRING *str);
-
-// ASN1_STRING_length returns the length of `str`, in bytes.
-//
-// The contents of an `ASN1_STRING` encode the value in some type-specific
-// representation that does not always correspond to the DER encoding of the
-// type. See the documentation for `ASN1_STRING` for details.
-OPENSSL_EXPORT int ASN1_STRING_length(const ASN1_STRING *str);
+OPENSSL_EXPORT size_t ASN1_STRING_get_length(const ASN1_STRING *str);
 
 // ASN1_STRING_cmp compares `a` and `b`'s type and contents. It returns an
 // integer equal to, less than, or greater than zero if `a` is equal to, less
@@ -1810,6 +1799,25 @@
 // ASN1_STRING_TABLE_cleanup does nothing.
 OPENSSL_EXPORT void ASN1_STRING_TABLE_cleanup(void);
 
+// ASN1_STRING_data returns a mutable pointer to `str`'s contents. Callers
+// should use `ASN1_STRING_length` to determine the length of the string. The
+// string may have embedded NUL bytes and may not be NUL-terminated.
+//
+// The contents of an `ASN1_STRING` encode the value in some type-specific
+// representation that does not always correspond to the DER encoding of the
+// type. See the documentation for `ASN1_STRING` for details.
+//
+// Prefer `ASN1_STRING_get0_data`. See crbug.com/574019297.
+OPENSSL_EXPORT unsigned char *ASN1_STRING_data(ASN1_STRING *str);
+
+// ASN1_STRING_length returns the length of `str`, in bytes. Prefer
+// `ASN1_STRING_get_length`.
+//
+// The contents of an `ASN1_STRING` encode the value in some type-specific
+// representation that does not always correspond to the DER encoding of the
+// type. See the documentation for `ASN1_STRING` for details.
+OPENSSL_EXPORT int ASN1_STRING_length(const ASN1_STRING *str);
+
 // M_ASN1_* are legacy aliases for various `ASN1_STRING` functions. Use the
 // functions themselves.
 #define M_ASN1_STRING_length(x) ASN1_STRING_length(x)
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/base.h b/pkgs/boring/third_party/boringssl/src/include/openssl/base.h
index cb63423..e2741eb 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/base.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/base.h
@@ -73,7 +73,7 @@
 // A consumer may use this symbol in the preprocessor to temporarily build
 // against multiple revisions of BoringSSL at the same time. It is not
 // recommended to do so for longer than is necessary.
-#define BORINGSSL_API_VERSION 43
+#define BORINGSSL_API_VERSION 44
 
 #if defined(BORINGSSL_SHARED_LIBRARY)
 
@@ -395,7 +395,7 @@
 #if defined(BORINGSSL_NO_CXX)
 
 #define BORINGSSL_MAKE_DELETER(type, deleter)
-#define BORINGSSL_MAKE_UP_REF(type, up_ref_func)
+#define BORINGSSL_MAKE_UP_REF(type, dup_ref_func)
 #define BORINGSSL_MAKE_STACK_TRAITS(type, init_func, cleanup_func)
 #define BORINGSSL_MAKE_STACK_TRAITS_MOVABLE(type, init_func, cleanup_func, \
                                             move_func)
@@ -527,12 +527,9 @@
 template <typename T>
 using UniquePtr = std::unique_ptr<T, internal::Deleter>;
 
-#define BORINGSSL_MAKE_UP_REF(type, up_ref_func)             \
-  inline UniquePtr<type> UpRef(type *v) {                    \
-    if (v != nullptr) {                                      \
-      up_ref_func(v);                                        \
-    }                                                        \
-    return UniquePtr<type>(v);                               \
+#define BORINGSSL_MAKE_UP_REF(type, dup_ref_func)            \
+  inline UniquePtr<type> UpRef(const type *v) {              \
+    return UniquePtr<type>(dup_ref_func(v));                 \
   }                                                          \
                                                              \
   inline UniquePtr<type> UpRef(const UniquePtr<type> &ptr) { \
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/bio.h b/pkgs/boring/third_party/boringssl/src/include/openssl/bio.h
index 630be3e..48ee4ae 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/bio.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/bio.h
@@ -15,7 +15,7 @@
 #ifndef OPENSSL_HEADER_BIO_H
 #define OPENSSL_HEADER_BIO_H
 
-#include <openssl/base.h>   // IWYU pragma: export
+#include <openssl/base.h>  // IWYU pragma: export
 
 #include <stdio.h>  // For FILE
 
@@ -65,6 +65,10 @@
 // BIO_up_ref increments the reference count of `bio` and returns one.
 OPENSSL_EXPORT int BIO_up_ref(BIO *bio);
 
+// BIO_dup_ref increments the reference count of `bio` and returns the same
+// handle.
+OPENSSL_EXPORT BIO *BIO_dup_ref(const BIO *bio);
+
 
 // Basic I/O.
 
@@ -613,18 +617,20 @@
 
 #define BIO_CTRL_DGRAM_QUERY_MTU 40  // as kernel for current MTU
 
-#define BIO_CTRL_DGRAM_SET_MTU 42 /* set cached value for  MTU. want to use
-                                     this if asking the kernel fails */
+#define BIO_CTRL_DGRAM_SET_MTU                 \
+  42 /* set cached value for  MTU. want to use \
+        this if asking the kernel fails */
 
-#define BIO_CTRL_DGRAM_MTU_EXCEEDED 43 /* check whether the MTU was exceed in
-                                          the previous write operation. */
+#define BIO_CTRL_DGRAM_MTU_EXCEEDED         \
+  43 /* check whether the MTU was exceed in \
+        the previous write operation. */
 
 // BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT is unsupported as it is unused by consumers
 // and depends on `timeval`, which is not 2038-clean on all platforms.
 
-#define BIO_CTRL_DGRAM_GET_PEER           46
+#define BIO_CTRL_DGRAM_GET_PEER 46
 
-#define BIO_CTRL_DGRAM_GET_FALLBACK_MTU   47
+#define BIO_CTRL_DGRAM_GET_FALLBACK_MTU 47
 
 
 // BIO Pairs.
@@ -1031,7 +1037,7 @@
 BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(BIO, BIO_free)
-BORINGSSL_MAKE_UP_REF(BIO, BIO_up_ref)
+BORINGSSL_MAKE_UP_REF(BIO, BIO_dup_ref)
 BORINGSSL_MAKE_DELETER(BIO_METHOD, BIO_meth_free)
 
 BSSL_NAMESPACE_END
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/cms.h b/pkgs/boring/third_party/boringssl/src/include/openssl/cms.h
index d365a46..5a44517 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/cms.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/cms.h
@@ -15,7 +15,7 @@
 #ifndef OPENSSL_HEADER_CMS_H
 #define OPENSSL_HEADER_CMS_H
 
-#include <openssl/base.h>   // IWYU pragma: export
+#include <openssl/base.h>  // IWYU pragma: export
 
 #include <openssl/stack.h>
 
@@ -69,8 +69,9 @@
 // incomplete. `data` will then be ignored and should be NULL. The caller can
 // then continue configuring it and finalizing it with `CMS_final`. Otherwise,
 // the object will be finalized with `data` and `flags` passed to `CMS_final`.
-OPENSSL_EXPORT CMS_ContentInfo *CMS_sign(X509 *signcert, EVP_PKEY *pkey,
-                                         STACK_OF(X509) *certs, BIO *data,
+OPENSSL_EXPORT CMS_ContentInfo *CMS_sign(const X509 *signcert,
+                                         const EVP_PKEY *pkey,
+                                         const STACK_OF(X509) *certs, BIO *data,
                                          uint32_t flags);
 
 // CMS_ContentInfo_free releases memory associated with `cms`.
@@ -102,7 +103,8 @@
 // Subsequent calls will fail. Additionally, only RSA keys are currently
 // supported for `pkey`.
 OPENSSL_EXPORT CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms,
-                                               X509 *signcert, EVP_PKEY *pkey,
+                                               const X509 *signcert,
+                                               const EVP_PKEY *pkey,
                                                const EVP_MD *md,
                                                uint32_t flags);
 
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/dh.h b/pkgs/boring/third_party/boringssl/src/include/openssl/dh.h
index 59e8c9c..6cb5d9e 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/dh.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/dh.h
@@ -15,7 +15,7 @@
 #ifndef OPENSSL_HEADER_DH_H
 #define OPENSSL_HEADER_DH_H
 
-#include <openssl/base.h>   // IWYU pragma: export
+#include <openssl/base.h>  // IWYU pragma: export
 
 #if defined(__cplusplus)
 extern "C" {
@@ -49,6 +49,11 @@
 // mutate `dh` for thread-safety purposes and may be used concurrently.
 OPENSSL_EXPORT int DH_up_ref(DH *dh);
 
+// DH_dup_ref increments the reference count of `dh` and returns the same
+// handle. It does not mutate `dh` for thread-safety purposes and may be used
+// concurrently.
+OPENSSL_EXPORT DH *DH_dup_ref(const DH *dh);
+
 
 // Properties.
 
@@ -317,7 +322,7 @@
 BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(DH, DH_free)
-BORINGSSL_MAKE_UP_REF(DH, DH_up_ref)
+BORINGSSL_MAKE_UP_REF(DH, DH_dup_ref)
 
 BSSL_NAMESPACE_END
 
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/digest.h b/pkgs/boring/third_party/boringssl/src/include/openssl/digest.h
index 8a81bc1..73270de 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/digest.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/digest.h
@@ -44,6 +44,16 @@
 OPENSSL_EXPORT const EVP_MD *EVP_sha512_256(void);
 OPENSSL_EXPORT const EVP_MD *EVP_blake2b256(void);
 
+// SHA-3 family of digest functions.
+//
+// WARNING: Prefer to use SHA-2 (e.g. `EVP_sha256`) over SHA-3. Despite being a
+// newer hash function, SHA-3 performs poorly and should only be used when
+// necessary for interoperability with existing systems.
+
+OPENSSL_EXPORT const EVP_MD *EVP_sha3_256(void);
+OPENSSL_EXPORT const EVP_MD *EVP_sha3_384(void);
+OPENSSL_EXPORT const EVP_MD *EVP_sha3_512(void);
+
 // EVP_md5_sha1 is a TLS-specific `EVP_MD` which computes the concatenation of
 // MD5 and SHA-1, as used in TLS 1.1 and below.
 OPENSSL_EXPORT const EVP_MD *EVP_md5_sha1(void);
@@ -127,7 +137,7 @@
 
 // EVP_MAX_MD_BLOCK_SIZE is the largest digest block size supported, in
 // bytes.
-#define EVP_MAX_MD_BLOCK_SIZE 128  // SHA-512 is the longest so far.
+#define EVP_MAX_MD_BLOCK_SIZE 136  // SHA3-256 is the longest so far.
 
 // EVP_DigestFinal_ex finishes the digest in `ctx` and writes the output to
 // `md_out`. `EVP_MD_CTX_size` bytes are written, which is at most
@@ -344,9 +354,9 @@
 struct evp_md_pctx_ops;
 
 // EVP_MAX_MD_DATA_SIZE is a private constant which specifies the size of the
-// largest digest state. SHA-512 and BLAKE2b are joint-largest. Consuming code
-// only uses this via the `EVP_MD_CTX` type.
-#define EVP_MAX_MD_DATA_SIZE 208
+// largest digest state. SHA3-256 is the largest. Consuming code only uses this
+// via the `EVP_MD_CTX` type.
+#define EVP_MAX_MD_DATA_SIZE 240
 
 // env_md_ctx_st is typoed ("evp" -> "env"), but the typo comes from OpenSSL
 // and some consumers forward-declare these structures so we're leaving it
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/dsa.h b/pkgs/boring/third_party/boringssl/src/include/openssl/dsa.h
index 3ae4d11..a77bf91 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/dsa.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/dsa.h
@@ -375,7 +375,7 @@
 BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(DSA, DSA_free)
-BORINGSSL_MAKE_UP_REF(DSA, DSA_up_ref)
+BORINGSSL_MAKE_UP_REF(DSA, DSA_dup_ref)
 BORINGSSL_MAKE_DELETER(DSA_SIG, DSA_SIG_free)
 
 BSSL_NAMESPACE_END
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/ec_key.h b/pkgs/boring/third_party/boringssl/src/include/openssl/ec_key.h
index d894a67..296b7c0 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/ec_key.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/ec_key.h
@@ -352,7 +352,7 @@
 BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(EC_KEY, EC_KEY_free)
-BORINGSSL_MAKE_UP_REF(EC_KEY, EC_KEY_up_ref)
+BORINGSSL_MAKE_UP_REF(EC_KEY, EC_KEY_dup_ref)
 
 BSSL_NAMESPACE_END
 
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/evp.h b/pkgs/boring/third_party/boringssl/src/include/openssl/evp.h
index 8a767ca..4af1726 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/evp.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/evp.h
@@ -498,13 +498,10 @@
 // Ed25519, `type` should be NULL. The `EVP_MD_CTX` itself is unused but is
 // present so the API is uniform. See `EVP_DigestSign`.
 //
-// This function does not mutate `pkey` for thread-safety purposes and may be
-// used concurrently with other non-mutating functions on `pkey`.
-//
 // It returns one on success, or zero on error.
 OPENSSL_EXPORT int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
                                       const EVP_MD *type, ENGINE *e,
-                                      EVP_PKEY *pkey);
+                                      const EVP_PKEY *pkey);
 
 // EVP_DigestSignUpdate appends `len` bytes from `data` to the data which will
 // be signed in `EVP_DigestSignFinal`. It returns one.
@@ -554,13 +551,10 @@
 // Ed25519, `type` should be NULL. The `EVP_MD_CTX` itself is unused but is
 // present so the API is uniform. See `EVP_DigestVerify`.
 //
-// This function does not mutate `pkey` for thread-safety purposes and may be
-// used concurrently with other non-mutating functions on `pkey`.
-//
 // It returns one on success, or zero on error.
 OPENSSL_EXPORT int EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
                                         const EVP_MD *type, ENGINE *e,
-                                        EVP_PKEY *pkey);
+                                        const EVP_PKEY *pkey);
 
 // EVP_DigestVerifyUpdate appends `len` bytes from `data` to the data which
 // will be verified by `EVP_DigestVerifyFinal`. It returns one.
@@ -615,13 +609,9 @@
 // actual size of the signature is written to `*out_sig_len`.
 //
 // It returns one on success and zero otherwise.
-//
-// It does not modify `ctx`, thus it's possible to continue to use `ctx` in
-// order to sign a longer message. It also does not mutate `pkey` for
-// thread-safety purposes and may be used concurrently with other non-mutating
-// functions on `pkey`.
 OPENSSL_EXPORT int EVP_SignFinal(const EVP_MD_CTX *ctx, uint8_t *sig,
-                                 unsigned int *out_sig_len, EVP_PKEY *pkey);
+                                 unsigned int *out_sig_len,
+                                 const EVP_PKEY *pkey);
 
 
 // Verifying (old functions)
@@ -650,13 +640,8 @@
 // calls to `EVP_VerifyUpdate`.
 //
 // It returns one on success and zero otherwise.
-//
-// It does not modify `ctx`, thus it's possible to continue to use `ctx` in
-// order to verify a longer message. It also does not mutate `pkey` for
-// thread-safety purposes and may be used concurrently with other non-mutating
-// functions on `pkey`.
 OPENSSL_EXPORT int EVP_VerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig,
-                                   size_t sig_len, EVP_PKEY *pkey);
+                                   size_t sig_len, const EVP_PKEY *pkey);
 
 
 // Printing
@@ -752,7 +737,7 @@
 
 // EVP_PKEY_CTX_new allocates a fresh `EVP_PKEY_CTX` for use with `pkey`. It
 // returns the context or NULL on error.
-OPENSSL_EXPORT EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
+OPENSSL_EXPORT EVP_PKEY_CTX *EVP_PKEY_CTX_new(const EVP_PKEY *pkey, ENGINE *e);
 
 // EVP_PKEY_CTX_new_id allocates a fresh `EVP_PKEY_CTX` for a key of type `id`
 // (e.g. `EVP_PKEY_HMAC`). This can be used for key generation where
@@ -770,7 +755,7 @@
 OPENSSL_EXPORT EVP_PKEY_CTX *EVP_PKEY_CTX_dup(EVP_PKEY_CTX *ctx);
 
 // EVP_PKEY_CTX_get0_pkey returns the `EVP_PKEY` associated with `ctx`.
-OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_CTX_get0_pkey(EVP_PKEY_CTX *ctx);
+OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_CTX_get0_pkey(const EVP_PKEY_CTX *ctx);
 
 // EVP_PKEY_sign_init initialises an `EVP_PKEY_CTX` for a signing operation. It
 // should be called before `EVP_PKEY_sign`.
@@ -897,7 +882,8 @@
 // by `ctx` to `peer`. It should be called after `EVP_PKEY_derive_init`. (For
 // example, this is used to set the peer's key in (EC)DH.) It returns one on
 // success and zero on error.
-OPENSSL_EXPORT int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer);
+OPENSSL_EXPORT int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx,
+                                            const EVP_PKEY *peer);
 
 // EVP_PKEY_derive derives a shared key from `ctx`. If `key` is non-NULL then,
 // on entry, `out_key_len` must contain the amount of space at `key`. If
@@ -1633,7 +1619,7 @@
 BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(EVP_PKEY, EVP_PKEY_free)
-BORINGSSL_MAKE_UP_REF(EVP_PKEY, EVP_PKEY_up_ref)
+BORINGSSL_MAKE_UP_REF(EVP_PKEY, EVP_PKEY_dup_ref)
 BORINGSSL_MAKE_DELETER(EVP_PKEY_CTX, EVP_PKEY_CTX_free)
 
 BSSL_NAMESPACE_END
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/nid.h b/pkgs/boring/third_party/boringssl/src/include/openssl/nid.h
index 9ca8e67..38e5dc8 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/nid.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/nid.h
@@ -5511,48 +5511,47 @@
 #define SN_X_Wing "X-Wing"
 #define NID_X_Wing 972
 
-#define SN_alg_mtcProof_draft "alg-mtcProof-draft"
-#define NID_alg_mtcProof_draft 976
-#define OBJ_alg_mtcProof_draft 1L, 3L, 6L, 1L, 4L, 1L, 44363L, 47L, 0L
-#define OBJ_ENC_alg_mtcProof_draft \
-  0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xda, 0x4b, 0x2f, 0x00
-
-#define SN_rdna_trustAnchorID_draft "rdna-trustAnchorID-draft"
-#define NID_rdna_trustAnchorID_draft 977
-#define OBJ_rdna_trustAnchorID_draft 1L, 3L, 6L, 1L, 4L, 1L, 44363L, 47L, 1L
-#define OBJ_ENC_rdna_trustAnchorID_draft \
-  0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xda, 0x4b, 0x2f, 0x01
-
-#define SN_pe_mtcCertificationAuthority_draft \
-  "pe-mtcCertificationAuthority-draft"
-#define NID_pe_mtcCertificationAuthority_draft 978
-#define OBJ_pe_mtcCertificationAuthority_draft \
-  1L, 3L, 6L, 1L, 4L, 1L, 44363L, 47L, 2L
-#define OBJ_ENC_pe_mtcCertificationAuthority_draft \
-  0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xda, 0x4b, 0x2f, 0x02
-
 #define SN_SLH_DSA_SHA2_128s "id-slh-dsa-sha2-128s"
 #define LN_SLH_DSA_SHA2_128s "SLH-DSA-SHA2-128s"
-#define NID_SLH_DSA_SHA2_128s 979
+#define NID_SLH_DSA_SHA2_128s 973
 #define OBJ_SLH_DSA_SHA2_128s 2L, 16L, 840L, 1L, 101L, 3L, 4L, 3L, 20L
 #define OBJ_ENC_SLH_DSA_SHA2_128s \
   0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x14
 
 #define SN_SLH_DSA_SHAKE_256f "id-slh-dsa-shake-256f"
 #define LN_SLH_DSA_SHAKE_256f "SLH-DSA-SHAKE-256f"
-#define NID_SLH_DSA_SHAKE_256f 980
+#define NID_SLH_DSA_SHAKE_256f 974
 #define OBJ_SLH_DSA_SHAKE_256f 2L, 16L, 840L, 1L, 101L, 3L, 4L, 3L, 31L
 #define OBJ_ENC_SLH_DSA_SHAKE_256f \
   0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x1f
 
 #define SN_SLH_DSA_SHA2_128s_WITH_SHA256 "id-hash-slh-dsa-sha2-128s-with-sha256"
 #define LN_SLH_DSA_SHA2_128s_WITH_SHA256 "SLH-DSA-SHA2-128s-WITH-SHA256"
-#define NID_SLH_DSA_SHA2_128s_WITH_SHA256 981
+#define NID_SLH_DSA_SHA2_128s_WITH_SHA256 975
 #define OBJ_SLH_DSA_SHA2_128s_WITH_SHA256 \
   2L, 16L, 840L, 1L, 101L, 3L, 4L, 3L, 35L
 #define OBJ_ENC_SLH_DSA_SHA2_128s_WITH_SHA256 \
   0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x23
 
+#define SN_alg_mtcProof "alg-mtcProof"
+#define NID_alg_mtcProof 976
+#define OBJ_alg_mtcProof 1L, 3L, 6L, 1L, 5L, 5L, 7L, 6L, 67L
+#define OBJ_ENC_alg_mtcProof 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x06, 0x43
+
+#define SN_rdna_trustAnchorID "rdna-trustAnchorID"
+#define NID_rdna_trustAnchorID 977
+#define OBJ_rdna_trustAnchorID 1L, 3L, 6L, 1L, 5L, 5L, 7L, 25L, 3L
+#define OBJ_ENC_rdna_trustAnchorID \
+  0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x19, 0x03
+
+#define SN_pe_mtcCertificationAuthority_SHA256 \
+  "pe-mtcCertificationAuthority-SHA256"
+#define NID_pe_mtcCertificationAuthority_SHA256 978
+#define OBJ_pe_mtcCertificationAuthority_SHA256 \
+  1L, 3L, 6L, 1L, 5L, 5L, 7L, 1L, 38L
+#define OBJ_ENC_pe_mtcCertificationAuthority_SHA256 \
+  0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x26
+
 
 #if defined(__cplusplus)
 } /* extern C */
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/pkcs7.h b/pkgs/boring/third_party/boringssl/src/include/openssl/pkcs7.h
index 85c851e..b8f5b2b 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/pkcs7.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/pkcs7.h
@@ -15,7 +15,7 @@
 #ifndef OPENSSL_HEADER_PKCS7_H
 #define OPENSSL_HEADER_PKCS7_H
 
-#include <openssl/base.h>   // IWYU pragma: export
+#include <openssl/base.h>  // IWYU pragma: export
 
 #include <openssl/stack.h>
 
@@ -59,8 +59,8 @@
 
 // PKCS7_bundle_certificates behaves like `PKCS7_bundle_raw_certificates` but
 // takes `X509` objects as input.
-OPENSSL_EXPORT int PKCS7_bundle_certificates(
-    CBB *out, const STACK_OF(X509) *certs);
+OPENSSL_EXPORT int PKCS7_bundle_certificates(CBB *out,
+                                             const STACK_OF(X509) *certs);
 
 // PKCS7_get_CRLs parses a PKCS#7, SignedData structure from `cbs` and appends
 // the included CRLs to `out_crls`. It returns one on success and zero on error.
@@ -144,8 +144,7 @@
 
 // d2i_PKCS7 parses a BER-encoded, PKCS#7 signed data ContentInfo structure from
 // `len` bytes at `*inp`, as described in `d2i_SAMPLE`.
-OPENSSL_EXPORT PKCS7 *d2i_PKCS7(PKCS7 **out, const uint8_t **inp,
-                                size_t len);
+OPENSSL_EXPORT PKCS7 *d2i_PKCS7(PKCS7 **out, const uint8_t **inp, size_t len);
 
 // d2i_PKCS7_bio behaves like `d2i_PKCS7` but reads the input from `bio`.  If
 // the length of the object is indefinite the full contents of `bio` are read.
@@ -215,7 +214,7 @@
 //
 // Note this function only implements a subset of the corresponding OpenSSL
 // function. It is provided for backwards compatibility only.
-OPENSSL_EXPORT PKCS7 *PKCS7_sign(X509 *sign_cert, EVP_PKEY *pkey,
+OPENSSL_EXPORT PKCS7 *PKCS7_sign(X509 *sign_cert, const EVP_PKEY *pkey,
                                  STACK_OF(X509) *certs, BIO *data, int flags);
 
 
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/pool.h b/pkgs/boring/third_party/boringssl/src/include/openssl/pool.h
index 6eb759c..2c3855b 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/pool.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/pool.h
@@ -130,9 +130,9 @@
 BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(CRYPTO_BUFFER_POOL, CRYPTO_BUFFER_POOL_free)
-BORINGSSL_MAKE_UP_REF(CRYPTO_BUFFER_POOL, CRYPTO_BUFFER_POOL_up_ref)
+BORINGSSL_MAKE_UP_REF(CRYPTO_BUFFER_POOL, CRYPTO_BUFFER_POOL_dup_ref)
 BORINGSSL_MAKE_DELETER(CRYPTO_BUFFER, CRYPTO_BUFFER_free)
-BORINGSSL_MAKE_UP_REF(CRYPTO_BUFFER, CRYPTO_BUFFER_up_ref)
+BORINGSSL_MAKE_UP_REF(CRYPTO_BUFFER, CRYPTO_BUFFER_dup_ref)
 
 BSSL_NAMESPACE_END
 
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/prefix_symbols.h b/pkgs/boring/third_party/boringssl/src/include/openssl/prefix_symbols.h
index 58f1fa8..47df60c 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/prefix_symbols.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/prefix_symbols.h
@@ -130,6 +130,7 @@
 #pragma redefine_extname ASN1_STRING_free BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_STRING_free)
 #pragma redefine_extname ASN1_STRING_get0_data BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_STRING_get0_data)
 #pragma redefine_extname ASN1_STRING_get_default_mask BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_STRING_get_default_mask)
+#pragma redefine_extname ASN1_STRING_get_length BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_STRING_get_length)
 #pragma redefine_extname ASN1_STRING_length BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_STRING_length)
 #pragma redefine_extname ASN1_STRING_new BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_STRING_new)
 #pragma redefine_extname ASN1_STRING_print BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_STRING_print)
@@ -232,6 +233,7 @@
 #pragma redefine_extname BIO_ctrl_get_write_guarantee BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_ctrl_get_write_guarantee)
 #pragma redefine_extname BIO_ctrl_pending BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_ctrl_pending)
 #pragma redefine_extname BIO_do_connect BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_do_connect)
+#pragma redefine_extname BIO_dup_ref BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_dup_ref)
 #pragma redefine_extname BIO_eof BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_eof)
 #pragma redefine_extname BIO_f_base64 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_f_base64)
 #pragma redefine_extname BIO_f_ssl BORINGSSL_ADD_USER_LABEL_AND_PREFIX(BIO_f_ssl)
@@ -697,6 +699,7 @@
 #pragma redefine_extname DH_compute_key BORINGSSL_ADD_USER_LABEL_AND_PREFIX(DH_compute_key)
 #pragma redefine_extname DH_compute_key_hashed BORINGSSL_ADD_USER_LABEL_AND_PREFIX(DH_compute_key_hashed)
 #pragma redefine_extname DH_compute_key_padded BORINGSSL_ADD_USER_LABEL_AND_PREFIX(DH_compute_key_padded)
+#pragma redefine_extname DH_dup_ref BORINGSSL_ADD_USER_LABEL_AND_PREFIX(DH_dup_ref)
 #pragma redefine_extname DH_free BORINGSSL_ADD_USER_LABEL_AND_PREFIX(DH_free)
 #pragma redefine_extname DH_generate_key BORINGSSL_ADD_USER_LABEL_AND_PREFIX(DH_generate_key)
 #pragma redefine_extname DH_generate_parameters BORINGSSL_ADD_USER_LABEL_AND_PREFIX(DH_generate_parameters)
@@ -1378,6 +1381,9 @@
 #pragma redefine_extname EVP_sha224 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_sha224)
 #pragma redefine_extname EVP_sha256 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_sha256)
 #pragma redefine_extname EVP_sha384 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_sha384)
+#pragma redefine_extname EVP_sha3_256 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_sha3_256)
+#pragma redefine_extname EVP_sha3_384 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_sha3_384)
+#pragma redefine_extname EVP_sha3_512 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_sha3_512)
 #pragma redefine_extname EVP_sha512 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_sha512)
 #pragma redefine_extname EVP_sha512_256 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_sha512_256)
 #pragma redefine_extname EXTENDED_KEY_USAGE_free BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EXTENDED_KEY_USAGE_free)
@@ -2055,7 +2061,6 @@
 #pragma redefine_extname SSL_CTX_set_ex_data BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_CTX_set_ex_data)
 #pragma redefine_extname SSL_CTX_set_false_start_allowed_without_alpn BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_CTX_set_false_start_allowed_without_alpn)
 #pragma redefine_extname SSL_CTX_set_grease_enabled BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_CTX_set_grease_enabled)
-#pragma redefine_extname SSL_CTX_set_grease_sigalgs_enabled BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_CTX_set_grease_sigalgs_enabled)
 #pragma redefine_extname SSL_CTX_set_info_callback BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_CTX_set_info_callback)
 #pragma redefine_extname SSL_CTX_set_keylog_callback BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_CTX_set_keylog_callback)
 #pragma redefine_extname SSL_CTX_set_max_cert_list BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_CTX_set_max_cert_list)
@@ -2580,6 +2585,7 @@
 #pragma redefine_extname X509_CRL_delete_ext BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_CRL_delete_ext)
 #pragma redefine_extname X509_CRL_digest BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_CRL_digest)
 #pragma redefine_extname X509_CRL_dup BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_CRL_dup)
+#pragma redefine_extname X509_CRL_dup_ref BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_CRL_dup_ref)
 #pragma redefine_extname X509_CRL_free BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_CRL_free)
 #pragma redefine_extname X509_CRL_get0_by_cert BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_CRL_get0_by_cert)
 #pragma redefine_extname X509_CRL_get0_by_serial BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_CRL_get0_by_serial)
@@ -2746,7 +2752,6 @@
 #pragma redefine_extname X509_STORE_CTX_free BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_STORE_CTX_free)
 #pragma redefine_extname X509_STORE_CTX_get0_cert BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_STORE_CTX_get0_cert)
 #pragma redefine_extname X509_STORE_CTX_get0_chain BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_STORE_CTX_get0_chain)
-#pragma redefine_extname X509_STORE_CTX_get0_current_crl BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_STORE_CTX_get0_current_crl)
 #pragma redefine_extname X509_STORE_CTX_get0_param BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_STORE_CTX_get0_param)
 #pragma redefine_extname X509_STORE_CTX_get0_parent_ctx BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_STORE_CTX_get0_parent_ctx)
 #pragma redefine_extname X509_STORE_CTX_get0_store BORINGSSL_ADD_USER_LABEL_AND_PREFIX(X509_STORE_CTX_get0_store)
@@ -3294,6 +3299,7 @@
 #define ASN1_STRING_free BORINGSSL_ADD_PREFIX(ASN1_STRING_free)
 #define ASN1_STRING_get0_data BORINGSSL_ADD_PREFIX(ASN1_STRING_get0_data)
 #define ASN1_STRING_get_default_mask BORINGSSL_ADD_PREFIX(ASN1_STRING_get_default_mask)
+#define ASN1_STRING_get_length BORINGSSL_ADD_PREFIX(ASN1_STRING_get_length)
 #define ASN1_STRING_length BORINGSSL_ADD_PREFIX(ASN1_STRING_length)
 #define ASN1_STRING_new BORINGSSL_ADD_PREFIX(ASN1_STRING_new)
 #define ASN1_STRING_print BORINGSSL_ADD_PREFIX(ASN1_STRING_print)
@@ -3396,6 +3402,7 @@
 #define BIO_ctrl_get_write_guarantee BORINGSSL_ADD_PREFIX(BIO_ctrl_get_write_guarantee)
 #define BIO_ctrl_pending BORINGSSL_ADD_PREFIX(BIO_ctrl_pending)
 #define BIO_do_connect BORINGSSL_ADD_PREFIX(BIO_do_connect)
+#define BIO_dup_ref BORINGSSL_ADD_PREFIX(BIO_dup_ref)
 #define BIO_eof BORINGSSL_ADD_PREFIX(BIO_eof)
 #define BIO_f_base64 BORINGSSL_ADD_PREFIX(BIO_f_base64)
 #define BIO_f_ssl BORINGSSL_ADD_PREFIX(BIO_f_ssl)
@@ -3861,6 +3868,7 @@
 #define DH_compute_key BORINGSSL_ADD_PREFIX(DH_compute_key)
 #define DH_compute_key_hashed BORINGSSL_ADD_PREFIX(DH_compute_key_hashed)
 #define DH_compute_key_padded BORINGSSL_ADD_PREFIX(DH_compute_key_padded)
+#define DH_dup_ref BORINGSSL_ADD_PREFIX(DH_dup_ref)
 #define DH_free BORINGSSL_ADD_PREFIX(DH_free)
 #define DH_generate_key BORINGSSL_ADD_PREFIX(DH_generate_key)
 #define DH_generate_parameters BORINGSSL_ADD_PREFIX(DH_generate_parameters)
@@ -4542,6 +4550,9 @@
 #define EVP_sha224 BORINGSSL_ADD_PREFIX(EVP_sha224)
 #define EVP_sha256 BORINGSSL_ADD_PREFIX(EVP_sha256)
 #define EVP_sha384 BORINGSSL_ADD_PREFIX(EVP_sha384)
+#define EVP_sha3_256 BORINGSSL_ADD_PREFIX(EVP_sha3_256)
+#define EVP_sha3_384 BORINGSSL_ADD_PREFIX(EVP_sha3_384)
+#define EVP_sha3_512 BORINGSSL_ADD_PREFIX(EVP_sha3_512)
 #define EVP_sha512 BORINGSSL_ADD_PREFIX(EVP_sha512)
 #define EVP_sha512_256 BORINGSSL_ADD_PREFIX(EVP_sha512_256)
 #define EXTENDED_KEY_USAGE_free BORINGSSL_ADD_PREFIX(EXTENDED_KEY_USAGE_free)
@@ -5219,7 +5230,6 @@
 #define SSL_CTX_set_ex_data BORINGSSL_ADD_PREFIX(SSL_CTX_set_ex_data)
 #define SSL_CTX_set_false_start_allowed_without_alpn BORINGSSL_ADD_PREFIX(SSL_CTX_set_false_start_allowed_without_alpn)
 #define SSL_CTX_set_grease_enabled BORINGSSL_ADD_PREFIX(SSL_CTX_set_grease_enabled)
-#define SSL_CTX_set_grease_sigalgs_enabled BORINGSSL_ADD_PREFIX(SSL_CTX_set_grease_sigalgs_enabled)
 #define SSL_CTX_set_info_callback BORINGSSL_ADD_PREFIX(SSL_CTX_set_info_callback)
 #define SSL_CTX_set_keylog_callback BORINGSSL_ADD_PREFIX(SSL_CTX_set_keylog_callback)
 #define SSL_CTX_set_max_cert_list BORINGSSL_ADD_PREFIX(SSL_CTX_set_max_cert_list)
@@ -5744,6 +5754,7 @@
 #define X509_CRL_delete_ext BORINGSSL_ADD_PREFIX(X509_CRL_delete_ext)
 #define X509_CRL_digest BORINGSSL_ADD_PREFIX(X509_CRL_digest)
 #define X509_CRL_dup BORINGSSL_ADD_PREFIX(X509_CRL_dup)
+#define X509_CRL_dup_ref BORINGSSL_ADD_PREFIX(X509_CRL_dup_ref)
 #define X509_CRL_free BORINGSSL_ADD_PREFIX(X509_CRL_free)
 #define X509_CRL_get0_by_cert BORINGSSL_ADD_PREFIX(X509_CRL_get0_by_cert)
 #define X509_CRL_get0_by_serial BORINGSSL_ADD_PREFIX(X509_CRL_get0_by_serial)
@@ -5910,7 +5921,6 @@
 #define X509_STORE_CTX_free BORINGSSL_ADD_PREFIX(X509_STORE_CTX_free)
 #define X509_STORE_CTX_get0_cert BORINGSSL_ADD_PREFIX(X509_STORE_CTX_get0_cert)
 #define X509_STORE_CTX_get0_chain BORINGSSL_ADD_PREFIX(X509_STORE_CTX_get0_chain)
-#define X509_STORE_CTX_get0_current_crl BORINGSSL_ADD_PREFIX(X509_STORE_CTX_get0_current_crl)
 #define X509_STORE_CTX_get0_param BORINGSSL_ADD_PREFIX(X509_STORE_CTX_get0_param)
 #define X509_STORE_CTX_get0_parent_ctx BORINGSSL_ADD_PREFIX(X509_STORE_CTX_get0_parent_ctx)
 #define X509_STORE_CTX_get0_store BORINGSSL_ADD_PREFIX(X509_STORE_CTX_get0_store)
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/rsa.h b/pkgs/boring/third_party/boringssl/src/include/openssl/rsa.h
index 1abc611..f7c6cfa 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/rsa.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/rsa.h
@@ -837,7 +837,7 @@
 BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(RSA, RSA_free)
-BORINGSSL_MAKE_UP_REF(RSA, RSA_up_ref)
+BORINGSSL_MAKE_UP_REF(RSA, RSA_dup_ref)
 
 BSSL_NAMESPACE_END
 
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/trust_token.h b/pkgs/boring/third_party/boringssl/src/include/openssl/trust_token.h
index cf909ae..e0fbcea 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/trust_token.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/trust_token.h
@@ -15,7 +15,7 @@
 #ifndef OPENSSL_HEADER_TRUST_TOKEN_H
 #define OPENSSL_HEADER_TRUST_TOKEN_H
 
-#include <openssl/base.h>   // IWYU pragma: export
+#include <openssl/base.h>  // IWYU pragma: export
 #include <openssl/stack.h>
 
 #if defined(__cplusplus)
@@ -152,7 +152,7 @@
 // TRUST_TOKEN_CLIENT_set_srr_key sets the public key used to verify the SRR. It
 // returns one on success and zero on error.
 OPENSSL_EXPORT int TRUST_TOKEN_CLIENT_set_srr_key(TRUST_TOKEN_CLIENT *ctx,
-                                                  EVP_PKEY *key);
+                                                  const EVP_PKEY *key);
 
 // TRUST_TOKEN_CLIENT_begin_issuance produces a request for `count` trust tokens
 // and serializes the request into a newly-allocated buffer, setting `*out` to
@@ -180,11 +180,9 @@
 // arrives without the specified key present. The caller takes ownership of the
 // list and must call `sk_TRUST_TOKEN_pop_free` when done. The list is empty if
 // issuance fails.
-OPENSSL_EXPORT STACK_OF(TRUST_TOKEN) *
-    TRUST_TOKEN_CLIENT_finish_issuance(TRUST_TOKEN_CLIENT *ctx,
-                                       size_t *out_key_index,
-                                       const uint8_t *response,
-                                       size_t response_len);
+OPENSSL_EXPORT STACK_OF(TRUST_TOKEN) *TRUST_TOKEN_CLIENT_finish_issuance(
+    TRUST_TOKEN_CLIENT *ctx, size_t *out_key_index, const uint8_t *response,
+    size_t response_len);
 
 
 // TRUST_TOKEN_CLIENT_begin_redemption produces a request to redeem a token
@@ -243,7 +241,7 @@
 // TRUST_TOKEN_ISSUER_set_srr_key sets the private key used to sign the SRR. It
 // returns one on success and zero on error.
 OPENSSL_EXPORT int TRUST_TOKEN_ISSUER_set_srr_key(TRUST_TOKEN_ISSUER *ctx,
-                                                  EVP_PKEY *key);
+                                                  const EVP_PKEY *key);
 
 // TRUST_TOKEN_ISSUER_issue ingests `request` for token issuance
 // and generates up to `max_issuance` valid tokens, producing a list of blinded
diff --git a/pkgs/boring/third_party/boringssl/src/include/openssl/x509.h b/pkgs/boring/third_party/boringssl/src/include/openssl/x509.h
index 7e70ca2..6e2fa75 100644
--- a/pkgs/boring/third_party/boringssl/src/include/openssl/x509.h
+++ b/pkgs/boring/third_party/boringssl/src/include/openssl/x509.h
@@ -486,9 +486,8 @@
 OPENSSL_EXPORT int X509_set_subject_name(X509 *x509, const X509_NAME *name);
 
 // X509_set_pubkey sets `x509`'s public key to `pkey`. It returns one on success
-// and zero on error. This function does not take ownership of `pkey` and
-// internally copies and updates reference counts as needed.
-OPENSSL_EXPORT int X509_set_pubkey(X509 *x509, EVP_PKEY *pkey);
+// and zero on error.
+OPENSSL_EXPORT int X509_set_pubkey(X509 *x509, const EVP_PKEY *pkey);
 
 // X509_delete_ext removes the extension in `x` at index `loc` and returns the
 // removed extension, or NULL if `loc` was out of bounds. If non-NULL, the
@@ -682,6 +681,10 @@
 // X509_CRL_up_ref adds one to the reference count of `crl` and returns one.
 OPENSSL_EXPORT int X509_CRL_up_ref(X509_CRL *crl);
 
+// X509_CRL_dup_ref adds one to the reference count of `crl` and returns the
+// same.
+OPENSSL_EXPORT X509_CRL *X509_CRL_dup_ref(const X509_CRL *crl);
+
 // X509_CRL_dup returns a newly-allocated copy of `crl`, or NULL on error. This
 // function works by serializing the structure, so if `crl` is incomplete, it
 // may fail.
@@ -2819,7 +2822,7 @@
 // is a race condition whether `x509` is available for issuer lookups.
 // Moreover, the result may differ for each issuer lookup performed by a single
 // `X509_verify_cert` call.
-OPENSSL_EXPORT int X509_STORE_add_cert(X509_STORE *store, X509 *x509);
+OPENSSL_EXPORT int X509_STORE_add_cert(X509_STORE *store, const X509 *x509);
 
 // X509_STORE_add_crl adds `crl` to `store`. It returns one on success and zero
 // on error. This function internally increments `crl`'s reference count, so the
@@ -2836,7 +2839,7 @@
 // To vary the set of CRLs over time, callers should either create a new
 // `X509_STORE` or configure CRLs on a per-verification basis with
 // `X509_STORE_CTX_set0_crls`.
-OPENSSL_EXPORT int X509_STORE_add_crl(X509_STORE *store, X509_CRL *crl);
+OPENSSL_EXPORT int X509_STORE_add_crl(X509_STORE *store, const X509_CRL *crl);
 
 // X509_STORE_get0_param returns `store`'s verification parameters. This object
 // is mutable and may be modified by the caller. For an individual certificate
@@ -3068,11 +3071,6 @@
 // error returned by `X509_STORE_CTX_get_error`.
 OPENSSL_EXPORT X509 *X509_STORE_CTX_get_current_cert(const X509_STORE_CTX *ctx);
 
-// X509_STORE_CTX_get0_current_crl returns the CRL which caused the error
-// returned by `X509_STORE_CTX_get_error`.
-OPENSSL_EXPORT X509_CRL *X509_STORE_CTX_get0_current_crl(
-    const X509_STORE_CTX *ctx);
-
 // X509_STORE_CTX_get0_store returns the `X509_STORE` that `ctx` uses.
 OPENSSL_EXPORT X509_STORE *X509_STORE_CTX_get0_store(const X509_STORE_CTX *ctx);
 
@@ -3274,9 +3272,10 @@
 // X509_V_FLAG_USE_DELTAS causes all verifications to fail. Delta CRL support
 // has been removed.
 #define X509_V_FLAG_USE_DELTAS 0x2000
-// X509_V_FLAG_CHECK_SS_SIGNATURE checks the redundant signature on self-signed
-// trust anchors. This check provides no security benefit and only wastes CPU.
-#define X509_V_FLAG_CHECK_SS_SIGNATURE 0x4000
+// X509_V_FLAG_CHECK_SS_SIGNATURE does nothing. Historically it would check the
+// redundant signature on self-signed trust anchors. This check provided no
+// security benefit and only wasted CPU.
+#define X509_V_FLAG_CHECK_SS_SIGNATURE 0x0
 // X509_V_FLAG_TRUSTED_FIRST does nothing. The behavior it controls is always
 // enabled.
 #define X509_V_FLAG_TRUSTED_FIRST 0x0
@@ -3291,9 +3290,9 @@
 // X509_V_FLAG_ALLOW_TIMEZONE_OFFSET allows `notBefore` and `notAfter` fields
 // to contain a time zone offset.
 #define X509_V_FLAG_ALLOW_TIMEZONE_OFFSET 0x1000000
-// X509_V_FLAG_USE_MTC_DRAFT_PLANTS_05 enables the verification of Merkle Tree
-// Certificates as specified in draft-ietf-plants-merkle-tree-certs-05.
-#define X509_V_FLAG_USE_MTC_DRAFT_PLANTS_05 0x400000
+// X509_V_FLAG_USE_MTC_DRAFT_PLANTS_07 enables the verification of Merkle Tree
+// Certificates as specified in draft-ietf-plants-merkle-tree-certs-07.
+#define X509_V_FLAG_USE_MTC_DRAFT_PLANTS_07 0x400000
 
 // X509_VERIFY_PARAM_set_flags enables all values in `flags` in `param`'s
 // verification flags and returns one. `flags` should be a combination of
@@ -4518,7 +4517,7 @@
 OPENSSL_EXPORT int ASN1_item_sign(const ASN1_ITEM *it, X509_ALGOR *algor1,
                                   X509_ALGOR *algor2,
                                   ASN1_BIT_STRING *signature, void *data,
-                                  EVP_PKEY *pkey, const EVP_MD *type);
+                                  const EVP_PKEY *pkey, const EVP_MD *type);
 
 // ASN1_item_sign_ctx behaves like `ASN1_item_sign` except the signature is
 // signed with `ctx`, `ctx`, which must have been initialized with
@@ -5332,11 +5331,11 @@
 BORINGSSL_MAKE_DELETER(RSA_PSS_PARAMS, RSA_PSS_PARAMS_free)
 BORINGSSL_MAKE_DELETER(USERNOTICE, USERNOTICE_free)
 BORINGSSL_MAKE_DELETER(X509, X509_free)
-BORINGSSL_MAKE_UP_REF(X509, X509_up_ref)
+BORINGSSL_MAKE_UP_REF(X509, X509_dup_ref)
 BORINGSSL_MAKE_DELETER(X509_ALGOR, X509_ALGOR_free)
 BORINGSSL_MAKE_DELETER(X509_ATTRIBUTE, X509_ATTRIBUTE_free)
 BORINGSSL_MAKE_DELETER(X509_CRL, X509_CRL_free)
-BORINGSSL_MAKE_UP_REF(X509_CRL, X509_CRL_up_ref)
+BORINGSSL_MAKE_UP_REF(X509_CRL, X509_CRL_dup_ref)
 BORINGSSL_MAKE_DELETER(X509_EXTENSION, X509_EXTENSION_free)
 BORINGSSL_MAKE_DELETER(X509_INFO, X509_INFO_free)
 BORINGSSL_MAKE_DELETER(X509_LOOKUP, X509_LOOKUP_free)
@@ -5348,7 +5347,7 @@
 BORINGSSL_MAKE_DELETER(X509_REVOKED, X509_REVOKED_free)
 BORINGSSL_MAKE_DELETER(X509_SIG, X509_SIG_free)
 BORINGSSL_MAKE_DELETER(X509_STORE, X509_STORE_free)
-BORINGSSL_MAKE_UP_REF(X509_STORE, X509_STORE_up_ref)
+BORINGSSL_MAKE_UP_REF(X509_STORE, X509_STORE_dup_ref)
 BORINGSSL_MAKE_DELETER(X509_STORE_CTX, X509_STORE_CTX_free)
 BORINGSSL_MAKE_DELETER(X509_VERIFY_PARAM, X509_VERIFY_PARAM_free)
 
diff --git a/pkgs/boring/tool/REVISION b/pkgs/boring/tool/REVISION
index a2074dd..0678169 100644
--- a/pkgs/boring/tool/REVISION
+++ b/pkgs/boring/tool/REVISION
@@ -1 +1 @@
-3fed4f207543470b3d639db6ed40737e30308b51
+64188f173d4bd19873db8a9d247470e9e1580ec2
diff --git a/pkgs/markdown/CHANGELOG.md b/pkgs/markdown/CHANGELOG.md
index 3724066..166fce7 100644
--- a/pkgs/markdown/CHANGELOG.md
+++ b/pkgs/markdown/CHANGELOG.md
@@ -1,5 +1,9 @@
 ## 7.4.1-wip
 
+* Skip the regular expression in `EmojiSyntax`, `AutolinkSyntax`,
+  `CodeSyntax`, and `ColorSwatchSyntax` when the character at the current
+  position cannot start a match. This makes inline parsing faster, mostly on
+  text with many spaces, punctuation marks, or CJK characters.
 * Fix quadratic parsing time on blocks of consecutive link reference
   definitions by only consuming lines from `BlockParser` as needed for a
   single definition rather than joining and re-splitting the remainder of the
diff --git a/pkgs/markdown/lib/src/inline_syntaxes/autolink_syntax.dart b/pkgs/markdown/lib/src/inline_syntaxes/autolink_syntax.dart
index 01efd0c..3663c4d 100644
--- a/pkgs/markdown/lib/src/inline_syntaxes/autolink_syntax.dart
+++ b/pkgs/markdown/lib/src/inline_syntaxes/autolink_syntax.dart
@@ -3,13 +3,18 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import '../ast.dart';
+import '../charcode.dart';
 import '../inline_parser.dart';
 import '../util.dart';
 import 'inline_syntax.dart';
 
 /// Matches autolinks like `<http://foo.com>`.
 class AutolinkSyntax extends InlineSyntax {
-  AutolinkSyntax() : super(r'<(([a-zA-Z][a-zA-Z\-\+\.]+):(?://)?[^\s>]*)>');
+  AutolinkSyntax()
+    : super(
+        r'<(([a-zA-Z][a-zA-Z\-\+\.]+):(?://)?[^\s>]*)>',
+        startCharacter: $lt,
+      );
 
   @override
   bool onMatch(InlineParser parser, Match match) {
diff --git a/pkgs/markdown/lib/src/inline_syntaxes/code_syntax.dart b/pkgs/markdown/lib/src/inline_syntaxes/code_syntax.dart
index 35783d3..e69ede4 100644
--- a/pkgs/markdown/lib/src/inline_syntaxes/code_syntax.dart
+++ b/pkgs/markdown/lib/src/inline_syntaxes/code_syntax.dart
@@ -26,6 +26,7 @@
 
   @override
   bool tryMatch(InlineParser parser, [int? startMatchPos]) {
+    if (parser.charAt(parser.pos) != $backquote) return false;
     if (parser.pos > 0 && parser.charAt(parser.pos - 1) == $backquote) {
       // Not really a match! We can't just sneak past one backtick to try the
       // next character. An example of this situation would be:
diff --git a/pkgs/markdown/lib/src/inline_syntaxes/color_swatch_syntax.dart b/pkgs/markdown/lib/src/inline_syntaxes/color_swatch_syntax.dart
index 5955cd7..2c84c3d 100644
--- a/pkgs/markdown/lib/src/inline_syntaxes/color_swatch_syntax.dart
+++ b/pkgs/markdown/lib/src/inline_syntaxes/color_swatch_syntax.dart
@@ -44,6 +44,7 @@
 
   @override
   bool tryMatch(InlineParser parser, [int? startMatchPos]) {
+    if (parser.charAt(parser.pos) != $backquote) return false;
     if (parser.pos > 0 && parser.charAt(parser.pos - 1) == $backquote) {
       // Not really a match! We can't just sneak past one backtick to try the
       // next character. An example of this situation would be:
diff --git a/pkgs/markdown/lib/src/inline_syntaxes/emoji_syntax.dart b/pkgs/markdown/lib/src/inline_syntaxes/emoji_syntax.dart
index a068c6b..2501efc 100644
--- a/pkgs/markdown/lib/src/inline_syntaxes/emoji_syntax.dart
+++ b/pkgs/markdown/lib/src/inline_syntaxes/emoji_syntax.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import '../ast.dart';
+import '../charcode.dart';
 import '../emojis.dart';
 import '../inline_parser.dart';
 import 'inline_syntax.dart';
@@ -15,7 +16,7 @@
 class EmojiSyntax extends InlineSyntax {
   // Emoji "aliases" are mostly limited to lower-case letters, numbers, and
   // underscores, but GitHub also supports `:+1:` and `:-1:`.
-  EmojiSyntax() : super(':([a-z0-9_+-]+):');
+  EmojiSyntax() : super(':([a-z0-9_+-]+):', startCharacter: $colon);
 
   @override
   bool onMatch(InlineParser parser, Match match) {
diff --git a/pkgs/markdown/test/regression_test.dart b/pkgs/markdown/test/regression_test.dart
index 6ae65b9..eb4eb2f 100644
--- a/pkgs/markdown/test/regression_test.dart
+++ b/pkgs/markdown/test/regression_test.dart
@@ -188,6 +188,50 @@
       }
     },
   );
+
+  group('inline syntaxes that check their first character', () {
+    // These syntaxes skip the regex when the first character cannot match.
+    // They must still match after CJK text and punctuation, and leave a lone
+    // first character as text.
+    test('EmojiSyntax', () {
+      expect(
+        markdownToHtml(
+          '中文:smile: a: b, (:+1:)',
+          inlineSyntaxes: [EmojiSyntax()],
+        ),
+        '<p>中文😄 a: b, (👍)</p>\n',
+      );
+    });
+
+    test('AutolinkSyntax', () {
+      expect(
+        markdownToHtml('中文<http://a.b>, (<mailto:x@y.z>) a < b'),
+        '<p>中文<a href="http://a.b">http://a.b</a>, '
+        '(<a href="mailto:x@y.z">mailto:x@y.z</a>) a &lt; b</p>\n',
+      );
+    });
+
+    test('CodeSyntax', () {
+      expect(
+        markdownToHtml('中文`code`, (``a`b``) a ` b'),
+        '<p>中文<code>code</code>, (<code>a`b</code>) a ` b</p>\n',
+      );
+    });
+
+    test('ColorSwatchSyntax', () {
+      expect(
+        markdownToHtml(
+          '中文`#f00`, (`rgb(0,255,0)`) `#ba`',
+          inlineSyntaxes: [ColorSwatchSyntax()],
+        ),
+        '<p>中文<code class="gfm-color_chip">#f00<span '
+        'style="background-color:#f00;"></span></code>, '
+        '(<code class="gfm-color_chip">rgb(0,255,0)<span '
+        'style="background-color:rgb(0,255,0);"></span></code>) '
+        '<code>#ba</code></p>\n',
+      );
+    });
+  });
 }
 
 class _CountingLine extends Line {
diff --git a/pkgs/pubspec_parse/CHANGELOG.md b/pkgs/pubspec_parse/CHANGELOG.md
index 41f52a0..6971f50 100644
--- a/pkgs/pubspec_parse/CHANGELOG.md
+++ b/pkgs/pubspec_parse/CHANGELOG.md
@@ -1,8 +1,16 @@
-## 1.7.0-wip
+## 1.7.0
 
 - Added `tagPattern` and `version` fields to `GitDependency`, to support the
   `tag_pattern` key for git dependencies introduced in Dart 3.9.
 - Added a usage example.
+- Fix `Pubspec.fromJson` with `lenient: true` to preserve the input `Map`
+  (`YamlMap`) when `name` is invalid or missing.
+- Preserve nested `Map` (`YamlMap`) keys and spans when `git` or `hosted`
+  dependency shorthands fail to parse or when `Pubspec.fromJson` is called with
+  a non-`YamlMap` `Map`.
+- Added fields to `Pubspec`: `falseSecrets`, `hooks`, `platforms`.
+- Added `version` to `PathDependency`, exposing the constraint from an
+  accompanying `version` key instead of silently discarding it.
 
 ## 1.6.0
 
diff --git a/pkgs/pubspec_parse/lib/src/dependency.dart b/pkgs/pubspec_parse/lib/src/dependency.dart
index 6fd01ac..2585368 100644
--- a/pkgs/pubspec_parse/lib/src/dependency.dart
+++ b/pkgs/pubspec_parse/lib/src/dependency.dart
@@ -16,7 +16,7 @@
       try {
         value = _fromJson(v, k);
       } on CheckedFromJsonException catch (e) {
-        if (e.map is! YamlMap) {
+        if (v is! Map || (source is YamlMap && e.map is! YamlMap)) {
           // This is likely a "synthetic" map created from a String value
           // Use `source` to throw this exception with an actual YamlMap and
           // extract the associated error information.
@@ -83,9 +83,12 @@
         return switch (key) {
           'git' => GitDependency.fromData(
             data[key],
-            version: _optionalConstraintFromData(data),
+            version: _optionalConstraintFromData(data, 'GitDependency'),
           ),
-          'path' => PathDependency.fromData(data[key]),
+          'path' => PathDependency.fromData(
+            data[key],
+            version: _optionalConstraintFromData(data, 'PathDependency'),
+          ),
           'sdk' => _$SdkDependencyFromJson(data),
           'hosted' => _$HostedDependencyFromJson(
             data,
@@ -153,7 +156,11 @@
 
   factory GitDependency.fromData(Object? data, {VersionConstraint? version}) {
     if (data is String) {
-      data = {'url': data};
+      try {
+        return GitDependency(parseGitUri(data), version: version);
+      } on FormatException catch (e) {
+        throw ArgumentError.value(data, 'git', e.message);
+      }
     }
 
     if (data is Map) {
@@ -261,27 +268,37 @@
 class PathDependency extends Dependency {
   final String path;
 
-  PathDependency(this.path);
+  /// The version constraint specified next to the `path` source.
+  ///
+  /// `null` if no constraint is specified, which pub treats as any version.
+  /// This isn't used to select the dependency, but pub's solver still
+  /// validates it against the resolved package's version.
+  final VersionConstraint? version;
 
-  factory PathDependency.fromData(Object? data) {
+  PathDependency(this.path, {this.version});
+
+  factory PathDependency.fromData(Object? data, {VersionConstraint? version}) {
     if (data is String) {
-      return PathDependency(data);
+      return PathDependency(data, version: version);
     }
     throw ArgumentError.value(data, 'path', 'Must be a String.');
   }
 
   @override
   bool operator ==(Object other) =>
-      other is PathDependency && other.path == path;
+      other is PathDependency && other.path == path && other.version == version;
 
   @override
-  int get hashCode => path.hashCode;
+  int get hashCode => Object.hash(path, version);
 
   @override
   String toString() => 'PathDependency: path@$path';
 
   @override
-  Map<String, dynamic> toJson() => {'path': path};
+  Map<String, dynamic> toJson() => {
+    'path': path,
+    'version': ?version?.toString(),
+  };
 }
 
 @JsonSerializable(disallowUnrecognizedKeys: true)
@@ -339,7 +356,7 @@
 
   factory HostedDetails.fromJson(Object data) {
     if (data is String) {
-      data = {'url': data};
+      return HostedDetails(null, _parseUriOrNull(data));
     }
 
     if (data is Map) {
@@ -366,7 +383,7 @@
 VersionConstraint _constraintFromString(String? input) =>
     input == null ? VersionConstraint.any : VersionConstraint.parse(input);
 
-VersionConstraint? _optionalConstraintFromData(Map data) {
+VersionConstraint? _optionalConstraintFromData(Map data, String className) {
   final value = data['version'];
   if (value == null) {
     return null;
@@ -375,13 +392,13 @@
     throw CheckedFromJsonException(
       data,
       'version',
-      'GitDependency',
+      className,
       '`$value` is not a String.',
     );
   }
   try {
     return VersionConstraint.parse(value);
   } on FormatException catch (e) {
-    throw CheckedFromJsonException(data, 'version', 'GitDependency', e.message);
+    throw CheckedFromJsonException(data, 'version', className, e.message);
   }
 }
diff --git a/pkgs/pubspec_parse/lib/src/pubspec.dart b/pkgs/pubspec_parse/lib/src/pubspec.dart
index dd51fe5..a2a8ac4 100644
--- a/pkgs/pubspec_parse/lib/src/pubspec.dart
+++ b/pkgs/pubspec_parse/lib/src/pubspec.dart
@@ -99,6 +99,27 @@
   /// Specifies how to resolve dependencies with the surrounding Pub Workspace.
   final String? resolution;
 
+  /// The platforms this package explicitly declares support for.
+  ///
+  /// Keys are platform names, such as `android`, `ios`, `linux`, `macos`,
+  /// `web` and `windows`. Values are currently always `null`.
+  ///
+  /// See https://dart.dev/tools/pub/pubspec#platforms.
+  final Map<String, Object?>? platforms;
+
+  /// Gitignore style patterns for files that pub should not search for
+  /// leaked secrets when publishing.
+  ///
+  /// See https://dart.dev/tools/pub/pubspec#false_secrets.
+  final List<String>? falseSecrets;
+
+  /// Configuration for hooks, such as native build or link hooks.
+  ///
+  /// May include `user_defines` and other settings.
+  ///
+  /// See https://dart.dev/tools/pub/pubspec#hooks.
+  final Map<String, Object?>? hooks;
+
   /// If [author] and [authors] are both provided, their values are combined
   /// with duplicates eliminated.
   Pubspec(
@@ -121,6 +142,9 @@
     this.description,
     this.workspace,
     this.resolution,
+    this.platforms,
+    this.falseSecrets,
+    this.hooks,
     Map<String, Dependency>? dependencies,
     Map<String, Dependency>? devDependencies,
     Map<String, Dependency>? dependencyOverrides,
@@ -154,14 +178,26 @@
 
   factory Pubspec.fromJson(Map json, {bool lenient = false}) {
     if (lenient) {
-      while (json.isNotEmpty) {
+      var currentJson = json;
+      while (currentJson.isNotEmpty) {
         // Attempting to remove top-level properties that cause parsing errors.
         try {
-          return _$PubspecFromJson(json);
+          return _$PubspecFromJson(currentJson);
         } on CheckedFromJsonException catch (e) {
-          if (e.map == json && json.containsKey(e.key)) {
-            json = Map.from(json)..remove(e.key);
-            continue;
+          if (e.map == currentJson) {
+            if (e.key != 'name' && currentJson.containsKey(e.key)) {
+              currentJson = Map.from(currentJson)..remove(e.key);
+              continue;
+            }
+            if (currentJson != json) {
+              throw CheckedFromJsonException(
+                json,
+                e.key,
+                e.className!,
+                e.message,
+                badKey: e.badKey,
+              );
+            }
           }
           rethrow;
         }
diff --git a/pkgs/pubspec_parse/lib/src/pubspec.g.dart b/pkgs/pubspec_parse/lib/src/pubspec.g.dart
index cfbab51..0c0d738 100644
--- a/pkgs/pubspec_parse/lib/src/pubspec.g.dart
+++ b/pkgs/pubspec_parse/lib/src/pubspec.g.dart
@@ -61,6 +61,18 @@
         (v) => (v as List<dynamic>?)?.map((e) => e as String).toList(),
       ),
       resolution: $checkedConvert('resolution', (v) => v as String?),
+      platforms: $checkedConvert(
+        'platforms',
+        (v) => (v as Map?)?.map((k, e) => MapEntry(k as String, e)),
+      ),
+      falseSecrets: $checkedConvert(
+        'false_secrets',
+        (v) => (v as List<dynamic>?)?.map((e) => e as String).toList(),
+      ),
+      hooks: $checkedConvert(
+        'hooks',
+        (v) => (v as Map?)?.map((k, e) => MapEntry(k as String, e)),
+      ),
       dependencies: $checkedConvert(
         'dependencies',
         (v) => parseDeps(v as Map?),
@@ -88,6 +100,7 @@
     'publishTo': 'publish_to',
     'issueTracker': 'issue_tracker',
     'ignoredAdvisories': 'ignored_advisories',
+    'falseSecrets': 'false_secrets',
     'devDependencies': 'dev_dependencies',
     'dependencyOverrides': 'dependency_overrides',
   },
@@ -116,4 +129,7 @@
   'executables': _serializeExecutables(instance.executables),
   'workspace': instance.workspace,
   'resolution': instance.resolution,
+  'platforms': instance.platforms,
+  'false_secrets': instance.falseSecrets,
+  'hooks': instance.hooks,
 };
diff --git a/pkgs/pubspec_parse/pubspec.yaml b/pkgs/pubspec_parse/pubspec.yaml
index 67b56a6..bacf663 100644
--- a/pkgs/pubspec_parse/pubspec.yaml
+++ b/pkgs/pubspec_parse/pubspec.yaml
@@ -1,5 +1,5 @@
 name: pubspec_parse
-version: 1.7.0-wip
+version: 1.7.0
 description: >-
   Simple package for parsing pubspec.yaml files with a type-safe API and rich
   error reporting.
diff --git a/pkgs/pubspec_parse/test/dependency_test.dart b/pkgs/pubspec_parse/test/dependency_test.dart
index 104f1ad..784ff89 100644
--- a/pkgs/pubspec_parse/test/dependency_test.dart
+++ b/pkgs/pubspec_parse/test/dependency_test.dart
@@ -4,6 +4,7 @@
 
 import 'dart:io';
 
+import 'package:json_annotation/json_annotation.dart';
 import 'package:pub_semver/pub_semver.dart';
 import 'package:pubspec_parse/pubspec_parse.dart';
 import 'package:test/test.dart';
@@ -65,6 +66,23 @@
   ╵''',
       );
     });
+
+    test('plain Map in Pubspec.fromJson preserves nested key and map', () {
+      final gitMap = {'url': 42};
+      expect(
+        () => Pubspec.fromJson({
+          'name': 'sample',
+          'dependencies': {
+            'dep': {'git': gitMap},
+          },
+        }),
+        throwsA(
+          isA<CheckedFromJsonException>()
+              .having((e) => e.key, 'key', 'url')
+              .having((e) => e.map, 'map', same(gitMap)),
+        ),
+      );
+    });
   });
 }
 
@@ -204,6 +222,18 @@
     );
   });
 
+  test('map w/ invalid hosted String should error on hosted key', () {
+    _expectThrows(
+      {'hosted': '://bad_uri'},
+      r'''
+line 5, column 14: Unsupported value for "hosted". Invalid empty scheme at offset 0.
+  ╷
+5 │    "hosted": "://bad_uri"
+  │              ^^^^^^^^^^^^
+  ╵''',
+    );
+  });
+
   test('map w/ null version is fine', () async {
     final dep = await _dependency<HostedDependency>({'version': null});
     expect(dep.version, VersionConstraint.any);
@@ -465,6 +495,18 @@
       'git': {'url': 42},
     }, r"type 'int' is not a subtype of type 'String'");
   });
+
+  test('git - invalid string url', () {
+    _expectThrows(
+      {'git': '://bad_uri'},
+      r'''
+line 5, column 11: Unsupported value for "git". Invalid empty scheme
+  ╷
+5 │    "git": "://bad_uri"
+  │           ^^^^^^^^^^^^
+  ╵''',
+    );
+  });
 }
 
 void _pathDependency() {
@@ -474,15 +516,47 @@
     expect(dep.toString(), 'PathDependency: path@../path');
   });
 
-  test('valid with version key is ignored', () async {
+  test('valid with version key exposes the constraint', () async {
     final dep = await _dependency<PathDependency>({
       'path': '../path',
       'version': '^1.2.3',
     });
     expect(dep.path, '../path');
+    expect(dep.version, VersionConstraint.parse('^1.2.3'));
     expect(dep.toString(), 'PathDependency: path@../path');
   });
 
+  test('without a version key defaults to null', () async {
+    final dep = await _dependency<PathDependency>({'path': '../path'});
+    expect(dep.version, isNull);
+  });
+
+  test('invalid version key fails', () {
+    _expectThrows(
+      {'path': '../path', 'version': 'not a version'},
+      r'''
+line 6, column 15: Unsupported value for "version". Could not parse version "not a version". Unknown text at "not a version".
+  ╷
+6 │    "version": "not a version"
+  │               ^^^^^^^^^^^^^^^
+  ╵''',
+    );
+  });
+
+  test('non-String version key fails', () {
+    _expectThrows(
+      {'path': '../path', 'version': 42},
+      r'''
+line 6, column 15: Unsupported value for "version". `42` is not a String.
+  ╷
+6 │      "version": 42
+  │ ┌───────────────^
+7 │ │   }
+  │ └──^
+  ╵''',
+    );
+  });
+
   test('valid with random extra key fails', () {
     _expectThrows(
       {'path': '../path', 'bob': '^1.2.3'},
diff --git a/pkgs/pubspec_parse/test/parse_test.dart b/pkgs/pubspec_parse/test/parse_test.dart
index f700b4e..64573b8 100644
--- a/pkgs/pubspec_parse/test/parse_test.dart
+++ b/pkgs/pubspec_parse/test/parse_test.dart
@@ -34,6 +34,9 @@
     expect(value.workspace, isNull);
     expect(value.resolution, isNull);
     expect(value.executables, isEmpty);
+    expect(value.platforms, isNull);
+    expect(value.falseSecrets, isNull);
+    expect(value.hooks, isNull);
   });
 
   test('all fields set', () async {
@@ -58,6 +61,13 @@
       ],
       'workspace': ['pkg1', 'pkg2'],
       'resolution': 'workspace',
+      'platforms': {'android': null, 'web': null},
+      'false_secrets': ['/lib/src/hardcoded_api_key.dart', '/test/*.pem'],
+      'hooks': {
+        'user_defines': {
+          'my_package': {'enable_experimental': true},
+        },
+      },
       'executables': {
         'my_script': 'bin/my_script.dart',
         'my_script2': 'bin/my_script2.dart',
@@ -101,6 +111,16 @@
     expect(value.workspace!.first, 'pkg1');
     expect(value.workspace!.last, 'pkg2');
     expect(value.resolution, 'workspace');
+    expect(value.platforms, {'android': null, 'web': null});
+    expect(value.falseSecrets, [
+      '/lib/src/hardcoded_api_key.dart',
+      '/test/*.pem',
+    ]);
+    expect(value.hooks, {
+      'user_defines': {
+        'my_package': {'enable_experimental': true},
+      },
+    });
   });
 
   test('environment values can be null', () async {
@@ -398,6 +418,118 @@
     });
   });
 
+  group('platforms', () {
+    test('map with null values', () async {
+      final value = await parse({
+        ...defaultPubspec,
+        'platforms': {'android': null, 'ios': null, 'web': null},
+      });
+      expect(value.platforms, {'android': null, 'ios': null, 'web': null});
+    });
+
+    test('not a map', () {
+      expectParseThrowsContaining(
+        {...defaultPubspec, 'platforms': 1},
+        "Unsupported value for \"platforms\". type 'int' is not a subtype of type 'Map<dynamic, dynamic>?'",
+        skipTryPub: true,
+      );
+    });
+
+    test('invalid data - lenient', () async {
+      final value = await parse(
+        {...defaultPubspec, 'platforms': 1},
+        skipTryPub: true,
+        lenient: true,
+      );
+      expect(value.name, 'sample');
+      expect(value.platforms, isNull);
+    });
+  });
+
+  group('false_secrets', () {
+    test('list of patterns', () async {
+      final value = await parse({
+        ...defaultPubspec,
+        'false_secrets': [
+          '/lib/src/hardcoded_api_key.dart',
+          '/test/localhost_certificates/*.pem',
+        ],
+      });
+      expect(value.falseSecrets, [
+        '/lib/src/hardcoded_api_key.dart',
+        '/test/localhost_certificates/*.pem',
+      ]);
+    });
+
+    test('not a list', () {
+      expectParseThrowsContaining(
+        {...defaultPubspec, 'false_secrets': 1},
+        "Unsupported value for \"false_secrets\". type 'int' is not a subtype of type 'List<dynamic>?'",
+        skipTryPub: true,
+      );
+    });
+
+    test('not a string', () {
+      expectParseThrowsContaining(
+        {
+          ...defaultPubspec,
+          'false_secrets': [1],
+        },
+        "Unsupported value for \"false_secrets\". type 'int' is not a subtype of type 'String'",
+        skipTryPub: true,
+      );
+    });
+
+    test('invalid data - lenient', () async {
+      final value = await parse(
+        {
+          ...defaultPubspec,
+          'false_secrets': [1],
+        },
+        skipTryPub: true,
+        lenient: true,
+      );
+      expect(value.name, 'sample');
+      expect(value.falseSecrets, isNull);
+    });
+  });
+
+  group('hooks', () {
+    test('map', () async {
+      final value = await parse({
+        ...defaultPubspec,
+        'hooks': {
+          'user_defines': {
+            'my_package': {'enable_experimental': true},
+          },
+        },
+      });
+      expect(value.hooks, {
+        'user_defines': {
+          'my_package': {'enable_experimental': true},
+        },
+      });
+    });
+
+    test('not a map', () {
+      expectParseThrowsContaining(
+        {...defaultPubspec, 'hooks': 1},
+        "Unsupported value for \"hooks\". type 'int' is not a subtype of type 'Map<dynamic, dynamic>?'",
+        skipTryPub: true,
+      );
+    });
+
+    test('invalid data - lenient', () async {
+      final value = await parse(
+        {...defaultPubspec, 'hooks': 1},
+        skipTryPub: true,
+        lenient: true,
+      );
+      expect(value.name, 'sample');
+      expect(value.hooks, isNull);
+    });
+  });
+
   group('funding', () {
     test('not a list', () {
       expectParseThrowsContaining(
@@ -676,6 +808,28 @@
       );
     });
 
+    test('name cannot be null', () {
+      expectParseThrowsContaining(
+        {...defaultPubspec, 'name': null},
+        "Unsupported value for \"name\". type 'Null' is not a subtype of type 'String'",
+        lenient: true,
+      );
+    });
+
+    test('name cannot be empty string with invalid other property', () {
+      expectParseThrows(
+        {'name': '', 'version': 'invalid'},
+        r'''
+line 2, column 10: Unsupported value for "name". "name" cannot be empty.
+  ╷
+2 │  "name": "",
+  │          ^^
+  ╵''',
+        lenient: true,
+        skipTryPub: true,
+      );
+    });
+
     test('bad repository url', () async {
       final value = await parse({
         ...defaultPubspec,
diff --git a/pkgs/pubspec_parse/test/tojson_test.dart b/pkgs/pubspec_parse/test/tojson_test.dart
index 60d320f..c19ffef 100644
--- a/pkgs/pubspec_parse/test/tojson_test.dart
+++ b/pkgs/pubspec_parse/test/tojson_test.dart
@@ -39,6 +39,9 @@
         'funding': null,
         'topics': null,
         'ignored_advisories': null,
+        'platforms': null,
+        'false_secrets': null,
+        'hooks': null,
       });
     });
 
@@ -65,6 +68,13 @@
         ],
         'workspace': ['pkg1', 'pkg2'],
         'resolution': 'workspace',
+        'platforms': {'android': null, 'web': null},
+        'false_secrets': ['/lib/src/hardcoded_api_key.dart', '/test/*.pem'],
+        'hooks': {
+          'user_defines': {
+            'my_package': {'enable_experimental': true},
+          },
+        },
         'executables': {
           'my_script': 'bin/my_script.dart',
           'my_script2': 'bin/my_script2.dart',
@@ -106,6 +116,13 @@
         'ignored_advisories': ['111', '222'],
         'workspace': ['pkg1', 'pkg2'],
         'resolution': 'workspace',
+        'platforms': {'android': null, 'web': null},
+        'false_secrets': ['/lib/src/hardcoded_api_key.dart', '/test/*.pem'],
+        'hooks': {
+          'user_defines': {
+            'my_package': {'enable_experimental': true},
+          },
+        },
         'executables': {
           'my_script': 'bin/my_script.dart',
           'my_script2': 'bin/my_script2.dart',
@@ -139,6 +156,9 @@
       expect(newValue.workspace, value.workspace);
       expect(newValue.resolution, value.resolution);
       expect(newValue.executables, value.executables);
+      expect(newValue.platforms, value.platforms);
+      expect(newValue.falseSecrets, value.falseSecrets);
+      expect(newValue.hooks, value.hooks);
     });
 
     test('all fields set', () async {
@@ -163,6 +183,13 @@
         ],
         'workspace': ['pkg1', 'pkg2'],
         'resolution': 'workspace',
+        'platforms': {'android': null, 'web': null},
+        'false_secrets': ['/lib/src/hardcoded_api_key.dart', '/test/*.pem'],
+        'hooks': {
+          'user_defines': {
+            'my_package': {'enable_experimental': true},
+          },
+        },
         'executables': {
           'my_script': 'bin/my_script.dart',
           'my_script2': 'bin/my_script2.dart',
@@ -200,6 +227,9 @@
       expect(newValue.workspace, value.workspace);
       expect(newValue.resolution, value.resolution);
       expect(newValue.executables, value.executables);
+      expect(newValue.platforms, value.platforms);
+      expect(newValue.falseSecrets, value.falseSecrets);
+      expect(newValue.hooks, value.hooks);
     });
 
     test('dependencies', () async {
@@ -230,6 +260,10 @@
             'version': '^2.0.1',
           },
           'local_utils': {'path': '../local_utils'},
+          'versioned_local': {
+            'path': '../versioned_local',
+            'version': '^1.2.3',
+          },
         },
       }, skipTryPub: true);
 
@@ -237,7 +271,7 @@
 
       final newValue = await parse(jsonValue, skipTryPub: true);
 
-      expect(value.dependencies, hasLength(9));
+      expect(value.dependencies, hasLength(10));
       expect(value.dependencies, {
         'flutter': isA<SdkDependency>(),
         'http': isA<HostedDependency>(),
@@ -248,6 +282,7 @@
         'shared_preferences': isA<GitDependency>(),
         'kittens': isA<GitDependency>(),
         'local_utils': isA<PathDependency>(),
+        'versioned_local': isA<PathDependency>(),
       });
 
       expect(
@@ -293,6 +328,14 @@
         value.dependencies['local_utils']?.toJson(),
         newValue.dependencies['local_utils']?.toJson(),
       );
+      expect(value.dependencies['versioned_local']?.toJson(), {
+        'path': '../versioned_local',
+        'version': '^1.2.3',
+      });
+      expect(
+        value.dependencies['versioned_local']?.toJson(),
+        newValue.dependencies['versioned_local']?.toJson(),
+      );
     });
   });
 }
diff --git a/pkgs/unified_analytics/CHANGELOG.md b/pkgs/unified_analytics/CHANGELOG.md
index 23bf450..5efe77e 100644
--- a/pkgs/unified_analytics/CHANGELOG.md
+++ b/pkgs/unified_analytics/CHANGELOG.md
@@ -1,4 +1,14 @@
-## 8.1.0-wip
+## 8.2.0
+
+- Default `agent` in `Analytics` and `Analytics.development` to
+  `AiAgent.detectAgentName(Platform.environment)` when omitted.
+- Tightened `AiAgent.cursor` detection in `AiAgent.detectAgentName` to check
+  `CURSOR_AGENT` and `CURSOR_EXTENSION_HOST_ROLE=agent-exec` rather than
+  `TERM_PROGRAM=cursor` or any `CURSOR_*` environment variable.
+- Added `Analytics.isBot` to detect CI and bot environments from environment
+  variables.
+
+## 8.1.0
 
 - Added optional `isExternal` parameter to `Analytics.fake` to allow overriding
   the build configuration during testing.
diff --git a/pkgs/unified_analytics/lib/src/analytics.dart b/pkgs/unified_analytics/lib/src/analytics.dart
index 5a8722d..7f51869 100644
--- a/pkgs/unified_analytics/lib/src/analytics.dart
+++ b/pkgs/unified_analytics/lib/src/analytics.dart
@@ -44,7 +44,8 @@
   ///
   /// [agent] is an optional parameter to capture the name of any AI coding
   /// assistant/agent executing the tool. Examples include "Claude Code",
-  /// "Cursor", or "Copilot".
+  /// "Cursor", or "Copilot". If omitted, defaults to
+  /// `AiAgent.detectAgentName(io.Platform.environment)`.
   factory Analytics({
     required DashTool tool,
     required String dartVersion,
@@ -103,7 +104,7 @@
       enableAsserts: enableAsserts,
       clientIde: clientIde,
       enabledFeatures: enabledFeatures,
-      agent: agent,
+      agent: agent ?? AiAgent.detectAgentName(io.Platform.environment),
       firstRun: firstRun,
     );
   }
@@ -120,7 +121,8 @@
   ///
   /// [agent] is an optional parameter to capture the name of any AI coding
   /// assistant/agent executing the tool. Examples include "Claude Code",
-  /// "Cursor", or "Copilot".
+  /// "Cursor", or "Copilot". If omitted, defaults to
+  /// `AiAgent.detectAgentName(io.Platform.environment)`.
   factory Analytics.development({
     required DashTool tool,
     required String dartVersion,
@@ -188,11 +190,41 @@
       enableAsserts: enableAsserts,
       clientIde: clientIde,
       enabledFeatures: enabledFeatures,
-      agent: agent,
+      agent: agent ?? AiAgent.detectAgentName(io.Platform.environment),
       firstRun: firstRun,
     );
   }
 
+  /// Returns `true` if [environment] indicates that the tool is running in an
+  /// automated CI or bot environment.
+  static bool isBot(Map<String, String> environment) {
+    final bot = environment['BOT']?.trim().toLowerCase();
+    if (bot == 'false' ||
+        bot == '0' ||
+        environment.containsKey('FLUTTER_HOST') ||
+        environment.containsKey('FLUTTER_ANALYTICS_LOG_FILE')) {
+      return false;
+    }
+
+    final ci = environment['CI']?.trim().toLowerCase();
+    return (bot != null && bot.isNotEmpty) ||
+        (ci != null && ci.isNotEmpty && ci != 'false' && ci != '0') ||
+        environment['TRAVIS'] == 'true' ||
+        environment['CONTINUOUS_INTEGRATION'] == 'true' ||
+        environment.containsKey('APPVEYOR') ||
+        environment.containsKey('CIRRUS_CI') ||
+        (environment.containsKey('AWS_REGION') &&
+            environment.containsKey('CODEBUILD_INITIATOR')) ||
+        environment.containsKey('JENKINS_URL') ||
+        environment.containsKey('GITHUB_ACTIONS') ||
+        environment['CHROME_HEADLESS'] == '1' ||
+        environment.containsKey('BUILDBOT_BUILDERNAME') ||
+        environment.containsKey('SWARMING_TASK_ID') ||
+        environment.containsKey('BORG_ALLOC_DIR') ||
+        environment.containsKey('TF_BUILD') ||
+        environment.containsKey('AZURE_HTTP_USER_AGENT');
+  }
+
   /// The shared identifier for Flutter and Dart related tooling using
   /// package:unified_analytics.
   String get clientId;
@@ -311,12 +343,12 @@
   /// Returns an instance of [FakeAnalytics] which can be used in tests to check
   /// for certain [Event] instances within [FakeAnalytics.sentEvents].
   ///
-  /// The returned instance checks `DASH__SUPPRESS_ANALYTICS` in [environment]
-  /// rather than in the process environment. [environment] defaults to an
-  /// empty map, so tests aren't affected by the shell they run in. Pass
-  /// `Platform.environment` to follow the real environment. The production
-  /// [Analytics] factories don't offer this, and always respect the real
-  /// environment.
+  /// The returned instance checks `DASH__SUPPRESS_ANALYTICS` and AI agent
+  /// variables in [environment] rather than in the process environment.
+  /// [environment] defaults to an empty map, so tests aren't affected by the
+  /// shell they run in. Pass `Platform.environment` to follow the real
+  /// environment. The production [Analytics] factories don't offer this, and
+  /// always respect the real environment.
   @visibleForTesting
   static FakeAnalytics fake({
     required DashTool tool,
@@ -362,7 +394,7 @@
       gaClient: gaClient ?? const FakeGAClient(),
       clientIde: clientIde,
       enabledFeatures: enabledFeatures,
-      agent: agent,
+      agent: agent ?? AiAgent.detectAgentName(environment),
       firstRun: firstRun,
       enableAsserts: enableAsserts,
       isExternal: isExternal,
diff --git a/pkgs/unified_analytics/lib/src/constants.dart b/pkgs/unified_analytics/lib/src/constants.dart
index c3fa98b..3adeab4 100644
--- a/pkgs/unified_analytics/lib/src/constants.dart
+++ b/pkgs/unified_analytics/lib/src/constants.dart
@@ -99,7 +99,7 @@
 const String kLogFileName = 'dart-flutter-telemetry.log';
 
 /// The current version of the package, should be in line with pubspec version.
-const String kPackageVersion = '8.1.0-wip';
+const String kPackageVersion = '8.2.0';
 
 /// The minimum length for a session.
 const int kSessionDurationMinutes = 30;
diff --git a/pkgs/unified_analytics/lib/src/enums.dart b/pkgs/unified_analytics/lib/src/enums.dart
index 218598c..b789c60 100644
--- a/pkgs/unified_analytics/lib/src/enums.dart
+++ b/pkgs/unified_analytics/lib/src/enums.dart
@@ -328,8 +328,12 @@
       return gemini.label;
     }
 
-    if (environment['TERM_PROGRAM'] == 'cursor' ||
-        environment.keys.any((String key) => key.startsWith('CURSOR_'))) {
+    final cursorAgent = environment['CURSOR_AGENT']?.trim();
+    if ((cursorAgent != null &&
+            cursorAgent.isNotEmpty &&
+            cursorAgent != '0' &&
+            cursorAgent.toLowerCase() != 'false') ||
+        environment['CURSOR_EXTENSION_HOST_ROLE'] == 'agent-exec') {
       return cursor.label;
     }
 
diff --git a/pkgs/unified_analytics/pubspec.yaml b/pkgs/unified_analytics/pubspec.yaml
index 53ae6ca..3246b2d 100644
--- a/pkgs/unified_analytics/pubspec.yaml
+++ b/pkgs/unified_analytics/pubspec.yaml
@@ -5,7 +5,7 @@
 # LINT.IfChange
 # When updating this, keep the version consistent with the changelog and the
 # value in lib/src/constants.dart.
-version: 8.1.0-wip
+version: 8.2.0
 # LINT.ThenChange(lib/src/constants.dart)
 repository: https://github.com/dart-lang/tools/tree/main/pkgs/unified_analytics
 issue_tracker: https://github.com/dart-lang/tools/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Aunified_analytics
diff --git a/pkgs/unified_analytics/test/unified_analytics_test.dart b/pkgs/unified_analytics/test/unified_analytics_test.dart
index 4b31e24..0e70686 100644
--- a/pkgs/unified_analytics/test/unified_analytics_test.dart
+++ b/pkgs/unified_analytics/test/unified_analytics_test.dart
@@ -902,6 +902,14 @@
   test(
     'The UserProperty class correctly sets and exposes the ai_agent value',
     () {
+      expect(
+        analytics.userPropertyMap['ai_agent']?['value'],
+        isNull,
+        reason:
+            'The ai_agent user property should default to null when agent and '
+            'environment are omitted',
+      );
+
       final secondAnalytics = Analytics.fake(
         tool: initialTool,
         homeDirectory: home,
@@ -926,6 +934,45 @@
         'CursorIDE',
         reason: 'The client_ide user property should be set to CursorIDE',
       );
+
+      final envDetectedAnalytics = Analytics.fake(
+        tool: initialTool,
+        homeDirectory: home,
+        flutterChannel: flutterChannel,
+        toolsMessageVersion: toolsMessageVersion,
+        toolsMessage: toolsMessage,
+        flutterVersion: flutterVersion,
+        dartVersion: dartVersion,
+        fs: fs,
+        platform: platform,
+        environment: const {'CURSOR_AGENT': '1'},
+      );
+      expect(
+        envDetectedAnalytics.userPropertyMap['ai_agent']?['value'],
+        'Cursor',
+        reason:
+            'The ai_agent user property should be detected from environment '
+            'when agent is omitted',
+      );
+
+      final overrideAgentAnalytics = Analytics.fake(
+        tool: initialTool,
+        homeDirectory: home,
+        flutterChannel: flutterChannel,
+        toolsMessageVersion: toolsMessageVersion,
+        toolsMessage: toolsMessage,
+        flutterVersion: flutterVersion,
+        dartVersion: dartVersion,
+        fs: fs,
+        platform: platform,
+        agent: 'CustomAgent',
+        environment: const {'CURSOR_AGENT': '1'},
+      );
+      expect(
+        overrideAgentAnalytics.userPropertyMap['ai_agent']?['value'],
+        'CustomAgent',
+        reason: 'An explicit agent argument should override environment',
+      );
     },
   );
 
@@ -987,10 +1034,44 @@
     );
     expect(
       AiAgent.detectAgentName(<String, String>{'TERM_PROGRAM': 'cursor'}),
-      'Cursor',
+      isNull,
+    );
+    expect(
+      AiAgent.detectAgentName(<String, String>{'CURSOR_TRACE_ID': 'abc'}),
+      isNull,
     );
     expect(
       AiAgent.detectAgentName(<String, String>{'CURSOR_APP_VERSION': '1'}),
+      isNull,
+    );
+    expect(
+      AiAgent.detectAgentName(<String, String>{'CURSOR_AGENT': ''}),
+      isNull,
+    );
+    expect(
+      AiAgent.detectAgentName(<String, String>{'CURSOR_AGENT': '0'}),
+      isNull,
+    );
+    expect(
+      AiAgent.detectAgentName(<String, String>{'CURSOR_AGENT': 'false'}),
+      isNull,
+    );
+    expect(
+      AiAgent.detectAgentName(<String, String>{'CURSOR_AGENT': 'FALSE'}),
+      isNull,
+    );
+    expect(
+      AiAgent.detectAgentName(<String, String>{'CURSOR_AGENT': '1'}),
+      'Cursor',
+    );
+    expect(
+      AiAgent.detectAgentName(<String, String>{'CURSOR_AGENT': 'true'}),
+      'Cursor',
+    );
+    expect(
+      AiAgent.detectAgentName(<String, String>{
+        'CURSOR_EXTENSION_HOST_ROLE': 'agent-exec',
+      }),
       'Cursor',
     );
     expect(
@@ -1051,6 +1132,130 @@
     );
   });
 
+  group('Analytics.isBot', () {
+    test('returns false for empty environment', () {
+      expect(Analytics.isBot(<String, String>{}), isFalse);
+    });
+
+    test('returns false for non-bot overrides even when CI vars are set', () {
+      expect(
+        Analytics.isBot(<String, String>{'BOT': 'false', 'CI': 'true'}),
+        isFalse,
+      );
+      expect(
+        Analytics.isBot(<String, String>{'BOT': 'FALSE', 'CI': 'true'}),
+        isFalse,
+      );
+      expect(
+        Analytics.isBot(<String, String>{'BOT': '0', 'GITHUB_ACTIONS': 'true'}),
+        isFalse,
+      );
+      expect(
+        Analytics.isBot(<String, String>{
+          'FLUTTER_HOST': '1',
+          'CI': 'true',
+          'BOT': 'true',
+        }),
+        isFalse,
+      );
+      expect(
+        Analytics.isBot(<String, String>{
+          'FLUTTER_ANALYTICS_LOG_FILE': '/tmp/analytics.log',
+          'CI': 'true',
+          'BOT': 'true',
+        }),
+        isFalse,
+      );
+    });
+
+    test('returns false for falsy or empty CI and BOT values', () {
+      expect(Analytics.isBot(<String, String>{'CI': ''}), isFalse);
+      expect(Analytics.isBot(<String, String>{'CI': '   '}), isFalse);
+      expect(Analytics.isBot(<String, String>{'CI': 'false'}), isFalse);
+      expect(Analytics.isBot(<String, String>{'CI': 'FALSE'}), isFalse);
+      expect(Analytics.isBot(<String, String>{'CI': '0'}), isFalse);
+      expect(Analytics.isBot(<String, String>{'BOT': ''}), isFalse);
+      expect(Analytics.isBot(<String, String>{'BOT': '   '}), isFalse);
+    });
+
+    test('returns true for truthy CI and BOT values', () {
+      expect(Analytics.isBot(<String, String>{'CI': 'true'}), isTrue);
+      expect(Analytics.isBot(<String, String>{'CI': 'TRUE'}), isTrue);
+      expect(Analytics.isBot(<String, String>{'CI': '1'}), isTrue);
+      expect(Analytics.isBot(<String, String>{'BOT': 'true'}), isTrue);
+      expect(Analytics.isBot(<String, String>{'BOT': 'TRUE'}), isTrue);
+      expect(Analytics.isBot(<String, String>{'BOT': '1'}), isTrue);
+    });
+
+    test('returns true for known CI provider environment variables', () {
+      expect(Analytics.isBot(<String, String>{'TRAVIS': 'true'}), isTrue);
+      expect(Analytics.isBot(<String, String>{'TRAVIS': 'false'}), isFalse);
+      expect(
+        Analytics.isBot(<String, String>{'CONTINUOUS_INTEGRATION': 'true'}),
+        isTrue,
+      );
+      expect(
+        Analytics.isBot(<String, String>{'CONTINUOUS_INTEGRATION': 'false'}),
+        isFalse,
+      );
+      expect(Analytics.isBot(<String, String>{'APPVEYOR': 'True'}), isTrue);
+      expect(Analytics.isBot(<String, String>{'CIRRUS_CI': 'true'}), isTrue);
+      expect(
+        Analytics.isBot(<String, String>{
+          'AWS_REGION': 'us-east-1',
+          'CODEBUILD_INITIATOR': 'codepipeline/my-pipeline',
+        }),
+        isTrue,
+      );
+      expect(
+        Analytics.isBot(<String, String>{'AWS_REGION': 'us-east-1'}),
+        isFalse,
+      );
+      expect(
+        Analytics.isBot(<String, String>{
+          'CODEBUILD_INITIATOR': 'codepipeline/my-pipeline',
+        }),
+        isFalse,
+      );
+      expect(
+        Analytics.isBot(<String, String>{
+          'JENKINS_URL': 'https://jenkins.example.com/',
+        }),
+        isTrue,
+      );
+      expect(
+        Analytics.isBot(<String, String>{'GITHUB_ACTIONS': 'true'}),
+        isTrue,
+      );
+      expect(Analytics.isBot(<String, String>{'CHROME_HEADLESS': '1'}), isTrue);
+      expect(
+        Analytics.isBot(<String, String>{'CHROME_HEADLESS': '0'}),
+        isFalse,
+      );
+      expect(
+        Analytics.isBot(<String, String>{
+          'BUILDBOT_BUILDERNAME': 'Linux Engine',
+        }),
+        isTrue,
+      );
+      expect(
+        Analytics.isBot(<String, String>{'SWARMING_TASK_ID': '12345'}),
+        isTrue,
+      );
+      expect(
+        Analytics.isBot(<String, String>{'BORG_ALLOC_DIR': '/borg/alloc'}),
+        isTrue,
+      );
+      expect(Analytics.isBot(<String, String>{'TF_BUILD': 'True'}), isTrue);
+      expect(
+        Analytics.isBot(<String, String>{
+          'AZURE_HTTP_USER_AGENT': 'VSTS_12345',
+        }),
+        isTrue,
+      );
+    });
+  });
+
   test('The minimum session duration should be at least 30 minutes', () {
     expect(
       kSessionDurationMinutes < 30,
diff --git a/pkgs/yaml/CHANGELOG.md b/pkgs/yaml/CHANGELOG.md
index e00e04c..e91c317 100644
--- a/pkgs/yaml/CHANGELOG.md
+++ b/pkgs/yaml/CHANGELOG.md
@@ -2,6 +2,13 @@
 
 * Export `ErrorListener` and `ErrorCollector` from `package:yaml/yaml.dart`.
 * Improve error messages when mixing list and key-value syntax.
+* Preserve scanned local tag names and throw `YamlException` on malformed or
+  non-UTF-8 percent-encoded tag URIs.
+* Support single-digit `!!float` scalars and throw `YamlException` instead of
+  `RangeError` on empty or invalid single-character `!!int` and `!!float`
+  scalars.
+* Speed up scanning deeply nested flow collections: the scanner no longer
+  walks every open flow level for each token.
 
 ## 3.1.4
 
diff --git a/pkgs/yaml/lib/src/loader.dart b/pkgs/yaml/lib/src/loader.dart
index 4f57f7f..771257c 100644
--- a/pkgs/yaml/lib/src/loader.dart
+++ b/pkgs/yaml/lib/src/loader.dart
@@ -284,13 +284,16 @@
       {bool allowInt = true, bool allowFloat = true}) {
     assert(allowInt || allowFloat);
 
-    var firstChar = contents.codeUnitAt(0);
     var length = contents.length;
+    if (length == 0) return null;
 
-    // Quick check for single digit integers.
-    if (allowInt && length == 1) {
+    var firstChar = contents.codeUnitAt(0);
+
+    // Quick check for single digit numbers.
+    if (length == 1) {
       var value = firstChar - $0;
-      return value >= 0 && value <= 9 ? value : null;
+      if (value < 0 || value > 9) return null;
+      return allowInt ? value : value.toDouble();
     }
 
     var secondChar = contents.codeUnitAt(1);
diff --git a/pkgs/yaml/lib/src/parser.dart b/pkgs/yaml/lib/src/parser.dart
index 5141a19..c89a956 100644
--- a/pkgs/yaml/lib/src/parser.dart
+++ b/pkgs/yaml/lib/src/parser.dart
@@ -78,10 +78,6 @@
         return _parseDocumentEnd();
       case _State.BLOCK_NODE:
         return _parseNode(block: true);
-      case _State.BLOCK_NODE_OR_INDENTLESS_SEQUENCE:
-        return _parseNode(block: true, indentlessSequence: true);
-      case _State.FLOW_NODE:
-        return _parseNode();
       case _State.BLOCK_SEQUENCE_FIRST_ENTRY:
         // Scan past the `BLOCK-SEQUENCE-FIRST-ENTRY` token to the
         // `BLOCK-SEQUENCE-ENTRY` token.
@@ -299,7 +295,7 @@
           throw YamlException('Undefined tag handle.', tagToken!.span);
         }
 
-        tag = tagDirective.prefix + (tagToken?.suffix ?? '');
+        tag = tagDirective.prefix + tagToken!.suffix;
       }
     }
 
@@ -757,13 +753,6 @@
   /// Expect a block node.
   static const BLOCK_NODE = _State('BLOCK_NODE');
 
-  /// Expect a block node or indentless sequence.
-  static const BLOCK_NODE_OR_INDENTLESS_SEQUENCE =
-      _State('BLOCK_NODE_OR_INDENTLESS_SEQUENCE');
-
-  /// Expect a flow node.
-  static const FLOW_NODE = _State('FLOW_NODE');
-
   /// Expect the first entry of a block sequence.
   static const BLOCK_SEQUENCE_FIRST_ENTRY =
       _State('BLOCK_SEQUENCE_FIRST_ENTRY');
diff --git a/pkgs/yaml/lib/src/scanner.dart b/pkgs/yaml/lib/src/scanner.dart
index 2f7055e..197ac01 100644
--- a/pkgs/yaml/lib/src/scanner.dart
+++ b/pkgs/yaml/lib/src/scanner.dart
@@ -157,6 +157,13 @@
   /// parser to tell that the key is intended to be a mapping key.
   final _simpleKeys = <_SimpleKey?>[null];
 
+  /// The first live key in [_simpleKeys], or `null` if there is none.
+  ///
+  /// Keys are saved in increasing token order by flow level, so this is the
+  /// live key with the smallest token number. Tracking it avoids walking every
+  /// open flow level for each token.
+  _SimpleKey? _earliestSimpleKey;
+
   /// The current indentation level.
   int get _indent => _indentLevels.last.column;
 
@@ -234,13 +241,13 @@
 
   /// Whether the character at the current position is a hexidecimal
   /// digit.
-  bool get _isHex {
-    var char = _scanner.peekChar();
-    if (char == null) return false;
-    return (char >= NUMBER_0 && char <= NUMBER_9) ||
-        (char >= LETTER_A && char <= LETTER_F) ||
-        (char >= LETTER_CAP_A && char <= LETTER_CAP_F);
-  }
+  bool get _isHex => _isHexChar(_scanner.peekChar());
+
+  bool _isHexChar(int? char) =>
+      char != null &&
+      ((char >= NUMBER_0 && char <= NUMBER_9) ||
+          (char >= LETTER_A && char <= LETTER_F) ||
+          (char >= LETTER_CAP_A && char <= LETTER_CAP_F));
 
   /// Whether the character at the current position is a plain character.
   ///
@@ -328,10 +335,7 @@
         // If the current token could be a simple key, we need to scan more
         // tokens until we determine whether it is or not. Otherwise we might
         // not emit the `KEY` token before we emit the value of the key.
-        if (!_simpleKeys
-            .any((key) => key != null && key.tokenNumber == _tokensParsed)) {
-          break;
-        }
+        if (_earliestSimpleKey?.tokenNumber != _tokensParsed) break;
       }
 
       _fetchNextToken();
@@ -470,61 +474,53 @@
   /// Checks the list of potential simple keys and remove the positions that
   /// cannot contain simple keys anymore.
   void _staleSimpleKeys() {
-    for (var i = 0; i < _simpleKeys.length; i++) {
-      var key = _simpleKeys[i];
-      if (key == null) continue;
+    // Simple keys are only staled in the block context, where there is a
+    // single level.
+    if (!_inBlockContext) return;
+    var key = _simpleKeys.single;
+    if (key == null) return;
 
-      // libyaml requires that all simple keys be a single line and no longer
-      // than 1024 characters. However, in section 7.4.2 of the spec
-      // (http://yaml.org/spec/1.2/spec.html#id2790832), these restrictions are
-      // only applied when the curly braces are omitted. It's difficult to
-      // retain enough context to know which keys need to have the restriction
-      // placed on them, so for now we go the other direction and allow
-      // everything but multiline simple keys in a block context.
-      if (!_inBlockContext) continue;
+    // libyaml requires that all simple keys be a single line and no longer
+    // than 1024 characters. However, in section 7.4.2 of the spec
+    // (http://yaml.org/spec/1.2/spec.html#id2790832), these restrictions are
+    // only applied when the curly braces are omitted. It's difficult to
+    // retain enough context to know which keys need to have the restriction
+    // placed on them, so for now we go the other direction and allow
+    // everything but multiline simple keys in a block context.
+    if (key.line == _scanner.line) return;
 
-      if (key.line == _scanner.line) continue;
-
-      if (key.required) {
-        final keyIndentIdx = _indentLevels
-            .lastIndexWhere((indent) => indent.column == key.column);
-        final inBlockSequence = keyIndentIdx >= 0 &&
-            _indentLevels[keyIndentIdx].type == TokenType.blockSequenceStart;
-        final message = StringBuffer("Expected ':'.");
-        if (inBlockSequence) {
-          message.write(" If this is a list entry, it must start with '- '.");
-        }
-        _reportError(YamlException(message.toString(), _scanner.emptySpan));
-        _tokens.insert(key.tokenNumber - _tokensParsed,
-            Token(TokenType.key, key.location.pointSpan() as FileSpan));
+    if (key.required) {
+      final inBlockSequence =
+          _indentLevels.last.type == TokenType.blockSequenceStart;
+      final message = StringBuffer("Expected ':'.");
+      if (inBlockSequence) {
+        message.write(" If this is a list entry, it must start with '- '.");
       }
-
-      _simpleKeys[i] = null;
+      _reportError(YamlException(message.toString(), _scanner.emptySpan));
+      _tokens.insert(key.tokenNumber - _tokensParsed,
+          Token(TokenType.key, key.location.pointSpan() as FileSpan));
     }
+
+    _clearLastSimpleKey();
   }
 
   /// Checks if a simple key may start at the current position and saves it if
   /// so.
   void _saveSimpleKey() {
+    if (!_simpleKeyAllowed) return;
+
     // A simple key is required at the current position if the scanner is in the
     // block context and the current column coincides with the indentation
     // level.
     var required = _inBlockContext && _indent == _scanner.column;
 
-    // A simple key is required only when it is the first token in the current
-    // line. Therefore it is always allowed. But we add a check anyway.
-    assert(_simpleKeyAllowed || !required);
-
-    if (!_simpleKeyAllowed) return;
-
     // If the current position may start a simple key, save it.
     _removeSimpleKey();
-    _simpleKeys[_simpleKeys.length - 1] = _SimpleKey(
-        _tokensParsed + _tokens.length,
-        _scanner.line,
-        _scanner.column,
-        _scanner.location,
+    final key = _SimpleKey(_tokensParsed + _tokens.length, _scanner.line,
+        _scanner.column, _scanner.location,
         required: required);
+    _simpleKeys[_simpleKeys.length - 1] = key;
+    _earliestSimpleKey ??= key;
   }
 
   /// Removes a potential simple key at the current flow level.
@@ -535,6 +531,14 @@
           key.location.pointSpan());
     }
 
+    _clearLastSimpleKey();
+  }
+
+  /// Clears the key at the current flow level.
+  void _clearLastSimpleKey() {
+    if (identical(_simpleKeys.last, _earliestSimpleKey)) {
+      _earliestSimpleKey = null;
+    }
     _simpleKeys[_simpleKeys.length - 1] = null;
   }
 
@@ -546,6 +550,9 @@
   /// Decreases the flow level.
   void _decreaseFlowLevel() {
     if (_inBlockContext) return;
+    if (identical(_simpleKeys.last, _earliestSimpleKey)) {
+      _earliestSimpleKey = null;
+    }
     _simpleKeys.removeLast();
   }
 
@@ -712,7 +719,7 @@
           tokenNumber: simpleKey.tokenNumber);
 
       // Remove the simple key.
-      _simpleKeys[_simpleKeys.length - 1] = null;
+      _clearLastSimpleKey();
 
       // A simple key cannot follow another simple key.
       _simpleKeyAllowed = false;
@@ -966,14 +973,9 @@
     var next = _scanner.peekChar();
     if (name.isEmpty ||
         (!_isBlankOrEnd &&
-            next != QUESTION &&
-            next != COLON &&
             next != COMMA &&
             next != RIGHT_SQUARE &&
-            next != RIGHT_CURLY &&
-            next != PERCENT &&
-            next != AT &&
-            next != GRAVE_ACCENT)) {
+            next != RIGHT_CURLY)) {
       throw YamlException(
           'Expected alphanumeric character.', _scanner.emptySpan);
     }
@@ -1010,7 +1012,8 @@
       if (handle.length > 1 && handle.startsWith('!') && handle.endsWith('!')) {
         suffix = _scanTagUri(flowSeparators: false);
       } else {
-        suffix = _scanTagUri(head: handle, flowSeparators: false);
+        suffix =
+            _scanTagUri(head: handle, headStart: start, flowSeparators: false);
 
         // There was no explicit handle.
         if (suffix.isEmpty) {
@@ -1060,7 +1063,8 @@
   /// [head] is the initial portion of the tag that's already been scanned.
   /// [flowSeparators] indicates whether the tag URI can contain flow
   /// separators.
-  String _scanTagUri({String? head, bool flowSeparators = true}) {
+  String _scanTagUri(
+      {String? head, LineScannerState? headStart, bool flowSeparators = true}) {
     var length = head == null ? 0 : head.length;
     var buffer = StringBuffer();
 
@@ -1077,6 +1081,7 @@
     //
     // In a shorthand tag annotation, the flow separators ',', '[', and ']' are
     // disallowed.
+    var startState = headStart ?? _scanner.state;
     var start = _scanner.position;
     var char = _scanner.peekChar();
     while (_isTagChar ||
@@ -1086,8 +1091,31 @@
       char = _scanner.peekChar();
     }
 
+    buffer.write(_scanner.substring(start));
+
     // libyaml manually decodes the URL, but we don't have to do that.
-    return Uri.decodeFull(_scanner.substring(start));
+    return _decodeTagUri(buffer.toString(), _scanner.spanFrom(startState));
+  }
+
+  /// Decodes a percent-encoded tag [uri], throwing a [YamlException] at [span]
+  /// if it contains malformed percent-encoding or invalid UTF-8 bytes.
+  String _decodeTagUri(String uri, FileSpan span) {
+    for (var i = 0; i < uri.length; i++) {
+      if (uri.codeUnitAt(i) == PERCENT) {
+        if (i + 2 >= uri.length ||
+            !_isHexChar(uri.codeUnitAt(i + 1)) ||
+            !_isHexChar(uri.codeUnitAt(i + 2))) {
+          throw YamlException('Expected 2-digit hexadecimal number.', span);
+        }
+        i += 2;
+      }
+    }
+
+    try {
+      return Uri.decodeFull(uri);
+    } on FormatException catch (error) {
+      throw YamlException(error.message, span);
+    }
   }
 
   /// Scans a block scalar.
@@ -1608,8 +1636,7 @@
   }
 
   bool _isStandardCharacterAt(int offset) {
-    var first = _scanner.peekChar(offset);
-    if (first == null) return false;
+    var first = _scanner.peekChar(offset)!;
 
     if (isHighSurrogate(first)) {
       var next = _scanner.peekChar(offset + 1);
diff --git a/pkgs/yaml/test/flow_nesting_performance_test.dart b/pkgs/yaml/test/flow_nesting_performance_test.dart
new file mode 100644
index 0000000..60943f8
--- /dev/null
+++ b/pkgs/yaml/test/flow_nesting_performance_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2026, the Dart project authors.
+// Copyright (c) 2006, Kirill Simonov.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+@TestOn('vm')
+library;
+
+import 'package:test/test.dart';
+import 'package:yaml/yaml.dart';
+
+void main() {
+  const items = 100000;
+
+  /// The fastest of a few loads, which is not thrown off by a GC pause or a
+  /// busy machine, after a warm-up load so the JIT has optimized the scanner.
+  int millisecondsToLoad(String text) {
+    loadYaml(text);
+    var fastest = 1 << 30;
+    for (var i = 0; i < 3; i++) {
+      final stopwatch = Stopwatch()..start();
+      loadYaml(text);
+      final elapsed = stopwatch.elapsedMilliseconds;
+      if (elapsed < fastest) fastest = elapsed;
+    }
+    return fastest;
+  }
+
+  // Before the fix the scanner walked every open flow level for each token, so
+  // the document nested 1000 levels deep took about 6 times longer to scan
+  // than the same scalars at depth 1; now the two take about the same time.
+  void expectDepthDoesNotMatter(String Function(int depth) document) {
+    final shallow = millisecondsToLoad(document(1));
+    final deep = millisecondsToLoad(document(1000));
+    expect(deep, lessThan(shallow * 3 + 50));
+  }
+
+  test('scanning flow sequences does not depend on nesting depth', () {
+    final scalars = List.filled(items, '1').join(',');
+    expectDepthDoesNotMatter(
+      (depth) => '${'[' * depth}$scalars${']' * depth}',
+    );
+  });
+
+  test('scanning flow mapping values does not depend on nesting depth', () {
+    final pairs = List.generate(items, (i) => 'k$i: 1').join(',');
+    expectDepthDoesNotMatter(
+      (depth) => '${'{a: ' * depth}{$pairs}${'}' * depth}',
+    );
+  });
+
+  test('nested flow collections still load as the same structure', () {
+    expect(loadYaml('[[a, {b: [c]}], {d: [e, f]}]'), [
+      [
+        'a',
+        {
+          'b': ['c']
+        }
+      ],
+      {
+        'd': ['e', 'f']
+      },
+    ]);
+  });
+}
diff --git a/pkgs/yaml/test/yaml_test.dart b/pkgs/yaml/test/yaml_test.dart
index 4f512c1..e987290 100644
--- a/pkgs/yaml/test/yaml_test.dart
+++ b/pkgs/yaml/test/yaml_test.dart
@@ -1154,6 +1154,71 @@
         First occurrence: &anchor Value
         Second occurrence: *anchor''');
     });
+
+    test('rejects malformed percent-encoding in tag URIs with YamlException',
+        () {
+      for (var input in [
+        '!<%> foo',
+        '!a!% foo',
+        '%TAG ! tag:%\n---\n!foo bar',
+        '!% foo',
+        '!<%GG> foo',
+      ]) {
+        expect(() => loadYaml(input), throwsYamlException);
+        expect(() => loadYamlNode(input), throwsYamlException);
+      }
+    });
+
+    test(
+        'rejects invalid UTF-8 percent-encoding in tag URIs with YamlException',
+        () {
+      for (var input in [
+        '!<%C3%28> foo',
+        '!%C3%28 foo',
+      ]) {
+        expect(() => loadYaml(input), throwsYamlException);
+        expect(() => loadYamlNode(input), throwsYamlException);
+      }
+    });
+
+    test('rejects undefined local tags with YamlException', () {
+      for (var input in [
+        '!custom 123',
+        '!<custom> 123',
+      ]) {
+        expect(() => loadYaml(input), throwsYamlException);
+        expect(() => loadYamlNode(input), throwsYamlException);
+      }
+
+      // When %TAG ! is mapped to the standard YAML tag prefix, shorthand !str
+      // and !int tags resolve through the primary handle.
+      expectYamlLoads(['12', 12], '''
+        %TAG ! tag:yaml.org,2002:
+        ---
+        - !str 12
+        - !int "12"''');
+    });
+
+    test('handles block sequence, simple key, and CRLF edge cases cleanly', () {
+      for (var input in [
+        '- :?\t-\r:',
+        'a: "\r\n:',
+      ]) {
+        expect(() => loadYaml(input), throwsYamlException);
+        expect(() => loadYamlNode(input), throwsYamlException);
+      }
+
+      expect(
+          loadYaml('?\r-'),
+          deepEquals(deepEqualsMap({
+            [null]: null
+          })));
+      expect(loadYamlNode('?\r-'), isA<YamlMap>());
+      expect(loadYaml('-\n...'), equals([null]));
+      expect(loadYamlNode('-\n...'), isA<YamlList>());
+      expect(loadYaml(':?\t'), equals(':?'));
+      expect(loadYamlNode(':?\t'), isA<YamlScalar>());
+    });
   });
 
   // Chapter 7: Flow Styles
@@ -1941,7 +2006,36 @@
         positive: !!float 2.3e4
         infinity: !!float .inf
         not a number: !!float .nan''');
-    }, skip: 'Fails for single digit float');
+    });
+
+    test('rejects empty and invalid 1-character !!int and !!float scalars', () {
+      for (var input in [
+        '!!int',
+        '!!int ""',
+        '- !!int\n',
+        '!!float',
+        '!!float ""',
+        '- !!float\n',
+        '!!float a',
+        '!!float .',
+        '!!float -',
+      ]) {
+        expect(() => loadYaml(input), throwsYamlException);
+        expect(() => loadYamlNode(input), throwsYamlException);
+      }
+    });
+
+    test(
+        'rejects unseparated content after a multiline quoted scalar at block '
+        'indent', () {
+      for (var input in [
+        ' a: "\n"b',
+        " a: '\n'b",
+      ]) {
+        expect(() => loadYaml(input), throwsYamlException);
+        expect(() => loadYamlNode(input), throwsYamlException);
+      }
+    });
 
     test('[Example 10.8]', () {
       expectYamlStreamLoads([
diff --git a/pkgs/yaml/test/yaml_test_suite_test.dart b/pkgs/yaml/test/yaml_test_suite_test.dart
index a538d22..1debbae 100644
--- a/pkgs/yaml/test/yaml_test_suite_test.dart
+++ b/pkgs/yaml/test/yaml_test_suite_test.dart
@@ -59,7 +59,6 @@
   'DK95-0',
   'DK95-1',
   'DK95-4',
-  'LHL4-0',
   'ZXT5-0',
   'P76L-0',
   '9JBA-0',
@@ -72,9 +71,24 @@
   'ZYU8-2',
   'RHX7-0',
   '2XXW-0',
-  '9HCY-0',
   'KS4U-0',
   'Z9M4-0',
   'EB22-0',
   '3HFZ-0',
+  // These cases use custom local or %TAG-mapped tags (!foo, !local, !shape,
+  // etc.), which Loader intentionally rejects as undefined tags (see
+  // yaml_test.dart Examples 2.24, 5.6, 6.18, and 8.21). They previously passed
+  // only because Scanner._scanTagUri dropped the scanned tag head and rewrote
+  // shorthand '!tag' tokens to the non-specific '!' tag.
+  // Note: LHL4-0 ('!invalid{}tag scalar') and 9HCY-0 (missing '...' before
+  // '%TAG' after '!foo "bar"') were removed from _expectedFailures for the
+  // same reason: their 'fail: true' expectation is now satisfied by Loader
+  // rejecting '!invalid' / '!foo', even though the scanner/parser still does
+  // not reject the underlying syntax error.
+  '6WLZ-0',
+  '9WXW-0',
+  'C4HZ-0',
+  'CUP7-0',
+  'M5C3-0',
+  'Z67P-0',
 ];