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
22namespace Poco {
23namespace Crypto {
24
25
26namespace
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
247CipherImpl::CipherImpl(const CipherKey& key):
248 _key(key)
249{
250}
251
252
253CipherImpl::~CipherImpl()
254{
255}
256
257
258CryptoTransform* 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
265CryptoTransform* 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