Skip to content

fix(oe-rfc3161-asn1,oe-tsa-core,oe-config): genTime porte sa fraction de seconde - #53

Closed
PhilippeVienne wants to merge 2 commits into
feat/tsa-audit-token-serialfrom
fix/tsa-gen-time-fraction
Closed

PhilippeVienne wants to merge 2 commits into
feat/tsa-audit-token-serialfrom
fix/tsa-gen-time-fraction

Conversation

@PhilippeVienne

Copy link
Copy Markdown
Contributor

Objet

Constat T-1 de l'audit de préparation eIDAS/WebTrust du 2026-09-25
(docs/AUDIT_2026_09_25.md, EN 319 421 TIS-7.7.1-05/-06, TIS-7.7.2-03 ;
EN 319 422 §5.2.2) : genTime était construit à la seconde près, alors que
le jeton annonce accuracy = 1 s (CPS B.1). En ajoutant la dérive tolérée
(OPENEIDAS_TIME_MAX_OFFSET, 500 ms par défaut), l'écart réel entre
genTime et l'UTC pouvait atteindre environ 1,5 s.

der::asn1::GeneralizedTime suit délibérément le profil RFC 5280
(certificats X.509), qui interdit les fractions de seconde — RFC 3161 ne
reprend pas cette restriction pour TSTInfo.genTime. Ce PR ajoute donc à
oe-rfc3161-asn1 un module gen_time qui encode/décode le DER à la main
(forme canonique X.690 §11.7), et change le type de TstInfo.gen_time en
der::asn1::Any pour l'accueillir.

Compatibilité préservée : la forme canonique reste octet pour octet
identique au corpus historique (tests/fixtures/rfc3161/, sans fraction) —
round_trips_every_response_in_the_corpus continue de passer sans y
toucher, et openssl ts -verify accepte toujours les jetons produits (avec
ou sans fraction).

oe-tsa-core encode genTime avec 3 décimales (milliseconde, minimum
recommandé par l'audit). oe-config refuse de démarrer si
OPENEIDAS_ACCURACY < OPENEIDAS_TIME_MAX_OFFSET + résolution de genTime
(1 ms) — deuxième volet de la recommandation T-1.

oe-crosstsa, seul consommateur externe de gen_time (contreseing), migré
vers le nouveau type.

Empilée sur #52 (J-3) : les deux touchent des lignes adjacentes d'oe-tsa-core.
Base : feat/tsa-audit-token-serial (#52), pas dev.

Vérifications

  • cargo fmt --check : silencieux.
  • cargo clippy --workspace --all-targets -- -D warnings : silencieux, sur
    toolchain 1.97 (local) et 1.98.1 (CI).
  • cargo test --workspace (avec OE_CASTORE_TEST_DSN) : tout au vert,
    round-trip du corpus RFC 3161 et interopérabilité openssl comprises.
  • cargo audit --ignore RUSTSEC-2023-0071 : aucune vulnérabilité.
  • Nouveaux tests oe-rfc3161-asn1::gen_time : fraction tronquée sans zéros
    de fin (forme canonique DER), forme sans fraction (compatibilité
    historique), rejet d'un tag ou d'un contenu invalide.
  • Nouveau test oe-tsa-core : genTime porte la fraction attendue (horloge
    de test à précision connue, .123Z pour 123 456 789 ns tronqués à 3
    chiffres). Mutation : en repassant digits à 0, le test échoue bien.
  • Nouveau test oe-config : refus de démarrer si
    accuracy < max_offset + résolution. Mutation : en retirant la garde, le
    test échoue bien.

Revue humaine obligatoire

Voir PROVENANCE.md. Chaque case est cochée par le
contributeur humain qui valide la PR, après l'avoir fait lui-même.

  • Revue d'architecture validée par l'humain
  • Code relu et tests unitaires/intégration vérifiés localement
  • Absence de dépendances tierces incompatibles avec la double licence EUPL-1.2 / AGPL-3.0 (make licenses)
  • Validation de l'apport intellectuel et de la paternité humaine sur la modification

Assistance par IA

  • Cette PR a été produite avec l'assistance de Claude Code : les commits concernés portent la remorque Co-authored-by: Claude <noreply@anthropic.com>, auteur et committer restent humains, et scripts/provenance.py archive a été lancé
  • Cette PR a été écrite sans assistance par IA

PhilippeVienne and others added 2 commits September 26, 2026 13:40
…ertificat

Constat J-3 de l'audit du 2026-09-25 (EN 319 421 OVR-7.13-05) : le journal
consignait, pour chaque jeton, la série du certificat de la TSU — identique
d'un appel à l'autre — au lieu de celle du TSTInfo réellement émis. Après un
incident (horloge fautive, clé suspecte), il était impossible d'identifier
quels jetons avaient été affectés.

oe-tsa-core : `timestamp.granted` journalise maintenant la série du jeton
(`tst_info.serial_number`, tirée à chaque appel), l'OID et la valeur du
`messageImprint` soumis, et l'empreinte du certificat de la TSU — tous tirés
du jeton et de la requête réellement traités, pas reconstruits après coup.
`gen_time` passe au format RFC 3339 (avec fraction si non nulle) plutôt que
le format `Debug` de `time::OffsetDateTime`. L'ordre journal-avant-signature
(§15 étape 2b) est inchangé : ces valeurs sont déjà connues avant de signer,
aucune raison de journaliser après.

tsa-server : ajoute aussi l'état de l'horloge (traçabilité, écart, sources)
au moment de l'émission, dernier volet de la recommandation de l'audit.
`oe_tsa_core::Clock` reste délibérément découplé d'`oe-timesource` (décision
déjà prise, voir la note de module) : l'enrichissement se fait dans un
`Recorder` englobant côté `tsa-server`, qui connaît déjà les deux, sans que
`oe-tsa-core` ait à en savoir quoi que ce soit.

- oe-tsa-core : nouveau test unitaire prouvant que deux jetons produisent
  deux séries distinctes au journal, et que la série journalisée n'est
  jamais celle du certificat (recommandation explicite de l'audit). Testé
  par mutation.
- tsa-server : nouveaux tests unitaires pour `describe_clock_status` et pour
  la présence effective du champ `horloge` dans le journal écrit. Testé par
  mutation.

Co-authored-by: Claude <noreply@anthropic.com>
… de seconde

Constat T-1 de l'audit du 2026-09-25 (EN 319 421 TIS-7.7.1-05/-06,
TIS-7.7.2-03 ; EN 319 422 §5.2.2) : `genTime` était construit à la seconde
près, alors que le jeton annonce `accuracy = 1 s`. En ajoutant la dérive
tolérée (OPENEIDAS_TIME_MAX_OFFSET, 500 ms par défaut), l'écart réel entre
`genTime` et l'UTC pouvait atteindre environ 1,5 s.

`der::asn1::GeneralizedTime` suit délibérément le profil RFC 5280
(certificats X.509), qui interdit les fractions de seconde — RFC 3161 ne
reprend pas cette restriction pour `TSTInfo.genTime`. Impossible donc de
simplement ajuster ce type : `oe-rfc3161-asn1` gagne un module `gen_time`
qui encode/décode le DER à la main (forme canonique X.690 §11.7 : pas de
zéros de fin, fraction absente si nulle), et `TstInfo.gen_time` change de
type (`der::asn1::Any`, qui porte l'octet brut) pour l'accueillir.

Cette forme canonique reste octet pour octet compatible avec le corpus
historique (`tests/fixtures/rfc3161/`, sans fraction) : le test de
round-trip existant (`round_trips_every_response_in_the_corpus`) continue
de passer sans y toucher, et `openssl ts -verify` accepte toujours les
jetons produits avec fraction.

`oe-tsa-core` encode désormais `genTime` avec 3 décimales (milliseconde,
minimum recommandé par l'audit). `oe-config` refuse de démarrer si
`OPENEIDAS_ACCURACY < OPENEIDAS_TIME_MAX_OFFSET + résolution de genTime`
(1 ms) : une exactitude annoncée que la dérive tolérée peut à elle seule
dépasser ne veut rien dire.

`oe-crosstsa`, seul consommateur externe de `TstInfo.gen_time`, adapté au
nouveau type (lit directement la chaîne ASCII, déjà au format attendu).

- oe-rfc3161-asn1 : tests unitaires du nouveau module (fraction tronquée
  sans zéros de fin, forme sans fraction pour compatibilité historique,
  rejet d'un tag ou d'un contenu invalide).
- oe-tsa-core : nouveau test prouvant que `genTime` porte la fraction
  attendue (horloge de test à précision connue). Testé par mutation.
- oe-config : nouveau test prouvant le refus de démarrer sur une exactitude
  trop serrée. Testé par mutation.

Co-authored-by: Claude <noreply@anthropic.com>
@PhilippeVienne

Copy link
Copy Markdown
Contributor Author

Remplacée par #76 (base dev) : GitHub a fermé cette PR à la suppression de sa branche de base après la fusion de #52, et refuse de rebrancher une PR d'une pile.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant