diff --git a/Userland/Libraries/LibCrypto/PK/Code/EMSA_PKCS1_V1_5.h b/Userland/Libraries/LibCrypto/PK/Code/EMSA_PKCS1_V1_5.h new file mode 100644 index 0000000000..987e5d56c3 --- /dev/null +++ b/Userland/Libraries/LibCrypto/PK/Code/EMSA_PKCS1_V1_5.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2022, Michiel Visser + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Crypto { +namespace PK { + +template +class EMSA_PKCS1_V1_5 : public Code { +public: + template + EMSA_PKCS1_V1_5(Args... args) + : Code(args...) + { + } + + virtual void encode(ReadonlyBytes in, ByteBuffer& out, size_t em_bits) override + { + auto& hash_fn = this->hasher(); + hash_fn.update(in); + auto message_digest = hash_fn.digest(); + auto message_digest_size = message_digest.bytes().size(); + + auto digest_info = hash_function_digest_info(); + auto encoded_message_length = digest_info.size() + message_digest_size; + + auto em_bytes = (em_bits + 7) / 8; + // RFC8017 section 9.2: 3. If emLen < tLen + 11, output "intended encoded message length too short" and stop. + if (em_bytes < encoded_message_length + 11) { + dbgln("EMSA-PKCS1-V1_5-ENCODE: intended encoded message length too short"); + return; + } + + auto offset = 0; + // Build the padding 0x0001ffff..ff00 + out[offset++] = 0x00; + out[offset++] = 0x01; + for (size_t i = 0; i < em_bytes - encoded_message_length - 3; i++) + out[offset++] = 0xff; + out[offset++] = 0x00; + // Add the digest info and message digest + out.overwrite(offset, digest_info.data(), digest_info.size()); + offset += digest_info.size(); + out.overwrite(offset, message_digest.immutable_data(), message_digest.data_length()); + } + + virtual VerificationConsistency verify(ReadonlyBytes msg, ReadonlyBytes emsg, size_t em_bits) override + { + auto em_bytes = (em_bits + 7) / 8; + auto buffer_result = ByteBuffer::create_uninitialized(em_bytes); + if (buffer_result.is_error()) { + dbgln("EMSA-PKCS1-V1_5-VERIFY: out of memory"); + return VerificationConsistency::Inconsistent; + } + auto buffer = buffer_result.release_value(); + + // Encode the supplied message into the buffer + encode(msg, buffer, em_bits); + + // Check that the expected message matches the encoded original message + if (emsg != buffer) { + return VerificationConsistency::Inconsistent; + } + return VerificationConsistency::Consistent; + } + +private: + inline ReadonlyBytes hash_function_digest_info(); +}; + +template<> +inline ReadonlyBytes EMSA_PKCS1_V1_5::hash_function_digest_info() +{ + // RFC8017 section 9.2 notes 1 + return { "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10", 18 }; +} + +template<> +inline ReadonlyBytes EMSA_PKCS1_V1_5::hash_function_digest_info() +{ + // RFC8017 section 9.2 notes 1 + return { "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14", 15 }; +} + +template<> +inline ReadonlyBytes EMSA_PKCS1_V1_5::hash_function_digest_info() +{ + // RFC8017 section 9.2 notes 1 + return { "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19 }; +} + +template<> +inline ReadonlyBytes EMSA_PKCS1_V1_5::hash_function_digest_info() +{ + // RFC8017 section 9.2 notes 1 + return { "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19 }; +} + +template<> +inline ReadonlyBytes EMSA_PKCS1_V1_5::hash_function_digest_info() +{ + // RFC8017 section 9.2 notes 1 + return { "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19 }; +} + +} +}