mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 06:05:58 -06:00
Core/SSL: AES-256-GCM encryption and decryption (#266)
This commit is contained in:
parent
446d84c649
commit
218ee862ef
8 changed files with 697 additions and 1 deletions
10
autotest/AES256/AES256.upp
Normal file
10
autotest/AES256/AES256.upp
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
uses
|
||||
Core,
|
||||
Core/SSL;
|
||||
|
||||
file
|
||||
main.cpp;
|
||||
|
||||
mainconfig
|
||||
"" = "";
|
||||
|
||||
85
autotest/AES256/main.cpp
Normal file
85
autotest/AES256/main.cpp
Normal 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");
|
||||
}
|
||||
12
reference/AESEncryption/AESEncryption.upp
Normal file
12
reference/AESEncryption/AESEncryption.upp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
description "Demonstrates AES-256-GCM encryption and decryption.\377";
|
||||
|
||||
uses
|
||||
Core,
|
||||
Core/SSL;
|
||||
|
||||
file
|
||||
main.cpp;
|
||||
|
||||
mainconfig
|
||||
"" = "";
|
||||
|
||||
40
reference/AESEncryption/main.cpp
Normal file
40
reference/AESEncryption/main.cpp
Normal 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
333
uppsrc/Core/SSL/AES.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,5 +12,8 @@ file
|
|||
InitExit.cpp,
|
||||
Socket.cpp,
|
||||
P7S.cpp,
|
||||
SSL.icpp;
|
||||
AES.cpp,
|
||||
SSL.icpp,
|
||||
Docs readonly separator,
|
||||
src.tpp;
|
||||
|
||||
|
|
|
|||
176
uppsrc/Core/SSL/src.tpp/Upp_SSL_AES256GCM_en-us.tpp
Normal file
176
uppsrc/Core/SSL/src.tpp/Upp_SSL_AES256GCM_en-us.tpp
Normal 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; ]]
|
||||
Loading…
Add table
Add a link
Reference in a new issue