parseEuTrustedList function
({List<PdfTrustListEntry> entries, DateTime nextUpdate})
parseEuTrustedList(
- Uint8List bytes,
- PdfTrustListPointer pointer, {
- DateTime? now,
Parses and verifies one Member State list against its LOTL pointer:
the list's signature must verify with one of the pointer's certificates,
and it must not be expired at now. Returns the active qualified CA
service certificates and the list's NextUpdate.
Implementation
({List<PdfTrustListEntry> entries, DateTime nextUpdate}) parseEuTrustedList(
Uint8List bytes, PdfTrustListPointer pointer,
{DateTime? now}) {
final doc = XmlLiteDocument.parse(bytes);
final check = verifyEnvelopedXmlSignature(doc);
if (!check.valid) {
throw FormatException('signature is invalid: ${check.problems}');
}
final signer = _fingerprint(check.signer!.der);
if (!pointer.signers.any((c) => _fingerprint(c.der) == signer)) {
throw const FormatException(
'signed by a certificate the LOTL does not list for this territory');
}
final nextUpdate = _nextUpdateOf(doc.root.child('SchemeInformation'));
_requireCurrent('the ${pointer.territory} trusted list', nextUpdate,
(now ?? DateTime.now()).toUtc());
final entries = <PdfTrustListEntry>[];
for (final service in doc.root.descendantsNamed('TSPService')) {
final info = service.child('ServiceInformation');
if (info == null) continue;
final type = info.child('ServiceTypeIdentifier')?.text.trim() ?? '';
final status = info.child('ServiceStatus')?.text.trim() ?? '';
if (!_anchorServiceTypes.contains(type) ||
!_activeStatuses.contains(status)) {
continue;
}
final name = info
.child('ServiceName')
?.childrenNamed('Name')
.map((n) => n.text.trim())
.firstOrNull ??
'';
for (final c in info
.child('ServiceDigitalIdentity')
?.descendantsNamed('X509Certificate') ??
const <XmlLiteElement>[]) {
try {
final der = base64.decode(c.text.replaceAll(RegExp(r'\s'), ''));
X509Certificate.parse(der); // keep only parsable certificates
entries.add(PdfTrustListEntry(
territory: pointer.territory,
serviceName: name,
serviceType: type,
certificate: der,
));
} on Object {
// skip a malformed service certificate
}
}
}
return (entries: entries, nextUpdate: nextUpdate!);
}