diff --git a/Tests/LibCrypto/TestRSA.cpp b/Tests/LibCrypto/TestRSA.cpp index 56dd4950636..322f03cfcfb 100644 --- a/Tests/LibCrypto/TestRSA.cpp +++ b/Tests/LibCrypto/TestRSA.cpp @@ -4,8 +4,8 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include -#include #include #include #include @@ -108,6 +108,39 @@ l3vmuDEF3/Bo1C1HTg0xRV/l } } +TEST_CASE(test_RSA_keygen_enc) +{ + auto keypem = R"(-----BEGIN PRIVATE KEY----- +MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA5HMXMnY+RhEcYXsa +OyB/YkcrO1nxIeyDCMqwg5MDrSXO8vPXSEb9AZUNMF1jKiFWPoHxZ+foRxrLv4d9 +sV/ETwIDAQABAkBpC37UJkjWQRHyxP83xuasExuO6/mT5sQN692kcppTJ9wHNWoD +9ZcREk4GGiklu4qx48/fYt8Cv6z6JuQ0ZQExAiEA9XRZVUnCJ2xOcCFCbyIF+d3F +9Kht5rR77F9KsRlgUbkCIQDuQ7YzLpQ8V8BJwKbDeXw1vQvcPEnyKnTOoALpF6bq +RwIhAIDSm8Ajgf7m3RQEoLVrCe/l8WtCqsuWliOsr6rbQq4hAiEAx8R16wvOtZlN +W4jvSU1+WwAaBZl21lfKf8OhLRXrmNkCIG9IRdcSiNR/Ut8QfD3N9Bb1HsUm+Bvz +c8yGzl89pYST +-----END PRIVATE KEY-----)"sv; + auto decoded = Crypto::decode_pem(keypem.bytes()); + auto keypair = Crypto::PK::RSA::parse_rsa_key(decoded); + auto priv_der = MUST(keypair.private_key.export_as_der()); + auto priv_pem = MUST(Crypto::encode_pem(priv_der, Crypto::PEMType::PrivateKey)); + auto rsa_from_pair = Crypto::PK::RSA(keypair.public_key, keypair.private_key); + auto rsa_from_pem = Crypto::PK::RSA(priv_pem); + + u8 enc_buffer[rsa_from_pair.output_size()]; + u8 dec_buffer[rsa_from_pair.output_size()]; + + auto enc = Bytes { enc_buffer, rsa_from_pair.output_size() }; + auto dec = Bytes { dec_buffer, rsa_from_pair.output_size() }; + + dec.overwrite(0, "WellHelloFriends", 16); + + rsa_from_pair.encrypt(dec, enc); + rsa_from_pem.decrypt(enc, dec); + + EXPECT_EQ(memcmp(dec.data(), "WellHelloFriends", 16), 0); +} + TEST_CASE(test_RSA_encrypt_decrypt) { Crypto::PK::RSA rsa( diff --git a/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp b/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp index 52b30896855..ca74749ea80 100644 --- a/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp +++ b/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp @@ -10,6 +10,14 @@ namespace Crypto::NumberTheory { +UnsignedBigInteger Mod(UnsignedBigInteger const& a, UnsignedBigInteger const& b) +{ + UnsignedBigInteger result; + result.set_to(a); + result.set_to(result.divided_by(b).remainder); + return result; +} + UnsignedBigInteger ModularInverse(UnsignedBigInteger const& a_, UnsignedBigInteger const& b) { if (b == 1) diff --git a/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.h b/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.h index 21cabcb0ce7..156d4f05471 100644 --- a/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.h +++ b/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.h @@ -11,6 +11,7 @@ namespace Crypto::NumberTheory { +UnsignedBigInteger Mod(UnsignedBigInteger const& a, UnsignedBigInteger const& b); UnsignedBigInteger ModularInverse(UnsignedBigInteger const& a_, UnsignedBigInteger const& b); UnsignedBigInteger ModularPower(UnsignedBigInteger const& b, UnsignedBigInteger const& e, UnsignedBigInteger const& m); diff --git a/Userland/Libraries/LibCrypto/PK/RSA.cpp b/Userland/Libraries/LibCrypto/PK/RSA.cpp index 9a0fb956036..e033b1c0382 100644 --- a/Userland/Libraries/LibCrypto/PK/RSA.cpp +++ b/Userland/Libraries/LibCrypto/PK/RSA.cpp @@ -132,81 +132,85 @@ RSA::KeyPairType RSA::parse_rsa_key(ReadonlyBytes der) if (first_integer == 0) { // This is a private key, parse the rest. auto modulus_result = decoder.read(); - if (modulus_result.is_error()) { - dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#1 private key parse failed: {}", modulus_result.error()); - return keypair; - } - auto modulus = modulus_result.release_value(); - auto public_exponent_result = decoder.read(); - if (public_exponent_result.is_error()) { - dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#1 private key parse failed: {}", public_exponent_result.error()); - return keypair; - } - auto public_exponent = public_exponent_result.release_value(); - auto private_exponent_result = decoder.read(); - if (private_exponent_result.is_error()) { - dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#1 private key parse failed: {}", private_exponent_result.error()); - return keypair; - } - auto private_exponent = private_exponent_result.release_value(); + auto prime1_result = decoder.read(); + auto prime2_result = decoder.read(); + auto exponent1_result = decoder.read(); + auto exponent2_result = decoder.read(); + auto coefficient_result = decoder.read(); - // Drop the rest of the fields on the floor, we don't use them. - // FIXME: Actually use them... - keypair.private_key = { modulus, move(private_exponent), public_exponent }; - keypair.public_key = { move(modulus), move(public_exponent) }; + Array results = { &modulus_result, &public_exponent_result, &private_exponent_result, &prime1_result, &prime2_result, &exponent1_result, &exponent2_result, &coefficient_result }; + for (auto& result : results) { + if (result->is_error()) { + dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#1 private key parse failed: {}", result->error()); + return keypair; + } + } + + keypair.private_key = { + modulus_result.value(), + private_exponent_result.release_value(), + public_exponent_result.value(), + prime1_result.release_value(), + prime2_result.release_value(), + exponent1_result.release_value(), + exponent2_result.release_value(), + coefficient_result.release_value(), + }; + keypair.public_key = { modulus_result.release_value(), public_exponent_result.release_value() }; return keypair; - } else if (first_integer == 1) { + } + + if (first_integer == 1) { // This is a multi-prime key, we don't support that. dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#1 private key parse failed: Multi-prime key not supported"); return keypair; - } else { - auto&& modulus = move(first_integer); + } - // Try reading a public key, `first_integer` is the modulus. - auto public_exponent_result = decoder.read(); - if (public_exponent_result.is_error()) { - // Bad public key. - dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#1 public key parse failed: {}", public_exponent_result.error()); - return keypair; - } - - auto public_exponent = public_exponent_result.release_value(); - keypair.public_key.set(move(modulus), move(public_exponent)); + auto&& modulus = move(first_integer); + // Try reading a public key, `first_integer` is the modulus. + auto public_exponent_result = decoder.read(); + if (public_exponent_result.is_error()) { + // Bad public key. + dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#1 public key parse failed: {}", public_exponent_result.error()); return keypair; } - } else { - // It wasn't a PKCS#1 key, let's try our luck with PKCS#8. - if (!check_if_pkcs8_rsa_key()) - return keypair; + auto public_exponent = public_exponent_result.release_value(); + keypair.public_key.set(move(modulus), move(public_exponent)); - if (has_read_error) - return keypair; - - // Now we have a bit string, which contains the PKCS#1 encoded public key. - auto data_result = decoder.read(); - if (data_result.is_error()) { - dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#8 public key parse failed: {}", data_result.error()); - return keypair; - } - - // Now just read it as a PKCS#1 DER. - auto data = data_result.release_value(); - // FIXME: This is pretty awkward, maybe just generate a zero'd out ByteBuffer from the parser instead? - auto padded_data_result = ByteBuffer::create_zeroed(data.size_in_bytes()); - if (padded_data_result.is_error()) { - dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#1 key parse failed: Not enough memory"); - return keypair; - } - auto padded_data = padded_data_result.release_value(); - padded_data.overwrite(0, data.data(), data.size_in_bytes()); - - return parse_rsa_key(padded_data.bytes()); + return keypair; } + + // It wasn't a PKCS#1 key, let's try our luck with PKCS#8. + if (!check_if_pkcs8_rsa_key()) + return keypair; + + if (has_read_error) + return keypair; + + // Now we have a bit string, which contains the PKCS#1 encoded public key. + auto data_result = decoder.read(); + if (data_result.is_error()) { + dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#8 public key parse failed: {}", data_result.error()); + return keypair; + } + + // Now just read it as a PKCS#1 DER. + auto data = data_result.release_value(); + // FIXME: This is pretty awkward, maybe just generate a zero'd out ByteBuffer from the parser instead? + auto padded_data_result = ByteBuffer::create_zeroed(data.size_in_bytes()); + if (padded_data_result.is_error()) { + dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#1 key parse failed: Not enough memory"); + return keypair; + } + auto padded_data = padded_data_result.release_value(); + padded_data.overwrite(0, data.data(), data.size_in_bytes()); + + return parse_rsa_key(padded_data.bytes()); } void RSA::encrypt(ReadonlyBytes in, Bytes& out) diff --git a/Userland/Libraries/LibCrypto/PK/RSA.h b/Userland/Libraries/LibCrypto/PK/RSA.h index e25ffd29720..242cfa2ebf6 100644 --- a/Userland/Libraries/LibCrypto/PK/RSA.h +++ b/Userland/Libraries/LibCrypto/PK/RSA.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -36,6 +37,18 @@ public: size_t length() const { return m_length; } void set_length(size_t length) { m_length = length; } + ErrorOr export_as_der() const + { + ASN1::Encoder encoder; + TRY(encoder.write_constructed(ASN1::Class::Universal, ASN1::Kind::Sequence, [&]() -> ErrorOr { + TRY(encoder.write(m_modulus)); + TRY(encoder.write(m_public_exponent)); + return {}; + })); + + return encoder.finish(); + } + void set(Integer n, Integer e) { m_modulus = move(n); @@ -52,10 +65,28 @@ private: template class RSAPrivateKey { public: - RSAPrivateKey(Integer n, Integer d, Integer e) + RSAPrivateKey(Integer n, Integer d, Integer e, Integer p, Integer q) : m_modulus(move(n)) , m_private_exponent(move(d)) , m_public_exponent(move(e)) + , m_prime_1(move(p)) + , m_prime_2(move(q)) + , m_exponent_1(NumberTheory::Mod(m_private_exponent, m_prime_1.minus(1))) + , m_exponent_2(NumberTheory::Mod(m_private_exponent, m_prime_2.minus(1))) + , m_coefficient(NumberTheory::ModularInverse(m_prime_2, m_prime_1)) + , m_length(m_modulus.trimmed_length() * sizeof(u32)) + { + } + + RSAPrivateKey(Integer n, Integer d, Integer e, Integer p, Integer q, Integer dp, Integer dq, Integer qinv) + : m_modulus(move(n)) + , m_private_exponent(move(d)) + , m_public_exponent(move(e)) + , m_prime_1(move(p)) + , m_prime_2(move(q)) + , m_exponent_1(move(dp)) + , m_exponent_2(move(dq)) + , m_coefficient(move(qinv)) , m_length(m_modulus.trimmed_length() * sizeof(u32)) { } @@ -65,21 +96,41 @@ public: Integer const& modulus() const { return m_modulus; } Integer const& private_exponent() const { return m_private_exponent; } Integer const& public_exponent() const { return m_public_exponent; } + Integer const& prime1() const { return m_prime_1; } + Integer const& prime2() const { return m_prime_2; } + Integer const& exponent1() const { return m_exponent_1; } + Integer const& exponent2() const { return m_exponent_2; } + Integer const& coefficient() const { return m_coefficient; } size_t length() const { return m_length; } - void set_length(size_t length) { m_length = length; } - void set(Integer n, Integer d, Integer e) + ErrorOr export_as_der() const { - m_modulus = move(n); - m_private_exponent = move(d); - m_public_exponent = move(e); - m_length = m_modulus.trimmed_length() * sizeof(u32); + ASN1::Encoder encoder; + TRY(encoder.write_constructed(ASN1::Class::Universal, ASN1::Kind::Sequence, [&]() -> ErrorOr { + TRY(encoder.write(0x00u)); // version + TRY(encoder.write(m_modulus)); + TRY(encoder.write(m_public_exponent)); + TRY(encoder.write(m_private_exponent)); + TRY(encoder.write(m_prime_1)); + TRY(encoder.write(m_prime_2)); + TRY(encoder.write(m_exponent_1)); + TRY(encoder.write(m_exponent_2)); + TRY(encoder.write(m_coefficient)); + return {}; + })); + + return encoder.finish(); } private: Integer m_modulus; Integer m_private_exponent; Integer m_public_exponent; + Integer m_prime_1; + Integer m_prime_2; + Integer m_exponent_1; // d mod (p-1) + Integer m_exponent_2; // d mod (q-1) + Integer m_coefficient; // q^-1 mod p size_t m_length { 0 }; }; @@ -114,20 +165,18 @@ public: auto n = p.multiplied_by(q); auto d = NumberTheory::ModularInverse(e, lambda); - dbgln("Your keys are Pub(n={}, e={}) and Priv(n={}, d={})", n, e, n, d); + dbgln("Your keys are Pub(n={}, e={}) and Priv(n={}, d={}, p={}, q={})", n, e, n, d, p, q); RSAKeyPair keys { { n, e }, - { n, d, e } + { n, d, e, p, q } }; - keys.public_key.set_length(bits / 2 / 8); - keys.private_key.set_length(bits / 2 / 8); return keys; } RSA(IntegerType n, IntegerType d, IntegerType e) { m_public_key.set(n, e); - m_private_key.set(n, d, e); + m_private_key = { n, d, e, 0, 0, 0, 0, 0 }; } RSA(PublicKeyType& pubkey, PrivateKeyType& privkey)