@@ -21,12 +21,18 @@ const REAL_APPLE_INTERMEDIATE_BASE64_ENCODED = "MIIDFjCCApygAwIBAgIUIsGhRwp0c2nv
2121const REAL_APPLE_SIGNING_CERTIFICATE_BASE64_ENCODED = "MIIEMDCCA7agAwIBAgIQaPoPldvpSoEH0lBrjDPv9jAKBggqhkjOPQQDAzB1MUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTELMAkGA1UECwwCRzYxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTIxMDgyNTAyNTAzNFoXDTIzMDkyNDAyNTAzM1owgZIxQDA+BgNVBAMMN1Byb2QgRUNDIE1hYyBBcHAgU3RvcmUgYW5kIGlUdW5lcyBTdG9yZSBSZWNlaXB0IFNpZ25pbmcxLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zMRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOoTcaPcpeipNL9eQ06tCu7pUcwdCXdN8vGqaUjd58Z8tLxiUC0dBeA+euMYggh1/5iAk+FMxUFmA2a1r4aCZ8SjggIIMIICBDAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFD8vlCNR01DJmig97bB85c+lkGKZMHAGCCsGAQUFBwEBBGQwYjAtBggrBgEFBQcwAoYhaHR0cDovL2NlcnRzLmFwcGxlLmNvbS93d2RyZzYuZGVyMDEGCCsGAQUFBzABhiVodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLXd3ZHJnNjAyMIIBHgYDVR0gBIIBFTCCAREwggENBgoqhkiG92NkBQYBMIH+MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eS8wHQYDVR0OBBYEFCOCmMBq//1L5imvVmqX1oCYeqrMMA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG92NkBgsBBAIFADAKBggqhkjOPQQDAwNoADBlAjEAl4JB9GJHixP2nuibyU1k3wri5psGIxPME05sFKq7hQuzvbeyBu82FozzxmbzpogoAjBLSFl0dZWIYl2ejPV+Di5fBnKPu8mymBQtoE/H2bES0qAs8bNueU3CBjjh1lwnDsI=" ;
2222
2323const EFFECTIVE_DATE = new Date ( 1681312846000 ) ; // April 2023
24+
25+ const CLOCK_DATE = 41231
2426class SignedJWTVerifierTest extends SignedDataVerifier {
2527 effectiveDate = EFFECTIVE_DATE
2628 async testVerifyCertificateChain ( trustedRoots : X509Certificate [ ] , leaf : string , intermediate : string ) : Promise < KeyObject > {
2729 return await this . verifyCertificateChain ( trustedRoots , new X509Certificate ( Buffer . from ( leaf , 'base64' ) ) , new X509Certificate ( Buffer . from ( intermediate , 'base64' ) ) , this . effectiveDate )
2830 }
2931
32+ public async verifyCertificateChainWithoutCaching ( trustedRoots : X509Certificate [ ] , leaf : X509Certificate , intermediate : X509Certificate , effectiveDate : Date ) : Promise < KeyObject > {
33+ return await super . verifyCertificateChainWithoutCaching ( trustedRoots , leaf , intermediate , effectiveDate )
34+ }
35+
3036 getRootCertificates ( ) {
3137 return this . rootCertificates
3238 }
@@ -113,6 +119,62 @@ describe("Chain Verification Checks", () => {
113119 }
114120 assert ( false )
115121 } )
122+
123+ it ( 'should cache OCSP responses' , async ( ) => {
124+ jest . useFakeTimers ( )
125+ jest . setSystemTime ( CLOCK_DATE )
126+ const verifier = new SignedJWTVerifierTest ( [ Buffer . from ( ROOT_CA_BASE64_ENCODED , 'base64' ) ] , true , Environment . PRODUCTION , "com.example" , 1234 ) ;
127+ let spy = jest . spyOn ( verifier , 'verifyCertificateChainWithoutCaching' ) . mockImplementation ( ( _ , _2 , _3 , _4 ) => Promise . resolve ( new X509Certificate ( Buffer . from ( LEAF_CERT_BASE64_ENCODED , 'base64' ) ) . publicKey ) ) ;
128+ await verifier . testVerifyCertificateChain ( verifier . getRootCertificates ( ) , LEAF_CERT_BASE64_ENCODED , INTERMEDIATE_CA_BASE64_ENCODED )
129+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
130+ jest . setSystemTime ( CLOCK_DATE + 1_000 ) // 1 second
131+ await verifier . testVerifyCertificateChain ( verifier . getRootCertificates ( ) , LEAF_CERT_BASE64_ENCODED , INTERMEDIATE_CA_BASE64_ENCODED )
132+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
133+ jest . runOnlyPendingTimers ( )
134+ jest . useRealTimers ( )
135+ } )
136+
137+ it ( 'should cache OCSP responses for a limited time' , async ( ) => {
138+ jest . useFakeTimers ( )
139+ jest . setSystemTime ( CLOCK_DATE )
140+ const verifier = new SignedJWTVerifierTest ( [ Buffer . from ( ROOT_CA_BASE64_ENCODED , 'base64' ) ] , true , Environment . PRODUCTION , "com.example" , 1234 ) ;
141+ let spy = jest . spyOn ( verifier , 'verifyCertificateChainWithoutCaching' ) . mockImplementation ( ( _ , _2 , _3 , _4 ) => Promise . resolve ( new X509Certificate ( Buffer . from ( LEAF_CERT_BASE64_ENCODED , 'base64' ) ) . publicKey ) ) ;
142+ await verifier . testVerifyCertificateChain ( verifier . getRootCertificates ( ) , LEAF_CERT_BASE64_ENCODED , INTERMEDIATE_CA_BASE64_ENCODED )
143+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
144+ jest . setSystemTime ( CLOCK_DATE + 15 * 60 * 1_000 ) // 15 minutes
145+ await verifier . testVerifyCertificateChain ( verifier . getRootCertificates ( ) , LEAF_CERT_BASE64_ENCODED , INTERMEDIATE_CA_BASE64_ENCODED )
146+ expect ( spy ) . toHaveBeenCalledTimes ( 2 ) ;
147+ jest . runOnlyPendingTimers ( )
148+ jest . useRealTimers ( )
149+ } )
150+
151+ it ( 'should not return cached OCSP responses for a different chain' , async ( ) => {
152+ jest . useFakeTimers ( )
153+ jest . setSystemTime ( CLOCK_DATE )
154+ const verifier = new SignedJWTVerifierTest ( [ Buffer . from ( ROOT_CA_BASE64_ENCODED , 'base64' ) ] , true , Environment . PRODUCTION , "com.example" , 1234 ) ;
155+ let spy = jest . spyOn ( verifier , 'verifyCertificateChainWithoutCaching' ) . mockImplementation ( ( _ , _2 , _3 , _4 ) => Promise . resolve ( new X509Certificate ( Buffer . from ( LEAF_CERT_BASE64_ENCODED , 'base64' ) ) . publicKey ) ) ;
156+ await verifier . testVerifyCertificateChain ( verifier . getRootCertificates ( ) , LEAF_CERT_BASE64_ENCODED , INTERMEDIATE_CA_BASE64_ENCODED )
157+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
158+ jest . setSystemTime ( CLOCK_DATE + 15 * 60 * 1_000 ) // 15 minutes
159+ await verifier . testVerifyCertificateChain ( verifier . getRootCertificates ( ) , REAL_APPLE_SIGNING_CERTIFICATE_BASE64_ENCODED , REAL_APPLE_INTERMEDIATE_BASE64_ENCODED )
160+ expect ( spy ) . toHaveBeenCalledTimes ( 2 ) ;
161+ jest . runOnlyPendingTimers ( )
162+ jest . useRealTimers ( )
163+ } )
164+
165+ it ( 'should not return cached OCSP responses for a slightly different chain' , async ( ) => {
166+ jest . useFakeTimers ( )
167+ jest . setSystemTime ( CLOCK_DATE )
168+ const verifier = new SignedJWTVerifierTest ( [ Buffer . from ( ROOT_CA_BASE64_ENCODED , 'base64' ) ] , true , Environment . PRODUCTION , "com.example" , 1234 ) ;
169+ let spy = jest . spyOn ( verifier , 'verifyCertificateChainWithoutCaching' ) . mockImplementation ( ( _ , _2 , _3 , _4 ) => Promise . resolve ( new X509Certificate ( Buffer . from ( LEAF_CERT_BASE64_ENCODED , 'base64' ) ) . publicKey ) ) ;
170+ await verifier . testVerifyCertificateChain ( verifier . getRootCertificates ( ) , LEAF_CERT_BASE64_ENCODED , INTERMEDIATE_CA_BASE64_ENCODED )
171+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
172+ jest . setSystemTime ( CLOCK_DATE + 15 * 60 * 1_000 ) // 15 minutes
173+ await verifier . testVerifyCertificateChain ( verifier . getRootCertificates ( ) , LEAF_CERT_BASE64_ENCODED , REAL_APPLE_INTERMEDIATE_BASE64_ENCODED )
174+ expect ( spy ) . toHaveBeenCalledTimes ( 2 ) ;
175+ jest . runOnlyPendingTimers ( )
176+ jest . useRealTimers ( )
177+ } )
116178} )
117179
118180describe ( "Decoding checks" , ( ) => {
0 commit comments