1 /**
2 * OAEP
3 * 
4 * Copyright:
5 * (C) 1999-2007 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.pk_pad.oaep;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_EME_OAEP):
15 
16 import botan.pk_pad.eme;
17 import botan.kdf.kdf;
18 import botan.hash.hash;
19 import botan.rng.rng;
20 import botan.pk_pad.mgf1;
21 import botan.utils.mem_ops;
22 import botan.utils.types;
23 
24 /**
25 * OAEP (called EME1 in IEEE 1363 and in earlier versions of the library)
26 */
27 final class OAEP : EME
28 {
29 public:
30     /*
31     * Return the max input size for a given key size
32     */
33     override size_t maximumInputSize(size_t keybits) const
34     {
35         if (keybits / 8 > 2*m_Phash.length + 1)
36             return ((keybits / 8) - 2*m_Phash.length - 1);
37         else
38             return 0;
39     }
40 
41     /**
42     * Params:
43     *  hash = object to use for hashing (takes ownership)
44     *  P = an optional label. Normally empty.
45     */
46     this(HashFunction hash, in string P = "")
47     {
48         m_hash = hash;
49         m_Phash = m_hash.process(P);
50     }
51 
52     /*
53     * OAEP Pad Operation
54     */
55     override SecureVector!ubyte pad(const(ubyte)* input, size_t in_length, size_t key_length,
56                                     RandomNumberGenerator rng) const
57     {
58         key_length /= 8;
59         
60         if (key_length < in_length + 2*m_Phash.length + 1)
61             throw new InvalidArgument("OAEP: Input is too large");
62         
63         SecureVector!ubyte output = SecureVector!ubyte(key_length);
64         rng.randomize(output.ptr, m_Phash.length);
65         
66         bufferInsert(output, m_Phash.length, m_Phash.ptr, m_Phash.length);
67         output[output.length - in_length - 1] = 0x01;
68         bufferInsert(output, output.length - in_length, input, in_length);
69         
70         mgf1Mask(cast() *m_hash, output.ptr, m_Phash.length, &output[m_Phash.length], output.length - m_Phash.length);
71         
72         mgf1Mask(cast() *m_hash, &output[m_Phash.length], output.length - m_Phash.length,
73         output.ptr, m_Phash.length);
74         
75         return output;
76     }
77 
78     /*
79     * OAEP Unpad Operation
80     */
81     override SecureVector!ubyte unpad(const(ubyte)* input_, size_t in_length, size_t key_length) const
82     {
83         /*
84         Must be careful about error messages here; if an attacker can
85         distinguish them, it is easy to use the differences as an oracle to
86         find the secret key, as described in "A Chosen Ciphertext Attack on
87         RSA Optimal Asymmetric Encryption Padding (OAEP) as Standardized in
88         PKCS #1 v2.0", James Manger, Crypto 2001
89 
90         Also have to be careful about timing attacks! Pointed out by Falko
91         Strenzke.
92         */
93         
94         key_length /= 8;
95         
96         // Invalid input: truncate to zero length input, causing later
97         // checks to fail
98         if (in_length > key_length)
99             in_length = 0;
100         
101         SecureVector!ubyte input = SecureVector!ubyte(key_length);
102         bufferInsert(input, key_length - in_length, input_, in_length);
103         
104         mgf1Mask(cast() *m_hash, &input[m_Phash.length], input.length - m_Phash.length, input.ptr, m_Phash.length);        
105         mgf1Mask(cast() *m_hash, input.ptr, m_Phash.length, &input[m_Phash.length], input.length - m_Phash.length);
106         
107         bool waiting_for_delim = true;
108         bool bad_input = false;
109         size_t delim_idx = 2 * m_Phash.length;
110         
111         /*
112         * GCC 4.5 on x86-64 compiles this in a way that is still vunerable
113         * to timing analysis. Other compilers, or GCC on other platforms,
114         * may or may not.
115         */
116         for (size_t i = delim_idx; i < input.length; ++i)
117         {
118             const bool zero_p = !input[i];
119             const bool one_p = input[i] == 0x01;
120             
121             const bool add_1 = waiting_for_delim && zero_p;
122             
123             bad_input |= waiting_for_delim && !(zero_p || one_p);
124             
125             delim_idx += add_1;
126             
127             waiting_for_delim &= zero_p;
128         }
129         
130         // If we never saw any non-zero ubyte, then it's not valid input
131         bad_input |= waiting_for_delim;
132         
133         bad_input |= !sameMem(&input[m_Phash.length], m_Phash.ptr, m_Phash.length);
134         
135         if (bad_input)
136             throw new DecodingError("Invalid OAEP encoding");
137 
138         return SecureVector!ubyte(input.ptr[delim_idx + 1 .. input.length]);
139     }
140 
141     SecureVector!ubyte m_Phash;
142     Unique!HashFunction m_hash;
143 }