From 218ee862efb7ddedbc38d45a74ffe894cb684516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0smail=20Y=C4=B1lmaz?= <32938453+ismail-yilmaz@users.noreply.github.com> Date: Mon, 5 May 2025 15:31:58 +0300 Subject: [PATCH] Core/SSL: AES-256-GCM encryption and decryption (#266) --- autotest/AES256/AES256.upp | 10 + autotest/AES256/main.cpp | 85 +++++ reference/AESEncryption/AESEncryption.upp | 12 + reference/AESEncryption/main.cpp | 40 +++ uppsrc/Core/SSL/AES.cpp | 333 ++++++++++++++++++ uppsrc/Core/SSL/SSL.h | 37 ++ uppsrc/Core/SSL/SSL.upp | 5 +- .../SSL/src.tpp/Upp_SSL_AES256GCM_en-us.tpp | 176 +++++++++ 8 files changed, 697 insertions(+), 1 deletion(-) create mode 100644 autotest/AES256/AES256.upp create mode 100644 autotest/AES256/main.cpp create mode 100644 reference/AESEncryption/AESEncryption.upp create mode 100644 reference/AESEncryption/main.cpp create mode 100644 uppsrc/Core/SSL/AES.cpp create mode 100644 uppsrc/Core/SSL/src.tpp/Upp_SSL_AES256GCM_en-us.tpp diff --git a/autotest/AES256/AES256.upp b/autotest/AES256/AES256.upp new file mode 100644 index 000000000..ee9212a4f --- /dev/null +++ b/autotest/AES256/AES256.upp @@ -0,0 +1,10 @@ +uses + Core, + Core/SSL; + +file + main.cpp; + +mainconfig + "" = ""; + diff --git a/autotest/AES256/main.cpp b/autotest/AES256/main.cpp new file mode 100644 index 000000000..8522f2963 --- /dev/null +++ b/autotest/AES256/main.cpp @@ -0,0 +1,85 @@ +#include +#include + +using namespace Upp; + + +CONSOLE_APP_MAIN +{ + StdLogSetup(LOG_FILE | LOG_COUT); + + const String password = "password@!^+1234"; + const String original = "data: username=admin; password=12345!"; + + // Encrypt must succeed and NOT match original + String encrypted = AES256Encrypt(original, password); + ASSERT(!encrypted.IsVoid()); + ASSERT(encrypted != original); + + // Decrypt must succeed and match original + String decrypted = AES256Decrypt(encrypted, password); + ASSERT(decrypted == original); + + // Wrong password should fail + ASSERT(AES256Decrypt(encrypted, "wrong_password").IsVoid()); + + // Empty string encryption/decryption + encrypted = AES256Encrypt("", password); + ASSERT(encrypted.IsVoid()); + decrypted = AES256Decrypt(encrypted, password); + ASSERT(decrypted.IsVoid()); + + // Multiple encryptions of same blob yield different cipherblobs (non-deterministic output) + String encrypted2 = AES256Encrypt(original, password); + String encrypted3 = AES256Encrypt(original, password); + ASSERT(encrypted2 != encrypted3); + + // Decrypt both variants + ASSERT(AES256Decrypt(encrypted2, password) == original); + ASSERT(AES256Decrypt(encrypted3, password) == original); + + // Allow empty passwords + encrypted = AES256Encrypt(original, Null); + decrypted = AES256Decrypt(encrypted, Null); + ASSERT(original == decrypted); + + // Tampering + StringBuffer tampered = encrypted; + tampered[tampered.GetLength() - 1]^= 0x01; + ASSERT(AES256Decrypt(tampered, password).IsVoid()); + + // Large input (to ensure stream-based encryption works) + String large_string('A', 1024 * 1024 * 4); + encrypted = AES256Encrypt(large_string, password); + decrypted = AES256Decrypt(encrypted, password); + ASSERT(!encrypted.IsVoid()); + ASSERT(!decrypted.IsVoid()); + ASSERT(large_string == decrypted); + + // Large input + tampering + tampered = encrypted; + tampered[tampered.GetLength() - 1] ^= 0x01; + ASSERT(AES256Decrypt(tampered, password).IsVoid()); + + { + const String filename = GetTempFileName(); + { + // Write encrypted blob to a file + MemReadStream ms(large_string, large_string.GetLength()); + FileOut fo(filename); + ASSERT(AES256Encrypt(ms, password, fo)); + } + { + // Read encrypted blob from a file + StringStream ss; + FileIn fi(filename); + ASSERT(AES256Decrypt(fi, password, ss)); + ASSERT(ss.GetResult() == large_string); + } + + if(FileExists(filename)) + DeleteFile(filename); + } + + LOG("================ OK"); +} diff --git a/reference/AESEncryption/AESEncryption.upp b/reference/AESEncryption/AESEncryption.upp new file mode 100644 index 000000000..80d709a84 --- /dev/null +++ b/reference/AESEncryption/AESEncryption.upp @@ -0,0 +1,12 @@ +description "Demonstrates AES-256-GCM encryption and decryption.\377"; + +uses + Core, + Core/SSL; + +file + main.cpp; + +mainconfig + "" = ""; + diff --git a/reference/AESEncryption/main.cpp b/reference/AESEncryption/main.cpp new file mode 100644 index 000000000..2e5d628d5 --- /dev/null +++ b/reference/AESEncryption/main.cpp @@ -0,0 +1,40 @@ +#include +#include + +using namespace Upp; + +CONSOLE_APP_MAIN +{ + StdLogSetup(LOG_COUT | LOG_FILE); + + const String plaintext = "Secret message: Doomsday code is 12345."; + const String password = "password123456!@#"; + + String encrypted, decrypted; + + // Encrypt + if(encrypted = AES256Encrypt(plaintext, password); !encrypted.IsVoid()) { + RLOG("Encryption successful."); + RLOG("Encrypted data (hex): " << HexString(encrypted)); + } + else { + RLOG("Encryption failed."); + return; + } + + // Decrypt + if(decrypted = AES256Decrypt(encrypted, password); !decrypted.IsVoid()) { + RLOG("Decryption successful."); + RLOG("Decrypted text: " << decrypted); + } + else { + RLOG("Decryption failed."); + return; + } + + // Check if original matches decrypted + if(decrypted == plaintext) + RLOG("Roundtrip successful: Decrypted text matches original."); + else + RLOG("Roundtrip failed: Data mismatch."); +} diff --git a/uppsrc/Core/SSL/AES.cpp b/uppsrc/Core/SSL/AES.cpp new file mode 100644 index 000000000..7774e3ef9 --- /dev/null +++ b/uppsrc/Core/SSL/AES.cpp @@ -0,0 +1,333 @@ +#include "SSL.h" + +// Encrypts a string using AES-256-GCM with PBKDF2 key derivation +// Format of encrypted data: "GCMv1__" + salt(16) + iv(12) + ciphertext + tag(16) + +#define LLOG(x) // DLOG(x) + +namespace Upp { + +namespace { + constexpr const char* AES_GCM_FORMAT_PREFIX = "GCMv1__"; + constexpr const int AES_GCM_PREFIX_LEN = 7; + constexpr const int AES_GCM_SALT_SIZE = 16; + constexpr const int AES_GCM_KEY_SIZE = 32; + constexpr const int AES_GCM_IV_SIZE = 12; // GCM standard IV size + constexpr const int AES_GCM_TAG_SIZE = 16; + constexpr const int AES_GCM_ENVELOPE_SIZE = AES_GCM_PREFIX_LEN + AES_GCM_SALT_SIZE + AES_GCM_IV_SIZE + AES_GCM_TAG_SIZE; +} + +Aes256Gcm::Aes256Gcm() +: iteration(AES_GCM_DEFAULT_ITERATION) +, chunksize(1024) +{ + ctx = EVP_CIPHER_CTX_new(); + cipher = EVP_CIPHER_fetch(nullptr, "AES-256-GCM", nullptr); +} + +Aes256Gcm::~Aes256Gcm() +{ + if(ctx) + EVP_CIPHER_CTX_free(ctx); + + if(cipher) + EVP_CIPHER_free(cipher); +} + +void Aes256Gcm::SetError(const String& txt) +{ + err = txt; + LLOG("Aes256Gcm: " << txt); +} + +bool Aes256Gcm::Encrypt(Stream& in, const String& password, Stream& out) +{ + err.Clear(); + + byte key[AES_GCM_KEY_SIZE]; + + if(!ctx) { + SetError("Failed to create context"); + return false; + } + + if(!cipher) { + SetError("Failed to fetch AES-256-GCM cipher"); + return false; + } + + // Require size information + if(in.GetSize() <= 0) { + SetError("Invalid stream size or no data to encrypt"); + return false; + } + + byte salt[AES_GCM_SALT_SIZE], iv[AES_GCM_IV_SIZE], tag[AES_GCM_TAG_SIZE]; + int64 processed = 0; + + try { + // Generate random salt + if(!RAND_bytes(salt, sizeof(salt))) + throw Exc("Salt generation failed"); + + // Generate random initialization vector + if(!RAND_bytes(iv, sizeof(iv))) + throw Exc("IV generation failed"); + + // Derive key from password (can be Null) + if(!PKCS5_PBKDF2_HMAC(~password, password.GetLength(), salt, sizeof(salt), iteration, EVP_sha256(), sizeof(key), key)) + throw Exc("PBKDF2: Key derivation failed"); + + // Initialize cipher + if(!EVP_EncryptInit_ex2(ctx, cipher, key, iv, nullptr)) + throw Exc("Cipher initialization failed"); + + // Put header + out.Put(AES_GCM_FORMAT_PREFIX, AES_GCM_PREFIX_LEN); + out.Put(salt, sizeof(salt)); + out.Put(iv, sizeof(iv)); + + // Update counter with header size + processed = AES_GCM_PREFIX_LEN + sizeof(salt) + sizeof(iv); + + if(WhenProgress(processed, in.GetSize() + AES_GCM_ENVELOPE_SIZE)) + throw Exc("Encryption aborted"); + + Buffer buffer(chunksize); + int buflen = 0; + + while(!in.IsEof()) { + String chunk = in.Get(chunksize); + if(chunk.IsEmpty()) + break; + + // Encrypt the chunk + if(!EVP_EncryptUpdate(ctx, buffer, &buflen, (const byte*) ~chunk, chunk.GetLength())) + throw Exc("Encryption failed"); + + if(buflen > 0) { + out.Put(buffer, buflen); + } + + processed += chunk.GetLength(); + if(WhenProgress(processed, in.GetSize() + AES_GCM_ENVELOPE_SIZE)) + throw Exc("Encryption aborted"); + } + + // Finalize + if(!EVP_EncryptFinal_ex(ctx, buffer, &buflen)) + throw Exc("Finalization failed"); + + // Write final block (if any) + if(buflen > 0) { + out.Put(buffer, buflen); + processed += buflen; + } + + // Get GCM tag + OSSL_PARAM params[] = { + OSSL_PARAM_construct_octet_string("tag", tag, sizeof(tag)), + OSSL_PARAM_construct_end() + }; + if(!EVP_CIPHER_CTX_get_params(ctx, params)) + throw Exc("Failed to get tag"); + + // Put tag + out.Put(tag, sizeof(tag)); + processed += sizeof(tag); + + // Final progress (100%) + if(WhenProgress(processed, in.GetSize() + AES_GCM_ENVELOPE_SIZE)) + throw Exc("Encryption aborted"); + + // Leave no trace + OPENSSL_cleanse(&key, sizeof(key)); + return true; + } + catch(const Exc& e) { + SetError(e); + } + catch(...) { + SetError("Unknown exception"); + } + + // Leave no trace + OPENSSL_cleanse(&key, sizeof(key)); + return false; +} + +bool Aes256Gcm::Decrypt(Stream& in, const String& password, Stream& out) +{ + err.Clear(); + + byte key[AES_GCM_KEY_SIZE]; + + if(!ctx) { + SetError("Failed to create context"); + return false; + } + + if(!cipher) { + SetError("Failed to fetch AES-256-GCM cipher"); + return false; + } + + // Require size information + if(in.GetSize() <= 0) { + SetError("Invalid stream size or no data to decrypt"); + return false; + } + + // Read and validate header + String header = in.Get(AES_GCM_PREFIX_LEN); + if(header.GetLength() < AES_GCM_PREFIX_LEN || !header.StartsWith(AES_GCM_FORMAT_PREFIX)) { + SetError("Invalid format"); + return false; + } + + // Read salt + String salt = in.Get(AES_GCM_SALT_SIZE); + if(salt.GetLength() < AES_GCM_SALT_SIZE) { + SetError("Failed to read salt"); + return false; + } + + // Read initialization vector + String iv = in.Get(AES_GCM_IV_SIZE); + if(iv.GetLength() < AES_GCM_IV_SIZE) { + SetError("Failed to read initialization vector"); + return false; + } + + // Get the size of the ciphertext content + const int64 ciphertextlen = in.GetSize() - AES_GCM_ENVELOPE_SIZE; + if(ciphertextlen <= 0) { + SetError("Encrypted input is too short"); + return false; + } + + try { + // Derive key from password (can be Null) + if(!PKCS5_PBKDF2_HMAC(~password, password.GetLength(), salt, AES_GCM_SALT_SIZE, iteration, EVP_sha256(), sizeof(key), key)) + throw Exc("PBKDF2: Key derivation failed"); + + // Init decryption + if(!EVP_DecryptInit_ex2(ctx, cipher, key, iv, nullptr)) + throw Exc("Initialization failed"); + + Buffer buffer(min((int64) chunksize, ciphertextlen)); + int buflen = 0; + int64 remaining = ciphertextlen, processed = AES_GCM_ENVELOPE_SIZE; + + if(WhenProgress(processed, in.GetSize())) + throw Exc("Decryption aborted"); + + while(remaining > 0) { + String chunk = in.Get(min((int64) chunksize, remaining)); + if(chunk.IsEmpty()) + break; + + // Decrypt the chunk + if(!EVP_DecryptUpdate(ctx, buffer, &buflen, (const byte*) ~chunk, chunk.GetLength())) + throw Exc("Decryption failed"); + + if(buflen > 0) + out.Put(buffer, buflen); + + processed += chunk.GetLength(); + remaining -= chunk.GetLength(); + + if(WhenProgress(processed, in.GetSize())) + throw Exc("Decryption aborted"); + } + + String tag = in.Get(AES_GCM_TAG_SIZE); + if(tag.GetLength() < AES_GCM_TAG_SIZE) + throw Exc("Unable to retrieve authentication tag"); + + if(!in.IsEof()) + throw Exc("Trailing data found after authentication tag"); + + // Set GCM tag + OSSL_PARAM params[] = { + OSSL_PARAM_construct_octet_string("tag", (void*) ~tag, AES_GCM_TAG_SIZE), + OSSL_PARAM_construct_end() + }; + + if(!EVP_CIPHER_CTX_set_params(ctx, params)) + throw Exc("Failed to set tag"); + + // Finalize decryption + if(!EVP_DecryptFinal_ex(ctx, buffer, &buflen)) + throw Exc("Authentication failed"); + + // Write final block (if any) + if(buflen > 0) + out.Put(buffer, buflen); + + // Final progress (100%) + if(WhenProgress(in.GetSize(), in.GetSize())) + throw Exc("Decryption aborted"); + + // Leave no trace + OPENSSL_cleanse(&key, sizeof(key)); + return true; + } + catch(const Exc& e) { + SetError(e); + } + catch(...) { + SetError("Unknown exception"); + } + + // Leave no trace + OPENSSL_cleanse(&key, sizeof(key)); + return false; +} + +bool Aes256Gcm::EncDec(bool enc, const String& in, const String& pwd, String& out) +{ + StringStream sin(in), sout; + if(bool b = enc ? Encrypt(sin, pwd, sout) : Decrypt(sin, pwd, sout); b) { + out = sout.GetResult(); + return true; + } + return false; +} + +String AES256Encrypt(const String& in, const String& password, Gate WhenProgress) +{ + // Use sane defaults + String out; + Aes256Gcm aes; + aes.WhenProgress = WhenProgress; + return aes.Encrypt(in, password, out) ? out : String::GetVoid(); +} + +String AES256Decrypt(const String& in, const String& password, Gate WhenProgress) +{ + // Use sane defaults + String out; + Aes256Gcm aes; + aes.WhenProgress = WhenProgress; + return aes.Decrypt(in, password, out) ? out : String::GetVoid(); +} + +bool AES256Encrypt(Stream& in, const String& password, Stream& out, Gate WhenProgress) +{ + // Use sane defaults + Aes256Gcm aes; + aes.WhenProgress = WhenProgress; + return aes.Encrypt(in, password, out); +} + +bool AES256Decrypt(Stream& in, const String& password, Stream& out, Gate WhenProgress) +{ + // Use sane defaults + Aes256Gcm aes; + aes.WhenProgress = WhenProgress; + return aes.Decrypt(in, password, out); +} + + +} \ No newline at end of file diff --git a/uppsrc/Core/SSL/SSL.h b/uppsrc/Core/SSL/SSL.h index 87e1ccad6..a3f43a2cc 100644 --- a/uppsrc/Core/SSL/SSL.h +++ b/uppsrc/Core/SSL/SSL.h @@ -143,4 +143,41 @@ String SslToString(X509_NAME *name); Date Asn1ToDate(ASN1_STRING *time); String Asn1ToString(ASN1_STRING *s); +constexpr const int AES_GCM_MIN_ITERATION = 10000; +constexpr const int AES_GCM_MAX_ITERATION = 1000000; +constexpr const int AES_GCM_DEFAULT_ITERATION = 100000; + +class Aes256Gcm : NoCopy { +public: + Aes256Gcm(); + virtual ~Aes256Gcm(); + + Aes256Gcm& Iteration(int n) { iteration = clamp(n, AES_GCM_MIN_ITERATION, AES_GCM_MAX_ITERATION); return *this; } + Aes256Gcm& Chunksize(int sz) { chunksize = clamp(sz, 128, INT_MAX); return *this; } + + bool Encrypt(Stream& in, const String& password, Stream& out); + bool Encrypt(const String& in, const String& password, String& out) { return EncDec(true, in, password, out); } + bool Decrypt(Stream& in, const String& password, Stream& out); + bool Decrypt(const String& in, const String& password, String& out) { return EncDec(false, in, password, out); } + + Gate WhenProgress; + + String GetErrorDesc() const { return err; } + +private: + bool EncDec(bool enc, const String& in, const String& pwd, String& out); + void SetError(const String& txt); + + EVP_CIPHER_CTX* ctx; + EVP_CIPHER* cipher; + int chunksize; + int iteration; + String err; +}; + +String AES256Encrypt(const String& in, const String& password, Gate WhenProgress = Null); +String AES256Decrypt(const String& in, const String& password, Gate WhenProgress = Null); +bool AES256Encrypt(Stream& in, const String& password, Stream& out, Gate WhenProgress = Null); +bool AES256Decrypt(Stream& in, const String& password, Stream& out, Gate WhenProgress = Null); + } diff --git a/uppsrc/Core/SSL/SSL.upp b/uppsrc/Core/SSL/SSL.upp index 49c091992..a4b721f1b 100644 --- a/uppsrc/Core/SSL/SSL.upp +++ b/uppsrc/Core/SSL/SSL.upp @@ -12,5 +12,8 @@ file InitExit.cpp, Socket.cpp, P7S.cpp, - SSL.icpp; + AES.cpp, + SSL.icpp, + Docs readonly separator, + src.tpp; diff --git a/uppsrc/Core/SSL/src.tpp/Upp_SSL_AES256GCM_en-us.tpp b/uppsrc/Core/SSL/src.tpp/Upp_SSL_AES256GCM_en-us.tpp new file mode 100644 index 000000000..4fd4da5d3 --- /dev/null +++ b/uppsrc/Core/SSL/src.tpp/Upp_SSL_AES256GCM_en-us.tpp @@ -0,0 +1,176 @@ +topic "AES256 Encryption & Decryption"; +[i448;a25;kKO9;2 $$1,0#37138531426314131252341829483380:class] +[l288;2 $$2,2#27521748481378242620020725143825:desc] +[0 $$3,0#96390100711032703541132217272105:end] +[H6;0 $$4,0#05600065144404261032431302351956:begin] +[i448;a25;kKO9;2 $$5,0#37138531426314131252341829483370:item] +[l288;a4;*@5;1 $$6,6#70004532496200323422659154056402:requirement] +[l288;i1121;b17;O9;~~~.1408;2 $$7,0#10431211400427159095818037425705:param] +[i448;b42;O9;2 $$8,8#61672508125594000341940100500538:tparam] +[b42;2 $$9,9#13035079074754324216151401829390:normal] +[2 $$0,0#00000000000000000000000000000000:Default] +[{_}%EN-US +[ {{10000@(113.42.0) [s0; [*@7;4 Aes256Gcm]]}}&] +[s1;%- &] +[s1;:Upp`:`:Aes256Gcm:%- [*3 Aes256Gcm][3 ][@(0.0.255)3 :][3 NoCopy]&] +[s0;l288; Provides AES`-256`-GCM authenticated encryption and decryption +with OpenSSL 3.0 or later.&] +[s2; This class supports both stream`-based and in`-memory encryption, +deriving a strong key from a password using PBKDF2`-HMAC`-SHA256. +Intended for secure, verifiable data encryption using modern +cryptographic primitives. This class is non`-copyable.&] +[s2; &] +[s2; [* Format]&] +[s2;* &] +[s2; Encrypted data is stored in a binary envelope with the following +layout:&] +[s2; &] +[s0;=l288; [C `[GCMv1`_`_`]`[SALT`]`[IV`]`[CIPHERTEXT`]`[TAG`]]&] +[s0;=l288;C &] +[s0;l288;i150;O0; [C GCMv1`_`_]: 7`-byte version identifier (AES`_GCM`_FORMAT`_PREFIX).&] +[s0;l288;i150;O0; [C SALT]: 16`-byte random salt for PBKDF2.&] +[s0;l288;i150;O0; [C IV]: 12`-byte random initialization vector for +AES`-GCM.&] +[s0;l288;i150;O0; [C CIPHERTEXT]: AES`-GCM encrypted payload.&] +[s0;l288;i150;O0; [C TAG]: 16`-byte authentication tag appended at the +end.&] +[s0; &] +[s2; The version prefix allows future format changes without ambiguity. +Authentication is enforced via the GCM tag; tampering or wrong +passwords result in decryption failure.&] +[s2; &] +[s2; [* Thread Safety]&] +[s2; &] +[s0;l288;i150;O0; Instances of Aes256Gcm are [/_ not thread`-safe].&] +[s2;i150;O0; Use separate instances for parallel encryption/decryption.&] +[s2; &] +[s2; [* Performance Considerations]&] +[s2; &] +[s0;l288;i150;O0; Key derivation (PBKDF2) is intentionally CPU`-expensive +to resist brute`-force attacks. The default iteration count balances +security and responsiveness.&] +[s2;i150;O0; Use [^topic`:`/`/Core`/SSL`/src`/Upp`_SSL`_AES256GCM`_en`-us`#Upp`:`:Aes256Gcm`:`:Iteration`(int`)^ I +teration()] method to configure the number of PBKDF2 rounds.&] +[s2;i150;O0; [^topic`:`/`/Core`/SSL`/src`/Upp`_SSL`_AES256GCM`_en`-us`#Upp`:`:Aes256Gcm`:`:Chunksize`(int`)^ C +hunksize()] method can be used to control the size of internal +I/O buffers for streaming scenarios.&] +[s2; &] +[s3;%- &] +[ {{10000F(128)G(128)@1 [s0; [* Constructor detail]]}}&] +[s3;%- &] +[s5;:Upp`:`:Aes256Gcm`:`:Aes256Gcm`(`):%- [* Aes256Gcm]()&] +[s2; Default constructor. Initializes context and AES`-256`-GCM cipher.&] +[s3;%- &] +[ {{10000F(128)G(128)@1 [s0;%- Constants]}}&] +[s3;%- &] +[s5;:Upp`:`:AES`_GCM`_DEFAULT`_ITERATION:%- [@(0.0.255) constexpr] +[@(0.0.255) int] [* AES`_GCM`_DEFAULT`_ITERATION]&] +[s2; Default PBKDF2 iteration count (100000).&] +[s3;%- &] +[ {{10000F(128)G(128)@1 [s0; [* Public Method List]]}}&] +[s3;%- &] +[s5;:Upp`:`:Aes256Gcm`:`:Iteration`(int`):%- Aes256Gcm[@(0.0.255) `&] +[* Iteration]([@(0.0.255) int] [*@3 n])&] +[s2; Sets the PBKDF2 iteration count. The value is clamped to a safe +range. Default is implementation`-defined. Returns `*this for +method chaining.&] +[s3;%- &] +[s4;%- &] +[s5;:Upp`:`:Aes256Gcm`:`:Chunksize`(int`):%- Aes256Gcm[@(0.0.255) `&] +[* Chunksize]([@(0.0.255) int] [*@3 sz])&] +[s2; Sets the processing chunk size (for streaming I/O) to [%-*@3 sz]. +Must be at least 128 bytes. Returns `*this for method chaining.&] +[s3;%- &] +[s4;%- &] +[s5;:Upp`:`:Aes256Gcm`:`:Encrypt`(Stream`&`,const String`&`,Stream`&`):%- [@(0.0.255) b +ool] [* Encrypt](Stream[@(0.0.255) `&] [*@3 in], [@(0.0.255) const] String[@(0.0.255) `&] +[*@3 password], Stream[@(0.0.255) `&] [*@3 out])&] +[s2; Encrypts data from an input stream [%-*@3 in ]using the provided +[%-*@3 password] . Writes the result to the output stream [%-*@3 out]. +Returns true on success. On failure the error description can +be get using [^topic`:`/`/Core`/SSL`/src`/Upp`_SSL`_AES256GCM`_en`-us`#Upp`:`:Aes256Gcm`:`:GetErrorDesc`(`)const^ G +etErrorDesc()] method.&] +[s3;%- &] +[s4;%- &] +[s5;:Upp`:`:Aes256Gcm`:`:Encrypt`(const String`&`,const String`&`,String`&`):%- [@(0.0.255) b +ool] [* Encrypt]([@(0.0.255) const ]String[@(0.0.255) `&] [*@3 in], [@(0.0.255) const] +String[@(0.0.255) `&] [*@3 password], String[@(0.0.255) `&] [*@3 out])&] +[s2; Encrypts the input string [%-*@3 in]. Writes the result to [%-*@3 out]. +Returns true on success. On failure the error description can +be get using [^topic`:`/`/Core`/SSL`/src`/Upp`_SSL`_AES256GCM`_en`-us`#Upp`:`:Aes256Gcm`:`:GetErrorDesc`(`)const^ G +etErrorDesc()] method.&] +[s3;%- &] +[s4;%- &] +[s5;:Upp`:`:Aes256Gcm`:`:Decrypt`(Stream`&`,const String`&`,Stream`&`):%- [@(0.0.255) b +ool] [* Decrypt](Stream[@(0.0.255) `&] [*@3 in], [@(0.0.255) const] String[@(0.0.255) `&] +[*@3 password], Stream[@(0.0.255) `&] [*@3 out])&] +[s2; Decrypts data from an input stream [%-*@3 in ]using the provided +[%-*@3 password] . Writes the result to the output stream [%-*@3 out]. +Returns true on success. On failure the error description can +be get using [^topic`:`/`/Core`/SSL`/src`/Upp`_SSL`_AES256GCM`_en`-us`#Upp`:`:Aes256Gcm`:`:GetErrorDesc`(`)const^ G +etErrorDesc()] method.&] +[s3;%- &] +[s4;%- &] +[s5;:Upp`:`:Aes256Gcm`:`:Decrypt`(const String`&`,const String`&`,String`&`):%- [@(0.0.255) b +ool] [* Decrypt]([@(0.0.255) const] String[@(0.0.255) `&] [*@3 in], [@(0.0.255) const] +String[@(0.0.255) `&] [*@3 password], String[@(0.0.255) `&] [*@3 out])&] +[s2; Decrypts the input string [%-*@3 in]. Writes the result to [%-*@3 out]. +Returns true on success. On failure the error description can +be get using [^topic`:`/`/Core`/SSL`/src`/Upp`_SSL`_AES256GCM`_en`-us`#Upp`:`:Aes256Gcm`:`:GetErrorDesc`(`)const^ G +etErrorDesc()] method.&] +[s3;%- &] +[s4;%- &] +[s5;:Upp`:`:Aes256Gcm`:`:WhenProgress:%- Gate [* WhenProgress]&] +[s2; Optional event for progress tracking. Called with (processed, +total) values during streaming encryption/decryption. Returning +true will abort the operation.&] +[s3;%- &] +[s4;%- &] +[s5;:Upp`:`:Aes256Gcm`:`:GetErrorDesc`(`)const:%- String [* GetErrorDesc]() +[@(0.0.255) const]&] +[s2; Returns the last error message as a string or empty if no error +occured.&] +[s3;%- &] +[s0;i448;a25;kKO9;@(0.0.255)%- &] +[ {{10000F(128)G(128)@1 [s0; [* Function List]]}}&] +[s3;%- &] +[s5;:Upp`:`:AES256Encrypt`(const String`&`,const String`&`,Gate`):%- String +[* AES256Encrypt]([@(0.0.255) const] String[@(0.0.255) `&] [*@3 in], +[@(0.0.255) const] String[@(0.0.255) `&] [*@3 password], Gate WhenProgress [@(0.0.255) `=] [*@3 Null])&] +[s2;%- [%% Encrypts the input string using AES`-256`-GCM and returns +the result. Returns a void string on failure. ][*@3 password ][%% can +be empty or null. ]WhenProgress event can be used to track or +abort the process.&] +[s3;%- &] +[s4;%- &] +[s5;:Upp`:`:AES256Decrypt`(const String`&`,const String`&`,Gate`):%- String +[* AES256Decrypt]([@(0.0.255) const] String[@(0.0.255) `&] [*@3 in], +[@(0.0.255) const] String[@(0.0.255) `&] [*@3 password], Gate WhenProgress [@(0.0.255) `=] [*@3 Null])&] +[s2;%- [%% Decrypts the AES`-256`-GCM encrypted input string and returns +the result. Returns a void string on failure. ][*@3 password ][%% can +be empty or null. ]WhenProgress event can be used to track or +abort the process.&] +[s3;%- &] +[s4;%- &] +[s5;:Upp`:`:AES256Encrypt`(Stream`&`,const String`&`,Stream`&`,Gate`):%- [@(0.0.255) bo +ol] [* AES256Encrypt](Stream[@(0.0.255) `&] [*@3 in], [@(0.0.255) const] +String[@(0.0.255) `&] [*@3 password], Stream[@(0.0.255) `&] [*@3 out], +Gate WhenProgress [@(0.0.255) `=] [*@3 Null])&] +[s2;%- [%% Encrypts the input stream with AES`-256`-GCM and writes +the encrypted chunks into ][*@3 out][%% stream. Returns true on +success. ][*@3 password ][%% can be empty or null. ]WhenProgress +event can be used to track or abort the process.&] +[s3;%- &] +[s4;%- &] +[s5;:Upp`:`:AES256Decrypt`(Stream`&`,const String`&`,Stream`&`,Gate`):%- [@(0.0.255) bo +ol] [* AES256Decrypt](Stream[@(0.0.255) `&] [*@3 in], [@(0.0.255) const] +String[@(0.0.255) `&] [*@3 password], Stream[@(0.0.255) `&] [*@3 out], +Gate WhenProgress [@(0.0.255) `=] [*@3 Null])&] +[s2;%- [%% Decrypts the input stream encrypted with AES`-256`-GCM and +writes the decrypted chunks into ][*@3 out][%% stream. Returns true +on success. ][*@3 password ][%% can be empty or null. ]WhenProgress +event can be used to track or abort the process.&] +[s3;%- &] +[s0; ]] \ No newline at end of file