From f897af36c2b5536b0f4baa092352c25bbb1dff52 Mon Sep 17 00:00:00 2001 From: devgianlu Date: Tue, 26 Nov 2024 17:43:41 +0100 Subject: [PATCH] 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. --- Libraries/LibCrypto/Curves/SECPxxxr1.h | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/Libraries/LibCrypto/Curves/SECPxxxr1.h b/Libraries/LibCrypto/Curves/SECPxxxr1.h index 8c5871f0db1..b4fd2110ae6 100644 --- a/Libraries/LibCrypto/Curves/SECPxxxr1.h +++ b/Libraries/LibCrypto/Curves/SECPxxxr1.h @@ -27,6 +27,20 @@ struct SECPxxxr1CurveParameters { StringView generator_point; }; +struct SECPxxxr1Point { + UnsignedBigInteger x; + UnsignedBigInteger y; + + ErrorOr 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 class SECPxxxr1 : public EllipticCurve { private: @@ -145,11 +159,24 @@ public: return buffer; } + ErrorOr generate_private_key_scalar() + { + auto buffer = TRY(generate_private_key()); + return UnsignedBigInteger::import_data(buffer.data(), buffer.size()); + } + ErrorOr generate_public_key(ReadonlyBytes a) override { return compute_coordinate(a, GENERATOR_POINT); } + ErrorOr 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 compute_coordinate(ReadonlyBytes scalar_bytes, ReadonlyBytes point_bytes) override { AK::FixedMemoryStream scalar_stream { scalar_bytes }; @@ -168,6 +195,22 @@ public: return buf; } + ErrorOr 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 derive_premaster_key(ReadonlyBytes shared_point) override { VERIFY(shared_point.size() == POINT_BYTE_SIZE); @@ -178,6 +221,11 @@ public: return premaster_key; } + ErrorOr derive_premaster_key_point(SECPxxxr1Point shared_point) + { + return shared_point; + } + ErrorOr verify(ReadonlyBytes hash, ReadonlyBytes pubkey, ReadonlyBytes signature) { Crypto::ASN1::Decoder asn1_decoder(signature);