Skip to content

Commit 3aae951

Browse files
committed
crypto: add core primitives, examples, and smoke tests
Introduce explicit cryptography building blocks for Vix: - secure randomness (CSPRNG) - hashing (SHA-256) - HMAC and HKDF - secret key handling - AEAD (AES-256-GCM) - Ed25519 signatures Includes clear Result/Error-based APIs (no exceptions), self-contained examples, and a minimal smoke test suite.
1 parent 01e0205 commit 3aae951

23 files changed

Lines changed: 2701 additions & 2 deletions

README.md

Lines changed: 193 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,193 @@
1-
# crypto
2-
Modern cryptography primitives for Vix. Explicit, auditable, dependency-light crypto building blocks for secure runtimes.
1+
# vix::crypto
2+
3+
Modern cryptography primitives for Vix.
4+
5+
This module provides **explicit, auditable, dependency-light** cryptographic building blocks
6+
designed for secure runtimes, offline-first systems, and peer-to-peer protocols.
7+
8+
No hidden magic. No implicit control flow. No exceptions in public APIs.
9+
10+
---
11+
12+
## Design principles
13+
14+
- **Explicit errors**
15+
All operations return `Result<T>` or `Result<void>`. Failure is visible at call sites.
16+
17+
- **Composable primitives**
18+
Small building blocks that can be combined safely in higher-level systems.
19+
20+
- **Provider-agnostic**
21+
Interfaces are stable. Providers (OpenSSL, OS RNG) are behind clear boundaries.
22+
23+
- **Predictable behavior**
24+
No global state. No hidden initialization. No surprising allocations.
25+
26+
---
27+
28+
## Provided primitives
29+
30+
### Randomness
31+
32+
- `random_bytes(span<uint8_t>)`
33+
- `random_uint(max)`
34+
35+
Backed by:
36+
- OpenSSL CSPRNG (when enabled)
37+
- Linux `getrandom()` syscall
38+
39+
---
40+
41+
### Hashing
42+
43+
- `sha256(...)`
44+
- Generic `hash(HashAlg, ...)`
45+
46+
Used for:
47+
- content identifiers
48+
- integrity checks
49+
- signatures
50+
- WAL and sync engines
51+
52+
---
53+
54+
### HMAC
55+
56+
- `hmac_sha256(...)`
57+
- Generic `hmac(HmacAlg, ...)`
58+
59+
Provides message authentication with shared secrets.
60+
61+
---
62+
63+
### Key derivation
64+
65+
- `hkdf_sha256(...)`
66+
- Generic `kdf(KdfAlg, ...)`
67+
68+
RFC 5869 compliant.
69+
70+
---
71+
72+
### Secret keys
73+
74+
- `SecretKey` (owning, zeroized on destruction)
75+
- `generate_secret_key(size)`
76+
77+
Designed for in-memory safety and explicit lifetimes.
78+
79+
---
80+
81+
### Authenticated encryption (AEAD)
82+
83+
- `aes_256_gcm` via `aead_encrypt` / `aead_decrypt`
84+
85+
Provides:
86+
- confidentiality
87+
- integrity
88+
- authenticity
89+
90+
Nonce and tag handling is explicit and strict.
91+
92+
---
93+
94+
### Signatures
95+
96+
- `ed25519`
97+
- key generation
98+
- signing
99+
- verification
100+
101+
Deterministic, fast, and widely deployed.
102+
103+
---
104+
105+
## Error handling
106+
107+
All APIs return explicit errors:
108+
109+
```cpp
110+
auto r = sha256(data, out);
111+
if (!r.ok())
112+
{
113+
// inspect r.error().code and r.error().message
114+
}
115+
```
116+
117+
No exceptions are thrown by public APIs.
118+
119+
---
120+
121+
## Build and integration
122+
123+
### As part of the Vix umbrella
124+
125+
The crypto module is designed to be added via:
126+
127+
```
128+
modules/crypto
129+
```
130+
131+
Dependencies are managed by the umbrella build.
132+
133+
---
134+
135+
### Standalone build
136+
137+
```bash
138+
cmake -S . -B build
139+
cmake --build build
140+
```
141+
142+
OpenSSL is used automatically when available.
143+
144+
---
145+
146+
## Examples
147+
148+
See the `examples/` directory:
149+
150+
- `hash_sha256.cpp`
151+
- `aead_roundtrip.cpp`
152+
- `sign_verify.cpp`
153+
154+
Each example is small, self-contained, and demonstrates correct usage.
155+
156+
---
157+
158+
## Tests
159+
160+
The `tests/` directory contains a minimal smoke test validating:
161+
162+
- randomness
163+
- hashing
164+
- HMAC
165+
- KDF
166+
- AEAD
167+
- signatures
168+
169+
These tests ensure wiring and providers work as expected.
170+
171+
---
172+
173+
## Scope
174+
175+
This module intentionally does **not** provide:
176+
177+
- TLS
178+
- certificate handling
179+
- key storage
180+
- protocol-level logic
181+
182+
Those belong in higher-level modules (`p2p`, `net`, `sync`).
183+
184+
---
185+
186+
## License
187+
188+
MIT. See `LICENSE`.
189+
190+
---
191+
192+
> Cryptography does not create trust.
193+
> It makes systems **work without having to trust**.

examples/CMakeLists.txt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# @file CMakeLists.txt
2+
# @author Gaspard Kirira
3+
#
4+
# Copyright 2025, Gaspard Kirira.
5+
# All rights reserved.
6+
# https://github.com/vixcpp/vix
7+
# Use of this source code is governed by a MIT license
8+
# that can be found in the License file.
9+
#
10+
# ====================================================================
11+
# Vix.cpp - Crypto Module Examples
12+
# ====================================================================
13+
# Purpose:
14+
# Small, self-contained examples demonstrating the crypto primitives.
15+
# These binaries are meant for developers and documentation, not install.
16+
# ====================================================================
17+
18+
cmake_minimum_required(VERSION 3.20)
19+
20+
# Examples are only meaningful if the crypto target exists
21+
if (NOT TARGET vix::crypto)
22+
message(FATAL_ERROR "[crypto/examples] vix::crypto target not found")
23+
endif()
24+
25+
# Helper macro to reduce boilerplate
26+
function(vix_crypto_example name)
27+
add_executable(${name} ${name}.cpp)
28+
target_link_libraries(${name} PRIVATE vix::crypto)
29+
30+
target_compile_features(${name} PRIVATE cxx_std_20)
31+
32+
if (TARGET vix_warnings)
33+
target_link_libraries(${name} PRIVATE vix_warnings)
34+
endif()
35+
36+
if (VIX_ENABLE_SANITIZERS AND TARGET vix_sanitizers)
37+
target_link_libraries(${name} PRIVATE vix_sanitizers)
38+
endif()
39+
endfunction()
40+
41+
# --------------------------------------------------------------------
42+
# Examples
43+
# --------------------------------------------------------------------
44+
45+
vix_crypto_example(hash_sha256)
46+
vix_crypto_example(aead_roundtrip)
47+
vix_crypto_example(sign_verify)

examples/aead_roundtrip.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
*
3+
* @file aead_roundtrip.cpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2025, Gaspard Kirira.
7+
* All rights reserved.
8+
* https://github.com/vixcpp/vix
9+
*
10+
* Use of this source code is governed by a MIT license
11+
* that can be found in the License file.
12+
*
13+
* Vix.cpp
14+
*
15+
*/
16+
#include <vix/crypto/crypto.hpp>
17+
18+
#include <array>
19+
#include <cstdint>
20+
#include <iomanip>
21+
#include <iostream>
22+
#include <string_view>
23+
#include <vector>
24+
25+
namespace
26+
{
27+
28+
void print_hex(std::span<const std::uint8_t> bytes)
29+
{
30+
std::ios old_state(nullptr);
31+
old_state.copyfmt(std::cout);
32+
33+
for (std::size_t i = 0; i < bytes.size(); ++i)
34+
{
35+
std::cout << std::hex << std::setw(2) << std::setfill('0')
36+
<< static_cast<int>(bytes[i]);
37+
}
38+
39+
std::cout.copyfmt(old_state);
40+
}
41+
42+
int run()
43+
{
44+
using namespace vix::crypto;
45+
46+
constexpr AeadAlg alg = AeadAlg::aes_256_gcm;
47+
48+
std::array<std::uint8_t, 32> key{};
49+
std::array<std::uint8_t, 12> nonce{};
50+
51+
auto rk = random_bytes(key);
52+
if (!rk.ok())
53+
{
54+
std::cerr << "random key failed: "
55+
<< static_cast<int>(rk.error().code)
56+
<< " " << rk.error().message << "\n";
57+
return 1;
58+
}
59+
60+
auto rn = random_bytes(nonce);
61+
if (!rn.ok())
62+
{
63+
std::cerr << "random nonce failed: "
64+
<< static_cast<int>(rn.error().code)
65+
<< " " << rn.error().message << "\n";
66+
return 1;
67+
}
68+
69+
constexpr std::string_view aad_sv = "vix-crypto:aead-demo";
70+
constexpr std::string_view msg_sv = "hello from vix crypto";
71+
72+
std::vector<std::uint8_t> plaintext(msg_sv.begin(), msg_sv.end());
73+
std::vector<std::uint8_t> ciphertext(plaintext.size());
74+
std::vector<std::uint8_t> decrypted(plaintext.size());
75+
std::array<std::uint8_t, 16> tag{};
76+
77+
auto enc = aead_encrypt(
78+
alg,
79+
key,
80+
nonce,
81+
std::span<const std::uint8_t>(
82+
reinterpret_cast<const std::uint8_t *>(aad_sv.data()),
83+
aad_sv.size()),
84+
plaintext,
85+
ciphertext,
86+
tag);
87+
88+
if (!enc.ok())
89+
{
90+
std::cerr << "encrypt failed: "
91+
<< static_cast<int>(enc.error().code)
92+
<< " " << enc.error().message << "\n";
93+
return 1;
94+
}
95+
96+
auto dec = aead_decrypt(
97+
alg,
98+
key,
99+
nonce,
100+
std::span<const std::uint8_t>(
101+
reinterpret_cast<const std::uint8_t *>(aad_sv.data()),
102+
aad_sv.size()),
103+
ciphertext,
104+
tag,
105+
decrypted);
106+
107+
if (!dec.ok())
108+
{
109+
std::cerr << "decrypt failed: "
110+
<< static_cast<int>(dec.error().code)
111+
<< " " << dec.error().message << "\n";
112+
return 1;
113+
}
114+
115+
const bool same = (decrypted == plaintext);
116+
117+
std::cout << "alg: aes-256-gcm\n";
118+
std::cout << "aad: " << aad_sv << "\n";
119+
std::cout << "msg: " << msg_sv << "\n";
120+
std::cout << "ciphertext: ";
121+
print_hex(ciphertext);
122+
std::cout << "\n";
123+
std::cout << "tag: ";
124+
print_hex(tag);
125+
std::cout << "\n";
126+
std::cout << "roundtrip: " << (same ? "OK" : "FAILED") << "\n";
127+
128+
return same ? 0 : 2;
129+
}
130+
131+
} // namespace
132+
133+
int main()
134+
{
135+
return run();
136+
}

0 commit comments

Comments
 (0)