mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
Core/SSL: Added SecureBuffer and SecureZero (#272)
This commit is contained in:
parent
6a87189ef6
commit
7fb2d67521
6 changed files with 459 additions and 0 deletions
148
autotest/SecureBuffer/SecureBuffer.cpp
Normal file
148
autotest/SecureBuffer/SecureBuffer.cpp
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#include <Core/Core.h>
|
||||
#include <Core/SSL/SSL.h>
|
||||
|
||||
using namespace Upp;
|
||||
|
||||
template<typename T>
|
||||
bool IsZeroed(const T* ptr, size_t count)
|
||||
{
|
||||
auto bytes = reinterpret_cast<const byte*>(ptr);
|
||||
for(size_t i = 0; i < count * sizeof(T); ++i) {
|
||||
if(bytes[i] != 0) {
|
||||
LOG(Format("Memory not zeroed at offset %d: found value 0x%02X", (int) i, (int) bytes[i]));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CONSOLE_APP_MAIN
|
||||
{
|
||||
StdLogSetup(LOG_COUT | LOG_FILE);
|
||||
|
||||
auto Test = [](const String& name, const Function<void()>& fn) {
|
||||
String txt = "---" + name + ": ";
|
||||
fn();
|
||||
LOG(txt << "PASSED");
|
||||
};
|
||||
|
||||
Test("SecureZero: Basic integer array", [&]{
|
||||
int buffer[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||
SecureZero(buffer);
|
||||
ASSERT(IsZeroed(buffer, 10));
|
||||
});
|
||||
|
||||
Test("SecureZero: Empty array (edge case)", [&]{
|
||||
int buffer[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||
SecureZero(buffer, 0);
|
||||
ASSERT(buffer[0] == 1 && buffer[9] == 10); // Should not change anything
|
||||
});
|
||||
|
||||
Test("SecureZero: NULL pointer (edge case)", [&]{
|
||||
// This SHOULD NOT crash
|
||||
SecureZero<int>(nullptr, 10);
|
||||
});
|
||||
|
||||
Test("SecureZero: Byte-by-byte zeroing", [&]{
|
||||
byte buffer[7] = {1, 2, 3, 4, 5, 6, 7}; // Odd size to test boundary cases
|
||||
SecureZero(buffer, 7);
|
||||
ASSERT(IsZeroed(buffer, 7));
|
||||
});
|
||||
|
||||
Test("SecureZero: Complex types", [&]{
|
||||
struct TestStruct {
|
||||
int a;
|
||||
double b;
|
||||
char c[10];
|
||||
} buffer[5];
|
||||
|
||||
for(int i = 0; i < 5; ++i) {
|
||||
buffer[i].a = i;
|
||||
buffer[i].b = i * 3.14;
|
||||
memset(buffer[i].c, 'A' + i, 10);
|
||||
}
|
||||
|
||||
SecureZero(buffer, 5);
|
||||
ASSERT(IsZeroed(buffer, 5));
|
||||
});
|
||||
|
||||
Test("SecureZero: Partial array zeroing", [&]{
|
||||
int buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
SecureZero(buffer + 3, 4); // Zero elements 3, 4, 5, 6
|
||||
ASSERT(buffer[0] == 1);
|
||||
ASSERT(buffer[1] == 2);
|
||||
ASSERT(buffer[2] == 3);
|
||||
ASSERT(buffer[3] == 0);
|
||||
ASSERT(buffer[4] == 0);
|
||||
ASSERT(buffer[5] == 0);
|
||||
ASSERT(buffer[6] == 0);
|
||||
ASSERT(buffer[7] == 8);
|
||||
ASSERT(buffer[8] == 9);
|
||||
ASSERT(buffer[9] == 10);
|
||||
});
|
||||
|
||||
Test("SecureZero: Resistance to compiler optimization", [&]{
|
||||
const int BUFSIZE = 1024;
|
||||
Buffer<byte> buffer(BUFSIZE, 0xFF);
|
||||
SecureZero(~buffer, BUFSIZE);
|
||||
volatile byte checksum = 0;
|
||||
for(int i = 0 ; i < BUFSIZE; i++)
|
||||
checksum ^= buffer[i];
|
||||
ASSERT(checksum == 0);
|
||||
});
|
||||
|
||||
Test("SecureZero: Overlapping memory regions", [&] {
|
||||
byte buffer[16];
|
||||
memset(buffer, 0xAB, 16);
|
||||
SecureZero(buffer + 4, 8); // Zero middle 8 bytes
|
||||
for(int i = 0; i < 4; ++i) ASSERT(buffer[i] == 0xAB);
|
||||
for(int i = 4; i < 12; ++i) ASSERT(buffer[i] == 0x00);
|
||||
for(int i = 12; i < 16; ++i) ASSERT(buffer[i] == 0xAB);
|
||||
});
|
||||
|
||||
Test("SecureBuffer: Basic functionality", [&]{
|
||||
SecureBuffer<int> buffer(10);
|
||||
ASSERT(buffer.GetSize() == 10);
|
||||
buffer[0] = 42;
|
||||
ASSERT(buffer[0] == 42);
|
||||
buffer.Clear();
|
||||
ASSERT(buffer.GetSize() == 0);
|
||||
});
|
||||
|
||||
Test("SecureBuffer: Pick semantics", [&]{
|
||||
SecureBuffer<int> buffer1(5);
|
||||
buffer1[0] = 123;
|
||||
SecureBuffer<int> buffer2 = pick(buffer1);
|
||||
ASSERT(buffer2.GetSize() == 5);
|
||||
ASSERT(buffer2[0] == 123);
|
||||
ASSERT(buffer1.GetSize() == 0); // NOLINT
|
||||
});
|
||||
|
||||
Test("SecureBuffer: Zeroing verification (security critical)", [&]{
|
||||
struct TestStruct {
|
||||
int a = 0xDEADBEEF;
|
||||
char b[8] = "TEST";
|
||||
};
|
||||
SecureBuffer<TestStruct> buffer(1);
|
||||
buffer[0].a = 0xCAFEBABE;
|
||||
strcpy(buffer[0].b, "SECRET");
|
||||
TestStruct *ptr = ~buffer;
|
||||
buffer.Zero();
|
||||
ASSERT(IsZeroed(ptr, buffer.GetSize()));
|
||||
});
|
||||
|
||||
Test("SecureBuffer: Multiple clear calls", [&]{
|
||||
SecureBuffer<int> buffer(4);
|
||||
buffer[0] = 123;
|
||||
buffer.Clear();
|
||||
buffer.Clear(); // Should be safe
|
||||
ASSERT(buffer.GetSize() == 0);
|
||||
});
|
||||
|
||||
Test("SecureBuffer: Edge case", [&]{
|
||||
SecureBuffer<char> buffer(0);
|
||||
ASSERT(buffer.GetSize() == 0);
|
||||
SecureBuffer<double> largebuffer(10000);
|
||||
ASSERT(largebuffer.GetSize() == 10000);
|
||||
});
|
||||
}
|
||||
12
autotest/SecureBuffer/SecureBuffer.upp
Normal file
12
autotest/SecureBuffer/SecureBuffer.upp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
description "SecureBuffer unit tests\377";
|
||||
|
||||
uses
|
||||
Core,
|
||||
Core/SSL;
|
||||
|
||||
file
|
||||
SecureBuffer.cpp;
|
||||
|
||||
mainconfig
|
||||
"" = "";
|
||||
|
||||
163
uppsrc/Core/SSL/Buffer.hpp
Normal file
163
uppsrc/Core/SSL/Buffer.hpp
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
#ifndef _Core_Ssl_SecureBuffer_h_
|
||||
#define _Core_Ssl_SecureBuffer_h_
|
||||
|
||||
#ifdef PLATFORM_POSIX
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#ifndef LLOG
|
||||
#define LLOG(x) // RLOG(x)
|
||||
|
||||
template <class T>
|
||||
void SecureZero(T* ptr, size_t count)
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>
|
||||
&& std::is_trivially_destructible_v<T>,
|
||||
"Upp::SecureZero: T must be trivially copyable & destructible");
|
||||
|
||||
if(!ptr || count == 0)
|
||||
return;
|
||||
|
||||
OPENSSL_cleanse(reinterpret_cast<void*>(ptr), count * sizeof(T));
|
||||
}
|
||||
|
||||
template<class T, size_t N>
|
||||
void SecureZero(T (&obj)[N]) // Safe overload for static arrays.
|
||||
{
|
||||
SecureZero(obj, N);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
class SecureBuffer : Moveable<SecureBuffer<T>>, NoCopy {
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<T>
|
||||
&& std::is_trivially_destructible_v<T>,
|
||||
"Upp::SecureBuffer: T must be trivially copyable & destructible");
|
||||
|
||||
public:
|
||||
SecureBuffer() { size = 0; ptr = nullptr; }
|
||||
SecureBuffer(size_t size_) { New(size_); }
|
||||
SecureBuffer(SecureBuffer&& src) { Pick(pick(src)); }
|
||||
~SecureBuffer() { Free(); }
|
||||
|
||||
SecureBuffer& operator=(SecureBuffer&& src) { Pick(pick(src)); return *this; }
|
||||
|
||||
void Alloc(size_t size) { Free(); New(size); }
|
||||
void Clear() { Free(); }
|
||||
void Zero() { SecureZero(ptr, size); };
|
||||
|
||||
size_t GetSize() const { return size; }
|
||||
|
||||
bool IsEmpty() const { return ptr == nullptr; }
|
||||
|
||||
operator T*() { return ptr; }
|
||||
operator const T*() const { return ptr; }
|
||||
T *operator~() { return ptr; }
|
||||
const T *operator~() const { return ptr; }
|
||||
T *Get() { return ptr; }
|
||||
const T *Get() const { return ptr; }
|
||||
T *begin() { return ptr; }
|
||||
const T *begin() const { return ptr; }
|
||||
|
||||
T* Begin() { return ptr; }
|
||||
const T* Begin() const { return ptr; }
|
||||
|
||||
|
||||
T* end() { return ptr + size; }
|
||||
const T* end() const { return ptr + size; }
|
||||
T* End() { return ptr + size; }
|
||||
const T* End() const { return ptr + size; }
|
||||
|
||||
|
||||
T& operator[](size_t i) { ASSERT(i < size); return ptr[i]; }
|
||||
const T& operator[](size_t i) const { ASSERT(i < size); return ptr[i]; }
|
||||
|
||||
|
||||
private:
|
||||
void New(size_t sz);
|
||||
void Free();
|
||||
void Pick(SecureBuffer&& src);
|
||||
void LockMemory();
|
||||
void UnlockMemory();
|
||||
|
||||
T* ptr = nullptr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void SecureBuffer<T>::New(size_t sz)
|
||||
{
|
||||
size = 0;
|
||||
ptr = nullptr;
|
||||
|
||||
if(sz > 0) {
|
||||
size = sz;
|
||||
ptr = (T*) MemoryAlloc(size * sizeof(T));
|
||||
LockMemory();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SecureBuffer<T>::Pick(SecureBuffer&& src)
|
||||
{
|
||||
if(this != &src) {
|
||||
Free();
|
||||
ptr = src.ptr;
|
||||
size = src.size;
|
||||
src.ptr = nullptr;
|
||||
src.size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SecureBuffer<T>::Free()
|
||||
{
|
||||
if(ptr) {
|
||||
Zero();
|
||||
UnlockMemory();
|
||||
MemoryFree(ptr);
|
||||
ptr = nullptr;
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SecureBuffer<T>::LockMemory()
|
||||
{
|
||||
if(!ptr || !size)
|
||||
return;
|
||||
#if defined(PLATFORM_WIN32)
|
||||
if(!VirtualLock((LPVOID) ptr, size * sizeof(T))) {
|
||||
LLOG("SecureBuffer::LockMemory: VirtualLock failed with error code " << GetLastError());
|
||||
}
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
if(mlock((void*) ptr, size * sizeof(T)) != 0) {
|
||||
LLOG("SecureBuffer::LockMemory: mlock failed with errno " << errno << " (" << strerror(errno) << ")");
|
||||
}
|
||||
#else
|
||||
NEVER();
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SecureBuffer<T>::UnlockMemory()
|
||||
{
|
||||
if(!ptr || !size)
|
||||
return;
|
||||
#if defined(PLATFORM_WIN32)
|
||||
if(!VirtualUnlock((LPVOID) ptr, size * sizeof(T))) {
|
||||
LLOG("SecureBuffer::UnlockMemory: VirtualUnlock failed with error code " << GetLastError());
|
||||
}
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
if(munlock((void*) ptr, size * sizeof(T)) != 0) {
|
||||
LLOG("SecureBuffer::UnlockMemory: munlock failed with errno " << errno << " (" << strerror(errno) << ")");
|
||||
}
|
||||
#else
|
||||
NEVER();
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef LLOG
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -180,4 +180,7 @@ String AES256Decrypt(const String& in, const String& password, Gate<int64, int64
|
|||
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);
|
||||
|
||||
// Secure buffer
|
||||
#include "Buffer.hpp"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ file
|
|||
Socket.cpp,
|
||||
P7S.cpp,
|
||||
AES.cpp,
|
||||
Buffer.hpp,
|
||||
SSL.icpp,
|
||||
Docs readonly separator,
|
||||
src.tpp;
|
||||
|
|
|
|||
132
uppsrc/Core/SSL/src.tpp/Upp_SSL_SecureBuffer_en-us.tpp
Normal file
132
uppsrc/Core/SSL/src.tpp/Upp_SSL_SecureBuffer_en-us.tpp
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
topic "SecureBuffer";
|
||||
[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]
|
||||
[{_}
|
||||
[ {{10000@(113.42.0) [s0;%% [*@7;4 SecureBuffer]]}}&]
|
||||
[s3; &]
|
||||
[s1;:noref: [@(0.0.255)3 template][3 ]<[@(0.0.255) class]_[*@4 T][@(0.0.255) >]&]
|
||||
[s1;:Upp`:`:SecureBuffer: [@(0.0.255) class ][* SecureBuffer ][*@(0.0.255) :][*
|
||||
][*@3 Moveable][*@(0.0.255) <][* _SecureBuffer][*@(0.0.255) <][*@4 T][*@(0.0.255) >][* _>_,
|
||||
NoCopy]&]
|
||||
[s6; [@4 T] must be trivially copyable and destructible.&]
|
||||
[s0;l288;%% A dynamic buffer designed for storing sensitive cryptographic
|
||||
data such as private keys, symmetric keys, passwords, certificates,
|
||||
nonces, and other security`-critical material. SecureBuffer attempts
|
||||
to lock its memory region in RAM (using mlock/VirtualLock) to
|
||||
prevent it from being swapped to disk. This locking is best`-effort
|
||||
and [/ may ]fail on systems with limited permissions or resource
|
||||
limits. Regardless of locking success, the buffer is securely
|
||||
zeroed before deallocation to reduce the risk of sensitive data
|
||||
lingering in memory. SecureBuffer is [/ not ]thread safe. External
|
||||
synchronization is required for concurrent access.&]
|
||||
[s3; &]
|
||||
[ {{10000F(128)G(128)@1 [s0;%% [* Constructor detail]]}}&]
|
||||
[s3; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:SecureBuffer`(`): [* SecureBuffer]()&]
|
||||
[s2;%% Default constructor. Creates an empty secure buffer.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:SecureBuffer`(size`_t`): [* SecureBuffer](size`_t
|
||||
[*@3 size])&]
|
||||
[s2;%% Constructs a buffer of given [%-*@3 size], allocates memory
|
||||
and locks it.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:SecureBuffer`(SecureBuffer`&`&`): [* SecureBuffer]([* SecureB
|
||||
uffer][@(0.0.255) `&`&] [*@3 src])&]
|
||||
[s2;%% Pick constructor. Destroys source container [%-*@3 src].&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:`~SecureBuffer`(`): [* `~SecureBuffer]()&]
|
||||
[s2;%% Destructor. Frees memory, securely zeroes the content, and
|
||||
unlocks it from physical memory.&]
|
||||
[s3; &]
|
||||
[ {{10000F(128)G(128)@1 [s0;%% [* Public Method List]]}}&]
|
||||
[s3; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:operator`=`(SecureBuffer`&`&`): SecureBuffer[@(0.0.255) `&]
|
||||
operator[@(0.0.255) `=]([* SecureBuffer][@(0.0.255) `&`&] [*@3 src])&]
|
||||
[s2;%% Pick operator. Destroys source buffer [%-*@3 src].&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:Alloc`(size`_t`): [@(0.0.255) void] [* Alloc](size`_t
|
||||
[*@3 size])&]
|
||||
[s2;%% Clears existing buffer and allocates a new one of specified
|
||||
[%-*@3 size].&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:Clear`(`): [@(0.0.255) void] [* Clear]()&]
|
||||
[s2;%% Releases the buffer and securely zeroes its contents.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:Zero`(`): [@(0.0.255) void] [* Zero]()&]
|
||||
[s2;%% Explicitly zeroes the contents of the buffer in a secure way.
|
||||
Doesn`'t release or destroy the buffer.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:GetSize`(`)const: size`_t [* GetSize]()
|
||||
[@(0.0.255) const]&]
|
||||
[s2;%% Returns the size of buffer. Return 0 if the buffer is empty.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:IsEmpty`(`)const: [@(0.0.255) bool] [* IsEmpty]()
|
||||
[@(0.0.255) const]&]
|
||||
[s2;%% Returns true if the buffer is empty.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:operator T`*`(`): operator T [@(0.0.255) `*]()&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:operator const T`*`(`)const: operator
|
||||
[@(0.0.255) const] T [@(0.0.255) `*]() [@(0.0.255) const]&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:operator`~`(`): T [@(0.0.255) `*]operator[@(0.0.255) `~]()&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:operator`~`(`)const: [@(0.0.255) const]
|
||||
T [@(0.0.255) `*]operator[@(0.0.255) `~]() [@(0.0.255) const]&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:Get`(`): T [@(0.0.255) `*][* Get]()&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:Get`(`)const: [@(0.0.255) const] T [@(0.0.255) `*][* Get]()
|
||||
[@(0.0.255) const]&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:begin`(`): T [@(0.0.255) `*][* begin]()&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:begin`(`)const: [@(0.0.255) const] T [@(0.0.255) `*][* begin]()
|
||||
[@(0.0.255) const]&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:Begin`(`): T [@(0.0.255) `*][* Begin]()&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:Begin`(`)const: [@(0.0.255) const] T [@(0.0.255) `*][* Begin]()
|
||||
[@(0.0.255) const]&]
|
||||
[s2;%% Returns a pointer to the first element of the buffer or [@(0.0.255) nullptr
|
||||
]if the buffer is empty.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:end`(`): T [@(0.0.255) `*][* end]()&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:end`(`)const: [@(0.0.255) const] T [@(0.0.255) `*][* end]()
|
||||
[@(0.0.255) const]&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:End`(`): T [@(0.0.255) `*][* End]()&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:End`(`)const: [@(0.0.255) const] T [@(0.0.255) `*][* End]()
|
||||
[@(0.0.255) const]&]
|
||||
[s2;%% Returns a pointer to the last element of the buffer or [@(0.0.255) nullptr
|
||||
]if the buffer is empty.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:operator`[`]`(size`_t`): T[@(0.0.255) `&]
|
||||
operator[@(0.0.255) `[`]](size`_t i)&]
|
||||
[s5;:Upp`:`:SecureBuffer`:`:operator`[`]`(size`_t`)const: [@(0.0.255) const]
|
||||
T[@(0.0.255) `&] operator[@(0.0.255) `[`]](size`_t i) [@(0.0.255) const]&]
|
||||
[s2;%% Provides indexed access to elements. Checks bounds (and asserts)
|
||||
in DEBUG mode.&]
|
||||
[s3; &]
|
||||
[ {{10000F(128)G(128)@1 [s0;%% [* Function List]]}}&]
|
||||
[s3; &]
|
||||
[s5;:Upp`:`:SecureZero`(T`*`,size`_t`): [@(0.0.255) template] <[@(0.0.255) class]
|
||||
T>&]
|
||||
[s5;:Upp`:`:SecureZero`(T`*`,size`_t`): [@(0.0.255) void] [* SecureZero](T
|
||||
[@(0.0.255) `*][*@3 ptr], size`_t [*@3 count])&]
|
||||
[s0;:Upp`:`:SecureZero`(T`& obj`): [@(0.0.255) void] [* SecureZero](T[@(0.0.255) `&]
|
||||
[*@3 obj])&]
|
||||
[s6; [@4 T] must be trivially copyable and destructible.&]
|
||||
[s2;%% A secure memory zeroing function used internally by SecureBuffer.
|
||||
This function overwrites memory contents in a way that is [/ not]
|
||||
optimized away by the compiler.&]
|
||||
[s0;%% ]]
|
||||
Loading…
Add table
Add a link
Reference in a new issue