4#include "Stroika/Foundation/StroikaPreComp.h"
6#if qStroika_HasComponent_OpenSSL
7#include <openssl/err.h>
8#include <openssl/evp.h>
11#include "Stroika/Foundation/Containers/Common.h"
13#include "Stroika/Foundation/Execution/Common.h"
15#include "Stroika/Foundation/Streams/InternallySynchronizedInputStream.h"
16#include "Stroika/Foundation/Streams/InternallySynchronizedOutputStream.h"
24using namespace Stroika::Foundation::Cryptography;
25using namespace Stroika::Foundation::Cryptography::Encoding;
26using namespace Stroika::Foundation::Memory;
27using namespace Stroika::Foundation::Streams;
33#if qStroika_HasComponent_OpenSSL && defined(_MSC_VER)
35#if OPENSSL_VERSION_NUMBER < 0x1010000fL
36#pragma comment(lib, "libeay32.lib")
37#pragma comment(lib, "ssleay32.lib")
39#pragma comment(lib, "libcrypto.lib")
40#pragma comment(lib, "libssl.lib")
41#pragma comment(lib, "ws2_32.lib")
42#pragma comment(lib, "crypt32.lib")
51#if qStroika_HasComponent_OpenSSL
54 struct InOutStrmCommon_ {
55 InOutStrmCommon_ (
const OpenSSLCryptoParams& cryptoParams, Direction d)
56 : fCTX_{::EVP_CIPHER_CTX_new ()}
59 cryptoParams.fInitializer (fCTX_, d);
62 ::EVP_CIPHER_CTX_free (fCTX_);
66 InOutStrmCommon_ (
const InOutStrmCommon_&) =
delete;
67 InOutStrmCommon_& operator= (
const InOutStrmCommon_&) =
delete;
68 virtual ~InOutStrmCommon_ ()
70 ::EVP_CIPHER_CTX_free (fCTX_);
72 static constexpr size_t _GetMinOutBufSize (
size_t n)
74 return n + EVP_MAX_BLOCK_LENGTH;
77 span<byte> _runOnce (span<const byte> data2Process, span<byte> outBuf)
79 Require (outBuf.size () >= _GetMinOutBufSize (data2Process.size ()));
81 Cryptography::Providers::OpenSSL::Exception::ThrowLastErrorIfFailed (
82 ::EVP_CipherUpdate (fCTX_,
reinterpret_cast<unsigned char*
> (outBuf.data ()), &outLen,
83 reinterpret_cast<const unsigned char*
> (data2Process.data ()),
static_cast<int> (data2Process.size ())));
85 Ensure (
static_cast<size_t> (outLen) <= outBuf.size ());
86 return outBuf.subspan (0, outLen);
90 size_t _cipherFinal (
byte* outBufStart, [[maybe_unused]]
byte* outBufEnd)
92 Require (outBufStart <= outBufEnd and
static_cast<size_t> (outBufEnd - outBufStart) >= _GetMinOutBufSize (0));
97 Cryptography::Providers::OpenSSL::Exception::ThrowLastErrorIfFailed (
98 ::EVP_CipherFinal_ex (fCTX_,
reinterpret_cast<unsigned char*
> (outBufStart), &outLen));
100 Ensure (outLen >= 0);
101 Ensure (outLen <= (outBufEnd - outBufStart));
102 return size_t (outLen);
104 EVP_CIPHER_CTX* fCTX_{
nullptr};
105 bool fFinalCalled_{
false};
110 class OpenSSLInputStreamRep_ :
public InputStream::IRep<byte>,
private InOutStrmCommon_ {
112 static constexpr size_t kInBufSize_ = 10 * 1024;
115 OpenSSLInputStreamRep_ (
const OpenSSLCryptoParams& cryptoParams, Direction d,
const InputStream::Ptr<byte>& realIn)
116 : InOutStrmCommon_{cryptoParams, d}
126 if (fRealIn_ !=
nullptr) {
129 Assert (fRealIn_ ==
nullptr);
130 Ensure (not IsOpenRead ());
134 return fRealIn_ !=
nullptr;
139 Require (IsOpenRead ());
145 if (not PreRead_ (NoDataAvailableHandling::eDontBlock)) {
148 return static_cast<size_t> (fOutBufEnd_ > fOutBufStart_);
161 Require (not intoBuffer.empty ());
162 [[maybe_unused]] lock_guard critSec{fCriticalSection_};
163 Require (IsOpenRead ());
164 if (not PreRead_ (blockFlag)) {
167 if (fOutBufStart_ < fOutBufEnd_) {
168 size_t n2Copy = min<size_t> (fOutBufEnd_ - fOutBufStart_, intoBuffer.size ());
169 (void)::memcpy (intoBuffer.data (), fOutBufStart_, n2Copy);
170 fOutBufStart_ += n2Copy;
171 return intoBuffer.subspan (0, n2Copy);
173 return span<ElementType>{};
178 if (fOutBufStart_ == fOutBufEnd_) {
183 byte toDecryptBuf[kInBufSize_];
185 size_t n2Decrypt = 0;
187 case NoDataAvailableHandling::eBlockIfNoDataAvailable: {
188 n2Decrypt = fRealIn_.ReadBlocking (span{toDecryptBuf}).size ();
190 case NoDataAvailableHandling::eDontBlock: {
191 if (
auto oRes = fRealIn_.ReadNonBlocking (span{toDecryptBuf})) {
192 n2Decrypt = oRes->size ();
202 if (n2Decrypt == 0) {
203 size_t nBytesInOutBuf = _cipherFinal (fOutBuf_.begin (), fOutBuf_.end ());
204 Assert (nBytesInOutBuf <= fOutBuf_.GetSize ());
205 fOutBufStart_ = fOutBuf_.begin ();
206 fOutBufEnd_ = fOutBufStart_ + nBytesInOutBuf;
209 fOutBuf_.GrowToSize_uninitialized (_GetMinOutBufSize (NEltsOf (toDecryptBuf)));
210 span<byte> outBufUsed = _runOnce (span{toDecryptBuf, n2Decrypt}, span{fOutBuf_});
211 Assert (outBufUsed.size () <= fOutBuf_.GetSize ());
212 if (outBufUsed.size () == 0) {
218 fOutBufStart_ = fOutBuf_.begin ();
219 fOutBufEnd_ = fOutBufStart_ + outBufUsed.size ();
227 mutable recursive_mutex fCriticalSection_;
229 byte* fOutBufStart_{
nullptr};
230 byte* fOutBufEnd_{
nullptr};
234 class OpenSSLOutputStreamRep_ :
public OutputStream::IRep<byte>,
private InOutStrmCommon_ {
236 OpenSSLOutputStreamRep_ (
const OpenSSLCryptoParams& cryptoParams, Direction d,
const OutputStream::Ptr<byte>& realOut)
237 : InOutStrmCommon_{cryptoParams, d}
241 virtual ~OpenSSLOutputStreamRep_ ()
257 if (fRealOut_ !=
nullptr) {
260 Ensure (fRealOut_ ==
nullptr);
264 return fRealOut_ !=
nullptr;
269 Require (IsOpenWrite ());
275 Require (IsOpenWrite ());
280 virtual void Write (span<const byte> elts)
override
282 Require (not elts.empty ());
283 Require (IsOpenWrite ());
285 [[maybe_unused]]
auto&& critSec = lock_guard{fCriticalSection_};
286 span<byte> outBufUsed = _runOnce (elts, span{outBuf});
287 Assert (outBufUsed.size () <= outBuf.GetSize ());
288 fRealOut_.Write (outBufUsed);
290 virtual void Flush ()
override
292 Require (IsOpenWrite ());
293 byte outBuf[EVP_MAX_BLOCK_LENGTH];
294 size_t nBytesInOutBuf = _cipherFinal (begin (outBuf), end (outBuf));
295 Assert (nBytesInOutBuf <
sizeof (outBuf));
296 fRealOut_.Write (span{outBuf, nBytesInOutBuf});
300 mutable recursive_mutex fCriticalSection_;
311 void ApplySettings2CTX_ (EVP_CIPHER_CTX* ctx,
const EVP_CIPHER* cipher, Direction d,
bool nopad,
bool useArgumentKeyLength,
316 bool enc = (d == Direction::eEncrypt);
318 Verify (::EVP_CIPHER_CTX_set_padding (ctx, 0) == 1);
320 Cryptography::Providers::OpenSSL::Exception::ThrowLastErrorIfFailed (::EVP_CipherInit_ex (ctx, cipher, NULL,
nullptr,
nullptr, enc));
321 size_t keyLen = ::EVP_CIPHER_CTX_key_length (ctx);
322 size_t ivLen = ::EVP_CIPHER_CTX_iv_length (ctx);
324 if (useArgumentKeyLength) {
326 Verify (::EVP_CIPHER_CTX_set_key_length (ctx,
static_cast<int> (keyLen)) == 1);
332 (void)::memset (useKey.begin (), 0, keyLen);
333 (void)::memset (useIV.begin (), 0, ivLen);
335 (void)::memcpy (useKey.begin (), key.
begin (), min (keyLen, key.
size ()));
336 if (not initialIV.
empty ()) {
337 (void)::memcpy (useIV.begin (), initialIV.
begin (), min (ivLen, initialIV.
size ()));
339 Cryptography::Providers::OpenSSL::Exception::ThrowLastErrorIfFailed (::EVP_CipherInit_ex (
340 ctx,
nullptr, NULL,
reinterpret_cast<unsigned char*
> (useKey.begin ()),
reinterpret_cast<unsigned char*
> (useIV.begin ()), enc));
344OpenSSLCryptoParams::OpenSSLCryptoParams (CipherAlgorithm alg,
const BLOB& key,
const BLOB& initialIV)
346 using namespace Providers::OpenSSL;
348 bool useArgumentKeyLength =
false;
349 if (alg == CipherAlgorithms::kRC2_CBC () or alg == CipherAlgorithms::kRC2_ECB () or alg == CipherAlgorithms::kRC2_CFB () or
350 alg == CipherAlgorithms::kRC2_OFB () or alg == CipherAlgorithms::kRC4 ()) {
351 useArgumentKeyLength =
true;
353 fInitializer = [=] (::EVP_CIPHER_CTX* ctx, Direction d) {
354 ApplySettings2CTX_ (ctx, alg, d, nopad, useArgumentKeyLength, key, initialIV);
358OpenSSLCryptoParams::OpenSSLCryptoParams (CipherAlgorithm alg,
const DerivedKey& derivedKey)
359 : OpenSSLCryptoParams{alg, derivedKey.fKey, derivedKey.fIV}
368auto OpenSSLInputStream::New (
const OpenSSLCryptoParams& cryptoParams, Direction direction,
const InputStream::Ptr<byte>& realIn)
377 switch (internallySynchronized) {
378 case Execution::eInternallySynchronized:
379 return Streams::InternallySynchronizedInputStream::New<OpenSSLInputStreamRep_> ({}, cryptoParams, direction, realIn);
380 case Execution::eNotKnownInternallySynchronized:
381 return New (cryptoParams, direction, realIn);
384 return New (cryptoParams, direction, realIn);
393auto OpenSSLOutputStream::New (
const OpenSSLCryptoParams& cryptoParams, Direction direction,
const OutputStream::Ptr<byte>& realOut)
402 switch (internallySynchronized) {
403 case Execution::eInternallySynchronized:
404 return Streams::InternallySynchronizedOutputStream::New<OpenSSLOutputStreamRep_> ({}, cryptoParams, direction, realOut);
405 case Execution::eNotKnownInternallySynchronized:
406 return New (cryptoParams, direction, realOut);
409 return New (cryptoParams, direction, realOut);
#define RequireNotReached()
#define RequireNotNull(p)
NoDataAvailableHandling
If eDontBlock passed to most Stream APIs, then when the code would do a blocking read,...
int64_t SignedSeekOffsetType
nonvirtual size_t length() const
nonvirtual bool empty() const
nonvirtual const byte * begin() const
nonvirtual size_t size() const
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
virtual bool IsSeekable() const =0
Abstract interface for output stream object. Don't call directly (use Ptr usually) - but use directly...
virtual void CloseWrite()=0
virtual void Write(span< const ELEMENT_TYPE > elts)=0
virtual bool IsOpenWrite() const =0
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.