mirror of
https://github.com/SerenityOS/serenity
synced 2024-09-06 17:06:31 +00:00
LibCrypto: Add Ed25519
This commit is contained in:
parent
9aaeaf8a22
commit
7d6b26e613
|
@ -4,6 +4,7 @@ set(TEST_SOURCES
|
||||||
TestChecksum.cpp
|
TestChecksum.cpp
|
||||||
TestChaCha20.cpp
|
TestChaCha20.cpp
|
||||||
TestCurves.cpp
|
TestCurves.cpp
|
||||||
|
TestEd25519.cpp
|
||||||
TestHash.cpp
|
TestHash.cpp
|
||||||
TestHMAC.cpp
|
TestHMAC.cpp
|
||||||
TestPoly1305.cpp
|
TestPoly1305.cpp
|
||||||
|
|
185
Tests/LibCrypto/TestEd25519.cpp
Normal file
185
Tests/LibCrypto/TestEd25519.cpp
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, stelar7 <dudedbz@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/ByteBuffer.h>
|
||||||
|
#include <LibCrypto/Curves/Ed25519.h>
|
||||||
|
#include <LibTest/TestCase.h>
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc8032#section-7.1
|
||||||
|
TEST_CASE(TEST_1)
|
||||||
|
{
|
||||||
|
u8 private_bytes[32] = {
|
||||||
|
0x9d, 0x61, 0xb1, 0x9d, 0xef, 0xfd, 0x5a, 0x60,
|
||||||
|
0xba, 0x84, 0x4a, 0xf4, 0x92, 0xec, 0x2c, 0xc4,
|
||||||
|
0x44, 0x49, 0xc5, 0x69, 0x7b, 0x32, 0x69, 0x19,
|
||||||
|
0x70, 0x3b, 0xac, 0x03, 0x1c, 0xae, 0x7f, 0x60
|
||||||
|
};
|
||||||
|
|
||||||
|
u8 public_bytes[32] = {
|
||||||
|
0xd7, 0x5a, 0x98, 0x01, 0x82, 0xb1, 0x0a, 0xb7,
|
||||||
|
0xd5, 0x4b, 0xfe, 0xd3, 0xc9, 0x64, 0x07, 0x3a,
|
||||||
|
0x0e, 0xe1, 0x72, 0xf3, 0xda, 0xa6, 0x23, 0x25,
|
||||||
|
0xaf, 0x02, 0x1a, 0x68, 0xf7, 0x07, 0x51, 0x1a
|
||||||
|
};
|
||||||
|
|
||||||
|
u8 message_bytes[0] = {};
|
||||||
|
|
||||||
|
u8 signature_bytes[64] = {
|
||||||
|
0xe5, 0x56, 0x43, 0x00, 0xc3, 0x60, 0xac, 0x72,
|
||||||
|
0x90, 0x86, 0xe2, 0xcc, 0x80, 0x6e, 0x82, 0x8a,
|
||||||
|
0x84, 0x87, 0x7f, 0x1e, 0xb8, 0xe5, 0xd9, 0x74,
|
||||||
|
0xd8, 0x73, 0xe0, 0x65, 0x22, 0x49, 0x01, 0x55,
|
||||||
|
0x5f, 0xb8, 0x82, 0x15, 0x90, 0xa3, 0x3b, 0xac,
|
||||||
|
0xc6, 0x1e, 0x39, 0x70, 0x1c, 0xf9, 0xb4, 0x6b,
|
||||||
|
0xd2, 0x5b, 0xf5, 0xf0, 0x59, 0x5b, 0xbe, 0x24,
|
||||||
|
0x65, 0x51, 0x41, 0x43, 0x8e, 0x7a, 0x10, 0x0b
|
||||||
|
};
|
||||||
|
|
||||||
|
ReadonlyBytes private_key { private_bytes, 32 };
|
||||||
|
ReadonlyBytes public_key { public_bytes, 32 };
|
||||||
|
ReadonlyBytes message { message_bytes, 0 };
|
||||||
|
ReadonlyBytes expected_signature { signature_bytes, 64 };
|
||||||
|
|
||||||
|
Crypto::Curves::Ed25519 curve;
|
||||||
|
|
||||||
|
auto generated_signature = MUST(curve.sign(public_key, private_key, message));
|
||||||
|
EXPECT_EQ(generated_signature, expected_signature);
|
||||||
|
EXPECT_EQ(true, curve.verify(public_key, expected_signature, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(TEST_2)
|
||||||
|
{
|
||||||
|
u8 private_bytes[32] = {
|
||||||
|
0x4c, 0xcd, 0x08, 0x9b, 0x28, 0xff, 0x96, 0xda,
|
||||||
|
0x9d, 0xb6, 0xc3, 0x46, 0xec, 0x11, 0x4e, 0x0f,
|
||||||
|
0x5b, 0x8a, 0x31, 0x9f, 0x35, 0xab, 0xa6, 0x24,
|
||||||
|
0xda, 0x8c, 0xf6, 0xed, 0x4f, 0xb8, 0xa6, 0xfb
|
||||||
|
};
|
||||||
|
|
||||||
|
u8 public_bytes[32] = {
|
||||||
|
0x3d, 0x40, 0x17, 0xc3, 0xe8, 0x43, 0x89, 0x5a,
|
||||||
|
0x92, 0xb7, 0x0a, 0xa7, 0x4d, 0x1b, 0x7e, 0xbc,
|
||||||
|
0x9c, 0x98, 0x2c, 0xcf, 0x2e, 0xc4, 0x96, 0x8c,
|
||||||
|
0xc0, 0xcd, 0x55, 0xf1, 0x2a, 0xf4, 0x66, 0x0c
|
||||||
|
};
|
||||||
|
|
||||||
|
u8 message_bytes[1] = { 0x72 };
|
||||||
|
|
||||||
|
u8 signature_bytes[64] = {
|
||||||
|
0x92, 0xa0, 0x09, 0xa9, 0xf0, 0xd4, 0xca, 0xb8,
|
||||||
|
0x72, 0x0e, 0x82, 0x0b, 0x5f, 0x64, 0x25, 0x40,
|
||||||
|
0xa2, 0xb2, 0x7b, 0x54, 0x16, 0x50, 0x3f, 0x8f,
|
||||||
|
0xb3, 0x76, 0x22, 0x23, 0xeb, 0xdb, 0x69, 0xda,
|
||||||
|
0x08, 0x5a, 0xc1, 0xe4, 0x3e, 0x15, 0x99, 0x6e,
|
||||||
|
0x45, 0x8f, 0x36, 0x13, 0xd0, 0xf1, 0x1d, 0x8c,
|
||||||
|
0x38, 0x7b, 0x2e, 0xae, 0xb4, 0x30, 0x2a, 0xee,
|
||||||
|
0xb0, 0x0d, 0x29, 0x16, 0x12, 0xbb, 0x0c, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
ReadonlyBytes private_key { private_bytes, 32 };
|
||||||
|
ReadonlyBytes public_key { public_bytes, 32 };
|
||||||
|
ReadonlyBytes message { message_bytes, 1 };
|
||||||
|
ReadonlyBytes expected_signature { signature_bytes, 64 };
|
||||||
|
|
||||||
|
Crypto::Curves::Ed25519 curve;
|
||||||
|
|
||||||
|
auto generated_signature = MUST(curve.sign(public_key, private_key, message));
|
||||||
|
EXPECT_EQ(generated_signature, expected_signature);
|
||||||
|
EXPECT_EQ(true, curve.verify(public_key, expected_signature, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(TEST_3)
|
||||||
|
{
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc8032#section-7.1
|
||||||
|
u8 private_bytes[32] = {
|
||||||
|
0xc5, 0xaa, 0x8d, 0xf4, 0x3f, 0x9f, 0x83, 0x7b,
|
||||||
|
0xed, 0xb7, 0x44, 0x2f, 0x31, 0xdc, 0xb7, 0xb1,
|
||||||
|
0x66, 0xd3, 0x85, 0x35, 0x07, 0x6f, 0x09, 0x4b,
|
||||||
|
0x85, 0xce, 0x3a, 0x2e, 0x0b, 0x44, 0x58, 0xf7
|
||||||
|
};
|
||||||
|
|
||||||
|
u8 public_bytes[32] = {
|
||||||
|
0xfc, 0x51, 0xcd, 0x8e, 0x62, 0x18, 0xa1, 0xa3,
|
||||||
|
0x8d, 0xa4, 0x7e, 0xd0, 0x02, 0x30, 0xf0, 0x58,
|
||||||
|
0x08, 0x16, 0xed, 0x13, 0xba, 0x33, 0x03, 0xac,
|
||||||
|
0x5d, 0xeb, 0x91, 0x15, 0x48, 0x90, 0x80, 0x25
|
||||||
|
};
|
||||||
|
|
||||||
|
u8 message_bytes[2] = { 0xaf, 0x82 };
|
||||||
|
|
||||||
|
u8 signature_bytes[64] = {
|
||||||
|
0x62, 0x91, 0xd6, 0x57, 0xde, 0xec, 0x24, 0x02,
|
||||||
|
0x48, 0x27, 0xe6, 0x9c, 0x3a, 0xbe, 0x01, 0xa3,
|
||||||
|
0x0c, 0xe5, 0x48, 0xa2, 0x84, 0x74, 0x3a, 0x44,
|
||||||
|
0x5e, 0x36, 0x80, 0xd7, 0xdb, 0x5a, 0xc3, 0xac,
|
||||||
|
0x18, 0xff, 0x9b, 0x53, 0x8d, 0x16, 0xf2, 0x90,
|
||||||
|
0xae, 0x67, 0xf7, 0x60, 0x98, 0x4d, 0xc6, 0x59,
|
||||||
|
0x4a, 0x7c, 0x15, 0xe9, 0x71, 0x6e, 0xd2, 0x8d,
|
||||||
|
0xc0, 0x27, 0xbe, 0xce, 0xea, 0x1e, 0xc4, 0x0a
|
||||||
|
};
|
||||||
|
|
||||||
|
ReadonlyBytes private_key { private_bytes, 32 };
|
||||||
|
ReadonlyBytes public_key { public_bytes, 32 };
|
||||||
|
ReadonlyBytes message { message_bytes, 2 };
|
||||||
|
ReadonlyBytes expected_signature { signature_bytes, 64 };
|
||||||
|
|
||||||
|
Crypto::Curves::Ed25519 curve;
|
||||||
|
|
||||||
|
auto generated_signature = MUST(curve.sign(public_key, private_key, message));
|
||||||
|
EXPECT_EQ(generated_signature, expected_signature);
|
||||||
|
EXPECT_EQ(true, curve.verify(public_key, expected_signature, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(TEST_SHA_ABC)
|
||||||
|
{
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc8032#section-7.1
|
||||||
|
u8 private_bytes[32] = {
|
||||||
|
0x83, 0x3f, 0xe6, 0x24, 0x09, 0x23, 0x7b, 0x9d,
|
||||||
|
0x62, 0xec, 0x77, 0x58, 0x75, 0x20, 0x91, 0x1e,
|
||||||
|
0x9a, 0x75, 0x9c, 0xec, 0x1d, 0x19, 0x75, 0x5b,
|
||||||
|
0x7d, 0xa9, 0x01, 0xb9, 0x6d, 0xca, 0x3d, 0x42
|
||||||
|
};
|
||||||
|
|
||||||
|
u8 public_bytes[32] = {
|
||||||
|
0xec, 0x17, 0x2b, 0x93, 0xad, 0x5e, 0x56, 0x3b,
|
||||||
|
0xf4, 0x93, 0x2c, 0x70, 0xe1, 0x24, 0x50, 0x34,
|
||||||
|
0xc3, 0x54, 0x67, 0xef, 0x2e, 0xfd, 0x4d, 0x64,
|
||||||
|
0xeb, 0xf8, 0x19, 0x68, 0x34, 0x67, 0xe2, 0xbf
|
||||||
|
};
|
||||||
|
|
||||||
|
u8 message_bytes[64] = {
|
||||||
|
0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
|
||||||
|
0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
|
||||||
|
0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
|
||||||
|
0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
|
||||||
|
0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
|
||||||
|
0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
|
||||||
|
0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
|
||||||
|
0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f
|
||||||
|
};
|
||||||
|
|
||||||
|
u8 signature_bytes[64] = {
|
||||||
|
0xdc, 0x2a, 0x44, 0x59, 0xe7, 0x36, 0x96, 0x33,
|
||||||
|
0xa5, 0x2b, 0x1b, 0xf2, 0x77, 0x83, 0x9a, 0x00,
|
||||||
|
0x20, 0x10, 0x09, 0xa3, 0xef, 0xbf, 0x3e, 0xcb,
|
||||||
|
0x69, 0xbe, 0xa2, 0x18, 0x6c, 0x26, 0xb5, 0x89,
|
||||||
|
0x09, 0x35, 0x1f, 0xc9, 0xac, 0x90, 0xb3, 0xec,
|
||||||
|
0xfd, 0xfb, 0xc7, 0xc6, 0x64, 0x31, 0xe0, 0x30,
|
||||||
|
0x3d, 0xca, 0x17, 0x9c, 0x13, 0x8a, 0xc1, 0x7a,
|
||||||
|
0xd9, 0xbe, 0xf1, 0x17, 0x73, 0x31, 0xa7, 0x04
|
||||||
|
};
|
||||||
|
|
||||||
|
ReadonlyBytes private_key { private_bytes, 32 };
|
||||||
|
ReadonlyBytes public_key { public_bytes, 32 };
|
||||||
|
ReadonlyBytes message { message_bytes, 64 };
|
||||||
|
ReadonlyBytes expected_signature { signature_bytes, 64 };
|
||||||
|
|
||||||
|
Crypto::Curves::Ed25519 curve;
|
||||||
|
|
||||||
|
auto generated_signature = MUST(curve.sign(public_key, private_key, message));
|
||||||
|
EXPECT_EQ(generated_signature, expected_signature);
|
||||||
|
EXPECT_EQ(true, curve.verify(public_key, expected_signature, message));
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ set(SOURCES
|
||||||
Cipher/AES.cpp
|
Cipher/AES.cpp
|
||||||
Cipher/ChaCha20.cpp
|
Cipher/ChaCha20.cpp
|
||||||
Curves/Curve25519.cpp
|
Curves/Curve25519.cpp
|
||||||
|
Curves/Ed25519.cpp
|
||||||
Curves/SECP256r1.cpp
|
Curves/SECP256r1.cpp
|
||||||
Curves/X25519.cpp
|
Curves/X25519.cpp
|
||||||
Curves/X448.cpp
|
Curves/X448.cpp
|
||||||
|
|
433
Userland/Libraries/LibCrypto/Curves/Ed25519.cpp
Normal file
433
Userland/Libraries/LibCrypto/Curves/Ed25519.cpp
Normal file
|
@ -0,0 +1,433 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, stelar7 <dudedbz@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/Random.h>
|
||||||
|
#include <LibCrypto/Curves/Curve25519.h>
|
||||||
|
#include <LibCrypto/Curves/Ed25519.h>
|
||||||
|
#include <LibCrypto/Hash/SHA2.h>
|
||||||
|
|
||||||
|
namespace Crypto::Curves {
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5
|
||||||
|
ErrorOr<ByteBuffer> 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_random(buffer.data(), buffer.size());
|
||||||
|
return buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5
|
||||||
|
ErrorOr<ByteBuffer> 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<ByteBuffer> 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.
|
||||||
|
Crypto::Hash::SHA512 hash;
|
||||||
|
// 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.
|
||||||
|
Crypto::Hash::SHA512 hash;
|
||||||
|
// 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
75
Userland/Libraries/LibCrypto/Curves/Ed25519.h
Normal file
75
Userland/Libraries/LibCrypto/Curves/Ed25519.h
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, stelar7 <dudedbz@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/ByteBuffer.h>
|
||||||
|
#include <LibCrypto/Curves/EllipticCurve.h>
|
||||||
|
|
||||||
|
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<ByteBuffer> generate_private_key();
|
||||||
|
ErrorOr<ByteBuffer> generate_public_key(ReadonlyBytes private_key);
|
||||||
|
|
||||||
|
ErrorOr<ByteBuffer> 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] {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue