parseEuLotl function
({DateTime? issued, DateTime? nextUpdate, List<PdfTrustListPointer> pointers, int? sequence})
parseEuLotl(})
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,
);
}