| Home | API Documentation |
Internal: Mug · ThorsMug · ThorsSlack · NisseServer · NisseHTTP · ThorsSocket · ThorsCrypto · ThorsSerializer · ThorsMongo · ThorsLogging · ThorsIOUtil
Detailed implementation notes for the ThorsCrypto library.
Source: third/ThorsCrypto/src/ThorsCrypto/
| File | Purpose |
|---|---|
hash.h |
Platform-native hash wrappers (CommonCrypto / OpenSSL) |
md5.h |
Pure C++ MD5 implementation (RFC 1321) |
base64.h |
Iterator-based Base64 encoding/decoding |
crc.h |
CRC32/CRC32C with precomputed lookup tables |
hmac.h |
HMAC (RFC 2104) |
pbkdf2.h |
PBKDF2 (RFC 2898) |
scram.h |
SCRAM authentication (RFC 5801) |
The library’s generic design allows core primitives to be composed:
Hash Algorithms: Md5, Sha1, Sha256
|
HMac<THash> -- HMAC built on any hash
|
Pbkdf2<HMac<THash>> -- PBKDF2 built on any HMAC
|
ScramClient / ScramServer -- SCRAM built on all three
Each layer conforms to the same structural interface (digestSize, DigestStore, static hash() method).
Fixed-size byte array holding a hash digest result:
template<std::size_t size>
class DigestStoreBase;
| Member | Description |
|---|---|
operator DigestPtr() |
Implicit conversion to raw Byte* for C APIs |
std::string_view view() |
Returns digest contents as string_view |
Byte& operator[](std::size_t i) |
Element access (wraps around via i % size) |
begin() / end() |
Iterator access |
On macOS, hash functions use CommonCrypto (CC_MD5, CC_SHA1, CC_SHA256). On other platforms, they use OpenSSL (MD5(), SHA1(), SHA256()).
Converts each byte to two lowercase hex characters using a std::stringstream with std::hex and std::setw(2).
struct Hash : std::array<std::uint32_t, 4>;
128-bit MD5 result stored as four 32-bit words. The hex string constructor parses 8 characters per word.
The MD5 class maintains:
m_state: four 32-bit accumulator words (A, B, C, D), initialized to the MD5 constantsm_buffer: internal buffer for partial blocks (64 bytes)m_count: total bits processedProcessing steps:
add() appends data to the internal buffer, processing complete 64-byte blocks via processBlock().hash() pads the message (1-bit, zeros, 64-bit length), processes final blocks, and returns the result.processBlock() implements the four rounds of MD5 transformation (F, G, H, I functions) with the standard sine-derived constants.The implementation is non-copyable. Calling add() after hash() throws std::runtime_error.
Maintains internal state:
nextByte: current decoded bytebitsLeft: number of valid bits remaining in the bufferatEnd: whether the underlying iterator has reached endReads 4 Base64 characters to produce 3 decoded bytes. Handles = padding. Throws std::runtime_error on invalid input.
Equality comparison: the iterator is “at end” only when the underlying iterator has reached end and all buffered bits are consumed.
Maintains internal state:
nextByte: current encoded characterbitsLeft: number of valid bits remainingatEnd: end of input detectedReads 1 byte at a time and outputs 6-bit encoded characters. Inserts = padding at the end of the stream.
CRC32 and CRC32C each contain a static constexpr std::uint32_t table[256] computed at compile time using the respective polynomials.
Maintains a running CRC value initialized to 0xFFFFFFFF. Each append() call XORs each byte with the low byte of the accumulator and looks up the next value in the table. checksum() returns the XOR-finalized result.
RAII builder. The constructor:
appendData() appends message data.
The destructor:
digest reference.Implements the PBKDF2 algorithm:
U_1 = HMAC(password, salt || INT_32_BE(1))U_i = HMAC(password, U_{i-1})DK = U_1 XOR U_2 XOR ... XOR U_iterThe salt is concatenated with a 4-byte big-endian block index (the S template parameter).
getFirstMessage(): Generates n,,n=<user>,r=<clientNonce>getFinalMessage(serverFirst): Parses server’s r=, s=, i= values. Computes:
SaltedPassword = Hi(password, salt, iterations)ClientKey = HMAC(SaltedPassword, "Client Key")StoredKey = H(ClientKey)AuthMessage = clientFirstBare,serverFirst,clientFinalWithoutProofClientSignature = HMAC(StoredKey, AuthMessage)ClientProof = ClientKey XOR ClientSignaturevalidateServer(serverFinal): Verifies v=<serverSignature> matches the expected value.getFirstMessage(clientFirst): Extracts username, looks up DBInfo, generates server nonce, returns r=<combinedNonce>,s=<salt>,i=<iterations>getFinalMessage(clientFinal): Verifies the client proof, returns {true, "v=<serverSignature>"} on success.Core proof calculation used by both client and server:
ClientKey, StoredKey, ServerKey from the salted password.AuthMessage from the three SCRAM messages.ClientSignature = HMAC(StoredKey, AuthMessage).ClientProof = ClientKey XOR ClientSignature.ServerSignature = HMAC(ServerKey, AuthMessage).The ThorsAnvil::Crypto::V1 namespace contains the original SCRAM API where:
ScramServer takes clientFirstMessage in the constructorDBInfoThe new API separates concerns: ScramMechanism pre-computes DBInfo, and ScramServer takes a callback returning DBInfo.