Protected Data
Overview
ProtectedByteBlock keeps application-lifetime secrets in an
opaque authenticated envelope.
The generic key-agreement API uses this storage for X25519 private keys and shared secrets.
Application Configuration
setProtectedDataMode() selects how ProtectedByteBlock
initializes its application-wide protection provider.
Call validateProtectedDataSupport() during startup when provider availability must become an explicit startup
failure.
Validation or the first non-empty protected block locks the mode; reset() preserves a locked mode.
The following sections describe provider behavior and lifecycle restrictions.
Protected Blocks
A protected block stores only provider-owned ciphertext and the authenticated plaintext length.
It never retains a provider pointer, key, native key reference, or registry entry.
Copying, moving, destroying, or reading its metadata therefore does not resolve the application protection service.
Use unprotect() when you need an owning sensitive byte block, or withUnprotectedData() to limit plaintext access
to one callback.
The callback storage is erased after a normal return and while an exception unwinds.
Protection is memory hardening for the lifetime of one Application.
It is not a persistent file format, a process isolation boundary, or protection from arbitrary code already executing
inside the process.
An envelope cannot be decrypted after its application is destroyed or by a later application instance.
Empty protected blocks do not require an application and never initialize a provider.
Provider Selection
ProtectedDataMode controls provider selection before the first
non-empty block or an explicit validateProtectedDataSupport() call locks the choice.
Automatic prefers the native provider on macOS and Windows, performs an authenticated round-trip self-test, and
falls back to internal AES-256-GCM only during initialization.
InternalOnly avoids native detection entirely.
PlatformOnly turns an unavailable or failing native provider into a
CryptologyError.
After lock-in, provider errors fail closed and never switch the provider behind existing envelopes.
On macOS, native protection uses an application-lifetime Secure Enclave P-256 key and Security framework ECIES/AES-GCM.
The process must have access to the required keychain facilities; otherwise Automatic uses the internal provider.
On Windows, native protection uses DPAPI-NG with a logon-local descriptor held by the application service.
The provider loads ncrypt.dll from the Windows system directory only when selected, resolves the four required entry
points explicitly, and releases the library after closing its descriptor.
Linux deliberately uses the bundled AES-256-GCM provider for Automatic and InternalOnly.
It does not probe AF_ALG, kernel keyrings, TPM services, or desktop key stores, and PlatformOnly fails.
The internal provider generates one AES-256 key per application, assigns a unique counter nonce to each envelope, and authenticates envelope metadata. Application destruction releases or deletes native references and securely erases an internally owned key.
Interface
-
class ProtectedByteBlock
An opaque byte block encrypted by the current application’s protected-data provider.
This type stores no key or provider reference. It is intended as application-lifetime memory hardening, not as a persistent encrypted format or an authorization boundary against code executing inside the process.
See: Protected Data
Public Types
-
using UnprotectedDataFn = std::function<void(mem::ConstByteSpan)>
A callback receiving a temporary unprotected byte view.
Public Functions
-
ProtectedByteBlock() noexcept = default
Create an empty protected block without accessing the application.
-
explicit ProtectedByteBlock(mem::ConstByteSpan data)
Protect a copy of the supplied bytes with the current application provider.
- Parameters:
data – The plaintext bytes to protect. An empty span creates an empty block.
- Throws:
CryptologyError – If provider initialization or encryption fails.
-
explicit ProtectedByteBlock(const mem::ByteBlock &data)
Protect an owning byte block with the current application provider.
- Parameters:
data – The plaintext bytes to protect. An empty block creates an empty protected block.
- Throws:
CryptologyError – If provider initialization or encryption fails.
-
ProtectedByteBlock(ProtectedByteBlock &&other) noexcept
Move a protected byte block.
-
ProtectedByteBlock &operator=(ProtectedByteBlock &&other) noexcept
Move a protected byte block.
-
void secureErase() noexcept
Erase this block’s encrypted envelope and restore the empty state.
-
mem::ByteBlock unprotect() const
Decrypt the block into sensitive storage.
- Throws:
CryptologyError – If the current application cannot authenticate or decrypt the envelope.
- Returns:
The authenticated plaintext, or an empty block for an empty protected block.
-
void withUnprotectedData(const UnprotectedDataFn &callback) const
Invoke a callback with an authenticated temporary plaintext view.
The temporary is securely erased before this method returns or propagates an exception.
- Parameters:
callback – The callback to invoke exactly once.
- Throws:
err::ParameterError – If the callback is empty.
CryptologyError – If the current application cannot authenticate or decrypt the envelope.
-
inline bool isEmpty() const noexcept
Test whether this protected block contains no data.
-
inline unit::ByteLength byteLength() const noexcept
Get the plaintext byte length without decrypting the block.
-
using UnprotectedDataFn = std::function<void(mem::ConstByteSpan)>
-
enum class erbsland::cryptology::ProtectedDataMode : uint8_t
Select how application-scoped protected data is encrypted.
Values:
-
enumerator Automatic
Prefer native protection and fall back to the internal provider during initialization.
-
enumerator PlatformOnly
Require the native platform provider.
-
enumerator InternalOnly
Use the bundled AES-256-GCM provider without probing native facilities.
-
enumerator Automatic