aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/pk_pad/eme_oaep/oaep.cpp
blob: 370a9fe4503ee711efe212b817a53e2f9db42519 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*
* OAEP
* (C) 1999-2010,2015 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/oaep.h>
#include <botan/mgf1.h>
#include <botan/internal/ct_utils.h>

namespace Botan {

OAEP* OAEP::make(const Spec& request)
   {
   if(request.algo_name() == "OAEP" && request.arg_count_between(1, 2))
      {
      if(request.arg_count() == 1 ||
         (request.arg_count() == 2 && request.arg(1) == "MGF1"))
         {
         if(auto hash = HashFunction::create(request.arg(0)))
            return new OAEP(hash.release());
         }
      }

   return nullptr;
   }

/*
* OAEP Pad Operation
*/
secure_vector<byte> OAEP::pad(const byte in[], size_t in_length,
                             size_t key_length,
                             RandomNumberGenerator& rng) const
   {
   key_length /= 8;

   if(key_length < in_length + 2*m_Phash.size() + 1)
      throw Invalid_Argument("OAEP: Input is too large");

   secure_vector<byte> out(key_length);

   rng.randomize(out.data(), m_Phash.size());

   buffer_insert(out, m_Phash.size(), m_Phash.data(), m_Phash.size());
   out[out.size() - in_length - 1] = 0x01;
   buffer_insert(out, out.size() - in_length, in, in_length);

   mgf1_mask(*m_hash,
             out.data(), m_Phash.size(),
             &out[m_Phash.size()], out.size() - m_Phash.size());

   mgf1_mask(*m_hash,
             &out[m_Phash.size()], out.size() - m_Phash.size(),
             out.data(), m_Phash.size());

   return out;
   }

/*
* OAEP Unpad Operation
*/
secure_vector<byte> OAEP::unpad(const byte in[], size_t in_length,
                                size_t key_length) const
   {
   /*
   Must be careful about error messages here; if an attacker can
   distinguish them, it is easy to use the differences as an oracle to
   find the secret key, as described in "A Chosen Ciphertext Attack on
   RSA Optimal Asymmetric Encryption Padding (OAEP) as Standardized in
   PKCS #1 v2.0", James Manger, Crypto 2001

   Also have to be careful about timing attacks! Pointed out by Falko
   Strenzke.
   */

   key_length /= 8;

   // Invalid input: truncate to zero length input, causing later
   // checks to fail
   if(in_length > key_length)
      in_length = 0;

   secure_vector<byte> input(key_length);
   buffer_insert(input, key_length - in_length, in, in_length);

   CT::poison(input.data(), input.size());

   const size_t hlen = m_Phash.size();

   mgf1_mask(*m_hash,
             &input[hlen], input.size() - hlen,
             input.data(), hlen);

   mgf1_mask(*m_hash,
             input.data(), hlen,
             &input[hlen], input.size() - hlen);

   size_t delim_idx = 2 * hlen;
   byte waiting_for_delim = 0xFF;
   byte bad_input = 0;

   for(size_t i = delim_idx; i < input.size(); ++i)
      {
      const byte zero_m = CT::is_zero<byte>(input[i]);
      const byte one_m = CT::is_equal<byte>(input[i], 1);

      const byte add_m = waiting_for_delim & zero_m;

      bad_input |= waiting_for_delim & ~(zero_m | one_m);

      delim_idx += CT::select<byte>(add_m, 1, 0);

      waiting_for_delim &= zero_m;
      }

   // If we never saw any non-zero byte, then it's not valid input
   bad_input |= waiting_for_delim;
   bad_input |= CT::expand_mask<byte>(!same_mem(&input[hlen], m_Phash.data(), hlen));

   CT::unpoison(input.data(), input.size());
   CT::unpoison(&bad_input, 1);
   CT::unpoison(&delim_idx, 1);

   if(bad_input)
      throw Decoding_Error("Invalid OAEP encoding");

   return secure_vector<byte>(input.begin() + delim_idx + 1, input.end());
   }

/*
* Return the max input size for a given key size
*/
size_t OAEP::maximum_input_size(size_t keybits) const
   {
   if(keybits / 8 > 2*m_Phash.size() + 1)
      return ((keybits / 8) - 2*m_Phash.size() - 1);
   else
      return 0;
   }

/*
* OAEP Constructor
*/
OAEP::OAEP(HashFunction* hash, const std::string& P) : m_hash(hash)
   {
   m_Phash = m_hash->process(P);
   }

}