Cryptography at the Machine Level
Cryptography is often perceived as a discipline dominated by mathematical abstraction and high-level formalism. In practice, however, the security and correctness of cryptographic systems ultimately depend on the exact machine instructions that execute them.
This volume, Low-Level Cryptography, focuses on the level where cryptography becomes concrete:
- CPU registers
- Status flags
- Memory alignment
- Instruction latency
- Bitwise transformations
- Register dependencies
- Deterministic state evolution
- Memory-access patterns
At this level, every instruction, branch, memory reference, and state transition can influence both correctness and security.
Objective of This Book
The primary objective of this book is to demonstrate, step by step, how modern symmetric cryptographic primitives can be implemented directly in x86-64 assembly on Linux.
All examples use:
- GNU Assembler (GAS)
- Intel syntax
- Linux x86-64
- Raw system-level execution
- Explicit register and memory management
The implementations do not rely on:
- Compilers
- External cryptographic libraries
- The C standard library
- Language runtimes
- Compiler intrinsics
- Dynamic memory allocation
- Abstractions that hide execution behavior
Every instruction is explicit, analyzable, and under direct programmer control.
Security Beyond Algorithmic Correctness
Modern cryptographic correctness cannot be separated from secure execution behavior.
A mathematically correct algorithm may still be vulnerable if its implementation exposes secrets through:
- Secret-dependent branches
- Data-indexed memory access
- Variable execution time
- Cache-access patterns
- Speculative execution
- Uncontrolled control flow
- Compiler-generated transformations
- Incomplete memory cleanup
For this reason, the material reflects post-2020 secure-software principles, emphasizing:
- Constant-time execution
- Predictable control flow
- Fixed memory-access patterns
- Strict register management
- Controlled stack usage
- Explicit state cleanup
- Microarchitectural awareness
Each implementation avoids secret-dependent branching, uncontrolled memory indexing, speculative side effects, and other behavior that may create side-channel leakage.
GNU Assembler Conventions
All assembly listings follow GNU Assembler conventions while using Intel syntax.
The examples consistently use:
- Intel operand order
- GAS directives
#for comments- System V AMD64 ABI rules
- Linux-compatible object and executable formats
- Explicit operand sizes
- Correct stack alignment
- Predictable register semantics
This ensures compatibility with modern Linux toolchains while maintaining the readability of Intel-style assembly.
Cryptographic Primitives Covered
The book examines three important cryptographic constructions:
- SHA-1
- HMAC-SHA1
- ChaCha20
These primitives were selected not only for their practical or historical significance, but also because their internal structures are well suited to instruction-level analysis.
Their designs allow the reader to observe how cryptographic transformations are implemented directly within a modern CPU.
ARX Operations
Many of the examined operations are based on the ARX model:
- Add
- Rotate
- XOR
ARX constructions are especially suitable for low-level implementation because they map naturally to common processor instructions.
Their behavior can be analyzed directly through:
- Integer arithmetic
- Bitwise logic
- Register rotations
- Data dependencies
- Instruction scheduling
- Pipeline behavior
The ChaCha20 quarter-round provides a clear example of how addition, rotation, and XOR operations produce strong diffusion using instructions efficiently supported by x86-64 processors.
Explicit Cryptographic State
Throughout the book, cryptographic state is represented explicitly using:
- Register-mapped variables
- 32-bit words
- Static buffers
- Stack-allocated buffers
- Manually aligned memory
- Explicit counters and state indices
The programmer retains direct control over where each value resides and how it moves through the system.
No hidden runtime determines:
- Register allocation
- Stack layout
- Temporary storage
- Buffer alignment
- Data lifetime
- Memory cleanup
This transparency makes it possible to follow every transformation instruction by instruction.
Endianness and Data Serialization
Cryptographic algorithms frequently define data in a specific byte order.
This book handles endianness manually through register-level operations, ensuring that every:
- Message block
- Digest word
- Internal state word
- Counter value
- Authentication value
- Keystream word
matches the algorithmic specification exactly.
The reader learns how to:
- Load words from byte arrays
- Convert between little-endian and big-endian formats
- Serialize internal state
- Store digests correctly
- Construct message blocks manually
- Validate byte-level output
Endianness is never delegated to an opaque helper function.
Transparent Algorithm Implementation
Every major cryptographic stage is implemented as a visible assembly routine.
These stages include:
- Message preparation
- Padding
- Length encoding
- Message scheduling
- State initialization
- State expansion
- Compression rounds
- Feed-forward operations
- Key processing
- Inner and outer hashing
- Keystream generation
- Counter advancement
- Digest serialization
- Constant-time verification
- Secure state cleanup
Each stage can be traced directly from the algorithmic definition to the instructions executed by the processor.
SHA-1
SHA-1 provides a clear example of structured bit diffusion through:
- XOR operations
- Rotations
- Modular addition
- Boolean functions
- Message-schedule expansion
- Repeated compression rounds
- Feed-forward state updates
The book examines how the SHA-1 state is represented using five 32-bit working variables and how each compression round transforms that state.
The implementation demonstrates:
- Message-block loading
- Big-endian conversion
- Schedule expansion
- Round-dependent Boolean functions
- Round constants
- Register rotation
- State accumulation
- Digest output formatting
Although SHA-1 is no longer recommended for collision-resistant digital signatures or new security designs, its structure remains valuable for studying low-level hash implementation and HMAC construction.
HMAC-SHA1
HMAC demonstrates how a cryptographic hash function can be transformed into a keyed message-authentication mechanism.
The implementation covers:
- Key normalization
- Block-sized key preparation
- Inner padding
- Outer padding
- Inner-hash calculation
- Outer-hash calculation
- Message-length handling
- Authentication-tag generation
- Constant-time tag comparison
The book emphasizes the importance of preserving:
- Domain separation
- Key isolation
- Correct padding
- Message-length binding
- Exact serialization
- Constant-time verification
When HMAC is implemented manually, even small mistakes in buffer construction, length calculation, or comparison logic can compromise correctness or security.
ChaCha20
ChaCha20 demonstrates the efficiency of ARX-based stream ciphers on modern processors.
Its core transformation uses:
- 32-bit modular addition
- XOR
- Fixed-count rotations
- Repeated quarter-round operations
The implementation examines:
- State initialization
- Constant words
- Key loading
- Counter placement
- Nonce placement
- Column rounds
- Diagonal rounds
- Feed-forward addition
- Keystream serialization
- Counter advancement
- Data encryption and decryption
Because encryption and decryption both use XOR with the generated keystream, the same low-level routine can support both operations.
The ChaCha20 quarter-round also provides a practical case study in:
- Register allocation
- Instruction dependencies
- Pipeline utilization
- Rotation efficiency
- Temporary-state management
Microarchitectural Awareness
A low-level cryptographic programmer must understand more than the mathematical steps of an algorithm.
The programmer must also understand how those steps interact with:
- Instruction pipelines
- Register dependencies
- Execution ports
- Memory alignment
- Cache behavior
- Branch prediction
- Speculative execution
- Instruction latency
- Instruction throughput
- Store-to-load behavior
Two implementations may produce identical output while having very different timing and side-channel characteristics.
For this reason, each routine is examined not only for functional correctness but also for predictable execution behavior.
Constant-Time Execution
Constant-time programming attempts to prevent secret values from influencing observable execution characteristics.
The book emphasizes rules such as:
- Do not branch on secret data
- Do not index memory using secret values
- Do not terminate comparisons early
- Do not vary loop counts based on secrets
- Do not expose secrets through lookup-table access
- Do not allow compiler transformations to alter critical routines
For example, authentication tags are compared using loops that process every byte regardless of where the first mismatch occurs.
A constant-time comparison accumulates differences across the entire input and produces a result only after all bytes have been examined.
Memory Alignment
Correct memory alignment improves predictability and may affect both performance and correctness.
The implementations use carefully aligned buffers for:
- Message blocks
- Expanded schedules
- Cryptographic state
- Digests
- Keys
- Nonces
- Keystream output
The book explains:
- Stack alignment under the System V AMD64 ABI
- Static-data alignment
- Word and block boundaries
- Safe unaligned access when necessary
- Performance implications of crossing cache lines
- Alignment requirements for future SIMD extensions
Alignment is treated as an explicit implementation responsibility rather than an assumption.
Stack-Frame Construction
All stack frames are constructed manually.
Each routine explicitly defines:
- Saved registers
- Local variables
- Temporary buffers
- Alignment padding
- Argument storage
- Cleanup behavior
This provides full visibility into:
- Data lifetime
- Register preservation
- Stack alignment
- Temporary-state exposure
- Memory-zeroization requirements
Manually constructed stack frames also prevent hidden runtime behavior from interfering with sensitive cryptographic state.
Controlled Zeroization
Cryptographic keys and intermediate state may remain in memory after an operation completes.
The book therefore introduces controlled zeroization routines for clearing:
- Expanded keys
- Hash state
- Message schedules
- Temporary authentication values
- Keystream buffers
- Stack-resident secrets
Zeroization must be explicit and carefully placed so that it is not omitted or optimized away.
In a pure assembly implementation, the programmer controls exactly which locations are overwritten and when cleanup occurs.
Deterministic Engineering
This book treats cryptographic implementation as a deterministic engineering discipline.
Security is achieved not through abstraction, but through explicit construction:
- Constant-time comparison loops
- Fixed execution paths
- Aligned buffers
- Controlled register usage
- Manually constructed stack frames
- Explicit length calculations
- Verified state transitions
- Controlled memory cleanup
These practices are essential for reducing:
- Side-channel leakage
- Undefined behavior
- Memory corruption
- Incorrect serialization
- Unintended toolchain interference
- Secret-dependent execution
No Runtime Dependencies
All code operates without:
libc- Compiler-generated startup code
- Compiler intrinsics
- Dynamic allocation
- External cryptographic libraries
- Hidden exception handling
- Runtime-managed memory
Linux interaction is performed through explicit system calls where required.
This keeps the execution path small, transparent, and suitable for direct inspection.
Relationship to the Series
This volume is part of the broader Low-Level Programming on Linux (x86-64) series.
It builds upon earlier topics such as:
- x86-64 architecture
- Assembly language
- Register usage
- Linux system calls
- Process memory
- Stack management
- Binary data representation
- Calling conventions
- The System V AMD64 ABI
It bridges the gap between cryptographic theory and complete system-level implementation.
The algorithms are not treated only as mathematical specifications. They are treated as executable systems whose security depends on the precise behavior of the processor, memory subsystem, and operating environment.
Practical Applications
By the end of the book, the reader will possess the practical knowledge required to build a minimal cryptographic assembly library suitable for:
- Controlled Linux environments
- Bare-metal experimentation
- Security research
- Educational cryptographic analysis
- Runtime development
- Embedded systems
- Performance-critical software
- Minimal operating environments
- Cryptographic testing tools
- Architecture-specific optimization
The resulting library is intentionally small, explicit, and suitable for inspection at the instruction level.
Final Objective
This book is written with the conviction that cryptography is strongest when its operation is fully visible.
Implementing cryptographic algorithms directly in assembly exposes:
- Every transformation
- Every state transition
- Every register dependency
- Every memory operation
- Every timing decision
- Every serialization step
- Every cleanup operation
By the end of this volume, the reader will understand both the internal design of key cryptographic primitives and the practical requirements for implementing them securely in x86-64 assembly.
In an era increasingly dependent on secure computation, this depth of understanding is not a luxury—it is a requirement.