1 | // |
2 | // CipherKeyImpl.cpp |
3 | // |
4 | // Library: Crypto |
5 | // Package: Cipher |
6 | // Module: CipherKeyImpl |
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/CipherKeyImpl.h" |
16 | #include "Poco/Crypto/CryptoTransform.h" |
17 | #include "Poco/Crypto/CipherFactory.h" |
18 | #include "Poco/Exception.h" |
19 | #include "Poco/RandomStream.h" |
20 | #include <openssl/err.h> |
21 | #include <openssl/evp.h> |
22 | |
23 | |
24 | namespace Poco { |
25 | namespace Crypto { |
26 | |
27 | |
28 | CipherKeyImpl::CipherKeyImpl(const std::string& name, |
29 | const std::string& passphrase, |
30 | const std::string& salt, |
31 | int iterationCount, |
32 | const std::string& digest): _pCipher(0), |
33 | _pDigest(0), |
34 | _name(name), |
35 | _key(), |
36 | _iv() |
37 | { |
38 | // dummy access to Cipherfactory so that the EVP lib is initilaized |
39 | CipherFactory::defaultFactory(); |
40 | _pCipher = EVP_get_cipherbyname(name.c_str()); |
41 | |
42 | if (!_pCipher) |
43 | throw Poco::NotFoundException("Cipher " + name + " was not found" ); |
44 | |
45 | _pDigest = EVP_get_digestbyname(digest.c_str()); |
46 | |
47 | if (!_pDigest) |
48 | throw Poco::NotFoundException("Digest " + name + " was not found" ); |
49 | |
50 | |
51 | _key = ByteVec(keySize()); |
52 | _iv = ByteVec(ivSize()); |
53 | generateKey(passphrase, salt, iterationCount); |
54 | } |
55 | |
56 | |
57 | CipherKeyImpl::CipherKeyImpl(const std::string& name, |
58 | const ByteVec& key, |
59 | const ByteVec& iv): _pCipher(0), |
60 | _pDigest(0), |
61 | _name(name), |
62 | _key(key), |
63 | _iv(iv) |
64 | { |
65 | // dummy access to Cipherfactory so that the EVP lib is initilaized |
66 | CipherFactory::defaultFactory(); |
67 | _pCipher = EVP_get_cipherbyname(name.c_str()); |
68 | |
69 | if (!_pCipher) |
70 | throw Poco::NotFoundException("Cipher " + name + " was not found" ); |
71 | } |
72 | |
73 | |
74 | CipherKeyImpl::CipherKeyImpl(const std::string& name): _pCipher(0), |
75 | _pDigest(0), |
76 | _name(name), |
77 | _key(), |
78 | _iv() |
79 | { |
80 | // dummy access to Cipherfactory so that the EVP lib is initilaized |
81 | CipherFactory::defaultFactory(); |
82 | _pCipher = EVP_get_cipherbyname(name.c_str()); |
83 | |
84 | if (!_pCipher) |
85 | throw Poco::NotFoundException("Cipher " + name + " was not found" ); |
86 | _key = ByteVec(keySize()); |
87 | _iv = ByteVec(ivSize()); |
88 | generateKey(); |
89 | } |
90 | |
91 | |
92 | CipherKeyImpl::~CipherKeyImpl() |
93 | { |
94 | } |
95 | |
96 | |
97 | CipherKeyImpl::Mode CipherKeyImpl::mode() const |
98 | { |
99 | switch (EVP_CIPHER_mode(_pCipher)) |
100 | { |
101 | case EVP_CIPH_STREAM_CIPHER: |
102 | return MODE_STREAM_CIPHER; |
103 | |
104 | case EVP_CIPH_ECB_MODE: |
105 | return MODE_ECB; |
106 | |
107 | case EVP_CIPH_CBC_MODE: |
108 | return MODE_CBC; |
109 | |
110 | case EVP_CIPH_CFB_MODE: |
111 | return MODE_CFB; |
112 | |
113 | case EVP_CIPH_OFB_MODE: |
114 | return MODE_OFB; |
115 | |
116 | #if OPENSSL_VERSION_NUMBER >= 0x10001000L |
117 | case EVP_CIPH_CTR_MODE: |
118 | return MODE_CTR; |
119 | |
120 | case EVP_CIPH_GCM_MODE: |
121 | return MODE_GCM; |
122 | |
123 | case EVP_CIPH_CCM_MODE: |
124 | return MODE_CCM; |
125 | #endif |
126 | } |
127 | throw Poco::IllegalStateException("Unexpected value of EVP_CIPHER_mode()" ); |
128 | } |
129 | |
130 | |
131 | void CipherKeyImpl::generateKey() |
132 | { |
133 | ByteVec vec; |
134 | |
135 | getRandomBytes(vec, keySize()); |
136 | setKey(vec); |
137 | |
138 | getRandomBytes(vec, ivSize()); |
139 | setIV(vec); |
140 | } |
141 | |
142 | |
143 | void CipherKeyImpl::getRandomBytes(ByteVec& vec, std::size_t count) |
144 | { |
145 | Poco::RandomInputStream random; |
146 | |
147 | vec.clear(); |
148 | vec.reserve(count); |
149 | |
150 | for (int i = 0; i < count; ++i) |
151 | vec.push_back(static_cast<unsigned char>(random.get())); |
152 | } |
153 | |
154 | |
155 | void CipherKeyImpl::generateKey( |
156 | const std::string& password, |
157 | const std::string& salt, |
158 | int iterationCount) |
159 | { |
160 | unsigned char keyBytes[EVP_MAX_KEY_LENGTH]; |
161 | unsigned char ivBytes[EVP_MAX_IV_LENGTH]; |
162 | |
163 | // OpenSSL documentation specifies that the salt must be an 8-byte array. |
164 | unsigned char saltBytes[8]; |
165 | if (!salt.empty()) |
166 | { |
167 | int len = static_cast<int>(salt.size()); |
168 | // Create the salt array from the salt string |
169 | for (int i = 0; i < 8; ++i) |
170 | saltBytes[i] = salt.at(i % len); |
171 | for (int i = 8; i < len; ++i) |
172 | saltBytes[i % 8] ^= salt.at(i); |
173 | } |
174 | |
175 | // Now create the key and IV, using the digest set in the constructor. |
176 | int keySize = EVP_BytesToKey( |
177 | _pCipher, |
178 | _pDigest ? _pDigest : EVP_md5(), |
179 | (salt.empty() ? 0 : saltBytes), |
180 | reinterpret_cast<const unsigned char*>(password.data()), |
181 | static_cast<int>(password.size()), |
182 | iterationCount, |
183 | keyBytes, |
184 | ivBytes); |
185 | |
186 | // Copy the buffers to our member byte vectors. |
187 | _key.assign(keyBytes, keyBytes + keySize); |
188 | |
189 | if (ivSize() == 0) |
190 | _iv.clear(); |
191 | else |
192 | _iv.assign(ivBytes, ivBytes + ivSize()); |
193 | } |
194 | |
195 | |
196 | int CipherKeyImpl::keySize() const |
197 | { |
198 | return EVP_CIPHER_key_length(_pCipher); |
199 | } |
200 | |
201 | |
202 | int CipherKeyImpl::blockSize() const |
203 | { |
204 | return EVP_CIPHER_block_size(_pCipher); |
205 | } |
206 | |
207 | |
208 | int CipherKeyImpl::ivSize() const |
209 | { |
210 | return EVP_CIPHER_iv_length(_pCipher); |
211 | } |
212 | |
213 | |
214 | void CipherKeyImpl::setIV(const ByteVec& iv) |
215 | { |
216 | poco_assert(mode() == MODE_GCM || iv.size() == static_cast<ByteVec::size_type>(ivSize())); |
217 | _iv = iv; |
218 | } |
219 | |
220 | |
221 | } } // namespace Poco::Crypto |
222 | |