Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
DerivedKey.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#if qStroika_HasComponent_OpenSSL
7#include <openssl/evp.h>
8#endif
9
12#include "Stroika/Foundation/Containers/Common.h"
14#include "Stroika/Foundation/Cryptography/Digest/Digester.h"
15#include "Stroika/Foundation/Cryptography/Digest/Hash.h"
17#include "Stroika/Foundation/Execution/Common.h"
19
20#include "Exception.h"
21
22#include "DerivedKey.h"
23
24using std::byte;
25
26using namespace Stroika::Foundation;
28using namespace Stroika::Foundation::Cryptography;
29using namespace Stroika::Foundation::Cryptography::Providers::OpenSSL;
30using namespace Stroika::Foundation::Memory;
31
32using Memory::BLOB;
34
35#if qStroika_HasComponent_OpenSSL && defined(_MSC_VER)
36// Use #pragma comment lib instead of explicit entry in the lib entry of the project file
37#if OPENSSL_VERSION_NUMBER < 0x1010000fL
38#pragma comment(lib, "libeay32.lib")
39#pragma comment(lib, "ssleay32.lib")
40#else
41#pragma comment(lib, "libcrypto.lib")
42#pragma comment(lib, "libssl.lib")
43#pragma comment(lib, "ws2_32.lib")
44#pragma comment(lib, "crypt32.lib")
45#endif
46#endif
47
48#if qStroika_HasComponent_OpenSSL
49/*
50 ********************************************************************************
51 **************** Cryptography::Providers::OpenSSL::DerivedKey ******************
52 ********************************************************************************
53 */
54String DerivedKey::ToString () const
55{
57 result << "{"sv;
58 result << "key: "sv << fKey;
59 result << ", IV: "sv << fIV;
60 result << "}"sv;
61 return result.str ();
62}
63#endif
64
65/*
66 ********************************************************************************
67 ************ Cryptography::Providers::OpenSSL::WinCryptDeriveKey ***************
68 ********************************************************************************
69 */
70#if qStroika_HasComponent_OpenSSL
71namespace {
72 pair<BLOB, BLOB> mkWinCryptDeriveKey_ (size_t keyLen, [[maybe_unused]] DigestAlgorithm digestAlgorithm, const BLOB& passwd)
73 {
74 // @todo http://stroika-bugs.sophists.com/browse/STK-192
75 /*
76 * From http://msdn2.microsoft.com/en-us/library/aa379916.aspx
77 *
78 * o Form a 64-byte buffer by repeating the constant 0x36 64 times.
79 * Let k be the length of the hash value that is represented by the
80 * input parameter hBaseData. Set the first k bytes of the buffer to the result
81 * of an XOR operation of the first k bytes of the buffer with the hash value that
82 * is represented by the input parameter hBaseData.
83 * o Form a 64-byte buffer by repeating the constant 0x5C 64 times.
84 * Set the first k bytes of the buffer to the result of an XOR operation of the
85 * first k bytes of the buffer with the hash value that is represented by the input parameter hBaseData.
86 * o Hash the result of step 1 by using the same hash algorithm as that used to compute the
87 * hash value that is represented by the hBaseData parameter.
88 * o Hash the result of step 2 by using the same hash algorithm as that used
89 * to compute the hash value that is represented by the hBaseData parameter.
90 * o Concatenate the result of step 3 with the result of step 4.
91 * o Use the first n bytes of the result of step 5 as the derived key.
92 */
93 size_t usePWDLen = min (passwd.length (), static_cast<size_t> (64));
94 const byte* passwordBytes = passwd.begin ();
95 byte buf1[64];
96 {
97 std::fill_n (buf1, NEltsOf (buf1), static_cast<byte> (0x36));
98 for (unsigned long i = 0; i < usePWDLen; ++i) {
99 buf1[i] ^= passwordBytes[i];
100 }
101 }
102 byte buf2[64];
103 {
104 std::fill_n (buf2, NEltsOf (buf2), static_cast<byte> (0x5C));
105 for (unsigned long i = 0; i < usePWDLen; ++i) {
106 buf2[i] ^= passwordBytes[i];
107 }
108 }
109 Require (digestAlgorithm == DigestAlgorithms::kMD5); // else NYI
111 Digest::ComputeDigest<Digest::Algorithm::MD5> (begin (buf1), end (buf1)),
112 Digest::ComputeDigest<Digest::Algorithm::MD5> (begin (buf2), end (buf2))};
113 Assert (keyLen <= sizeof (encodedResults)); // NYI otherwise - but we could zero fill
114 const byte* encodedResultBytes = reinterpret_cast<const byte*> (begin (encodedResults));
115 BLOB resultKey{encodedResultBytes, encodedResultBytes + std::min (sizeof (encodedResults), keyLen)};
116 BLOB iv;
117 return pair<BLOB, BLOB>{resultKey, iv};
118 }
119 size_t mkDefKeyLen_ (WinCryptDeriveKey::Provider provider, CipherAlgorithm cipherAlgorithm)
120 {
121 // @todo see table https://msdn.microsoft.com/en-us/library/aa379916.aspx
122
123 switch (provider) {
124 case WinCryptDeriveKey::Provider::Base: {
125 if (cipherAlgorithm == CipherAlgorithms::kRC2_CBC () or cipherAlgorithm == CipherAlgorithms::kRC2_CFB () or
126 cipherAlgorithm == CipherAlgorithms::kRC2_ECB () or cipherAlgorithm == CipherAlgorithms::kRC2_OFB () or
127 cipherAlgorithm == CipherAlgorithms::kRC4 ()) {
128 return 40 / 8;
129 }
130#if 0
131 case CipherAlgorithm::eDES {
132 return 56 / 8;
133 }
134#endif
135 } break;
136 case WinCryptDeriveKey::Provider::Enhanced: {
137 if (cipherAlgorithm == CipherAlgorithms::kRC2_CBC () or cipherAlgorithm == CipherAlgorithms::kRC2_CFB () or
138 cipherAlgorithm == CipherAlgorithms::kRC2_ECB () or cipherAlgorithm == CipherAlgorithms::kRC2_OFB () or
139 cipherAlgorithm == CipherAlgorithms::kRC4 ()) {
140 return 128 / 8;
141 }
142 } break;
143 }
144 AssertNotImplemented (); // incomplete set of defautl see above table
145 return 128 / 8;
146 }
147}
148WinCryptDeriveKey::WinCryptDeriveKey (size_t keyLen, DigestAlgorithm digestAlgorithm, const BLOB& passwd)
149 : DerivedKey{mkWinCryptDeriveKey_ (keyLen, digestAlgorithm, passwd)}
150{
151}
152
153WinCryptDeriveKey::WinCryptDeriveKey (Provider provider, CipherAlgorithm cipherAlgorithm, DigestAlgorithm digestAlgorithm, const BLOB& passwd)
154 : DerivedKey{WinCryptDeriveKey{mkDefKeyLen_ (provider, cipherAlgorithm), digestAlgorithm, passwd}}
155{
156}
157#endif
158
159#if qStroika_HasComponent_OpenSSL
160/*
161 ********************************************************************************
162 ************** Cryptography::Providers::OpenSSL::EVP_BytesToKey ****************
163 ********************************************************************************
164 */
165namespace {
166 pair<BLOB, BLOB> mkEVP_BytesToKey_ (CipherAlgorithm cipherAlgorithm, DigestAlgorithm digestAlgorithm, const BLOB& passwd,
167 unsigned int nRounds, const optional<BLOB>& salt)
168 {
169 Require (nRounds >= 1);
170 StackBuffer<byte> useKey{Memory::eUninitialized, cipherAlgorithm.KeyLength ()};
171 StackBuffer<byte> useIV{Memory::eUninitialized, cipherAlgorithm.IVLength ()};
172 if (salt and salt->GetSize () != 8) [[unlikely]] {
173 // Could truncate and fill to adapt to different sized salt...
174 Execution::Throw (Execution::Exception{"only 8-byte salt with EVP_BytesToKey"sv});
175 }
176 int i = ::EVP_BytesToKey (cipherAlgorithm, digestAlgorithm,
177 reinterpret_cast<const unsigned char*> (salt ? NullCoalesce (salt).begin () : nullptr),
178 reinterpret_cast<const unsigned char*> (passwd.begin ()), static_cast<int> (passwd.size ()), nRounds,
179 reinterpret_cast<unsigned char*> (useKey.begin ()), reinterpret_cast<unsigned char*> (useIV.begin ()));
180 Assert (i >= 0);
181 if (i == 0) {
182 Cryptography::Providers::OpenSSL::Exception::ThrowLastError ();
183 }
184 Assert (i == static_cast<int> (cipherAlgorithm.KeyLength ()));
185 return pair<BLOB, BLOB>{BLOB{useKey.begin (), useKey.end ()}, BLOB{useIV.begin (), useIV.end ()}};
186 }
187}
188template <>
189EVP_BytesToKey::EVP_BytesToKey (CipherAlgorithm cipherAlgorithm, DigestAlgorithm digestAlgorithm, const BLOB& passwd, unsigned int nRounds,
190 const optional<BLOB>& salt)
191 : DerivedKey{mkEVP_BytesToKey_ (cipherAlgorithm, digestAlgorithm, passwd, nRounds, salt)}
192{
193}
194
195/*
196 ********************************************************************************
197 ************ Cryptography::Providers::OpenSSL::PKCS5_PBKDF2_HMAC ***************
198 ********************************************************************************
199 */
200namespace {
201 pair<BLOB, BLOB> mkPKCS5_PBKDF2_HMAC_ (size_t keyLen, size_t ivLen, DigestAlgorithm digestAlgorithm, const BLOB& passwd,
202 unsigned int nRounds, const optional<BLOB>& salt)
203 {
204 StackBuffer<byte> outBuf{Memory::eUninitialized, keyLen + ivLen};
205 Assert (keyLen + ivLen < size_t (numeric_limits<int>::max ())); // for static cast below
206 int a = ::PKCS5_PBKDF2_HMAC (reinterpret_cast<const char*> (passwd.begin ()), static_cast<int> (passwd.length ()),
207 reinterpret_cast<const unsigned char*> (salt ? salt->begin () : nullptr),
208 static_cast<int> (salt ? salt->size () : 0), nRounds, digestAlgorithm,
209 static_cast<int> (keyLen + ivLen), reinterpret_cast<unsigned char*> (outBuf.begin ()));
210 if (a == 0) [[unlikely]] {
211 Execution::Throw (Execution::Exception{"PKCS5_PBKDF2_HMAC error"sv});
212 }
213 const byte* p = outBuf.begin ();
214 return pair<BLOB, BLOB> (BLOB{p, p + keyLen}, BLOB{p + keyLen, p + keyLen + ivLen});
215 }
216}
217template <>
218PKCS5_PBKDF2_HMAC::PKCS5_PBKDF2_HMAC (size_t keyLen, size_t ivLen, DigestAlgorithm digestAlgorithm, const BLOB& passwd,
219 unsigned int nRounds, const optional<BLOB>& salt)
220 : DerivedKey{mkPKCS5_PBKDF2_HMAC_ (keyLen, ivLen, digestAlgorithm, passwd, nRounds, salt)}
221{
222}
223#endif
#define AssertNotImplemented()
Definition Assertions.h:401
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
DigesterAlgorithm is specialized for each algorithm; generally don't use this directly,...
Definition Algorithm.h:55
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
Definition Exceptions.h:157
nonvirtual size_t length() const
Definition BLOB.inl:271
nonvirtual const byte * begin() const
Definition BLOB.inl:253
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43