From 60dcf3e023b3595c4f1f944eda7b90d340833b06 Mon Sep 17 00:00:00 2001 From: devgianlu Date: Sun, 16 Feb 2025 13:02:13 +0100 Subject: [PATCH] LibCrypto: Refactor Edwards-curves implementation with OpenSSL --- Libraries/LibCrypto/CMakeLists.txt | 6 +- Libraries/LibCrypto/Curves/Curve25519.cpp | 360 --------------- Libraries/LibCrypto/Curves/Curve25519.h | 71 --- Libraries/LibCrypto/Curves/Ed25519.cpp | 434 ------------------- Libraries/LibCrypto/Curves/Ed25519.h | 74 ---- Libraries/LibCrypto/Curves/Ed448.cpp | 82 ---- Libraries/LibCrypto/Curves/Ed448.h | 24 - Libraries/LibCrypto/Curves/EdwardsCurve.cpp | 113 +++++ Libraries/LibCrypto/Curves/EdwardsCurve.h | 83 ++++ Libraries/LibCrypto/Curves/X25519.cpp | 129 ------ Libraries/LibCrypto/Curves/X25519.h | 22 - Libraries/LibCrypto/Curves/X448.cpp | 384 ---------------- Libraries/LibCrypto/Curves/X448.h | 22 - Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp | 17 +- Tests/LibCrypto/TestCurves.cpp | 3 +- Tests/LibCrypto/TestEd25519.cpp | 18 +- 16 files changed, 216 insertions(+), 1626 deletions(-) delete mode 100644 Libraries/LibCrypto/Curves/Curve25519.cpp delete mode 100644 Libraries/LibCrypto/Curves/Curve25519.h delete mode 100644 Libraries/LibCrypto/Curves/Ed25519.cpp delete mode 100644 Libraries/LibCrypto/Curves/Ed25519.h delete mode 100644 Libraries/LibCrypto/Curves/Ed448.cpp delete mode 100644 Libraries/LibCrypto/Curves/Ed448.h create mode 100644 Libraries/LibCrypto/Curves/EdwardsCurve.cpp create mode 100644 Libraries/LibCrypto/Curves/EdwardsCurve.h delete mode 100644 Libraries/LibCrypto/Curves/X25519.cpp delete mode 100644 Libraries/LibCrypto/Curves/X25519.h delete mode 100644 Libraries/LibCrypto/Curves/X448.cpp delete mode 100644 Libraries/LibCrypto/Curves/X448.h diff --git a/Libraries/LibCrypto/CMakeLists.txt b/Libraries/LibCrypto/CMakeLists.txt index 719a3afeedb..7a659f3c04d 100644 --- a/Libraries/LibCrypto/CMakeLists.txt +++ b/Libraries/LibCrypto/CMakeLists.txt @@ -22,12 +22,8 @@ set(SOURCES Checksum/CRC32.cpp Cipher/AES.cpp Cipher/Cipher.cpp - Curves/Curve25519.cpp - Curves/Ed25519.cpp - Curves/Ed448.cpp + Curves/EdwardsCurve.cpp Curves/SECPxxxr1.cpp - Curves/X25519.cpp - Curves/X448.cpp Hash/BLAKE2b.cpp Hash/MD5.cpp Hash/SHA1.cpp diff --git a/Libraries/LibCrypto/Curves/Curve25519.cpp b/Libraries/LibCrypto/Curves/Curve25519.cpp deleted file mode 100644 index 5651598ae1e..00000000000 --- a/Libraries/LibCrypto/Curves/Curve25519.cpp +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (c) 2022, stelar7 - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Crypto::Curves { - -void Curve25519::set(u32* state, u32 value) -{ - state[0] = value; - - for (auto i = 1; i < WORDS; i++) { - state[i] = 0; - } -} - -void Curve25519::modular_square(u32* state, u32 const* value) -{ - // Compute R = (A ^ 2) mod p - modular_multiply(state, value, value); -} - -void Curve25519::modular_subtract(u32* state, u32 const* first, u32 const* second) -{ - // R = (A - B) mod p - i64 temp = -19; - for (auto i = 0; i < WORDS; i++) { - temp += first[i]; - temp -= second[i]; - state[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - // Compute R = A + (2^255 - 19) - B - state[7] += 0x80000000; - - modular_reduce(state, state); -} - -void Curve25519::modular_add(u32* state, u32 const* first, u32 const* second) -{ - // R = (A + B) mod p - u64 temp = 0; - for (auto i = 0; i < WORDS; i++) { - temp += first[i]; - temp += second[i]; - state[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - modular_reduce(state, state); -} - -void Curve25519::modular_multiply(u32* state, u32 const* first, u32 const* second) -{ - // Compute R = (A * B) mod p - u64 temp = 0; - u64 carry = 0; - u32 output[WORDS * 2]; - - // Comba's method - for (auto i = 0; i < 16; i++) { - if (i < WORDS) { - for (auto j = 0; j <= i; j++) { - temp += (u64)first[j] * second[i - j]; - carry += temp >> 32; - temp &= 0xFFFFFFFF; - } - } else { - for (auto j = i - 7; j < WORDS; j++) { - temp += (u64)first[j] * second[i - j]; - carry += temp >> 32; - temp &= 0xFFFFFFFF; - } - } - - output[i] = temp & 0xFFFFFFFF; - temp = carry & 0xFFFFFFFF; - carry >>= 32; - } - - // Reduce bit 255 (2^255 = 19 mod p) - temp = (output[7] >> 31) * 19; - // Mask the most significant bit - output[7] &= 0x7FFFFFFF; - - // Fast modular reduction 1st pass - for (auto i = 0; i < WORDS; i++) { - temp += output[i]; - temp += (u64)output[i + 8] * 38; - output[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - // Reduce bit 256 (2^256 = 38 mod p) - temp *= 38; - // Reduce bit 255 (2^255 = 19 mod p) - temp += (output[7] >> 31) * 19; - // Mask the most significant bit - output[7] &= 0x7FFFFFFF; - - // Fast modular reduction 2nd pass - for (auto i = 0; i < WORDS; i++) { - temp += output[i]; - output[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - modular_reduce(state, output); -} - -void Curve25519::export_state(u32* state, u8* output) -{ - for (u32 i = 0; i < WORDS; i++) { - state[i] = AK::convert_between_host_and_little_endian(state[i]); - } - - memcpy(output, state, BYTES); -} - -void Curve25519::import_state(u32* state, u8 const* data) -{ - memcpy(state, data, BYTES); - for (u32 i = 0; i < WORDS; i++) { - state[i] = AK::convert_between_host_and_little_endian(state[i]); - } -} - -void Curve25519::modular_subtract_single(u32* r, u32 const* a, u32 b) -{ - i64 temp = -19; - temp -= b; - - // Compute R = A - 19 - B - for (u32 i = 0; i < 8; i++) { - temp += a[i]; - r[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - // Compute R = A + (2^255 - 19) - B - r[7] += 0x80000000; - modular_reduce(r, r); -} - -void Curve25519::modular_add_single(u32* state, u32 const* first, u32 second) -{ - u64 temp = second; - - // Compute R = A + B - for (u32 i = 0; i < 8; i++) { - temp += first[i]; - state[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - modular_reduce(state, state); -} - -u32 Curve25519::modular_square_root(u32* r, u32 const* a, u32 const* b) -{ - u32 c[8]; - u32 u[8]; - u32 v[8]; - - // To compute the square root of (A / B), the first step is to compute the candidate root x = (A / B)^((p+3)/8) - modular_square(v, b); - modular_multiply(v, v, b); - modular_square(v, v); - modular_multiply(v, v, b); - modular_multiply(c, a, v); - modular_square(u, c); - modular_multiply(u, u, c); - modular_square(u, u); - modular_multiply(v, u, c); - to_power_of_2n(u, v, 3); - modular_multiply(u, u, v); - modular_square(u, u); - modular_multiply(v, u, c); - to_power_of_2n(u, v, 7); - modular_multiply(u, u, v); - modular_square(u, u); - modular_multiply(v, u, c); - to_power_of_2n(u, v, 15); - modular_multiply(u, u, v); - modular_square(u, u); - modular_multiply(v, u, c); - to_power_of_2n(u, v, 31); - modular_multiply(v, u, v); - to_power_of_2n(u, v, 62); - modular_multiply(u, u, v); - modular_square(u, u); - modular_multiply(v, u, c); - to_power_of_2n(u, v, 125); - modular_multiply(u, u, v); - modular_square(u, u); - modular_square(u, u); - modular_multiply(u, u, c); - - // The first candidate root is U = A * B^3 * (A * B^7)^((p - 5) / 8) - modular_multiply(u, u, a); - modular_square(v, b); - modular_multiply(v, v, b); - modular_multiply(u, u, v); - - // The second candidate root is V = U * sqrt(-1) - modular_multiply(v, u, SQRT_MINUS_1); - - modular_square(c, u); - modular_multiply(c, c, b); - - // Check whether B * U^2 = A - u32 first_comparison = compare(c, a); - - modular_square(c, v); - modular_multiply(c, c, b); - - // Check whether B * V^2 = A - u32 second_comparison = compare(c, a); - - // Select the first or the second candidate root - select(r, u, v, first_comparison); - - // Return 0 if the square root exists - return first_comparison & second_comparison; -} - -u32 Curve25519::compare(u32 const* a, u32 const* b) -{ - u32 mask = 0; - for (u32 i = 0; i < 8; i++) { - mask |= a[i] ^ b[i]; - } - - // Return 0 if A = B, else 1 - return ((u32)(mask | (~mask + 1))) >> 31; -} - -void Curve25519::modular_reduce(u32* state, u32 const* data) -{ - // R = A mod p - u64 temp = 19; - u32 other[WORDS]; - - for (auto i = 0; i < WORDS; i++) { - temp += data[i]; - other[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - // Compute B = A - (2^255 - 19) - other[7] -= 0x80000000; - - u32 mask = (other[7] & 0x80000000) >> 31; - select(state, other, data, mask); -} - -void Curve25519::to_power_of_2n(u32* state, u32 const* value, u8 n) -{ - // Pre-compute (A ^ 2) mod p - modular_square(state, value); - - // Compute R = (A ^ (2^n)) mod p - for (u32 i = 1; i < n; i++) { - modular_square(state, state); - } -} - -void Curve25519::select(u32* state, u32 const* a, u32 const* b, u32 condition) -{ - // If B < (2^255 - 19) then R = B, else R = A - u32 mask = condition - 1; - - for (auto i = 0; i < WORDS; i++) { - state[i] = (a[i] & mask) | (b[i] & ~mask); - } -} - -void Curve25519::copy(u32* state, u32 const* value) -{ - for (auto i = 0; i < WORDS; i++) { - state[i] = value[i]; - } -} - -void Curve25519::modular_multiply_inverse(u32* state, u32 const* value) -{ - // Compute R = A^-1 mod p - u32 u[WORDS]; - u32 v[WORDS]; - - // Fermat's little theorem - modular_square(u, value); - modular_multiply(u, u, value); - modular_square(u, u); - modular_multiply(v, u, value); - to_power_of_2n(u, v, 3); - modular_multiply(u, u, v); - modular_square(u, u); - modular_multiply(v, u, value); - to_power_of_2n(u, v, 7); - modular_multiply(u, u, v); - modular_square(u, u); - modular_multiply(v, u, value); - to_power_of_2n(u, v, 15); - modular_multiply(u, u, v); - modular_square(u, u); - modular_multiply(v, u, value); - to_power_of_2n(u, v, 31); - modular_multiply(v, u, v); - to_power_of_2n(u, v, 62); - modular_multiply(u, u, v); - modular_square(u, u); - modular_multiply(v, u, value); - to_power_of_2n(u, v, 125); - modular_multiply(u, u, v); - modular_square(u, u); - modular_square(u, u); - modular_multiply(u, u, value); - modular_square(u, u); - modular_square(u, u); - modular_multiply(u, u, value); - modular_square(u, u); - modular_multiply(state, u, value); -} - -void Curve25519::modular_multiply_single(u32* state, u32 const* first, u32 second) -{ - // Compute R = (A * B) mod p - u64 temp = 0; - u32 output[WORDS]; - - for (auto i = 0; i < WORDS; i++) { - temp += (u64)first[i] * second; - output[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - // Reduce bit 256 (2^256 = 38 mod p) - temp *= 38; - // Reduce bit 255 (2^255 = 19 mod p) - temp += (output[7] >> 31) * 19; - // Mask the most significant bit - output[7] &= 0x7FFFFFFF; - - // Fast modular reduction - for (auto i = 0; i < WORDS; i++) { - temp += output[i]; - output[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - modular_reduce(state, output); -} -} diff --git a/Libraries/LibCrypto/Curves/Curve25519.h b/Libraries/LibCrypto/Curves/Curve25519.h deleted file mode 100644 index b0ab7a9e86e..00000000000 --- a/Libraries/LibCrypto/Curves/Curve25519.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2022, stelar7 - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Crypto::Curves { - -class Curve25519 { -public: - static constexpr u8 BASE_POINT_L_ORDER[33] { - 0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, - 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x00 - }; - - static constexpr u32 CURVE_D[8] { - 0x135978A3, 0x75EB4DCA, 0x4141D8AB, 0x00700A4D, - 0x7779E898, 0x8CC74079, 0x2B6FFE73, 0x52036CEE - }; - - static constexpr u32 CURVE_D_2[8] { - 0x26B2F159, 0xEBD69B94, 0x8283B156, 0x00E0149A, - 0xEEF3D130, 0x198E80F2, 0x56DFFCE7, 0x2406D9DC - }; - - static constexpr u32 ZERO[8] { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 - }; - - static constexpr u32 SQRT_MINUS_1[8] { - 0x4A0EA0B0, 0xC4EE1B27, 0xAD2FE478, 0x2F431806, - 0x3DFBD7A7, 0x2B4D0099, 0x4FC1DF0B, 0x2B832480 - }; - - static constexpr u8 BARRETT_REDUCTION_QUOTIENT[33] { - 0x1B, 0x13, 0x2C, 0x0A, 0xA3, 0xE5, 0x9C, 0xED, - 0xA7, 0x29, 0x63, 0x08, 0x5D, 0x21, 0x06, 0x21, - 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x0F - }; - - static constexpr u8 BITS = 255; - static constexpr u8 BYTES = 32; - static constexpr u8 WORDS = 8; - static constexpr u32 A24 = 121666; - - static void set(u32* a, u32 b); - static void select(u32* r, u32 const* a, u32 const* b, u32 c); - static void copy(u32* a, u32 const* b); - static void modular_square(u32* r, u32 const* a); - static void modular_subtract(u32* r, u32 const* a, u32 const* b); - static void modular_reduce(u32* r, u32 const* a); - static void modular_add(u32* r, u32 const* a, u32 const* b); - static void modular_multiply(u32* r, u32 const* a, u32 const* b); - static void modular_multiply_inverse(u32* r, u32 const* a); - static void to_power_of_2n(u32* r, u32 const* a, u8 n); - static void export_state(u32* a, u8* data); - static void import_state(u32* a, u8 const* data); - static void modular_subtract_single(u32* r, u32 const* a, u32 b); - static void modular_multiply_single(u32* r, u32 const* a, u32 b); - static void modular_add_single(u32* r, u32 const* a, u32 b); - static u32 modular_square_root(u32* r, u32 const* a, u32 const* b); - static u32 compare(u32 const* a, u32 const* b); -}; -} diff --git a/Libraries/LibCrypto/Curves/Ed25519.cpp b/Libraries/LibCrypto/Curves/Ed25519.cpp deleted file mode 100644 index 2e0d09bc9df..00000000000 --- a/Libraries/LibCrypto/Curves/Ed25519.cpp +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Copyright (c) 2022, stelar7 - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Crypto::Curves { - -// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5 -ErrorOr Ed25519::generate_private_key() -{ - // The private key is 32 octets (256 bits, corresponding to b) of - // cryptographically secure random data. See [RFC4086] for a discussion - // about randomness. - - auto buffer = TRY(ByteBuffer::create_uninitialized(key_size())); - fill_with_secure_random(buffer); - return buffer; -} - -// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5 -ErrorOr Ed25519::generate_public_key(ReadonlyBytes private_key) -{ - // The 32-byte public key is generated by the following steps. - - // 1. Hash the 32-byte private key using SHA-512, storing the digest in a 64-octet large buffer, denoted h. - // Only the lower 32 bytes are used for generating the public key. - auto digest = Crypto::Hash::SHA512::hash(private_key); - - // NOTE: we do these steps in the opposite order (since its easier to modify s) - // 3. Interpret the buffer as the little-endian integer, forming a secret scalar s. - memcpy(s, digest.data, 32); - - // 2. Prune the buffer: - // The lowest three bits of the first octet are cleared, - s[0] &= 0xF8; - // the highest bit of the last octet is cleared, - s[31] &= 0x7F; - // and the second highest bit of the last octet is set. - s[31] |= 0x40; - - // Perform a fixed-base scalar multiplication [s]B. - point_multiply_scalar(&sb, s, &BASE_POINT); - - // 4. The public key A is the encoding of the point [s]B. - // First, encode the y-coordinate (in the range 0 <= y < p) as a little-endian string of 32 octets. - // The most significant bit of the final octet is always zero. - // To form the encoding of the point [s]B, copy the least significant bit of the x coordinate - // to the most significant bit of the final octet. The result is the public key. - auto public_key = TRY(ByteBuffer::create_uninitialized(key_size())); - encode_point(&sb, public_key.data()); - - return public_key; -} - -// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.6 -ErrorOr Ed25519::sign(ReadonlyBytes public_key, ReadonlyBytes private_key, ReadonlyBytes message) -{ - // 1. Hash the private key, 32 octets, using SHA-512. - // Let h denote the resulting digest. - auto h = Crypto::Hash::SHA512::hash(private_key); - - // Construct the secret scalar s from the first half of the digest, - memcpy(s, h.data, 32); - // NOTE: This is done later in step 4, but we can also do it here. - s[0] &= 0xF8; - s[31] &= 0x7F; - s[31] |= 0x40; - - // and the corresponding public key A, as described in the previous section. - // NOTE: The public key A is taken as input to this function. - - // Let prefix denote the second half of the hash digest, h[32],...,h[63]. - memcpy(p, h.data + 32, 32); - - // 2. Compute SHA-512(dom2(F, C) || p || PH(M)), where M is the message to be signed. - auto hash = Hash::SHA512::create(); - // NOTE: dom2(F, C) is a blank octet string when signing or verifying Ed25519 - hash->update(p, 32); - // NOTE: PH(M) = M - hash->update(message.data(), message.size()); - - // Interpret the 64-octet digest as a little-endian integer r. - // For efficiency, do this by first reducing r modulo L, the group order of B. - auto digest = hash->digest(); - barrett_reduce(r, digest.data); - - // 3. Compute the point [r]B. - point_multiply_scalar(&rb, r, &BASE_POINT); - - auto R = TRY(ByteBuffer::create_uninitialized(32)); - // Let the string R be the encoding of this point - encode_point(&rb, R.data()); - - // 4. Compute SHA512(dom2(F, C) || R || A || PH(M)), - // NOTE: We can reuse hash here, since digest() calls reset() - // NOTE: dom2(F, C) is a blank octet string when signing or verifying Ed25519 - hash->update(R.data(), R.size()); - // NOTE: A == public_key - hash->update(public_key.data(), public_key.size()); - // NOTE: PH(M) = M - hash->update(message.data(), message.size()); - - digest = hash->digest(); - // and interpret the 64-octet digest as a little-endian integer k. - memcpy(k, digest.data, 64); - - // 5. Compute S = (r + k * s) mod L. For efficiency, again reduce k modulo L first. - barrett_reduce(p, k); - multiply(k, k + 32, p, s, 32); - barrett_reduce(p, k); - add(s, p, r, 32); - - // modular reduction - auto reduced_s = TRY(ByteBuffer::create_uninitialized(32)); - auto is_negative = subtract(p, s, Curve25519::BASE_POINT_L_ORDER, 32); - select(reduced_s.data(), p, s, is_negative, 32); - - // 6. Form the signature of the concatenation of R (32 octets) and the little-endian encoding of S - // (32 octets; the three most significant bits of the final octet are always zero). - auto signature = TRY(ByteBuffer::copy(R)); - signature.append(reduced_s); - - return signature; -} - -// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.7 -bool Ed25519::verify(ReadonlyBytes public_key, ReadonlyBytes signature, ReadonlyBytes message) -{ - auto not_valid = false; - - // 1. To verify a signature on a message M using public key A, - // with F being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or Ed25519ph is being used, C being the context - // first split the signature into two 32-octet halves. - // If any of the decodings fail (including S being out of range), the signature is invalid. - - // NOTE: We dont care about F, since we dont implement Ed25519ctx or Ed25519PH - // NOTE: C is the internal state, so its not a parameter - - auto half_signature_size = signature_size() / 2; - - // Decode the first half as a point R - memcpy(r, signature.data(), half_signature_size); - - // and the second half as an integer S, in the range 0 <= s < L. - memcpy(s, signature.data() + half_signature_size, half_signature_size); - - // NOTE: Ed25519 and Ed448 signatures are not malleable due to the verification check that decoded S is smaller than l. - // Without this check, one can add a multiple of l into a scalar part and still pass signature verification, - // resulting in malleable signatures. - auto is_negative = subtract(p, s, Curve25519::BASE_POINT_L_ORDER, half_signature_size); - not_valid |= 1 ^ is_negative; - - // Decode the public key A as point A'. - not_valid |= decode_point(&ka, public_key.data()); - - // 2. Compute SHA512(dom2(F, C) || R || A || PH(M)), and interpret the 64-octet digest as a little-endian integer k. - auto hash = Hash::SHA512::create(); - // NOTE: dom2(F, C) is a blank octet string when signing or verifying Ed25519 - hash->update(r, half_signature_size); - // NOTE: A == public_key - hash->update(public_key.data(), key_size()); - // NOTE: PH(M) = M - hash->update(message.data(), message.size()); - - auto digest = hash->digest(); - auto k = digest.data; - - // 3. Check the group equation [8][S]B = [8]R + [8][k]A'. - // It's sufficient, but not required, to instead check [S]B = R + [k]A'. - // NOTE: For efficiency, do this by first reducing k modulo L. - barrett_reduce(k, k); - - // NOTE: We check [S]B - [k]A' == R - Curve25519::modular_subtract(ka.x, Curve25519::ZERO, ka.x); - Curve25519::modular_subtract(ka.t, Curve25519::ZERO, ka.t); - point_multiply_scalar(&sb, s, &BASE_POINT); - point_multiply_scalar(&ka, k, &ka); - point_add(&ka, &sb, &ka); - encode_point(&ka, p); - - not_valid |= compare(p, r, half_signature_size); - - return !not_valid; -} - -void Ed25519::point_double(Ed25519Point* result, Ed25519Point const* point) -{ - Curve25519::modular_square(a, point->x); - Curve25519::modular_square(b, point->y); - Curve25519::modular_square(c, point->z); - Curve25519::modular_add(c, c, c); - Curve25519::modular_add(e, a, b); - Curve25519::modular_add(f, point->x, point->y); - Curve25519::modular_square(f, f); - Curve25519::modular_subtract(f, e, f); - Curve25519::modular_subtract(g, a, b); - Curve25519::modular_add(h, c, g); - Curve25519::modular_multiply(result->x, f, h); - Curve25519::modular_multiply(result->y, e, g); - Curve25519::modular_multiply(result->z, g, h); - Curve25519::modular_multiply(result->t, e, f); -} - -void Ed25519::point_multiply_scalar(Ed25519Point* result, u8 const* scalar, Ed25519Point const* point) -{ - // Set U to the neutral element (0, 1, 1, 0) - Curve25519::set(u.x, 0); - Curve25519::set(u.y, 1); - Curve25519::set(u.z, 1); - Curve25519::set(u.t, 0); - - for (i32 i = Curve25519::BITS - 1; i >= 0; i--) { - u8 b = (scalar[i / 8] >> (i % 8)) & 1; - - // Compute U = 2 * U - point_double(&u, &u); - // Compute V = U + P - point_add(&v, &u, point); - - // If b is set, then U = V - Curve25519::select(u.x, u.x, v.x, b); - Curve25519::select(u.y, u.y, v.y, b); - Curve25519::select(u.z, u.z, v.z, b); - Curve25519::select(u.t, u.t, v.t, b); - } - - Curve25519::copy(result->x, u.x); - Curve25519::copy(result->y, u.y); - Curve25519::copy(result->z, u.z); - Curve25519::copy(result->t, u.t); -} - -// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.2 -void Ed25519::encode_point(Ed25519Point* point, u8* data) -{ - // Retrieve affine representation - Curve25519::modular_multiply_inverse(point->z, point->z); - Curve25519::modular_multiply(point->x, point->x, point->z); - Curve25519::modular_multiply(point->y, point->y, point->z); - Curve25519::set(point->z, 1); - Curve25519::modular_multiply(point->t, point->x, point->y); - - // First, encode the y-coordinate (in the range 0 <= y < p) as a little-endian string of 32 octets. - // The most significant bit of the final octet is always zero. - Curve25519::export_state(point->y, data); - - // To form the encoding of the point [s]B, - // copy the least significant bit of the x coordinate to the most significant bit of the final octet. - data[31] |= (point->x[0] & 1) << 7; -} - -void Ed25519::barrett_reduce(u8* result, u8 const* input) -{ - // Barrett reduction b = 2^8 && k = 32 - u8 is_negative; - u8 u[33]; - u8 v[33]; - - multiply(NULL, u, input + 31, Curve25519::BARRETT_REDUCTION_QUOTIENT, 33); - multiply(v, NULL, u, Curve25519::BASE_POINT_L_ORDER, 33); - - subtract(u, input, v, 33); - - is_negative = subtract(v, u, Curve25519::BASE_POINT_L_ORDER, 33); - select(u, v, u, is_negative, 33); - is_negative = subtract(v, u, Curve25519::BASE_POINT_L_ORDER, 33); - select(u, v, u, is_negative, 33); - - copy(result, u, 32); -} - -void Ed25519::multiply(u8* result_low, u8* result_high, u8 const* a, u8 const* b, u8 n) -{ - // Comba's algorithm - u32 temp = 0; - for (u32 i = 0; i < n; i++) { - for (u32 j = 0; j <= i; j++) { - temp += (uint16_t)a[j] * b[i - j]; - } - - if (result_low != NULL) { - result_low[i] = temp & 0xFF; - } - - temp >>= 8; - } - - if (result_high != NULL) { - for (u32 i = n; i < (2 * n); i++) { - for (u32 j = i + 1 - n; j < n; j++) { - temp += (uint16_t)a[j] * b[i - j]; - } - - result_high[i - n] = temp & 0xFF; - temp >>= 8; - } - } -} - -void Ed25519::add(u8* result, u8 const* a, u8 const* b, u8 n) -{ - // Compute R = A + B - u16 temp = 0; - for (u8 i = 0; i < n; i++) { - temp += a[i]; - temp += b[i]; - result[i] = temp & 0xFF; - temp >>= 8; - } -} - -u8 Ed25519::subtract(u8* result, u8 const* a, u8 const* b, u8 n) -{ - i16 temp = 0; - - // Compute R = A - B - for (i8 i = 0; i < n; i++) { - temp += a[i]; - temp -= b[i]; - result[i] = temp & 0xFF; - temp >>= 8; - } - - // Return 1 if the result of the subtraction is negative - return temp & 1; -} - -void Ed25519::select(u8* r, u8 const* a, u8 const* b, u8 c, u8 n) -{ - u8 mask = c - 1; - for (u8 i = 0; i < n; i++) { - r[i] = (a[i] & mask) | (b[i] & ~mask); - } -} - -// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.3 -u32 Ed25519::decode_point(Ed25519Point* point, u8 const* data) -{ - u32 u[8]; - u32 v[8]; - u32 ret; - u64 temp = 19; - - // 1. First, interpret the string as an integer in little-endian representation. - // Bit 255 of this number is the least significant bit of the x-coordinate and denote this value x_0. - u8 x0 = data[31] >> 7; - - // The y-coordinate is recovered simply by clearing this bit. - Curve25519::import_state(point->y, data); - point->y[7] &= 0x7FFFFFFF; - - // Compute U = Y + 19 - for (u32 i = 0; i < 8; i++) { - temp += point->y[i]; - u[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - // If the resulting value is >= p, decoding fails. - ret = (u[7] >> 31) & 1; - - // 2. To recover the x-coordinate, the curve equation implies x^2 = (y^2 - 1) / (d y^2 + 1) (mod p). - // The denominator is always non-zero mod p. - // Let u = y^2 - 1 and v = d * y^2 + 1 - Curve25519::modular_square(v, point->y); - Curve25519::modular_subtract_single(u, v, 1); - Curve25519::modular_multiply(v, v, Curve25519::CURVE_D); - Curve25519::modular_add_single(v, v, 1); - - // 3. Compute u = sqrt(u / v) - ret |= Curve25519::modular_square_root(u, u, v); - - // If x = 0, and x_0 = 1, decoding fails. - ret |= (Curve25519::compare(u, Curve25519::ZERO) ^ 1) & x0; - - // 4. Finally, use the x_0 bit to select the right square root. - Curve25519::modular_subtract(v, Curve25519::ZERO, u); - Curve25519::select(point->x, u, v, (x0 ^ u[0]) & 1); - Curve25519::set(point->z, 1); - Curve25519::modular_multiply(point->t, point->x, point->y); - - // Return 0 if the point has been successfully decoded, else 1 - return ret; -} - -void Ed25519::point_add(Ed25519Point* result, Ed25519Point const* p, Ed25519Point const* q) -{ - // Compute R = P + Q - Curve25519::modular_add(c, p->y, p->x); - Curve25519::modular_add(d, q->y, q->x); - Curve25519::modular_multiply(a, c, d); - Curve25519::modular_subtract(c, p->y, p->x); - Curve25519::modular_subtract(d, q->y, q->x); - Curve25519::modular_multiply(b, c, d); - Curve25519::modular_multiply(c, p->z, q->z); - Curve25519::modular_add(c, c, c); - Curve25519::modular_multiply(d, p->t, q->t); - Curve25519::modular_multiply(d, d, Curve25519::CURVE_D_2); - Curve25519::modular_add(e, a, b); - Curve25519::modular_subtract(f, a, b); - Curve25519::modular_add(g, c, d); - Curve25519::modular_subtract(h, c, d); - Curve25519::modular_multiply(result->x, f, h); - Curve25519::modular_multiply(result->y, e, g); - Curve25519::modular_multiply(result->z, g, h); - Curve25519::modular_multiply(result->t, e, f); -} - -u8 Ed25519::compare(u8 const* a, u8 const* b, u8 n) -{ - u8 mask = 0; - for (u32 i = 0; i < n; i++) { - mask |= a[i] ^ b[i]; - } - - // Return 0 if A = B, else 1 - return ((u8)(mask | (~mask + 1))) >> 7; -} - -void Ed25519::copy(u8* a, u8 const* b, u32 n) -{ - for (u32 i = 0; i < n; i++) { - a[i] = b[i]; - } -} - -} diff --git a/Libraries/LibCrypto/Curves/Ed25519.h b/Libraries/LibCrypto/Curves/Ed25519.h deleted file mode 100644 index e7f5b21a6cc..00000000000 --- a/Libraries/LibCrypto/Curves/Ed25519.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2022, stelar7 - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Crypto::Curves { - -struct Ed25519Point { - u32 x[8] {}; - u32 y[8] {}; - u32 z[8] {}; - u32 t[8] {}; -}; - -class Ed25519 { -public: - static constexpr Ed25519Point BASE_POINT = { - { 0x8F25D51A, 0xC9562D60, 0x9525A7B2, 0x692CC760, 0xFDD6DC5C, 0xC0A4E231, 0xCD6E53FE, 0x216936D3 }, - { 0x66666658, 0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666 }, - { 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, - { 0xA5B7DDA3, 0x6DDE8AB3, 0x775152F5, 0x20F09F80, 0x64ABE37D, 0x66EA4E8E, 0xD78B7665, 0x67875F0F } - }; - - size_t key_size() { return 32; } - size_t signature_size() { return 64; } - ErrorOr generate_private_key(); - ErrorOr generate_public_key(ReadonlyBytes private_key); - - ErrorOr sign(ReadonlyBytes public_key, ReadonlyBytes private_key, ReadonlyBytes message); - bool verify(ReadonlyBytes public_key, ReadonlyBytes signature, ReadonlyBytes message); - -private: - void encode_point(Ed25519Point* point, u8* data); - u32 decode_point(Ed25519Point* point, u8 const* data); - - void point_add(Ed25519Point* result, Ed25519Point const* p, Ed25519Point const* q); - void point_double(Ed25519Point* result, Ed25519Point const* point); - void point_multiply_scalar(Ed25519Point* result, u8 const* scalar, Ed25519Point const* point); - - void barrett_reduce(u8* result, u8 const* input); - - void add(u8* result, u8 const* a, u8 const* b, u8 n); - u8 subtract(u8* result, u8 const* a, u8 const* b, u8 n); - void multiply(u8* result_low, u8* result_high, u8 const* a, u8 const* b, u8 n); - - void select(u8* result, u8 const* a, u8 const* b, u8 c, u8 n); - u8 compare(u8 const* a, u8 const* b, u8 n); - void copy(u8* a, u8 const* b, u32 n); - - u8 k[64] {}; - u8 p[32] {}; - u8 r[32] {}; - u8 s[32] {}; - Ed25519Point ka {}; - Ed25519Point rb {}; - Ed25519Point sb {}; - Ed25519Point u {}; - Ed25519Point v {}; - u32 a[8] {}; - u32 b[8] {}; - u32 c[8] {}; - u32 d[8] {}; - u32 e[8] {}; - u32 f[8] {}; - u32 g[8] {}; - u32 h[8] {}; -}; - -} diff --git a/Libraries/LibCrypto/Curves/Ed448.cpp b/Libraries/LibCrypto/Curves/Ed448.cpp deleted file mode 100644 index 1129bf5cff6..00000000000 --- a/Libraries/LibCrypto/Curves/Ed448.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2024, Altomani Gianluca - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -#include -#include - -namespace Crypto::Curves { - -ErrorOr Ed448::generate_private_key() -{ - auto key = TRY(OpenSSL_PKEY::wrap(EVP_PKEY_Q_keygen(nullptr, nullptr, "ED448"))); - - size_t key_size = EVP_PKEY_get_size(key.ptr()); - auto buf = TRY(ByteBuffer::create_uninitialized(key_size)); - - OPENSSL_TRY(EVP_PKEY_get_raw_private_key(key.ptr(), buf.data(), &key_size)); - - return buf.slice(0, key_size); -} - -ErrorOr Ed448::generate_public_key(ReadonlyBytes private_key) -{ - auto key = TRY(OpenSSL_PKEY::wrap(EVP_PKEY_new_raw_private_key(EVP_PKEY_ED448, nullptr, private_key.data(), private_key.size()))); - - size_t key_size = EVP_PKEY_get_size(key.ptr()); - auto buf = TRY(ByteBuffer::create_uninitialized(key_size)); - - OPENSSL_TRY(EVP_PKEY_get_raw_public_key(key.ptr(), buf.data(), &key_size)); - - return buf.slice(0, key_size); -} - -ErrorOr Ed448::sign(ReadonlyBytes private_key, ReadonlyBytes message, ReadonlyBytes context) -{ - auto key = TRY(OpenSSL_PKEY::wrap(EVP_PKEY_new_raw_private_key_ex(nullptr, "ED448", nullptr, private_key.data(), private_key.size()))); - - auto ctx = TRY(OpenSSL_MD_CTX::create()); - - OSSL_PARAM params[] = { - OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, const_cast(context.data()), context.size()), - OSSL_PARAM_END - }; - - OPENSSL_TRY(EVP_DigestSignInit_ex(ctx.ptr(), nullptr, nullptr, nullptr, nullptr, key.ptr(), params)); - - size_t sig_len = signature_size(); - auto sig = TRY(ByteBuffer::create_uninitialized(sig_len)); - - OPENSSL_TRY(EVP_DigestSign(ctx.ptr(), sig.data(), &sig_len, message.data(), message.size())); - - return sig.slice(0, sig_len); -} - -ErrorOr Ed448::verify(ReadonlyBytes public_key, ReadonlyBytes signature, ReadonlyBytes message, ReadonlyBytes context) -{ - auto key = TRY(OpenSSL_PKEY::wrap(EVP_PKEY_new_raw_public_key_ex(nullptr, "ED448", nullptr, public_key.data(), public_key.size()))); - - auto ctx = TRY(OpenSSL_MD_CTX::create()); - - OSSL_PARAM params[] = { - OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, const_cast(context.data()), context.size()), - OSSL_PARAM_END - }; - - OPENSSL_TRY(EVP_DigestVerifyInit_ex(ctx.ptr(), nullptr, nullptr, nullptr, nullptr, key.ptr(), params)); - - auto res = EVP_DigestVerify(ctx.ptr(), signature.data(), signature.size(), message.data(), message.size()); - if (res == 1) - return true; - if (res == 0) - return false; - OPENSSL_TRY(res); - VERIFY_NOT_REACHED(); -} -} diff --git a/Libraries/LibCrypto/Curves/Ed448.h b/Libraries/LibCrypto/Curves/Ed448.h deleted file mode 100644 index bba9a0456d9..00000000000 --- a/Libraries/LibCrypto/Curves/Ed448.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2024, Altomani Gianluca - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Crypto::Curves { - -class Ed448 { -public: - constexpr size_t key_size() const { return 57; } - constexpr size_t signature_size() const { return 114; } - ErrorOr generate_private_key(); - ErrorOr generate_public_key(ReadonlyBytes private_key); - - ErrorOr sign(ReadonlyBytes private_key, ReadonlyBytes message, ReadonlyBytes context = {}); - ErrorOr verify(ReadonlyBytes public_key, ReadonlyBytes signature, ReadonlyBytes message, ReadonlyBytes context = {}); -}; - -} diff --git a/Libraries/LibCrypto/Curves/EdwardsCurve.cpp b/Libraries/LibCrypto/Curves/EdwardsCurve.cpp new file mode 100644 index 00000000000..662ceed19e1 --- /dev/null +++ b/Libraries/LibCrypto/Curves/EdwardsCurve.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +#include +#include + +namespace Crypto::Curves { + +ErrorOr EdwardsCurve::generate_private_key() +{ + auto key = TRY(OpenSSL_PKEY::wrap(EVP_PKEY_Q_keygen(nullptr, nullptr, m_curve_name))); + + size_t key_size = 0; + OPENSSL_TRY(EVP_PKEY_get_raw_private_key(key.ptr(), nullptr, &key_size)); + + auto buf = TRY(ByteBuffer::create_uninitialized(key_size)); + OPENSSL_TRY(EVP_PKEY_get_raw_private_key(key.ptr(), buf.data(), &key_size)); + + return buf; +} + +ErrorOr EdwardsCurve::generate_public_key(ReadonlyBytes private_key) +{ + auto key = TRY(OpenSSL_PKEY::wrap(EVP_PKEY_new_raw_private_key_ex(nullptr, m_curve_name, nullptr, private_key.data(), private_key.size()))); + + size_t key_size = 0; + OPENSSL_TRY(EVP_PKEY_get_raw_public_key(key.ptr(), nullptr, &key_size)); + + auto buf = TRY(ByteBuffer::create_uninitialized(key_size)); + OPENSSL_TRY(EVP_PKEY_get_raw_public_key(key.ptr(), buf.data(), &key_size)); + + return buf; +} + +ErrorOr SignatureEdwardsCurve::sign(ReadonlyBytes private_key, ReadonlyBytes message, ReadonlyBytes context) +{ + auto key = TRY(OpenSSL_PKEY::wrap(EVP_PKEY_new_raw_private_key_ex(nullptr, m_curve_name, nullptr, private_key.data(), private_key.size()))); + + auto ctx = TRY(OpenSSL_MD_CTX::create()); + + OSSL_PARAM params[2] = { + OSSL_PARAM_END, + OSSL_PARAM_END + }; + + if (!context.is_null()) { + params[0] = OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, const_cast(context.data()), context.size()); + } + + OPENSSL_TRY(EVP_DigestSignInit_ex(ctx.ptr(), nullptr, nullptr, nullptr, nullptr, key.ptr(), params)); + + size_t sig_len = 0; + OPENSSL_TRY(EVP_DigestSign(ctx.ptr(), nullptr, &sig_len, message.data(), message.size())); + + auto sig = TRY(ByteBuffer::create_uninitialized(sig_len)); + OPENSSL_TRY(EVP_DigestSign(ctx.ptr(), sig.data(), &sig_len, message.data(), message.size())); + + return sig; +} + +ErrorOr SignatureEdwardsCurve::verify(ReadonlyBytes public_key, ReadonlyBytes signature, ReadonlyBytes message, ReadonlyBytes context) +{ + auto key = TRY(OpenSSL_PKEY::wrap(EVP_PKEY_new_raw_public_key_ex(nullptr, m_curve_name, nullptr, public_key.data(), public_key.size()))); + + auto ctx = TRY(OpenSSL_MD_CTX::create()); + + OSSL_PARAM params[2] = { + OSSL_PARAM_END, + OSSL_PARAM_END + }; + + if (!context.is_null()) { + params[0] = OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, const_cast(context.data()), context.size()); + } + + OPENSSL_TRY(EVP_DigestVerifyInit_ex(ctx.ptr(), nullptr, nullptr, nullptr, nullptr, key.ptr(), params)); + + auto res = EVP_DigestVerify(ctx.ptr(), signature.data(), signature.size(), message.data(), message.size()); + if (res == 1) + return true; + if (res == 0) + return false; + OPENSSL_TRY(res); + VERIFY_NOT_REACHED(); +} + +ErrorOr ExchangeEdwardsCurve::compute_coordinate(ReadonlyBytes private_key, ReadonlyBytes public_key) +{ + auto key = TRY(OpenSSL_PKEY::wrap(EVP_PKEY_new_raw_private_key_ex(nullptr, m_curve_name, nullptr, private_key.data(), private_key.size()))); + auto peerkey = TRY(OpenSSL_PKEY::wrap(EVP_PKEY_new_raw_public_key_ex(nullptr, m_curve_name, nullptr, public_key.data(), public_key.size()))); + + auto ctx = TRY(OpenSSL_PKEY_CTX::wrap(EVP_PKEY_CTX_new(key.ptr(), nullptr))); + + OPENSSL_TRY(EVP_PKEY_derive_init(ctx.ptr())); + OPENSSL_TRY(EVP_PKEY_derive_set_peer(ctx.ptr(), peerkey.ptr())); + + size_t key_size = 0; + OPENSSL_TRY(EVP_PKEY_derive(ctx.ptr(), nullptr, &key_size)); + + auto buf = TRY(ByteBuffer::create_uninitialized(key_size)); + OPENSSL_TRY(EVP_PKEY_derive(ctx.ptr(), buf.data(), &key_size)); + + return buf; +} + +} diff --git a/Libraries/LibCrypto/Curves/EdwardsCurve.h b/Libraries/LibCrypto/Curves/EdwardsCurve.h new file mode 100644 index 00000000000..2829e35646a --- /dev/null +++ b/Libraries/LibCrypto/Curves/EdwardsCurve.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Crypto::Curves { + +class EdwardsCurve { +public: + ErrorOr generate_private_key(); + ErrorOr generate_public_key(ReadonlyBytes private_key); + +protected: + EdwardsCurve(char const* curve_name) + : m_curve_name(curve_name) + { + } + + char const* m_curve_name; +}; + +class SignatureEdwardsCurve : public EdwardsCurve { +public: + ErrorOr sign(ReadonlyBytes private_key, ReadonlyBytes message, ReadonlyBytes context = {}); + ErrorOr verify(ReadonlyBytes public_key, ReadonlyBytes signature, ReadonlyBytes message, ReadonlyBytes context = {}); + +protected: + explicit SignatureEdwardsCurve(char const* curve_name) + : EdwardsCurve(curve_name) + { + } +}; + +class ExchangeEdwardsCurve : public EdwardsCurve { +public: + ErrorOr compute_coordinate(ReadonlyBytes private_key, ReadonlyBytes public_key); + +protected: + explicit ExchangeEdwardsCurve(char const* curve_name) + : EdwardsCurve(curve_name) + { + } +}; + +class Ed448 : public SignatureEdwardsCurve { +public: + Ed448() + : SignatureEdwardsCurve("ED448") + { + } +}; + +class X448 : public ExchangeEdwardsCurve { +public: + X448() + : ExchangeEdwardsCurve("X448") + { + } +}; + +class Ed25519 : public SignatureEdwardsCurve { +public: + Ed25519() + : SignatureEdwardsCurve("ED25519") + { + } +}; + +class X25519 : public ExchangeEdwardsCurve { +public: + X25519() + : ExchangeEdwardsCurve("X25519") + { + } +}; + +} diff --git a/Libraries/LibCrypto/Curves/X25519.cpp b/Libraries/LibCrypto/Curves/X25519.cpp deleted file mode 100644 index abac457b5ca..00000000000 --- a/Libraries/LibCrypto/Curves/X25519.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2022, stelar7 - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Crypto::Curves { - -static constexpr u8 BITS = 255; -static constexpr u8 BYTES = 32; -static constexpr u8 WORDS = 8; -static constexpr u32 A24 = 121666; - -static void conditional_swap(u32* first, u32* second, u32 condition) -{ - u32 mask = ~condition + 1; - for (auto i = 0; i < WORDS; i++) { - u32 temp = mask & (first[i] ^ second[i]); - first[i] ^= temp; - second[i] ^= temp; - } -} - -ErrorOr X25519::generate_private_key() -{ - auto buffer = TRY(ByteBuffer::create_uninitialized(BYTES)); - fill_with_secure_random(buffer); - return buffer; -} - -ErrorOr X25519::generate_public_key(ReadonlyBytes a) -{ - u8 generator[BYTES] { 9 }; - return compute_coordinate(a, { generator, BYTES }); -} - -// https://datatracker.ietf.org/doc/html/rfc7748#section-5 -ErrorOr X25519::compute_coordinate(ReadonlyBytes input_k, ReadonlyBytes input_u) -{ - u32 k[WORDS] {}; - u32 u[WORDS] {}; - u32 x1[WORDS] {}; - u32 x2[WORDS] {}; - u32 z1[WORDS] {}; - u32 z2[WORDS] {}; - u32 t1[WORDS] {}; - u32 t2[WORDS] {}; - - // Copy input to internal state - Curve25519::import_state(k, input_k.data()); - - // Set the three least significant bits of the first byte and the most significant bit of the last to zero, - // set the second most significant bit of the last byte to 1 - k[0] &= 0xFFFFFFF8; - k[7] &= 0x7FFFFFFF; - k[7] |= 0x40000000; - - // Copy coordinate to internal state - Curve25519::import_state(u, input_u.data()); - // mask the most significant bit in the final byte. - u[7] &= 0x7FFFFFFF; - - // Implementations MUST accept non-canonical values and process them as - // if they had been reduced modulo the field prime. - Curve25519::modular_reduce(u, u); - - Curve25519::set(x1, 1); - Curve25519::set(z1, 0); - Curve25519::copy(x2, u); - Curve25519::set(z2, 1); - - // Montgomery ladder - u32 swap = 0; - for (auto i = BITS - 1; i >= 0; i--) { - u32 b = (k[i / BYTES] >> (i % BYTES)) & 1; - - conditional_swap(x1, x2, swap ^ b); - conditional_swap(z1, z2, swap ^ b); - - swap = b; - - Curve25519::modular_add(t1, x2, z2); - Curve25519::modular_subtract(x2, x2, z2); - Curve25519::modular_add(z2, x1, z1); - Curve25519::modular_subtract(x1, x1, z1); - Curve25519::modular_multiply(t1, t1, x1); - Curve25519::modular_multiply(x2, x2, z2); - Curve25519::modular_square(z2, z2); - Curve25519::modular_square(x1, x1); - Curve25519::modular_subtract(t2, z2, x1); - Curve25519::modular_multiply_single(z1, t2, A24); - Curve25519::modular_add(z1, z1, x1); - Curve25519::modular_multiply(z1, z1, t2); - Curve25519::modular_multiply(x1, x1, z2); - Curve25519::modular_subtract(z2, t1, x2); - Curve25519::modular_square(z2, z2); - Curve25519::modular_multiply(z2, z2, u); - Curve25519::modular_add(x2, x2, t1); - Curve25519::modular_square(x2, x2); - } - - conditional_swap(x1, x2, swap); - conditional_swap(z1, z2, swap); - - // Retrieve affine representation - Curve25519::modular_multiply_inverse(u, z1); - Curve25519::modular_multiply(u, u, x1); - - // Encode state for export - auto buffer = TRY(ByteBuffer::create_uninitialized(BYTES)); - Curve25519::export_state(u, buffer.data()); - - return buffer; -} - -ErrorOr X25519::derive_premaster_key(ReadonlyBytes shared_point) -{ - VERIFY(shared_point.size() == BYTES); - ByteBuffer premaster_key = TRY(ByteBuffer::copy(shared_point)); - return premaster_key; -} - -} diff --git a/Libraries/LibCrypto/Curves/X25519.h b/Libraries/LibCrypto/Curves/X25519.h deleted file mode 100644 index 6fd62ec274d..00000000000 --- a/Libraries/LibCrypto/Curves/X25519.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2022, stelar7 - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Crypto::Curves { - -class X25519 { -public: - size_t key_size() { return 32; } - ErrorOr generate_private_key(); - ErrorOr generate_public_key(ReadonlyBytes a); - ErrorOr compute_coordinate(ReadonlyBytes a, ReadonlyBytes b); - ErrorOr derive_premaster_key(ReadonlyBytes shared_point); -}; - -} diff --git a/Libraries/LibCrypto/Curves/X448.cpp b/Libraries/LibCrypto/Curves/X448.cpp deleted file mode 100644 index 0e4cf4df187..00000000000 --- a/Libraries/LibCrypto/Curves/X448.cpp +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (c) 2022, stelar7 - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Crypto::Curves { - -static constexpr u16 BITS = 448; -static constexpr u8 BYTES = 56; -static constexpr u8 WORDS = 14; -static constexpr u32 A24 = 39082; - -static void import_state(u32* state, ReadonlyBytes data) -{ - for (auto i = 0; i < WORDS; i++) { - u32 value = ByteReader::load32(data.offset_pointer(sizeof(u32) * i)); - state[i] = AK::convert_between_host_and_little_endian(value); - } -} - -static ErrorOr export_state(u32* data) -{ - auto buffer = TRY(ByteBuffer::create_uninitialized(BYTES)); - - for (auto i = 0; i < WORDS; i++) { - u32 value = AK::convert_between_host_and_little_endian(data[i]); - ByteReader::store(buffer.offset_pointer(sizeof(u32) * i), value); - } - - return buffer; -} - -static void select(u32* state, u32* a, u32* b, u32 condition) -{ - // If B < (2^448 - 2^224 + 1) then R = B, else R = A - u32 mask = condition - 1; - - for (auto i = 0; i < WORDS; i++) { - state[i] = (a[i] & mask) | (b[i] & ~mask); - } -} - -static void set(u32* state, u32 value) -{ - state[0] = value; - - for (auto i = 1; i < WORDS; i++) { - state[i] = 0; - } -} - -static void copy(u32* state, u32* value) -{ - for (auto i = 0; i < WORDS; i++) { - state[i] = value[i]; - } -} - -static void conditional_swap(u32* first, u32* second, u32 condition) -{ - u32 mask = ~condition + 1; - for (auto i = 0; i < WORDS; i++) { - u32 temp = mask & (first[i] ^ second[i]); - first[i] ^= temp; - second[i] ^= temp; - } -} - -static void modular_reduce(u32* state, u32* data, u32 a_high) -{ - u64 temp = 1; - u32 other[WORDS]; - - // Compute B = A - (2^448 - 2^224 - 1) - for (auto i = 0; i < WORDS / 2; i++) { - temp += data[i]; - other[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - temp += 1; - - for (auto i = 7; i < WORDS; i++) { - temp += data[i]; - other[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - auto condition = (a_high + (u32)temp - 1) & 1; - select(state, other, data, condition); -} - -static void modular_multiply_single(u32* state, u32* first, u32 second) -{ - // Compute R = (A * B) mod p - u64 temp = 0; - u64 carry = 0; - u32 output[WORDS]; - - for (auto i = 0; i < WORDS; i++) { - temp += (u64)first[i] * second; - output[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - // Fast modular reduction - carry = temp; - for (auto i = 0; i < WORDS / 2; i++) { - temp += output[i]; - output[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - temp += carry; - for (auto i = WORDS / 2; i < WORDS; i++) { - temp += output[i]; - output[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - modular_reduce(state, output, (u32)temp); -} - -static void modular_multiply(u32* state, u32* first, u32* second) -{ - // Compute R = (A * B) mod p - - u64 temp = 0; - u64 carry = 0; - u32 output[WORDS * 2]; - - // Comba's method - for (auto i = 0; i < WORDS * 2; i++) { - if (i < 14) { - for (auto j = 0; j <= i; j++) { - temp += (u64)first[j] * second[i - j]; - carry += temp >> 32; - temp &= 0xFFFFFFFF; - } - } else { - for (auto j = i - 13; j < WORDS; j++) { - temp += (u64)first[j] * second[i - j]; - carry += temp >> 32; - temp &= 0xFFFFFFFF; - } - } - - output[i] = temp & 0xFFFFFFFF; - temp = carry & 0xFFFFFFFF; - carry >>= 32; - } - - // Fast modular reduction (first pass) - temp = 0; - for (auto i = 0; i < WORDS / 2; i++) { - temp += output[i]; - temp += output[i + 14]; - temp += output[i + 21]; - output[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - for (auto i = WORDS / 2; i < WORDS; i++) { - temp += output[i]; - temp += output[i + 7]; - temp += output[i + 14]; - temp += output[i + 14]; - output[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - // Fast modular reduction (second pass) - carry = temp; - for (auto i = 0; i < WORDS / 2; i++) { - temp += output[i]; - output[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - temp += carry; - for (auto i = WORDS / 2; i < WORDS; i++) { - temp += output[i]; - output[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - modular_reduce(state, output, (u32)temp); -} - -static void modular_square(u32* state, u32* value) -{ - // Compute R = (A ^ 2) mod p - modular_multiply(state, value, value); -} - -static void modular_add(u32* state, u32* first, u32* second) -{ - u64 temp = 0; - - // Compute R = A + B - for (auto i = 0; i < WORDS; i++) { - temp += first[i]; - temp += second[i]; - state[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - modular_reduce(state, state, (u32)temp); -} - -static void modular_subtract(u32* state, u32* first, u32* second) -{ - i64 temp = -1; - - // Compute R = A + (2^448 - 2^224 - 1) - B - for (auto i = 0; i < 7; i++) { - temp += first[i]; - temp -= second[i]; - state[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - temp -= 1; - - for (auto i = 7; i < 14; i++) { - temp += first[i]; - temp -= second[i]; - state[i] = temp & 0xFFFFFFFF; - temp >>= 32; - } - - temp += 1; - - modular_reduce(state, state, (u32)temp); -} - -static void to_power_of_2n(u32* state, u32* value, u8 n) -{ - // Compute R = (A ^ (2^n)) mod p - modular_square(state, value); - for (auto i = 1; i < n; i++) { - modular_square(state, state); - } -} - -static void modular_multiply_inverse(u32* state, u32* value) -{ - // Compute R = A^-1 mod p - u32 u[WORDS]; - u32 v[WORDS]; - - modular_square(u, value); - modular_multiply(u, u, value); - modular_square(u, u); - modular_multiply(v, u, value); - to_power_of_2n(u, v, 3); - modular_multiply(v, u, v); - to_power_of_2n(u, v, 6); - modular_multiply(u, u, v); - modular_square(u, u); - modular_multiply(v, u, value); - to_power_of_2n(u, v, 13); - modular_multiply(u, u, v); - modular_square(u, u); - modular_multiply(v, u, value); - to_power_of_2n(u, v, 27); - modular_multiply(u, u, v); - modular_square(u, u); - modular_multiply(v, u, value); - to_power_of_2n(u, v, 55); - modular_multiply(u, u, v); - modular_square(u, u); - modular_multiply(v, u, value); - to_power_of_2n(u, v, 111); - modular_multiply(v, u, v); - modular_square(u, v); - modular_multiply(u, u, value); - to_power_of_2n(u, u, 223); - modular_multiply(u, u, v); - modular_square(u, u); - modular_square(u, u); - modular_multiply(state, u, value); -} - -ErrorOr X448::generate_private_key() -{ - auto buffer = TRY(ByteBuffer::create_uninitialized(BYTES)); - fill_with_secure_random(buffer); - return buffer; -} - -ErrorOr X448::generate_public_key(ReadonlyBytes a) -{ - u8 generator[BYTES] { 5 }; - return compute_coordinate(a, { generator, BYTES }); -} - -// https://datatracker.ietf.org/doc/html/rfc7748#section-5 -ErrorOr X448::compute_coordinate(ReadonlyBytes input_k, ReadonlyBytes input_u) -{ - u32 k[WORDS] {}; - u32 u[WORDS] {}; - u32 x1[WORDS] {}; - u32 x2[WORDS] {}; - u32 z1[WORDS] {}; - u32 z2[WORDS] {}; - u32 t1[WORDS] {}; - u32 t2[WORDS] {}; - - // Copy input to internal state - import_state(k, input_k); - - // Set the two least significant bits of the first byte to 0, and the most significant bit of the last byte to 1 - k[0] &= 0xFFFFFFFC; - k[13] |= 0x80000000; - - // Copy coordinate to internal state - import_state(u, input_u); - - // Implementations MUST accept non-canonical values and process them as - // if they had been reduced modulo the field prime. - modular_reduce(u, u, 0); - - set(x1, 1); - set(z1, 0); - copy(x2, u); - set(z2, 1); - - // Montgomery ladder - u32 swap = 0; - for (auto i = BITS - 1; i >= 0; i--) { - u32 b = (k[i / 32] >> (i % 32)) & 1; - - conditional_swap(x1, x2, swap ^ b); - conditional_swap(z1, z2, swap ^ b); - - swap = b; - - modular_add(t1, x2, z2); - modular_subtract(x2, x2, z2); - modular_add(z2, x1, z1); - modular_subtract(x1, x1, z1); - modular_multiply(t1, t1, x1); - modular_multiply(x2, x2, z2); - modular_square(z2, z2); - modular_square(x1, x1); - modular_subtract(t2, z2, x1); - modular_multiply_single(z1, t2, A24); - modular_add(z1, z1, x1); - modular_multiply(z1, z1, t2); - modular_multiply(x1, x1, z2); - modular_subtract(z2, t1, x2); - modular_square(z2, z2); - modular_multiply(z2, z2, u); - modular_add(x2, x2, t1); - modular_square(x2, x2); - } - - conditional_swap(x1, x2, swap); - conditional_swap(z1, z2, swap); - - // Retrieve affine representation - modular_multiply_inverse(u, z1); - modular_multiply(u, u, x1); - - // Encode state for export - return export_state(u); -} - -ErrorOr X448::derive_premaster_key(ReadonlyBytes shared_point) -{ - VERIFY(shared_point.size() == BYTES); - ByteBuffer premaster_key = TRY(ByteBuffer::copy(shared_point)); - return premaster_key; -} - -} diff --git a/Libraries/LibCrypto/Curves/X448.h b/Libraries/LibCrypto/Curves/X448.h deleted file mode 100644 index 9dffca25881..00000000000 --- a/Libraries/LibCrypto/Curves/X448.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2022, stelar7 - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Crypto::Curves { - -class X448 { -public: - size_t key_size() { return 56; } - ErrorOr generate_private_key(); - ErrorOr generate_public_key(ReadonlyBytes a); - ErrorOr compute_coordinate(ReadonlyBytes a, ReadonlyBytes b); - ErrorOr derive_premaster_key(ReadonlyBytes shared_point); -}; - -} diff --git a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index 11f223f1fcd..de8ea53c89f 100644 --- a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -3,7 +3,7 @@ * Copyright (c) 2024, stelar7 * Copyright (c) 2024, Jelle Raaijmakers * Copyright (c) 2024, Andreas Kling - * Copyright (c) 2024, Altomani Gianluca + * Copyright (c) 2024-2025, Altomani Gianluca * * SPDX-License-Identifier: BSD-2-Clause */ @@ -17,11 +17,8 @@ #include #include #include -#include -#include +#include #include -#include -#include #include #include #include @@ -6091,7 +6088,7 @@ WebIDL::ExceptionOr> ED25519::sign([[maybe_unused]] Alg return WebIDL::OperationError::create(realm, "Failed to generate public key"_string); auto public_key = maybe_public_key.release_value(); - auto maybe_signature = curve.sign(public_key, private_key, message); + auto maybe_signature = curve.sign(private_key, message); if (maybe_signature.is_error()) return WebIDL::OperationError::create(realm, "Failed to sign message"_string); auto signature = maybe_signature.release_value(); @@ -6122,10 +6119,14 @@ WebIDL::ExceptionOr ED25519::verify([[maybe_unused]] AlgorithmParams // 9. Let result be a boolean with the value true if the signature is valid and the value false otherwise. ::Crypto::Curves::Ed25519 curve; - auto result = curve.verify(public_key, signature, message); + auto maybe_verified = curve.verify(key->handle().get(), signature, message); + if (maybe_verified.is_error()) { + auto error_message = MUST(String::from_utf8(maybe_verified.error().string_literal())); + return WebIDL::OperationError::create(realm, error_message); + } // 10. Return result. - return JS::Value(result); + return maybe_verified.release_value(); } // https://wicg.github.io/webcrypto-secure-curves/#ed448-operations diff --git a/Tests/LibCrypto/TestCurves.cpp b/Tests/LibCrypto/TestCurves.cpp index 356cb4416a1..7dd36665d8e 100644 --- a/Tests/LibCrypto/TestCurves.cpp +++ b/Tests/LibCrypto/TestCurves.cpp @@ -5,9 +5,8 @@ */ #include +#include #include -#include -#include #include TEST_CASE(test_x25519) diff --git a/Tests/LibCrypto/TestEd25519.cpp b/Tests/LibCrypto/TestEd25519.cpp index 1b7751061b9..553588b29f6 100644 --- a/Tests/LibCrypto/TestEd25519.cpp +++ b/Tests/LibCrypto/TestEd25519.cpp @@ -5,7 +5,7 @@ */ #include -#include +#include #include // https://datatracker.ietf.org/doc/html/rfc8032#section-7.1 @@ -45,9 +45,9 @@ TEST_CASE(TEST_1) Crypto::Curves::Ed25519 curve; - auto generated_signature = MUST(curve.sign(public_key, private_key, message)); + auto generated_signature = TRY_OR_FAIL(curve.sign(private_key, message)); EXPECT_EQ(generated_signature, expected_signature); - EXPECT_EQ(true, curve.verify(public_key, expected_signature, message)); + EXPECT(TRY_OR_FAIL(curve.verify(public_key, expected_signature, message))); } TEST_CASE(TEST_2) @@ -86,9 +86,9 @@ TEST_CASE(TEST_2) Crypto::Curves::Ed25519 curve; - auto generated_signature = MUST(curve.sign(public_key, private_key, message)); + auto generated_signature = TRY_OR_FAIL(curve.sign(private_key, message)); EXPECT_EQ(generated_signature, expected_signature); - EXPECT_EQ(true, curve.verify(public_key, expected_signature, message)); + EXPECT(TRY_OR_FAIL(curve.verify(public_key, expected_signature, message))); } TEST_CASE(TEST_3) @@ -128,9 +128,9 @@ TEST_CASE(TEST_3) Crypto::Curves::Ed25519 curve; - auto generated_signature = MUST(curve.sign(public_key, private_key, message)); + auto generated_signature = TRY_OR_FAIL(curve.sign(private_key, message)); EXPECT_EQ(generated_signature, expected_signature); - EXPECT_EQ(true, curve.verify(public_key, expected_signature, message)); + EXPECT(TRY_OR_FAIL(curve.verify(public_key, expected_signature, message))); } TEST_CASE(TEST_SHA_ABC) @@ -179,7 +179,7 @@ TEST_CASE(TEST_SHA_ABC) Crypto::Curves::Ed25519 curve; - auto generated_signature = MUST(curve.sign(public_key, private_key, message)); + auto generated_signature = TRY_OR_FAIL(curve.sign(private_key, message)); EXPECT_EQ(generated_signature, expected_signature); - EXPECT_EQ(true, curve.verify(public_key, expected_signature, message)); + EXPECT(TRY_OR_FAIL(curve.verify(public_key, expected_signature, message))); }