Stroika Library 3.0d18
 
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
36/*
37 ********************************************************************************
38 **************** Cryptography::Providers::OpenSSL::DerivedKey ******************
39 ********************************************************************************
40 */
41String DerivedKey::ToString () const
42{
44 result << "{"sv;
45 result << "key: "sv << fKey;
46 result << ", IV: "sv << fIV;
47 result << "}"sv;
48 return result.str ();
49}
50#endif
51
52/*
53 ********************************************************************************
54 ************ Cryptography::Providers::OpenSSL::WinCryptDeriveKey ***************
55 ********************************************************************************
56 */
57#if qStroika_HasComponent_OpenSSL
58namespace {
59 pair<BLOB, BLOB> mkWinCryptDeriveKey_ (size_t keyLen, [[maybe_unused]] DigestAlgorithm digestAlgorithm, const BLOB& passwd)
60 {
61 // @todo http://stroika-bugs.sophists.com/browse/STK-192
62 /*
63 * From http://msdn2.microsoft.com/en-us/library/aa379916.aspx
64 *
65 * o Form a 64-byte buffer by repeating the constant 0x36 64 times.
66 * Let k be the length of the hash value that is represented by the
67 * input parameter hBaseData. Set the first k bytes of the buffer to the result
68 * of an XOR operation of the first k bytes of the buffer with the hash value that
69 * is represented by the input parameter hBaseData.
70 * o Form a 64-byte buffer by repeating the constant 0x5C 64 times.
71 * Set the first k bytes of the buffer to the result of an XOR operation of the
72 * first k bytes of the buffer with the hash value that is represented by the input parameter hBaseData.
73 * o Hash the result of step 1 by using the same hash algorithm as that used to compute the
74 * hash value that is represented by the hBaseData parameter.
75 * o Hash the result of step 2 by using the same hash algorithm as that used
76 * to compute the hash value that is represented by the hBaseData parameter.
77 * o Concatenate the result of step 3 with the result of step 4.
78 * o Use the first n bytes of the result of step 5 as the derived key.
79 */
80 size_t usePWDLen = min (passwd.length (), static_cast<size_t> (64));
81 const byte* passwordBytes = passwd.begin ();
82 byte buf1[64];
83 {
84 std::fill_n (buf1, NEltsOf (buf1), static_cast<byte> (0x36));
85 for (unsigned long i = 0; i < usePWDLen; ++i) {
86 buf1[i] ^= passwordBytes[i];
87 }
88 }
89 byte buf2[64];
90 {
91 std::fill_n (buf2, NEltsOf (buf2), static_cast<byte> (0x5C));
92 for (unsigned long i = 0; i < usePWDLen; ++i) {
93 buf2[i] ^= passwordBytes[i];
94 }
95 }
96 Require (digestAlgorithm == DigestAlgorithms::kMD5); // else NYI
98 Digest::ComputeDigest<Digest::Algorithm::MD5> (begin (buf1), end (buf1)),
99 Digest::ComputeDigest<Digest::Algorithm::MD5> (begin (buf2), end (buf2))};
100 Assert (keyLen <= sizeof (encodedResults)); // NYI otherwise - but we could zero fill
101 const byte* encodedResultBytes = reinterpret_cast<const byte*> (begin (encodedResults));
102 BLOB resultKey{encodedResultBytes, encodedResultBytes + std::min (sizeof (encodedResults), keyLen)};
103 BLOB iv;
104 return pair<BLOB, BLOB>{resultKey, iv};
105 }
106 size_t mkDefKeyLen_ (WinCryptDeriveKey::Provider provider, CipherAlgorithm cipherAlgorithm)
107 {
108 // @todo see table https://msdn.microsoft.com/en-us/library/aa379916.aspx
109
110 switch (provider) {
111 case WinCryptDeriveKey::Provider::Base: {
112 if (cipherAlgorithm == CipherAlgorithms::kRC2_CBC () or cipherAlgorithm == CipherAlgorithms::kRC2_CFB () or
113 cipherAlgorithm == CipherAlgorithms::kRC2_ECB () or cipherAlgorithm == CipherAlgorithms::kRC2_OFB () or
114 cipherAlgorithm == CipherAlgorithms::kRC4 ()) {
115 return 40 / 8;
116 }
117#if 0
118 case CipherAlgorithm::eDES {
119 return 56 / 8;
120 }
121#endif
122 } break;
123 case WinCryptDeriveKey::Provider::Enhanced: {
124 if (cipherAlgorithm == CipherAlgorithms::kRC2_CBC () or cipherAlgorithm == CipherAlgorithms::kRC2_CFB () or
125 cipherAlgorithm == CipherAlgorithms::kRC2_ECB () or cipherAlgorithm == CipherAlgorithms::kRC2_OFB () or
126 cipherAlgorithm == CipherAlgorithms::kRC4 ()) {
127 return 128 / 8;
128 }
129 } break;
130 }
131 AssertNotImplemented (); // incomplete set of defautl see above table
132 return 128 / 8;
133 }
134}
135WinCryptDeriveKey::WinCryptDeriveKey (size_t keyLen, DigestAlgorithm digestAlgorithm, const BLOB& passwd)
136 : DerivedKey{mkWinCryptDeriveKey_ (keyLen, digestAlgorithm, passwd)}
137{
138}
139
140WinCryptDeriveKey::WinCryptDeriveKey (Provider provider, CipherAlgorithm cipherAlgorithm, DigestAlgorithm digestAlgorithm, const BLOB& passwd)
141 : DerivedKey{WinCryptDeriveKey{mkDefKeyLen_ (provider, cipherAlgorithm), digestAlgorithm, passwd}}
142{
143}
144#endif
145
146#if qStroika_HasComponent_OpenSSL
147/*
148 ********************************************************************************
149 ************** Cryptography::Providers::OpenSSL::EVP_BytesToKey ****************
150 ********************************************************************************
151 */
152namespace {
153 pair<BLOB, BLOB> mkEVP_BytesToKey_ (CipherAlgorithm cipherAlgorithm, DigestAlgorithm digestAlgorithm, const BLOB& passwd,
154 unsigned int nRounds, const optional<BLOB>& salt)
155 {
156 Require (nRounds >= 1);
157 StackBuffer<byte> useKey{Memory::eUninitialized, cipherAlgorithm.KeyLength ()};
158 StackBuffer<byte> useIV{Memory::eUninitialized, cipherAlgorithm.IVLength ()};
159 if (salt and salt->GetSize () != 8) [[unlikely]] {
160 // Could truncate and fill to adapt to different sized salt...
161 Execution::Throw (Execution::Exception{"only 8-byte salt with EVP_BytesToKey"sv});
162 }
163 int i = ::EVP_BytesToKey (cipherAlgorithm, digestAlgorithm,
164 reinterpret_cast<const unsigned char*> (salt ? NullCoalesce (salt).begin () : nullptr),
165 reinterpret_cast<const unsigned char*> (passwd.begin ()), static_cast<int> (passwd.size ()), nRounds,
166 reinterpret_cast<unsigned char*> (useKey.begin ()), reinterpret_cast<unsigned char*> (useIV.begin ()));
167 Assert (i >= 0);
168 if (i == 0) {
169 Cryptography::Providers::OpenSSL::Exception::ThrowLastError ();
170 }
171 Assert (i == static_cast<int> (cipherAlgorithm.KeyLength ()));
172 return pair<BLOB, BLOB>{BLOB{useKey.begin (), useKey.end ()}, BLOB{useIV.begin (), useIV.end ()}};
173 }
174}
175template <>
176EVP_BytesToKey::EVP_BytesToKey (CipherAlgorithm cipherAlgorithm, DigestAlgorithm digestAlgorithm, const BLOB& passwd, unsigned int nRounds,
177 const optional<BLOB>& salt)
178 : DerivedKey{mkEVP_BytesToKey_ (cipherAlgorithm, digestAlgorithm, passwd, nRounds, salt)}
179{
180}
181
182/*
183 ********************************************************************************
184 ************ Cryptography::Providers::OpenSSL::PKCS5_PBKDF2_HMAC ***************
185 ********************************************************************************
186 */
187namespace {
188 pair<BLOB, BLOB> mkPKCS5_PBKDF2_HMAC_ (size_t keyLen, size_t ivLen, DigestAlgorithm digestAlgorithm, const BLOB& passwd,
189 unsigned int nRounds, const optional<BLOB>& salt)
190 {
191 StackBuffer<byte> outBuf{Memory::eUninitialized, keyLen + ivLen};
192 Assert (keyLen + ivLen < size_t (numeric_limits<int>::max ())); // for static cast below
193 int a = ::PKCS5_PBKDF2_HMAC (reinterpret_cast<const char*> (passwd.begin ()), static_cast<int> (passwd.length ()),
194 reinterpret_cast<const unsigned char*> (salt ? salt->begin () : nullptr),
195 static_cast<int> (salt ? salt->size () : 0), nRounds, digestAlgorithm,
196 static_cast<int> (keyLen + ivLen), reinterpret_cast<unsigned char*> (outBuf.begin ()));
197 if (a == 0) [[unlikely]] {
198 Execution::Throw (Execution::Exception{"PKCS5_PBKDF2_HMAC error"sv});
199 }
200 const byte* p = outBuf.begin ();
201 return pair<BLOB, BLOB> (BLOB{p, p + keyLen}, BLOB{p + keyLen, p + keyLen + ivLen});
202 }
203}
204template <>
205PKCS5_PBKDF2_HMAC::PKCS5_PBKDF2_HMAC (size_t keyLen, size_t ivLen, DigestAlgorithm digestAlgorithm, const BLOB& passwd,
206 unsigned int nRounds, const optional<BLOB>& salt)
207 : DerivedKey{mkPKCS5_PBKDF2_HMAC_ (keyLen, ivLen, digestAlgorithm, passwd, nRounds, salt)}
208{
209}
210#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...