Audit s2n-tls — relevé chiffré pour la contingence air-tls maison
Note de travail (2026-07-07). Objectif : mesurer s2n-tls (la meilleure pile TLS
tout-C, AWS) pour outiller la décision sur la contingence air-tls maison
(ADR-042 §Contingence) — ne pas vendorer, ne pas porter le C, mais connaître
précisément ce qu’on reprendrait en couverture fonctionnelle si l’on activait
l’option pur-Rust. Source analysée : aws/s2n-tls, tag effectif v1.7.5,
commit 632eb7cef3eb72a3a5466c62896b9c2879f30295 (2026-07-02), clone shallow +
approfondissement des tags. Analyse statique (compte de fonctions, LOC,
assembleur, dépendances CMake) — aucun build lancé. Cette note ne tranche
rien : elle alimente la spec docs/specs/layer-2/air-tls.md.
0. Pourquoi cet audit
ADR-042 a acté la pile de production
(rustls + aws-lc-rs + rustls-webpki) et gardé ouverte une contingence maison
(pur Rust, patron sans-IO). Pour instruire cette contingence de façon crédible, il
faut savoir ce que couvre la référence tout-C la plus sûre : combien de
surface, combien de code, combien d’assembleur, quelles dépendances, quelle
maintenance. C’est l’objet de ce relevé, dont la spec air-tls tire son inventaire
d’objets (~61) et son périmètre.
1. Volumétrie du code (cœur de bibliothèque, hors tests)
| Dossier | .c | .h | Lignes | Rôle |
|---|---|---|---|---|
tls/ | 125 | 95 | 44 903 (75 %) | protocole : handshake, record, extensions, state machine |
crypto/ | 29 | 27 | 8 359 | wrappers vers libcrypto (PAS de crypto : ni primitive, ni asm) |
utils/ | 14 | 21 | 4 054 | stuffer d’appoint, safety macros, mem |
stuffer/ | 7 | 1 | 1 784 | buffers de (dé)sérialisation bornés |
error/ | 1 | 1 | 931 | codes + messages |
| Cœur | 176 | 145 | 60 031 | — |
api/ (headers publics) | — | 12 | 5 398 | s2n.h + 11 headers unstable/ |
~60 k lignes de C portable pour le cœur. Les tests (volumineux) ne sont pas embarqués.
2. Surface API publique — 303 fonctions (macro S2N_API)
Décompte robuste : les occurrences S2N_API (303) et les identifiants s2n_*(...)
uniques (303) concordent exactement.
| Famille | Fn | Rôle | Objet air-tls correspondant |
|---|---|---|---|
s2n_connection_* | 91 | cycle de vie + état par connexion | TlsClient/TlsServer |
s2n_config_* | 75 | politique partagée | TlsClientConfig/TlsServerConfig |
s2n_client_hello_* | 25 | introspection ClientHello | ClientHello |
I/O (send/recv/negotiate/shutdown/peek/sendv/sendfile…) | 16 | handshake + transfert | TlsStream + cœur sans-IO |
s2n_psk_* / s2n_offered_psk_* | 14 | pre-shared keys (1.3) | Psk/OfferedPsk |
init/mem/rand global (s2n_init, s2n_cleanup, s2n_mem_*, s2n_rand_*, s2n_errno…) | 14 | bootstrap process-wide | majoritairement éliminé (RAII Rust : ni init/cleanup global, ni callbacks mem) |
s2n_cert_chain_and_key_* | 12 | chaîne + clé privée | CertifiedKey |
s2n_crl_* | 9 | listes de révocation | CertificateRevocationList |
s2n_fingerprint_* | 8 | JA3/JA4 | ClientHelloFingerprint |
s2n_async_pkey_* | 8 | offload signature (HSM) | AsyncSigner (trait) |
s2n_cert_* | 7 | trust store / validation | RootCertStore + ServerCertVerifier |
s2n_certificate_* | 5 | accès aux champs X.509 | Certificate |
s2n_offered_early_data_* | 4 | 0-RTT | EarlyDataConfig |
s2n_session_* | 3 | reprise | SessionTicket/SessionStore |
| Total | 303 | — | ~61 objets Rust (spec air-tls) |
Fonctions internes : ~1 695 symboles s2n_* uniques définis dans le cœur
.c (~1 751 blocs de définition). Ratio surface/interne ≈ 18 % exposé.
3. Assembleur pur — ZÉRO ligne (résultat structurant)
Recherche exhaustive sur tout le dépôt : 0 fichier .S/.s/.asm, 0 asm
inline (__asm__/asm volatile/asm(). Tout l’assembleur crypto vit dans
libcrypto (AWS-LC), jamais dans s2n-tls. s2n = C portable pur (-std=gnu99)
qui orchestre le protocole et délègue 100 % des primitives.
Corrobore exactement ADR-042 : « protocole memory-safe + primitives formellement vérifiées ». En maison, on transpose : protocole pur Rust (
air-tls) + primitivesair-crypto(RustCrypto). Le « 0 asm » de s2n confirme qu’un port de protocole n’a pas à toucher d’assembleur — l’asm est un problème de primitives, réglé parair-crypto.
4. Architectures processeur
| Tier | Plateformes CI | Arches |
|---|---|---|
| Tier 1 (build+tests garantis en CI) | Ubuntu, Amazon Linux 2/2023, NixOS, OpenBSD, FreeBSD, macOS | x86_64, aarch64, i686 |
| Tier 2 (build garanti) | Fedora, Ubuntu anciens | x86_64, aarch64 |
Couvre les deux cibles jour-1 d’Air (x86_64 + ARM64, ADR-014). C portable → aucune
dépendance d’architecture dans s2n lui-même (elle est dans libcrypto).
5. Cadence de mise à jour — très active
- 169 tags de release. Rythme ~2 releases/mois :
v1.7.5(2026-06-24),v1.7.4(2026-06-05),v1.7.3(2026-05-04),v1.7.2(2026-03-31)… - ~25 à 53 commits/mois sur 12 mois glissants (juin 2026 : 53). Équipe AWS financée, réponse sécurité rapide → fiabilité sur 10-20 ans (argument ADR-042 pour le provider aws-lc-rs).
6. Dépendances
libcrypto(obligatoire, enfichable) : AWS-LC (recommandé), OpenSSL1.0.2/1.1.1/3.0.x, LibreSSL, BoringSSL. Aucun crypto vendoré dans s2n.- pthreads (
find_package(Threads REQUIRED)). - Python3 : tests uniquement, pas à l’exécution.
- Bindings Rust officiels :
bindings/rust/(crates2n-tls) — pertinent si Air voulait wrapper s2n plutôt que le réécrire (option écartée par ADR-042 : le C du protocole reste exposé).
Fonctionnalités liées au provider : PQ key exchange + FIPS + sandboxing = AWS-LC uniquement.
7. Toolchain Linux
- Build system : CMake (préféré ; Makefile en repli). Standard
-std=gnu99. - Compilateur : gcc ET clang supportés et testés en CI (matrice ~5 clang / 3 gcc). clang privilégié pour sanitizers/fuzzing.
- Provider recommandé : AWS-LC (perf + sécurité + FIPS 140-3 + PQ). Option
S2N_INTERN_LIBCRYPTOpour figer/isoler une version (repro ADR-025, évite les collisions de symboles).
8. Ce que l’on retient pour air-tls maison
- Empreinte à reprendre : ~60 k lignes de protocole (dont
tls/= 45 k, la vraie masse). En maison, cette masse devient du Rust memory-safe ; les primitives (le reste) restent horsair-tls(dansair-crypto). - Zéro asm à porter : le port maison ne touche aucun assembleur — confirmé
par le 0 de s2n. L’asm est un sujet
air-crypto/RustCrypto (AES-NI/ARMv8 détectés automatiquement), pasair-tls. - Couverture fonctionnelle cible : les 303 fonctions publiques se replient
sur ~61 objets Rust (spec
air-tls§Inventaire) — le facteur ~5 vient des idiomes (builder au lieu de 75 settersconfig, une struct-connexion au lieu de 91 fonctionsconnection, RAII qui supprime les 14 fonctions init/mem/rand). - Périmètre sécurité : on n’importe que le moderne. s2n supporte encore des
provider OpenSSL
1.0.2/1.1.1(fin de vie) ;air-tlsne spécifie que TLS 1.3 — le legacy est omis par conception (cf. spec §Périmètre de sécurité). - Règle des 80 % (ADR-024) : un client TLS Air ne consommerait qu’une fraction
des 303 fonctions → au sens strict, vendoring exigé, exclu pour du crypto
(ADR-034). D’où soit l’exception nommée rustls (ADR-042, production), soit la
maison (
air-tls, zéro tierce) — les deux options que ce projet garde.
9. Provenance & reproductibilité de l’audit
Commandes clés (rejouables) : clone shallow aws/s2n-tls → git fetch --tags --deepen=3000 (cadence) ; find/grep pour LOC, .S/.s/.asm et asm inline ;
grep -c S2N_API api/ + extraction des identifiants s2n_*( (surface) ; parse de
CMakeLists.txt (find_package, SEARCH_LIBCRYPTO, S2N_INTERN_LIBCRYPTO) et
README.md/docs/BUILD.md (plateformes, providers, compilateurs). Aucun build
crypto lancé, analyse statique seule.
Licence du document : MPL 2.0 — note de travail, exploratoire (non engageante).
Alimente docs/specs/layer-2/air-tls.md (contingence maison, ADR-042 §Contingence).