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