From 6322d68b1b985eaeaa37de877510cd9beffa200b Mon Sep 17 00:00:00 2001 From: Michiel Visser Date: Fri, 10 Nov 2023 16:21:43 +0100 Subject: [PATCH] LibCrypto: Add SECP384r1 implementation This implementation is basically a copy-paste of the SECP256r1 implementation with all "256" replaced with "384". In the future it might be nice to make this generic, instead of having two almost identical copies of code. --- Userland/Libraries/LibCrypto/CMakeLists.txt | 1 + .../Libraries/LibCrypto/Curves/SECP384r1.cpp | 635 ++++++++++++++++++ .../Libraries/LibCrypto/Curves/SECP384r1.h | 26 + 3 files changed, 662 insertions(+) create mode 100644 Userland/Libraries/LibCrypto/Curves/SECP384r1.cpp create mode 100644 Userland/Libraries/LibCrypto/Curves/SECP384r1.h diff --git a/Userland/Libraries/LibCrypto/CMakeLists.txt b/Userland/Libraries/LibCrypto/CMakeLists.txt index 21ab5436de..80729cc9fe 100644 --- a/Userland/Libraries/LibCrypto/CMakeLists.txt +++ b/Userland/Libraries/LibCrypto/CMakeLists.txt @@ -24,6 +24,7 @@ set(SOURCES Curves/Curve25519.cpp Curves/Ed25519.cpp Curves/SECP256r1.cpp + Curves/SECP384r1.cpp Curves/X25519.cpp Curves/X448.cpp Hash/BLAKE2b.cpp diff --git a/Userland/Libraries/LibCrypto/Curves/SECP384r1.cpp b/Userland/Libraries/LibCrypto/Curves/SECP384r1.cpp new file mode 100644 index 0000000000..86d9e075fb --- /dev/null +++ b/Userland/Libraries/LibCrypto/Curves/SECP384r1.cpp @@ -0,0 +1,635 @@ +/* + * Copyright (c) 2023, Michiel Visser + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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(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(1u) << 384u; + u1536 r2 = r * r; + u1536 result = r2 % static_cast(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 SECP384r1::generate_private_key() +{ + auto buffer = TRY(ByteBuffer::create_uninitialized(48)); + fill_with_random(buffer); + return buffer; +} + +ErrorOr 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 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 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 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::ASN1::Class::Universal, Crypto::ASN1::Kind::Integer)); + auto s_bigint = TRY(asn1_decoder.read(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); +} + +} diff --git a/Userland/Libraries/LibCrypto/Curves/SECP384r1.h b/Userland/Libraries/LibCrypto/Curves/SECP384r1.h new file mode 100644 index 0000000000..9ffe95b784 --- /dev/null +++ b/Userland/Libraries/LibCrypto/Curves/SECP384r1.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023, Michiel Visser + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Crypto::Curves { + +class SECP384r1 : public EllipticCurve { +public: + size_t key_size() override { return 1 + 2 * 48; } + ErrorOr generate_private_key() override; + ErrorOr generate_public_key(ReadonlyBytes a) override; + ErrorOr compute_coordinate(ReadonlyBytes scalar_bytes, ReadonlyBytes point_bytes) override; + ErrorOr derive_premaster_key(ReadonlyBytes shared_point) override; + + ErrorOr verify(ReadonlyBytes hash, ReadonlyBytes pubkey, ReadonlyBytes signature); +}; + +}