mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-28 05:35:52 +03:00
LibCrypto+LibTLS: Switch to the generic SECPxxxr1
implementation
This commit is contained in:
parent
59c22c0349
commit
bc9cdd4394
Notes:
sideshowbarker
2024-07-17 01:06:10 +09:00
Author: https://github.com/msvisser Commit: https://github.com/SerenityOS/serenity/commit/bc9cdd4394 Pull-request: https://github.com/SerenityOS/serenity/pull/21949 Reviewed-by: https://github.com/DanShaders Reviewed-by: https://github.com/alimpfard
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <LibCrypto/Curves/SECP256r1.h>
|
||||
#include <LibCrypto/Curves/SECPxxxr1.h>
|
||||
#include <LibCrypto/Curves/X25519.h>
|
||||
#include <LibCrypto/Curves/X448.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
|
@ -23,8 +23,6 @@ set(SOURCES
|
||||
Cipher/ChaCha20.cpp
|
||||
Curves/Curve25519.cpp
|
||||
Curves/Ed25519.cpp
|
||||
Curves/SECP256r1.cpp
|
||||
Curves/SECP384r1.cpp
|
||||
Curves/X25519.cpp
|
||||
Curves/X448.cpp
|
||||
Hash/BLAKE2b.cpp
|
||||
|
@ -1,625 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Michiel Visser <opensource@webmichiel.nl>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteReader.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/UFixedBigInt.h>
|
||||
#include <AK/UFixedBigIntDivision.h>
|
||||
#include <LibCrypto/ASN1/DER.h>
|
||||
#include <LibCrypto/Curves/SECP256r1.h>
|
||||
|
||||
namespace Crypto::Curves {
|
||||
|
||||
struct JacobianPoint {
|
||||
u256 x { 0u };
|
||||
u256 y { 0u };
|
||||
u256 z { 0u };
|
||||
};
|
||||
|
||||
static constexpr u256 calculate_modular_inverse_mod_r(u256 value)
|
||||
{
|
||||
// Calculate the modular multiplicative inverse of value mod 2^256 using the extended euclidean algorithm
|
||||
u512 old_r = value;
|
||||
u512 r = static_cast<u512>(1u) << 256u;
|
||||
u512 old_s = 1u;
|
||||
u512 s = 0u;
|
||||
|
||||
while (!r.is_zero_constant_time()) {
|
||||
u512 quotient = old_r / r;
|
||||
u512 temp = r;
|
||||
r = old_r - quotient * r;
|
||||
old_r = temp;
|
||||
|
||||
temp = s;
|
||||
s = old_s - quotient * s;
|
||||
old_s = temp;
|
||||
}
|
||||
|
||||
return old_s.low();
|
||||
}
|
||||
|
||||
static constexpr u256 calculate_r2_mod(u256 modulus)
|
||||
{
|
||||
// Calculate the value of R^2 mod modulus, where R = 2^256
|
||||
u1024 r = static_cast<u1024>(1u) << 256u;
|
||||
u1024 r2 = r * r;
|
||||
u1024 result = r2 % static_cast<u1024>(modulus);
|
||||
return result.low().low();
|
||||
}
|
||||
|
||||
// SECP256r1 curve parameters
|
||||
static constexpr u256 PRIME { { 0xffffffffffffffffull, 0x00000000ffffffffull, 0x0000000000000000ull, 0xffffffff00000001ull } };
|
||||
static constexpr u256 A { { 0xfffffffffffffffcull, 0x00000000ffffffffull, 0x0000000000000000ull, 0xffffffff00000001ull } };
|
||||
static constexpr u256 B { { 0x3bce3c3e27d2604bull, 0x651d06b0cc53b0f6ull, 0xb3ebbd55769886bcull, 0x5ac635d8aa3a93e7ull } };
|
||||
static constexpr u256 ORDER { { 0xf3b9cac2fc632551ull, 0xbce6faada7179e84ull, 0xffffffffffffffffull, 0xffffffff00000000ull } };
|
||||
|
||||
// Verify that A = -3 mod p, which is required for some optimizations
|
||||
static_assert(A == PRIME - 3);
|
||||
|
||||
// Precomputed helper values for reduction and Montgomery multiplication
|
||||
static constexpr u256 REDUCE_PRIME = u256 { 0 } - PRIME;
|
||||
static constexpr u256 REDUCE_ORDER = u256 { 0 } - ORDER;
|
||||
static constexpr u256 PRIME_INVERSE_MOD_R = u256 { 0 } - calculate_modular_inverse_mod_r(PRIME);
|
||||
static constexpr u256 ORDER_INVERSE_MOD_R = u256 { 0 } - calculate_modular_inverse_mod_r(ORDER);
|
||||
static constexpr u256 R2_MOD_PRIME = calculate_r2_mod(PRIME);
|
||||
static constexpr u256 R2_MOD_ORDER = calculate_r2_mod(ORDER);
|
||||
|
||||
static u256 import_big_endian(ReadonlyBytes data)
|
||||
{
|
||||
VERIFY(data.size() == 32);
|
||||
|
||||
u64 d = AK::convert_between_host_and_big_endian(ByteReader::load64(data.offset_pointer(0 * sizeof(u64))));
|
||||
u64 c = AK::convert_between_host_and_big_endian(ByteReader::load64(data.offset_pointer(1 * sizeof(u64))));
|
||||
u64 b = AK::convert_between_host_and_big_endian(ByteReader::load64(data.offset_pointer(2 * sizeof(u64))));
|
||||
u64 a = AK::convert_between_host_and_big_endian(ByteReader::load64(data.offset_pointer(3 * sizeof(u64))));
|
||||
|
||||
return u256 { { a, b, c, d } };
|
||||
}
|
||||
|
||||
static void export_big_endian(u256 const& value, Bytes data)
|
||||
{
|
||||
u64 a = AK::convert_between_host_and_big_endian(value.low().low());
|
||||
u64 b = AK::convert_between_host_and_big_endian(value.low().high());
|
||||
u64 c = AK::convert_between_host_and_big_endian(value.high().low());
|
||||
u64 d = AK::convert_between_host_and_big_endian(value.high().high());
|
||||
|
||||
ByteReader::store(data.offset_pointer(3 * sizeof(u64)), a);
|
||||
ByteReader::store(data.offset_pointer(2 * sizeof(u64)), b);
|
||||
ByteReader::store(data.offset_pointer(1 * sizeof(u64)), c);
|
||||
ByteReader::store(data.offset_pointer(0 * sizeof(u64)), d);
|
||||
}
|
||||
|
||||
static constexpr u256 select(u256 const& left, u256 const& right, bool condition)
|
||||
{
|
||||
// If condition = 0 return left else right
|
||||
u256 mask = (u256)condition - 1;
|
||||
|
||||
return (left & mask) | (right & ~mask);
|
||||
}
|
||||
|
||||
static constexpr u512 multiply(u256 const& left, u256 const& right)
|
||||
{
|
||||
return left.wide_multiply(right);
|
||||
}
|
||||
|
||||
static constexpr u256 modular_reduce(u256 const& value)
|
||||
{
|
||||
// Add -prime % 2^256 = 2^224-2^192-2^96+1
|
||||
bool carry = false;
|
||||
u256 other = value.addc(REDUCE_PRIME, carry);
|
||||
|
||||
// Check for overflow
|
||||
return select(value, other, carry);
|
||||
}
|
||||
|
||||
static constexpr u256 modular_reduce_order(u256 const& value)
|
||||
{
|
||||
// Add -order % 2^256
|
||||
bool carry = false;
|
||||
u256 other = value.addc(REDUCE_ORDER, carry);
|
||||
|
||||
// Check for overflow
|
||||
return select(value, other, carry);
|
||||
}
|
||||
|
||||
static constexpr u256 modular_add(u256 const& left, u256 const& right, bool carry_in = false)
|
||||
{
|
||||
bool carry = carry_in;
|
||||
u256 output = left.addc(right, carry);
|
||||
|
||||
// If there is a carry, subtract p by adding 2^256 - p
|
||||
u256 addend = select(0u, REDUCE_PRIME, carry);
|
||||
carry = false;
|
||||
output = output.addc(addend, carry);
|
||||
|
||||
// If there is still a carry, subtract p by adding 2^256 - p
|
||||
addend = select(0u, REDUCE_PRIME, carry);
|
||||
return output + addend;
|
||||
}
|
||||
|
||||
static constexpr u256 modular_sub(u256 const& left, u256 const& right)
|
||||
{
|
||||
bool borrow = false;
|
||||
u256 output = left.subc(right, borrow);
|
||||
|
||||
// If there is a borrow, add p by subtracting 2^256 - p
|
||||
u256 sub = select(0u, REDUCE_PRIME, borrow);
|
||||
borrow = false;
|
||||
output = output.subc(sub, borrow);
|
||||
|
||||
// If there is still a borrow, add p by subtracting 2^256 - p
|
||||
sub = select(0u, REDUCE_PRIME, borrow);
|
||||
return output - sub;
|
||||
}
|
||||
|
||||
static constexpr u256 modular_multiply(u256 const& left, u256 const& right)
|
||||
{
|
||||
// Modular multiplication using the Montgomery method: https://en.wikipedia.org/wiki/Montgomery_modular_multiplication
|
||||
// This requires that the inputs to this function are in Montgomery form.
|
||||
|
||||
// T = left * right
|
||||
u512 mult = multiply(left, right);
|
||||
|
||||
// m = ((T mod R) * curve_p')
|
||||
u512 m = multiply(mult.low(), PRIME_INVERSE_MOD_R);
|
||||
|
||||
// mp = (m mod R) * curve_p
|
||||
u512 mp = multiply(m.low(), PRIME);
|
||||
|
||||
// t = (T + mp)
|
||||
bool carry = false;
|
||||
mult.low().addc(mp.low(), carry);
|
||||
|
||||
// output = t / R
|
||||
return modular_add(mult.high(), mp.high(), carry);
|
||||
}
|
||||
|
||||
static constexpr u256 modular_square(u256 const& value)
|
||||
{
|
||||
return modular_multiply(value, value);
|
||||
}
|
||||
|
||||
static constexpr u256 to_montgomery(u256 const& value)
|
||||
{
|
||||
return modular_multiply(value, R2_MOD_PRIME);
|
||||
}
|
||||
|
||||
static constexpr u256 from_montgomery(u256 const& value)
|
||||
{
|
||||
return modular_multiply(value, 1u);
|
||||
}
|
||||
|
||||
static constexpr u256 modular_inverse(u256 const& value)
|
||||
{
|
||||
// Modular inverse modulo the curve prime can be computed using Fermat's little theorem: a^(p-2) mod p = a^-1 mod p.
|
||||
// Calculating a^(p-2) mod p can be done using the square-and-multiply exponentiation method, as p-2 is constant.
|
||||
u256 base = value;
|
||||
u256 result = to_montgomery(1u);
|
||||
u256 prime_minus_2 = PRIME - 2u;
|
||||
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
if ((prime_minus_2 & 1u) == 1u) {
|
||||
result = modular_multiply(result, base);
|
||||
}
|
||||
base = modular_square(base);
|
||||
prime_minus_2 >>= 1u;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static constexpr u256 modular_add_order(u256 const& left, u256 const& right, bool carry_in = false)
|
||||
{
|
||||
bool carry = carry_in;
|
||||
u256 output = left.addc(right, carry);
|
||||
|
||||
// If there is a carry, subtract n by adding 2^256 - n
|
||||
u256 addend = select(0u, REDUCE_ORDER, carry);
|
||||
carry = false;
|
||||
output = output.addc(addend, carry);
|
||||
|
||||
// If there is still a carry, subtract n by adding 2^256 - n
|
||||
addend = select(0u, REDUCE_ORDER, carry);
|
||||
return output + addend;
|
||||
}
|
||||
|
||||
static constexpr u256 modular_multiply_order(u256 const& left, u256 const& right)
|
||||
{
|
||||
// Modular multiplication using the Montgomery method: https://en.wikipedia.org/wiki/Montgomery_modular_multiplication
|
||||
// This requires that the inputs to this function are in Montgomery form.
|
||||
|
||||
// T = left * right
|
||||
u512 mult = multiply(left, right);
|
||||
|
||||
// m = ((T mod R) * curve_n')
|
||||
u512 m = multiply(mult.low(), ORDER_INVERSE_MOD_R);
|
||||
|
||||
// mp = (m mod R) * curve_n
|
||||
u512 mp = multiply(m.low(), ORDER);
|
||||
|
||||
// t = (T + mp)
|
||||
bool carry = false;
|
||||
mult.low().addc(mp.low(), carry);
|
||||
|
||||
// output = t / R
|
||||
return modular_add_order(mult.high(), mp.high(), carry);
|
||||
}
|
||||
|
||||
static constexpr u256 modular_square_order(u256 const& value)
|
||||
{
|
||||
return modular_multiply_order(value, value);
|
||||
}
|
||||
|
||||
static constexpr u256 to_montgomery_order(u256 const& value)
|
||||
{
|
||||
return modular_multiply_order(value, R2_MOD_ORDER);
|
||||
}
|
||||
|
||||
static constexpr u256 from_montgomery_order(u256 const& value)
|
||||
{
|
||||
return modular_multiply_order(value, 1u);
|
||||
}
|
||||
|
||||
static constexpr u256 modular_inverse_order(u256 const& value)
|
||||
{
|
||||
// Modular inverse modulo the curve order can be computed using Fermat's little theorem: a^(n-2) mod n = a^-1 mod n.
|
||||
// Calculating a^(n-2) mod n can be done using the square-and-multiply exponentiation method, as n-2 is constant.
|
||||
u256 base = value;
|
||||
u256 result = to_montgomery_order(1u);
|
||||
u256 order_minus_2 = ORDER - 2u;
|
||||
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
if ((order_minus_2 & 1u) == 1u) {
|
||||
result = modular_multiply_order(result, base);
|
||||
}
|
||||
base = modular_square_order(base);
|
||||
order_minus_2 >>= 1u;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void point_double(JacobianPoint& output_point, JacobianPoint const& point)
|
||||
{
|
||||
// Based on "Point Doubling" from http://point-at-infinity.org/ecc/Prime_Curve_Jacobian_Coordinates.html
|
||||
|
||||
// if (Y == 0)
|
||||
// return POINT_AT_INFINITY
|
||||
if (point.y.is_zero_constant_time()) {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
u256 temp;
|
||||
|
||||
// Y2 = Y^2
|
||||
u256 y2 = modular_square(point.y);
|
||||
|
||||
// S = 4*X*Y2
|
||||
u256 s = modular_multiply(point.x, y2);
|
||||
s = modular_add(s, s);
|
||||
s = modular_add(s, s);
|
||||
|
||||
// M = 3*X^2 + a*Z^4 = 3*(X + Z^2)*(X - Z^2)
|
||||
// This specific equation from https://github.com/earlephilhower/bearssl-esp8266/blob/6105635531027f5b298aa656d44be2289b2d434f/src/ec/ec_p256_m64.c#L811-L816
|
||||
// This simplification only works because a = -3 mod p
|
||||
temp = modular_square(point.z);
|
||||
u256 m = modular_add(point.x, temp);
|
||||
temp = modular_sub(point.x, temp);
|
||||
m = modular_multiply(m, temp);
|
||||
temp = modular_add(m, m);
|
||||
m = modular_add(m, temp);
|
||||
|
||||
// X' = M^2 - 2*S
|
||||
u256 xp = modular_square(m);
|
||||
xp = modular_sub(xp, s);
|
||||
xp = modular_sub(xp, s);
|
||||
|
||||
// Y' = M*(S - X') - 8*Y2^2
|
||||
u256 yp = modular_sub(s, xp);
|
||||
yp = modular_multiply(yp, m);
|
||||
temp = modular_square(y2);
|
||||
temp = modular_add(temp, temp);
|
||||
temp = modular_add(temp, temp);
|
||||
temp = modular_add(temp, temp);
|
||||
yp = modular_sub(yp, temp);
|
||||
|
||||
// Z' = 2*Y*Z
|
||||
u256 zp = modular_multiply(point.y, point.z);
|
||||
zp = modular_add(zp, zp);
|
||||
|
||||
// return (X', Y', Z')
|
||||
output_point.x = xp;
|
||||
output_point.y = yp;
|
||||
output_point.z = zp;
|
||||
}
|
||||
|
||||
static void point_add(JacobianPoint& output_point, JacobianPoint const& point_a, JacobianPoint const& point_b)
|
||||
{
|
||||
// Based on "Point Addition" from http://point-at-infinity.org/ecc/Prime_Curve_Jacobian_Coordinates.html
|
||||
if (point_a.x.is_zero_constant_time() && point_a.y.is_zero_constant_time() && point_a.z.is_zero_constant_time()) {
|
||||
output_point.x = point_b.x;
|
||||
output_point.y = point_b.y;
|
||||
output_point.z = point_b.z;
|
||||
return;
|
||||
}
|
||||
|
||||
u256 temp;
|
||||
|
||||
temp = modular_square(point_b.z);
|
||||
// U1 = X1*Z2^2
|
||||
u256 u1 = modular_multiply(point_a.x, temp);
|
||||
// S1 = Y1*Z2^3
|
||||
u256 s1 = modular_multiply(point_a.y, temp);
|
||||
s1 = modular_multiply(s1, point_b.z);
|
||||
|
||||
temp = modular_square(point_a.z);
|
||||
// U2 = X2*Z1^2
|
||||
u256 u2 = modular_multiply(point_b.x, temp);
|
||||
// S2 = Y2*Z1^3
|
||||
u256 s2 = modular_multiply(point_b.y, temp);
|
||||
s2 = modular_multiply(s2, point_a.z);
|
||||
|
||||
// if (U1 == U2)
|
||||
// if (S1 != S2)
|
||||
// return POINT_AT_INFINITY
|
||||
// else
|
||||
// return POINT_DOUBLE(X1, Y1, Z1)
|
||||
if (u1.is_equal_to_constant_time(u2)) {
|
||||
if (s1.is_equal_to_constant_time(s2)) {
|
||||
point_double(output_point, point_a);
|
||||
return;
|
||||
} else {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
// H = U2 - U1
|
||||
u256 h = modular_sub(u2, u1);
|
||||
u256 h2 = modular_square(h);
|
||||
u256 h3 = modular_multiply(h2, h);
|
||||
// R = S2 - S1
|
||||
u256 r = modular_sub(s2, s1);
|
||||
// X3 = R^2 - H^3 - 2*U1*H^2
|
||||
u256 x3 = modular_square(r);
|
||||
x3 = modular_sub(x3, h3);
|
||||
temp = modular_multiply(u1, h2);
|
||||
temp = modular_add(temp, temp);
|
||||
x3 = modular_sub(x3, temp);
|
||||
// Y3 = R*(U1*H^2 - X3) - S1*H^3
|
||||
u256 y3 = modular_multiply(u1, h2);
|
||||
y3 = modular_sub(y3, x3);
|
||||
y3 = modular_multiply(y3, r);
|
||||
temp = modular_multiply(s1, h3);
|
||||
y3 = modular_sub(y3, temp);
|
||||
// Z3 = H*Z1*Z2
|
||||
u256 z3 = modular_multiply(h, point_a.z);
|
||||
z3 = modular_multiply(z3, point_b.z);
|
||||
// return (X3, Y3, Z3)
|
||||
output_point.x = x3;
|
||||
output_point.y = y3;
|
||||
output_point.z = z3;
|
||||
}
|
||||
|
||||
static void convert_jacobian_to_affine(JacobianPoint& point)
|
||||
{
|
||||
u256 temp;
|
||||
// X' = X/Z^2
|
||||
temp = modular_square(point.z);
|
||||
temp = modular_inverse(temp);
|
||||
point.x = modular_multiply(point.x, temp);
|
||||
// Y' = Y/Z^3
|
||||
temp = modular_square(point.z);
|
||||
temp = modular_multiply(temp, point.z);
|
||||
temp = modular_inverse(temp);
|
||||
point.y = modular_multiply(point.y, temp);
|
||||
// Z' = 1
|
||||
point.z = to_montgomery(1u);
|
||||
}
|
||||
|
||||
static bool is_point_on_curve(JacobianPoint const& point)
|
||||
{
|
||||
// This check requires the point to be in Montgomery form, with Z=1
|
||||
u256 temp, temp2;
|
||||
|
||||
// Calulcate Y^2 - X^3 - a*X - b = Y^2 - X^3 + 3*X - b
|
||||
temp = modular_square(point.y);
|
||||
temp2 = modular_square(point.x);
|
||||
temp2 = modular_multiply(temp2, point.x);
|
||||
temp = modular_sub(temp, temp2);
|
||||
temp = modular_add(temp, point.x);
|
||||
temp = modular_add(temp, point.x);
|
||||
temp = modular_add(temp, point.x);
|
||||
temp = modular_sub(temp, to_montgomery(B));
|
||||
temp = modular_reduce(temp);
|
||||
|
||||
return temp.is_zero_constant_time() && point.z.is_equal_to_constant_time(to_montgomery(1u));
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> SECP256r1::generate_private_key()
|
||||
{
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(32));
|
||||
fill_with_random(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> SECP256r1::generate_public_key(ReadonlyBytes a)
|
||||
{
|
||||
// clang-format off
|
||||
u8 generator_bytes[65] {
|
||||
0x04,
|
||||
0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2,
|
||||
0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96,
|
||||
0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16,
|
||||
0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5,
|
||||
};
|
||||
// clang-format on
|
||||
return compute_coordinate(a, { generator_bytes, 65 });
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> SECP256r1::compute_coordinate(ReadonlyBytes scalar_bytes, ReadonlyBytes point_bytes)
|
||||
{
|
||||
VERIFY(scalar_bytes.size() == 32);
|
||||
|
||||
u256 scalar = import_big_endian(scalar_bytes);
|
||||
// FIXME: This will slightly bias the distribution of client secrets
|
||||
scalar = modular_reduce_order(scalar);
|
||||
if (scalar.is_zero_constant_time())
|
||||
return Error::from_string_literal("SECP256r1: scalar is zero");
|
||||
|
||||
// Make sure the point is uncompressed
|
||||
if (point_bytes.size() != 65 || point_bytes[0] != 0x04)
|
||||
return Error::from_string_literal("SECP256r1: point is not uncompressed format");
|
||||
|
||||
JacobianPoint point {
|
||||
import_big_endian(point_bytes.slice(1, 32)),
|
||||
import_big_endian(point_bytes.slice(33, 32)),
|
||||
1u,
|
||||
};
|
||||
|
||||
// Convert the input point into Montgomery form
|
||||
point.x = to_montgomery(point.x);
|
||||
point.y = to_montgomery(point.y);
|
||||
point.z = to_montgomery(point.z);
|
||||
|
||||
// Check that the point is on the curve
|
||||
if (!is_point_on_curve(point))
|
||||
return Error::from_string_literal("SECP256r1: point is not on the curve");
|
||||
|
||||
JacobianPoint result;
|
||||
JacobianPoint temp_result;
|
||||
|
||||
// Calculate the scalar times point multiplication in constant time
|
||||
for (auto i = 0; i < 256; i++) {
|
||||
point_add(temp_result, result, point);
|
||||
|
||||
auto condition = (scalar & 1u) == 1u;
|
||||
result.x = select(result.x, temp_result.x, condition);
|
||||
result.y = select(result.y, temp_result.y, condition);
|
||||
result.z = select(result.z, temp_result.z, condition);
|
||||
|
||||
point_double(point, point);
|
||||
scalar >>= 1u;
|
||||
}
|
||||
|
||||
// Convert from Jacobian coordinates back to Affine coordinates
|
||||
convert_jacobian_to_affine(result);
|
||||
|
||||
// Make sure the resulting point is on the curve
|
||||
VERIFY(is_point_on_curve(result));
|
||||
|
||||
// Convert the result back from Montgomery form
|
||||
result.x = from_montgomery(result.x);
|
||||
result.y = from_montgomery(result.y);
|
||||
// Final modular reduction on the coordinates
|
||||
result.x = modular_reduce(result.x);
|
||||
result.y = modular_reduce(result.y);
|
||||
|
||||
// Export the values into an output buffer
|
||||
auto buf = TRY(ByteBuffer::create_uninitialized(65));
|
||||
buf[0] = 0x04;
|
||||
export_big_endian(result.x, buf.bytes().slice(1, 32));
|
||||
export_big_endian(result.y, buf.bytes().slice(33, 32));
|
||||
return buf;
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> SECP256r1::derive_premaster_key(ReadonlyBytes shared_point)
|
||||
{
|
||||
VERIFY(shared_point.size() == 65);
|
||||
VERIFY(shared_point[0] == 0x04);
|
||||
|
||||
ByteBuffer premaster_key = TRY(ByteBuffer::create_uninitialized(32));
|
||||
premaster_key.overwrite(0, shared_point.data() + 1, 32);
|
||||
return premaster_key;
|
||||
}
|
||||
|
||||
ErrorOr<bool> SECP256r1::verify(ReadonlyBytes hash, ReadonlyBytes pubkey, ReadonlyBytes signature)
|
||||
{
|
||||
Crypto::ASN1::Decoder asn1_decoder(signature);
|
||||
TRY(asn1_decoder.enter());
|
||||
|
||||
auto r_bigint = TRY(asn1_decoder.read<Crypto::UnsignedBigInteger>(Crypto::ASN1::Class::Universal, Crypto::ASN1::Kind::Integer));
|
||||
auto s_bigint = TRY(asn1_decoder.read<Crypto::UnsignedBigInteger>(Crypto::ASN1::Class::Universal, Crypto::ASN1::Kind::Integer));
|
||||
|
||||
u256 r = 0u;
|
||||
u256 s = 0u;
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
u256 rr = r_bigint.words()[i];
|
||||
u256 ss = s_bigint.words()[i];
|
||||
r |= (rr << (i * 32));
|
||||
s |= (ss << (i * 32));
|
||||
}
|
||||
|
||||
// z is the hash
|
||||
u256 z = import_big_endian(hash.slice(0, 32));
|
||||
|
||||
u256 r_mo = to_montgomery_order(r);
|
||||
u256 s_mo = to_montgomery_order(s);
|
||||
u256 z_mo = to_montgomery_order(z);
|
||||
|
||||
u256 s_inv = modular_inverse_order(s_mo);
|
||||
|
||||
u256 u1 = modular_multiply_order(z_mo, s_inv);
|
||||
u256 u2 = modular_multiply_order(r_mo, s_inv);
|
||||
|
||||
u1 = from_montgomery_order(u1);
|
||||
u2 = from_montgomery_order(u2);
|
||||
|
||||
auto u1_buf = TRY(ByteBuffer::create_uninitialized(32));
|
||||
export_big_endian(u1, u1_buf.bytes());
|
||||
auto u2_buf = TRY(ByteBuffer::create_uninitialized(32));
|
||||
export_big_endian(u2, u2_buf.bytes());
|
||||
|
||||
auto p1 = TRY(generate_public_key(u1_buf));
|
||||
auto p2 = TRY(compute_coordinate(u2_buf, pubkey));
|
||||
|
||||
JacobianPoint point1 {
|
||||
import_big_endian(TRY(p1.slice(1, 32))),
|
||||
import_big_endian(TRY(p1.slice(33, 32))),
|
||||
1u,
|
||||
};
|
||||
|
||||
// Convert the input point into Montgomery form
|
||||
point1.x = to_montgomery(point1.x);
|
||||
point1.y = to_montgomery(point1.y);
|
||||
point1.z = to_montgomery(point1.z);
|
||||
|
||||
VERIFY(is_point_on_curve(point1));
|
||||
|
||||
JacobianPoint point2 {
|
||||
import_big_endian(TRY(p2.slice(1, 32))),
|
||||
import_big_endian(TRY(p2.slice(33, 32))),
|
||||
1u,
|
||||
};
|
||||
|
||||
// Convert the input point into Montgomery form
|
||||
point2.x = to_montgomery(point2.x);
|
||||
point2.y = to_montgomery(point2.y);
|
||||
point2.z = to_montgomery(point2.z);
|
||||
|
||||
VERIFY(is_point_on_curve(point2));
|
||||
|
||||
JacobianPoint result;
|
||||
point_add(result, point1, point2);
|
||||
|
||||
// Convert from Jacobian coordinates back to Affine coordinates
|
||||
convert_jacobian_to_affine(result);
|
||||
|
||||
// Make sure the resulting point is on the curve
|
||||
VERIFY(is_point_on_curve(result));
|
||||
|
||||
// Convert the result back from Montgomery form
|
||||
result.x = from_montgomery(result.x);
|
||||
result.y = from_montgomery(result.y);
|
||||
// Final modular reduction on the coordinates
|
||||
result.x = modular_reduce(result.x);
|
||||
result.y = modular_reduce(result.y);
|
||||
|
||||
return r.is_equal_to_constant_time(result.x);
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Michiel Visser <opensource@webmichiel.nl>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/UFixedBigInt.h>
|
||||
#include <LibCrypto/Curves/EllipticCurve.h>
|
||||
|
||||
namespace Crypto::Curves {
|
||||
|
||||
class SECP256r1 : public EllipticCurve {
|
||||
public:
|
||||
size_t key_size() override { return 1 + 2 * 32; }
|
||||
ErrorOr<ByteBuffer> generate_private_key() override;
|
||||
ErrorOr<ByteBuffer> generate_public_key(ReadonlyBytes a) override;
|
||||
ErrorOr<ByteBuffer> compute_coordinate(ReadonlyBytes scalar_bytes, ReadonlyBytes point_bytes) override;
|
||||
ErrorOr<ByteBuffer> derive_premaster_key(ReadonlyBytes shared_point) override;
|
||||
|
||||
ErrorOr<bool> verify(ReadonlyBytes hash, ReadonlyBytes pubkey, ReadonlyBytes signature);
|
||||
};
|
||||
|
||||
}
|
@ -1,635 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Michiel Visser <opensource@webmichiel.nl>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteReader.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/UFixedBigInt.h>
|
||||
#include <AK/UFixedBigIntDivision.h>
|
||||
#include <LibCrypto/ASN1/DER.h>
|
||||
#include <LibCrypto/Curves/SECP384r1.h>
|
||||
|
||||
namespace Crypto::Curves {
|
||||
|
||||
struct JacobianPoint {
|
||||
u384 x { 0u };
|
||||
u384 y { 0u };
|
||||
u384 z { 0u };
|
||||
};
|
||||
|
||||
static constexpr u384 calculate_modular_inverse_mod_r(u384 value)
|
||||
{
|
||||
// Calculate the modular multiplicative inverse of value mod 2^384 using the extended euclidean algorithm
|
||||
u768 old_r = value;
|
||||
u768 r = static_cast<u768>(1u) << 384u;
|
||||
u768 old_s = 1u;
|
||||
u768 s = 0u;
|
||||
|
||||
while (!r.is_zero_constant_time()) {
|
||||
u768 quotient = old_r / r;
|
||||
u768 temp = r;
|
||||
r = old_r - quotient * r;
|
||||
old_r = temp;
|
||||
|
||||
temp = s;
|
||||
s = old_s - quotient * s;
|
||||
old_s = temp;
|
||||
}
|
||||
|
||||
return old_s.low();
|
||||
}
|
||||
|
||||
static constexpr u384 calculate_r2_mod(u384 modulus)
|
||||
{
|
||||
// Calculate the value of R^2 mod modulus, where R = 2^384
|
||||
u1536 r = static_cast<u1536>(1u) << 384u;
|
||||
u1536 r2 = r * r;
|
||||
u1536 result = r2 % static_cast<u1536>(modulus);
|
||||
return result.low().low();
|
||||
}
|
||||
|
||||
// SECP384r1 curve parameters
|
||||
static constexpr u384 PRIME { { 0x00000000ffffffffull, 0xffffffff00000000ull, 0xfffffffffffffffeull, 0xffffffffffffffffull, 0xffffffffffffffffull, 0xffffffffffffffffull } };
|
||||
static constexpr u384 A { { 0x00000000fffffffcull, 0xffffffff00000000ull, 0xfffffffffffffffeull, 0xffffffffffffffffull, 0xffffffffffffffffull, 0xffffffffffffffffull } };
|
||||
static constexpr u384 B { { 0x2a85c8edd3ec2aefull, 0xc656398d8a2ed19dull, 0x0314088f5013875aull, 0x181d9c6efe814112ull, 0x988e056be3f82d19ull, 0xb3312fa7e23ee7e4ull } };
|
||||
static constexpr u384 ORDER { { 0xecec196accc52973ull, 0x581a0db248b0a77aull, 0xc7634d81f4372ddfull, 0xffffffffffffffffull, 0xffffffffffffffffull, 0xffffffffffffffffull } };
|
||||
|
||||
// Verify that A = -3 mod p, which is required for some optimizations
|
||||
static_assert(A == PRIME - 3);
|
||||
|
||||
// Precomputed helper values for reduction and Montgomery multiplication
|
||||
static constexpr u384 REDUCE_PRIME = u384 { 0 } - PRIME;
|
||||
static constexpr u384 REDUCE_ORDER = u384 { 0 } - ORDER;
|
||||
static constexpr u384 PRIME_INVERSE_MOD_R = u384 { 0 } - calculate_modular_inverse_mod_r(PRIME);
|
||||
static constexpr u384 ORDER_INVERSE_MOD_R = u384 { 0 } - calculate_modular_inverse_mod_r(ORDER);
|
||||
static constexpr u384 R2_MOD_PRIME = calculate_r2_mod(PRIME);
|
||||
static constexpr u384 R2_MOD_ORDER = calculate_r2_mod(ORDER);
|
||||
|
||||
static u384 import_big_endian(ReadonlyBytes data)
|
||||
{
|
||||
VERIFY(data.size() == 48);
|
||||
|
||||
u64 f = AK::convert_between_host_and_big_endian(ByteReader::load64(data.offset_pointer(0 * sizeof(u64))));
|
||||
u64 e = AK::convert_between_host_and_big_endian(ByteReader::load64(data.offset_pointer(1 * sizeof(u64))));
|
||||
u64 d = AK::convert_between_host_and_big_endian(ByteReader::load64(data.offset_pointer(2 * sizeof(u64))));
|
||||
u64 c = AK::convert_between_host_and_big_endian(ByteReader::load64(data.offset_pointer(3 * sizeof(u64))));
|
||||
u64 b = AK::convert_between_host_and_big_endian(ByteReader::load64(data.offset_pointer(4 * sizeof(u64))));
|
||||
u64 a = AK::convert_between_host_and_big_endian(ByteReader::load64(data.offset_pointer(5 * sizeof(u64))));
|
||||
|
||||
return u384 { { a, b, c, d, e, f } };
|
||||
}
|
||||
|
||||
static void export_big_endian(u384 const& value, Bytes data)
|
||||
{
|
||||
auto span = value.span();
|
||||
|
||||
u64 a = AK::convert_between_host_and_big_endian(span[0]);
|
||||
u64 b = AK::convert_between_host_and_big_endian(span[1]);
|
||||
u64 c = AK::convert_between_host_and_big_endian(span[2]);
|
||||
u64 d = AK::convert_between_host_and_big_endian(span[3]);
|
||||
u64 e = AK::convert_between_host_and_big_endian(span[4]);
|
||||
u64 f = AK::convert_between_host_and_big_endian(span[5]);
|
||||
|
||||
ByteReader::store(data.offset_pointer(5 * sizeof(u64)), a);
|
||||
ByteReader::store(data.offset_pointer(4 * sizeof(u64)), b);
|
||||
ByteReader::store(data.offset_pointer(3 * sizeof(u64)), c);
|
||||
ByteReader::store(data.offset_pointer(2 * sizeof(u64)), d);
|
||||
ByteReader::store(data.offset_pointer(1 * sizeof(u64)), e);
|
||||
ByteReader::store(data.offset_pointer(0 * sizeof(u64)), f);
|
||||
}
|
||||
|
||||
static constexpr u384 select(u384 const& left, u384 const& right, bool condition)
|
||||
{
|
||||
// If condition = 0 return left else right
|
||||
u384 mask = (u384)condition - 1;
|
||||
|
||||
return (left & mask) | (right & ~mask);
|
||||
}
|
||||
|
||||
static constexpr u768 multiply(u384 const& left, u384 const& right)
|
||||
{
|
||||
return left.wide_multiply(right);
|
||||
}
|
||||
|
||||
static constexpr u384 modular_reduce(u384 const& value)
|
||||
{
|
||||
// Add -prime % 2^384
|
||||
bool carry = false;
|
||||
u384 other = value.addc(REDUCE_PRIME, carry);
|
||||
|
||||
// Check for overflow
|
||||
return select(value, other, carry);
|
||||
}
|
||||
|
||||
static constexpr u384 modular_reduce_order(u384 const& value)
|
||||
{
|
||||
// Add -order % 2^384
|
||||
bool carry = false;
|
||||
u384 other = value.addc(REDUCE_ORDER, carry);
|
||||
|
||||
// Check for overflow
|
||||
return select(value, other, carry);
|
||||
}
|
||||
|
||||
static constexpr u384 modular_add(u384 const& left, u384 const& right, bool carry_in = false)
|
||||
{
|
||||
bool carry = carry_in;
|
||||
u384 output = left.addc(right, carry);
|
||||
|
||||
// If there is a carry, subtract p by adding 2^384 - p
|
||||
u384 addend = select(0u, REDUCE_PRIME, carry);
|
||||
carry = false;
|
||||
output = output.addc(addend, carry);
|
||||
|
||||
// If there is still a carry, subtract p by adding 2^384 - p
|
||||
addend = select(0u, REDUCE_PRIME, carry);
|
||||
return output + addend;
|
||||
}
|
||||
|
||||
static constexpr u384 modular_sub(u384 const& left, u384 const& right)
|
||||
{
|
||||
bool borrow = false;
|
||||
u384 output = left.subc(right, borrow);
|
||||
|
||||
// If there is a borrow, add p by subtracting 2^384 - p
|
||||
u384 sub = select(0u, REDUCE_PRIME, borrow);
|
||||
borrow = false;
|
||||
output = output.subc(sub, borrow);
|
||||
|
||||
// If there is still a borrow, add p by subtracting 2^384 - p
|
||||
sub = select(0u, REDUCE_PRIME, borrow);
|
||||
return output - sub;
|
||||
}
|
||||
|
||||
static constexpr u384 modular_multiply(u384 const& left, u384 const& right)
|
||||
{
|
||||
// Modular multiplication using the Montgomery method: https://en.wikipedia.org/wiki/Montgomery_modular_multiplication
|
||||
// This requires that the inputs to this function are in Montgomery form.
|
||||
|
||||
// T = left * right
|
||||
u768 mult = multiply(left, right);
|
||||
|
||||
// m = ((T mod R) * curve_p')
|
||||
u768 m = multiply(mult.low(), PRIME_INVERSE_MOD_R);
|
||||
|
||||
// mp = (m mod R) * curve_p
|
||||
u768 mp = multiply(m.low(), PRIME);
|
||||
|
||||
// t = (T + mp)
|
||||
bool carry = false;
|
||||
mult.low().addc(mp.low(), carry);
|
||||
|
||||
// output = t / R
|
||||
return modular_add(mult.high(), mp.high(), carry);
|
||||
}
|
||||
|
||||
static constexpr u384 modular_square(u384 const& value)
|
||||
{
|
||||
return modular_multiply(value, value);
|
||||
}
|
||||
|
||||
static constexpr u384 to_montgomery(u384 const& value)
|
||||
{
|
||||
return modular_multiply(value, R2_MOD_PRIME);
|
||||
}
|
||||
|
||||
static constexpr u384 from_montgomery(u384 const& value)
|
||||
{
|
||||
return modular_multiply(value, 1u);
|
||||
}
|
||||
|
||||
static constexpr u384 modular_inverse(u384 const& value)
|
||||
{
|
||||
// Modular inverse modulo the curve prime can be computed using Fermat's little theorem: a^(p-2) mod p = a^-1 mod p.
|
||||
// Calculating a^(p-2) mod p can be done using the square-and-multiply exponentiation method, as p-2 is constant.
|
||||
u384 base = value;
|
||||
u384 result = to_montgomery(1u);
|
||||
u384 prime_minus_2 = PRIME - 2u;
|
||||
|
||||
for (size_t i = 0; i < 384; i++) {
|
||||
if ((prime_minus_2 & 1u) == 1u) {
|
||||
result = modular_multiply(result, base);
|
||||
}
|
||||
base = modular_square(base);
|
||||
prime_minus_2 >>= 1u;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static constexpr u384 modular_add_order(u384 const& left, u384 const& right, bool carry_in = false)
|
||||
{
|
||||
bool carry = carry_in;
|
||||
u384 output = left.addc(right, carry);
|
||||
|
||||
// If there is a carry, subtract n by adding 2^384 - n
|
||||
u384 addend = select(0u, REDUCE_ORDER, carry);
|
||||
carry = false;
|
||||
output = output.addc(addend, carry);
|
||||
|
||||
// If there is still a carry, subtract n by adding 2^384 - n
|
||||
addend = select(0u, REDUCE_ORDER, carry);
|
||||
return output + addend;
|
||||
}
|
||||
|
||||
static constexpr u384 modular_multiply_order(u384 const& left, u384 const& right)
|
||||
{
|
||||
// Modular multiplication using the Montgomery method: https://en.wikipedia.org/wiki/Montgomery_modular_multiplication
|
||||
// This requires that the inputs to this function are in Montgomery form.
|
||||
|
||||
// T = left * right
|
||||
u768 mult = multiply(left, right);
|
||||
|
||||
// m = ((T mod R) * curve_n')
|
||||
u768 m = multiply(mult.low(), ORDER_INVERSE_MOD_R);
|
||||
|
||||
// mp = (m mod R) * curve_n
|
||||
u768 mp = multiply(m.low(), ORDER);
|
||||
|
||||
// t = (T + mp)
|
||||
bool carry = false;
|
||||
mult.low().addc(mp.low(), carry);
|
||||
|
||||
// output = t / R
|
||||
return modular_add_order(mult.high(), mp.high(), carry);
|
||||
}
|
||||
|
||||
static constexpr u384 modular_square_order(u384 const& value)
|
||||
{
|
||||
return modular_multiply_order(value, value);
|
||||
}
|
||||
|
||||
static constexpr u384 to_montgomery_order(u384 const& value)
|
||||
{
|
||||
return modular_multiply_order(value, R2_MOD_ORDER);
|
||||
}
|
||||
|
||||
static constexpr u384 from_montgomery_order(u384 const& value)
|
||||
{
|
||||
return modular_multiply_order(value, 1u);
|
||||
}
|
||||
|
||||
static constexpr u384 modular_inverse_order(u384 const& value)
|
||||
{
|
||||
// Modular inverse modulo the curve order can be computed using Fermat's little theorem: a^(n-2) mod n = a^-1 mod n.
|
||||
// Calculating a^(n-2) mod n can be done using the square-and-multiply exponentiation method, as n-2 is constant.
|
||||
u384 base = value;
|
||||
u384 result = to_montgomery_order(1u);
|
||||
u384 order_minus_2 = ORDER - 2u;
|
||||
|
||||
for (size_t i = 0; i < 384; i++) {
|
||||
if ((order_minus_2 & 1u) == 1u) {
|
||||
result = modular_multiply_order(result, base);
|
||||
}
|
||||
base = modular_square_order(base);
|
||||
order_minus_2 >>= 1u;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void point_double(JacobianPoint& output_point, JacobianPoint const& point)
|
||||
{
|
||||
// Based on "Point Doubling" from http://point-at-infinity.org/ecc/Prime_Curve_Jacobian_Coordinates.html
|
||||
|
||||
// if (Y == 0)
|
||||
// return POINT_AT_INFINITY
|
||||
if (point.y.is_zero_constant_time()) {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
u384 temp;
|
||||
|
||||
// Y2 = Y^2
|
||||
u384 y2 = modular_square(point.y);
|
||||
|
||||
// S = 4*X*Y2
|
||||
u384 s = modular_multiply(point.x, y2);
|
||||
s = modular_add(s, s);
|
||||
s = modular_add(s, s);
|
||||
|
||||
// M = 3*X^2 + a*Z^4 = 3*(X + Z^2)*(X - Z^2)
|
||||
// This specific equation from https://github.com/earlephilhower/bearssl-esp8266/blob/6105635531027f5b298aa656d44be2289b2d434f/src/ec/ec_p256_m64.c#L811-L816
|
||||
// This simplification only works because a = -3 mod p
|
||||
temp = modular_square(point.z);
|
||||
u384 m = modular_add(point.x, temp);
|
||||
temp = modular_sub(point.x, temp);
|
||||
m = modular_multiply(m, temp);
|
||||
temp = modular_add(m, m);
|
||||
m = modular_add(m, temp);
|
||||
|
||||
// X' = M^2 - 2*S
|
||||
u384 xp = modular_square(m);
|
||||
xp = modular_sub(xp, s);
|
||||
xp = modular_sub(xp, s);
|
||||
|
||||
// Y' = M*(S - X') - 8*Y2^2
|
||||
u384 yp = modular_sub(s, xp);
|
||||
yp = modular_multiply(yp, m);
|
||||
temp = modular_square(y2);
|
||||
temp = modular_add(temp, temp);
|
||||
temp = modular_add(temp, temp);
|
||||
temp = modular_add(temp, temp);
|
||||
yp = modular_sub(yp, temp);
|
||||
|
||||
// Z' = 2*Y*Z
|
||||
u384 zp = modular_multiply(point.y, point.z);
|
||||
zp = modular_add(zp, zp);
|
||||
|
||||
// return (X', Y', Z')
|
||||
output_point.x = xp;
|
||||
output_point.y = yp;
|
||||
output_point.z = zp;
|
||||
}
|
||||
|
||||
static void point_add(JacobianPoint& output_point, JacobianPoint const& point_a, JacobianPoint const& point_b)
|
||||
{
|
||||
// Based on "Point Addition" from http://point-at-infinity.org/ecc/Prime_Curve_Jacobian_Coordinates.html
|
||||
if (point_a.x.is_zero_constant_time() && point_a.y.is_zero_constant_time() && point_a.z.is_zero_constant_time()) {
|
||||
output_point.x = point_b.x;
|
||||
output_point.y = point_b.y;
|
||||
output_point.z = point_b.z;
|
||||
return;
|
||||
}
|
||||
|
||||
u384 temp;
|
||||
|
||||
temp = modular_square(point_b.z);
|
||||
// U1 = X1*Z2^2
|
||||
u384 u1 = modular_multiply(point_a.x, temp);
|
||||
// S1 = Y1*Z2^3
|
||||
u384 s1 = modular_multiply(point_a.y, temp);
|
||||
s1 = modular_multiply(s1, point_b.z);
|
||||
|
||||
temp = modular_square(point_a.z);
|
||||
// U2 = X2*Z1^2
|
||||
u384 u2 = modular_multiply(point_b.x, temp);
|
||||
// S2 = Y2*Z1^3
|
||||
u384 s2 = modular_multiply(point_b.y, temp);
|
||||
s2 = modular_multiply(s2, point_a.z);
|
||||
|
||||
// if (U1 == U2)
|
||||
// if (S1 != S2)
|
||||
// return POINT_AT_INFINITY
|
||||
// else
|
||||
// return POINT_DOUBLE(X1, Y1, Z1)
|
||||
if (u1.is_equal_to_constant_time(u2)) {
|
||||
if (s1.is_equal_to_constant_time(s2)) {
|
||||
point_double(output_point, point_a);
|
||||
return;
|
||||
} else {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
// H = U2 - U1
|
||||
u384 h = modular_sub(u2, u1);
|
||||
u384 h2 = modular_square(h);
|
||||
u384 h3 = modular_multiply(h2, h);
|
||||
// R = S2 - S1
|
||||
u384 r = modular_sub(s2, s1);
|
||||
// X3 = R^2 - H^3 - 2*U1*H^2
|
||||
u384 x3 = modular_square(r);
|
||||
x3 = modular_sub(x3, h3);
|
||||
temp = modular_multiply(u1, h2);
|
||||
temp = modular_add(temp, temp);
|
||||
x3 = modular_sub(x3, temp);
|
||||
// Y3 = R*(U1*H^2 - X3) - S1*H^3
|
||||
u384 y3 = modular_multiply(u1, h2);
|
||||
y3 = modular_sub(y3, x3);
|
||||
y3 = modular_multiply(y3, r);
|
||||
temp = modular_multiply(s1, h3);
|
||||
y3 = modular_sub(y3, temp);
|
||||
// Z3 = H*Z1*Z2
|
||||
u384 z3 = modular_multiply(h, point_a.z);
|
||||
z3 = modular_multiply(z3, point_b.z);
|
||||
// return (X3, Y3, Z3)
|
||||
output_point.x = x3;
|
||||
output_point.y = y3;
|
||||
output_point.z = z3;
|
||||
}
|
||||
|
||||
static void convert_jacobian_to_affine(JacobianPoint& point)
|
||||
{
|
||||
u384 temp;
|
||||
// X' = X/Z^2
|
||||
temp = modular_square(point.z);
|
||||
temp = modular_inverse(temp);
|
||||
point.x = modular_multiply(point.x, temp);
|
||||
// Y' = Y/Z^3
|
||||
temp = modular_square(point.z);
|
||||
temp = modular_multiply(temp, point.z);
|
||||
temp = modular_inverse(temp);
|
||||
point.y = modular_multiply(point.y, temp);
|
||||
// Z' = 1
|
||||
point.z = to_montgomery(1u);
|
||||
}
|
||||
|
||||
static bool is_point_on_curve(JacobianPoint const& point)
|
||||
{
|
||||
// This check requires the point to be in Montgomery form, with Z=1
|
||||
u384 temp, temp2;
|
||||
|
||||
// Calulcate Y^2 - X^3 - a*X - b = Y^2 - X^3 + 3*X - b
|
||||
temp = modular_square(point.y);
|
||||
temp2 = modular_square(point.x);
|
||||
temp2 = modular_multiply(temp2, point.x);
|
||||
temp = modular_sub(temp, temp2);
|
||||
temp = modular_add(temp, point.x);
|
||||
temp = modular_add(temp, point.x);
|
||||
temp = modular_add(temp, point.x);
|
||||
temp = modular_sub(temp, to_montgomery(B));
|
||||
temp = modular_reduce(temp);
|
||||
|
||||
return temp.is_zero_constant_time() && point.z.is_equal_to_constant_time(to_montgomery(1u));
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> SECP384r1::generate_private_key()
|
||||
{
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(48));
|
||||
fill_with_random(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> SECP384r1::generate_public_key(ReadonlyBytes a)
|
||||
{
|
||||
// clang-format off
|
||||
u8 generator_bytes[97] {
|
||||
0x04,
|
||||
0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, 0x37, 0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD, 0x74,
|
||||
0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, 0x98, 0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, 0x38,
|
||||
0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29, 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, 0xB7,
|
||||
0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, 0x6F, 0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC, 0x29,
|
||||
0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, 0x7C, 0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, 0xC0,
|
||||
0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81, 0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, 0x5F,
|
||||
};
|
||||
// clang-format on
|
||||
return compute_coordinate(a, { generator_bytes, 97 });
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> SECP384r1::compute_coordinate(ReadonlyBytes scalar_bytes, ReadonlyBytes point_bytes)
|
||||
{
|
||||
VERIFY(scalar_bytes.size() == 48);
|
||||
|
||||
u384 scalar = import_big_endian(scalar_bytes);
|
||||
// FIXME: This will slightly bias the distribution of client secrets
|
||||
scalar = modular_reduce_order(scalar);
|
||||
if (scalar.is_zero_constant_time())
|
||||
return Error::from_string_literal("SECP384r1: scalar is zero");
|
||||
|
||||
// Make sure the point is uncompressed
|
||||
if (point_bytes.size() != 97 || point_bytes[0] != 0x04)
|
||||
return Error::from_string_literal("SECP384r1: point is not uncompressed format");
|
||||
|
||||
JacobianPoint point {
|
||||
import_big_endian(point_bytes.slice(1, 48)),
|
||||
import_big_endian(point_bytes.slice(49, 48)),
|
||||
1u,
|
||||
};
|
||||
|
||||
// Convert the input point into Montgomery form
|
||||
point.x = to_montgomery(point.x);
|
||||
point.y = to_montgomery(point.y);
|
||||
point.z = to_montgomery(point.z);
|
||||
|
||||
// Check that the point is on the curve
|
||||
if (!is_point_on_curve(point))
|
||||
return Error::from_string_literal("SECP384r1: point is not on the curve");
|
||||
|
||||
JacobianPoint result;
|
||||
JacobianPoint temp_result;
|
||||
|
||||
// Calculate the scalar times point multiplication in constant time
|
||||
for (auto i = 0; i < 384; i++) {
|
||||
point_add(temp_result, result, point);
|
||||
|
||||
auto condition = (scalar & 1u) == 1u;
|
||||
result.x = select(result.x, temp_result.x, condition);
|
||||
result.y = select(result.y, temp_result.y, condition);
|
||||
result.z = select(result.z, temp_result.z, condition);
|
||||
|
||||
point_double(point, point);
|
||||
scalar >>= 1u;
|
||||
}
|
||||
|
||||
// Convert from Jacobian coordinates back to Affine coordinates
|
||||
convert_jacobian_to_affine(result);
|
||||
|
||||
// Make sure the resulting point is on the curve
|
||||
VERIFY(is_point_on_curve(result));
|
||||
|
||||
// Convert the result back from Montgomery form
|
||||
result.x = from_montgomery(result.x);
|
||||
result.y = from_montgomery(result.y);
|
||||
// Final modular reduction on the coordinates
|
||||
result.x = modular_reduce(result.x);
|
||||
result.y = modular_reduce(result.y);
|
||||
|
||||
// Export the values into an output buffer
|
||||
auto buf = TRY(ByteBuffer::create_uninitialized(97));
|
||||
buf[0] = 0x04;
|
||||
export_big_endian(result.x, buf.bytes().slice(1, 48));
|
||||
export_big_endian(result.y, buf.bytes().slice(49, 48));
|
||||
return buf;
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> SECP384r1::derive_premaster_key(ReadonlyBytes shared_point)
|
||||
{
|
||||
VERIFY(shared_point.size() == 97);
|
||||
VERIFY(shared_point[0] == 0x04);
|
||||
|
||||
ByteBuffer premaster_key = TRY(ByteBuffer::create_uninitialized(48));
|
||||
premaster_key.overwrite(0, shared_point.data() + 1, 48);
|
||||
return premaster_key;
|
||||
}
|
||||
|
||||
ErrorOr<bool> SECP384r1::verify(ReadonlyBytes hash, ReadonlyBytes pubkey, ReadonlyBytes signature)
|
||||
{
|
||||
Crypto::ASN1::Decoder asn1_decoder(signature);
|
||||
TRY(asn1_decoder.enter());
|
||||
|
||||
auto r_bigint = TRY(asn1_decoder.read<Crypto::UnsignedBigInteger>(Crypto::ASN1::Class::Universal, Crypto::ASN1::Kind::Integer));
|
||||
auto s_bigint = TRY(asn1_decoder.read<Crypto::UnsignedBigInteger>(Crypto::ASN1::Class::Universal, Crypto::ASN1::Kind::Integer));
|
||||
|
||||
u384 r = 0u;
|
||||
u384 s = 0u;
|
||||
for (size_t i = 0; i < 12; i++) {
|
||||
u384 rr = r_bigint.words()[i];
|
||||
u384 ss = s_bigint.words()[i];
|
||||
r |= (rr << (i * 32));
|
||||
s |= (ss << (i * 32));
|
||||
}
|
||||
|
||||
// z is the hash
|
||||
u384 z = import_big_endian(hash.slice(0, 48));
|
||||
|
||||
u384 r_mo = to_montgomery_order(r);
|
||||
u384 s_mo = to_montgomery_order(s);
|
||||
u384 z_mo = to_montgomery_order(z);
|
||||
|
||||
u384 s_inv = modular_inverse_order(s_mo);
|
||||
|
||||
u384 u1 = modular_multiply_order(z_mo, s_inv);
|
||||
u384 u2 = modular_multiply_order(r_mo, s_inv);
|
||||
|
||||
u1 = from_montgomery_order(u1);
|
||||
u2 = from_montgomery_order(u2);
|
||||
|
||||
auto u1_buf = TRY(ByteBuffer::create_uninitialized(48));
|
||||
export_big_endian(u1, u1_buf.bytes());
|
||||
auto u2_buf = TRY(ByteBuffer::create_uninitialized(48));
|
||||
export_big_endian(u2, u2_buf.bytes());
|
||||
|
||||
auto p1 = TRY(generate_public_key(u1_buf));
|
||||
auto p2 = TRY(compute_coordinate(u2_buf, pubkey));
|
||||
|
||||
JacobianPoint point1 {
|
||||
import_big_endian(TRY(p1.slice(1, 48))),
|
||||
import_big_endian(TRY(p1.slice(49, 48))),
|
||||
1u,
|
||||
};
|
||||
|
||||
// Convert the input point into Montgomery form
|
||||
point1.x = to_montgomery(point1.x);
|
||||
point1.y = to_montgomery(point1.y);
|
||||
point1.z = to_montgomery(point1.z);
|
||||
|
||||
VERIFY(is_point_on_curve(point1));
|
||||
|
||||
JacobianPoint point2 {
|
||||
import_big_endian(TRY(p2.slice(1, 48))),
|
||||
import_big_endian(TRY(p2.slice(49, 48))),
|
||||
1u,
|
||||
};
|
||||
|
||||
// Convert the input point into Montgomery form
|
||||
point2.x = to_montgomery(point2.x);
|
||||
point2.y = to_montgomery(point2.y);
|
||||
point2.z = to_montgomery(point2.z);
|
||||
|
||||
VERIFY(is_point_on_curve(point2));
|
||||
|
||||
JacobianPoint result;
|
||||
point_add(result, point1, point2);
|
||||
|
||||
// Convert from Jacobian coordinates back to Affine coordinates
|
||||
convert_jacobian_to_affine(result);
|
||||
|
||||
// Make sure the resulting point is on the curve
|
||||
VERIFY(is_point_on_curve(result));
|
||||
|
||||
// Convert the result back from Montgomery form
|
||||
result.x = from_montgomery(result.x);
|
||||
result.y = from_montgomery(result.y);
|
||||
// Final modular reduction on the coordinates
|
||||
result.x = modular_reduce(result.x);
|
||||
result.y = modular_reduce(result.y);
|
||||
|
||||
return r.is_equal_to_constant_time(result.x);
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Michiel Visser <opensource@webmichiel.nl>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/UFixedBigInt.h>
|
||||
#include <LibCrypto/Curves/EllipticCurve.h>
|
||||
|
||||
namespace Crypto::Curves {
|
||||
|
||||
class SECP384r1 : public EllipticCurve {
|
||||
public:
|
||||
size_t key_size() override { return 1 + 2 * 48; }
|
||||
ErrorOr<ByteBuffer> generate_private_key() override;
|
||||
ErrorOr<ByteBuffer> generate_public_key(ReadonlyBytes a) override;
|
||||
ErrorOr<ByteBuffer> compute_coordinate(ReadonlyBytes scalar_bytes, ReadonlyBytes point_bytes) override;
|
||||
ErrorOr<ByteBuffer> derive_premaster_key(ReadonlyBytes shared_point) override;
|
||||
|
||||
ErrorOr<bool> verify(ReadonlyBytes hash, ReadonlyBytes pubkey, ReadonlyBytes signature);
|
||||
};
|
||||
|
||||
}
|
@ -13,8 +13,7 @@
|
||||
#include <LibCrypto/ASN1/DER.h>
|
||||
#include <LibCrypto/Curves/Ed25519.h>
|
||||
#include <LibCrypto/Curves/EllipticCurve.h>
|
||||
#include <LibCrypto/Curves/SECP256r1.h>
|
||||
#include <LibCrypto/Curves/SECP384r1.h>
|
||||
#include <LibCrypto/Curves/SECPxxxr1.h>
|
||||
#include <LibCrypto/Curves/X25519.h>
|
||||
#include <LibCrypto/Curves/X448.h>
|
||||
#include <LibCrypto/PK/Code/EMSA_PKCS1_V1_5.h>
|
||||
|
@ -15,8 +15,7 @@
|
||||
#include <LibCrypto/ASN1/ASN1.h>
|
||||
#include <LibCrypto/ASN1/PEM.h>
|
||||
#include <LibCrypto/Curves/Ed25519.h>
|
||||
#include <LibCrypto/Curves/SECP256r1.h>
|
||||
#include <LibCrypto/Curves/SECP384r1.h>
|
||||
#include <LibCrypto/Curves/SECPxxxr1.h>
|
||||
#include <LibCrypto/PK/Code/EMSA_PKCS1_V1_5.h>
|
||||
#include <LibCrypto/PK/Code/EMSA_PSS.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
|
Loading…
Reference in New Issue
Block a user