/src/botan/src/lib/pk_pad/eme_oaep/oaep.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * OAEP |
3 | | * (C) 1999-2010,2015,2018 Jack Lloyd |
4 | | * |
5 | | * Botan is released under the Simplified BSD License (see license.txt) |
6 | | */ |
7 | | |
8 | | #include <botan/oaep.h> |
9 | | #include <botan/mgf1.h> |
10 | | #include <botan/exceptn.h> |
11 | | #include <botan/rng.h> |
12 | | #include <botan/internal/ct_utils.h> |
13 | | |
14 | | namespace Botan { |
15 | | |
16 | | /* |
17 | | * OAEP Pad Operation |
18 | | */ |
19 | | secure_vector<uint8_t> OAEP::pad(const uint8_t in[], size_t in_length, |
20 | | size_t key_length, |
21 | | RandomNumberGenerator& rng) const |
22 | 0 | { |
23 | 0 | key_length /= 8; |
24 | 0 |
|
25 | 0 | if(in_length > maximum_input_size(key_length * 8)) |
26 | 0 | { |
27 | 0 | throw Invalid_Argument("OAEP: Input is too large"); |
28 | 0 | } |
29 | 0 | |
30 | 0 | secure_vector<uint8_t> out(key_length); |
31 | 0 |
|
32 | 0 | rng.randomize(out.data(), m_Phash.size()); |
33 | 0 |
|
34 | 0 | buffer_insert(out, m_Phash.size(), m_Phash.data(), m_Phash.size()); |
35 | 0 | out[out.size() - in_length - 1] = 0x01; |
36 | 0 | buffer_insert(out, out.size() - in_length, in, in_length); |
37 | 0 |
|
38 | 0 | mgf1_mask(*m_mgf1_hash, |
39 | 0 | out.data(), m_Phash.size(), |
40 | 0 | &out[m_Phash.size()], out.size() - m_Phash.size()); |
41 | 0 |
|
42 | 0 | mgf1_mask(*m_mgf1_hash, |
43 | 0 | &out[m_Phash.size()], out.size() - m_Phash.size(), |
44 | 0 | out.data(), m_Phash.size()); |
45 | 0 |
|
46 | 0 | return out; |
47 | 0 | } |
48 | | |
49 | | /* |
50 | | * OAEP Unpad Operation |
51 | | */ |
52 | | secure_vector<uint8_t> OAEP::unpad(uint8_t& valid_mask, |
53 | | const uint8_t in[], size_t in_length) const |
54 | 0 | { |
55 | 0 | /* |
56 | 0 | Must be careful about error messages here; if an attacker can |
57 | 0 | distinguish them, it is easy to use the differences as an oracle to |
58 | 0 | find the secret key, as described in "A Chosen Ciphertext Attack on |
59 | 0 | RSA Optimal Asymmetric Encryption Padding (OAEP) as Standardized in |
60 | 0 | PKCS #1 v2.0", James Manger, Crypto 2001 |
61 | 0 |
|
62 | 0 | Also have to be careful about timing attacks! Pointed out by Falko |
63 | 0 | Strenzke. |
64 | 0 |
|
65 | 0 | According to the standard (Section 7.1.1), the encryptor always |
66 | 0 | creates a message as follows: |
67 | 0 | i. Concatenate a single octet with hexadecimal value 0x00, |
68 | 0 | maskedSeed, and maskedDB to form an encoded message EM of |
69 | 0 | length k octets as |
70 | 0 | EM = 0x00 || maskedSeed || maskedDB. |
71 | 0 | where k is the length of the modulus N. |
72 | 0 | Therefore, the first byte can always be skipped safely. |
73 | 0 | */ |
74 | 0 |
|
75 | 0 | const uint8_t skip_first = CT::Mask<uint8_t>::is_zero(in[0]).if_set_return(1); |
76 | 0 |
|
77 | 0 | secure_vector<uint8_t> input(in + skip_first, in + in_length); |
78 | 0 |
|
79 | 0 | const size_t hlen = m_Phash.size(); |
80 | 0 |
|
81 | 0 | mgf1_mask(*m_mgf1_hash, |
82 | 0 | &input[hlen], input.size() - hlen, |
83 | 0 | input.data(), hlen); |
84 | 0 |
|
85 | 0 | mgf1_mask(*m_mgf1_hash, |
86 | 0 | input.data(), hlen, |
87 | 0 | &input[hlen], input.size() - hlen); |
88 | 0 |
|
89 | 0 | return oaep_find_delim(valid_mask, input.data(), input.size(), m_Phash); |
90 | 0 | } |
91 | | |
92 | | secure_vector<uint8_t> |
93 | | oaep_find_delim(uint8_t& valid_mask, |
94 | | const uint8_t input[], size_t input_len, |
95 | | const secure_vector<uint8_t>& Phash) |
96 | 214 | { |
97 | 214 | const size_t hlen = Phash.size(); |
98 | 214 | |
99 | 214 | // Too short to be valid, reject immediately |
100 | 214 | if(input_len < 1 + 2*hlen) |
101 | 6 | { |
102 | 6 | return secure_vector<uint8_t>(); |
103 | 6 | } |
104 | 208 | |
105 | 208 | CT::poison(input, input_len); |
106 | 208 | |
107 | 208 | size_t delim_idx = 2 * hlen; |
108 | 208 | CT::Mask<uint8_t> waiting_for_delim = CT::Mask<uint8_t>::set(); |
109 | 208 | CT::Mask<uint8_t> bad_input_m = CT::Mask<uint8_t>::cleared(); |
110 | 208 | |
111 | 194k | for(size_t i = delim_idx; i < input_len; ++i) |
112 | 194k | { |
113 | 194k | const auto zero_m = CT::Mask<uint8_t>::is_zero(input[i]); |
114 | 194k | const auto one_m = CT::Mask<uint8_t>::is_equal(input[i], 1); |
115 | 194k | |
116 | 194k | const auto add_m = waiting_for_delim & zero_m; |
117 | 194k | |
118 | 194k | bad_input_m |= waiting_for_delim & ~(zero_m | one_m); |
119 | 194k | |
120 | 194k | delim_idx += add_m.if_set_return(1); |
121 | 194k | |
122 | 194k | waiting_for_delim &= zero_m; |
123 | 194k | } |
124 | 208 | |
125 | 208 | // If we never saw any non-zero byte, then it's not valid input |
126 | 208 | bad_input_m |= waiting_for_delim; |
127 | 208 | bad_input_m |= CT::Mask<uint8_t>::is_zero(ct_compare_u8(&input[hlen], Phash.data(), hlen)); |
128 | 208 | |
129 | 208 | delim_idx += 1; |
130 | 208 | |
131 | 208 | valid_mask = (~bad_input_m).unpoisoned_value(); |
132 | 208 | const secure_vector<uint8_t> output = CT::copy_output(bad_input_m, input, input_len, delim_idx); |
133 | 208 | |
134 | 208 | CT::unpoison(input, input_len); |
135 | 208 | |
136 | 208 | return output; |
137 | 208 | } |
138 | | |
139 | | /* |
140 | | * Return the max input size for a given key size |
141 | | */ |
142 | | size_t OAEP::maximum_input_size(size_t keybits) const |
143 | 0 | { |
144 | 0 | if(keybits / 8 > 2*m_Phash.size() + 1) |
145 | 0 | return ((keybits / 8) - 2*m_Phash.size() - 1); |
146 | 0 | else |
147 | 0 | return 0; |
148 | 0 | } |
149 | | |
150 | | /* |
151 | | * OAEP Constructor |
152 | | */ |
153 | | OAEP::OAEP(HashFunction* hash, const std::string& P) : m_mgf1_hash(hash) |
154 | 0 | { |
155 | 0 | m_Phash = m_mgf1_hash->process(P); |
156 | 0 | } |
157 | | |
158 | | OAEP::OAEP(HashFunction* hash, |
159 | | HashFunction* mgf1_hash, |
160 | | const std::string& P) : m_mgf1_hash(mgf1_hash) |
161 | 0 | { |
162 | 0 | std::unique_ptr<HashFunction> phash(hash); // takes ownership |
163 | 0 | m_Phash = phash->process(P); |
164 | 0 | } |
165 | | |
166 | | } |