lib/reticulum/src/crypto/root.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 //! The constructions a Reticulum payload is sealed with once both ends already
2 //! share a key: AES in cipher block chaining mode, the padding that fills a
3 //! plaintext out to whole blocks, HMAC-SHA256, the key derivation that turns
4 //! one secret into as many bytes as a caller needs, and the sealed format that
5 //! composes them.
6 //!
7 //! What this code produces has to match the reference byte for byte, because
8 //! the program at the other end is a different implementation. The code runs
9 //! where memory is fixed before the program starts, so every call works in a
10 //! buffer the caller owns. A tag comparison that gives away how many bytes
11 //! matched would hand an attacker a way to forge one, so the comparison runs in
12 //! constant time.
13 //!
14 //! The primitives themselves come from the standard library, and what
15 //! interoperability turns on is the framing around them: where the
16 //! initialization vector sits, where the tag sits, which half of a key does
17 //! which job, and how the derivation counts its blocks. The reference is
18 //! written in Python, and its unpadding inherits Python's own slicing behavior
19 //! when the final byte claims more padding than the data holds.
20 //!
21 //! The subtree follows Reticulum 1.5.0, the reference implementation, pinned to
22 //! one upstream commit by the package README and the generated conformance
23 //! corpus. What it takes is the block cipher use of Reticulum@1.5.0
24 //! RNS/Cryptography/AES.py:43-111, the padding of Reticulum@1.5.0
25 //! RNS/Cryptography/PKCS7.py:32-48, the signing and comparison of
26 //! Reticulum@1.5.0 RNS/Cryptography/HMAC.py:27-45,185-188, the derivation of
27 //! Reticulum@1.5.0 RNS/Cryptography/HKDF.py:35-62, and the sealed layout of
28 //! Reticulum@1.5.0 RNS/Cryptography/Token.py:61-114. The package generates
29 //! token, derivation, and padding vectors from that release and replays them in
30 //! tests, so each of those claims is checkable from this tree.
31 //!
32 //! Every call here writes into a slice the caller supplied and returns a slice
33 //! of it, so this code calls no allocator. The sealed format (a *token*)
34 //! borrows its two key halves from the caller's bytes and copies nothing. A
35 //! call whose input and output buffers overlap is refused, and the block cipher
36 //! allows the one case where they start at the same address. Unpadding
37 //! reproduces the reference's Python slicing, so a final byte claiming more
38 //! padding than the data holds gives what the reference gives. The subtree
39 //! names its pieces: the block cipher, the derivation, the tag, the padding,
40 //! and the sealed format.
41 //!
42 //! - *token key*: 32 or 64 bytes split in half, the first half signing and the
43 //! second half encrypting, with the total length picking AES-128 or AES-256.
44
45 pub const cbc = @import("cbc.zig");
46 pub const hkdf = @import("hkdf.zig");
47 pub const hmac = @import("hmac.zig");
48 pub const pkcs7 = @import("pkcs7.zig");
49 pub const token = @import("token.zig");