parseEuLotl function

({DateTime? issued, DateTime? nextUpdate, List<PdfTrustListPointer> pointers, int? sequence}) parseEuLotl(
  1. Uint8List bytes, {
  2. Set<String> pinnedSigners = PdfEuLotl.signerFingerprints,
  3. DateTime? now,
})

Parses and verifies the LOTL: its signature must verify, its signing certificate must be one of pinnedSigners (SHA-256 fingerprints), and it must not be expired at now (NextUpdate + pdfTrustListExpiryGrace). Returns the pointers to the Member State XML lists.

Implementation

({
  List<PdfTrustListPointer> pointers,
  int? sequence,
  DateTime? issued,
  DateTime? nextUpdate
}) parseEuLotl(Uint8List bytes,
    {Set<String> pinnedSigners = PdfEuLotl.signerFingerprints, DateTime? now}) {
  final doc = XmlLiteDocument.parse(bytes);
  final check = verifyEnvelopedXmlSignature(doc);
  if (!check.valid) {
    throw FormatException('LOTL signature is invalid: ${check.problems}');
  }
  if (!pinnedSigners.contains(_fingerprint(check.signer!.der))) {
    throw const FormatException(
        'LOTL is signed by a certificate outside the pinned LOTL signers');
  }
  final scheme = doc.root.child('SchemeInformation');
  final nextUpdate = _nextUpdateOf(scheme);
  _requireCurrent('LOTL', nextUpdate, (now ?? DateTime.now()).toUtc());
  final pointers = <PdfTrustListPointer>[];
  for (final p in scheme
          ?.child('PointersToOtherTSL')
          ?.childrenNamed('OtherTSLPointer') ??
      const <XmlLiteElement>[]) {
    final location = p.child('TSLLocation')?.text.trim() ?? '';
    final territory =
        p.descendantsNamed('SchemeTerritory').firstOrNull?.text.trim() ?? '';
    final mime = p.descendantsNamed('MimeType').firstOrNull?.text.trim() ?? '';
    final uri = Uri.tryParse(location);
    if (uri == null || territory.isEmpty || territory == 'EU') continue;
    final isXml = mime.contains('xml') ||
        (mime.isEmpty &&
            (location.endsWith('.xml') || location.endsWith('.xtsl')));
    if (!isXml) continue;
    final signers = <X509Certificate>[];
    for (final c in p.descendantsNamed('X509Certificate')) {
      try {
        signers.add(X509Certificate.parse(
            base64.decode(c.text.replaceAll(RegExp(r'\s'), ''))));
      } on Object {
        // skip a malformed pointer certificate
      }
    }
    pointers.add(PdfTrustListPointer(territory, uri, signers));
  }
  return (
    pointers: pointers,
    sequence:
        int.tryParse(scheme?.child('TSLSequenceNumber')?.text.trim() ?? ''),
    issued: DateTime.tryParse(
        scheme?.child('ListIssueDateTime')?.text.trim() ?? ''),
    nextUpdate: nextUpdate,
  );
}