Botan 3.6.1
Crypto and TLS for C&
oaep.cpp
Go to the documentation of this file.
1/*
2* OAEP
3* (C) 1999-2010,2015,2018,2024 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/internal/oaep.h>
9
10#include <botan/exceptn.h>
11#include <botan/rng.h>
12#include <botan/internal/ct_utils.h>
13#include <botan/internal/mgf1.h>
14#include <botan/internal/stl_util.h>
15
16namespace Botan {
17
18/*
19* OAEP Pad Operation
20*/
21size_t OAEP::pad(std::span<uint8_t> output,
22 std::span<const uint8_t> input,
23 size_t key_length,
24 RandomNumberGenerator& rng) const {
25 key_length /= 8;
26
27 if(input.size() > maximum_input_size(key_length * 8)) {
28 throw Invalid_Argument("OAEP: Input is too large");
29 }
30
31 const size_t output_size = key_length;
32
33 output = output.first(output_size); // remainder ignored
34
35 BufferStuffer stuffer(output);
36
37 // We always use a seed len equal to the underlying hash
38 rng.randomize(stuffer.next(m_Phash.size()));
39 stuffer.append(m_Phash);
40 stuffer.append(0x00, stuffer.remaining_capacity() - (1 + input.size()));
41 stuffer.append(0x01);
42 stuffer.append(input);
43 BOTAN_ASSERT_NOMSG(stuffer.full());
44
45 const size_t hlen = m_Phash.size();
46
47 mgf1_mask(*m_mgf1_hash, output.first(hlen), output.subspan(hlen));
48
49 mgf1_mask(*m_mgf1_hash, output.subspan(hlen), output.first(hlen));
50
51 return key_length;
52}
53
54/*
55* OAEP Unpad Operation
56*/
57CT::Option<size_t> OAEP::unpad(std::span<uint8_t> output, std::span<const uint8_t> input) const {
58 BOTAN_ASSERT_NOMSG(output.size() >= input.size());
59
60 /*
61 Must be careful about error messages here; if an attacker can
62 distinguish them, it is easy to use the differences as an oracle to
63 find the secret key, as described in "A Chosen Ciphertext Attack on
64 RSA Optimal Asymmetric Encryption Padding (OAEP) as Standardized in
65 PKCS #1 v2.0", James Manger, Crypto 2001
66
67 Also have to be careful about timing attacks! Pointed out by Falko
68 Strenzke.
69
70 According to the standard (RFC 3447 Section 7.1.1), the encryptor always
71 creates a message as follows:
72 i. Concatenate a single octet with hexadecimal value 0x00,
73 maskedSeed, and maskedDB to form an encoded message EM of
74 length k octets as
75 EM = 0x00 || maskedSeed || maskedDB.
76 where k is the length of the modulus N.
77 Therefore, the first byte should always be zero.
78 */
79
80 if(input.empty()) {
81 return {};
82 }
83
84 auto scope = CT::scoped_poison(input);
85
86 const auto has_leading_0 = CT::Mask<uint8_t>::is_zero(input[0]).as_choice();
87
88 secure_vector<uint8_t> decoded(input.begin() + 1, input.end());
89 auto buf = std::span{decoded};
90
91 const size_t hlen = m_Phash.size();
92
93 mgf1_mask(*m_mgf1_hash, buf.subspan(hlen), buf.first(hlen));
94
95 mgf1_mask(*m_mgf1_hash, buf.first(hlen), buf.subspan(hlen));
96
97 auto delim = oaep_find_delim(buf, m_Phash);
98
99 return CT::copy_output(delim.has_value() && has_leading_0, output, buf, delim.value_or(0));
100}
101
102CT::Option<size_t> oaep_find_delim(std::span<const uint8_t> input, std::span<const uint8_t> phash) {
103 // Too short to be valid, reject immediately
104 if(input.size() < 1 + 2 * phash.size()) {
105 return {};
106 }
107
108 size_t delim_idx = 2 * phash.size();
109 CT::Mask<uint8_t> waiting_for_delim = CT::Mask<uint8_t>::set();
111
112 for(uint8_t ib : input.subspan(2 * phash.size())) {
113 const auto zero_m = CT::Mask<uint8_t>::is_zero(ib);
114 const auto one_m = CT::Mask<uint8_t>::is_equal(ib, 1);
115
116 const auto add_m = waiting_for_delim & zero_m;
117
118 bad_input_m |= waiting_for_delim & ~(zero_m | one_m);
119
120 delim_idx += add_m.if_set_return(1);
121
122 waiting_for_delim &= zero_m;
123 }
124
125 // If we never saw any non-zero byte, then it's not valid input
126 bad_input_m |= waiting_for_delim;
127
128 // If the P hash is wrong, then it's not valid
129 bad_input_m |= CT::is_not_equal(&input[phash.size()], phash.data(), phash.size());
130
131 delim_idx += 1;
132
133 const auto accept = !(bad_input_m.as_choice());
134
135 return CT::Option(delim_idx, accept);
136}
137
138/*
139* Return the max input size for a given key size
140*/
141size_t OAEP::maximum_input_size(size_t keybits) const {
142 if(keybits / 8 > 2 * m_Phash.size() + 1) {
143 return ((keybits / 8) - 2 * m_Phash.size() - 1);
144 } else {
145 return 0;
146 }
147}
148
149OAEP::OAEP(std::unique_ptr<HashFunction> hash, std::string_view P) : m_mgf1_hash(std::move(hash)) {
150 m_Phash = m_mgf1_hash->process(P);
151}
152
153OAEP::OAEP(std::unique_ptr<HashFunction> hash, std::unique_ptr<HashFunction> mgf1_hash, std::string_view P) :
154 m_mgf1_hash(std::move(mgf1_hash)) {
155 auto phash = std::move(hash);
156 m_Phash = phash->process(P);
157}
158
159} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
static constexpr Mask< T > set()
Definition ct_utils.h:379
constexpr CT::Choice as_choice() const
Definition ct_utils.h:597
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:434
static constexpr Mask< T > is_zero(T x)
Definition ct_utils.h:429
static constexpr Mask< T > cleared()
Definition ct_utils.h:384
OAEP(std::unique_ptr< HashFunction > hash, std::string_view P="")
Definition oaep.cpp:149
size_t maximum_input_size(size_t) const override
Definition oaep.cpp:141
constexpr auto scoped_poison(const Ts &... xs)
Definition ct_utils.h:216
constexpr CT::Mask< T > is_not_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:784
BOTAN_TEST_API CT::Option< size_t > copy_output(CT::Choice accept, std::span< uint8_t > output, std::span< const uint8_t > input, size_t offset)
Definition ct_utils.cpp:13
void mgf1_mask(HashFunction &hash, const uint8_t in[], size_t in_len, uint8_t out[], size_t out_len)
Definition mgf1.cpp:15
CT::Option< size_t > oaep_find_delim(std::span< const uint8_t > input, std::span< const uint8_t > phash)
Definition oaep.cpp:102
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61