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"
16#include "Stroika/Foundation/Streams/InternallySynchronizedInputStream.h"
17#include "Stroika/Foundation/Streams/InternallySynchronizedOutputStream.h"
25using namespace Stroika::Foundation::Cryptography;
26using namespace Stroika::Foundation::Cryptography::Encoding;
27using namespace Stroika::Foundation::Memory;
28using namespace Stroika::Foundation::Streams;
39#if qStroika_HasComponent_OpenSSL
42 struct InOutStrmCommon_ {
43 InOutStrmCommon_ (
const OpenSSLCryptoParams& cryptoParams, Direction d)
44 : fCTX_{::EVP_CIPHER_CTX_new ()}
47 cryptoParams.fInitializer (fCTX_, d);
50 ::EVP_CIPHER_CTX_free (fCTX_);
51 Execution::ReThrow ();
54 InOutStrmCommon_ (
const InOutStrmCommon_&) =
delete;
55 InOutStrmCommon_& operator= (
const InOutStrmCommon_&) =
delete;
56 virtual ~InOutStrmCommon_ ()
58 ::EVP_CIPHER_CTX_free (fCTX_);
60 static constexpr size_t _GetMinOutBufSize (
size_t n)
62 return n + EVP_MAX_BLOCK_LENGTH;
65 span<byte> _runOnce (span<const byte> data2Process, span<byte> outBuf)
67 Require (outBuf.size () >= _GetMinOutBufSize (data2Process.size ()));
69 Cryptography::Providers::OpenSSL::Exception::ThrowLastErrorIfFailed (
70 ::EVP_CipherUpdate (fCTX_,
reinterpret_cast<unsigned char*
> (outBuf.data ()), &outLen,
71 reinterpret_cast<const unsigned char*
> (data2Process.data ()),
static_cast<int> (data2Process.size ())));
73 Ensure (
static_cast<size_t> (outLen) <= outBuf.size ());
74 return outBuf.subspan (0, outLen);
78 size_t _cipherFinal (
byte* outBufStart, [[maybe_unused]]
byte* outBufEnd)
80 Require (outBufStart <= outBufEnd and
static_cast<size_t> (outBufEnd - outBufStart) >= _GetMinOutBufSize (0));
85 Cryptography::Providers::OpenSSL::Exception::ThrowLastErrorIfFailed (
86 ::EVP_CipherFinal_ex (fCTX_,
reinterpret_cast<unsigned char*
> (outBufStart), &outLen));
89 Ensure (outLen <= (outBufEnd - outBufStart));
90 return size_t (outLen);
92 EVP_CIPHER_CTX* fCTX_{
nullptr};
93 bool fFinalCalled_{
false};
98 class OpenSSLInputStreamRep_ :
public InputStream::IRep<byte>,
private InOutStrmCommon_ {
100 static constexpr size_t kInBufSize_ = 10 * 1024;
103 OpenSSLInputStreamRep_ (
const OpenSSLCryptoParams& cryptoParams, Direction d,
const InputStream::Ptr<byte>& realIn)
104 : InOutStrmCommon_{cryptoParams, d}
114 if (fRealIn_ !=
nullptr) {
117 Assert (fRealIn_ ==
nullptr);
118 Ensure (not IsOpenRead ());
122 return fRealIn_ !=
nullptr;
127 Require (IsOpenRead ());
133 if (not PreRead_ (NoDataAvailableHandling::eDontBlock)) {
136 return static_cast<size_t> (fOutBufEnd_ > fOutBufStart_);
149 Require (not intoBuffer.empty ());
150 [[maybe_unused]] lock_guard critSec{fCriticalSection_};
151 Require (IsOpenRead ());
152 if (not PreRead_ (blockFlag)) {
155 if (fOutBufStart_ < fOutBufEnd_) {
156 size_t n2Copy = min<size_t> (fOutBufEnd_ - fOutBufStart_, intoBuffer.size ());
157 (void)::memcpy (intoBuffer.data (), fOutBufStart_, n2Copy);
158 fOutBufStart_ += n2Copy;
159 return intoBuffer.subspan (0, n2Copy);
161 return span<ElementType>{};
166 if (fOutBufStart_ == fOutBufEnd_) {
171 byte toDecryptBuf[kInBufSize_];
173 size_t n2Decrypt = 0;
175 case NoDataAvailableHandling::eBlockIfNoDataAvailable: {
176 n2Decrypt = fRealIn_.ReadBlocking (span{toDecryptBuf}).size ();
178 case NoDataAvailableHandling::eDontBlock: {
179 if (
auto oRes = fRealIn_.ReadNonBlocking (span{toDecryptBuf})) {
180 n2Decrypt = oRes->size ();
190 if (n2Decrypt == 0) {
191 size_t nBytesInOutBuf = _cipherFinal (fOutBuf_.begin (), fOutBuf_.end ());
192 Assert (nBytesInOutBuf <= fOutBuf_.GetSize ());
193 fOutBufStart_ = fOutBuf_.begin ();
194 fOutBufEnd_ = fOutBufStart_ + nBytesInOutBuf;
197 fOutBuf_.GrowToSize_uninitialized (_GetMinOutBufSize (std::size (toDecryptBuf)));
198 span<byte> outBufUsed = _runOnce (span{toDecryptBuf, n2Decrypt}, span{fOutBuf_});
199 Assert (outBufUsed.size () <= fOutBuf_.GetSize ());
200 if (outBufUsed.size () == 0) {
206 fOutBufStart_ = fOutBuf_.begin ();
207 fOutBufEnd_ = fOutBufStart_ + outBufUsed.size ();
215 mutable recursive_mutex fCriticalSection_;
217 byte* fOutBufStart_{
nullptr};
218 byte* fOutBufEnd_{
nullptr};
222 class OpenSSLOutputStreamRep_ :
public OutputStream::IRep<byte>,
private InOutStrmCommon_ {
224 OpenSSLOutputStreamRep_ (
const OpenSSLCryptoParams& cryptoParams, Direction d,
const OutputStream::Ptr<byte>& realOut)
225 : InOutStrmCommon_{cryptoParams, d}
229 virtual ~OpenSSLOutputStreamRep_ ()
245 if (fRealOut_ !=
nullptr) {
248 Ensure (fRealOut_ ==
nullptr);
252 return fRealOut_ !=
nullptr;
257 Require (IsOpenWrite ());
263 Require (IsOpenWrite ());
268 virtual void Write (span<const byte> elts)
override
270 Require (not elts.empty ());
271 Require (IsOpenWrite ());
273 [[maybe_unused]]
auto&& critSec = lock_guard{fCriticalSection_};
274 span<byte> outBufUsed = _runOnce (elts, span{outBuf});
275 Assert (outBufUsed.size () <= outBuf.GetSize ());
276 fRealOut_.Write (outBufUsed);
278 virtual void Flush ()
override
280 Require (IsOpenWrite ());
281 byte outBuf[EVP_MAX_BLOCK_LENGTH];
282 size_t nBytesInOutBuf = _cipherFinal (begin (outBuf), end (outBuf));
283 Assert (nBytesInOutBuf <
sizeof (outBuf));
284 fRealOut_.Write (span{outBuf, nBytesInOutBuf});
288 mutable recursive_mutex fCriticalSection_;
299 void ApplySettings2CTX_ (EVP_CIPHER_CTX* ctx,
const EVP_CIPHER* cipher, Direction d,
bool nopad,
bool useArgumentKeyLength,
304 bool enc = (d == Direction::eEncrypt);
306 Verify (::EVP_CIPHER_CTX_set_padding (ctx, 0) == 1);
308 Cryptography::Providers::OpenSSL::Exception::ThrowLastErrorIfFailed (::EVP_CipherInit_ex (ctx, cipher, NULL,
nullptr,
nullptr, enc));
309 size_t keyLen = ::EVP_CIPHER_CTX_key_length (ctx);
310 size_t ivLen = ::EVP_CIPHER_CTX_iv_length (ctx);
312 if (useArgumentKeyLength) {
314 Verify (::EVP_CIPHER_CTX_set_key_length (ctx,
static_cast<int> (keyLen)) == 1);
320 (void)::memset (useKey.begin (), 0, keyLen);
321 (void)::memset (useIV.begin (), 0, ivLen);
323 (void)::memcpy (useKey.begin (), key.
begin (), min (keyLen, key.
size ()));
324 if (not initialIV.
empty ()) {
325 (void)::memcpy (useIV.begin (), initialIV.
begin (), min (ivLen, initialIV.
size ()));
327 Cryptography::Providers::OpenSSL::Exception::ThrowLastErrorIfFailed (::EVP_CipherInit_ex (
328 ctx,
nullptr, NULL,
reinterpret_cast<unsigned char*
> (useKey.begin ()),
reinterpret_cast<unsigned char*
> (useIV.begin ()), enc));
332OpenSSLCryptoParams::OpenSSLCryptoParams (CipherAlgorithm alg,
const BLOB& key,
const BLOB& initialIV)
334 using namespace Providers::OpenSSL;
336 bool useArgumentKeyLength =
false;
337 if (alg == CipherAlgorithms::kRC2_CBC () or alg == CipherAlgorithms::kRC2_ECB () or alg == CipherAlgorithms::kRC2_CFB () or
338 alg == CipherAlgorithms::kRC2_OFB () or alg == CipherAlgorithms::kRC4 ()) {
339 useArgumentKeyLength =
true;
341 fInitializer = [=] (::EVP_CIPHER_CTX* ctx, Direction d) {
342 ApplySettings2CTX_ (ctx, alg, d, nopad, useArgumentKeyLength, key, initialIV);
346OpenSSLCryptoParams::OpenSSLCryptoParams (CipherAlgorithm alg,
const DerivedKey& derivedKey)
347 : OpenSSLCryptoParams{alg, derivedKey.fKey, derivedKey.fIV}
356auto OpenSSLInputStream::New (
const OpenSSLCryptoParams& cryptoParams, Direction direction,
const InputStream::Ptr<byte>& realIn)
365 switch (internallySynchronized) {
366 case Execution::eInternallySynchronized:
367 return Streams::InternallySynchronizedInputStream::New<OpenSSLInputStreamRep_> ({}, cryptoParams, direction, realIn);
368 case Execution::eNotKnownInternallySynchronized:
369 return New (cryptoParams, direction, realIn);
372 return New (cryptoParams, direction, realIn);
381auto OpenSSLOutputStream::New (
const OpenSSLCryptoParams& cryptoParams, Direction direction,
const OutputStream::Ptr<byte>& realOut)
390 switch (internallySynchronized) {
391 case Execution::eInternallySynchronized:
392 return Streams::InternallySynchronizedOutputStream::New<OpenSSLOutputStreamRep_> ({}, cryptoParams, direction, realOut);
393 case Execution::eNotKnownInternallySynchronized:
394 return New (cryptoParams, direction, realOut);
397 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.