LibCrypto: Add SECPxxxr1 methods to work with points

It looks like the `SECPxxxr1` was made mainly to work with the TLS
implementation which requires everything to be bytes. This is not always
 the case and a loss of generality.

 I have added some methods that take and return `UnsignedBigInteger`s
 for better interoperability with ASN.1 stuff. I would like to remove
 the old methods relying on bytes, but I haven't made my mind around how
  to generalize it for all curves.
This commit is contained in:
devgianlu 2024-11-26 17:43:41 +01:00 committed by Andreas Kling
parent 9eea94aa14
commit f897af36c2
Notes: github-actions[bot] 2024-11-27 10:01:55 +00:00

View file

@ -27,6 +27,20 @@ struct SECPxxxr1CurveParameters {
StringView generator_point;
};
struct SECPxxxr1Point {
UnsignedBigInteger x;
UnsignedBigInteger y;
ErrorOr<ByteBuffer> to_uncompressed() const
{
auto bytes = TRY(ByteBuffer::create_uninitialized(1 + x.byte_length() + y.byte_length()));
bytes[0] = 0x04; // uncompressed
auto x_size = x.export_data(bytes.span().slice(1));
auto y_size = y.export_data(bytes.span().slice(1 + x_size));
return bytes.slice(0, 1 + x_size + y_size);
}
};
template<size_t bit_size, SECPxxxr1CurveParameters const& CURVE_PARAMETERS>
class SECPxxxr1 : public EllipticCurve {
private:
@ -145,11 +159,24 @@ public:
return buffer;
}
ErrorOr<UnsignedBigInteger> generate_private_key_scalar()
{
auto buffer = TRY(generate_private_key());
return UnsignedBigInteger::import_data(buffer.data(), buffer.size());
}
ErrorOr<ByteBuffer> generate_public_key(ReadonlyBytes a) override
{
return compute_coordinate(a, GENERATOR_POINT);
}
ErrorOr<SECPxxxr1Point> generate_public_key_point(UnsignedBigInteger scalar)
{
VERIFY(scalar.byte_length() == KEY_BYTE_SIZE);
return compute_coordinate_point(scalar, SECPxxxr1Point { UnsignedBigInteger::import_data(GENERATOR_POINT.data() + 1, KEY_BYTE_SIZE), UnsignedBigInteger::import_data(GENERATOR_POINT.data() + 1 + KEY_BYTE_SIZE, KEY_BYTE_SIZE) });
}
ErrorOr<ByteBuffer> compute_coordinate(ReadonlyBytes scalar_bytes, ReadonlyBytes point_bytes) override
{
AK::FixedMemoryStream scalar_stream { scalar_bytes };
@ -168,6 +195,22 @@ public:
return buf;
}
ErrorOr<SECPxxxr1Point> compute_coordinate_point(UnsignedBigInteger scalar, SECPxxxr1Point point)
{
// FIXME: this is very ugly, but it gets the job done.
auto scalar_bytes = TRY(ByteBuffer::create_uninitialized(scalar.byte_length()));
auto scalar_size = scalar.export_data(scalar_bytes.span());
auto point_bytes = TRY(point.to_uncompressed());
auto result_bytes = TRY(compute_coordinate(scalar_bytes.span().slice(0, scalar_size), point_bytes));
VERIFY(result_bytes[0] == 0x04);
return SECPxxxr1Point {
.x = UnsignedBigInteger::import_data(result_bytes.data() + 1, KEY_BYTE_SIZE),
.y = UnsignedBigInteger::import_data(result_bytes.data() + 1 + KEY_BYTE_SIZE, KEY_BYTE_SIZE),
};
}
ErrorOr<ByteBuffer> derive_premaster_key(ReadonlyBytes shared_point) override
{
VERIFY(shared_point.size() == POINT_BYTE_SIZE);
@ -178,6 +221,11 @@ public:
return premaster_key;
}
ErrorOr<SECPxxxr1Point> derive_premaster_key_point(SECPxxxr1Point shared_point)
{
return shared_point;
}
ErrorOr<bool> verify(ReadonlyBytes hash, ReadonlyBytes pubkey, ReadonlyBytes signature)
{
Crypto::ASN1::Decoder asn1_decoder(signature);