summaryrefslogtreecommitdiff
path: root/docs/CHAT-OPENSSL-MIGRATION.txt
blob: 4bb87fbfa81a781541e0f15ebd60e09261fd4a80 (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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
Copyright (C) 2026 Doan Luu Duc Tai <doan@luuduct.ai>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.


OPENSSL BACKEND MIGRATION RECORD

    Date: 2026-10-30. Historical compatibility/dependency-security experiment
    using disposable identities. For the later messenger and packaged deployment,
    see CHAT-APPLICATION.txt and CHAT-PRODUCTION.txt.

DECISION AND FIXED ALGORITHMS

    Retain Sequoia OpenPGP 2.4.2, RFC 9980 ML-KEM-1024 + X448 encryption,
    ML-DSA-87 + Ed448 signatures, OpenPGP v6 keys, and AES-256 content encryption.
    Only the backend changes; algorithms and wire protocol stay fixed.
    Probe assertions reject unexpected backends or key algorithms.

    Previous Rust curve arithmetic reached cx448 0.1.1 -> crypto-bigint 0.5.5;
    the backend also included rsa 0.9.10. Neither RustSec issue was ignored;
    no mathematical implementation was patched locally.
    RUSTSEC-2026-0387 affected required X448/Ed448 curve arithmetic, not unused RSA.
    Its fix requires crypto-bigint >=0.7.6; cx448 0.1.1 requires incompatible 0.5.
    A lockfile update alone could not fix it. No practical browser key-extraction
    exploit was demonstrated; functional tests do not prove constant-time behavior.
    The closed probe used no RSA private operations, but that did not establish
    unreachability through hostile imports or future application interfaces.
        https://rustsec.org/advisories/RUSTSEC-2026-0387.html
        https://rustsec.org/advisories/RUSTSEC-2023-0071.html

    Only crypto-openssl is enabled. allow-experimental-crypto and
    allow-variable-time-crypto are removed. The resolved graph has no cx448,
    crypto-bigint, or rsa crate. Removing those dependencies does not prove
    absence of browser side channels or external C defects.

VERSIONS AND SOURCE PROVENANCE

    Rust toolchain: 1.99.1, pinned in rust-toolchain.toml.
    Sequoia OpenPGP: 2.4.2, exact requirement and Cargo.lock.
    OpenSSL: 3.6.5, native host library and source-built static WASI libraries.
    WASI SDK: 34.0 x86_64 Linux, bundled Clang/libclang 23.1.0-wasi-sdk.
    Browser test runner: Playwright 1.64.1, Chromium 156.0.8078.4.

    SDK archive:
        https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-x86_64-linux.tar.gz
        SHA-256 b761e3a0721dbae9c09a0059e5fdb2bf917d1b4a8a7b430fb3b5aafb0984b2c4

    OpenSSL archive:
        https://github.com/openssl/openssl/releases/download/openssl-3.6.5/openssl-3.6.5.tar.gz
        SHA-256 a2157c2830efdec3788939b00c9b0638306d3f0bbb76dc4832ee503bb397df98

    Digests match upstream GitHub release asset metadata; scripts verify them
    before extraction. This pins inputs, without independently verifying release
    signatures or proving cross-host bit-for-bit builds. Archives, SDK, libraries,
    and build products stay under ignored .tools/ and target/. The OS supplies
    the native development library.

WHY WASI

    The Rust proof used wasm32-unknown-unknown and wasm-bindgen glue. OpenSSL uses
    a wasm32-wasip1 command and explicit browser WASI bridge, calling the native
    tests' compatibility_probe. The browser loads this binary directly and
    requires output identifying OpenSSL 3.6.5; stale Rust-backend artifacts cannot
    count as OpenSSL evidence. Do not reuse historical browser-build commands.

BUILD CONFIGURATION AND BOUNDARIES

    infra/chat/openssl-wasi.conf adds a BASE_unix target with little-endian,
    32-bit bignums; cryptographic routines are unchanged. C uses -O2. No rand.patch,
    unconditional entropy-success patch, or custom RNG is applied. SHA-256 checks
    of all 5889 original regular files against the downloaded archive found none
    changed or missing after build; only the target configuration was added.
    OpenSSL 3.6.5 supports WASI getentropy in
    providers/implementations/rands/seeding/rand_unix.c.

    Configure selects --with-rand-seed=getrandom. WASI libc's entropy request
    reaches wasi_snapshot_preview1.random_get. The browser bridge services it
    exclusively through crypto.getRandomValues, splitting requests at 65536
    bytes. There is no Math.random, fixed seed, or success-on-failure fallback.
    The negative test replaces that API with a failure and requires rejection.

    Static builds disable assembly, async operations, automatic config loading,
    DSO/modules, POSIX I/O, secure-memory arenas, shared libraries, sockets,
    C stdio, thread pools, threads, and interactive console UI to avoid unsupported
    capabilities. Ordinary WASM linear memory provides no secure-erasure,
    locked-page, or delivered-JS protection. Rust output remains captured.

    _WASI_EMULATED_GETPID and libwasi-emulated-getpid supply SDK process-ID emulation.
    getuid/geteuid/getgid/getegid map to getpagesize, following common WASI port
    configuration. The browser module has no real OS user IDs or privilege boundary.
    These values supply neither randomness nor identity credentials. This portability
    assumption does not emulate general POSIX security.

    Bindings use SDK libclang/WASI sysroot, target wasm32-wasip1, and
    -fvisibility=default; without explicit visibility, bindgen omitted declarations.
    Rust's bundled WASI libc precedes SDK libraries; reversed order failed to link
    __wasi_init_tp. SDK libraries remain available for emulated-getpid. These were
    toolchain integration failures; generated cryptographic bindings were not edited.

BROWSER CAPABILITIES

    The WASI bridge supplies clocks, browser CSPRNG, empty arguments/environment,
    EOF on stdin, and bounded in-memory stdout/stderr for this closed test.
    It supplies no real files, preopened directories, sockets, or process launch.
    Listed filesystem/network operations return NOTCAPABLE or BADF. Unknown
    imports fail before instantiation. Standard output is capped at 65536 chars.

    Denials prevent accidental host storage/network access, not malicious JS from
    the same server. Memory, clocks, randomness, and bridge imports remain trusted.
    The test closes Chromium after 120 seconds if execution stalls.

VERIFICATION

    Native: the same probe passes using OpenSSL 3.6.5. It checks v6 generation,
    exact algorithm selection, protected-secret-key serialization/import,
    signed UTF-8 encryption to both participants, decryption by each, wrong
    password rejection, unexpected signer rejection, and ciphertext tampering.

    Browser: actual Chromium executes the OpenSSL WASI binary and passes the
    same checks, then repeats with a fresh module instance. One observed normal
    execution requested 1792 bytes across 72 browser CSPRNG calls. These counts
    are observations, not cryptographic requirements or entropy estimates.
    Removing secure randomness causes failure rather than a successful result.
    A modified module requesting an unknown host import is also rejected before
    instantiation. These negative tests require the expected failure reason.

    The harness observed no external requests, cookies, localStorage,
    sessionStorage, IndexedDB databases, or service-worker registrations; responses
    carry Cache-Control: no-store. This concerns only the probe. Browsers/OSes can
    retain memory, caches, crash dumps, swap, and restored state. No complete chat
    session existed at this checkpoint. Native tests, WASI build, and browser
    execution are separate gates; functional tests and scans are not cryptographic
    or side-channel audits.

DEPENDENCY POLICY REVIEW

    RustSec reports no advisories for this resolved graph; no ignore was added.
    Bans on wildcards, unknown Git/registry sources, and unreviewed duplicates remain.
    cargo update cannot unify these upstream API generations. Exceptions match only
    exact older versions; advisory/license checks still apply. Cargo.lock pins both sides:

        block-buffer 0.10.4 vs 0.12.1
        crypto-common 0.1.7 vs 0.2.2
        digest 0.10.7 vs 0.11.3
            Sequoia's Argon2 0.5.3 -> BLAKE2 0.10 uses the older digest API.
            Root SHA-256 and the parser generator's SHA3 use the newer API.

        cpufeatures 0.2.17 vs 0.3.1
            Argon2 uses 0.2; root SHA-256 and parser-generator SHA3 use 0.3.

        generic-array 0.14.7 vs 1.4.5
            Argon2/BLAKE2 digest uses 0.14; SHA1 collision detection uses 1.x.

        getrandom 0.2.17 vs 0.4.3
            Sequoia's unknown-unknown WASM target dependencies declare 0.2;
            the root CLI's tempfile uses 0.4. The current browser target is WASI,
            whose OpenSSL entropy source is the bridge described above. The
            all-target graph is still audited rather than excluding that edge.

        hashbrown 0.15.5 vs 0.17.1
            petgraph uses 0.15 directly and indexmap 2.14 uses 0.17. Both are
            brought in by the parser generator at build time.

        itertools 0.13.0 vs 0.15.0
        shlex 1.3.0 vs 2.0.1
            bindgen uses older APIs; lalrpop and cc require the newer ones.
            These are build dependencies, not a justification to skip auditing.

        syn 2.0.119 vs 3.0.9
            bindgen/prettyplease and Windows support use syn 2. Root derive
            macros and other dependencies use syn 3. Changing that would require
            upstream migration or unrelated dependency downgrades.

    The only new license allowance is ISC for bindgen/clang's libloading.
    GNU-compatible licenses retain attribution; cargo-about covers the workspace.
    OpenSSL C and SDK are outside Cargo. Distributed browser artifacts must carry
    their original licenses/sources as required. OpenSSL LICENSE.txt is Apache-2.0.
    Rust notices alone do not cover external C/toolchain distribution obligations.

EXTERNAL LIBRARY MAINTENANCE

    cargo-deny excludes external OpenSSL C and SDK. Review separately; rebuild as needed:
        https://openssl-library.org/news/timeline/
        https://openssl-library.org/news/vulnerabilities-3.6/
    Tested OpenSSL 3.6.5 is the 2026-09-29 security-patch release. A Rust audit
    does not guarantee this pinned C version's future security. Document a supported
    update policy before production deployment.

REPRODUCE FROM THE STACK ROOT

    Native prerequisites: OpenSSL >=3.5 development headers/library, libclang,
    pkg-config, and the pinned Rust toolchain. For the cross build also provide
    a Linux x86_64 build host, Perl, make, curl, tar, and sha256sum. No root-owned
    packages or system settings are changed by the cross-build script.

        cargo test --locked -p stack-chat-crypto
        cargo run --locked -p stack-chat-crypto
        sh infra/chat/build-openssl-wasi.sh
        npm run build
        PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" node_modules/.bin/playwright install chromium
        PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/browsers" npm run test:crypto
        cargo stack check

    The script pins versions/hashes, builds static OpenSSL under .tools/openssl-wasi,
    builds the Rust WASI command, and prints its SHA-256. It uses no unverified
    precompiled OpenSSL. Extracted sources may be reused; independent verification
    needs a fresh ignored tools/build tree. Downloads/test artifacts are not secrets.

    Build logs and complete dependency trees are local ignored diagnostics in
    .tools/chat-openssl-*.log and .tools/chat-openssl-duplicates.txt. The native
    result and the browser result must explicitly identify the OpenSSL backend.

REMAINING WORK

    Observed WASI artifact SHA-256 on this development machine:
        7fba6b96377619ef41f056d4027c4409107372e9300b2dd69be865e72892df79
    This is a diagnostic identifier, not a cross-host reproducibility promise.

    This checkpoint removed the Rust-backend blocker and demonstrated fixed
    algorithms in-browser. It was not a complete messenger, OpenSSL/WASI/JIT timing
    audit, LibreJS certification, or proof against malicious delivery. File format,
    authentication, server/database, quotas, hostile-input handling, pagination/counts,
    recovery, session lifecycle, and browser-state tests remained to implement.