validateAllSignatures method

Future<PdfSignatureValidationReport> validateAllSignatures(
  1. Uint8List pdfBytes, {
  2. List<String>? trustedRootsPem,
  3. TrustedRootsProvider? trustedRootsProvider,
  4. List<TrustedRootsProvider>? trustedRootsProviders,
  5. bool strictRevocation = false,
  6. bool fetchCrls = false,
  7. bool fetchOcsp = false,
  8. bool validateTemporal = false,
  9. bool temporalUseSigningTime = false,
  10. DateTime? validationTime,
  11. bool temporalExpiredNeedsLtv = true,
  12. PdfRevocationDataProvider? revocationDataProvider,
  13. PdfHttpFetcherBase? certificateFetcher,
  14. bool includeCertificates = false,
  15. bool includeSignatureFields = true,
  16. PdfSignaturePreparedContext? preparedContext,
})

Valida todas as assinaturas do PDF.

Implementation

Future<PdfSignatureValidationReport> validateAllSignatures(
  Uint8List pdfBytes, {
  List<String>? trustedRootsPem,
  TrustedRootsProvider? trustedRootsProvider,
  List<TrustedRootsProvider>? trustedRootsProviders,
  bool strictRevocation = false,
  bool fetchCrls = false,
  bool fetchOcsp = false,
  bool validateTemporal = false,
  bool temporalUseSigningTime = false,
  DateTime? validationTime,
  bool temporalExpiredNeedsLtv = true,
  PdfRevocationDataProvider? revocationDataProvider,
  PdfHttpFetcherBase? certificateFetcher,
  bool includeCertificates = false,
  bool includeSignatureFields = true,
  PdfSignaturePreparedContext? preparedContext,
}) async {
  final roots = await _collectTrustedRoots(
    trustedRootsPem: trustedRootsPem,
    trustedRootsProvider: trustedRootsProvider,
    trustedRootsProviders: trustedRootsProviders,
  );

  final parseCache = _resolveParseCache(
    pdfBytes,
    includeSignatureFields: includeSignatureFields,
    includeSignatureContents: false,
    baseContext: preparedContext,
  );
  final quickInfo = parseCache.quickInfo;
  final permissionP = quickInfo.docMdpPermissionP;
  final fieldByRange = parseCache.fieldByRange;
  final ranges = parseCache.ranges;
  final results = <PdfSignatureInfoReport>[];

  for (var i = 0; i < ranges.length; i++) {
    final range = ranges[i];
    final fieldInfo =
        includeSignatureFields ? fieldByRange[_byteRangeKey(range)] : null;
    final intact = _isValidByteRange(pdfBytes.length, range);
    var cmsValid = false;
    var digestValid = false;
    bool? certValid;
    var revocation = const PdfSignatureRevocationInfo();
    String? message;
    DateTime? signingTime;
    String? signaturePolicyOid;
    List<String>? signedAttrsOids;
    PdfSignatureSignedAttrsReport? signedAttrsReport;

    if (!intact) {
      results.add(PdfSignatureInfoReport(
        signatureIndex: i,
        cmsValid: false,
        digestValid: false,
        intact: false,
        docMdp: _buildDocMdpInfo(i, permissionP),
        revocation: revocation,
        signatureField: fieldInfo,
        signatureDictionaryPresent: fieldInfo?.signatureDictionaryPresent,
        signingTime: null,
        signaturePolicyOid: null,
        signedAttrsOids: null,
        signedAttrsReport: null,
        certificates: null,
        signerCertificate: null,
        chainTrusted: null,
        chainErrors: null,
        certValid: null,
        validationStatus: PdfSignatureValidationStatus.rejected,
        message: 'ByteRange inconsistente.',
      ));
      continue;
    }

    final contents = parseCache.signatureContentsAt(i) ??
        _extractContentsFromByteRange(pdfBytes, range);
    if (contents == null || contents.isEmpty) {
      results.add(PdfSignatureInfoReport(
        signatureIndex: i,
        cmsValid: false,
        digestValid: false,
        intact: true,
        docMdp: _buildDocMdpInfo(i, permissionP),
        revocation: revocation,
        signatureField: fieldInfo,
        signatureDictionaryPresent: fieldInfo?.signatureDictionaryPresent,
        signingTime: null,
        signaturePolicyOid: null,
        signedAttrsOids: null,
        signedAttrsReport: null,
        certificates: null,
        signerCertificate: null,
        chainTrusted: null,
        chainErrors: null,
        certValid: null,
        validationStatus: PdfSignatureValidationStatus.rejected,
        message: 'Conteúdo de assinatura ausente ou inválido.',
      ));
      continue;
    }

    final certInfos =
        includeCertificates ? _extractCertificatesInfo(contents) : null;
    final signerCertInfo =
        includeCertificates ? _extractSignerCertificateInfo(contents) : null;

    cmsValid = await _verifyCmsSignature(contents);
    signingTime = _extractSigningTimeFromCms(contents);
    if (signingTime == null && fieldInfo?.signingTimeRaw != null) {
      signingTime = _parsePdfDate(fieldInfo!.signingTimeRaw!);
    }
    if (signingTime == null) {
      final raw = _scanSigningTimeNearByteRange(pdfBytes, range);
      if (raw != null) {
        signingTime = _parsePdfDate(raw);
      }
    }
    signaturePolicyOid = _extractSignaturePolicyOid(contents);
    signedAttrsOids = _extractSignedAttrsOids(contents);
    signedAttrsReport = _buildSignedAttrsReport(signedAttrsOids);
    final digestOid = _extractDigestOid(contents);
    final contentDigest =
        _computeByteRangeDigestForOid(pdfBytes, range, digestOid);
    final messageDigest = _extractMessageDigest(contents);
    if (messageDigest != null) {
      digestValid = _listEquals(contentDigest, messageDigest);
    } else {
      message = 'Atributo messageDigest não encontrado no CMS.';
    }

    _ChainResult? chainResult;
    if (roots.isNotEmpty) {
      chainResult = await _buildCertificateChainFromCms(
        cmsBytes: contents,
        roots: roots,
        fetcher: certificateFetcher,
      );
      certValid = chainResult.trusted;
      if (includeCertificates && certInfos != null) {
        _mergeCertificateInfos(certInfos, chainResult.chain);
      }
    }

    var chainTrusted = certValid;
    if (chainTrusted == false &&
        signaturePolicyOid != null &&
        cmsValid &&
        digestValid) {
      chainTrusted = true;
    }
    final chainErrors = certValid == false
        ? const <String>['Signer certificate not trusted.']
        : null;

    if (revocationDataProvider != null && (fetchCrls || fetchOcsp)) {
      revocation = await _checkRevocation(
        cmsBytes: contents,
        roots: roots,
        fetchCrls: fetchCrls,
        fetchOcsp: fetchOcsp,
        provider: revocationDataProvider,
      );

      if (revocation.crlRevoked || revocation.ocspRevoked) {
        certValid = false;
        message = 'Certificado revogado.';
      } else if (strictRevocation && revocation.revocationUnknown) {
        certValid = false;
        message = 'Revogação não comprovada.';
      }
    }

    final temporalResult = _evaluateTemporalStatus(
      validateTemporal: validateTemporal,
      temporalUseSigningTime: temporalUseSigningTime,
      validationTime: validationTime,
      temporalExpiredNeedsLtv: temporalExpiredNeedsLtv,
      signerCertInfo: signerCertInfo,
      signingTime: signingTime,
    );

    if (temporalResult.certValidOverride != null) {
      certValid = temporalResult.certValidOverride;
    }

    final status = _deriveValidationStatus(
      intact: true,
      cmsValid: cmsValid,
      digestValid: digestValid,
      chainTrusted: chainTrusted,
      certValid: certValid,
      temporalStatusOverride: temporalResult.statusOverride,
    );

    if (temporalResult.messageOverride != null) {
      message = temporalResult.messageOverride;
    }

    results.add(PdfSignatureInfoReport(
      signatureIndex: i,
      cmsValid: cmsValid,
      digestValid: digestValid,
      intact: true,
      docMdp: _buildDocMdpInfo(i, permissionP),
      revocation: revocation,
      signatureField: fieldInfo,
      signatureDictionaryPresent: fieldInfo?.signatureDictionaryPresent,
      signingTime: signingTime,
      signaturePolicyOid: signaturePolicyOid,
      signedAttrsOids: signedAttrsOids,
      signedAttrsReport: signedAttrsReport,
      certificates: certInfos,
      signerCertificate: signerCertInfo,
      chainTrusted: chainTrusted,
      chainErrors: chainErrors,
      certValid: certValid,
      validationStatus: status,
      message: message,
    ));
  }

  return PdfSignatureValidationReport(signatures: results);
}