Add saturated X25519 for x86_64+ADX running Linux
Did 29000 Curve25519 arbitrary point multiplication operations in 1026074us (28263.1 ops/sec) [+31.2%]
Change-Id: I9c7d47a047dc68d37202b6cf40d7d12b5b4936f8
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60385
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index ef47623..5f0c160 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -15,6 +15,7 @@
set(
CRYPTO_SOURCES_ASM
curve25519/asm/x25519-asm-arm.S
+ curve25519/asm/fiat_curve25519_adx.S
hrss/asm/poly_rq_mul.S
poly1305/poly1305_arm_asm.S
)
@@ -137,6 +138,7 @@
cpu_intel.c
crypto.c
curve25519/curve25519.c
+ curve25519/curve25519_64_adx.c
curve25519/spake25519.c
des/des.c
dh_extra/params.c
diff --git a/crypto/curve25519/asm/fiat_curve25519_adx.S b/crypto/curve25519/asm/fiat_curve25519_adx.S
new file mode 100644
index 0000000..0c42181
--- /dev/null
+++ b/crypto/curve25519/asm/fiat_curve25519_adx.S
@@ -0,0 +1,11 @@
+#if !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_SMALL) && defined(__x86_64__)
+
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
+
+.intel_syntax noprefix
+#include "../../../third_party/fiat/asm/fiat_curve25519_adx_mul.S"
+#include "../../../third_party/fiat/asm/fiat_curve25519_adx_square.S"
+
+#endif
diff --git a/crypto/curve25519/curve25519.c b/crypto/curve25519/curve25519.c
index d4a3f21..ec3067b 100644
--- a/crypto/curve25519/curve25519.c
+++ b/crypto/curve25519/curve25519.c
@@ -19,8 +19,6 @@
//
// The field functions are shared by Ed25519 and X25519 where possible.
-#include <openssl/curve25519.h>
-
#include <assert.h>
#include <string.h>
@@ -31,7 +29,6 @@
#include "internal.h"
#include "../internal.h"
-
// Various pre-computed constants.
#include "./curve25519_tables.h"
@@ -2069,6 +2066,12 @@
x25519_NEON(out, scalar, point);
return;
}
+#elif defined(BORINGSSL_FE25519_ADX)
+ if (CRYPTO_is_BMI1_capable() && CRYPTO_is_BMI2_capable() &&
+ CRYPTO_is_ADX_capable()) {
+ x25519_scalar_mult_adx(out, scalar, point);
+ return;
+ }
#endif
x25519_scalar_mult_generic(out, scalar, point);
diff --git a/crypto/curve25519/curve25519_64_adx.c b/crypto/curve25519/curve25519_64_adx.c
new file mode 100644
index 0000000..2768989
--- /dev/null
+++ b/crypto/curve25519/curve25519_64_adx.c
@@ -0,0 +1,18 @@
+/* Copyright (c) 2023, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include "internal.h"
+#if defined(BORINGSSL_FE25519_ADX)
+#include "../../third_party/fiat/curve25519_64_adx.h"
+#endif
diff --git a/crypto/curve25519/internal.h b/crypto/curve25519/internal.h
index 1420601..4de1344 100644
--- a/crypto/curve25519/internal.h
+++ b/crypto/curve25519/internal.h
@@ -15,14 +15,13 @@
#ifndef OPENSSL_HEADER_CURVE25519_INTERNAL_H
#define OPENSSL_HEADER_CURVE25519_INTERNAL_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-#include <openssl/base.h>
+#include <openssl/curve25519.h>
#include "../internal.h"
+#if defined(__cplusplus)
+extern "C" {
+#endif
#if defined(OPENSSL_ARM) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_APPLE)
#define BORINGSSL_X25519_NEON
@@ -32,6 +31,26 @@
const uint8_t point[32]);
#endif
+#if !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_SMALL) && \
+ defined(__GNUC__) && defined(__x86_64__)
+#define BORINGSSL_FE25519_ADX
+
+// fiat_curve25519_adx_mul is defined in
+// third_party/fiat/asm/fiat_curve25519_adx_mul.S
+void __attribute__((sysv_abi))
+fiat_curve25519_adx_mul(uint64_t out[4], const uint64_t in1[4],
+ const uint64_t in2[4]);
+
+// fiat_curve25519_adx_square is defined in
+// third_party/fiat/asm/fiat_curve25519_adx_square.S
+void __attribute__((sysv_abi))
+fiat_curve25519_adx_square(uint64_t out[4], const uint64_t in[4]);
+
+// x25519_scalar_mult_adx is defined in third_party/fiat/curve25519_64_adx.h
+void x25519_scalar_mult_adx(uint8_t out[32], const uint8_t scalar[32],
+ const uint8_t point[32]);
+#endif
+
#if defined(OPENSSL_64_BIT)
// fe means field element. Here the field is \Z/(2^255-19). An element t,
// entries t[0]...t[4], represents the integer t[0]+2^51 t[1]+2^102 t[2]+2^153
diff --git a/crypto/curve25519/x25519_test.cc b/crypto/curve25519/x25519_test.cc
index f512d01..8c08ee2 100644
--- a/crypto/curve25519/x25519_test.cc
+++ b/crypto/curve25519/x25519_test.cc
@@ -20,6 +20,7 @@
#include <openssl/curve25519.h>
+#include "internal.h"
#include "../internal.h"
#include "../test/abi_test.h"
#include "../test/file_test.h"
@@ -231,3 +232,27 @@
CHECK_ABI(x25519_NEON, secret, kScalar, kPoint);
}
#endif // BORINGSSL_X25519_NEON && SUPPORTS_ABI_TEST
+
+#if defined(BORINGSSL_FE25519_ADX) && defined(SUPPORTS_ABI_TEST)
+TEST(X25519Test, AdxMulABI) {
+ static const uint64_t in1[4] = {0}, in2[4] = {0};
+ uint64_t out[4];
+ if (CRYPTO_is_BMI1_capable() && CRYPTO_is_BMI2_capable() &&
+ CRYPTO_is_ADX_capable()) {
+ CHECK_ABI(fiat_curve25519_adx_mul, out, in1, in2);
+ } else {
+ GTEST_SKIP() << "Can't test ABI of ADX code without ADX";
+ }
+}
+
+TEST(X25519Test, AdxSquareABI) {
+ static const uint64_t in[4] = {0};
+ uint64_t out[4];
+ if (CRYPTO_is_BMI1_capable() && CRYPTO_is_BMI2_capable() &&
+ CRYPTO_is_ADX_capable()) {
+ CHECK_ABI(fiat_curve25519_adx_square, out, in);
+ } else {
+ GTEST_SKIP() << "Can't test ABI of ADX code without ADX";
+ }
+}
+#endif // BORINGSSL_FE25519_ADX && SUPPORTS_ABI_TEST
diff --git a/third_party/fiat/asm/fiat_curve25519_adx_mul.S b/third_party/fiat/asm/fiat_curve25519_adx_mul.S
new file mode 100644
index 0000000..acbac97
--- /dev/null
+++ b/third_party/fiat/asm/fiat_curve25519_adx_mul.S
@@ -0,0 +1,155 @@
+.text
+#if defined(__APPLE__)
+.global _fiat_curve25519_adx_mul
+_fiat_curve25519_adx_mul:
+#else
+.global fiat_curve25519_adx_mul
+fiat_curve25519_adx_mul:
+#endif
+
+.cfi_startproc
+mov [rsp - 0x08], rbp
+.cfi_offset rbp, -8-0x08
+mov rbp, rsp
+
+mov rax, rdx
+mov rdx, [ rsi + 0x18 ]
+mulx r11, r10, [ rax + 0x8 ]
+mov rdx, [ rax + 0x0 ]
+mov [ rsp - 0x58 ], r15
+.cfi_offset r15, -8-0x58
+mulx r8, rcx, [ rsi + 0x18 ]
+mov rdx, [ rsi + 0x8 ]
+mov [ rsp - 0x80 ], rbx
+.cfi_offset rbx, -8-0x80
+mulx rbx, r9, [ rax + 0x18 ]
+mov rdx, [ rsi + 0x8 ]
+mov [ rsp - 0x70 ], r12
+.cfi_offset r12, -8-0x70
+mulx r15, r12, [ rax + 0x8 ]
+mov rdx, [ rsi + 0x0 ]
+mov [ rsp - 0x68 ], r13
+.cfi_offset r13, -8-0x68
+mov [ rsp - 0x60 ], r14
+.cfi_offset r14, -8-0x60
+mulx r14, r13, [ rax + 0x0 ]
+mov rdx, [ rax + 0x10 ]
+mov [ rsp - 0x18 ], r15
+mov [ rsp - 0x50 ], rdi
+mulx rdi, r15, [ rsi + 0x0 ]
+mov rdx, [ rax + 0x18 ]
+mov [ rsp - 0x48 ], r13
+mov [ rsp - 0x40 ], r9
+mulx r9, r13, [ rsi + 0x0 ]
+test al, al
+adox rcx, rdi
+mov rdx, [ rsi + 0x10 ]
+mov [ rsp - 0x38 ], r13
+mulx r13, rdi, [ rax + 0x8 ]
+adox r10, r9
+mov rdx, 0x0
+adox rbx, rdx
+adcx rdi, rcx
+adcx r8, r10
+mov r9, rdx
+adcx r9, rbx
+mov rdx, [ rsi + 0x10 ]
+mulx r10, rcx, [ rax + 0x0 ]
+mov rdx, [ rsi + 0x0 ]
+mov [ rsp - 0x30 ], r15
+mulx r15, rbx, [ rax + 0x8 ]
+mov rdx, -0x2
+inc rdx
+adox rcx, r15
+setc r15b
+clc
+adcx rcx, r12
+adox r10, rdi
+mov rdx, [ rax + 0x10 ]
+mov [ rsp - 0x78 ], rcx
+mulx rcx, rdi, [ rsi + 0x10 ]
+adox rdi, r8
+mov rdx, [ rax + 0x18 ]
+mov [ rsp - 0x28 ], rcx
+mulx rcx, r8, [ rsi + 0x10 ]
+mov rdx, [ rax + 0x10 ]
+mov [ rsp - 0x20 ], r8
+mulx r12, r8, [ rsi + 0x18 ]
+adox r8, r9
+mov rdx, [ rsi + 0x8 ]
+mov [ rsp - 0x10 ], r12
+mulx r12, r9, [ rax + 0x10 ]
+movzx rdx, r15b
+lea rdx, [ rdx + rcx ]
+adcx r9, r10
+adcx r13, rdi
+mov r15, 0x0
+mov r10, r15
+adox r10, rdx
+mov rdx, [ rax + 0x18 ]
+mulx rcx, rdi, [ rsi + 0x18 ]
+adox rcx, r15
+adcx r11, r8
+mov rdx, r15
+adcx rdx, r10
+adcx rcx, r15
+mov r8, rdx
+mov rdx, [ rax + 0x0 ]
+mulx r15, r10, [ rsi + 0x8 ]
+test al, al
+adox r10, r14
+adcx rbx, r10
+adox r15, [ rsp - 0x78 ]
+adcx r15, [ rsp - 0x30 ]
+adox r9, [ rsp - 0x18 ]
+adcx r9, [ rsp - 0x38 ]
+adox r13, [ rsp - 0x40 ]
+adcx r12, r13
+adox r11, [ rsp - 0x20 ]
+adcx r11, [ rsp - 0x28 ]
+mov rdx, 0x26
+mulx rsi, r14, r12
+adox rdi, r8
+adcx rdi, [ rsp - 0x10 ]
+mulx r10, r8, r11
+mov r13, 0x0
+adox rcx, r13
+adcx rcx, r13
+mulx r11, r12, rdi
+xor rdi, rdi
+adox r8, rbx
+adox r12, r15
+mulx rbx, r13, rcx
+adcx r14, [ rsp - 0x48 ]
+adox r13, r9
+adox rbx, rdi
+adcx rsi, r8
+adcx r10, r12
+adcx r11, r13
+adc rbx, 0x0
+mulx r9, r15, rbx
+xor r9, r9
+adox r15, r14
+mov rdi, r9
+adox rdi, rsi
+mov rcx, r9
+adox rcx, r10
+mov r8, [ rsp - 0x50 ]
+mov [ r8 + 0x8 ], rdi
+mov r12, r9
+adox r12, r11
+mov r14, r9
+cmovo r14, rdx
+mov [ r8 + 0x18 ], r12
+adcx r15, r14
+mov [ r8 + 0x0 ], r15
+mov [ r8 + 0x10 ], rcx
+mov rbx, [ rsp - 0x80 ]
+mov r12, [ rsp - 0x70 ]
+mov r13, [ rsp - 0x68 ]
+mov r14, [ rsp - 0x60 ]
+mov r15, [ rsp - 0x58 ]
+
+mov rbp, [rsp - 0x08]
+ret
+.cfi_endproc
diff --git a/third_party/fiat/asm/fiat_curve25519_adx_square.S b/third_party/fiat/asm/fiat_curve25519_adx_square.S
new file mode 100644
index 0000000..2ba3fd4
--- /dev/null
+++ b/third_party/fiat/asm/fiat_curve25519_adx_square.S
@@ -0,0 +1,123 @@
+.text
+#if defined(__APPLE__)
+.global _fiat_curve25519_adx_square
+_fiat_curve25519_adx_square:
+#else
+.global fiat_curve25519_adx_square
+fiat_curve25519_adx_square:
+#endif
+
+.cfi_startproc
+mov [rsp - 0x08], rbp
+.cfi_offset rbp, -8-0x08
+mov rbp, rsp
+
+mov rdx, [ rsi + 0x0 ]
+mulx r10, rax, [ rsi + 0x8 ]
+mov rdx, [ rsi + 0x0 ]
+mulx rcx, r11, [ rsi + 0x10 ]
+xor rdx, rdx
+adox r11, r10
+mov rdx, [ rsi + 0x0 ]
+mulx r9, r8, [ rsi + 0x18 ]
+mov rdx, [ rsi + 0x8 ]
+mov [ rsp - 0x80 ], rbx
+.cfi_offset rbx, -8-0x80
+mulx rbx, r10, [ rsi + 0x18 ]
+adox r8, rcx
+mov [rsp - 0x48 ], rdi
+adox r10, r9
+adcx rax, rax
+mov rdx, [ rsi + 0x10 ]
+mulx r9, rcx, [ rsi + 0x18 ]
+adox rcx, rbx
+mov rdx, [ rsi + 0x10 ]
+mulx rdi, rbx, [ rsi + 0x8 ]
+mov rdx, 0x0
+adox r9, rdx
+mov [ rsp - 0x70 ], r12
+.cfi_offset r12, -8-0x70
+mov r12, -0x3
+inc r12
+adox rbx, r8
+adox rdi, r10
+adcx r11, r11
+mov r8, rdx
+adox r8, rcx
+mov r10, rdx
+adox r10, r9
+adcx rbx, rbx
+mov rdx, [ rsi + 0x0 ]
+mulx r9, rcx, rdx
+mov rdx, [ rsi + 0x8 ]
+mov [ rsp - 0x68 ], r13
+.cfi_offset r13, -8-0x68
+mov [ rsp - 0x60 ], r14
+.cfi_offset r14, -8-0x60
+mulx r14, r13, rdx
+seto dl
+inc r12
+adox r9, rax
+adox r13, r11
+adox r14, rbx
+adcx rdi, rdi
+mov al, dl
+mov rdx, [ rsi + 0x10 ]
+mulx rbx, r11, rdx
+adox r11, rdi
+adcx r8, r8
+adox rbx, r8
+adcx r10, r10
+movzx rdx, al
+mov rdi, 0x0
+adcx rdx, rdi
+movzx r8, al
+lea r8, [ r8 + rdx ]
+mov rdx, [ rsi + 0x18 ]
+mulx rdi, rax, rdx
+adox rax, r10
+mov rdx, 0x26
+mov [ rsp - 0x58 ], r15
+.cfi_offset r15, -8-0x58
+mulx r15, r10, r11
+clc
+adcx r10, rcx
+mulx r11, rcx, rbx
+adox r8, rdi
+mulx rdi, rbx, r8
+inc r12
+adox rcx, r9
+mulx r8, r9, rax
+adcx r15, rcx
+adox r9, r13
+adcx r11, r9
+adox rbx, r14
+adox rdi, r12
+adcx r8, rbx
+adc rdi, 0x0
+mulx r14, r13, rdi
+test al, al
+mov rdi, [ rsp - 0x48 ]
+adox r13, r10
+mov r14, r12
+adox r14, r15
+mov [ rdi + 0x8 ], r14
+mov rax, r12
+adox rax, r11
+mov r10, r12
+adox r10, r8
+mov [ rdi + 0x10 ], rax
+mov rcx, r12
+cmovo rcx, rdx
+adcx r13, rcx
+mov [ rdi + 0x0 ], r13
+mov [ rdi + 0x18 ], r10
+mov rbx, [ rsp - 0x80 ]
+mov r12, [ rsp - 0x70 ]
+mov r13, [ rsp - 0x68 ]
+mov r14, [ rsp - 0x60 ]
+mov r15, [ rsp - 0x58 ]
+
+mov rbp, [rsp - 0x08]
+ret
+.cfi_endproc
diff --git a/third_party/fiat/curve25519_64_adx.h b/third_party/fiat/curve25519_64_adx.h
new file mode 100644
index 0000000..af9a9f8
--- /dev/null
+++ b/third_party/fiat/curve25519_64_adx.h
@@ -0,0 +1,528 @@
+#include <stdint.h>
+#include <immintrin.h>
+#include <string.h>
+
+typedef uint64_t fe4[4];
+typedef uint8_t fiat_uint1;
+typedef int8_t fiat_int1;
+
+static __inline__ uint64_t fiat_value_barrier_u64(uint64_t a) {
+ __asm__("" : "+r"(a) : /* no inputs */);
+ return a;
+}
+static inline void fe4_mul(fe4 out, const fe4 x, const fe4 y) { fiat_curve25519_adx_mul(out, x, y); }
+static inline void fe4_sq(fe4 out, const fe4 x) { fiat_curve25519_adx_square(out, x); }
+
+/*
+ * The function fiat_mulx_u64 is a multiplication, returning the full double-width result.
+ *
+ * Postconditions:
+ * out1 = (arg1 * arg2) mod 2^64
+ * out2 = ⌊arg1 * arg2 / 2^64⌋
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0xffffffffffffffff]
+ * arg2: [0x0 ~> 0xffffffffffffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0xffffffffffffffff]
+ * out2: [0x0 ~> 0xffffffffffffffff]
+ */
+static inline void fiat_mulx_u64(uint64_t* out1, uint64_t* out2, uint64_t arg1, uint64_t arg2) {
+// NOTE: edited after generation
+#if defined(_M_X64)
+ unsigned long long t;
+ *out1 = _umul128(arg1, arg2, &t);
+ *out2 = t;
+#elif defined(_M_ARM64)
+ *out1 = arg1 * arg2;
+ *out2 = __umulh(arg1, arg2);
+#else
+ unsigned __int128 t = (unsigned __int128)arg1 * arg2;
+ *out1 = t;
+ *out2 = (t >> 64);
+#endif
+}
+
+/*
+ * The function fiat_addcarryx_u64 is an addition with carry.
+ *
+ * Postconditions:
+ * out1 = (arg1 + arg2 + arg3) mod 2^64
+ * out2 = ⌊(arg1 + arg2 + arg3) / 2^64⌋
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0xffffffffffffffff]
+ * arg3: [0x0 ~> 0xffffffffffffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0xffffffffffffffff]
+ * out2: [0x0 ~> 0x1]
+ */
+static inline void fiat_addcarryx_u64(uint64_t* out1, fiat_uint1* out2, fiat_uint1 arg1, uint64_t arg2, uint64_t arg3) {
+// NOTE: edited after generation
+#if defined(__has_builtin)
+# if __has_builtin(__builtin_ia32_addcarryx_u64)
+# define addcarry64 __builtin_ia32_addcarryx_u64
+# endif
+#endif
+#if defined(addcarry64)
+ long long unsigned int t;
+ *out2 = addcarry64(arg1, arg2, arg3, &t);
+ *out1 = t;
+#elif defined(_M_X64)
+ long long unsigned int t;
+ *out2 = _addcarry_u64(arg1, arg2, arg3, out1);
+ *out1 = t;
+#else
+ arg2 += arg1;
+ arg1 = arg2 < arg1;
+ uint64_t ret = arg2 + arg3;
+ arg1 += ret < arg2;
+ *out1 = ret;
+ *out2 = arg1;
+#endif
+#undef addcarry64
+}
+
+/*
+ * The function fiat_subborrowx_u64 is a subtraction with borrow.
+ *
+ * Postconditions:
+ * out1 = (-arg1 + arg2 + -arg3) mod 2^64
+ * out2 = -⌊(-arg1 + arg2 + -arg3) / 2^64⌋
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0xffffffffffffffff]
+ * arg3: [0x0 ~> 0xffffffffffffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0xffffffffffffffff]
+ * out2: [0x0 ~> 0x1]
+ */
+static inline void fiat_subborrowx_u64(uint64_t* out1, fiat_uint1* out2, fiat_uint1 arg1, uint64_t arg2, uint64_t arg3) {
+#if defined(__has_builtin)
+# if __has_builtin(__builtin_ia32_subborrow_u64)
+# define subborrow64 __builtin_ia32_subborrow_u64
+# endif
+#endif
+#if defined(subborrow64)
+ long long unsigned int t;
+ *out2 = subborrow64(arg1, arg2, arg3, &t);
+ *out1 = t;
+#elif defined(_M_X64)
+ long long unsigned int t;
+ *out2 = _subborrow_u64(arg1, arg2, arg3, &t); // NOTE: edited after generation
+ *out1 = t;
+#else
+ *out1 = arg2 - arg3 - arg1;
+ *out2 = (arg2 < arg3) | ((arg2 == arg3) & arg1);
+#endif
+#undef subborrow64
+}
+
+/*
+ * The function fiat_cmovznz_u64 is a single-word conditional move.
+ *
+ * Postconditions:
+ * out1 = (if arg1 = 0 then arg2 else arg3)
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0xffffffffffffffff]
+ * arg3: [0x0 ~> 0xffffffffffffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0xffffffffffffffff]
+ */
+static inline void fiat_cmovznz_u64(uint64_t* out1, fiat_uint1 arg1, uint64_t arg2, uint64_t arg3) {
+ fiat_uint1 x1;
+ uint64_t x2;
+ uint64_t x3;
+ x1 = (!(!arg1));
+ x2 = ((fiat_int1)(0x0 - x1) & UINT64_C(0xffffffffffffffff));
+ x3 = ((fiat_value_barrier_u64(x2) & arg3) | (fiat_value_barrier_u64((~x2)) & arg2));
+ *out1 = x3;
+}
+
+/*
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ * arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ */
+static void fe4_add(uint64_t out1[4], const uint64_t arg1[4], const uint64_t arg2[4]) {
+ uint64_t x1;
+ fiat_uint1 x2;
+ uint64_t x3;
+ fiat_uint1 x4;
+ uint64_t x5;
+ fiat_uint1 x6;
+ uint64_t x7;
+ fiat_uint1 x8;
+ uint64_t x9;
+ uint64_t x10;
+ fiat_uint1 x11;
+ uint64_t x12;
+ fiat_uint1 x13;
+ uint64_t x14;
+ fiat_uint1 x15;
+ uint64_t x16;
+ fiat_uint1 x17;
+ uint64_t x18;
+ uint64_t x19;
+ fiat_uint1 x20;
+ fiat_addcarryx_u64(&x1, &x2, 0x0, (arg1[0]), (arg2[0]));
+ fiat_addcarryx_u64(&x3, &x4, x2, (arg1[1]), (arg2[1]));
+ fiat_addcarryx_u64(&x5, &x6, x4, (arg1[2]), (arg2[2]));
+ fiat_addcarryx_u64(&x7, &x8, x6, (arg1[3]), (arg2[3]));
+ fiat_cmovznz_u64(&x9, x8, 0x0, UINT8_C(0x26)); // NOTE: clang 14 for Zen 2 uses sbb, and
+ fiat_addcarryx_u64(&x10, &x11, 0x0, x1, x9);
+ fiat_addcarryx_u64(&x12, &x13, x11, x3, 0x0);
+ fiat_addcarryx_u64(&x14, &x15, x13, x5, 0x0);
+ fiat_addcarryx_u64(&x16, &x17, x15, x7, 0x0);
+ fiat_cmovznz_u64(&x18, x17, 0x0, UINT8_C(0x26)); // NOTE: clang 14 for Zen 2 uses sbb, and
+ fiat_addcarryx_u64(&x19, &x20, 0x0, x10, x18);
+ out1[0] = x19;
+ out1[1] = x12;
+ out1[2] = x14;
+ out1[3] = x16;
+}
+
+/*
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ * arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ */
+static void fe4_sub(uint64_t out1[4], const uint64_t arg1[4], const uint64_t arg2[4]) {
+ uint64_t x1;
+ uint64_t x2;
+ fiat_uint1 x3;
+ uint64_t x4;
+ uint64_t x5;
+ fiat_uint1 x6;
+ uint64_t x7;
+ uint64_t x8;
+ fiat_uint1 x9;
+ uint64_t x10;
+ uint64_t x11;
+ fiat_uint1 x12;
+ uint64_t x13;
+ uint64_t x14;
+ fiat_uint1 x15;
+ uint64_t x16;
+ fiat_uint1 x17;
+ uint64_t x18;
+ fiat_uint1 x19;
+ uint64_t x20;
+ fiat_uint1 x21;
+ uint64_t x22;
+ uint64_t x23;
+ fiat_uint1 x24;
+ x1 = (arg2[0]);
+ fiat_subborrowx_u64(&x2, &x3, 0x0, (arg1[0]), x1);
+ x4 = (arg2[1]);
+ fiat_subborrowx_u64(&x5, &x6, x3, (arg1[1]), x4);
+ x7 = (arg2[2]);
+ fiat_subborrowx_u64(&x8, &x9, x6, (arg1[2]), x7);
+ x10 = (arg2[3]);
+ fiat_subborrowx_u64(&x11, &x12, x9, (arg1[3]), x10);
+ fiat_cmovznz_u64(&x13, x12, 0x0, UINT8_C(0x26)); // NOTE: clang 14 for Zen 2 uses sbb, and
+ fiat_subborrowx_u64(&x14, &x15, 0x0, x2, x13);
+ fiat_subborrowx_u64(&x16, &x17, x15, x5, 0x0);
+ fiat_subborrowx_u64(&x18, &x19, x17, x8, 0x0);
+ fiat_subborrowx_u64(&x20, &x21, x19, x11, 0x0);
+ fiat_cmovznz_u64(&x22, x21, 0x0, UINT8_C(0x26)); // NOTE: clang 14 for Zen 2 uses sbb, and
+ fiat_subborrowx_u64(&x23, &x24, 0x0, x14, x22);
+ out1[0] = x23;
+ out1[1] = x16;
+ out1[2] = x18;
+ out1[3] = x20;
+}
+
+/*
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ * arg2: [0x0 ~> 0x3ffffffffffffff] // NOTE: this is not any uint64!
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ */
+static void fe4_scmul(uint64_t out1[4], const uint64_t arg1[4], uint64_t arg2) {
+ uint64_t x1;
+ uint64_t x2;
+ uint64_t x3;
+ uint64_t x4;
+ uint64_t x5;
+ fiat_uint1 x6;
+ uint64_t x7;
+ uint64_t x8;
+ uint64_t x9;
+ fiat_uint1 x10;
+ uint64_t x11;
+ uint64_t x12;
+ uint64_t x13;
+ fiat_uint1 x14;
+ uint64_t x15;
+ uint64_t x16;
+ uint64_t x17;
+ fiat_uint1 x18;
+ uint64_t x19;
+ fiat_uint1 x20;
+ uint64_t x21;
+ fiat_uint1 x22;
+ uint64_t x23;
+ fiat_uint1 x24;
+ uint64_t x25;
+ uint64_t x26;
+ fiat_uint1 x27;
+ fiat_mulx_u64(&x1, &x2, (arg1[0]), arg2);
+ fiat_mulx_u64(&x3, &x4, (arg1[1]), arg2);
+ fiat_addcarryx_u64(&x5, &x6, 0x0, x2, x3);
+ fiat_mulx_u64(&x7, &x8, (arg1[2]), arg2);
+ fiat_addcarryx_u64(&x9, &x10, x6, x4, x7);
+ fiat_mulx_u64(&x11, &x12, (arg1[3]), arg2);
+ fiat_addcarryx_u64(&x13, &x14, x10, x8, x11);
+ fiat_mulx_u64(&x15, &x16, (x12 + (uint64_t)x14), UINT8_C(0x26));
+ fiat_addcarryx_u64(&x17, &x18, 0x0, x1, x15);
+ fiat_addcarryx_u64(&x19, &x20, x18, x5, 0x0);
+ fiat_addcarryx_u64(&x21, &x22, x20, x9, 0x0);
+ fiat_addcarryx_u64(&x23, &x24, x22, x13, 0x0);
+ fiat_cmovznz_u64(&x25, x24, 0x0, UINT8_C(0x26)); // NOTE: clang 14 for Zen 2 uses sbb, and
+ fiat_addcarryx_u64(&x26, &x27, 0x0, x17, x25);
+ out1[0] = x26;
+ out1[1] = x19;
+ out1[2] = x21;
+ out1[3] = x23;
+}
+
+/*
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ */
+static void fe4_canon(uint64_t out1[4], const uint64_t arg1[4]) {
+ uint64_t x1;
+ fiat_uint1 x2;
+ uint64_t x3;
+ fiat_uint1 x4;
+ uint64_t x5;
+ fiat_uint1 x6;
+ uint64_t x7;
+ fiat_uint1 x8;
+ uint64_t x9;
+ uint64_t x10;
+ uint64_t x11;
+ uint64_t x12;
+ uint64_t x13;
+ fiat_uint1 x14;
+ uint64_t x15;
+ fiat_uint1 x16;
+ uint64_t x17;
+ fiat_uint1 x18;
+ uint64_t x19;
+ fiat_uint1 x20;
+ uint64_t x21;
+ uint64_t x22;
+ uint64_t x23;
+ uint64_t x24;
+ fiat_subborrowx_u64(&x1, &x2, 0x0, (arg1[0]), UINT64_C(0xffffffffffffffed));
+ fiat_subborrowx_u64(&x3, &x4, x2, (arg1[1]), UINT64_C(0xffffffffffffffff));
+ fiat_subborrowx_u64(&x5, &x6, x4, (arg1[2]), UINT64_C(0xffffffffffffffff));
+ fiat_subborrowx_u64(&x7, &x8, x6, (arg1[3]), UINT64_C(0x7fffffffffffffff));
+ fiat_cmovznz_u64(&x9, x8, x1, (arg1[0]));
+ fiat_cmovznz_u64(&x10, x8, x3, (arg1[1]));
+ fiat_cmovznz_u64(&x11, x8, x5, (arg1[2]));
+ fiat_cmovznz_u64(&x12, x8, x7, (arg1[3]));
+ fiat_subborrowx_u64(&x13, &x14, 0x0, x9, UINT64_C(0xffffffffffffffed));
+ fiat_subborrowx_u64(&x15, &x16, x14, x10, UINT64_C(0xffffffffffffffff));
+ fiat_subborrowx_u64(&x17, &x18, x16, x11, UINT64_C(0xffffffffffffffff));
+ fiat_subborrowx_u64(&x19, &x20, x18, x12, UINT64_C(0x7fffffffffffffff));
+ fiat_cmovznz_u64(&x21, x20, x13, x9);
+ fiat_cmovznz_u64(&x22, x20, x15, x10);
+ fiat_cmovznz_u64(&x23, x20, x17, x11);
+ fiat_cmovznz_u64(&x24, x20, x19, x12);
+ out1[0] = x21;
+ out1[1] = x22;
+ out1[2] = x23;
+ out1[3] = x24;
+}
+
+/*
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ * arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ * out2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+ */
+static void fe4_cswap(uint64_t out1[4], uint64_t out2[4], fiat_uint1 arg1, const uint64_t arg2[4], const uint64_t arg3[4]) {
+ uint64_t x1;
+ uint64_t x2;
+ uint64_t x3;
+ uint64_t x4;
+ uint64_t x5;
+ uint64_t x6;
+ uint64_t x7;
+ uint64_t x8;
+ // NOTE: clang 14 for Zen 2 uses YMM registers
+ fiat_cmovznz_u64(&x1, arg1, (arg2[0]), (arg3[0]));
+ fiat_cmovznz_u64(&x2, arg1, (arg2[1]), (arg3[1]));
+ fiat_cmovznz_u64(&x3, arg1, (arg2[2]), (arg3[2]));
+ fiat_cmovznz_u64(&x4, arg1, (arg2[3]), (arg3[3]));
+ fiat_cmovznz_u64(&x5, arg1, (arg3[0]), (arg2[0]));
+ fiat_cmovznz_u64(&x6, arg1, (arg3[1]), (arg2[1]));
+ fiat_cmovznz_u64(&x7, arg1, (arg3[2]), (arg2[2]));
+ fiat_cmovznz_u64(&x8, arg1, (arg3[3]), (arg2[3]));
+ out1[0] = x1;
+ out1[1] = x2;
+ out1[2] = x3;
+ out1[3] = x4;
+ out2[0] = x5;
+ out2[1] = x6;
+ out2[2] = x7;
+ out2[3] = x8;
+}
+
+// The following functions are adaped from crypto/curve25519/curve25519.c
+// It would be desirable to share the code, but with the current field
+// implementations both 4-limb and 5-limb versions of the curve-level code need
+// to be included in builds targetting an unknown variant of x86_64.
+
+static void fe4_invert(fe4 out, const fe4 z) {
+ fe4 t0;
+ fe4 t1;
+ fe4 t2;
+ fe4 t3;
+ int i;
+
+ fe4_sq(t0, z);
+ fe4_sq(t1, t0);
+ for (i = 1; i < 2; ++i) {
+ fe4_sq(t1, t1);
+ }
+ fe4_mul(t1, z, t1);
+ fe4_mul(t0, t0, t1);
+ fe4_sq(t2, t0);
+ fe4_mul(t1, t1, t2);
+ fe4_sq(t2, t1);
+ for (i = 1; i < 5; ++i) {
+ fe4_sq(t2, t2);
+ }
+ fe4_mul(t1, t2, t1);
+ fe4_sq(t2, t1);
+ for (i = 1; i < 10; ++i) {
+ fe4_sq(t2, t2);
+ }
+ fe4_mul(t2, t2, t1);
+ fe4_sq(t3, t2);
+ for (i = 1; i < 20; ++i) {
+ fe4_sq(t3, t3);
+ }
+ fe4_mul(t2, t3, t2);
+ fe4_sq(t2, t2);
+ for (i = 1; i < 10; ++i) {
+ fe4_sq(t2, t2);
+ }
+ fe4_mul(t1, t2, t1);
+ fe4_sq(t2, t1);
+ for (i = 1; i < 50; ++i) {
+ fe4_sq(t2, t2);
+ }
+ fe4_mul(t2, t2, t1);
+ fe4_sq(t3, t2);
+ for (i = 1; i < 100; ++i) {
+ fe4_sq(t3, t3);
+ }
+ fe4_mul(t2, t3, t2);
+ fe4_sq(t2, t2);
+ for (i = 1; i < 50; ++i) {
+ fe4_sq(t2, t2);
+ }
+ fe4_mul(t1, t2, t1);
+ fe4_sq(t1, t1);
+ for (i = 1; i < 5; ++i) {
+ fe4_sq(t1, t1);
+ }
+ fe4_mul(out, t1, t0);
+}
+
+void x25519_scalar_mult_adx(uint8_t out[32], const uint8_t scalar[32],
+ const uint8_t point[32]) {
+ uint8_t e[32];
+ memcpy(e, scalar, 32);
+ e[0] &= 248;
+ e[31] &= 127;
+ e[31] |= 64;
+
+ // The following implementation was transcribed to Coq and proven to
+ // correspond to unary scalar multiplication in affine coordinates given that
+ // x1 != 0 is the x coordinate of some point on the curve. It was also checked
+ // in Coq that doing a ladderstep with x1 = x3 = 0 gives z2' = z3' = 0, and z2
+ // = z3 = 0 gives z2' = z3' = 0. The statement was quantified over the
+ // underlying field, so it applies to Curve25519 itself and the quadratic
+ // twist of Curve25519. It was not proven in Coq that prime-field arithmetic
+ // correctly simulates extension-field arithmetic on prime-field values.
+ // The decoding of the byte array representation of e was not considered.
+ // Specification of Montgomery curves in affine coordinates:
+ // <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Spec/MontgomeryCurve.v#L27>
+ // Proof that these form a group that is isomorphic to a Weierstrass curve:
+ // <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/AffineProofs.v#L35>
+ // Coq transcription and correctness proof of the loop (where scalarbits=255):
+ // <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZ.v#L118>
+ // <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L278>
+ // preconditions: 0 <= e < 2^255 (not necessarily e < order), fe_invert(0) = 0
+ fe4 x1, x2 = {1}, z2 = {0}, x3, z3 = {1}, tmp0, tmp1;
+ OPENSSL_memcpy(x1, point, sizeof(fe4));
+ x1[3] &= (uint64_t)(-1)>>1;
+ OPENSSL_memcpy(x3, x1, sizeof(fe4));
+
+ unsigned swap = 0;
+ int pos;
+ for (pos = 254; pos >= 0; --pos) {
+ // loop invariant as of right before the test, for the case where x1 != 0:
+ // pos >= -1; if z2 = 0 then x2 is nonzero; if z3 = 0 then x3 is nonzero
+ // let r := e >> (pos+1) in the following equalities of projective points:
+ // to_xz (r*P) === if swap then (x3, z3) else (x2, z2)
+ // to_xz ((r+1)*P) === if swap then (x2, z2) else (x3, z3)
+ // x1 is the nonzero x coordinate of the nonzero point (r*P-(r+1)*P)
+ unsigned b = 1 & (e[pos / 8] >> (pos & 7));
+ swap ^= b;
+ fe4_cswap(x2, x3, swap, x2, x3);
+ fe4_cswap(z2, z3, swap, z2, z3);
+ swap = b;
+ // Coq transcription of ladderstep formula (called from transcribed loop):
+ // <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZ.v#L89>
+ // <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L131>
+ // x1 != 0 <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L217>
+ // x1 = 0 <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L147>
+ fe4_sub(tmp0, x3, z3);
+ fe4_sub(tmp1, x2, z2);
+ fe4_add(x2, x2, z2);
+ fe4_add(z2, x3, z3);
+ fe4_mul(z3, tmp0, x2);
+ fe4_mul(z2, z2, tmp1);
+ fe4_sq(tmp0, tmp1);
+ fe4_sq(tmp1, x2);
+ fe4_add(x3, z3, z2);
+ fe4_sub(z2, z3, z2);
+ fe4_mul(x2, tmp1, tmp0);
+ fe4_sub(tmp1, tmp1, tmp0);
+ fe4_sq(z2, z2);
+ fe4_scmul(z3, tmp1, 121666);
+ fe4_sq(x3, x3);
+ fe4_add(tmp0, tmp0, z3);
+ fe4_mul(z3, x1, z2);
+ fe4_mul(z2, tmp1, tmp0);
+ }
+ // here pos=-1, so r=e, so to_xz (e*P) === if swap then (x3, z3) else (x2, z2)
+ fe4_cswap(x2, x3, swap, x2, x3);
+ fe4_cswap(z2, z3, swap, z2, z3);
+
+ fe4_invert(z2, z2);
+ fe4_mul(x2, x2, z2);
+ fe4_canon(x2, x2);
+ OPENSSL_memcpy(out, x2, sizeof(fe4));
+}