bedrock2 platform, tests, 64-bit p256_coord_sub

This change adds the scaffolding to integrate code proven in Rocq using
Bedrock and Fiat Cryptography to libcrypto, in particular for optimized
elliptic-curve and finite-field arithmetic. A new implementation of p256
coordinate subtracton is added as an initial demonstration.

Change-Id: I73b3bd8258a33681a2e5b6532e389151e157985b
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/79208
Auto-Submit: Andres Erbsen <andreser@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/build.json b/build.json
index 641e0d4..152a991 100644
--- a/build.json
+++ b/build.json
@@ -565,7 +565,10 @@
             "third_party/fiat/curve25519_64_msvc.h",
             "third_party/fiat/p256_32.h",
             "third_party/fiat/p256_64.h",
-            "third_party/fiat/p256_64_msvc.h"
+            "third_party/fiat/p256_64_msvc.h",
+            "third_party/fiat/p256_bedrock.c.inc",
+            "third_party/fiat/bedrock_unverified_bareminimum.c.inc",
+            "third_party/fiat/bedrock_unverified_platform.c.inc"
         ],
         "err_data": [
             "crypto/err/*.errordata"
@@ -881,7 +884,9 @@
             "crypto/trust_token/trust_token_test.cc",
             "crypto/x509/x509_test.cc",
             "crypto/x509/x509_time_test.cc",
-            "crypto/xwing/xwing_test.cc"
+            "crypto/xwing/xwing_test.cc",
+            "third_party/fiat/bedrock_platform_test.cc",
+            "third_party/fiat/bedrock_polyfill_platform.c.inc"
         ],
         "data": [
             "crypto/blake2/blake2b256_tests.txt",
diff --git a/gen/sources.bzl b/gen/sources.bzl
index 6811bc0..9f976f0 100644
--- a/gen/sources.bzl
+++ b/gen/sources.bzl
@@ -661,6 +661,8 @@
     "crypto/spake2plus/internal.h",
     "crypto/trust_token/internal.h",
     "crypto/x509/internal.h",
+    "third_party/fiat/bedrock_unverified_bareminimum.c.inc",
+    "third_party/fiat/bedrock_unverified_platform.c.inc",
     "third_party/fiat/curve25519_32.h",
     "third_party/fiat/curve25519_64.h",
     "third_party/fiat/curve25519_64_adx.h",
@@ -668,6 +670,7 @@
     "third_party/fiat/p256_32.h",
     "third_party/fiat/p256_64.h",
     "third_party/fiat/p256_64_msvc.h",
+    "third_party/fiat/p256_bedrock.c.inc",
 ]
 
 crypto_sources_asm = [
@@ -782,6 +785,8 @@
     "crypto/x509/x509_test.cc",
     "crypto/x509/x509_time_test.cc",
     "crypto/xwing/xwing_test.cc",
+    "third_party/fiat/bedrock_platform_test.cc",
+    "third_party/fiat/bedrock_polyfill_platform.c.inc",
 ]
 
 crypto_test_data = [
diff --git a/gen/sources.cmake b/gen/sources.cmake
index 913bf97..978ada9 100644
--- a/gen/sources.cmake
+++ b/gen/sources.cmake
@@ -679,6 +679,8 @@
   crypto/spake2plus/internal.h
   crypto/trust_token/internal.h
   crypto/x509/internal.h
+  third_party/fiat/bedrock_unverified_bareminimum.c.inc
+  third_party/fiat/bedrock_unverified_platform.c.inc
   third_party/fiat/curve25519_32.h
   third_party/fiat/curve25519_64.h
   third_party/fiat/curve25519_64_adx.h
@@ -686,6 +688,7 @@
   third_party/fiat/p256_32.h
   third_party/fiat/p256_64.h
   third_party/fiat/p256_64_msvc.h
+  third_party/fiat/p256_bedrock.c.inc
 )
 
 set(
@@ -806,6 +809,8 @@
   crypto/x509/x509_test.cc
   crypto/x509/x509_time_test.cc
   crypto/xwing/xwing_test.cc
+  third_party/fiat/bedrock_platform_test.cc
+  third_party/fiat/bedrock_polyfill_platform.c.inc
 )
 
 set(
diff --git a/gen/sources.gni b/gen/sources.gni
index f2022e6..4a7947c 100644
--- a/gen/sources.gni
+++ b/gen/sources.gni
@@ -661,6 +661,8 @@
   "crypto/spake2plus/internal.h",
   "crypto/trust_token/internal.h",
   "crypto/x509/internal.h",
+  "third_party/fiat/bedrock_unverified_bareminimum.c.inc",
+  "third_party/fiat/bedrock_unverified_platform.c.inc",
   "third_party/fiat/curve25519_32.h",
   "third_party/fiat/curve25519_64.h",
   "third_party/fiat/curve25519_64_adx.h",
@@ -668,6 +670,7 @@
   "third_party/fiat/p256_32.h",
   "third_party/fiat/p256_64.h",
   "third_party/fiat/p256_64_msvc.h",
+  "third_party/fiat/p256_bedrock.c.inc",
 ]
 
 crypto_sources_asm = [
@@ -782,6 +785,8 @@
   "crypto/x509/x509_test.cc",
   "crypto/x509/x509_time_test.cc",
   "crypto/xwing/xwing_test.cc",
+  "third_party/fiat/bedrock_platform_test.cc",
+  "third_party/fiat/bedrock_polyfill_platform.c.inc",
 ]
 
 crypto_test_data = [
diff --git a/gen/sources.json b/gen/sources.json
index 9184e23..6284d04 100644
--- a/gen/sources.json
+++ b/gen/sources.json
@@ -643,13 +643,16 @@
       "crypto/spake2plus/internal.h",
       "crypto/trust_token/internal.h",
       "crypto/x509/internal.h",
+      "third_party/fiat/bedrock_unverified_bareminimum.c.inc",
+      "third_party/fiat/bedrock_unverified_platform.c.inc",
       "third_party/fiat/curve25519_32.h",
       "third_party/fiat/curve25519_64.h",
       "third_party/fiat/curve25519_64_adx.h",
       "third_party/fiat/curve25519_64_msvc.h",
       "third_party/fiat/p256_32.h",
       "third_party/fiat/p256_64.h",
-      "third_party/fiat/p256_64_msvc.h"
+      "third_party/fiat/p256_64_msvc.h",
+      "third_party/fiat/p256_bedrock.c.inc"
     ],
     "asm": [
       "crypto/curve25519/asm/x25519-asm-arm.S",
@@ -762,7 +765,9 @@
       "crypto/trust_token/trust_token_test.cc",
       "crypto/x509/x509_test.cc",
       "crypto/x509/x509_time_test.cc",
-      "crypto/xwing/xwing_test.cc"
+      "crypto/xwing/xwing_test.cc",
+      "third_party/fiat/bedrock_platform_test.cc",
+      "third_party/fiat/bedrock_polyfill_platform.c.inc"
     ],
     "data": [
       "crypto/blake2/blake2b256_tests.txt",
diff --git a/gen/sources.mk b/gen/sources.mk
index 37ff3bc..944b9dc 100644
--- a/gen/sources.mk
+++ b/gen/sources.mk
@@ -653,13 +653,16 @@
   crypto/spake2plus/internal.h \
   crypto/trust_token/internal.h \
   crypto/x509/internal.h \
+  third_party/fiat/bedrock_unverified_bareminimum.c.inc \
+  third_party/fiat/bedrock_unverified_platform.c.inc \
   third_party/fiat/curve25519_32.h \
   third_party/fiat/curve25519_64.h \
   third_party/fiat/curve25519_64_adx.h \
   third_party/fiat/curve25519_64_msvc.h \
   third_party/fiat/p256_32.h \
   third_party/fiat/p256_64.h \
-  third_party/fiat/p256_64_msvc.h
+  third_party/fiat/p256_64_msvc.h \
+  third_party/fiat/p256_bedrock.c.inc
 
 boringssl_crypto_sources_asm := \
   crypto/curve25519/asm/x25519-asm-arm.S \
@@ -770,7 +773,9 @@
   crypto/trust_token/trust_token_test.cc \
   crypto/x509/x509_test.cc \
   crypto/x509/x509_time_test.cc \
-  crypto/xwing/xwing_test.cc
+  crypto/xwing/xwing_test.cc \
+  third_party/fiat/bedrock_platform_test.cc \
+  third_party/fiat/bedrock_polyfill_platform.c.inc
 
 boringssl_crypto_test_data := \
   crypto/blake2/blake2b256_tests.txt \
diff --git a/third_party/fiat/README.md b/third_party/fiat/README.md
index 1035e7f..4595ba6 100644
--- a/third_party/fiat/README.md
+++ b/third_party/fiat/README.md
@@ -1,6 +1,6 @@
 # Fiat Cryptography
 
-The files in this directory are generated using [Fiat
+Most files in this directory are generated using [Fiat
 Cryptography](https://github.com/mit-plv/fiat-crypto) from the associated
 library of arithmetic-implementation templates. These files are included under
 the Apache 2.0 license. (See LICENSE file.)
@@ -21,3 +21,15 @@
 translation validator that is included in the Fiat-Cryptography repository.
 Correct unwinding and manual assembler-directive changes related to object-file
 conventions are validated using unit tests.
+
+# Bedrock
+
+Routines for which assembly-level optimization is not necessary are written in
+[Bedrock2](https://github.com/mit-plv/bedrock2) and tranlated to C. The file
+`bedrock_unverified_platform.c.inc` provides the platform functionality code
+thus translated expects.
+
+The P256 coordinate subtraction function in 64-bit C code generated by Fiat
+Cryptography has been manually replaced with a version written in Bedrock.
+This is an early step towards using Bedrock more widely, both on its own and as
+an alternative pathway from Fiat Cryptography to C.
diff --git a/third_party/fiat/bedrock_platform_test.cc b/third_party/fiat/bedrock_platform_test.cc
new file mode 100644
index 0000000..74c0d21
--- /dev/null
+++ b/third_party/fiat/bedrock_platform_test.cc
@@ -0,0 +1,105 @@
+// Copyright 2025 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.
+
+#include <gtest/gtest.h>
+#include <openssl/rand.h>
+
+#include "bedrock_unverified_platform.c.inc"
+namespace ref {
+#include "bedrock_unverified_bareminimum.c.inc" // currently unused but planned
+#include "bedrock_polyfill_platform.c.inc"
+}
+
+namespace {
+
+#define SECRET_EQ_W(a, b) EXPECT_EQ(br_declassify(a), br_declassify(b))
+
+TEST(BedrockTest, ArithmeticAndBroadcast) {
+  std::vector<br_word_t> test_values = {
+      0,
+      1,
+      1024,
+      12345,
+      32000,
+      0xffffffff / 2 - 1,
+      0xffffffff / 2,
+      0xffffffff / 2 + 1,
+      0xffffffff - 1,
+      0xffffffff,
+      std::numeric_limits<br_word_t>::max() / 2 - 1,
+      std::numeric_limits<br_word_t>::max() / 2,
+      std::numeric_limits<br_word_t>::max() / 2 + 1,
+      std::numeric_limits<br_word_t>::max() - 1,
+      std::numeric_limits<br_word_t>::max(),
+  };
+
+  for (int i = 0; i < 100; i++) {
+    br_word_t word;
+    RAND_bytes(reinterpret_cast<uint8_t *>(&word), sizeof(word));
+    test_values.push_back(word);
+  }
+
+  auto broadcast = [](bool b) { return b ? 0u-(br_word_t)1 : (br_word_t)0; };
+
+  for (br_word_t a : test_values) {
+    SCOPED_TRACE(a);
+
+    EXPECT_EQ(a, br_value_barrier(a));
+    EXPECT_EQ(a, ref::br_value_barrier(a));
+    EXPECT_EQ(a, br_declassify(a));
+    EXPECT_EQ(a, ref::br_declassify(a));
+    EXPECT_EQ(broadcast(a != 0), br_broadcast_nonzero(a));
+    EXPECT_EQ(broadcast(a != 0), ref::br_broadcast_nonzero(a));
+    EXPECT_EQ(broadcast((br_signed_t)a < 0), br_broadcast_negative(a));
+    EXPECT_EQ(broadcast((br_signed_t)a < 0), ref::br_broadcast_negative(a));
+
+    CONSTTIME_SECRET(&a, sizeof(a));
+
+    br_word_t t = broadcast(true);
+    SECRET_EQ_W(br_broadcast_negative(a) | br_broadcast_negative(~a), t);
+    SECRET_EQ_W(br_broadcast_nonzero(a) | br_broadcast_nonzero(~a), t);
+
+    for (br_word_t b : test_values) {
+      SCOPED_TRACE(b);
+      CONSTTIME_SECRET(&b, sizeof(b));
+
+      SECRET_EQ_W(a, br_cmov(broadcast(true), a, b));
+      SECRET_EQ_W(b, br_cmov(broadcast(false), a, b));
+
+      br_word_t lo = 0, lr = 0;
+      SECRET_EQ_W(br_full_mul(a, b, &lo), ref::br_full_mul(a, b, &lr));
+      SECRET_EQ_W(lo, lr);
+      SECRET_EQ_W(lo, a * b);
+
+      for (br_word_t c : {0, 1}) {
+        SCOPED_TRACE(c);
+        CONSTTIME_SECRET(&c, sizeof(c));
+
+        SECRET_EQ_W(br_declassify(a) ? b : c, br_cmov(a, b, c));
+        SECRET_EQ_W(br_declassify(a) ? c : b, br_cmov(a, c, b));
+
+        SECRET_EQ_W(br_full_add(a, b, c, &lo), ref::br_full_add(a, b, c, &lr));
+        SECRET_EQ_W(lo, lr);
+        SECRET_EQ_W(lo, a + b + c);
+
+        SECRET_EQ_W(br_full_sub(a, b, c, &lo), ref::br_full_sub(a, b, c, &lr));
+        SECRET_EQ_W(lo, lr);
+        SECRET_EQ_W(lo, a - b - c);
+      }
+    }
+  }
+}
+
+#undef SECRET_EQ_W
+} // namespace
diff --git a/third_party/fiat/bedrock_polyfill_platform.c.inc b/third_party/fiat/bedrock_polyfill_platform.c.inc
new file mode 100644
index 0000000..ec6c3a5
--- /dev/null
+++ b/third_party/fiat/bedrock_polyfill_platform.c.inc
@@ -0,0 +1,66 @@
+// Generated from Bedrock code in Fiat Cryptogrpahy. Avoid editing directly.
+
+static inline br_word_t br_full_add(br_word_t x, br_word_t y, br_word_t carry, br_word_t* _sum) {
+  br_word_t carry_out, sum;
+  x = x+carry;
+  carry_out = (br_word_t)(x<carry);
+  sum = x+y;
+  carry_out = carry_out+((br_word_t)(sum<y));
+  *_sum = sum;
+  return carry_out;
+}
+
+static inline br_word_t br_full_sub(br_word_t x, br_word_t y, br_word_t borrow, br_word_t* _diff) {
+  br_word_t out_borrow, diff;
+  out_borrow = (br_word_t)(x<y);
+  diff = x-y;
+  out_borrow = out_borrow+((br_word_t)(diff<borrow));
+  diff = diff-borrow;
+  *_diff = diff;
+  return out_borrow;
+}
+
+static inline br_word_t br_full_mul(br_word_t a, br_word_t b, br_word_t* _low) {
+  br_word_t high, hh, lh, hl, low, second_halfword_w_oflow, n, ll, M;
+  n = ((((0u-(br_word_t)1)>>27)&63)+1)>>1;
+  M = ((br_word_t)1<<(n&(sizeof(br_word_t)*8-1)))-1;
+  ll = (a&M)*(b&M);
+  lh = (a&M)*(b>>(n&(sizeof(br_word_t)*8-1)));
+  hl = (a>>(n&(sizeof(br_word_t)*8-1)))*(b&M);
+  hh = (a>>(n&(sizeof(br_word_t)*8-1)))*(b>>(n&(sizeof(br_word_t)*8-1)));
+  second_halfword_w_oflow = ((ll>>(n&(sizeof(br_word_t)*8-1)))+(lh&M))+(hl&M);
+  high = ((hh+(lh>>(n&(sizeof(br_word_t)*8-1))))+(hl>>(n&(sizeof(br_word_t)*8-1))))+(second_halfword_w_oflow>>(n&(sizeof(br_word_t)*8-1)));
+  low = (second_halfword_w_oflow<<(n&(sizeof(br_word_t)*8-1)))+(ll&M);
+  *_low = low;
+  return high;
+}
+
+static inline br_word_t br_value_barrier(br_word_t a) {
+  /*skip*/
+  return a;
+}
+
+static inline br_word_t br_declassify(br_word_t a) {
+  /*skip*/
+  return a;
+}
+
+static inline br_word_t br_broadcast_negative(br_word_t x) {
+  br_word_t y;
+  y = (br_word_t)((br_signed_t)x>>((((0u-(br_word_t)1)>>27)&63)&(sizeof(br_word_t)*8-1)));
+  y = br_value_barrier(y);
+  return y;
+}
+
+static inline br_word_t br_broadcast_nonzero(br_word_t x) {
+  br_word_t y;
+  y = br_broadcast_negative(x|(0u-x));
+  return y;
+}
+
+static inline br_word_t br_cmov(br_word_t c, br_word_t vnz, br_word_t vz) {
+  br_word_t r, m;
+  m = br_broadcast_nonzero(c);
+  r = (m&vnz)|((~m)&vz);
+  return r;
+}
diff --git a/third_party/fiat/bedrock_unverified_bareminimum.c.inc b/third_party/fiat/bedrock_unverified_bareminimum.c.inc
new file mode 100644
index 0000000..a03388b
--- /dev/null
+++ b/third_party/fiat/bedrock_unverified_bareminimum.c.inc
@@ -0,0 +1,47 @@
+#ifndef BEDROCK_UNVERIFIED_BAREMINIMUM_INC_H_
+#define BEDROCK_UNVERIFIED_BAREMINIMUM_INC_H_
+
+static_assert(sizeof(br_word_t) == sizeof(br_signed_t), "");
+static_assert(UINTPTR_MAX <= BR_WORD_MAX, "");
+
+// "An object shall have its stored value accessed only ... a character type."
+
+static inline br_word_t _br_load1(br_word_t a) {
+  return *((uint8_t *)a);
+}
+
+static inline br_word_t _br_load2(br_word_t a) {
+  uint16_t r = 0;
+  memcpy(&r, (void *)a, sizeof(r));
+  return r;
+}
+
+static inline br_word_t _br_load4(br_word_t a) {
+  uint32_t r = 0;
+  memcpy(&r, (void *)a, sizeof(r));
+  return r;
+}
+
+static inline br_word_t _br_load(br_word_t a) {
+  br_word_t r = 0;
+  memcpy(&r, (void *)a, sizeof(r));
+  return r;
+}
+
+static inline void _br_store1(br_word_t a, uint8_t v) {
+  *((uint8_t *)a) = v;
+}
+
+static inline void _br_store2(br_word_t a, uint16_t v) {
+  memcpy((void *)a, &v, sizeof(v));
+}
+
+static inline void _br_store4(br_word_t a, uint32_t v) {
+  memcpy((void *)a, &v, sizeof(v));
+}
+
+static inline void _br_store(br_word_t a, br_word_t v) {
+  memcpy((void *)a, &v, sizeof(v));
+}
+
+#endif // BEDROCK_UNVERIFIED_BAREMINIMUM_INC_H_
diff --git a/third_party/fiat/bedrock_unverified_platform.c.inc b/third_party/fiat/bedrock_unverified_platform.c.inc
new file mode 100644
index 0000000..fbfe90f
--- /dev/null
+++ b/third_party/fiat/bedrock_unverified_platform.c.inc
@@ -0,0 +1,121 @@
+#ifndef BEDROCK_UNVERIFIED_PLATFORM_INC_H_
+#define BEDROCK_UNVERIFIED_PLATFORM_INC_H_
+
+// This file is a manually maintained and audited replacement for
+// bedrock_polyfill_platform.c.inc with tests in bedrock_platform_test.cc
+
+#include "../../crypto/internal.h"
+
+#if defined(OPENSSL_64_BIT)
+#define BR_WORD_MAX UINT64_MAX
+typedef uint64_t br_word_t;
+typedef int64_t br_signed_t;
+#elif defined(OPENSSL_32_BIT)
+#define BR_WORD_MAX UINT32_MAX
+typedef uint32_t br_word_t;
+typedef int32_t br_signed_t;
+#else
+#error "Must define either OPENSSL_32_BIT or OPENSSL_64_BIT"
+#endif
+
+static_assert(sizeof(br_word_t) == sizeof(crypto_word_t));
+
+// Scalar memory operations
+
+#include "../../third_party/fiat/bedrock_unverified_bareminimum.c.inc"
+
+// Bulk memory operations
+
+static inline void br_memcpy(br_word_t d, br_word_t s, br_word_t n) {
+  OPENSSL_memcpy((void *)d, (const void *)s, n);
+}
+
+static inline void br_memset(br_word_t d, br_word_t v, br_word_t n) {
+  OPENSSL_memset((void *)d, v, n);
+}
+
+// CPU Arithmetic
+
+static inline br_word_t br_full_add(br_word_t x, br_word_t y, br_word_t carry,
+                                    br_word_t *_sum) {
+  br_word_t carry_out = 0;
+  static_assert(sizeof(br_word_t) == sizeof(crypto_word_t));
+  *_sum = CRYPTO_addc_w(x, y, carry, &carry_out);
+  return carry_out;
+}
+
+static inline br_word_t br_full_sub(br_word_t x, br_word_t y, br_word_t borrow,
+                                    br_word_t *_diff) {
+  br_word_t borrow_out = 0;
+  static_assert(sizeof(br_word_t) == sizeof(crypto_word_t));
+  *_diff = CRYPTO_subc_w(x, y, borrow, &borrow_out);
+  return borrow_out;
+}
+
+static inline br_word_t br_full_mul(br_word_t a, br_word_t b, br_word_t *_low) {
+#if BR_WORD_MAX == UINT32_MAX
+  uint64_t r = (uint64_t)a * b;
+  *_low = r;
+  return r >> 32;
+#elif defined(BORINGSSL_HAS_UINT128)
+  uint128_t r = (uint128_t)a * b;
+  *_low = r;
+  return r >> 64;
+#elif defined(_M_X64)
+  uint64_t hi;
+  *_low = _umul128(a, b, &hi);
+  return hi;
+#elif defined(_M_ARM64)
+  *_low = a * b;
+  return __umulh(a, b);
+#else
+#error "need 64 x 64 -> 128 multiplication"
+#endif
+}
+
+// Constant-time computations
+
+static inline br_word_t br_value_barrier(br_word_t a) {
+  static_assert(sizeof(br_word_t) == sizeof(crypto_word_t));
+  return value_barrier_w(a);
+}
+
+static inline br_word_t br_declassify(br_word_t a) {
+  static_assert(sizeof(br_word_t) == sizeof(crypto_word_t));
+  return constant_time_declassify_w(a);
+}
+
+static inline br_word_t br_broadcast_negative(br_word_t x) {
+  return br_value_barrier((br_signed_t)x >> (sizeof(br_word_t) * 8 - 1));
+}
+
+static inline br_word_t br_broadcast_nonzero(br_word_t x) {
+  return br_broadcast_negative(x | (0u - x));
+}
+
+static inline br_word_t br_cmov(br_word_t c, br_word_t vnz, br_word_t vz) {
+#if !defined(OPENSSL_NO_ASM) && (defined(__GNUC__) || defined(__clang__)) && \
+    defined(__x86_64__)
+  __asm__(
+      "testq %[c], %[c]\n"
+      "cmovzq %[vz], %[vnz]"
+      : [vnz] "+r"(vnz)
+      : [vz] "r"(vz), [c] "r"(c)
+      : "cc");
+  return vnz;
+#elif !defined(OPENSSL_NO_ASM) && (defined(__GNUC__) || defined(__clang__)) && \
+    defined(__i386__)
+  __asm__(
+      "testl %[c], %[c]\n"  // test%z[c] gives "invalid operand" on clang 16.0.6
+      "cmovzl %[vz], %[vnz]"
+      : [vnz] "+r"(vnz)
+      : [vz] "r"(vz), [c] "r"(c)
+      : "cc");
+  return vnz;
+#else
+  br_word_t m = br_broadcast_nonzero(c);
+  return (m & vnz) | (~m & vz);
+#endif
+}
+
+#endif  // BEDROCK_UNVERIFIED_PLATFORM_INC_H_
diff --git a/third_party/fiat/p256_64.h b/third_party/fiat/p256_64.h
index 48a1560..21f7a98 100644
--- a/third_party/fiat/p256_64.h
+++ b/third_party/fiat/p256_64.h
@@ -1,4 +1,6 @@
 #include <openssl/base.h>
+#include "bedrock_unverified_platform.c.inc"
+#include "p256_bedrock.c.inc"
 #include "../../crypto/internal.h"
 
 #if !defined(OPENSSL_NO_ASM) && defined(__GNUC__) && defined(__x86_64__)
@@ -857,36 +859,7 @@
  *
  */
 static FIAT_P256_FIAT_INLINE void fiat_p256_sub(fiat_p256_montgomery_domain_field_element out1, const fiat_p256_montgomery_domain_field_element arg1, const fiat_p256_montgomery_domain_field_element arg2) {
-  uint64_t x1;
-  fiat_p256_uint1 x2;
-  uint64_t x3;
-  fiat_p256_uint1 x4;
-  uint64_t x5;
-  fiat_p256_uint1 x6;
-  uint64_t x7;
-  fiat_p256_uint1 x8;
-  uint64_t x9;
-  uint64_t x10;
-  fiat_p256_uint1 x11;
-  uint64_t x12;
-  fiat_p256_uint1 x13;
-  uint64_t x14;
-  fiat_p256_uint1 x15;
-  uint64_t x16;
-  fiat_p256_uint1 x17;
-  fiat_p256_subborrowx_u64(&x1, &x2, 0x0, (arg1[0]), (arg2[0]));
-  fiat_p256_subborrowx_u64(&x3, &x4, x2, (arg1[1]), (arg2[1]));
-  fiat_p256_subborrowx_u64(&x5, &x6, x4, (arg1[2]), (arg2[2]));
-  fiat_p256_subborrowx_u64(&x7, &x8, x6, (arg1[3]), (arg2[3]));
-  fiat_p256_cmovznz_u64(&x9, x8, 0x0, UINT64_C(0xffffffffffffffff));
-  fiat_p256_addcarryx_u64(&x10, &x11, 0x0, x1, x9);
-  fiat_p256_addcarryx_u64(&x12, &x13, x11, x3, (x9 & UINT32_C(0xffffffff)));
-  fiat_p256_addcarryx_u64(&x14, &x15, x13, x5, 0x0);
-  fiat_p256_addcarryx_u64(&x16, &x17, x15, x7, (x9 & UINT64_C(0xffffffff00000001)));
-  out1[0] = x10;
-  out1[1] = x12;
-  out1[2] = x14;
-  out1[3] = x16;
+  p256_coord_sub((br_word_t)out1, (br_word_t)arg1, (br_word_t)arg2);
 }
 
 /*
diff --git a/third_party/fiat/p256_64_msvc.h b/third_party/fiat/p256_64_msvc.h
index 8b65a37..3e8a129 100644
--- a/third_party/fiat/p256_64_msvc.h
+++ b/third_party/fiat/p256_64_msvc.h
@@ -17,6 +17,8 @@
 /*   twos_complement_eval z = let x1 := z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) in */
 /*                            if x1 & (2^256-1) < 2^255 then x1 & (2^256-1) else (x1 & (2^256-1)) - 2^256 */
 
+#include "bedrock_unverified_platform.c.inc"
+#include "p256_bedrock.c.inc"
 #include <stdint.h>
 #include <intrin.h>
 #if defined(_M_X64)
@@ -824,36 +826,8 @@
  *
  */
 static FIAT_P256_FIAT_INLINE void fiat_p256_sub(fiat_p256_montgomery_domain_field_element out1, const fiat_p256_montgomery_domain_field_element arg1, const fiat_p256_montgomery_domain_field_element arg2) {
-  uint64_t x1;
-  fiat_p256_uint1 x2;
-  uint64_t x3;
-  fiat_p256_uint1 x4;
-  uint64_t x5;
-  fiat_p256_uint1 x6;
-  uint64_t x7;
-  fiat_p256_uint1 x8;
-  uint64_t x9;
-  uint64_t x10;
-  fiat_p256_uint1 x11;
-  uint64_t x12;
-  fiat_p256_uint1 x13;
-  uint64_t x14;
-  fiat_p256_uint1 x15;
-  uint64_t x16;
-  fiat_p256_uint1 x17;
-  fiat_p256_subborrowx_u64(&x1, &x2, 0x0, (arg1[0]), (arg2[0]));
-  fiat_p256_subborrowx_u64(&x3, &x4, x2, (arg1[1]), (arg2[1]));
-  fiat_p256_subborrowx_u64(&x5, &x6, x4, (arg1[2]), (arg2[2]));
-  fiat_p256_subborrowx_u64(&x7, &x8, x6, (arg1[3]), (arg2[3]));
-  fiat_p256_cmovznz_u64(&x9, x8, 0x0, UINT64_C(0xffffffffffffffff));
-  fiat_p256_addcarryx_u64(&x10, &x11, 0x0, x1, x9);
-  fiat_p256_addcarryx_u64(&x12, &x13, x11, x3, (x9 & UINT32_C(0xffffffff)));
-  fiat_p256_addcarryx_u64(&x14, &x15, x13, x5, 0x0);
-  fiat_p256_addcarryx_u64(&x16, &x17, x15, x7, (x9 & UINT64_C(0xffffffff00000001)));
-  out1[0] = x10;
-  out1[1] = x12;
-  out1[2] = x14;
-  out1[3] = x16;
+  // NOTE: edited by hand, see third_party/fiat/README.md
+  p256_coord_sub((br_word_t)&out1, (br_word_t)&arg1, (br_word_t)&arg2);
 }
 
 /*
diff --git a/third_party/fiat/p256_bedrock.c.inc b/third_party/fiat/p256_bedrock.c.inc
new file mode 100644
index 0000000..fb8c3de
--- /dev/null
+++ b/third_party/fiat/p256_bedrock.c.inc
@@ -0,0 +1,21 @@
+// Generated from Bedrock code in Fiat Cryptography. Avoid editing directly.
+
+static_assert(BR_WORD_MAX == UINT64_MAX, "");
+
+static inline void p256_coord_sub(br_word_t out, br_word_t x, br_word_t y) {
+  br_word_t borrow, t0, t1, t2, t3, mask, carry, r0, r1, r2, r3;
+  borrow = br_full_sub(_br_load(x), _br_load(y), (br_word_t)0, &t0);
+  borrow = br_full_sub(_br_load(x+8), _br_load(y+8), borrow, &t1);
+  borrow = br_full_sub(_br_load((x+8)+8), _br_load((y+8)+8), borrow, &t2);
+  borrow = br_full_sub(_br_load(((x+8)+8)+8), _br_load(((y+8)+8)+8), borrow, &t3);
+  mask = br_value_barrier(0u-borrow);
+  carry = br_full_add(t0, mask, (br_word_t)0, &r0);
+  carry = br_full_add(t1, mask&0xffffffff, carry, &r1);
+  carry = br_full_add(t2, (br_word_t)0, carry, &r2);
+  carry = br_full_add(t3, mask&0xffffffff00000001, carry, &r3);
+  _br_store(out, r0);
+  _br_store(out+8, r1);
+  _br_store((out+8)+8, r2);
+  _br_store(((out+8)+8)+8, r3);
+  return;
+}