1 | // |
2 | // CipherImpl.cpp |
3 | // |
4 | // Library: Crypto |
5 | // Package: Cipher |
6 | // Module: CipherImpl |
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/CipherImpl.h" |
16 | #include "Poco/Crypto/CryptoTransform.h" |
17 | #include "Poco/Exception.h" |
18 | #include "Poco/Buffer.h" |
19 | #include <openssl/err.h> |
20 | |
21 | |
22 | namespace Poco { |
23 | namespace Crypto { |
24 | |
25 | |
26 | namespace |
27 | { |
28 | void throwError() |
29 | { |
30 | unsigned long err; |
31 | std::string msg; |
32 | |
33 | while ((err = ERR_get_error())) |
34 | { |
35 | if (!msg.empty()) |
36 | msg.append("; " ); |
37 | msg.append(ERR_error_string(err, 0)); |
38 | } |
39 | |
40 | throw Poco::IOException(msg); |
41 | } |
42 | |
43 | |
44 | class CryptoTransformImpl: public CryptoTransform |
45 | { |
46 | public: |
47 | typedef Cipher::ByteVec ByteVec; |
48 | |
49 | enum Direction |
50 | { |
51 | DIR_ENCRYPT, |
52 | DIR_DECRYPT |
53 | }; |
54 | |
55 | CryptoTransformImpl( |
56 | const EVP_CIPHER* pCipher, |
57 | const ByteVec& key, |
58 | const ByteVec& iv, |
59 | Direction dir); |
60 | |
61 | ~CryptoTransformImpl(); |
62 | |
63 | std::size_t blockSize() const; |
64 | int setPadding(int padding); |
65 | std::string getTag(std::size_t tagSize); |
66 | void setTag(const std::string& tag); |
67 | |
68 | std::streamsize transform( |
69 | const unsigned char* input, |
70 | std::streamsize inputLength, |
71 | unsigned char* output, |
72 | std::streamsize outputLength); |
73 | |
74 | std::streamsize finalize( |
75 | unsigned char* output, |
76 | std::streamsize length); |
77 | |
78 | private: |
79 | const EVP_CIPHER* _pCipher; |
80 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
81 | EVP_CIPHER_CTX* _pContext; |
82 | #else |
83 | EVP_CIPHER_CTX _context; |
84 | #endif |
85 | ByteVec _key; |
86 | ByteVec _iv; |
87 | }; |
88 | |
89 | |
90 | CryptoTransformImpl::CryptoTransformImpl( |
91 | const EVP_CIPHER* pCipher, |
92 | const ByteVec& key, |
93 | const ByteVec& iv, |
94 | Direction dir): |
95 | _pCipher(pCipher), |
96 | _key(key), |
97 | _iv(iv) |
98 | { |
99 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
100 | _pContext = EVP_CIPHER_CTX_new(); |
101 | EVP_CipherInit( |
102 | _pContext, |
103 | _pCipher, |
104 | &_key[0], |
105 | _iv.empty() ? 0 : &_iv[0], |
106 | (dir == DIR_ENCRYPT) ? 1 : 0); |
107 | #else |
108 | EVP_CipherInit( |
109 | &_context, |
110 | _pCipher, |
111 | &_key[0], |
112 | _iv.empty() ? 0 : &_iv[0], |
113 | (dir == DIR_ENCRYPT) ? 1 : 0); |
114 | #endif |
115 | |
116 | #if OPENSSL_VERSION_NUMBER >= 0x10001000L |
117 | if (_iv.size() != EVP_CIPHER_iv_length(_pCipher) && EVP_CIPHER_mode(_pCipher) == EVP_CIPH_GCM_MODE) |
118 | { |
119 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
120 | int rc = EVP_CIPHER_CTX_ctrl(_pContext, EVP_CTRL_GCM_SET_IVLEN, static_cast<int>(_iv.size()), NULL); |
121 | #else |
122 | int rc = EVP_CIPHER_CTX_ctrl(&_context, EVP_CTRL_GCM_SET_IVLEN, static_cast<int>(_iv.size()), NULL); |
123 | #endif |
124 | if (rc == 0) throwError(); |
125 | } |
126 | #endif |
127 | } |
128 | |
129 | |
130 | CryptoTransformImpl::~CryptoTransformImpl() |
131 | { |
132 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
133 | EVP_CIPHER_CTX_cleanup(_pContext); |
134 | EVP_CIPHER_CTX_free(_pContext); |
135 | #else |
136 | EVP_CIPHER_CTX_cleanup(&_context); |
137 | #endif |
138 | } |
139 | |
140 | |
141 | std::size_t CryptoTransformImpl::blockSize() const |
142 | { |
143 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
144 | return EVP_CIPHER_CTX_block_size(_pContext); |
145 | #else |
146 | return EVP_CIPHER_CTX_block_size(&_context); |
147 | #endif |
148 | } |
149 | |
150 | |
151 | int CryptoTransformImpl::setPadding(int padding) |
152 | { |
153 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
154 | return EVP_CIPHER_CTX_block_size(_pContext); |
155 | #else |
156 | return EVP_CIPHER_CTX_set_padding(&_context, padding); |
157 | #endif |
158 | } |
159 | |
160 | |
161 | std::string CryptoTransformImpl::getTag(std::size_t tagSize) |
162 | { |
163 | std::string tag; |
164 | #if OPENSSL_VERSION_NUMBER >= 0x10001000L |
165 | Poco::Buffer<char> buffer(tagSize); |
166 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
167 | int rc = EVP_CIPHER_CTX_ctrl(_pContext, EVP_CTRL_GCM_GET_TAG, static_cast<int>(tagSize), buffer.begin()); |
168 | #else |
169 | int rc = EVP_CIPHER_CTX_ctrl(&_context, EVP_CTRL_GCM_GET_TAG, static_cast<int>(tagSize), buffer.begin()); |
170 | #endif |
171 | if (rc == 0) throwError(); |
172 | tag.assign(buffer.begin(), tagSize); |
173 | #endif |
174 | return tag; |
175 | } |
176 | |
177 | |
178 | void CryptoTransformImpl::setTag(const std::string& tag) |
179 | { |
180 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
181 | int rc = EVP_CIPHER_CTX_ctrl(_pContext, EVP_CTRL_GCM_SET_TAG, static_cast<int>(tag.size()), const_cast<char*>(tag.data())); |
182 | #elif OPENSSL_VERSION_NUMBER >= 0x10001000L |
183 | int rc = EVP_CIPHER_CTX_ctrl(&_context, EVP_CTRL_GCM_SET_TAG, static_cast<int>(tag.size()), const_cast<char*>(tag.data())); |
184 | #else |
185 | int rc = 0; |
186 | #endif |
187 | if (rc == 0) throwError(); |
188 | } |
189 | |
190 | |
191 | std::streamsize CryptoTransformImpl::transform( |
192 | const unsigned char* input, |
193 | std::streamsize inputLength, |
194 | unsigned char* output, |
195 | std::streamsize outputLength) |
196 | { |
197 | poco_assert (outputLength >= (inputLength + blockSize() - 1)); |
198 | |
199 | int outLen = static_cast<int>(outputLength); |
200 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
201 | int rc = EVP_CipherUpdate( |
202 | _pContext, |
203 | output, |
204 | &outLen, |
205 | input, |
206 | static_cast<int>(inputLength)); |
207 | #else |
208 | int rc = EVP_CipherUpdate( |
209 | &_context, |
210 | output, |
211 | &outLen, |
212 | input, |
213 | static_cast<int>(inputLength)); |
214 | #endif |
215 | if (rc == 0) |
216 | throwError(); |
217 | |
218 | return static_cast<std::streamsize>(outLen); |
219 | } |
220 | |
221 | |
222 | std::streamsize CryptoTransformImpl::finalize( |
223 | unsigned char* output, |
224 | std::streamsize length) |
225 | { |
226 | poco_assert (length >= blockSize()); |
227 | |
228 | int len = static_cast<int>(length); |
229 | |
230 | // Use the '_ex' version that does not perform implicit cleanup since we |
231 | // will call EVP_CIPHER_CTX_cleanup() from the dtor as there is no |
232 | // guarantee that finalize() will be called if an error occurred. |
233 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
234 | int rc = EVP_CipherFinal_ex(_pContext, output, &len); |
235 | #else |
236 | int rc = EVP_CipherFinal_ex(&_context, output, &len); |
237 | #endif |
238 | |
239 | if (rc == 0) |
240 | throwError(); |
241 | |
242 | return static_cast<std::streamsize>(len); |
243 | } |
244 | } |
245 | |
246 | |
247 | CipherImpl::CipherImpl(const CipherKey& key): |
248 | _key(key) |
249 | { |
250 | } |
251 | |
252 | |
253 | CipherImpl::~CipherImpl() |
254 | { |
255 | } |
256 | |
257 | |
258 | CryptoTransform* CipherImpl::createEncryptor() |
259 | { |
260 | CipherKeyImpl::Ptr p = _key.impl(); |
261 | return new CryptoTransformImpl(p->cipher(), p->getKey(), p->getIV(), CryptoTransformImpl::DIR_ENCRYPT); |
262 | } |
263 | |
264 | |
265 | CryptoTransform* CipherImpl::createDecryptor() |
266 | { |
267 | CipherKeyImpl::Ptr p = _key.impl(); |
268 | return new CryptoTransformImpl(p->cipher(), p->getKey(), p->getIV(), CryptoTransformImpl::DIR_DECRYPT); |
269 | } |
270 | |
271 | |
272 | } } // namespace Poco::Crypto |
273 | |