summaryrefslogtreecommitdiffstats
path: root/news/phase4-encryption-sealed/index.html
blob: dd7b5eb5ac6f95ad42cc4419513146cd3ab46d6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Phase 4: Encryption and Sealed Tesseras — Tesseras</title>
    <meta name="description" content="Tesseras now supports private and sealed memories with hybrid post-quantum encryption — AES-256-GCM, X25519 + ML-KEM-768, and time-lock key publication.">
    <!-- Open Graph -->
    <meta property="og:type" content="article">
    <meta property="og:title" content="Phase 4: Encryption and Sealed Tesseras">
    <meta property="og:description" content="Tesseras now supports private and sealed memories with hybrid post-quantum encryption — AES-256-GCM, X25519 + ML-KEM-768, and time-lock key publication.">
    <meta property="og:image" content="https://tesseras.net/images/social.jpg">
    <meta property="og:image:width" content="1200">
    <meta property="og:image:height" content="630">
    <meta property="og:site_name" content="Tesseras">
    <!-- Twitter Card -->
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:title" content="Phase 4: Encryption and Sealed Tesseras">
    <meta name="twitter:description" content="Tesseras now supports private and sealed memories with hybrid post-quantum encryption — AES-256-GCM, X25519 + ML-KEM-768, and time-lock key publication.">
    <meta name="twitter:image" content="https://tesseras.net/images/social.jpg">
    <link rel="stylesheet" href="https://tesseras.net/style.css?h=21f0f32121928ee5c690">
    
        
            <link rel="alternate" type="application/atom+xml" title="Tesseras" href="https://tesseras.net/atom.xml">
        
    
    <link rel="icon" type="image/png" sizes="32x32" href="https://tesseras.net/images/favicon.png?h=be4e123a23393b1a027d">
    
</head>
<body>
    <header>
        <h1>
            <a href="https:&#x2F;&#x2F;tesseras.net/">
                <img src="https://tesseras.net/images/logo-64.png?h=c1b8d0c4c5f93b49d40b" alt="Tesseras" width="40" height="40" class="logo">
                Tesseras
            </a>
        </h1>
        <nav>
            
                <a href="https://tesseras.net/about/">About</a>
                <a href="https://tesseras.net/news/">News</a>
                <a href="https://tesseras.net/releases/">Releases</a>
                <a href="https://tesseras.net/faq/">FAQ</a>
                <a href="https://tesseras.net/subscriptions/">Subscriptions</a>
                <a href="https://tesseras.net/contact/">Contact</a>
            
        </nav>
        <nav class="lang-switch">
            
                <strong>English</strong> | <a href="/pt-br&#x2F;news&#x2F;phase4-encryption-sealed&#x2F;">Português</a>
            
        </nav>
    </header>

    <main>
        
<article>
    <h2>Phase 4: Encryption and Sealed Tesseras</h2>
    <p class="news-date">2026-02-14</p>
    <p>Some memories are not meant for everyone. A private journal, a letter to be
opened in 2050, a family secret sealed until the grandchildren are old enough.
Until now, every tessera on the network was open. Phase 4 changes that: Tesseras
now encrypts private and sealed content with a hybrid cryptographic scheme
designed to resist both classical and quantum attacks.</p>
<p>The principle remains the same — encrypt as little as possible. Public memories
need availability, not secrecy. But when someone creates a private or sealed
tessera, the content is now locked behind AES-256-GCM encryption with keys
protected by a hybrid key encapsulation mechanism combining X25519 and
ML-KEM-768. Both algorithms must be broken to access the content.</p>
<h2 id="what-was-built">What was built</h2>
<p><strong>AES-256-GCM encryptor</strong> (<code>tesseras-crypto/src/encryption.rs</code>) — Symmetric
content encryption with random 12-byte nonces and authenticated associated data
(AAD). The AAD binds ciphertext to its context: for private tesseras, the
content hash is included; for sealed tesseras, both the content hash and the
<code>open_after</code> timestamp are bound into the AAD. This means moving ciphertext
between tesseras with different open dates causes decryption failure — you
cannot trick the system into opening a sealed memory early by swapping its
ciphertext into a tessera with an earlier seal date.</p>
<p><strong>Hybrid Key Encapsulation Mechanism</strong> (<code>tesseras-crypto/src/kem.rs</code>) — Key
exchange using X25519 (classical elliptic curve Diffie-Hellman) combined with
ML-KEM-768 (the NIST-standardized post-quantum lattice-based KEM, formerly
Kyber). Both shared secrets are combined via <code>blake3::derive_key</code> with a fixed
context string ("tesseras hybrid kem v1") to produce a single 256-bit content
encryption key. This follows the same "dual from day one" philosophy as the
project's dual signing (Ed25519 + ML-DSA): if either algorithm is broken in the
future, the other still protects the content.</p>
<p><strong>Sealed Key Envelope</strong> (<code>tesseras-crypto/src/sealed.rs</code>) — Wraps a content
encryption key using the hybrid KEM, so only the tessera owner can recover it.
The KEM produces a transport key, which is XORed with the content key to produce
a wrapped key stored alongside the KEM ciphertext. On unsealing, the owner
decapsulates the KEM ciphertext to recover the transport key, then XORs again to
recover the content key.</p>
<p><strong>Key Publication</strong> (<code>tesseras-crypto/src/sealed.rs</code>) — A standalone signed
artifact for publishing a sealed tessera's content key after its <code>open_after</code>
date has passed. The owner signs the content key, tessera hash, and publication
timestamp with their dual keys (Ed25519, with ML-DSA placeholder). The manifest
stays immutable — the key publication is a separate document. Other nodes verify
the signature against the owner's public key before using the published key to
decrypt the content.</p>
<p><strong>EncryptionContext</strong> (<code>tesseras-core/src/enums.rs</code>) — A domain type that
represents the AAD context for encryption. It lives in tesseras-core rather than
tesseras-crypto because it's a domain concept (not a crypto implementation
detail). The <code>to_aad_bytes()</code> method produces deterministic serialization: a tag
byte (0x00 for Private, 0x01 for Sealed), followed by the content hash, and for
Sealed, the <code>open_after</code> timestamp as little-endian i64.</p>
<p><strong>Domain validation</strong> (<code>tesseras-core/src/service.rs</code>) —
<code>TesseraService::create()</code> now rejects Sealed and Private tesseras that don't
provide encryption keys. This is a domain-level validation: the service layer
enforces that you cannot create a sealed memory without the cryptographic
machinery to protect it. The error message is clear: "missing encryption keys
for visibility sealed until 2050-01-01."</p>
<p><strong>Core type updates</strong> — <code>TesseraIdentity</code> now includes an optional
<code>encryption_public: Option&lt;HybridEncryptionPublic&gt;</code> field containing both the
X25519 and ML-KEM-768 public keys. <code>KeyAlgorithm</code> gained <code>X25519</code> and <code>MlKem768</code>
variants. The identity filesystem layout now supports <code>node.x25519.key</code>/<code>.pub</code>
and <code>node.mlkem768.key</code>/<code>.pub</code>.</p>
<p><strong>Testing</strong> — 8 unit tests for AES-256-GCM (roundtrip, wrong key, tampered
ciphertext, wrong AAD, cross-context decryption failure, unique nonces, plus 2
property-based tests for arbitrary payloads and nonce uniqueness). 5 unit tests
for HybridKem (roundtrip, wrong keypair, tampered X25519, KDF determinism, plus
1 property-based test). 4 unit tests for SealedKeyEnvelope and KeyPublication. 2
integration tests covering the complete sealed and private tessera lifecycle:
generate keys, create content key, encrypt, seal, unseal, decrypt, publish key,
and verify — the full cycle.</p>
<h2 id="architecture-decisions">Architecture decisions</h2>
<ul>
<li><strong>Hybrid KEM from day one</strong>: X25519 + ML-KEM-768 follows the same philosophy
as dual signing. We don't know which cryptographic assumptions will hold over
millennia, so we combine classical and post-quantum algorithms. The cost is
~1.2 KB of additional key material per identity — trivial compared to the
photos and videos in a tessera.</li>
<li><strong>BLAKE3 for KDF</strong>: rather than adding <code>hkdf</code> + <code>sha2</code> as new dependencies, we
use <code>blake3::derive_key</code> with a fixed context string. BLAKE3's key derivation
mode is specifically designed for this use case, and the project already
depends on BLAKE3 for content hashing.</li>
<li><strong>Immutable manifests</strong>: when a sealed tessera's <code>open_after</code> date passes, the
content key is published as a separate signed artifact (<code>KeyPublication</code>), not
by modifying the manifest. This preserves the append-only, content-addressed
nature of tesseras. The manifest was signed at creation time and never
changes.</li>
<li><strong>AAD binding prevents ciphertext swapping</strong>: the <code>EncryptionContext</code> binds
both the content hash and (for sealed tesseras) the <code>open_after</code> timestamp
into the AES-GCM authenticated data. An attacker who copies encrypted content
from a "sealed until 2050" tessera into a "sealed until 2025" tessera will
find that decryption fails — the AAD no longer matches.</li>
<li><strong>XOR key wrapping</strong>: the sealed key envelope uses a simple XOR of the content
key with the KEM-derived transport key, rather than an additional layer of
AES-GCM. Since the transport key is a fresh random value from the KEM and is
used exactly once, XOR is information-theoretically secure for this specific
use case and avoids unnecessary complexity.</li>
<li><strong>Domain validation, not storage validation</strong>: the "missing encryption keys"
check lives in <code>TesseraService::create()</code>, not in the storage layer. This
follows the hexagonal architecture pattern: domain rules are enforced at the
service boundary, not scattered across adapters.</li>
</ul>
<h2 id="what-comes-next">What comes next</h2>
<ul>
<li><strong>Phase 4 continued: Resilience and Scale</strong> — Shamir's Secret Sharing for heir
key distribution, advanced NAT traversal (STUN/TURN), performance tuning,
security audits, OS packaging</li>
<li><strong>Phase 5: Exploration and Culture</strong> — Public tessera browser by
era/location/theme/language, institutional curation, genealogy integration,
physical media export (M-DISC, microfilm, acid-free paper with QR)</li>
</ul>
<p>Sealed tesseras make Tesseras a true time capsule. A father can now record a
message for his unborn grandchild, seal it until 2060, and know that the
cryptographic envelope will hold — even if the quantum computers of the future
try to break it open early.</p>

</article>

    </main>

    <footer>
        <p>&copy; 2026 Tesseras Project. <a href="/atom.xml">News Feed</a> · <a href="https://git.sr.ht/~ijanc/tesseras">Source</a></p>
    </footer>
</body>
</html>