36
36
from pyhanko_certvalidator .ltv .poe import POEManager
37
37
from pyhanko_certvalidator .path import QualifiedPolicy , ValidationPath
38
38
from pyhanko_certvalidator .policy_decl import (
39
+ CertRevTrustPolicy ,
39
40
DisallowWeakAlgorithmsPolicy ,
40
41
NonRevokedStatusAssertion ,
42
+ RevocationCheckingPolicy ,
43
+ RevocationCheckingRule ,
41
44
)
42
45
from pyhanko_certvalidator .registry import (
43
46
CertificateRegistry ,
@@ -680,6 +683,77 @@ def test_nist_pkits(test_case: PKITSTestCase):
680
683
)
681
684
682
685
686
+ def nist_revocation_tests ():
687
+ specs = read_pkits_test_params ()
688
+ return [spec for spec in specs if spec .check_revocation ]
689
+
690
+
691
+ class ReturnPredeterminedCRLs (CRLFetcher ):
692
+ def __init__ (self , crls ):
693
+ self .crls = crls
694
+
695
+ def fetched_crls_for_cert (
696
+ self , cert : x509 .Certificate
697
+ ) -> Iterable [crl .CertificateList ]:
698
+ raise KeyError ()
699
+
700
+ def fetched_crls (self ) -> Iterable [crl .CertificateList ]:
701
+ return ()
702
+
703
+ async def fetch (self , cert : x509 .Certificate , * , use_deltas = None ):
704
+ return self .crls
705
+
706
+
707
+ @freeze_time ('2022-05-01' )
708
+ @pytest .mark .parametrize (
709
+ 'test_case' , nist_revocation_tests (), ids = lambda case : str (case .test_info )
710
+ )
711
+ def test_nist_pkits_with_simulated_crl_downloads (test_case : PKITSTestCase ):
712
+
713
+ fetchers = Fetchers (
714
+ ocsp_fetcher = MockOCSPFetcher (),
715
+ crl_fetcher = ReturnPredeterminedCRLs (test_case .crls ),
716
+ cert_fetcher = MockCertFetcher (),
717
+ )
718
+
719
+ # TODO rework failure messages and realign fixtures
720
+ # so we can do message validations here.
721
+ # Also consider having multiple variant runs with
722
+ # slightly different revo policies
723
+ policy = RevocationCheckingPolicy (
724
+ RevocationCheckingRule .CRL_REQUIRED ,
725
+ RevocationCheckingRule .CRL_REQUIRED ,
726
+ )
727
+ context = ValidationContext (
728
+ trust_roots = test_case .roots ,
729
+ other_certs = test_case .other_certs ,
730
+ allow_fetching = True ,
731
+ fetchers = fetchers ,
732
+ revinfo_policy = CertRevTrustPolicy (
733
+ revocation_checking_policy = policy ,
734
+ ),
735
+ # adjust default algo policy to pass NIST tests
736
+ algorithm_usage_policy = DisallowWeakAlgorithmsPolicy (
737
+ weak_hash_algos = {'md2' , 'md5' }, dsa_key_size_threshold = 1024
738
+ ),
739
+ )
740
+
741
+ if test_case .path is None :
742
+ paths = context .path_builder .build_paths (test_case .cert )
743
+ assert 1 == len (paths )
744
+ path : ValidationPath = paths [0 ]
745
+ else :
746
+ path = test_case .path
747
+
748
+ err = test_case .expected_error
749
+ params = test_case .pkix_params
750
+ if err is not None :
751
+ with pytest .raises (err .err_class ):
752
+ validate_path (context , path , parameters = params )
753
+ else :
754
+ validate_path (context , path , parameters = params )
755
+
756
+
683
757
@dataclass (frozen = True )
684
758
class PKITSUserNoticeTestCase :
685
759
test_info : CannedTestInfo
@@ -874,6 +948,13 @@ def test_do_not_fetch_crl_if_cache_sufficient():
874
948
]
875
949
moment = datetime (2020 , 10 , 2 , tzinfo = timezone .utc )
876
950
fetchers = MockFetcherBackend ().get_fetchers ()
951
+
952
+ crl_fetcher = MockCRLFetcher ()
953
+ fetchers = Fetchers (
954
+ ocsp_fetcher = MockOCSPFetcher (),
955
+ crl_fetcher = crl_fetcher ,
956
+ cert_fetcher = MockCertFetcher (),
957
+ )
877
958
context = ValidationContext (
878
959
trust_roots = ca_certs ,
879
960
other_certs = other_certs ,
@@ -884,7 +965,7 @@ def test_do_not_fetch_crl_if_cache_sufficient():
884
965
revocation_mode = 'require' ,
885
966
)
886
967
887
- assert fetchers . crl_fetcher .calls == 0
968
+ assert crl_fetcher .calls == 0
888
969
889
970
paths = context .path_builder .build_paths (cert )
890
971
assert 1 == len (paths )
@@ -963,3 +1044,36 @@ def test_fail_validation_if_required_delta_crl_not_available():
963
1044
path : ValidationPath = paths [0 ]
964
1045
with pytest .raises (InsufficientRevinfoError , match = ".*Delta CRL.*" ):
965
1046
validate_path (context , path )
1047
+
1048
+
1049
+ def test_context_retrieve_all_crls ():
1050
+
1051
+ cert = load_nist_cert ('InvaliddeltaCRLTest4EE.crt' )
1052
+ ca_certs = [load_nist_cert ('TrustAnchorRootCertificate.crt' )]
1053
+ other_certs = [load_nist_cert ('deltaCRLCA1Cert.crt' )]
1054
+ crl1 = load_nist_crl ('deltaCRLCA1CRL.crl' )
1055
+ crl2 = load_nist_crl ('TrustAnchorRootCRL.crl' )
1056
+ crl3 = load_nist_crl ('deltaCRLCA1deltaCRL.crl' )
1057
+ crls = [crl1 , crl2 ]
1058
+
1059
+ context = ValidationContext (
1060
+ trust_roots = ca_certs ,
1061
+ other_certs = other_certs ,
1062
+ crls = crls ,
1063
+ allow_fetching = True ,
1064
+ fetchers = Fetchers (
1065
+ ocsp_fetcher = MockOCSPFetcher (),
1066
+ crl_fetcher = ReturnPredeterminedCRLs ([crl3 ]),
1067
+ cert_fetcher = MockCertFetcher (),
1068
+ ),
1069
+ revocation_mode = "require" ,
1070
+ weak_hash_algos = {'md2' , 'md5' },
1071
+ )
1072
+
1073
+ with pytest .warns (DeprecationWarning ):
1074
+ retrieved_crls = context .retrieve_crls (cert )
1075
+ assert {c .dump () for c in retrieved_crls } == {
1076
+ crl1 .dump (),
1077
+ crl2 .dump (),
1078
+ crl3 .dump (),
1079
+ }
0 commit comments