Files
ladybird/Libraries/LibCrypto/PK/RSA.cpp
devgianlu 57ecd72256 LibCrypto: Return PEM type when decoding and sanity check footer
Improve PEM decoding by parsing the header and returning it along the
data. Also verify if the header is equal to the footer.
2024-11-30 11:17:44 +01:00

390 lines
13 KiB
C++

/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <AK/Random.h>
#include <AK/ScopeGuard.h>
#include <LibCrypto/ASN1/ASN1.h>
#include <LibCrypto/ASN1/DER.h>
#include <LibCrypto/ASN1/PEM.h>
#include <LibCrypto/Certificate/Certificate.h>
#include <LibCrypto/PK/RSA.h>
namespace Crypto::PK {
RSA::KeyPairType RSA::parse_rsa_key(ReadonlyBytes der)
{
// we are going to assign to at least one of these
KeyPairType keypair;
ASN1::Decoder decoder(der);
// There are four possible (supported) formats:
// PKCS#1 private key
// PKCS#1 public key
// PKCS#8 private key
// PKCS#8 public key
// They're all a single sequence, so let's check that first
{
auto result = decoder.peek();
if (result.is_error()) {
// Bad data.
dbgln_if(RSA_PARSE_DEBUG, "RSA key parse failed: {}", result.error());
return keypair;
}
auto tag = result.value();
if (tag.kind != ASN1::Kind::Sequence) {
dbgln_if(RSA_PARSE_DEBUG, "RSA key parse failed: Expected a Sequence but got {}", ASN1::kind_name(tag.kind));
return keypair;
}
}
// Then enter the sequence
{
auto error = decoder.enter();
if (error.is_error()) {
// Something was weird with the input.
dbgln_if(RSA_PARSE_DEBUG, "RSA key parse failed: {}", error.error());
return keypair;
}
}
bool has_read_error = false;
auto const check_if_pkcs8_rsa_key = [&] {
// see if it's a sequence:
auto tag_result = decoder.peek();
if (tag_result.is_error()) {
// Decode error :shrug:
dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#8 public key parse failed: {}", tag_result.error());
return false;
}
auto tag = tag_result.value();
if (tag.kind != ASN1::Kind::Sequence) {
// We don't know what this is, but it sure isn't a PKCS#8 key.
dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#8 public key parse failed: Expected a Sequence but got {}", ASN1::kind_name(tag.kind));
return false;
}
// It's a sequence, now let's see if it's actually an RSA key.
auto error = decoder.enter();
if (error.is_error()) {
// Shenanigans!
dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#8 public key parse failed: {}", error.error());
return false;
}
ScopeGuard leave { [&] {
auto error = decoder.leave();
if (error.is_error()) {
dbgln_if(RSA_PARSE_DEBUG, "RSA key parse failed: {}", error.error());
has_read_error = true;
}
} };
// Now let's read the OID.
auto oid_result = decoder.read<Vector<int>>();
if (oid_result.is_error()) {
dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#8 public key parse failed: {}", oid_result.error());
return false;
}
auto oid = oid_result.release_value();
// Now let's check that the OID matches "RSA key"
if (oid != Crypto::ASN1::rsa_encryption_oid) {
// Oh well. not an RSA key at all.
dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#8 public key parse failed: Not an RSA key");
return false;
}
return true;
};
auto integer_result = decoder.read<UnsignedBigInteger>();
if (!integer_result.is_error()) {
auto first_integer = integer_result.release_value();
// It's either a PKCS#1 key, or a PKCS#8 private key.
// Check for the PKCS#8 private key right away.
if (check_if_pkcs8_rsa_key()) {
if (has_read_error)
return keypair;
// Now read the private key, which is actually an octet string containing the PKCS#1 encoded private key.
auto data_result = decoder.read<StringView>();
if (data_result.is_error()) {
dbgln_if(RSA_PARSE_DEBUG, "RSA PKCS#8 private key parse failed: {}", data_result.error());
return keypair;
}
return parse_rsa_key(data_result.value().bytes());
}
if (has_read_error)
return keypair;
// It's not a PKCS#8 key, so it's a PKCS#1 key (or something we don't support)
// if the first integer is zero or one, it's a private key.
if (first_integer == 0) {
// This is a private key, parse the rest.
auto modulus_result = decoder.read<UnsignedBigInteger>();
auto public_exponent_result = decoder.read<UnsignedBigInteger>();
auto private_exponent_result = decoder.read<UnsignedBigInteger>();
auto prime1_result = decoder.read<UnsignedBigInteger>();
auto prime2_result = decoder.read<UnsignedBigInteger>();
auto exponent1_result = decoder.read<UnsignedBigInteger>();
auto exponent2_result = decoder.read<UnsignedBigInteger>();
auto coefficient_result = decoder.read<UnsignedBigInteger>();
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;
}
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;
}
auto&& modulus = move(first_integer);
// Try reading a public key, `first_integer` is the modulus.
auto public_exponent_result = decoder.read<UnsignedBigInteger>();
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));
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<BitmapView>();
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)
{
dbgln_if(CRYPTO_DEBUG, "in size: {}", in.size());
auto in_integer = UnsignedBigInteger::import_data(in.data(), in.size());
if (!(in_integer < m_public_key.modulus())) {
dbgln("value too large for key");
out = {};
return;
}
auto exp = NumberTheory::ModularPower(in_integer, m_public_key.public_exponent(), m_public_key.modulus());
auto size = exp.export_data(out);
auto outsize = out.size();
if (size != outsize) {
dbgln("POSSIBLE RSA BUG!!! Size mismatch: {} requested but {} bytes generated", outsize, size);
out = out.slice(outsize - size, size);
}
}
void RSA::decrypt(ReadonlyBytes in, Bytes& out)
{
// FIXME: Actually use the private key properly
auto in_integer = UnsignedBigInteger::import_data(in.data(), in.size());
auto exp = NumberTheory::ModularPower(in_integer, m_private_key.private_exponent(), m_private_key.modulus());
auto size = exp.export_data(out);
auto align = m_private_key.length();
auto aligned_size = (size + align - 1) / align * align;
for (auto i = size; i < aligned_size; ++i)
out[out.size() - i - 1] = 0; // zero the non-aligned values
out = out.slice(out.size() - aligned_size, aligned_size);
}
void RSA::sign(ReadonlyBytes in, Bytes& out)
{
auto in_integer = UnsignedBigInteger::import_data(in.data(), in.size());
auto exp = NumberTheory::ModularPower(in_integer, m_private_key.private_exponent(), m_private_key.modulus());
auto size = exp.export_data(out);
out = out.slice(out.size() - size, size);
}
void RSA::verify(ReadonlyBytes in, Bytes& out)
{
auto in_integer = UnsignedBigInteger::import_data(in.data(), in.size());
auto exp = NumberTheory::ModularPower(in_integer, m_public_key.public_exponent(), m_public_key.modulus());
auto size = exp.export_data(out);
out = out.slice(out.size() - size, size);
}
void RSA::import_private_key(ReadonlyBytes bytes, bool pem)
{
ByteBuffer buffer;
if (pem) {
buffer = decode_pem(bytes).data;
bytes = buffer;
}
auto key = parse_rsa_key(bytes);
if (!key.private_key.length()) {
dbgln("We expected to see a private key, but we found none");
VERIFY_NOT_REACHED();
}
m_private_key = key.private_key;
}
void RSA::import_public_key(ReadonlyBytes bytes, bool pem)
{
ByteBuffer buffer;
if (pem) {
buffer = decode_pem(bytes).data;
bytes = buffer;
}
auto key = parse_rsa_key(bytes);
if (!key.public_key.length()) {
dbgln("We expected to see a public key, but we found none");
VERIFY_NOT_REACHED();
}
m_public_key = key.public_key;
}
void RSA_PKCS1_EME::encrypt(ReadonlyBytes in, Bytes& out)
{
auto mod_len = (m_public_key.modulus().trimmed_length() * sizeof(u32) * 8 + 7) / 8;
dbgln_if(CRYPTO_DEBUG, "key size: {}", mod_len);
if (in.size() > mod_len - 11) {
dbgln("message too long :(");
out = out.trim(0);
return;
}
if (out.size() < mod_len) {
dbgln("output buffer too small");
return;
}
auto ps_length = mod_len - in.size() - 3;
Vector<u8, 8096> ps;
ps.resize(ps_length);
fill_with_random(ps);
// since fill_with_random can create zeros (shocking!)
// we have to go through and un-zero the zeros
for (size_t i = 0; i < ps_length; ++i) {
while (!ps[i])
ps[i] = get_random<u8>();
}
u8 paddings[] { 0x00, 0x02 };
out.overwrite(0, paddings, 2);
out.overwrite(2, ps.data(), ps_length);
out.overwrite(2 + ps_length, paddings, 1);
out.overwrite(3 + ps_length, in.data(), in.size());
out = out.trim(3 + ps_length + in.size()); // should be a single block
dbgln_if(CRYPTO_DEBUG, "padded output size: {} buffer size: {}", 3 + ps_length + in.size(), out.size());
RSA::encrypt(out, out);
}
void RSA_PKCS1_EME::decrypt(ReadonlyBytes in, Bytes& out)
{
auto mod_len = (m_public_key.modulus().trimmed_length() * sizeof(u32) * 8 + 7) / 8;
if (in.size() != mod_len) {
dbgln("decryption error: wrong amount of data: {}", in.size());
out = out.trim(0);
return;
}
RSA::decrypt(in, out);
if (out.size() < RSA::output_size()) {
dbgln("decryption error: not enough data after decryption: {}", out.size());
out = out.trim(0);
return;
}
if (out[0] != 0x00) {
dbgln("invalid padding byte 0 : {}", out[0]);
return;
}
if (out[1] != 0x02) {
dbgln("invalid padding byte 1 : {}", out[1]);
return;
}
size_t offset = 2;
while (offset < out.size() && out[offset])
++offset;
if (offset == out.size()) {
dbgln("garbage data, no zero to split padding");
return;
}
++offset;
if (offset - 3 < 8) {
dbgln("PS too small");
return;
}
out = out.slice(offset, out.size() - offset);
}
void RSA_PKCS1_EME::sign(ReadonlyBytes, Bytes&)
{
dbgln("FIXME: RSA_PKCS_EME::sign");
}
void RSA_PKCS1_EME::verify(ReadonlyBytes, Bytes&)
{
dbgln("FIXME: RSA_PKCS_EME::verify");
}
}