Skip to content

Commit be13847

Browse files
msvisseralimpfard
authored andcommitted
LibCrypto: Add EMSA-PKCS1-V1_5 encoder and verification
This add an implementation for the EMSA-PKCS1-V1_5-ENCODE function from RFC8017 section 9.2. The verification of this encoding is implemented by simply encoding the message to be verified, and then comparing the two encoded string. The digest info for the different hash function is from RFC8017 section 9.2 notes 1. These byte sequences are actually ASN.1 encoded data, however these are always constant for a specific hash function and can be treated as opaque byte sequences.
1 parent edee8ab commit be13847

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright (c) 2022, Michiel Visser <opensource@webmichiel.nl>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#pragma once
8+
9+
#include <AK/Array.h>
10+
#include <AK/Format.h>
11+
#include <AK/Random.h>
12+
#include <AK/Vector.h>
13+
#include <LibCrypto/Hash/MD5.h>
14+
#include <LibCrypto/Hash/SHA1.h>
15+
#include <LibCrypto/Hash/SHA2.h>
16+
#include <LibCrypto/PK/Code/Code.h>
17+
18+
namespace Crypto {
19+
namespace PK {
20+
21+
template<typename HashFunction>
22+
class EMSA_PKCS1_V1_5 : public Code<HashFunction> {
23+
public:
24+
template<typename... Args>
25+
EMSA_PKCS1_V1_5(Args... args)
26+
: Code<HashFunction>(args...)
27+
{
28+
}
29+
30+
virtual void encode(ReadonlyBytes in, ByteBuffer& out, size_t em_bits) override
31+
{
32+
auto& hash_fn = this->hasher();
33+
hash_fn.update(in);
34+
auto message_digest = hash_fn.digest();
35+
auto message_digest_size = message_digest.bytes().size();
36+
37+
auto digest_info = hash_function_digest_info();
38+
auto encoded_message_length = digest_info.size() + message_digest_size;
39+
40+
auto em_bytes = (em_bits + 7) / 8;
41+
// RFC8017 section 9.2: 3. If emLen < tLen + 11, output "intended encoded message length too short" and stop.
42+
if (em_bytes < encoded_message_length + 11) {
43+
dbgln("EMSA-PKCS1-V1_5-ENCODE: intended encoded message length too short");
44+
return;
45+
}
46+
47+
auto offset = 0;
48+
// Build the padding 0x0001ffff..ff00
49+
out[offset++] = 0x00;
50+
out[offset++] = 0x01;
51+
for (size_t i = 0; i < em_bytes - encoded_message_length - 3; i++)
52+
out[offset++] = 0xff;
53+
out[offset++] = 0x00;
54+
// Add the digest info and message digest
55+
out.overwrite(offset, digest_info.data(), digest_info.size());
56+
offset += digest_info.size();
57+
out.overwrite(offset, message_digest.immutable_data(), message_digest.data_length());
58+
}
59+
60+
virtual VerificationConsistency verify(ReadonlyBytes msg, ReadonlyBytes emsg, size_t em_bits) override
61+
{
62+
auto em_bytes = (em_bits + 7) / 8;
63+
auto buffer_result = ByteBuffer::create_uninitialized(em_bytes);
64+
if (buffer_result.is_error()) {
65+
dbgln("EMSA-PKCS1-V1_5-VERIFY: out of memory");
66+
return VerificationConsistency::Inconsistent;
67+
}
68+
auto buffer = buffer_result.release_value();
69+
70+
// Encode the supplied message into the buffer
71+
encode(msg, buffer, em_bits);
72+
73+
// Check that the expected message matches the encoded original message
74+
if (emsg != buffer) {
75+
return VerificationConsistency::Inconsistent;
76+
}
77+
return VerificationConsistency::Consistent;
78+
}
79+
80+
private:
81+
inline ReadonlyBytes hash_function_digest_info();
82+
};
83+
84+
template<>
85+
inline ReadonlyBytes EMSA_PKCS1_V1_5<Crypto::Hash::MD5>::hash_function_digest_info()
86+
{
87+
// RFC8017 section 9.2 notes 1
88+
return { "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10", 18 };
89+
}
90+
91+
template<>
92+
inline ReadonlyBytes EMSA_PKCS1_V1_5<Crypto::Hash::SHA1>::hash_function_digest_info()
93+
{
94+
// RFC8017 section 9.2 notes 1
95+
return { "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14", 15 };
96+
}
97+
98+
template<>
99+
inline ReadonlyBytes EMSA_PKCS1_V1_5<Crypto::Hash::SHA256>::hash_function_digest_info()
100+
{
101+
// RFC8017 section 9.2 notes 1
102+
return { "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19 };
103+
}
104+
105+
template<>
106+
inline ReadonlyBytes EMSA_PKCS1_V1_5<Crypto::Hash::SHA384>::hash_function_digest_info()
107+
{
108+
// RFC8017 section 9.2 notes 1
109+
return { "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19 };
110+
}
111+
112+
template<>
113+
inline ReadonlyBytes EMSA_PKCS1_V1_5<Crypto::Hash::SHA512>::hash_function_digest_info()
114+
{
115+
// RFC8017 section 9.2 notes 1
116+
return { "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19 };
117+
}
118+
119+
}
120+
}

0 commit comments

Comments
 (0)