Core/SSL: AES-256-GCM encryption and decryption (#266)

This commit is contained in:
İsmail Yılmaz 2025-05-05 15:31:58 +03:00 committed by GitHub
parent 446d84c649
commit 218ee862ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 697 additions and 1 deletions

View file

@ -0,0 +1,10 @@
uses
Core,
Core/SSL;
file
main.cpp;
mainconfig
"" = "";

85
autotest/AES256/main.cpp Normal file
View file

@ -0,0 +1,85 @@
#include <Core/Core.h>
#include <Core/SSL/SSL.h>
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");
}

View file

@ -0,0 +1,12 @@
description "Demonstrates AES-256-GCM encryption and decryption.\377";
uses
Core,
Core/SSL;
file
main.cpp;
mainconfig
"" = "";

View file

@ -0,0 +1,40 @@
#include <Core/Core.h>
#include <Core/SSL/SSL.h>
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.");
}

333
uppsrc/Core/SSL/AES.cpp Normal file
View file

@ -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<byte> 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<byte> 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<int64, int64> 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<int64, int64> 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<int64, int64> 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<int64, int64> WhenProgress)
{
// Use sane defaults
Aes256Gcm aes;
aes.WhenProgress = WhenProgress;
return aes.Decrypt(in, password, out);
}
}

View file

@ -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<int64, int64> 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<int64, int64> WhenProgress = Null);
String AES256Decrypt(const String& in, const String& password, Gate<int64, int64> WhenProgress = Null);
bool AES256Encrypt(Stream& in, const String& password, Stream& out, Gate<int64, int64> WhenProgress = Null);
bool AES256Decrypt(Stream& in, const String& password, Stream& out, Gate<int64, int64> WhenProgress = Null);
}

View file

@ -12,5 +12,8 @@ file
InitExit.cpp,
Socket.cpp,
P7S.cpp,
SSL.icpp;
AES.cpp,
SSL.icpp,
Docs readonly separator,
src.tpp;

View file

@ -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<int64, int64> [* 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<int64,
int64> 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<int64,
int64> 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<int64, int64> 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<int64, int64> 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; ]]