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;
38#if qStroika_HasComponent_OpenSSL
41 struct InOutStrmCommon_ {
42 InOutStrmCommon_ (
const OpenSSLCryptoParams& cryptoParams, Direction d)
43 : fCTX_{::EVP_CIPHER_CTX_new ()}
46 cryptoParams.fInitializer (fCTX_, d);
49 ::EVP_CIPHER_CTX_free (fCTX_);
50 Execution::ReThrow ();
53 InOutStrmCommon_ (
const InOutStrmCommon_&) =
delete;
54 InOutStrmCommon_& operator= (
const InOutStrmCommon_&) =
delete;
55 virtual ~InOutStrmCommon_ ()
57 ::EVP_CIPHER_CTX_free (fCTX_);
59 static constexpr size_t _GetMinOutBufSize (
size_t n)
61 return n + EVP_MAX_BLOCK_LENGTH;
64 span<byte> _runOnce (span<const byte> data2Process, span<byte> outBuf)
66 Require (outBuf.size () >= _GetMinOutBufSize (data2Process.size ()));
68 Cryptography::Providers::OpenSSL::Exception::ThrowLastErrorIfFailed (
69 ::EVP_CipherUpdate (fCTX_,
reinterpret_cast<unsigned char*
> (outBuf.data ()), &outLen,
70 reinterpret_cast<const unsigned char*
> (data2Process.data ()),
static_cast<int> (data2Process.size ())));
72 Ensure (
static_cast<size_t> (outLen) <= outBuf.size ());
73 return outBuf.subspan (0, outLen);
77 size_t _cipherFinal (
byte* outBufStart, [[maybe_unused]]
byte* outBufEnd)
79 Require (outBufStart <= outBufEnd and
static_cast<size_t> (outBufEnd - outBufStart) >= _GetMinOutBufSize (0));
84 Cryptography::Providers::OpenSSL::Exception::ThrowLastErrorIfFailed (
85 ::EVP_CipherFinal_ex (fCTX_,
reinterpret_cast<unsigned char*
> (outBufStart), &outLen));
88 Ensure (outLen <= (outBufEnd - outBufStart));
89 return size_t (outLen);
91 EVP_CIPHER_CTX* fCTX_{
nullptr};
92 bool fFinalCalled_{
false};
97 class OpenSSLInputStreamRep_ :
public InputStream::IRep<byte>,
private InOutStrmCommon_ {
99 static constexpr size_t kInBufSize_ = 10 * 1024;
102 OpenSSLInputStreamRep_ (
const OpenSSLCryptoParams& cryptoParams, Direction d,
const InputStream::Ptr<byte>& realIn)
103 : InOutStrmCommon_{cryptoParams, d}
113 if (fRealIn_ !=
nullptr) {
116 Assert (fRealIn_ ==
nullptr);
117 Ensure (not IsOpenRead ());
121 return fRealIn_ !=
nullptr;
126 Require (IsOpenRead ());
132 if (not PreRead_ (NoDataAvailableHandling::eDontBlock)) {
135 return static_cast<size_t> (fOutBufEnd_ > fOutBufStart_);
148 Require (not intoBuffer.empty ());
149 [[maybe_unused]] lock_guard critSec{fCriticalSection_};
150 Require (IsOpenRead ());
151 if (not PreRead_ (blockFlag)) {
154 if (fOutBufStart_ < fOutBufEnd_) {
155 size_t n2Copy = min<size_t> (fOutBufEnd_ - fOutBufStart_, intoBuffer.size ());
156 (void)::memcpy (intoBuffer.data (), fOutBufStart_, n2Copy);
157 fOutBufStart_ += n2Copy;
158 return intoBuffer.subspan (0, n2Copy);
160 return span<ElementType>{};
165 if (fOutBufStart_ == fOutBufEnd_) {
170 byte toDecryptBuf[kInBufSize_];
172 size_t n2Decrypt = 0;
174 case NoDataAvailableHandling::eBlockIfNoDataAvailable: {
175 n2Decrypt = fRealIn_.ReadBlocking (span{toDecryptBuf}).size ();
177 case NoDataAvailableHandling::eDontBlock: {
178 if (
auto oRes = fRealIn_.ReadNonBlocking (span{toDecryptBuf})) {
179 n2Decrypt = oRes->size ();
189 if (n2Decrypt == 0) {
190 size_t nBytesInOutBuf = _cipherFinal (fOutBuf_.begin (), fOutBuf_.end ());
191 Assert (nBytesInOutBuf <= fOutBuf_.GetSize ());
192 fOutBufStart_ = fOutBuf_.begin ();
193 fOutBufEnd_ = fOutBufStart_ + nBytesInOutBuf;
196 fOutBuf_.GrowToSize_uninitialized (_GetMinOutBufSize (NEltsOf (toDecryptBuf)));
197 span<byte> outBufUsed = _runOnce (span{toDecryptBuf, n2Decrypt}, span{fOutBuf_});
198 Assert (outBufUsed.size () <= fOutBuf_.GetSize ());
199 if (outBufUsed.size () == 0) {
205 fOutBufStart_ = fOutBuf_.begin ();
206 fOutBufEnd_ = fOutBufStart_ + outBufUsed.size ();
214 mutable recursive_mutex fCriticalSection_;
216 byte* fOutBufStart_{
nullptr};
217 byte* fOutBufEnd_{
nullptr};
221 class OpenSSLOutputStreamRep_ :
public OutputStream::IRep<byte>,
private InOutStrmCommon_ {
223 OpenSSLOutputStreamRep_ (
const OpenSSLCryptoParams& cryptoParams, Direction d,
const OutputStream::Ptr<byte>& realOut)
224 : InOutStrmCommon_{cryptoParams, d}
228 virtual ~OpenSSLOutputStreamRep_ ()
244 if (fRealOut_ !=
nullptr) {
247 Ensure (fRealOut_ ==
nullptr);
251 return fRealOut_ !=
nullptr;
256 Require (IsOpenWrite ());
262 Require (IsOpenWrite ());
267 virtual void Write (span<const byte> elts)
override
269 Require (not elts.empty ());
270 Require (IsOpenWrite ());
272 [[maybe_unused]]
auto&& critSec = lock_guard{fCriticalSection_};
273 span<byte> outBufUsed = _runOnce (elts, span{outBuf});
274 Assert (outBufUsed.size () <= outBuf.GetSize ());
275 fRealOut_.Write (outBufUsed);
277 virtual void Flush ()
override
279 Require (IsOpenWrite ());
280 byte outBuf[EVP_MAX_BLOCK_LENGTH];
281 size_t nBytesInOutBuf = _cipherFinal (begin (outBuf), end (outBuf));
282 Assert (nBytesInOutBuf <
sizeof (outBuf));
283 fRealOut_.Write (span{outBuf, nBytesInOutBuf});
287 mutable recursive_mutex fCriticalSection_;
298 void ApplySettings2CTX_ (EVP_CIPHER_CTX* ctx,
const EVP_CIPHER* cipher, Direction d,
bool nopad,
bool useArgumentKeyLength,
303 bool enc = (d == Direction::eEncrypt);
305 Verify (::EVP_CIPHER_CTX_set_padding (ctx, 0) == 1);
307 Cryptography::Providers::OpenSSL::Exception::ThrowLastErrorIfFailed (::EVP_CipherInit_ex (ctx, cipher, NULL,
nullptr,
nullptr, enc));
308 size_t keyLen = ::EVP_CIPHER_CTX_key_length (ctx);
309 size_t ivLen = ::EVP_CIPHER_CTX_iv_length (ctx);
311 if (useArgumentKeyLength) {
313 Verify (::EVP_CIPHER_CTX_set_key_length (ctx,
static_cast<int> (keyLen)) == 1);
319 (void)::memset (useKey.begin (), 0, keyLen);
320 (void)::memset (useIV.begin (), 0, ivLen);
322 (void)::memcpy (useKey.begin (), key.
begin (), min (keyLen, key.
size ()));
323 if (not initialIV.
empty ()) {
324 (void)::memcpy (useIV.begin (), initialIV.
begin (), min (ivLen, initialIV.
size ()));
326 Cryptography::Providers::OpenSSL::Exception::ThrowLastErrorIfFailed (::EVP_CipherInit_ex (
327 ctx,
nullptr, NULL,
reinterpret_cast<unsigned char*
> (useKey.begin ()),
reinterpret_cast<unsigned char*
> (useIV.begin ()), enc));
331OpenSSLCryptoParams::OpenSSLCryptoParams (CipherAlgorithm alg,
const BLOB& key,
const BLOB& initialIV)
333 using namespace Providers::OpenSSL;
335 bool useArgumentKeyLength =
false;
336 if (alg == CipherAlgorithms::kRC2_CBC () or alg == CipherAlgorithms::kRC2_ECB () or alg == CipherAlgorithms::kRC2_CFB () or
337 alg == CipherAlgorithms::kRC2_OFB () or alg == CipherAlgorithms::kRC4 ()) {
338 useArgumentKeyLength =
true;
340 fInitializer = [=] (::EVP_CIPHER_CTX* ctx, Direction d) {
341 ApplySettings2CTX_ (ctx, alg, d, nopad, useArgumentKeyLength, key, initialIV);
345OpenSSLCryptoParams::OpenSSLCryptoParams (CipherAlgorithm alg,
const DerivedKey& derivedKey)
346 : OpenSSLCryptoParams{alg, derivedKey.fKey, derivedKey.fIV}
355auto OpenSSLInputStream::New (
const OpenSSLCryptoParams& cryptoParams, Direction direction,
const InputStream::Ptr<byte>& realIn)
364 switch (internallySynchronized) {
365 case Execution::eInternallySynchronized:
366 return Streams::InternallySynchronizedInputStream::New<OpenSSLInputStreamRep_> ({}, cryptoParams, direction, realIn);
367 case Execution::eNotKnownInternallySynchronized:
368 return New (cryptoParams, direction, realIn);
371 return New (cryptoParams, direction, realIn);
380auto OpenSSLOutputStream::New (
const OpenSSLCryptoParams& cryptoParams, Direction direction,
const OutputStream::Ptr<byte>& realOut)
389 switch (internallySynchronized) {
390 case Execution::eInternallySynchronized:
391 return Streams::InternallySynchronizedOutputStream::New<OpenSSLOutputStreamRep_> ({}, cryptoParams, direction, realOut);
392 case Execution::eNotKnownInternallySynchronized:
393 return New (cryptoParams, direction, realOut);
396 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.