> subsystem-summary-of-crypto
read this skill for a token-efficient summary of the crypto subsystem
curl "https://skillshub.wtf/stellar/stellar-core/subsystem-summary-of-crypto?format=md"Crypto Subsystem Technical Summary
Overview
The src/crypto/ subsystem provides all cryptographic primitives for stellar-core: hashing (SHA-256, BLAKE2b, SipHash), Ed25519 key management and signatures, Curve25519 ECDH key agreement, StrKey encoding/decoding, hex utilities, and random number generation. It is built on top of libsodium and uses xdrpp for serialization. There are no background threads or event loops in this subsystem; it is a stateless utility layer with one notable piece of process-wide shared state: the signature verification cache.
Key Classes and Data Structures
ByteSlice (ByteSlice.h)
A lightweight, non-owning, read-only view over contiguous byte data. Acts as a universal adaptor for passing byte containers into crypto functions. Implicitly constructs from xdr::opaque_array<N>, xdr::msg_ptr, std::vector<uint8_t>, std::string, rust::Vec<uint8_t>, RustBuf, char const*, and raw (void*, size_t). Provides data(), size(), begin(), end(), operator[] (bounds-checked), and empty().
CryptoError (CryptoError.h)
Simple exception class inheriting std::runtime_error. Thrown by all crypto functions on failure (e.g., libsodium errors, invalid inputs).
SecretKey (SecretKey.h / SecretKey.cpp)
Represents an Ed25519 signing keypair (secret key + derived public key).
Internal state:
mKeyType(PublicKeyType) — alwaysPUBLIC_KEY_TYPE_ED25519mSecretKey(uint512/ 64-byte opaque array) — the libsodium combined secret keymPublicKey(PublicKey) — the corresponding public key (XDR type)- Nested
Seedstruct holds a 32-byte seed; its destructor zeroes memory.
Key methods:
getPublicKey()— returns const ref tomPublicKeygetStrKeySeed()— returns StrKey-encoded seed asSecretValuegetStrKeyPublic()— returns StrKey-encoded public key asstd::stringisZero()— true if seed is all-zerosign(ByteSlice)— produces a 64-byte Ed25519 detached signature viacrypto_sign_detachedrandom()— generates a cryptographically random keypair viacrypto_sign_keypairfromSeed(ByteSlice)— derives keypair from a 32-byte seed viacrypto_sign_seed_keypairfromStrKeySeed(string)— decodes a StrKey seed string, then derives keypair
Destructor zeroes mSecretKey memory to prevent key leakage.
PublicKey (XDR-defined, utilities in SecretKey.h/cpp)
The XDR union type for public keys. The crypto subsystem provides KeyFunctions<PublicKey> specialization and the PubKeyUtils namespace.
PubKeyUtils namespace (SecretKey.h / SecretKey.cpp)
Signature verification and public key utilities.
Key functions:
verifySig(PublicKey, Signature, ByteSlice)→VerifySigResult— Verifies an Ed25519 signature. Uses a process-wideRandomEvictionCache<Hash, bool>(capacity 250,000 entries) protected bygVerifySigCacheMutex. Returns both the validity result and whether it was a cache hit/miss. Supports switching between libsodium (crypto_sign_verify_detached) and Rust ed25519-dalek (rust_bridge::verify_ed25519_signature_dalek) at the protocol 24 boundary.clearVerifySigCache()— clears the global cacheseedVerifySigCache(unsigned int)— seeds the cache's random eviction PRNGflushVerifySigCacheCounts(hits, misses)— atomically reads and resets cache hit/miss countersenableRustDalekVerify()— one-way flag to switch signature verification to Rust ed25519-dalekrandom()— generates a random (non-signing-capable) public key
VerifySigResult (SecretKey.h)
Struct with two fields: bool valid (signature validity) and VerifySigCacheLookupResult cacheResult (enum: MISS, HIT, NO_LOOKUP).
Hashing Modules
SHA-256 (SHA.h / SHA.cpp)
Free functions:
sha256(ByteSlice)→uint256— one-shot SHA-256 hash viacrypto_hash_sha256subSha256(ByteSlice seed, uint64_t counter)→Hash— SHA-256 of seed concatenated with XDR-serialized counter; used for sub-seeding per-transaction PRNGs in SorobanhmacSha256(HmacSha256Key, ByteSlice)→HmacSha256Mac— HMAC-SHA-256 viacrypto_auth_hmacsha256hmacSha256Verify(HmacSha256Mac, HmacSha256Key, ByteSlice)→bool— constant-time HMAC verification viacrypto_auth_hmacsha256_verifyhkdfExtract(ByteSlice)→HmacSha256Key— unsalted HKDF-extract:HMAC(<zero_key>, bytes)hkdfExpand(HmacSha256Key, ByteSlice)→HmacSha256Key— single-step HKDF-expand:HMAC(key, bytes||0x01)
SHA256 class — incremental (streaming) SHA-256 hasher:
reset()— reinitializes stateadd(ByteSlice)— feeds datafinish()→uint256— finalizes and returns hash (single use; throws if called again)
XDRSHA256 struct — CRTP subclass of XDRHasher<XDRSHA256> wrapping SHA256. Used by xdrSha256<T>(t) template to hash any XDR object without intermediate serialization buffer.
BLAKE2b (BLAKE2.h / BLAKE2.cpp)
Free function:
blake2(ByteSlice)→uint256— one-shot BLAKE2b (256-bit output) viacrypto_generichash
BLAKE2 class — incremental BLAKE2b hasher (same API pattern as SHA256):
reset(),add(ByteSlice),finish()→uint256
XDRBLAKE2 struct — CRTP subclass of XDRHasher<XDRBLAKE2> wrapping BLAKE2. Used by xdrBlake2<T>(t) template.
BLAKE2 is used internally in the signature verification cache key computation (verifySigCacheKey hashes public key + signature + message via BLAKE2).
ShortHash / SipHash (ShortHash.h / ShortHash.cpp)
Fast, randomized, non-cryptographic hash for in-memory data structures (hash maps, etc.).
shortHash namespace:
initialize()— generates a random per-process SipHash key viacrypto_shorthash_keygen; must be called once at startupgetShortHashInitKey()→array<unsigned char, 16>— returns current key (used for child process inheritance)computeHash(ByteSlice)→uint64_t— SipHash-2-4 viacrypto_shorthash, mutex-protected to setgHaveHashedflagxdrComputeHash<T>(t)→uint64_t— hashes any XDR object without intermediate buffer, usingXDRShortHasher
XDRShortHasher struct — CRTP subclass of XDRHasher<XDRShortHasher> wrapping SipHash24 state. Initialized with the process-wide key.
Thread safety: All access to the global key gKey is mutex-protected via gKeyMutex.
XDRHasher (XDRHasher.h)
CRTP base class template XDRHasher<Derived> providing an xdrpp-compatible archiver that feeds XDR-serialized bytes to a hash function without allocating an intermediate serialization buffer.
Mechanism:
- Maintains a 256-byte internal buffer (
mBuf) for batching small writes queueOrHash(bytes, size)— buffers small writes; flushes and callsDerived::hashBytes()for larger onesflush()— sends any buffered bytes to the derived hasheroperator()overloads handle XDR scalars (32/64-bit with endian swap), byte arrays (with XDR padding), and composite types (delegating toxdr::xdr_traits<T>::save)
Three concrete derivations: XDRSHA256, XDRBLAKE2, XDRShortHasher.
Key Encoding / Decoding Modules
StrKey (StrKey.h / StrKey.cpp)
Implements Stellar's base32-encoded key format with version byte and CRC-16 checksum.
strKey namespace:
StrKeyVersionByteenum — version bytes for different key types:STRKEY_PUBKEY_ED25519('G'),STRKEY_SEED_ED25519('S'),STRKEY_PRE_AUTH_TX('T'),STRKEY_HASH_X('X'),STRKEY_SIGNED_PAYLOAD_ED25519('P'),STRKEY_MUXED_ACCOUNT_ED25519('M'),STRKEY_CONTRACT('C')toStrKey(ver, ByteSlice)→SecretValue— encodes:base32(version_byte || payload || crc16)fromStrKey(string, &ver, &decoded)→bool— decodes and validates CRC-16 checksumgetStrKeySize(dataSize)→size_t— computes encoded string length for a given payload size
KeyUtils (KeyUtils.h / KeyUtils.cpp)
Template-based key conversion utilities using the KeyFunctions<T> trait.
KeyFunctions<T> trait — specialization point for each key type, providing:
getKeyTypeName(),getKeyVersionIsSupported(),getKeyVersionIsVariableLength()toKeyType()/toKeyVersion()— convert betweenStrKeyVersionByteand the key type enumgetEd25519Value()/getKeyValue()/setKeyValue()— access raw key bytes
Specializations provided: KeyFunctions<PublicKey> (in SecretKey.h/cpp), KeyFunctions<SignerKey> (in SignerKey.h/cpp).
KeyUtils namespace template functions:
toStrKey<T>(key)— converts any key type to StrKey stringtoShortString<T>(key)— first 5 characters of StrKey (for logging)fromStrKey<T>(string)— parses StrKey string into a typed keygetKeyVersionSize(StrKeyVersionByte)— returns expected raw key size for a version bytecanConvert<T, F>(fromKey)— checks if key type conversion is possibleconvertKey<T, F>(fromKey)— converts between key types sharing the same Ed25519 value
Hex (Hex.h / Hex.cpp)
Hex encoding/decoding utilities using libsodium.
binToHex(ByteSlice)→string— hex-encodes byteshexAbbrev(ByteSlice)→string— returns first 6 hex characters (3 bytes) for logginghexToBin(string)→vector<uint8_t>— hex-decodes a stringhexToBin256(string)→uint256— hex-decodes exactly 32 bytes, throws otherwise
Signer Key Utilities
SignerKey (SignerKey.h / SignerKey.cpp)
Provides KeyFunctions<SignerKey> specialization supporting four signer key types:
SIGNER_KEY_TYPE_ED25519— standard Ed25519 public keySIGNER_KEY_TYPE_PRE_AUTH_TX— pre-authorized transaction hashSIGNER_KEY_TYPE_HASH_X— hash(x) preimage signerSIGNER_KEY_TYPE_ED25519_SIGNED_PAYLOAD— Ed25519 key with attached payload (variable length, up to 96 bytes)
SignerKeyUtils (SignerKeyUtils.h / SignerKeyUtils.cpp)
Factory functions for creating SignerKey objects:
preAuthTxKey(TransactionFrame)— creates a pre-auth signer key from a transaction's contents hashpreAuthTxKey(FeeBumpTransactionFrame)— same for fee-bump transactionshashXKey(ByteSlice)— creates a hash-x signer key by SHA-256 hashing the inputed25519PayloadKey(uint256, opaque_vec<64>)— creates an ed25519-signed-payload signer key
Curve25519 / ECDH (Curve25519.h / Curve25519.cpp)
Provides Curve25519 Diffie-Hellman key agreement for the P2P overlay authentication system (PeerAuth). These keys are distinct from Ed25519 signing keys and are generated per-session.
Key functions:
curve25519RandomSecret()→Curve25519Secret— generates random 32-byte scalar viarandombytes_bufcurve25519DerivePublic(Curve25519Secret)→Curve25519Public— derives public point viacrypto_scalarmult_baseclearCurve25519Keys(pub, sec)— zeroes both keys viasodium_memzerocurve25519DeriveSharedKey(localSecret, localPublic, remotePublic, localFirst)→HmacSha256Key— performs ECDH (crypto_scalarmult), concatenatesshared_secret || publicA || publicB(ordered bylocalFirst), then applieshkdfExtractcurve25519Encrypt<N>(remotePublic, ByteSlice)→opaque_vec<N>— sealed box encryption viacrypto_box_sealcurve25519Decrypt(localSecret, localPublic, ByteSlice)→opaque_vec<>— sealed box decryption viacrypto_box_seal_open
Random Number Generation (Random.h / Random.cpp)
randomBytes(size_t length)→vector<uint8_t>— generates cryptographically secure random bytes via libsodium'srandombytes_buf. In fuzzing builds, uses a deterministic PRNG instead.
Logging Utilities
StrKeyUtils::logKey(ostream, string) (SecretKey.cpp)
Attempts to interpret a key string in all known formats (public key StrKey, seed StrKey, raw hex) and logs all possible interpretations. Used for diagnostic/debugging output.
HashUtils namespace (SecretKey.h / SecretKey.cpp)
random()→Hash— generates a random 32-byte hash viarandombytes_buf
Thread Safety and Shared State
The crypto subsystem is mostly stateless/pure-functional. The two pieces of process-wide shared mutable state are:
-
Signature verification cache (
gVerifySigCache,gVerifyCacheHit,gVerifyCacheMiss,gUseRustDalekVerify) — protected bygVerifySigCacheMutex. The cache is aRandomEvictionCache<Hash, bool>of 250K entries. Cache keys are BLAKE2 hashes of(public_key || signature || message). Access is serialized by mutex, but individual verify operations are performed outside the lock. -
ShortHash global key (
gKey,gHaveHashed) — protected bygKeyMutex. Initialized once at startup viashortHash::initialize().
There are no background threads, event loops, or async tasks in the crypto subsystem.
Key Data Flows
Signature Creation
SecretKey::sign(message) → crypto_sign_detached(message, secret_key) → 64-byte Signature
Signature Verification
PubKeyUtils::verifySig(pubkey, sig, message) → compute BLAKE2 cache key → lock mutex → check gVerifySigCache → if miss, unlock, verify via libsodium or Rust dalek (depending on gUseRustDalekVerify flag) → lock mutex → insert result into cache → return VerifySigResult
StrKey Encoding
Raw key bytes → strKey::toStrKey(version_byte, bytes) → prepend version byte, append CRC-16 → base32-encode → StrKey string
StrKey Decoding
StrKey string → strKey::fromStrKey() → base32-decode → verify CRC-16 → extract version byte and payload → KeyUtils::fromStrKey<T>() → construct typed key
ECDH Shared Key Derivation (P2P overlay)
curve25519RandomSecret() → curve25519DerivePublic() → exchange public keys → curve25519DeriveSharedKey() → crypto_scalarmult (ECDH) → concatenate with ordered public keys → hkdfExtract() → HmacSha256Key
XDR Hashing (zero-copy)
Any XDR object → xdrSha256<T>(t) / xdrBlake2<T>(t) / shortHash::xdrComputeHash<T>(t) → xdr::archive(hasher, t) → XDRHasher::operator() dispatches by XDR type → batched hashBytes() calls → finalize
Ownership Relationships
SecretKeyowns itsmSecretKey(64-byte secret),mPublicKey(XDR PublicKey), andmKeyTypeByteSliceborrows (non-owning view) from any byte containerSHA256,BLAKE2classes own their respective libsodium hash statesXDRHasher<D>owns a 256-byte internal buffer; derived types own their hash stateXDRShortHasherowns aSipHash24state initialized from the global key- The global signature cache (
gVerifySigCache) is a process-wide singletonRandomEvictionCache<Hash, bool> - The global short-hash key (
gKey) is a process-wide singleton byte array Curve25519Secret/Curve25519Publicare value types (XDRopaque_array<32>wrappers)SignerKeyis an XDR union owned by its creator;SignerKeyUtilsfunctions are pure factories
> related_skills --same-repo
> validating-a-change
comprehensive validation of a change to ensure it is correct and ready for a pull request
> regenerating a technical summary of stellar-core
Instructions for regenerating the full set of subsystem and whole-system technical summary skill documents for stellar-core
> subsystem-summary-of-work
read this skill for a token-efficient summary of the work subsystem
> subsystem-summary-of-util
read this skill for a token-efficient summary of the util subsystem