diff --git a/Libraries/LibCrypto/ASN1/PEM.cpp b/Libraries/LibCrypto/ASN1/PEM.cpp index 4e1d035f8d1..a9f782d49dd 100644 --- a/Libraries/LibCrypto/ASN1/PEM.cpp +++ b/Libraries/LibCrypto/ASN1/PEM.cpp @@ -10,10 +10,26 @@ namespace Crypto { -ByteBuffer decode_pem(ReadonlyBytes data) +static PEMType pem_header_to_type(StringView header) +{ + if (header == "CERTIFICATE"sv) + return PEMType::Certificate; + if (header == "PRIVATE KEY"sv) + return PEMType::PrivateKey; + if (header == "RSA PRIVATE KEY"sv) + return PEMType::RSAPrivateKey; + if (header == "PUBLIC KEY"sv) + return PEMType::PublicKey; + if (header == "RSA PUBLIC KEY"sv) + return PEMType::RSAPublicKey; + return PEMType::Unknown; +} + +DecodedPEM decode_pem(ReadonlyBytes data) { GenericLexer lexer { data }; - ByteBuffer decoded; + DecodedPEM decoded; + StringView header_type; // FIXME: Parse multiple. enum { @@ -24,14 +40,23 @@ ByteBuffer decode_pem(ReadonlyBytes data) while (!lexer.is_eof()) { switch (state) { case PreStartData: - if (lexer.consume_specific("-----BEGIN"sv)) + if (lexer.consume_specific("-----BEGIN "sv)) { state = Started; + header_type = lexer.consume_until("-----"); + } lexer.consume_line(); break; case Started: { - if (lexer.consume_specific("-----END"sv)) { + if (lexer.consume_specific("-----END "sv)) { state = Ended; + + if (lexer.consume_until("-----") != header_type) { + dbgln("PEM type mismatch"); + return {}; + } lexer.consume_line(); + + decoded.type = pem_header_to_type(header_type); break; } auto b64decoded = decode_base64(lexer.consume_line().trim_whitespace(TrimMode::Right)); @@ -39,7 +64,7 @@ ByteBuffer decode_pem(ReadonlyBytes data) dbgln("Failed to decode PEM: {}", b64decoded.error().string_literal()); return {}; } - if (decoded.try_append(b64decoded.value().data(), b64decoded.value().size()).is_error()) { + if (decoded.data.try_append(b64decoded.value().data(), b64decoded.value().size()).is_error()) { dbgln("Failed to decode PEM, likely OOM condition"); return {}; } @@ -56,11 +81,13 @@ ByteBuffer decode_pem(ReadonlyBytes data) return decoded; } -ErrorOr> decode_pems(ReadonlyBytes data) +ErrorOr> decode_pems(ReadonlyBytes data) { GenericLexer lexer { data }; - ByteBuffer decoded; - Vector pems; + Vector pems; + + DecodedPEM decoded; + StringView header_type; enum { Junk, @@ -69,20 +96,28 @@ ErrorOr> decode_pems(ReadonlyBytes data) while (!lexer.is_eof()) { switch (state) { case Junk: - if (lexer.consume_specific("-----BEGIN"sv)) + if (lexer.consume_specific("-----BEGIN "sv)) { state = Parsing; + header_type = lexer.consume_until("-----"); + } lexer.consume_line(); break; case Parsing: { - if (lexer.consume_specific("-----END"sv)) { + if (lexer.consume_specific("-----END "sv)) { state = Junk; + + if (lexer.consume_until("-----") != header_type) { + return Error::from_string_literal("PEM type mismatch"); + } lexer.consume_line(); + TRY(pems.try_append(decoded)); - decoded.clear(); + decoded = {}; + header_type = {}; break; } auto b64decoded = TRY(decode_base64(lexer.consume_line().trim_whitespace(TrimMode::Right))); - TRY(decoded.try_append(b64decoded.data(), b64decoded.size())); + TRY(decoded.data.try_append(b64decoded.data(), b64decoded.size())); break; } default: @@ -108,6 +143,18 @@ ErrorOr encode_pem(ReadonlyBytes data, PEMType type) block_start = "-----BEGIN PRIVATE KEY-----\n"sv; block_end = "-----END PRIVATE KEY-----\n"sv; break; + case PEMType::RSAPrivateKey: + block_start = "-----BEGIN RSA PRIVATE KEY-----\n"sv; + block_end = "-----END RSA PRIVATE KEY-----\n"sv; + break; + case PEMType::PublicKey: + block_start = "-----BEGIN PUBLIC KEY-----\n"sv; + block_end = "-----END PUBLIC KEY-----\n"sv; + break; + case PEMType::RSAPublicKey: + block_start = "-----BEGIN RSA PUBLIC KEY-----\n"sv; + block_end = "-----END RSA PUBLIC KEY-----\n"sv; + break; default: VERIFY_NOT_REACHED(); } diff --git a/Libraries/LibCrypto/ASN1/PEM.h b/Libraries/LibCrypto/ASN1/PEM.h index c2747ee5a75..75249f62442 100644 --- a/Libraries/LibCrypto/ASN1/PEM.h +++ b/Libraries/LibCrypto/ASN1/PEM.h @@ -7,17 +7,25 @@ #pragma once #include -#include namespace Crypto { enum class PEMType { + Unknown, Certificate, PrivateKey, + PublicKey, + RSAPrivateKey, + RSAPublicKey }; -ByteBuffer decode_pem(ReadonlyBytes); -ErrorOr> decode_pems(ReadonlyBytes); +struct DecodedPEM { + PEMType type { PEMType::Unknown }; + ByteBuffer data; +}; + +DecodedPEM decode_pem(ReadonlyBytes); +ErrorOr> decode_pems(ReadonlyBytes); ErrorOr encode_pem(ReadonlyBytes, PEMType = PEMType::Certificate); } diff --git a/Libraries/LibCrypto/PK/RSA.cpp b/Libraries/LibCrypto/PK/RSA.cpp index 00203f46a3d..6e46ab529e4 100644 --- a/Libraries/LibCrypto/PK/RSA.cpp +++ b/Libraries/LibCrypto/PK/RSA.cpp @@ -266,7 +266,7 @@ void RSA::import_private_key(ReadonlyBytes bytes, bool pem) { ByteBuffer buffer; if (pem) { - buffer = decode_pem(bytes); + buffer = decode_pem(bytes).data; bytes = buffer; } @@ -282,7 +282,7 @@ void RSA::import_public_key(ReadonlyBytes bytes, bool pem) { ByteBuffer buffer; if (pem) { - buffer = decode_pem(bytes); + buffer = decode_pem(bytes).data; bytes = buffer; } diff --git a/Libraries/LibTLS/TLSv12.cpp b/Libraries/LibTLS/TLSv12.cpp index 1d053d10bba..3d95d804c61 100644 --- a/Libraries/LibTLS/TLSv12.cpp +++ b/Libraries/LibTLS/TLSv12.cpp @@ -501,12 +501,12 @@ Vector TLSv12::parse_pem_certificate(ReadonlyBytes certificate_pem_ } auto decoded_certificate = Crypto::decode_pem(certificate_pem_buffer); - if (decoded_certificate.is_empty()) { + if (decoded_certificate.type != Crypto::PEMType::Certificate) { dbgln("Certificate not PEM"); return {}; } - auto maybe_certificate = Certificate::parse_certificate(decoded_certificate); + auto maybe_certificate = Certificate::parse_certificate(decoded_certificate.data); if (!maybe_certificate.is_error()) { dbgln("Invalid certificate"); return {}; @@ -576,7 +576,7 @@ ErrorOr> DefaultRootCACertificates::parse_pem_root_certifica auto certs = TRY(Crypto::decode_pems(data)); for (auto& cert : certs) { - auto certificate_result = Certificate::parse_certificate(cert.bytes()); + auto certificate_result = Certificate::parse_certificate(cert.data); if (certificate_result.is_error()) { // FIXME: It would be nice to have more informations about the certificate we failed to parse. // Like: Issuer, Algorithm, CN, etc