1//
2// RSACipherImpl.cpp
3//
4// Library: Crypto
5// Package: RSA
6// Module: RSACipherImpl
7//
8// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Crypto/RSACipherImpl.h"
16#include "Poco/Crypto/CryptoTransform.h"
17#include "Poco/Exception.h"
18#include <openssl/err.h>
19#include <openssl/rsa.h>
20#include <cstring>
21
22
23namespace Poco {
24namespace Crypto {
25
26
27namespace
28{
29 void throwError()
30 {
31 unsigned long err;
32 std::string msg;
33
34 while ((err = ERR_get_error()))
35 {
36 if (!msg.empty())
37 msg.append("; ");
38 msg.append(ERR_error_string(err, 0));
39 }
40
41 throw Poco::IOException(msg);
42 }
43
44
45 int mapPaddingMode(RSAPaddingMode paddingMode)
46 {
47 switch (paddingMode)
48 {
49 case RSA_PADDING_PKCS1:
50 return RSA_PKCS1_PADDING;
51 case RSA_PADDING_PKCS1_OAEP:
52 return RSA_PKCS1_OAEP_PADDING;
53 case RSA_PADDING_SSLV23:
54 return RSA_SSLV23_PADDING;
55 case RSA_PADDING_NONE:
56 return RSA_NO_PADDING;
57 default:
58 poco_bugcheck();
59 return RSA_NO_PADDING;
60 }
61 }
62
63
64 class RSAEncryptImpl: public CryptoTransform
65 {
66 public:
67 RSAEncryptImpl(const RSA* pRSA, RSAPaddingMode paddingMode);
68 ~RSAEncryptImpl();
69
70 std::size_t blockSize() const;
71 std::size_t maxDataSize() const;
72
73 std::streamsize transform(const unsigned char* input,
74 std::streamsize inputLength,
75 unsigned char* output,
76 std::streamsize outputLength);
77
78 std::streamsize finalize(unsigned char* output, std::streamsize length);
79
80 private:
81 const RSA* _pRSA;
82 RSAPaddingMode _paddingMode;
83 std::streamsize _pos;
84 unsigned char* _pBuf;
85 };
86
87
88 RSAEncryptImpl::RSAEncryptImpl(const RSA* pRSA, RSAPaddingMode paddingMode):
89 _pRSA(pRSA),
90 _paddingMode(paddingMode),
91 _pos(0),
92 _pBuf(0)
93 {
94 _pBuf = new unsigned char[blockSize()];
95 }
96
97
98 RSAEncryptImpl::~RSAEncryptImpl()
99 {
100 delete [] _pBuf;
101 }
102
103
104 std::size_t RSAEncryptImpl::blockSize() const
105 {
106 return RSA_size(_pRSA);
107 }
108
109
110 std::size_t RSAEncryptImpl::maxDataSize() const
111 {
112 std::size_t size = blockSize();
113 switch (_paddingMode)
114 {
115 case RSA_PADDING_PKCS1:
116 case RSA_PADDING_SSLV23:
117 size -= 11;
118 break;
119 case RSA_PADDING_PKCS1_OAEP:
120 size -= 41;
121 break;
122 default:
123 break;
124 }
125 return size;
126 }
127
128
129 std::streamsize RSAEncryptImpl::transform(const unsigned char* input, std::streamsize inputLength,
130 unsigned char* output, std::streamsize outputLength)
131 {
132 // always fill up the buffer before writing!
133 std::streamsize maxSize = static_cast<std::streamsize>(maxDataSize());
134 std::streamsize rsaSize = static_cast<std::streamsize>(blockSize());
135 poco_assert_dbg(_pos <= maxSize);
136 poco_assert (outputLength >= rsaSize);
137 int rc = 0;
138 while (inputLength > 0)
139 {
140 // check how many data bytes we are missing to get the buffer full
141 poco_assert_dbg (maxSize >= _pos);
142 std::streamsize missing = maxSize - _pos;
143 if (missing == 0)
144 {
145 poco_assert (outputLength >= rsaSize);
146 int n = RSA_public_encrypt(static_cast<int>(maxSize), _pBuf, output,
147 const_cast<RSA*>(_pRSA), mapPaddingMode(_paddingMode));
148 if (n == -1)
149 throwError();
150 rc += n;
151 output += n;
152 outputLength -= n;
153 _pos = 0;
154
155 }
156 else
157 {
158 if (missing > inputLength) missing = inputLength;
159 std::memcpy(_pBuf + _pos, input, static_cast<std::size_t>(missing));
160 input += missing;
161 _pos += missing;
162 inputLength -= missing;
163 }
164 }
165 return rc;
166 }
167
168
169 std::streamsize RSAEncryptImpl::finalize(unsigned char* output, std::streamsize length)
170 {
171 poco_assert (length >= blockSize());
172 poco_assert (_pos <= maxDataSize());
173 int rc = 0;
174 if (_pos > 0)
175 {
176 rc = RSA_public_encrypt(static_cast<int>(_pos), _pBuf, output,
177 const_cast<RSA*>(_pRSA), mapPaddingMode(_paddingMode));
178 if (rc == -1) throwError();
179 }
180 return rc;
181 }
182
183
184 class RSADecryptImpl: public CryptoTransform
185 {
186 public:
187 RSADecryptImpl(const RSA* pRSA, RSAPaddingMode paddingMode);
188 ~RSADecryptImpl();
189
190 std::size_t blockSize() const;
191
192 std::streamsize transform(const unsigned char* input,
193 std::streamsize inputLength,
194 unsigned char* output,
195 std::streamsize outputLength);
196
197 std::streamsize finalize(unsigned char* output,
198 std::streamsize length);
199
200 private:
201 const RSA* _pRSA;
202 RSAPaddingMode _paddingMode;
203 std::streamsize _pos;
204 unsigned char* _pBuf;
205 };
206
207
208 RSADecryptImpl::RSADecryptImpl(const RSA* pRSA, RSAPaddingMode paddingMode):
209 _pRSA(pRSA),
210 _paddingMode(paddingMode),
211 _pos(0),
212 _pBuf(0)
213 {
214 _pBuf = new unsigned char[blockSize()];
215 }
216
217
218 RSADecryptImpl::~RSADecryptImpl()
219 {
220 delete [] _pBuf;
221 }
222
223
224 std::size_t RSADecryptImpl::blockSize() const
225 {
226 return RSA_size(_pRSA);
227 }
228
229
230 std::streamsize RSADecryptImpl::transform(
231 const unsigned char* input,
232 std::streamsize inputLength,
233 unsigned char* output,
234 std::streamsize outputLength)
235 {
236
237 // always fill up the buffer before decrypting!
238 std::streamsize rsaSize = static_cast<std::streamsize>(blockSize());
239 poco_assert_dbg(_pos <= rsaSize);
240 poco_assert (outputLength >= rsaSize);
241 int rc = 0;
242 while (inputLength > 0)
243 {
244 // check how many data bytes we are missing to get the buffer full
245 poco_assert_dbg (rsaSize >= _pos);
246 std::streamsize missing = rsaSize - _pos;
247 if (missing == 0)
248 {
249 int tmp = RSA_private_decrypt(static_cast<int>(rsaSize), _pBuf, output, const_cast<RSA*>(_pRSA), mapPaddingMode(_paddingMode));
250 if (tmp == -1)
251 throwError();
252 rc += tmp;
253 output += tmp;
254 outputLength -= tmp;
255 _pos = 0;
256
257 }
258 else
259 {
260 if (missing > inputLength)
261 missing = inputLength;
262
263 std::memcpy(_pBuf + _pos, input, static_cast<std::size_t>(missing));
264 input += missing;
265 _pos += missing;
266 inputLength -= missing;
267 }
268 }
269 return rc;
270 }
271
272
273 std::streamsize RSADecryptImpl::finalize(unsigned char* output, std::streamsize length)
274 {
275 poco_assert (length >= blockSize());
276 int rc = 0;
277 if (_pos > 0)
278 {
279 rc = RSA_private_decrypt(static_cast<int>(_pos), _pBuf, output, const_cast<RSA*>(_pRSA), mapPaddingMode(_paddingMode));
280 if (rc == -1)
281 throwError();
282 }
283 return rc;
284 }
285}
286
287
288RSACipherImpl::RSACipherImpl(const RSAKey& key, RSAPaddingMode paddingMode):
289 _key(key),
290 _paddingMode(paddingMode)
291{
292}
293
294
295RSACipherImpl::~RSACipherImpl()
296{
297}
298
299
300CryptoTransform* RSACipherImpl::createEncryptor()
301{
302 return new RSAEncryptImpl(_key.impl()->getRSA(), _paddingMode);
303}
304
305
306CryptoTransform* RSACipherImpl::createDecryptor()
307{
308 return new RSADecryptImpl(_key.impl()->getRSA(), _paddingMode);
309}
310
311
312} } // namespace Poco::Crypto
313