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
21namespace Poco {
22namespace Crypto {
23
24
25namespace
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
203CipherImpl::CipherImpl(const CipherKey& key):
204 _key(key)
205{
206}
207
208
209CipherImpl::~CipherImpl()
210{
211}
212
213
214CryptoTransform* 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
221CryptoTransform* 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