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 | throw Poco::IllegalStateException("Unexpected value of EVP_CIPHER_mode()" ); |
117 | } |
118 | |
119 | |
120 | void CipherKeyImpl::generateKey() |
121 | { |
122 | ByteVec vec; |
123 | |
124 | getRandomBytes(vec, keySize()); |
125 | setKey(vec); |
126 | |
127 | getRandomBytes(vec, ivSize()); |
128 | setIV(vec); |
129 | } |
130 | |
131 | |
132 | void CipherKeyImpl::getRandomBytes(ByteVec& vec, std::size_t count) |
133 | { |
134 | Poco::RandomInputStream random; |
135 | |
136 | vec.clear(); |
137 | vec.reserve(count); |
138 | |
139 | for (int i = 0; i < count; ++i) |
140 | vec.push_back(static_cast<unsigned char>(random.get())); |
141 | } |
142 | |
143 | |
144 | void CipherKeyImpl::generateKey( |
145 | const std::string& password, |
146 | const std::string& salt, |
147 | int iterationCount) |
148 | { |
149 | unsigned char keyBytes[EVP_MAX_KEY_LENGTH]; |
150 | unsigned char ivBytes[EVP_MAX_IV_LENGTH]; |
151 | |
152 | // OpenSSL documentation specifies that the salt must be an 8-byte array. |
153 | unsigned char saltBytes[8]; |
154 | if (!salt.empty()) |
155 | { |
156 | int len = static_cast<int>(salt.size()); |
157 | // Create the salt array from the salt string |
158 | for (int i = 0; i < 8; ++i) |
159 | saltBytes[i] = salt.at(i % len); |
160 | for (int i = 8; i < len; ++i) |
161 | saltBytes[i % 8] ^= salt.at(i); |
162 | } |
163 | |
164 | // Now create the key and IV, using the digest set in the constructor. |
165 | int keySize = EVP_BytesToKey( |
166 | _pCipher, |
167 | _pDigest ? _pDigest : EVP_md5(), |
168 | (salt.empty() ? 0 : saltBytes), |
169 | reinterpret_cast<const unsigned char*>(password.data()), |
170 | static_cast<int>(password.size()), |
171 | iterationCount, |
172 | keyBytes, |
173 | ivBytes); |
174 | |
175 | // Copy the buffers to our member byte vectors. |
176 | _key.assign(keyBytes, keyBytes + keySize); |
177 | |
178 | if (ivSize() == 0) |
179 | _iv.clear(); |
180 | else |
181 | _iv.assign(ivBytes, ivBytes + ivSize()); |
182 | } |
183 | |
184 | |
185 | int CipherKeyImpl::keySize() const |
186 | { |
187 | return EVP_CIPHER_key_length(_pCipher); |
188 | } |
189 | |
190 | |
191 | int CipherKeyImpl::blockSize() const |
192 | { |
193 | return EVP_CIPHER_block_size(_pCipher); |
194 | } |
195 | |
196 | |
197 | int CipherKeyImpl::ivSize() const |
198 | { |
199 | return EVP_CIPHER_iv_length(_pCipher); |
200 | } |
201 | |
202 | |
203 | } } // namespace Poco::Crypto |
204 | |