# TODO - Conformité PAdES Complète (Niveau AES eIDAS)

## 🎯 Objectif

Améliorer la conformité de la signature Odentas Sign pour atteindre le niveau **AES (Advanced Electronic Signature)** selon le règlement eIDAS et la norme ETSI TS 102 778 (PAdES).

---

## 🔴 Problèmes détectés par le validateur EU DSS

### 1. **FORMAT_FAILURE: PDF-NOT-ETSI au lieu de PAdES-BASELINE-B**

**Statut actuel :** ❌ Non conforme  
**Impact :** La signature n'est pas reconnue comme PAdES standard  
**Priorité :** 🔥 CRITIQUE

**Erreurs spécifiques :**
- `The signed attribute: 'signing-certificate' is absent!`
- `The /ByteRange dictionary is not consistent!`
- `The reference data object is not intact!`

---

### 2. **Attribut signing-certificate-v2 manquant**

**Description :**  
L'attribut `signing-certificate-v2` (OID: 1.2.840.113549.1.9.16.2.47) est **obligatoire** selon ETSI TS 102 778-3 pour PAdES-BASELINE-B.

**Ce qu'il contient :**
- Hash SHA-256 du certificat de signature
- IssuerSerial du certificat

**Modifications nécessaires :**

#### Fichier : `lambda-odentas-pades-sign/helpers/pades.js`

```javascript
// 1. Ajouter l'OID (DÉJÀ FAIT)
const OID_ATTR_SIGNING_CERTIFICATE_V2 = '1.2.840.113549.1.9.16.2.47';

// 2. Modifier la signature de buildSignedAttributesDigest
export function buildSignedAttributesDigest(pdfWithRevision, byteRange, signingTime, signerCertDer) {
  // ... code existant ...
  
  // Calculer le hash du certificat
  const certHash = crypto.createHash('sha256').update(signerCertDer).digest();
  
  // Construire ESSCertIDv2
  const essCertIDv2 = new asn1js.Sequence({
    value: [
      new asn1js.Sequence({ // hashAlgorithm
        value: [
          new asn1js.ObjectIdentifier({ value: '2.16.840.1.101.3.4.2.1' }), // SHA-256
        ]
      }),
      new asn1js.OctetString({ valueHex: certHash }), // certHash
      new asn1js.Sequence({ // issuerSerial
        value: [
          // Extraire issuer et serial du certificat
          signerCert.issuer,
          signerCert.serialNumber
        ]
      })
    ]
  });
  
  // Construire SigningCertificateV2
  const signingCertV2 = new asn1js.Sequence({
    value: [
      new asn1js.Sequence({
        value: [essCertIDv2]
      })
    ]
  });
  
  const attrSigningCertV2 = new Attribute({
    type: OID_ATTR_SIGNING_CERTIFICATE_V2,
    values: [signingCertV2]
  });
  
  // Ajouter dans signedAttrs
  const signedAttrsForDigest = new asn1js.Set({
    value: [
      attrContentType.toSchema(),
      attrSigningTime.toSchema(),
      attrMessageDigest.toSchema(),
      attrSigningCertV2.toSchema() // ← NOUVEAU
    ]
  });
  
  // ...
  
  return {
    signedAttrs: [attrContentType, attrSigningTime, attrMessageDigest, attrSigningCertV2],
    signedAttrsDigest,
    byteRange,
    pdfDigest
  };
}
```

#### Fichier : `lambda-odentas-pades-sign/index.js`

```javascript
// Passer le certificat à buildSignedAttributesDigest
const {
  signedAttrs,
  signedAttrsDigest,
  pdfDigest
} = pades.buildSignedAttributesDigest(
  pdfWithRevision, 
  byteRange, 
  signingTime,
  chainPem // ← Passer la chaîne de certificats
);
```

**Tests à effectuer :**
- [ ] Rebuild Lambda avec modifications
- [ ] Tester génération de signature
- [ ] Valider sur https://ec.europa.eu/digital-building-blocks/DSS/webapp-demo/validation
- [ ] Vérifier que `signing-certificate-v2` apparaît dans les attributs signés

---

### 3. **ByteRange inconsistant**

**Problème actuel :**
```
Document ByteRange : [0, 30578, 96114, 647]
```

Le dernier segment (647 bytes) semble trop court. Le ByteRange devrait couvrir **tout le document** sauf le placeholder de signature.

**Formule correcte :**
```
ByteRange = [0, a, b, c]
où:
- a = position de /Contents <
- b = position après le placeholder
- c = taille du fichier - b
```

**Vérification à ajouter :**

```javascript
// Dans finalizePdfWithCms()
const fileSize = finalPdf.length;
const expectedEnd = byteRange[2] + byteRange[3];

if (expectedEnd !== fileSize) {
  console.warn(`⚠️ ByteRange ne couvre pas tout le fichier!`);
  console.warn(`  - Taille fichier: ${fileSize}`);
  console.warn(`  - ByteRange end: ${expectedEnd}`);
  console.warn(`  - Différence: ${fileSize - expectedEnd} bytes`);
}
```

**Cause possible :**
- Le placeholder de signature est trop grand (65536 bytes)
- Des bytes supplémentaires sont ajoutés après la signature
- Le calcul du ByteRange dans `preparePdfWithPlaceholder()` est incorrect

**Tests à effectuer :**
- [ ] Vérifier la taille du placeholder vs taille réelle du CMS
- [ ] Ajuster la taille du placeholder si nécessaire
- [ ] S'assurer que `byteRange[3]` va jusqu'à EOF

---

### 4. **SubFilter doit être /ETSI.CAdES.detached**

**Statut actuel :** ✅ Déjà correct dans le code

```javascript
/SubFilter /ETSI.CAdES.detached
```

Pas de modification nécessaire.

---

## 🟡 Améliorations optionnelles (pour AES complet)

### 5. **Ajouter l'attribut content-hints (optionnel)**

Pour indiquer le type de contenu signé :

```javascript
const OID_ATTR_CONTENT_HINTS = '1.2.840.113549.1.9.16.2.4';

const attrContentHints = new Attribute({
  type: OID_ATTR_CONTENT_HINTS,
  values: [
    new asn1js.Sequence({
      value: [
        new asn1js.UTF8String({ value: 'Contrat de travail' }),
        new asn1js.ObjectIdentifier({ value: OID_ID_DATA })
      ]
    })
  ]
});
```

---

### 6. **Intégrer le timestamp TSA dans le CMS (pour PAdES-LT)**

**Objectif :** Passer de PAdES-B à PAdES-LT (Long Term)

**Modifications nécessaires :**
1. Obtenir le TSA **après** la signature
2. L'ajouter comme **unsignedAttrs** dans le SignerInfo

```javascript
// Dans buildCmsSignedData(), après avoir créé signerInfo
const tsaResponse = await fetch(process.env.TSA_URL, {
  method: 'POST',
  headers: { 'Content-Type': 'application/timestamp-query' },
  body: createTSQ(signatureBytes)
});

const tsr = await tsaResponse.arrayBuffer();

signerInfo.unsignedAttrs = new SignedAndUnsignedAttributes({
  type: 1, // unsigned
  attributes: [
    new Attribute({
      type: '1.2.840.113549.1.9.16.2.14', // id-aa-signatureTimeStampToken
      values: [new asn1js.OctetString({ valueHex: Buffer.from(tsr) })]
    })
  ]
});
```

**Impact :**
- ✅ Résout le problème "L'heure de signature est déterminée à partir de l'horloge"
- ✅ Conforme PAdES-LT (Long Term validation)
- ✅ Plus robuste pour archivage long terme

---

## 📋 Plan d'action

### Phase 1 : Conformité PAdES-BASELINE-B (PRIORITÉ HAUTE)
- [ ] Implémenter `signing-certificate-v2`
- [ ] Corriger le ByteRange
- [ ] Tester avec validateur EU DSS
- [ ] Vérifier que format = "PAdES-BASELINE-B"

**Temps estimé :** 4-6 heures  
**Complexité :** Moyenne

---

### Phase 2 : Intégration TSA dans CMS (PRIORITÉ MOYENNE)
- [ ] Ajouter unsignedAttrs avec timestamp
- [ ] Modifier workflow pour obtenir TSA après signature
- [ ] Tester conformité PAdES-LT

**Temps estimé :** 6-8 heures  
**Complexité :** Élevée

---

### Phase 3 : Certification (PRIORITÉ BASSE)
- [ ] Décider si viser AES ou rester en SES
- [ ] Évaluer coût certification ISO 27001
- [ ] Audit externe du système

**Temps estimé :** Plusieurs mois  
**Coût estimé :** 10-20k€

---

## 🧪 Tests de validation

### Validateur officiel EU DSS
https://ec.europa.eu/digital-building-blocks/DSS/webapp-demo/validation

**Critères de succès :**
- [ ] Format = "PAdES-BASELINE-B" (au lieu de "PDF-NOT-ETSI")
- [ ] Indication = "TOTAL_PASSED" (au lieu de "TOTAL_FAILED")
- [ ] Aucune erreur "FORMAT_FAILURE"
- [ ] Attribut `signing-certificate-v2` présent

### Outils complémentaires
- [ ] Adobe Acrobat Reader (vérification visuelle)
- [ ] pdfsig (Poppler) - validation technique
- [ ] VeraPDF - conformité PDF/A

---

## 📚 Références

### Standards eIDAS
- **Règlement eIDAS** : (UE) N°910/2014
- **ETSI TS 102 778** : PAdES (PDF Advanced Electronic Signatures)
- **ETSI TS 119 172** : PAdES Baseline Profile
- **RFC 5035** : ESS - Enhanced Security Services (ESSCertIDv2)

### Documentation technique
- **PKI.js** : https://github.com/PeculiarVentures/PKI.js
- **ASN.1 JavaScript** : https://github.com/PeculiarVentures/ASN1.js
- **PDF Reference 1.7** : https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf

### Validateurs
- **EU DSS Validator** : https://ec.europa.eu/digital-building-blocks/DSS/webapp-demo/validation
- **ANSSI** : https://www.ssi.gouv.fr/
- **VeraPDF** : https://verapdf.org/

---

## 💡 Notes importantes

1. **Ne pas casser l'existant** : Les signatures actuelles fonctionnent, gardons la compatibilité
2. **Tests progressifs** : Valider chaque modification avec le validateur EU
3. **Backup des certificats** : Les clés privées sont critiques, bien les sauvegarder
4. **Documentation** : Documenter chaque changement pour audit futur

---

## ✅ Statut actuel

**Niveau eIDAS :** SES (Signature Électronique Simple)  
**Format signature :** PDF-NOT-ETSI (non conforme PAdES)  
**Validation EU DSS :** ❌ TOTAL_FAILED  

**Objectif :** PAdES-BASELINE-B conforme, validation ✅ TOTAL_PASSED

---

_Dernière mise à jour : 28 octobre 2025_
_Créé par : GitHub Copilot_
