From 938de11b3d277087d3a2120957a1b237e091f456 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Sat, 18 Feb 2017 00:39:39 -0500 Subject: [PATCH 001/126] Ability to use TrustKit in non-singleton mode --- .gitignore | 2 +- TrustKit.xcodeproj/project.pbxproj | 70 ++- TrustKit/Pinning/TSKPublicKeyAlgorithm.h | 26 + TrustKit/Pinning/TSKSPKIHashCache.h | 27 + ...{public_key_utils.m => TSKSPKIHashCache.m} | 465 +++++++++--------- TrustKit/Pinning/public_key_utils.h | 46 -- TrustKit/Pinning/ssl_pin_verifier.h | 42 +- TrustKit/Pinning/ssl_pin_verifier.m | 10 +- TrustKit/TSKPinValidatorResult.h | 71 +++ TrustKit/TSKPinningValidator.h | 94 +++- TrustKit/TSKPinningValidator.m | 73 ++- TrustKit/TSKPinningValidatorResult.h | 25 + TrustKit/TSKPinningValidatorResult.m | 29 ++ TrustKit/TrustKit.h | 47 +- TrustKit/TrustKit.m | 310 ++++++------ TrustKit/parse_configuration.m | 4 +- 16 files changed, 784 insertions(+), 557 deletions(-) create mode 100644 TrustKit/Pinning/TSKPublicKeyAlgorithm.h create mode 100644 TrustKit/Pinning/TSKSPKIHashCache.h rename TrustKit/Pinning/{public_key_utils.m => TSKSPKIHashCache.m} (65%) delete mode 100644 TrustKit/Pinning/public_key_utils.h create mode 100644 TrustKit/TSKPinValidatorResult.h create mode 100644 TrustKit/TSKPinningValidatorResult.h create mode 100644 TrustKit/TSKPinningValidatorResult.m diff --git a/.gitignore b/.gitignore index 7763e044..65dbb122 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,4 @@ _site # Jazzy *.tgz - +.DS_Store diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index c8c06f6f..5d93b3e4 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -42,7 +42,6 @@ 8C84CB931D6E0981009B3E7D /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C5D98B21CEFF079008E654B /* parse_configuration.m */; }; 8C84CB941D6E0981009B3E7D /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B2B06AE1B05157400FC749E /* TSKBackgroundReporter.m */; }; 8C84CB951D6E0981009B3E7D /* TSKNSURLSessionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CD5F7481BCB535E005801D8 /* TSKNSURLSessionDelegateProxy.m */; }; - 8C84CB961D6E0981009B3E7D /* public_key_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE9191D1AEA073C002B29AE /* public_key_utils.m */; }; 8C84CB971D6E0981009B3E7D /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C15F99F1B16094D00F06C0E /* TSKPinFailureReport.m */; }; 8C84CB991D6E0981009B3E7D /* TSKNSURLConnectionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CD5F7301BC5ED4A005801D8 /* TSKNSURLConnectionDelegateProxy.m */; }; 8C84CB9A1D6E0981009B3E7D /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C84806C1A896F660017C155 /* TrustKit.m */; }; @@ -50,7 +49,6 @@ 8C84CB9C1D6E0981009B3E7D /* RSSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CD5F7411BCB06F4005801D8 /* RSSwizzle.m */; settings = {COMPILER_FLAGS = "-Wno-sign-conversion -Wno-sign-compare"; }; }; 8C84CBA21D6E0981009B3E7D /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919291AEA0F7E002B29AE /* domain_registry.h */; }; 8C84CBA41D6E0981009B3E7D /* TSKNSURLSessionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CD5F7471BCB535E005801D8 /* TSKNSURLSessionDelegateProxy.h */; }; - 8C84CBA51D6E0981009B3E7D /* public_key_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE9191C1AEA073C002B29AE /* public_key_utils.h */; }; 8C84CBA61D6E0981009B3E7D /* TSKBackgroundReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B2B06AC1B05154A00FC749E /* TSKBackgroundReporter.h */; }; 8C84CBA71D6E0981009B3E7D /* TSKNSURLConnectionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CD5F72F1BC5ED4A005801D8 /* TSKNSURLConnectionDelegateProxy.h */; }; 8C84CBA81D6E0981009B3E7D /* TSKReportsRateLimiter.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C9EBE001B619BBE00CA7EE0 /* TSKReportsRateLimiter.h */; }; @@ -107,7 +105,6 @@ 8C8716B21B23A9F400267E1D /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B2B06AE1B05157400FC749E /* TSKBackgroundReporter.m */; }; 8C8716B31B23A9F700267E1D /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C15F99F1B16094D00F06C0E /* TSKPinFailureReport.m */; }; 8C8716B41B23A9FA00267E1D /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCBD15A1B186D1100CB88AF /* reporting_utils.m */; }; - 8C8716B51B23AA0600267E1D /* public_key_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE9191D1AEA073C002B29AE /* public_key_utils.m */; }; 8C8716B61B23AA0800267E1D /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */; }; 8C8716B81B23AA0D00267E1D /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C84806C1A896F660017C155 /* TrustKit.m */; }; 8C9492F61B2379A100F5DF38 /* reporting_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C9492F51B2379A100F5DF38 /* reporting_utils.h */; }; @@ -122,8 +119,6 @@ 8CA6CC1C1BAE2B6600BDA419 /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C15F99F1B16094D00F06C0E /* TSKPinFailureReport.m */; }; 8CA6CC1D1BAE2B6600BDA419 /* reporting_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C9492F51B2379A100F5DF38 /* reporting_utils.h */; }; 8CA6CC1E1BAE2B6600BDA419 /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCBD15A1B186D1100CB88AF /* reporting_utils.m */; }; - 8CA6CC1F1BAE2B6A00BDA419 /* public_key_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE9191C1AEA073C002B29AE /* public_key_utils.h */; }; - 8CA6CC201BAE2B6A00BDA419 /* public_key_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE9191D1AEA073C002B29AE /* public_key_utils.m */; }; 8CA6CC211BAE2B6A00BDA419 /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */; }; 8CA6CC221BAE2B6A00BDA419 /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */; }; 8CA6CC251BAE2B6A00BDA419 /* TrustKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C84804C1A896EE30017C155 /* TrustKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -169,7 +164,6 @@ 8CC5D2291D6E64D10074F515 /* trie_search.c in Sources */ = {isa = PBXBuildFile; fileRef = 8C84CCC91D6E5D5A009B3E7D /* trie_search.c */; }; 8CC5D22A1D6E64D10074F515 /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B2B06AE1B05157400FC749E /* TSKBackgroundReporter.m */; }; 8CC5D22B1D6E64D10074F515 /* TSKNSURLSessionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CD5F7481BCB535E005801D8 /* TSKNSURLSessionDelegateProxy.m */; }; - 8CC5D22C1D6E64D10074F515 /* public_key_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE9191D1AEA073C002B29AE /* public_key_utils.m */; }; 8CC5D22D1D6E64D10074F515 /* registry_search.c in Sources */ = {isa = PBXBuildFile; fileRef = 8C84CCC31D6E5D5A009B3E7D /* registry_search.c */; }; 8CC5D22E1D6E64D10074F515 /* assert.c in Sources */ = {isa = PBXBuildFile; fileRef = 8C84CCBF1D6E5D5A009B3E7D /* assert.c */; }; 8CC5D22F1D6E64D10074F515 /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C15F99F1B16094D00F06C0E /* TSKPinFailureReport.m */; }; @@ -180,7 +174,6 @@ 8CC5D2371D6E64D10074F515 /* registry_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C84CCC41D6E5D5A009B3E7D /* registry_types.h */; }; 8CC5D2381D6E64D10074F515 /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919291AEA0F7E002B29AE /* domain_registry.h */; }; 8CC5D23A1D6E64D10074F515 /* TSKNSURLSessionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CD5F7471BCB535E005801D8 /* TSKNSURLSessionDelegateProxy.h */; }; - 8CC5D23B1D6E64D10074F515 /* public_key_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE9191C1AEA073C002B29AE /* public_key_utils.h */; }; 8CC5D23C1D6E64D10074F515 /* registry_tables.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C84CCF01D6E5DE9009B3E7D /* registry_tables.h */; }; 8CC5D23D1D6E64D10074F515 /* assert.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C84CCC01D6E5D5A009B3E7D /* assert.h */; }; 8CC5D23E1D6E64D10074F515 /* TSKBackgroundReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B2B06AC1B05154A00FC749E /* TSKBackgroundReporter.h */; }; @@ -222,11 +215,25 @@ 8CDF246B1D71347000CCA32A /* OCMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C5AB4671CF26A2900234B30 /* OCMock.framework */; }; 8CDF246C1D71347E00CCA32A /* OCMock.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8CDF245C1D70DEBA00CCA32A /* OCMock.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 8CDF246D1D71348C00CCA32A /* OCMock.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8CDF245F1D70DED000CCA32A /* OCMock.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 8CE9191E1AEA073C002B29AE /* public_key_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE9191C1AEA073C002B29AE /* public_key_utils.h */; }; - 8CE9191F1AEA073C002B29AE /* public_key_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE9191D1AEA073C002B29AE /* public_key_utils.m */; }; 8CE919221AEA077F002B29AE /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */; }; 8CE919251AEA07C5002B29AE /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */; }; 8CE9192D1AEA0F7E002B29AE /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919291AEA0F7E002B29AE /* domain_registry.h */; }; + FC1A09001E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; + FC1A09011E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; + FC1A09021E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; + FC1A09031E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; + FC1A09041E57A4BB0055B12C /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */; }; + FC1A09051E57A4BB0055B12C /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */; }; + FC1A09061E57A4BB0055B12C /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */; }; + FC1A09071E57A4BB0055B12C /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */; }; + FC1A090A1E57AC450055B12C /* TSKSPKIHashCache.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A09081E57AC450055B12C /* TSKSPKIHashCache.h */; }; + FC1A090B1E57AC450055B12C /* TSKSPKIHashCache.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A09081E57AC450055B12C /* TSKSPKIHashCache.h */; }; + FC1A090C1E57AC450055B12C /* TSKSPKIHashCache.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A09081E57AC450055B12C /* TSKSPKIHashCache.h */; }; + FC1A090D1E57AC450055B12C /* TSKSPKIHashCache.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A09081E57AC450055B12C /* TSKSPKIHashCache.h */; }; + FC1A090E1E57AC450055B12C /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */; }; + FC1A090F1E57AC450055B12C /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */; }; + FC1A09101E57AC450055B12C /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */; }; + FC1A09111E57AC450055B12C /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -361,11 +368,15 @@ 8CD5F7561BCB7219005801D8 /* TSKNSURLSessionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKNSURLSessionTests.m; sourceTree = ""; }; 8CDF245C1D70DEBA00CCA32A /* OCMock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OCMock.framework; path = Dependencies/OCMock/Mac/OCMock.framework; sourceTree = ""; }; 8CDF245F1D70DED000CCA32A /* OCMock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OCMock.framework; path = Dependencies/OCMock/tvOS/OCMock.framework; sourceTree = ""; }; - 8CE9191C1AEA073C002B29AE /* public_key_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = public_key_utils.h; path = Pinning/public_key_utils.h; sourceTree = ""; }; - 8CE9191D1AEA073C002B29AE /* public_key_utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = public_key_utils.m; path = Pinning/public_key_utils.m; sourceTree = ""; }; 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ssl_pin_verifier.m; path = Pinning/ssl_pin_verifier.m; sourceTree = ""; }; 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ssl_pin_verifier.h; path = Pinning/ssl_pin_verifier.h; sourceTree = ""; }; 8CE919291AEA0F7E002B29AE /* domain_registry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = domain_registry.h; path = Dependencies/domain_registry/domain_registry.h; sourceTree = ""; }; + FC1A08FD1E579F630055B12C /* TSKPinValidatorResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinValidatorResult.h; sourceTree = ""; }; + FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; + FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; + FC1A09081E57AC450055B12C /* TSKSPKIHashCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKSPKIHashCache.h; path = Pinning/TSKSPKIHashCache.h; sourceTree = ""; }; + FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKSPKIHashCache.m; path = Pinning/TSKSPKIHashCache.m; sourceTree = ""; }; + FC1A09121E57C6820055B12C /* TSKPublicKeyAlgorithm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TSKPublicKeyAlgorithm.h; path = Pinning/TSKPublicKeyAlgorithm.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -516,6 +527,9 @@ 8C84806C1A896F660017C155 /* TrustKit.m */, 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */, 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */, + FC1A08FD1E579F630055B12C /* TSKPinValidatorResult.h */, + FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */, + FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */, 2FA286123F801C437F35D240 /* TrustKit+Private.h */, 8C84804A1A896EE30017C155 /* Supporting Files */, 8C5D98B21CEFF079008E654B /* parse_configuration.m */, @@ -659,10 +673,11 @@ 8CE9191B1AEA072A002B29AE /* Pinning */ = { isa = PBXGroup; children = ( - 8CE9191C1AEA073C002B29AE /* public_key_utils.h */, - 8CE9191D1AEA073C002B29AE /* public_key_utils.m */, 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */, 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */, + FC1A09081E57AC450055B12C /* TSKSPKIHashCache.h */, + FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */, + FC1A09121E57C6820055B12C /* TSKPublicKeyAlgorithm.h */, ); name = Pinning; sourceTree = ""; @@ -704,7 +719,6 @@ 8C0C904E1E3C4210003851A8 /* TSKPinningValidator.h in Headers */, 8CE9192D1AEA0F7E002B29AE /* domain_registry.h in Headers */, 8CD5F7491BCB535E005801D8 /* TSKNSURLSessionDelegateProxy.h in Headers */, - 8CE9191E1AEA073C002B29AE /* public_key_utils.h in Headers */, 8C84CCF11D6E5DE9009B3E7D /* registry_tables.h in Headers */, 8C84CCCE1D6E5D5A009B3E7D /* assert.h in Headers */, 6B2B06AD1B05154A00FC749E /* TSKBackgroundReporter.h in Headers */, @@ -712,9 +726,11 @@ 8C84CCE01D6E5D5A009B3E7D /* string_util.h in Headers */, 8CD5F7311BC5ED4A005801D8 /* TSKNSURLConnectionDelegateProxy.h in Headers */, 8C84CC091D6E3C67009B3E7D /* vendor_identifier.h in Headers */, + FC1A090A1E57AC450055B12C /* TSKSPKIHashCache.h in Headers */, 8C9EBE021B619BBE00CA7EE0 /* TSKReportsRateLimiter.h in Headers */, 8C84CCEC1D6E5D5A009B3E7D /* trie_search.h in Headers */, 8CD5F7421BCB06F4005801D8 /* RSSwizzle.h in Headers */, + FC1A09001E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, 8C9492F61B2379A100F5DF38 /* reporting_utils.h in Headers */, 8C84804D1A896EE30017C155 /* TrustKit.h in Headers */, 8C15F9A01B16094D00F06C0E /* TSKPinFailureReport.h in Headers */, @@ -730,7 +746,6 @@ 8C0C90501E3C4212003851A8 /* TSKPinningValidator.h in Headers */, 8C84CBA21D6E0981009B3E7D /* domain_registry.h in Headers */, 8C84CBA41D6E0981009B3E7D /* TSKNSURLSessionDelegateProxy.h in Headers */, - 8C84CBA51D6E0981009B3E7D /* public_key_utils.h in Headers */, 8C84CCF31D6E5DE9009B3E7D /* registry_tables.h in Headers */, 8C84CCD01D6E5D5A009B3E7D /* assert.h in Headers */, 8C84CBA61D6E0981009B3E7D /* TSKBackgroundReporter.h in Headers */, @@ -738,9 +753,11 @@ 8C84CCE21D6E5D5A009B3E7D /* string_util.h in Headers */, 8C84CBA71D6E0981009B3E7D /* TSKNSURLConnectionDelegateProxy.h in Headers */, 8C84CC0B1D6E3C67009B3E7D /* vendor_identifier.h in Headers */, + FC1A090C1E57AC450055B12C /* TSKSPKIHashCache.h in Headers */, 8C84CBA81D6E0981009B3E7D /* TSKReportsRateLimiter.h in Headers */, 8C84CCEE1D6E5D5A009B3E7D /* trie_search.h in Headers */, 8C84CBA91D6E0981009B3E7D /* RSSwizzle.h in Headers */, + FC1A09021E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, 8C84CBAA1D6E0981009B3E7D /* reporting_utils.h in Headers */, 8C84CBAB1D6E0981009B3E7D /* TrustKit.h in Headers */, 8C84CBAC1D6E0981009B3E7D /* TSKPinFailureReport.h in Headers */, @@ -764,13 +781,14 @@ 8C84CCE11D6E5D5A009B3E7D /* string_util.h in Headers */, 8CD5F7321BC5ED4A005801D8 /* TSKNSURLConnectionDelegateProxy.h in Headers */, 8C84CC0A1D6E3C67009B3E7D /* vendor_identifier.h in Headers */, + FC1A090B1E57AC450055B12C /* TSKSPKIHashCache.h in Headers */, 8CA6CC211BAE2B6A00BDA419 /* ssl_pin_verifier.h in Headers */, 8C84CCED1D6E5D5A009B3E7D /* trie_search.h in Headers */, 8CD5F7431BCB06F4005801D8 /* RSSwizzle.h in Headers */, + FC1A09011E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, 8CA6CC191BAE2B6600BDA419 /* TSKBackgroundReporter.h in Headers */, 8CA6CC271BAE2B7000BDA419 /* domain_registry.h in Headers */, 8CA6CC1D1BAE2B6600BDA419 /* reporting_utils.h in Headers */, - 8CA6CC1F1BAE2B6A00BDA419 /* public_key_utils.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -782,7 +800,6 @@ 8C0C90511E3C4213003851A8 /* TSKPinningValidator.h in Headers */, 8CC5D2381D6E64D10074F515 /* domain_registry.h in Headers */, 8CC5D23A1D6E64D10074F515 /* TSKNSURLSessionDelegateProxy.h in Headers */, - 8CC5D23B1D6E64D10074F515 /* public_key_utils.h in Headers */, 8CC5D23C1D6E64D10074F515 /* registry_tables.h in Headers */, 8CC5D23D1D6E64D10074F515 /* assert.h in Headers */, 8CC5D23E1D6E64D10074F515 /* TSKBackgroundReporter.h in Headers */, @@ -790,9 +807,11 @@ 8CC5D2401D6E64D10074F515 /* string_util.h in Headers */, 8CC5D2411D6E64D10074F515 /* TSKNSURLConnectionDelegateProxy.h in Headers */, 8CC5D2421D6E64D10074F515 /* vendor_identifier.h in Headers */, + FC1A090D1E57AC450055B12C /* TSKSPKIHashCache.h in Headers */, 8CC5D2431D6E64D10074F515 /* TSKReportsRateLimiter.h in Headers */, 8CC5D2441D6E64D10074F515 /* trie_search.h in Headers */, 8CC5D2451D6E64D10074F515 /* RSSwizzle.h in Headers */, + FC1A09031E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, 8CC5D2461D6E64D10074F515 /* reporting_utils.h in Headers */, 8CC5D2471D6E64D10074F515 /* TrustKit.h in Headers */, 8CC5D2481D6E64D10074F515 /* TSKPinFailureReport.h in Headers */, @@ -1088,12 +1107,12 @@ 8C84CCD11D6E5D5A009B3E7D /* init_registry_tables.c in Sources */, 8CE919221AEA077F002B29AE /* ssl_pin_verifier.m in Sources */, 8C84CC0C1D6E3C67009B3E7D /* vendor_identifier.m in Sources */, + FC1A090E1E57AC450055B12C /* TSKSPKIHashCache.m in Sources */, 8C9EBE031B619BBE00CA7EE0 /* TSKReportsRateLimiter.m in Sources */, 8C5D98B31CEFF079008E654B /* parse_configuration.m in Sources */, 8C84CCE91D6E5D5A009B3E7D /* trie_search.c in Sources */, 6B2B06AF1B05157400FC749E /* TSKBackgroundReporter.m in Sources */, 8CD5F74B1BCB535E005801D8 /* TSKNSURLSessionDelegateProxy.m in Sources */, - 8CE9191F1AEA073C002B29AE /* public_key_utils.m in Sources */, 8C84CCD71D6E5D5A009B3E7D /* registry_search.c in Sources */, 8C84CCCB1D6E5D5A009B3E7D /* assert.c in Sources */, 8C15F9A11B16094E00F06C0E /* TSKPinFailureReport.m in Sources */, @@ -1101,6 +1120,7 @@ 8C84806D1A896F660017C155 /* TrustKit.m in Sources */, 8C0C90491E3C41F9003851A8 /* TSKPinningValidator.m in Sources */, 8CCBD15B1B186D1100CB88AF /* reporting_utils.m in Sources */, + FC1A09041E57A4BB0055B12C /* TSKPinningValidatorResult.m in Sources */, 8CD5F7441BCB06F4005801D8 /* RSSwizzle.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1127,12 +1147,12 @@ 8C84CCD31D6E5D5A009B3E7D /* init_registry_tables.c in Sources */, 8C84CB911D6E0981009B3E7D /* ssl_pin_verifier.m in Sources */, 8C84CC0F1D6E3C67009B3E7D /* vendor_identifier.m in Sources */, + FC1A09101E57AC450055B12C /* TSKSPKIHashCache.m in Sources */, 8C84CB921D6E0981009B3E7D /* TSKReportsRateLimiter.m in Sources */, 8C84CB931D6E0981009B3E7D /* parse_configuration.m in Sources */, 8C84CCEB1D6E5D5A009B3E7D /* trie_search.c in Sources */, 8C84CB941D6E0981009B3E7D /* TSKBackgroundReporter.m in Sources */, 8C84CB951D6E0981009B3E7D /* TSKNSURLSessionDelegateProxy.m in Sources */, - 8C84CB961D6E0981009B3E7D /* public_key_utils.m in Sources */, 8C84CCD91D6E5D5A009B3E7D /* registry_search.c in Sources */, 8C84CCCD1D6E5D5A009B3E7D /* assert.c in Sources */, 8C84CB971D6E0981009B3E7D /* TSKPinFailureReport.m in Sources */, @@ -1140,6 +1160,7 @@ 8C84CB9A1D6E0981009B3E7D /* TrustKit.m in Sources */, 8C0C904C1E3C41FB003851A8 /* TSKPinningValidator.m in Sources */, 8C84CB9B1D6E0981009B3E7D /* reporting_utils.m in Sources */, + FC1A09061E57A4BB0055B12C /* TSKPinningValidatorResult.m in Sources */, 8C84CB9C1D6E0981009B3E7D /* RSSwizzle.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1168,7 +1189,6 @@ 0DB3B67F1DA3B26700DA730D /* trie_search.c in Sources */, 0DB3B67C1DA3B24100DA730D /* init_registry_tables.c in Sources */, 8C84CC0D1D6E3C67009B3E7D /* vendor_identifier.m in Sources */, - 8C8716B51B23AA0600267E1D /* public_key_utils.m in Sources */, 8C5D98B41CEFF079008E654B /* parse_configuration.m in Sources */, 8C8716B31B23A9F700267E1D /* TSKPinFailureReport.m in Sources */, 8C8716B41B23A9FA00267E1D /* reporting_utils.m in Sources */, @@ -1189,7 +1209,7 @@ files = ( 8C84CCD21D6E5D5A009B3E7D /* init_registry_tables.c in Sources */, 8C84CC0E1D6E3C67009B3E7D /* vendor_identifier.m in Sources */, - 8CA6CC201BAE2B6A00BDA419 /* public_key_utils.m in Sources */, + FC1A090F1E57AC450055B12C /* TSKSPKIHashCache.m in Sources */, 8CA6CC1A1BAE2B6600BDA419 /* TSKBackgroundReporter.m in Sources */, 8CA6CC1C1BAE2B6600BDA419 /* TSKPinFailureReport.m in Sources */, 8C84CCEA1D6E5D5A009B3E7D /* trie_search.c in Sources */, @@ -1203,6 +1223,7 @@ 8CD5F7351BC5ED4A005801D8 /* TSKNSURLConnectionDelegateProxy.m in Sources */, 8C0C904B1E3C41FB003851A8 /* TSKPinningValidator.m in Sources */, 8CA6CC221BAE2B6A00BDA419 /* ssl_pin_verifier.m in Sources */, + FC1A09051E57A4BB0055B12C /* TSKPinningValidatorResult.m in Sources */, 8CD5F7451BCB06F4005801D8 /* RSSwizzle.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1229,12 +1250,12 @@ 8CC5D2241D6E64D10074F515 /* init_registry_tables.c in Sources */, 8CC5D2251D6E64D10074F515 /* ssl_pin_verifier.m in Sources */, 8CC5D2261D6E64D10074F515 /* vendor_identifier.m in Sources */, + FC1A09111E57AC450055B12C /* TSKSPKIHashCache.m in Sources */, 8CC5D2271D6E64D10074F515 /* TSKReportsRateLimiter.m in Sources */, 8CC5D2281D6E64D10074F515 /* parse_configuration.m in Sources */, 8CC5D2291D6E64D10074F515 /* trie_search.c in Sources */, 8CC5D22A1D6E64D10074F515 /* TSKBackgroundReporter.m in Sources */, 8CC5D22B1D6E64D10074F515 /* TSKNSURLSessionDelegateProxy.m in Sources */, - 8CC5D22C1D6E64D10074F515 /* public_key_utils.m in Sources */, 8CC5D22D1D6E64D10074F515 /* registry_search.c in Sources */, 8CC5D22E1D6E64D10074F515 /* assert.c in Sources */, 8CC5D22F1D6E64D10074F515 /* TSKPinFailureReport.m in Sources */, @@ -1242,6 +1263,7 @@ 8CC5D2321D6E64D10074F515 /* TrustKit.m in Sources */, 8C0C904D1E3C41FE003851A8 /* TSKPinningValidator.m in Sources */, 8CC5D2331D6E64D10074F515 /* reporting_utils.m in Sources */, + FC1A09071E57A4BB0055B12C /* TSKPinningValidatorResult.m in Sources */, 8CC5D2341D6E64D10074F515 /* RSSwizzle.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1406,7 +1428,7 @@ ENABLE_BITCODE = "$(inherited)"; INFOPLIST_FILE = TrustKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; ONLY_ACTIVE_ARCH = NO; @@ -1434,7 +1456,7 @@ ENABLE_BITCODE = "$(inherited)"; INFOPLIST_FILE = TrustKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.$(PRODUCT_NAME:rfc1034identifier)"; diff --git a/TrustKit/Pinning/TSKPublicKeyAlgorithm.h b/TrustKit/Pinning/TSKPublicKeyAlgorithm.h new file mode 100644 index 00000000..1bf43b87 --- /dev/null +++ b/TrustKit/Pinning/TSKPublicKeyAlgorithm.h @@ -0,0 +1,26 @@ +/* + + TSKPublicKeyAlgorithm.h + TrustKit + + Copyright 2015 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ + +#ifndef TSKPublicKeyAlgorithm_h +#define TSKPublicKeyAlgorithm_h + +typedef NS_ENUM(NSInteger, TSKPublicKeyAlgorithm) +{ + // Some assumptions are made about this specific ordering in public_key_utils.m + TSKPublicKeyAlgorithmRsa2048 = 0, + TSKPublicKeyAlgorithmRsa4096 = 1, + TSKPublicKeyAlgorithmEcDsaSecp256r1 = 2, + TSKPublicKeyAlgorithmEcDsaSecp384r1 = 3, + + TSKPublicKeyAlgorithmLast = TSKPublicKeyAlgorithmEcDsaSecp384r1 +}; + +#endif /* TSKPublicKeyAlgorithm_h */ diff --git a/TrustKit/Pinning/TSKSPKIHashCache.h b/TrustKit/Pinning/TSKSPKIHashCache.h new file mode 100644 index 00000000..cb52dfcd --- /dev/null +++ b/TrustKit/Pinning/TSKSPKIHashCache.h @@ -0,0 +1,27 @@ +/* + + TSKSPKIHashCache.h + TrustKit + + Copyright 2015 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ + +#import +#import "TSKPublicKeyAlgorithm.h" + +@import Security; + +// Each key is a raw certificate data (for easy lookup) and each value is the certificate's raw SPKI data +typedef NSMutableDictionary SpkiCacheDictionnary; + +@interface TSKSPKIHashCache : NSObject + +- (NSData *)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certificate publicKeyAlgorithm:(TSKPublicKeyAlgorithm)publicKeyAlgorithm; + +- (NSMutableDictionary *)getSpkiCache; +- (NSMutableDictionary *)getSpkiCacheFromFileSystem; + +@end diff --git a/TrustKit/Pinning/public_key_utils.m b/TrustKit/Pinning/TSKSPKIHashCache.m similarity index 65% rename from TrustKit/Pinning/public_key_utils.m rename to TrustKit/Pinning/TSKSPKIHashCache.m index 0421aaeb..dc4b1d4b 100644 --- a/TrustKit/Pinning/public_key_utils.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -1,6 +1,6 @@ /* - public_key_utils.m + TSKSPKIHashCache.m TrustKit Copyright 2015 The TrustKit Project Authors @@ -9,24 +9,28 @@ */ - -#import "public_key_utils.h" -#include -#import +#import "TSKSPKIHashCache.h" #import "../TrustKit+Private.h" +#import +#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED <= 100000 +// Need to support iOS before 10.0 +// The one and only way to get a key's data in a buffer on iOS is to put it in the Keychain and then ask for the data back... +#define LEGACY_IOS_KEY_EXTRACTION 1 +static const NSString *kTSKKeychainPublicKeyTag = @"TSKKeychainPublicKeyTag"; // Used to add and find the public key in the Keychain +#endif -#pragma mark Global Cache for SPKI Hashes +#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 +#define LEGACY_MACOS_KEY_EXTRACTION 1 +#endif -// Dictionnary to cache SPKI hashes instead of having to compute them on every connection -// We store one cache dictionnary per TSKPublicKeyAlgorithm we support -NSMutableDictionary *_subjectPublicKeyInfoHashesCache; +#if TARGET_OS_WATCH || TARGET_OS_TV || (TARGET_OS_IOS &&__IPHONE_OS_VERSION_MAX_ALLOWED >= 100000) || (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101200) +#define UNIFIED_KEY_EXTRACTION 1 +#endif -// Used to lock access to our SPKI cache -static pthread_mutex_t _spkiCacheLock; -// File name for persisting the cache in the filesystem -static NSString *_spkiCacheFilename = @"TrustKitSpkiCache.plist"; +// Each key is a raw certificate data (for easy lookup) and each value is the certificate's raw SPKI data +typedef NSMutableDictionary SpkiCacheDictionnary; #pragma mark Missing ASN1 SPKI Headers @@ -59,12 +63,215 @@ sizeof(ecDsaSecp256r1Asn1Header), sizeof(ecDsaSecp384r1Asn1Header) }; -#if TARGET_OS_WATCH || TARGET_OS_TV || (TARGET_OS_IOS &&__IPHONE_OS_VERSION_MAX_ALLOWED >= 100000) || (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101200) +@interface TSKSPKIHashCache () +// Dictionnary to cache SPKI hashes instead of having to compute them on every connection +// We store one cache dictionnary per TSKPublicKeyAlgorithm we support +@property (nonatomic) NSMutableDictionary *subjectPublicKeyInfoHashesCache; +@property (nonatomic) dispatch_queue_t lockQueue; +@property (nonatomic) NSString *spkiCacheFilename; +@end + +#if LEGACY_IOS_KEY_EXTRACTION +@interface TSKSPKIHashCache () +@property (nonatomic) dispatch_queue_t keychainQueue; +@end + +@interface TSKSPKIHashCache (LegacyIos) +- (NSData *)getPublicKeyDataFromCertificate_legacy_ios:(SecCertificateRef)certificate; +@end +#endif + +#if LEGACY_MACOS_KEY_EXTRACTION +@interface TSKSPKIHashCache (LegacyMacOS) +- (NSData *)getPublicKeyDataFromCertificate_legacy_macos:(SecCertificateRef)certificate; +#endif + +#if UNIFIED_KEY_EXTRACTION +@interface TSKSPKIHashCache (Unified) +- (NSData *)getPublicKeyDataFromCertificate_unified:(SecCertificateRef)certificate; +@end +#endif + +@implementation TSKSPKIHashCache + +- (instancetype)init +{ + self = [super init]; + if (self) { + // Initialize our cache of SPKI hashes + // First try to load a cached version from the filesystem + _subjectPublicKeyInfoHashesCache = [self getSpkiCacheFromFileSystem]; + TSKLog(@"Loaded %d SPKI cache entries from the filesystem", _subjectPublicKeyInfoHashesCache.count); + + if (_subjectPublicKeyInfoHashesCache == nil) + { + _subjectPublicKeyInfoHashesCache = [NSMutableDictionary new]; + } + + // Initialize any sub-dictionnary that hasn't been initialized + for (int i=0; i<=TSKPublicKeyAlgorithmLast; i++) + { + NSNumber *algorithmKey = @(i); + if (_subjectPublicKeyInfoHashesCache[algorithmKey] == nil) + { + _subjectPublicKeyInfoHashesCache[algorithmKey] = [NSMutableDictionary new]; + } + + } + + // Initialize our locks + _lockQueue = dispatch_queue_create("TSKSPKIHashLock", DISPATCH_QUEUE_CONCURRENT); + +#if LEGACY_IOS_KEY_EXTRACTION + _keychainQueue = dispatch_queue_create("TSKSPKIKeychainLock", DISPATCH_QUEUE_SERIAL); + // Cleanup the Keychain in case the App previously crashed + NSMutableDictionary * publicKeyGet = [[NSMutableDictionary alloc] init]; + [publicKeyGet setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; + [publicKeyGet setObject:(kTSKKeychainPublicKeyTag) forKey:(__bridge id)kSecAttrApplicationTag]; + [publicKeyGet setObject:(__bridge id)(kCFBooleanTrue) forKey:(__bridge id)kSecReturnData]; + dispatch_sync(_keychainQueue, ^{ + SecItemDelete((__bridge CFDictionaryRef)(publicKeyGet)); + }); +#endif + } + return self; +} + +- (NSData *)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certificate publicKeyAlgorithm:(TSKPublicKeyAlgorithm)publicKeyAlgorithm +{ + __block NSData *cachedSubjectPublicKeyInfo; + NSNumber *algorithmKey = [NSNumber numberWithInt:publicKeyAlgorithm]; + + // Have we seen this certificate before? Look for the SPKI in the cache + NSData *certificateData = (__bridge_transfer NSData *)(SecCertificateCopyData(certificate)); + + dispatch_sync(_lockQueue, ^{ + cachedSubjectPublicKeyInfo = _subjectPublicKeyInfoHashesCache[algorithmKey][certificateData]; + }); + + if (cachedSubjectPublicKeyInfo) + { + TSKLog(@"Subject Public Key Info hash was found in the cache"); + return cachedSubjectPublicKeyInfo; + } + + // We didn't this certificate in the cache so we need to generate its SPKI hash + TSKLog(@"Generating Subject Public Key Info hash..."); + + // First extract the public key bytes + NSData *publicKeyData = [self getPublicKeyDataFromCertificate:certificate]; + if (publicKeyData == nil) + { + TSKLog(@"Error - could not extract the public key bytes"); + return nil; + } + + + // Generate a hash of the subject public key info + NSMutableData *subjectPublicKeyInfoHash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH]; + CC_SHA256_CTX shaCtx; + CC_SHA256_Init(&shaCtx); + + // Add the missing ASN1 header for public keys to re-create the subject public key info + CC_SHA256_Update(&shaCtx, asn1HeaderBytes[publicKeyAlgorithm], asn1HeaderSizes[publicKeyAlgorithm]); + + // Add the public key + CC_SHA256_Update(&shaCtx, [publicKeyData bytes], (unsigned int)[publicKeyData length]); + CC_SHA256_Final((unsigned char *)[subjectPublicKeyInfoHash bytes], &shaCtx); + + + // Store the hash in our memory cache + dispatch_barrier_sync(_lockQueue, ^{ + _subjectPublicKeyInfoHashesCache[algorithmKey][certificateData] = subjectPublicKeyInfoHash; + }); + + // Update the cache on the filesystem + NSString *spkiCachePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] + stringByAppendingPathComponent:self.spkiCacheFilename]; + NSData *serializedSpkiCache = [NSKeyedArchiver archivedDataWithRootObject:_subjectPublicKeyInfoHashesCache]; + if ([serializedSpkiCache writeToFile:spkiCachePath atomically:YES] == NO) + { + TSKLog(@"Could not persist SPKI cache to the filesystem"); + } + + return subjectPublicKeyInfoHash; +} + +- (NSMutableDictionary *)getSpkiCacheFromFileSystem +{ + NSMutableDictionary *spkiCache; + NSString *spkiCachePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] + stringByAppendingPathComponent:self.spkiCacheFilename]; + NSData *serializedSpkiCache = [NSData dataWithContentsOfFile:spkiCachePath]; + if (serializedSpkiCache) { + spkiCache = [NSKeyedUnarchiver unarchiveObjectWithData:serializedSpkiCache]; + } + return spkiCache; +} + +- (NSMutableDictionary *)getSpkiCache +{ + return _subjectPublicKeyInfoHashesCache; +} + +#pragma mark Private + +- (NSData *)getPublicKeyDataFromCertificate:(SecCertificateRef)certificate +{ +#if TARGET_OS_WATCH || TARGET_OS_TV + // watchOS 3+ or tvOS 10+ + return [self getPublicKeyDataFromCertificate_unified:certificate]; +#elif TARGET_OS_IOS + // iOS 7+ +#if __IPHONE_OS_VERSION_MAX_ALLOWED < 100000 + // Base SDK is iOS 7, 8 or 9 + return [self getPublicKeyDataFromCertificate_legacy_ios:certificate ]; +#else + // Base SDK is iOS 10+ - try to use the unified Security APIs if available + NSProcessInfo *processInfo = [NSProcessInfo processInfo]; + if ([processInfo respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)] && [processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10, 0, 0}]) + { + // iOS 10+ + return [self getPublicKeyDataFromCertificate_unified:certificate]; + } + else + { + // iOS 7, 8, 9 + return [self getPublicKeyDataFromCertificate_legacy_ios:certificate]; + } +#endif +#else + // macOS 10.9+ +#if LEGACY_MACOS_KEY_EXTRACTION + // Base SDK is macOS 10.9, 10.10 or 10.11 + return [self getPublicKeyDataFromCertificate_legacy_macos:certificate]; +#else + // Base SDK is macOS 10.12 - try to use the unified Security APIs if available + NSProcessInfo *processInfo = [NSProcessInfo processInfo]; + if ([processInfo respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)] + && [processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10, 12, 0}]) + { + // macOS 10.12+ + return [self getPublicKeyDataFromCertificate_unified:certificate]; + } + else + { + // macOS 10.9, 10.10, 10.11 + return [self getPublicKeyDataFromCertificate_legacy_macos:certificate]; + } +#endif +#endif +} + +@end + #pragma mark Public Key Converter - iOS 10.0+, macOS 10.12+, watchOS 3.0, tvOS 10.0 +#if UNIFIED_KEY_EXTRACTION +@implementation TSKSPKIHashCache (Unified) // Use the unified SecKey API (specifically SecKeyCopyExternalRepresentation()) -static NSData *getPublicKeyDataFromCertificate_unified(SecCertificateRef certificate) +- (NSData *)getPublicKeyDataFromCertificate_unified:(SecCertificateRef)certificate { SecKeyRef publicKey; SecTrustRef tempTrust; @@ -81,26 +288,19 @@ CFRelease(publicKey); return (NSData *)CFBridgingRelease(publicKeyData); } -#endif +@end +#endif -#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000 #pragma mark Public Key Converter - iOS before 10.0 +#if LEGACY_IOS_KEY_EXTRACTION +@implementation TSKSPKIHashCache (LegacyIOS) -// Need to support iOS before 10.0 -// The one and only way to get a key's data in a buffer on iOS is to put it in the Keychain and then ask for the data back... -#define LEGACY_IOS_KEY_EXTRACTION 1 - -static const NSString *kTSKKeychainPublicKeyTag = @"TSKKeychainPublicKeyTag"; // Used to add and find the public key in the Keychain - -static pthread_mutex_t _keychainLock; // Used to lock access to our Keychain item - - -static NSData *getPublicKeyDataFromCertificate_legacy_ios(SecCertificateRef certificate) +- (NSData *)getPublicKeyDataFromCertificate_legacy_ios:(SecCertificateRef)certificate { NSData *publicKeyData = nil; - OSStatus resultAdd, resultDel = noErr; + __block OSStatus resultAdd, __block resultDel = noErr; SecKeyRef publicKey; SecTrustRef tempTrust; SecPolicyRef policy = SecPolicyCreateBasicX509(); @@ -134,12 +334,10 @@ // Get the key bytes from the Keychain atomically - pthread_mutex_lock(&_keychainLock); - { + dispatch_sync(self.keychainQueue, ^{ resultAdd = SecItemAdd((__bridge CFDictionaryRef) peerPublicKeyAdd, (void *)&publicKeyData); resultDel = SecItemDelete((__bridge CFDictionaryRef)(publicKeyGet)); - } - pthread_mutex_unlock(&_keychainLock); + }); CFRelease(publicKey); if ((resultAdd != errSecSuccess) || (resultDel != errSecSuccess)) @@ -151,16 +349,18 @@ return publicKeyData; } + +@end #endif -#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 - #pragma mark Public Key Converter - macOS before 10.12 +#if LEGACY_MACOS_KEY_EXTRACTION +@implementation TSKSPKIHashCache (LegacyMacOS) // Need to support macOS before 10.12 -static NSData *getPublicKeyDataFromCertificate_legacy_macos(SecCertificateRef certificate) +- (NSData *)getPublicKeyDataFromCertificate_legacy_macos:(SecCertificateRef)certificate { NSData *publicKeyData = nil; CFErrorRef error = NULL; @@ -188,201 +388,18 @@ CFRelease(certificateValues); return publicKeyData; } -#endif - - -static NSData *getPublicKeyDataFromCertificate(SecCertificateRef certificate) -{ -#if TARGET_OS_WATCH || TARGET_OS_TV - // watchOS 3+ or tvOS 10+ - return getPublicKeyDataFromCertificate_unified(certificate); -#elif TARGET_OS_IOS - // iOS 7+ -#if __IPHONE_OS_VERSION_MAX_ALLOWED < 100000 - // Base SDK is iOS 7, 8 or 9 - return getPublicKeyDataFromCertificate_legacy_ios(certificate); -#else - // Base SDK is iOS 10+ - try to use the unified Security APIs if available - NSProcessInfo *processInfo = [NSProcessInfo processInfo]; - if ([processInfo respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)] && [processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10, 0, 0}]) - { - // iOS 10+ - return getPublicKeyDataFromCertificate_unified(certificate); - } - else - { - // iOS 7, 8, 9 - return getPublicKeyDataFromCertificate_legacy_ios(certificate); - } -#endif -#else - // macOS 10.9+ -#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101200 - // Base SDK is macOS 10.9, 10.10 or 10.11 - return getPublicKeyDataFromCertificate_legacy_macos(certificate); -#else - // Base SDK is macOS 10.12 - try to use the unified Security APIs if available - NSProcessInfo *processInfo = [NSProcessInfo processInfo]; - if ([processInfo respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)] && [processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10, 12, 0}]) - { - // macOS 10.12+ - return getPublicKeyDataFromCertificate_unified(certificate); - } - else - { - // macOS 10.9, 10.10, 10.11 - return getPublicKeyDataFromCertificate_legacy_macos(certificate); - } -#endif -#endif -} - - -#pragma mark SPKI Hashing Function - -NSData *hashSubjectPublicKeyInfoFromCertificate(SecCertificateRef certificate, TSKPublicKeyAlgorithm publicKeyAlgorithm) -{ - NSData *cachedSubjectPublicKeyInfo = NULL; - NSNumber *algorithmKey = [NSNumber numberWithInt:publicKeyAlgorithm]; - - // Have we seen this certificate before? Look for the SPKI in the cache - NSData *certificateData = (__bridge NSData *)(SecCertificateCopyData(certificate)); - - pthread_mutex_lock(&_spkiCacheLock); - { - cachedSubjectPublicKeyInfo = _subjectPublicKeyInfoHashesCache[algorithmKey][certificateData]; - } - pthread_mutex_unlock(&_spkiCacheLock); - - if (cachedSubjectPublicKeyInfo) - { - TSKLog(@"Subject Public Key Info hash was found in the cache"); - CFRelease((__bridge CFTypeRef)(certificateData)); - return cachedSubjectPublicKeyInfo; - } - - // We didn't this certificate in the cache so we need to generate its SPKI hash - TSKLog(@"Generating Subject Public Key Info hash..."); - - // First extract the public key bytes - NSData *publicKeyData = getPublicKeyDataFromCertificate(certificate); - if (publicKeyData == nil) - { - TSKLog(@"Error - could not extract the public key bytes"); - CFRelease((__bridge CFTypeRef)(certificateData)); - return nil; - } - - - // Generate a hash of the subject public key info - NSMutableData *subjectPublicKeyInfoHash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH]; - CC_SHA256_CTX shaCtx; - CC_SHA256_Init(&shaCtx); - - // Add the missing ASN1 header for public keys to re-create the subject public key info - CC_SHA256_Update(&shaCtx, asn1HeaderBytes[publicKeyAlgorithm], asn1HeaderSizes[publicKeyAlgorithm]); - - // Add the public key - CC_SHA256_Update(&shaCtx, [publicKeyData bytes], (unsigned int)[publicKeyData length]); - CC_SHA256_Final((unsigned char *)[subjectPublicKeyInfoHash bytes], &shaCtx); - - - // Store the hash in our memory cache - pthread_mutex_lock(&_spkiCacheLock); - { - _subjectPublicKeyInfoHashesCache[algorithmKey][certificateData] = subjectPublicKeyInfoHash; - } - pthread_mutex_unlock(&_spkiCacheLock); - - // Update the cache on the filesystem - NSString *spkiCachePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:_spkiCacheFilename]; - NSData *serializedSpkiCache = [NSKeyedArchiver archivedDataWithRootObject:_subjectPublicKeyInfoHashesCache]; - if ([serializedSpkiCache writeToFile:spkiCachePath atomically:YES] == NO) - { - TSKLog(@"Could not persist SPKI cache to the filesystem"); - } - - CFRelease((__bridge CFTypeRef)(certificateData)); - return subjectPublicKeyInfoHash; -} - -void initializeSubjectPublicKeyInfoCache(void) -{ - // Initialize our cache of SPKI hashes - // First try to load a cached version from the filesystem - _subjectPublicKeyInfoHashesCache = getSpkiCacheFromFileSystem(); - TSKLog(@"Loaded %d SPKI cache entries from the filesystem", [_subjectPublicKeyInfoHashesCache count]); - - if (_subjectPublicKeyInfoHashesCache == nil) - { - _subjectPublicKeyInfoHashesCache = [[NSMutableDictionary alloc]init]; - } - - // Initialize any sub-dictionnary that hasn't been initialized - for (int i=0; i<=TSKPublicKeyAlgorithmLast; i++) - { - NSNumber *algorithmKey = [NSNumber numberWithInt:i]; - if (_subjectPublicKeyInfoHashesCache[algorithmKey] == nil) - { - _subjectPublicKeyInfoHashesCache[algorithmKey] = [[NSMutableDictionary alloc]init]; - } - - } - - // Initialize our locks - pthread_mutex_init(&_spkiCacheLock, NULL); - -#if LEGACY_IOS_KEY_EXTRACTION - pthread_mutex_init(&_keychainLock, NULL); - // Cleanup the Keychain in case the App previously crashed - NSMutableDictionary * publicKeyGet = [[NSMutableDictionary alloc] init]; - [publicKeyGet setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; - [publicKeyGet setObject:(kTSKKeychainPublicKeyTag) forKey:(__bridge id)kSecAttrApplicationTag]; - [publicKeyGet setObject:(__bridge id)(kCFBooleanTrue) forKey:(__bridge id)kSecReturnData]; - pthread_mutex_lock(&_keychainLock); - { - SecItemDelete((__bridge CFDictionaryRef)(publicKeyGet)); - } - pthread_mutex_unlock(&_keychainLock); +@end #endif -} +@implementation TSKSPKIHashCache (TestSupport) -#pragma mark Functions used by the Test Suite - -void resetSubjectPublicKeyInfoCache(void) -{ - // This is only used for tests - // Destroy our locks - pthread_mutex_destroy(&_spkiCacheLock); - -#if LEGACY_IOS_KEY_EXTRACTION - pthread_mutex_destroy(&_keychainLock); -#endif - +- (void)resetSubjectPublicKeyInfoDiskCache { // Discard SPKI cache - _subjectPublicKeyInfoHashesCache = nil; - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSString *spkiCachePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:_spkiCacheFilename]; + NSString *spkiCachePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] + stringByAppendingPathComponent:self.spkiCacheFilename]; [fileManager removeItemAtPath:spkiCachePath error:nil]; } - -NSMutableDictionary *getSpkiCacheFromFileSystem(void) -{ - NSMutableDictionary *spkiCache = nil; - NSString *spkiCachePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:_spkiCacheFilename]; - NSData *serializedSpkiCache = [NSData dataWithContentsOfFile:spkiCachePath]; - if (serializedSpkiCache) { - spkiCache = [NSKeyedUnarchiver unarchiveObjectWithData:serializedSpkiCache]; - } - return spkiCache; -} - - -NSMutableDictionary *getSpkiCache(void) -{ - return _subjectPublicKeyInfoHashesCache; -} +@end diff --git a/TrustKit/Pinning/public_key_utils.h b/TrustKit/Pinning/public_key_utils.h deleted file mode 100644 index d182a2e2..00000000 --- a/TrustKit/Pinning/public_key_utils.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - - public_key_utils.h - TrustKit - - Copyright 2015 The TrustKit Project Authors - Licensed under the MIT license, see associated LICENSE file for terms. - See AUTHORS file for the list of project authors. - - */ - -#ifndef TrustKit_subjectPublicKeyHash_h -#define TrustKit_subjectPublicKeyHash_h - -#import -@import Security; - - -typedef NS_ENUM(NSInteger, TSKPublicKeyAlgorithm) -{ - // Some assumptions are made about this specific ordering in public_key_utils.m - TSKPublicKeyAlgorithmRsa2048 = 0, - TSKPublicKeyAlgorithmRsa4096 = 1, - TSKPublicKeyAlgorithmEcDsaSecp256r1 = 2, - TSKPublicKeyAlgorithmEcDsaSecp384r1 = 3, - - TSKPublicKeyAlgorithmLast = TSKPublicKeyAlgorithmEcDsaSecp384r1 -}; - - -void initializeSubjectPublicKeyInfoCache(void); - -NSData *hashSubjectPublicKeyInfoFromCertificate(SecCertificateRef certificate, TSKPublicKeyAlgorithm publicKeyAlgorithm); - - -// For tests -void resetSubjectPublicKeyInfoCache(void); - -// Each key is a raw certificate data (for easy lookup) and each value is the certificate's raw SPKI data -typedef NSMutableDictionary SpkiCacheDictionnary; - -NSMutableDictionary *getSpkiCache(void); -NSMutableDictionary *getSpkiCacheFromFileSystem(void); - - -#endif diff --git a/TrustKit/Pinning/ssl_pin_verifier.h b/TrustKit/Pinning/ssl_pin_verifier.h index 82f5950c..58960c8d 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.h +++ b/TrustKit/Pinning/ssl_pin_verifier.h @@ -11,48 +11,12 @@ #import +#import "../TSKPinValidatorResult.h" - -/** - Possible return values when verifying a server's identity. - */ -typedef NS_ENUM(NSInteger, TSKPinValidationResult) -{ - /** - The server trust was succesfully evaluated and contained at least one of the configured pins. - */ - TSKPinValidationResultSuccess, - - /** - The server trust was succesfully evaluated but did not contain any of the configured pins. - */ - TSKPinValidationResultFailed, - - /** - The server trust's evaluation failed: the server's certificate chain is not trusted. - */ - TSKPinValidationResultFailedCertificateChainNotTrusted, - - /** - The server trust could not be evaluated due to invalid parameters. - */ - TSKPinValidationResultErrorInvalidParameters, - - /** - The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to OS X's trust store). Only available on OS X. - */ - TSKPinValidationResultFailedUserDefinedTrustAnchor NS_AVAILABLE_MAC(10_9), - - /** - The server trust could not be evaluated due to an error when trying to generate the certificate's subject public key info hash. On iOS, this could be caused by a Keychain failure when trying to extract the certificate's public key bytes. - */ - TSKPinValidationResultErrorCouldNotGenerateSpkiHash, -}; - +@class TSKSPKIHashCache; // Figure out if a specific domain is pinned and retrieve this domain's configuration key; returns nil if no configuration was found NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *trustKitConfiguration); // Validate that the server trust contains at least one of the know/expected pins -TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *serverHostname, NSArray *supportedAlgorithms, NSSet *knownPins); - +TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *serverHostname, NSArray *supportedAlgorithms, NSSet *knownPins, TSKSPKIHashCache *hashCache); diff --git a/TrustKit/Pinning/ssl_pin_verifier.m b/TrustKit/Pinning/ssl_pin_verifier.m index 712a17ac..58866c4c 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.m +++ b/TrustKit/Pinning/ssl_pin_verifier.m @@ -10,8 +10,8 @@ */ #import "ssl_pin_verifier.h" +#import "TSKSPKIHashCache.h" #import "../Dependencies/domain_registry/domain_registry.h" -#import "public_key_utils.h" #import "../TrustKit+Private.h" @@ -46,10 +46,9 @@ static BOOL isSubdomain(NSString *domain, NSString *subdomain) } -NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *trustKitConfiguration) +NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *domainsPinningPolicy) { NSString *configHostname = nil; - NSDictionary *domainsPinningPolicy = trustKitConfiguration[kTSKPinnedDomains]; if (domainsPinningPolicy[hostname] == nil) { @@ -88,7 +87,7 @@ static BOOL isSubdomain(NSString *domain, NSString *subdomain) #pragma mark SSL Pin Verifier -TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *serverHostname, NSArray *supportedAlgorithms, NSSet *knownPins) +TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *serverHostname, NSArray *supportedAlgorithms, NSSet *knownPins, TSKSPKIHashCache *hashCache) { if ((serverTrust == NULL) || (supportedAlgorithms == nil) || (knownPins == nil)) { @@ -139,7 +138,8 @@ TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *ser for (NSNumber *savedAlgorithm in supportedAlgorithms) { TSKPublicKeyAlgorithm algorithm = [savedAlgorithm integerValue]; - NSData *subjectPublicKeyInfoHash = hashSubjectPublicKeyInfoFromCertificate(certificate, algorithm); + NSData *subjectPublicKeyInfoHash = [hashCache hashSubjectPublicKeyInfoFromCertificate:certificate + publicKeyAlgorithm:algorithm]; if (subjectPublicKeyInfoHash == nil) { TSKLog(@"Error - could not generate the SPKI hash for %@", serverHostname); diff --git a/TrustKit/TSKPinValidatorResult.h b/TrustKit/TSKPinValidatorResult.h new file mode 100644 index 00000000..463870fd --- /dev/null +++ b/TrustKit/TSKPinValidatorResult.h @@ -0,0 +1,71 @@ +/* + + TSKPinningValidator.h + TrustKit + + Copyright 2015 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ + +/** + Possible return values when verifying a server's identity. + */ +typedef NS_ENUM(NSInteger, TSKPinValidationResult) +{ + /** + The server trust was succesfully evaluated and contained at least one of the configured pins. + */ + TSKPinValidationResultSuccess, + + /** + The server trust was succesfully evaluated but did not contain any of the configured pins. + */ + TSKPinValidationResultFailed, + + /** + The server trust's evaluation failed: the server's certificate chain is not trusted. + */ + TSKPinValidationResultFailedCertificateChainNotTrusted, + + /** + The server trust could not be evaluated due to invalid parameters. + */ + TSKPinValidationResultErrorInvalidParameters, + + /** + The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to OS X's trust store). Only available on OS X. + */ + TSKPinValidationResultFailedUserDefinedTrustAnchor NS_AVAILABLE_MAC(10_9), + + /** + The server trust could not be evaluated due to an error when trying to generate the certificate's subject public key info hash. On iOS, this could be caused by a Keychain failure when trying to extract the certificate's public key bytes. + */ + TSKPinValidationResultErrorCouldNotGenerateSpkiHash, +}; + +/** + Possible return values when verifying a server's identity against the global SSL pinning policy using `TSKPinningValidator`. + + */ +typedef NS_ENUM(NSInteger, TSKTrustDecision) +{ + /** + Based on the server's certificate chain and the global pinning policy for this domain, the SSL connection should be allowed. + This return value does not necessarily mean that the pinning validation succeded (for example if `kTSKEnforcePinning` was set to `NO` for this domain). If a pinning validation failure occured and if a report URI was configured, a pin failure report was sent. + */ + TSKTrustDecisionShouldAllowConnection, + + /** + Based on the server's certificate chain and the global pinning policy for this domain, the SSL connection should be blocked. + A pinning validation failure occured and if a report URI was configured, a pin failure report was sent. + */ + TSKTrustDecisionShouldBlockConnection, + + /** + No pinning policy was configured for this domain and TrustKit did not validate the server's identity. + Because this will happen in an authentication handler, it means that the server's _serverTrust_ object __needs__ to be verified against the device's trust store using `SecTrustEvaluate()`. Failing to do so will __disable SSL certificate validation__. + */ + TSKTrustDecisionDomainNotPinned, +}; diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index ee81bdad..810df876 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -10,34 +10,12 @@ */ #import +#import "TSKPinValidatorResult.h" +#import "Pinning/TSKPublicKeyAlgorithm.h" +@class TSKPinningValidatorResult; - -/** - Possible return values when verifying a server's identity against the global SSL pinning policy using `TSKPinningValidator`. - - */ -typedef NS_ENUM(NSInteger, TSKTrustDecision) -{ -/** - Based on the server's certificate chain and the global pinning policy for this domain, the SSL connection should be allowed. - This return value does not necessarily mean that the pinning validation succeded (for example if `kTSKEnforcePinning` was set to `NO` for this domain). If a pinning validation failure occured and if a report URI was configured, a pin failure report was sent. - */ - TSKTrustDecisionShouldAllowConnection, - -/** - Based on the server's certificate chain and the global pinning policy for this domain, the SSL connection should be blocked. - A pinning validation failure occured and if a report URI was configured, a pin failure report was sent. - */ - TSKTrustDecisionShouldBlockConnection, - -/** - No pinning policy was configured for this domain and TrustKit did not validate the server's identity. - Because this will happen in an authentication handler, it means that the server's _serverTrust_ object __needs__ to be verified against the device's trust store using `SecTrustEvaluate()`. Failing to do so will __disable SSL certificate validation__. - */ - TSKTrustDecisionDomainNotPinned, -}; - +typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef certificate, TSKPublicKeyAlgorithm algorithm); /** `TSKPinningValidator` is a class for manually verifying a server's identity against the global SSL pinning policy. @@ -63,6 +41,70 @@ typedef NS_ENUM(NSInteger, TSKTrustDecision) /// @name Manual SSL Pinning Validation ///------------------------------------ +@property (nonatomic, readonly, nullable) NSDictionary *pinnedDomains; +@property (nonatomic, readonly) BOOL ignorePinsForUserTrustAnchors; +@property (nonatomic, readonly, nonnull) dispatch_queue_t validationResultQueue; +@property (nonatomic, readonly, nonnull) void(^validationResultHandler)(TSKPinningValidatorResult * _Nonnull result); + +- (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains + ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors + validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue + validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler; + +/** + Evaluate the supplied server trust against the global SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent. + + When using the `NSURLSession` or `WKWebView` network APIs, the `handleChallenge:completionHandler:` method should be called instead, as it is simpler to use. + + When using low-level network APIs (such as `NSStream`), instructions on how to retrieve the connection's `serverTrust` are available at https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html . + + @param serverTrust The trust object representing the server's certificate chain. The trust's evaluation policy is always overridden using `SecTrustSetPolicies()` to ensure all the proper SSL checks (expiration, hostname validation, etc.) are enabled. + + @param serverHostname The hostname of the server whose identity is being validated. + + @return A `TSKTrustDecision` which describes whether the SSL connection should be allowed or blocked, based on the global pinning policy. + + @warning If no SSL pinning policy was configured for the supplied _serverHostname_, this method has no effect and will return `TSKTrustDecisionDomainNotPinned` without validating the supplied _serverTrust_ at all. This means that the server's _serverTrust_ object __must__ be verified against the device's trust store using `SecTrustEvaluate()`. Failing to do so will __disable SSL certificate validation__. + + @exception NSException Thrown when TrustKit has not been initialized with a pinning policy. + */ +- (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname:(NSString * _Nonnull)serverHostname; + + +/** + Helper method for handling authentication challenges received within a `NSURLSessionDelegate`, `NSURLSessionTaskDelegate` or `WKNavigationDelegate`. + + This method will evaluate the server trust within the authentication challenge against the global SSL pinning policy previously configured, and then call the `completionHandler` with the corresponding `disposition` and `credential`. For example, this method can be leveraged in a `WKNavigationDelegate` challenge handler method: + + - (void)webView:(WKWebView *)webView + didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential *credential))completionHandler + { + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) + { + [TSKPinningValidator handleChallenge:challenge completionHandler:completionHandler]; + } + } + + @param challenge The authentication challenge, supplied by the URL loading system to the delegate's challenge handler method. + + @param completionHandler A block to invoke to respond to the challenge, supplied by the URL loading system to the delegate's challenge handler method. + + @return `YES` if the challenge was handled and the `completionHandler` was successfuly invoked. `NO` if the challenge could not be handled because it was not for server certificate validation (ie. the challenge's `authenticationMethod` was not `NSURLAuthenticationMethodServerTrust`). + + @exception NSException Thrown when TrustKit has not been initialized with a pinning policy. + */ +- (BOOL) handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge + completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential * _Nullable credential))completionHandler; + +@end + +#pragma mark Global Singleton-Based Methods Category + +@interface TSKPinningValidator (GlobalTrustKit) + /** Evaluate the supplied server trust against the global SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent. diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index 42fc58b9..6a804c51 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -10,19 +10,52 @@ */ #import "TrustKit+Private.h" +#import "TSKPinValidatorResult.h" +#import "TSKPinningValidatorResult.h" +#import "Pinning/TSKSPKIHashCache.h" +@interface TSKPinningValidator () +@property (nonatomic) TSKSPKIHashCache *spkiHashCache; +@end + @implementation TSKPinningValidator +#pragma mark Class Methods For Shared Singleton + + (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname:(NSString * _Nonnull)serverHostname { - TSKTrustDecision finalTrustDecision = TSKTrustDecisionShouldBlockConnection; - - if ([TrustKit wasTrustKitInitialized] == NO) - { - [NSException raise:@"TrustKit not initialized" - format:@"TrustKit has not been initialized with a pinning configuration"]; + TrustKit *trustKit = [TrustKit sharedInstance]; + return [trustKit.pinningValidator evaluateTrust:serverTrust forHostname:serverHostname]; +} + ++ (BOOL) handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler +{ + TrustKit *trustKit = [TrustKit sharedInstance]; + return [trustKit.pinningValidator handleChallenge:challenge completionHandler:completionHandler]; +} + +#pragma mark Instance Methods + +- (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains + ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors + validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue + validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler +{ + self = [super init]; + if (self) { + _pinnedDomains = pinnedDomains; + _ignorePinsForUserTrustAnchors = ignorePinsForUserTrustAnchors; + _validationResultQueue = validationResultQueue; + _validationResultHandler = validationResultHandler; + _spkiHashCache = [TSKSPKIHashCache new]; } + return self; +} + +- (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname:(NSString * _Nonnull)serverHostname +{ + TSKTrustDecision finalTrustDecision = TSKTrustDecisionShouldBlockConnection; if ((serverTrust == NULL) || (serverHostname == nil)) { @@ -35,8 +68,7 @@ + (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname NSTimeInterval validationStartTime = [NSDate timeIntervalSinceReferenceDate]; // Retrieve the pinning configuration for this specific domain, if there is one - NSDictionary *trustKitConfig = [TrustKit configuration]; - NSString *domainConfigKey = getPinningConfigurationKeyForDomain(serverHostname, trustKitConfig); + NSString *domainConfigKey = getPinningConfigurationKeyForDomain(serverHostname, self.pinnedDomains); if (domainConfigKey == nil) { // The domain is not pinned: nothing to do/validate @@ -45,7 +77,7 @@ + (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname else { // This domain is pinned - NSDictionary *domainConfig = trustKitConfig[kTSKPinnedDomains][domainConfigKey]; + NSDictionary *domainConfig = self.pinnedDomains[domainConfigKey]; // Has the pinning policy expired? NSDate *expirationDate = domainConfig[kTSKExpirationDate]; @@ -59,7 +91,7 @@ + (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname { // The pinning policy has not expired // Look for one the configured public key pins in the server's evaluated certificate chain - TSKPinValidationResult validationResult = verifyPublicKeyPin(serverTrust, serverHostname, domainConfig[kTSKPublicKeyAlgorithms], domainConfig[kTSKPublicKeyHashes]); + TSKPinValidationResult validationResult = verifyPublicKeyPin(serverTrust, serverHostname, domainConfig[kTSKPublicKeyAlgorithms], domainConfig[kTSKPublicKeyHashes], self.spkiHashCache); if (validationResult == TSKPinValidationResultSuccess) { // Pin validation was successful @@ -72,7 +104,7 @@ + (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname TSKLog(@"Pin validation failed for %@", serverHostname); #if !TARGET_OS_IPHONE if ((validationResult == TSKPinValidationResultFailedUserDefinedTrustAnchor) - && ([trustKitConfig[kTSKIgnorePinningForUserDefinedTrustAnchors] boolValue] == YES)) + && (self.ignorePinsForUserTrustAnchors) { // OS-X only: user-defined trust anchors can be whitelisted (for corporate proxies, etc.) so don't send reports TSKLog(@"Ignoring pinning failure due to user-defined trust anchor for %@", serverHostname); @@ -102,8 +134,19 @@ + (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname } } // Send a notification after all validation is done; this will also trigger a report if pin validation failed - NSTimeInterval validationDuration = [NSDate timeIntervalSinceReferenceDate] - validationStartTime; - sendValidationNotification_async(serverHostname, serverTrust, domainConfigKey, validationResult, finalTrustDecision, validationDuration); + if (self.validationResultQueue && self.validationResultHandler) { + dispatch_async(self.validationResultQueue, ^{ + TSKPinningValidatorResult *result = [TSKPinningValidatorResult new]; + result.serverHostname = serverHostname; + result.serverTrust = serverTrust; + result.notedHostname = domainConfigKey; + result.validationResult = validationResult; + result.finalTrustDecision = finalTrustDecision; + result.validationDuration = [NSDate timeIntervalSinceReferenceDate] - validationStartTime; + + self.validationResultHandler(result); + }); + } } } CFRelease(serverTrust); @@ -112,7 +155,7 @@ + (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname } -+ (BOOL) handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler +- (BOOL) handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler { BOOL wasChallengeHandled = NO; if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) @@ -121,7 +164,7 @@ + (BOOL) handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge comp SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; NSString *serverHostname = challenge.protectionSpace.host; - TSKTrustDecision trustDecision = [TSKPinningValidator evaluateTrust:serverTrust forHostname:serverHostname]; + TSKTrustDecision trustDecision = [self evaluateTrust:serverTrust forHostname:serverHostname]; if (trustDecision == TSKTrustDecisionShouldAllowConnection) { // Success diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h new file mode 100644 index 00000000..18d903e7 --- /dev/null +++ b/TrustKit/TSKPinningValidatorResult.h @@ -0,0 +1,25 @@ +/* + + TSKPinningValidator.h + TrustKit + + Copyright 2015 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ + +#import +#import "TSKPinValidatorResult.h" + +@interface TSKPinningValidatorResult : NSObject + +@property (nonatomic, nonnull) NSString *serverHostname; +@property (nonatomic, nonnull) SecTrustRef serverTrust; +@property (nonatomic, nonnull) NSString *notedHostname; +@property (nonatomic) TSKPinValidationResult validationResult; +@property (nonatomic) TSKTrustDecision finalTrustDecision; +@property (nonatomic) NSTimeInterval validationDuration; +@property (nonatomic, readonly, nullable) NSArray *certificateChain; + +@end diff --git a/TrustKit/TSKPinningValidatorResult.m b/TrustKit/TSKPinningValidatorResult.m new file mode 100644 index 00000000..a6ae217c --- /dev/null +++ b/TrustKit/TSKPinningValidatorResult.m @@ -0,0 +1,29 @@ +/* + + TSKPinningValidator.h + TrustKit + + Copyright 2015 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ + +#import "TSKPinningValidatorResult.h" +#import "Reporting/reporting_utils.h" + +@implementation TSKPinningValidatorResult + +@synthesize certificateChain=_certificateChain; + +- (NSArray * _Nullable)certificateChain +{ + if (!_certificateChain) { + // Convert the server trust to a certificate chain + // This cannot be done in the dispatch_async() block as sometimes the serverTrust seems to become invalid once the block gets scheduled, even tho its retain count is still positive + _certificateChain = convertTrustToPemArray(self.serverTrust); + } + return _certificateChain; +} + +@end diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 62e4304c..f60e346b 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -334,6 +334,16 @@ FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationServerHostnameN ///--------------------- /// @name Initialization ///--------------------- +#pragma mark Class Methods + + +/** + Access the shared TrustKit singleton instance. Raises an exception if +initializeWithConfiguration: + has not yet been invoked. + + @return the shared TrustKit singleton + */ ++ (instancetype)sharedInstance; /** Initialize the global SSL pinning policy with the supplied configuration. @@ -344,7 +354,7 @@ FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationServerHostnameN @exception NSException Thrown when the supplied configuration is invalid or TrustKit has already been initialized. */ -+ (void) initializeWithConfiguration:(NSDictionary *)trustKitConfig; ++ (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig; ///---------------------------- @@ -356,7 +366,7 @@ FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationServerHostnameN @return A dictionary with a copy of the current TrustKit configuration, or `nil` if TrustKit has not been initialized. */ -+ (nullable NSDictionary *) configuration; ++ (NSDictionary * _Nullable) configuration; /** Set the global logger. @@ -365,7 +375,38 @@ FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationServerHostnameN If a global logger is not set, the default logger will be used, which will print TrustKit log messages (using `NSLog()`) when the App is built in Debug mode. If the App was built for Release, the default logger will not print any messages at all. */ -+ (void)setLoggerBlock:(void (^)(NSString *))block; ++ (void)setLoggerBlock:(void (^ _Nullable)(NSString * _Nonnull))block; + +#pragma mark Instance Methods + +/** + Initialize a TrustKit instance with the supplied SSL pinning policy configuration. + + This method should be called as early as possible in the App's lifecycle to ensure that the App's very first SSL connections are validated by TrustKit. Once TrustKit has been initialized, notifications will be posted for any SSL pinning validation performed. + + @param trustKitConfig A dictionary containing various keys for configuring the global SSL pinning policy. + */ +- (instancetype)initWithConfiguration:(NSDictionary * _Nullable)trustKitConfig; + +/** + Retrieve the SSL pinning policy for this instance. + + @return A dictionary with the current TrustKit configuration + */ +@property (nonatomic, readonly, nullable) NSDictionary *configuration; + +/** + Set the logger used by this instance, used when TrustKit needs to display a message to the developer. + + If a global logger is not set, the default logger will be used, which will print TrustKit log messages (using `NSLog()`) when the App is built in Debug mode. If the App was built for Release, the default logger will not print any messages at all. + */ +@property (nonatomic, nullable) void(^loggerBlock)(NSString * _Nonnull msg); + + +/** + A pinning validator instance conforming to the configuration of this TrustKit instance. + */ +@property (nonatomic, nonnull) TSKPinningValidator *pinningValidator; @end NS_ASSUME_NONNULL_END diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index b515f61e..e95d8522 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -10,12 +10,11 @@ */ #import "TrustKit+Private.h" -#import "Pinning/public_key_utils.h" #import "Reporting/TSKBackgroundReporter.h" #import "Swizzling/TSKNSURLConnectionDelegateProxy.h" #import "Swizzling/TSKNSURLSessionDelegateProxy.h" #import "parse_configuration.h" -#import "Reporting/reporting_utils.h" +#import "TSKPinningValidatorResult.h" NSString * const TrustKitVersion = @"1.4.1"; @@ -59,19 +58,10 @@ #pragma mark TrustKit Global State -// Global dictionary for storing the public key hashes and domains -static NSDictionary *_trustKitGlobalConfiguration = nil; +// Shared TrustKit singleton instance +static TrustKit *sharedTrustKit = nil; -// Global preventing multiple initializations (double method swizzling, etc.) -static BOOL _isTrustKitInitialized = NO; -static dispatch_once_t dispatchOnceTrustKitInit; - -// Reporter for sending pin violation reports -static TSKBackgroundReporter *_pinFailureReporter = nil; static char kTSKPinFailureReporterQueueLabel[] = "com.datatheorem.trustkit.reporterqueue"; -static dispatch_queue_t _pinFailureReporterQueue = NULL; -static id _pinValidationObserver = nil; - // Default report URI - can be disabled with TSKDisableDefaultReportUri // Email info@datatheorem.com if you need a free dashboard to see your App's reports @@ -100,38 +90,106 @@ void TSKLog(NSString *format, ...) } -#pragma mark Helper Function to Send Notifications and Reports +#pragma mark TrustKit Initialization Helper Functions + +@interface TrustKit () +@property (nonatomic) TSKBackgroundReporter *pinFailureReporter; +@property (nonatomic) dispatch_queue_t pinFailureReporterQueue; +@end + +@implementation TrustKit + +#pragma mark Shared TrustKit Explicit Initialization + ++ (instancetype)sharedInstance +{ + if (!sharedTrustKit) { + // TrustKit should only be initialized once so we don't double interpose SecureTransport or get into anything unexpected + [NSException raise:@"TrustKit was not initialized" + format:@"TrustKit must be initialized using +initializeWithConfiguration: prior to accessing sharedInstance"]; + } + return sharedTrustKit; +} -// Send a notification and release the serverTrust -void sendValidationNotification_async(NSString *serverHostname, SecTrustRef serverTrust, NSString *notedHostname, TSKPinValidationResult validationResult, TSKTrustDecision finalTrustDecision, NSTimeInterval validationDuration) ++ (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig { - // Convert the server trust to a certificate chain - // This cannot be done in the dispatch_async() block as sometimes the serverTrust seems to become invalid once the block gets scheduled, even tho its retain count is still positive - CFRetain(serverTrust); - NSArray *certificateChain = convertTrustToPemArray(serverTrust); - CFRelease(serverTrust); + TSKLog(@"Configuration passed via explicit call to initializeWithConfiguration:"); - // Send the notification to consumers that want to get notified about all validations performed - // We use the _pinFailureReporterQueue so our receving block sendReportFromNotificationBlock gets executed on this queue as well - dispatch_async(_pinFailureReporterQueue, ^(void) - { - [[NSNotificationCenter defaultCenter] postNotificationName:kTSKValidationCompletedNotification - object:nil - userInfo:@{kTSKValidationDurationNotificationKey: @(validationDuration), - kTSKValidationDecisionNotificationKey: @(finalTrustDecision), - kTSKValidationResultNotificationKey: @(validationResult), - kTSKValidationCertificateChainNotificationKey: certificateChain, - kTSKValidationNotedHostnameNotificationKey: notedHostname, - kTSKValidationServerHostnameNotificationKey: serverHostname}]; - }); + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedTrustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + + // Hook network APIs if needed + if ([sharedTrustKit.configuration[kTSKSwizzleNetworkDelegates] boolValue]) { + // NSURLConnection + [TSKNSURLConnectionDelegateProxy swizzleNSURLConnectionConstructors]; + + // NSURLSession + [TSKNSURLSessionDelegateProxy swizzleNSURLSessionConstructors]; + } + }); +} + ++ (void)setLoggerBlock:(void (^)(NSString *))block +{ + TrustKit *singleton = [self sharedInstance]; + singleton.loggerBlock = block; +} + ++ (NSDictionary * _Nullable)configuration +{ + TrustKit *singleton = [self sharedInstance]; + return singleton.configuration; } +#pragma mark Instance + +- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig +{ + NSParameterAssert(trustKitConfig); + if (!trustKitConfig) { + return nil; + } + + self = [super init]; + if (self && [trustKitConfig count] > 0) { + // Convert and store the SSL pins in our global variable + _configuration = parseTrustKitConfiguration(trustKitConfig); + + // Create a dispatch queue for activating the reporter + // We use a serial queue targetting the global default queue in order to ensure reports are sent one by one + // even when a lot of pin failures are occuring, instead of spamming the global queue with events to process + _pinFailureReporterQueue = dispatch_queue_create(kTSKPinFailureReporterQueueLabel, DISPATCH_QUEUE_SERIAL); + + // Create our reporter for sending pin validation failures; do this before hooking NSURLSession so we don't hook ourselves + _pinFailureReporter = [[TSKBackgroundReporter alloc] initAndRateLimitReports:YES]; + + // Configure the pinning validator and register for pinning callbacks in order to + // trigger reports on the pinning failure reporter background queue. +#if TARGET_OS_IPHONE + BOOL userTrustAnchorBypass = NO; +#else + BOOL userTrustAnchorBypass = [_configuration[kTSKIgnorePinningForUserDefinedTrustAnchors] boolValue]; +#endif + __weak typeof(self) weakSelf = self; + _pinningValidator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:_configuration[kTSKPinnedDomains] + ignorePinsForUserTrustAnchors:userTrustAnchorBypass + validationResultQueue:_pinFailureReporterQueue + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { + [weakSelf sendValidationReport:result]; + }]; + + TSKLog(@"Successfully initialized with configuration %@", _configuration); + } + return self; +} + +#pragma mark Notification Handlers // The block which receives pin validation notification and turns them into pin validation reports -static void (^sendReportFromNotificationBlock)(NSNotification *note) = ^void(NSNotification *note) +- (void)sendValidationReport:(TSKPinningValidatorResult *)result { - NSDictionary *userInfo = [note userInfo]; - TSKPinValidationResult validationResult = [userInfo[kTSKValidationResultNotificationKey] integerValue]; + TSKPinValidationResult validationResult = result.validationResult; // Send a report only if the there was a pinning failure if (validationResult != TSKPinValidationResultSuccess) @@ -140,8 +198,8 @@ static void (^sendReportFromNotificationBlock)(NSNotification *note) = ^void(NSN if (validationResult != TSKPinValidationResultFailedUserDefinedTrustAnchor) #endif { - NSString *notedHostname = userInfo[kTSKValidationNotedHostnameNotificationKey]; - NSDictionary *notedHostnameConfig = _trustKitGlobalConfiguration[kTSKPinnedDomains][notedHostname]; + NSString *notedHostname = result.notedHostname; + NSDictionary *notedHostnameConfig = self.configuration[kTSKPinnedDomains][notedHostname]; // Pin validation failed: retrieve the list of configured report URLs NSMutableArray *reportUris = [NSMutableArray arrayWithArray:notedHostnameConfig[kTSKReportUris]]; @@ -153,142 +211,50 @@ static void (^sendReportFromNotificationBlock)(NSNotification *note) = ^void(NSN } // If some report URLs have been defined, send the pin failure report - if ((reportUris != nil) && ([reportUris count] > 0)) + if (reportUris.count > 0) { - [_pinFailureReporter pinValidationFailedForHostname:userInfo[kTSKValidationServerHostnameNotificationKey] - port:nil - certificateChain:userInfo[kTSKValidationCertificateChainNotificationKey] - notedHostname:notedHostname - reportURIs:reportUris - includeSubdomains:[notedHostnameConfig[kTSKIncludeSubdomains] boolValue] - enforcePinning:[notedHostnameConfig[kTSKEnforcePinning] boolValue] - knownPins:notedHostnameConfig[kTSKPublicKeyHashes] - validationResult:validationResult - expirationDate:notedHostnameConfig[kTSKExpirationDate]]; + [self.pinFailureReporter pinValidationFailedForHostname:result.serverHostname + port:nil + certificateChain:result.certificateChain + notedHostname:notedHostname + reportURIs:reportUris + includeSubdomains:[notedHostnameConfig[kTSKIncludeSubdomains] boolValue] + enforcePinning:[notedHostnameConfig[kTSKEnforcePinning] boolValue] + knownPins:notedHostnameConfig[kTSKPublicKeyHashes] + validationResult:validationResult + expirationDate:notedHostnameConfig[kTSKExpirationDate]]; } } } -}; - - -#pragma mark TrustKit Initialization Helper Functions - -static void initializeTrustKit(NSDictionary *trustKitConfig) -{ - if (trustKitConfig == nil) - { - return; - } - - if (_isTrustKitInitialized == YES) - { - // TrustKit should only be initialized once so we don't double interpose SecureTransport or get into anything unexpected - [NSException raise:@"TrustKit already initialized" - format:@"TrustKit was already initialized with the following SSL pins: %@", _trustKitGlobalConfiguration]; - } - - if ([trustKitConfig count] > 0) - { - initializeSubjectPublicKeyInfoCache(); - - // Convert and store the SSL pins in our global variable - _trustKitGlobalConfiguration = [[NSDictionary alloc]initWithDictionary:parseTrustKitConfiguration(trustKitConfig)]; - - - // We use dispatch_once() here only so that unit tests don't reset the reporter - // or the swizzling logic when calling [TrustKit resetConfiguration] - dispatch_once(&dispatchOnceTrustKitInit, ^{ - // Create our reporter for sending pin validation failures; do this before hooking NSURLSession so we don't hook ourselves - _pinFailureReporter = [[TSKBackgroundReporter alloc]initAndRateLimitReports:YES]; - - - // Create a dispatch queue for activating the reporter - // We use a serial queue targetting the global default queue in order to ensure reports are sent one by one - // even when a lot of pin failures are occuring, instead of spamming the global queue with events to process - _pinFailureReporterQueue = dispatch_queue_create(kTSKPinFailureReporterQueueLabel, DISPATCH_QUEUE_SERIAL); - dispatch_set_target_queue(_pinFailureReporterQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); - - - // Register for pinning notifications in order to trigger reports - // Nil queue to run the block on the _pinFailureReporterQueue (where the notification is posted from) - _pinValidationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:sendReportFromNotificationBlock]; - - // Hook network APIs if needed - if ([_trustKitGlobalConfiguration[kTSKSwizzleNetworkDelegates] boolValue] == YES) - { - // NSURLConnection - [TSKNSURLConnectionDelegateProxy swizzleNSURLConnectionConstructors]; - - // NSURLSession - [TSKNSURLSessionDelegateProxy swizzleNSURLSessionConstructors]; - } - }); - - // All done - _isTrustKitInitialized = YES; - TSKLog(@"Successfully initialized with configuration %@", _trustKitGlobalConfiguration); - } -} - - -@implementation TrustKit - - -#pragma mark TrustKit Explicit Initialization - -+ (void) initializeWithConfiguration:(NSDictionary *)trustKitConfig -{ - TSKLog(@"Configuration passed via explicit call to initializeWithConfiguration:"); - initializeTrustKit(trustKitConfig); } -+ (void)setLoggerBlock:(void (^)(NSString *))block -{ - _loggerBlock = block; -} - - # pragma mark Private / Test Methods -+ (NSDictionary *) configuration -{ - return [_trustKitGlobalConfiguration copy]; -} - - -+ (BOOL) wasTrustKitInitialized -{ - return _isTrustKitInitialized; -} - - -+ (void) resetConfiguration ++ (void)resetConfiguration { + //sharedTrustKitOnceToken = 0; // Reset is only available/used for tests - resetSubjectPublicKeyInfoCache(); - _trustKitGlobalConfiguration = nil; - _isTrustKitInitialized = NO; + //resetSubjectPublicKeyInfoCache(); + //_spkiHashCache = [TSKSPKIHashCache new]; + //_configuration = nil; +// _isTrustKitInitialized = NO; } - -+ (NSString *) getDefaultReportUri ++ (NSString *)getDefaultReportUri { return kTSKDefaultReportUri; } - -+ (TSKBackgroundReporter *) getGlobalPinFailureReporter ++ (TSKBackgroundReporter *)getGlobalPinFailureReporter { - return _pinFailureReporter; + TrustKit *singleton = [self sharedInstance]; + return singleton.pinFailureReporter; } - -+ (void) setGlobalPinFailureReporter:(TSKBackgroundReporter *) reporter ++ (void)setGlobalPinFailureReporter:(TSKBackgroundReporter *) reporter { - _pinFailureReporter = reporter; + TrustKit *singleton = [self sharedInstance]; + singleton.pinFailureReporter = reporter; } @end @@ -299,20 +265,20 @@ + (void) setGlobalPinFailureReporter:(TSKBackgroundReporter *) reporter // TRUSTKIT_SKIP_LIB_INITIALIZATION define allows consumers to opt out of the dylib constructor. // This might be useful to mitigate integration risks, if the consumer doens't wish to use // plist file, and wants to initialize lib manually later on. -#ifndef TRUSTKIT_SKIP_LIB_INITIALIZATION - -__attribute__((constructor)) static void initializeWithInfoPlist(int argc, const char **argv) -{ - // TrustKit just got started in the App - CFBundleRef appBundle = CFBundleGetMainBundle(); - - // Retrieve the configuration from the App's Info.plist file - NSDictionary *trustKitConfigFromInfoPlist = (__bridge NSDictionary *)CFBundleGetValueForInfoDictionaryKey(appBundle, (__bridge CFStringRef)kTSKConfiguration); - if (trustKitConfigFromInfoPlist) - { - TSKLog(@"Configuration supplied via the App's Info.plist"); - initializeTrustKit(trustKitConfigFromInfoPlist); - } -} - -#endif +//#ifndef TRUSTKIT_SKIP_LIB_INITIALIZATION +// +//__attribute__((constructor)) static void initializeWithInfoPlist(int argc, const char **argv) +//{ +// // TrustKit just got started in the App +// CFBundleRef appBundle = CFBundleGetMainBundle(); +// +// // Retrieve the configuration from the App's Info.plist file +// NSDictionary *trustKitConfigFromInfoPlist = (__bridge NSDictionary *)CFBundleGetValueForInfoDictionaryKey(appBundle, (__bridge CFStringRef)kTSKConfiguration); +// if (trustKitConfigFromInfoPlist) +// { +// TSKLog(@"Configuration supplied via the App's Info.plist"); +// initializeTrustKit(trustKitConfigFromInfoPlist); +// } +//} +// +//#endif diff --git a/TrustKit/parse_configuration.m b/TrustKit/parse_configuration.m index 59306006..d4185eea 100644 --- a/TrustKit/parse_configuration.m +++ b/TrustKit/parse_configuration.m @@ -10,7 +10,7 @@ #import "TrustKit.h" #import "Dependencies/domain_registry/domain_registry.h" #import "parse_configuration.h" -#import "Pinning/public_key_utils.h" +#import "Pinning/TSKPublicKeyAlgorithm.h" #import @@ -230,7 +230,7 @@ finalConfiguration[kTSKPinnedDomains][domainName] = [NSDictionary dictionaryWithDictionary:domainFinalConfiguration]; } - return finalConfiguration; + return [finalConfiguration copy]; } From c8fd2e0aff3c557251ca9eaa54a609ea987da16b Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Mon, 20 Mar 2017 11:28:41 -0400 Subject: [PATCH 002/126] =?UTF-8?q?Fixed=20tests=20and=20more=20small=20ch?= =?UTF-8?q?anges:=20-=20Streamlined=20logging=20and=20removed=20loggerBloc?= =?UTF-8?q?k.=20We=20should=20switch=20to=20=20=20os=5Flog=20if=20availabl?= =?UTF-8?q?e=20(iOS=2010)=20and=20perhaps=20reconsider=20providing=20=20?= =?UTF-8?q?=20a=20backwards=20compatible=20solution=20unless=20it's=20real?= =?UTF-8?q?ly=20compelling.=20-=20Re-indented=20TrustKit.h=20-=20Switched?= =?UTF-8?q?=20TrustKit=20to=20use=20GCD=20block=20callbacks=20for=20valida?= =?UTF-8?q?tion=20result=20=20=20instead=20of=20notifications.=20It's=20a?= =?UTF-8?q?=20lot=20more=20performant=20to=20not=20dispatch=20=20=20main?= =?UTF-8?q?=20thread=20notifications=20for=20every=20network=20requests=20?= =?UTF-8?q?unless=20they=20are=20=20=20actually=20needed.=20It's=20also=20?= =?UTF-8?q?cleaner=20to=20not=20support=20loose=20coupling=20=E2=80=93=20a?= =?UTF-8?q?pp=20=20=20can=20do=20whatever=20they=20want=20in=20the=20callb?= =?UTF-8?q?ack,=20including=20to=20send=20NSNotification.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TrustKit.xcodeproj/project.pbxproj | 19 +- .../xcshareddata/xcschemes/TrustKit.xcscheme | 4 +- TrustKit/Pinning/TSKSPKIHashCache.m | 2 +- TrustKit/TSKPinningValidatorResult.h | 43 ++++ TrustKit/TrustKit+Private.h | 12 +- TrustKit/TrustKit-Prefix.pch | 7 + TrustKit/TrustKit.h | 227 +++++++++--------- TrustKit/TrustKit.m | 52 ++-- TrustKitTests/TSKLoggerTests.m | 89 +++---- TrustKitTests/TSKNSURLConnectionTests.m | 11 +- TrustKitTests/TSKPinConfigurationTests.m | 2 +- TrustKitTests/TSKPinningValidatorTests.m | 63 +++-- TrustKitTests/TSKPublicKeyAlgorithmTests.m | 28 +-- TrustKitTests/TSKTestURLSessionDelegate.h | 13 + TrustKitTests/TSKTestURLSessionDelegate.m | 13 + 15 files changed, 346 insertions(+), 239 deletions(-) create mode 100644 TrustKit/TrustKit-Prefix.pch create mode 100644 TrustKitTests/TSKTestURLSessionDelegate.h create mode 100644 TrustKitTests/TSKTestURLSessionDelegate.m diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index cf8c1019..d1e6475b 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -28,10 +28,7 @@ 8C15F9A01B16094D00F06C0E /* TSKPinFailureReport.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C15F99E1B16094D00F06C0E /* TSKPinFailureReport.h */; }; 8C15F9A11B16094E00F06C0E /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C15F99F1B16094D00F06C0E /* TSKPinFailureReport.m */; }; 8C15F9A41B17564400F06C0E /* TSKPinConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C15F9A31B17564400F06C0E /* TSKPinConfigurationTests.m */; }; - 8C4346D61E5B894A008023F9 /* configuration_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C4346D41E5B894A008023F9 /* configuration_utils.h */; }; 8C4346D71E5B894A008023F9 /* configuration_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C4346D41E5B894A008023F9 /* configuration_utils.h */; }; - 8C4346D81E5B894A008023F9 /* configuration_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C4346D41E5B894A008023F9 /* configuration_utils.h */; }; - 8C4346D91E5B894A008023F9 /* configuration_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C4346D41E5B894A008023F9 /* configuration_utils.h */; }; 8C4346DA1E5B894A008023F9 /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C4346D51E5B894A008023F9 /* configuration_utils.m */; }; 8C4346DB1E5B894A008023F9 /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C4346D51E5B894A008023F9 /* configuration_utils.m */; }; 8C4346DC1E5B894A008023F9 /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C4346D51E5B894A008023F9 /* configuration_utils.m */; }; @@ -227,6 +224,7 @@ 8CE919221AEA077F002B29AE /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */; }; 8CE919251AEA07C5002B29AE /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */; }; 8CE9192D1AEA0F7E002B29AE /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919291AEA0F7E002B29AE /* domain_registry.h */; }; + FC06FE821E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FC06FE811E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.m */; }; FC1A09001E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; FC1A09011E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; FC1A09021E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; @@ -382,6 +380,9 @@ 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ssl_pin_verifier.m; path = Pinning/ssl_pin_verifier.m; sourceTree = ""; }; 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ssl_pin_verifier.h; path = Pinning/ssl_pin_verifier.h; sourceTree = ""; }; 8CE919291AEA0F7E002B29AE /* domain_registry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = domain_registry.h; path = Dependencies/domain_registry/domain_registry.h; sourceTree = ""; }; + FC06FE7F1E7C3FFA00AF0B4D /* TrustKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TrustKit-Prefix.pch"; sourceTree = ""; }; + FC06FE801E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKTestURLSessionDelegate.h; sourceTree = ""; }; + FC06FE811E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKTestURLSessionDelegate.m; sourceTree = ""; }; FC1A08FD1E579F630055B12C /* TSKPinValidatorResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinValidatorResult.h; sourceTree = ""; }; FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; @@ -536,17 +537,17 @@ 8CE9191B1AEA072A002B29AE /* Pinning */, 8C84804C1A896EE30017C155 /* TrustKit.h */, 8C84806C1A896F660017C155 /* TrustKit.m */, + 2FA286123F801C437F35D240 /* TrustKit+Private.h */, 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */, 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */, FC1A08FD1E579F630055B12C /* TSKPinValidatorResult.h */, FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */, FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */, - 2FA286123F801C437F35D240 /* TrustKit+Private.h */, - 8C84804A1A896EE30017C155 /* Supporting Files */, 8C5D98B21CEFF079008E654B /* parse_configuration.m */, 8C5D98B61CEFF103008E654B /* parse_configuration.h */, 8C4346D41E5B894A008023F9 /* configuration_utils.h */, 8C4346D51E5B894A008023F9 /* configuration_utils.m */, + 8C84804A1A896EE30017C155 /* Supporting Files */, ); path = TrustKit; sourceTree = ""; @@ -555,6 +556,7 @@ isa = PBXGroup; children = ( 8C84804B1A896EE30017C155 /* Info.plist */, + FC06FE7F1E7C3FFA00AF0B4D /* TrustKit-Prefix.pch */, ); name = "Supporting Files"; sourceTree = ""; @@ -574,6 +576,8 @@ 8CC78B1F1B1B586F00523A25 /* TSKCertificateUtils.m */, 8CC78B241B1B616500523A25 /* TSKPublicKeyAlgorithmTests.m */, 8C80249C1D750D0100678959 /* TSKLoggerTests.m */, + FC06FE801E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.h */, + FC06FE811E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.m */, ); path = TrustKitTests; sourceTree = ""; @@ -1152,6 +1156,7 @@ 6B032D401AF1AEC200EAFA69 /* TSKReporterTests.m in Sources */, 8CD5F7571BCB7219005801D8 /* TSKNSURLSessionTests.m in Sources */, 8CC78B201B1B586F00523A25 /* TSKCertificateUtils.m in Sources */, + FC06FE821E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1445,6 +1450,8 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "TrustKit/TrustKit-Prefix.pch"; INFOPLIST_FILE = TrustKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -1473,6 +1480,8 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "TrustKit/TrustKit-Prefix.pch"; INFOPLIST_FILE = TrustKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 10.0; diff --git a/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit.xcscheme b/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit.xcscheme index 2011ef6d..380aae95 100644 --- a/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit.xcscheme +++ b/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit.xcscheme @@ -22,10 +22,10 @@ + buildForAnalyzing = "NO"> + +#endif diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 4f04b2d7..6aa51ed1 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -34,7 +34,8 @@ typedef NSString *TSKGlobalConfigurationKey; /** - A domain-specific configuration key (to defined for a domain under the `kTSKPinnedDomains` key) that can be set in the pinning policy. + A domain-specific configuration key (to defined for a domain under the `kTSKPinnedDomains` + key) that can be set in the pinning policy. */ typedef NSString *TSKDomainConfigurationKey; @@ -43,16 +44,24 @@ typedef NSString *TSKDomainConfigurationKey; /** - A boolean. If set to `YES`, TrustKit will perform method swizzling on the App's `NSURLConnection` and `NSURLSession` delegates in order to automatically add SSL pinning validation to the App's connections. + A boolean. If set to `YES`, TrustKit will perform method swizzling on the App's + `NSURLConnection` and `NSURLSession` delegates in order to automatically add SSL pinning + validation to the App's connections. - Swizzling allows enabling pinning within an App without having to find and modify each and every instance of `NSURLConnection` or `NSURLSession` delegates. - However, it should only be enabled for simple Apps, as it may not work properly in several scenarios including: + Swizzling allows enabling pinning within an App without having to find and modify each + and every instance of `NSURLConnection` or `NSURLSession` delegates. + However, it should only be enabled for simple Apps, as it may not work properly in several + scenarios including: - * Apps with complex connection delegates, for example to handle client authentication via certificates or basic authentication. - * Apps where method swizzling of the connection delegates is already performed by another module or library (such as Analytics SDKs). + * Apps with complex connection delegates, for example to handle client authentication + via certificates or basic authentication. + * Apps where method swizzling of the connection delegates is already performed by another + module or library (such as Analytics SDKs). * Apps that do no use `NSURLSession` or `NSURLConnection` for their connections. - In such scenarios or if the developer wants a tigher control on the App's networking behavior, `kTSKSwizzleNetworkDelegates` should be set to `NO`; the developer should then manually add pinning validation to the App's authentication handlers. + In such scenarios or if the developer wants a tigher control on the App's networking + behavior, `kTSKSwizzleNetworkDelegates` should be set to `NO`; the developer should then + manually add pinning validation to the App's authentication handlers. See the `TSKPinningValidator` class for instructions on how to do so. */ @@ -62,7 +71,9 @@ FOUNDATION_EXPORT const TSKGlobalConfigurationKey kTSKSwizzleNetworkDelegates; /** A dictionary with domains (such as _www.domain.com_) as keys and dictionaries as values. - Each entry should contain domain-specific settings for performing pinning validation when connecting to the domain, including for example the domain's public key hashes. A list of all domain-specific keys is available in the "Domain-specific Keys" sections. + Each entry should contain domain-specific settings for performing pinning validation when + connecting to the domain, including for example the domain's public key hashes. A list of + all domain-specific keys is available in the "Domain-specific Keys" sections. */ FOUNDATION_EXPORT const TSKGlobalConfigurationKey kTSKPinnedDomains; @@ -72,9 +83,13 @@ FOUNDATION_EXPORT const TSKGlobalConfigurationKey kTSKPinnedDomains; /** - A boolean. If set to `YES`, pinning validation will be skipped if the server's certificate chain terminates at a user-defined trust anchor (such as a root CA that isn't part of OS X's default trust store) and no pin failure reports will be sent; default value is `YES`. + A boolean. If set to `YES`, pinning validation will be skipped if the server's certificate + chain terminates at a user-defined trust anchor (such as a root CA that isn't part of OS X's + default trust store) and no pin failure reports will be sent; default value is `YES`. - This is useful for allowing SSL connections through corporate proxies or firewalls. See "How does key pinning interact with local proxies and filters?" within the Chromium security FAQ at https://www.chromium.org/Home/chromium-security/security-faq for more information. + This is useful for allowing SSL connections through corporate proxies or firewalls. See + "How does key pinning interact with local proxies and filters?" within the Chromium security + FAQ at https://www.chromium.org/Home/chromium-security/security-faq for more information. Only available on macOS. */ @@ -84,17 +99,23 @@ FOUNDATION_EXPORT const TSKGlobalConfigurationKey kTSKIgnorePinningForUserDefine #pragma mark Domain-Specific Configuration Keys - Required /** - An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate's Subject Public Key Info. + An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate's + Subject Public Key Info. - TrustKit will verify that at least one of the specified pins is found in the server's evaluated certificate chain. + TrustKit will verify that at least one of the specified pins is found in the server's + evaluated certificate chain. */ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKPublicKeyHashes; /** - An array of `TSKSupportedAlgorithm` constants to specify the public key algorithms for the keys to be pinned. + An array of `TSKSupportedAlgorithm` constants to specify the public key algorithms for the + keys to be pinned. - TrustKit requires this information in order to compute SSL pins when validating a server's certificate chain, because the `Security` framework does not provide APIs to extract the key's algorithm from an SSL certificate. To minimize the performance impact of Trustkit, only one algorithm should be enabled. + TrustKit requires this information in order to compute SSL pins when validating a server's + certificate chain, because the `Security` framework does not provide APIs to extract the + key's algorithm from an SSL certificate. To minimize the performance impact of Trustkit, + only one algorithm should be enabled. */ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKPublicKeyAlgorithms; @@ -102,23 +123,28 @@ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKPublicKeyAlgorithms; #pragma mark Domain-Specific Configuration Keys - Optional /** - A boolean. If set to `NO`, TrustKit will not block SSL connections that caused a pin or certificate validation error; default value is `YES`. + A boolean. If set to `NO`, TrustKit will not block SSL connections that caused a pin or + certificate validation error; default value is `YES`. - When a pinning failure occurs, pin failure reports will always be sent to the configured report URIs regardless of the value of `kTSKEnforcePinning`. + When a pinning failure occurs, pin failure reports will always be sent to the configured + report URIs regardless of the value of `kTSKEnforcePinning`. */ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKEnforcePinning; /** - A boolean. If set to `YES`, also pin all the subdomains of the specified domain; default value is `NO`. + A boolean. If set to `YES`, also pin all the subdomains of the specified domain; default + value is `NO`. */ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKIncludeSubdomains; /** - A boolean. If set to `YES`, TrustKit will not pin this specific domain if `kTSKIncludeSubdomains` was set for this domain's parent domain. + A boolean. If set to `YES`, TrustKit will not pin this specific domain if `kTSKIncludeSubdomains` + was set for this domain's parent domain. - This allows excluding specific subdomains from a pinning policy that was applied to a parent domain. + This allows excluding specific subdomains from a pinning policy that was applied to a + parent domain. */ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKExcludeSubdomainFromParentPolicy; @@ -126,7 +152,12 @@ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKExcludeSubdomainFromParent /** An array of URLs to which pin validation failures should be reported. - To minimize the performance impact of sending reports on each validation failure, the reports are uploaded using the background transfer service and are also rate-limited to one per day and per type of failure. For HTTPS report URLs, the HTTPS connections will ignore the SSL pinning policy and use the default certificate validation mechanisms, in order to maximize the chance of the reports reaching the server. The format of the reports is similar to the one described in RFC 7469 for the HPKP specification: + To minimize the performance impact of sending reports on each validation failure, the reports + are uploaded using the background transfer service and are also rate-limited to one per day + and per type of failure. For HTTPS report URLs, the HTTPS connections will ignore the SSL + pinning policy and use the default certificate validation mechanisms, in order to maximize + the chance of the reports reaching the server. The format of the reports is similar to the + one described in RFC 7469 for the HPKP specification: { "app-bundle-id": "com.datatheorem.testtrustkit2", @@ -154,17 +185,25 @@ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKReportUris; /** - A boolean. If set to `YES`, the default report URL for sending pin failure reports will be disabled; default value is `NO`. + A boolean. If set to `YES`, the default report URL for sending pin failure reports will + be disabled; default value is `NO`. - By default, pin failure reports are sent to a report server hosted by Data Theorem, for detecting potential CA compromises and man-in-the-middle attacks, as well as providing a free dashboard for developers; email info@datatheorem.com if you'd like a dashboard for your App. Only pin failure reports are sent, which contain the App's bundle ID, the IDFV, and the server's hostname and certificate chain that failed validation. + By default, pin failure reports are sent to a report server hosted by Data Theorem, for + detecting potential CA compromises and man-in-the-middle attacks, as well as providing a + free dashboard for developers; email info@datatheorem.com if you'd like a dashboard for + your App. Only pin failure reports are sent, which contain the App's bundle ID, the IDFV, + and the server's hostname and certificate chain that failed validation. */ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKDisableDefaultReportUri; /** - A string containing the date, in yyyy-MM-dd format, on which the domain's configured SSL pins expire, thus disabling pinning validation. If the key is not set, then the pins do not expire. + A string containing the date, in yyyy-MM-dd format, on which the domain's configured SSL + pins expire, thus disabling pinning validation. If the key is not set, then the pins do + not expire. - Expiration helps prevent connectivity issues in Apps which do not get updates to their pin set, such as when the user disables App updates. + Expiration helps prevent connectivity issues in Apps which do not get updates to their + pin set, such as when the user disables App updates. */ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKExpirationDate; @@ -208,70 +247,20 @@ FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp256r1; */ FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp384r1; - -#pragma mark Pinning Validation Notification Name - /** - The `name` of the notification to be posted for every request that is going through TrustKit's pinning validation mechanism. + `TrustKit` is a class for programmatically configuring the global SSL pinning policy + within an App. - Once TrustKit has been initialized, notifications will be posted with this `name` every time TrustKit validates the certificate chain for a server configured in the SSL pinning policy; if the server's hostname does not have an entry in the pinning policy, no notifications get posted as no pinning validation was performed. + The policy can be set either by adding it to the App's _Info.plist_ under the + `TSKConfiguration` key, or by programmatically supplying it using the `TrustKit` class + described here. Throughout the App's lifecycle, TrustKit can only be initialized once so + only one of the two techniques should be used. - These notifications can be used for performance measurement or to act upon any pinning validation performed by TrustKit (for example to customize the reporting mechanism). The notifications provide details about TrustKit's inner-workings which most Apps should not need to process. Hence, these notifications can be ignored unless the App requires some advanced customization in regards to pinning validation. - */ -FOUNDATION_EXPORT NSString *kTSKValidationCompletedNotification; - - -#pragma mark Pinning Validation Notification UserInfo Keys - -/** - A key to be used to retrieve data about the pinning validation that occured, from the `userInfo` dictionary attached to a `kTSKValidationCompletedNotification` notification. - */ -typedef NSString *TSKNotificationUserInfoKey; - - -/** - The time in seconds it took for the SSL pinning validation to be performed. - */ -FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationDurationNotificationKey; - - -/** - The `TSKPinningValidationResult` returned when validating the server's certificate chain, which represents the result of evaluating the certificate chain against the configured SSL pins for this server. - */ -FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationResultNotificationKey; - -/** - The `TSKTrustDecision` returned when validating the certificate's chain, which describes whether the connection should be blocked or allowed, based on the `TSKPinningValidationResult` returned when evaluating the server's certificate chain and the SSL pining policy configured for this server. - - For example, the pinning validation could have failed (returning `TSKPinningValidationFailed`) but the policy might be set to ignore pinning validation failures for this server, thereby returning `TSKTrustDecisionShouldAllowConnection`. - */ -FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationDecisionNotificationKey; - -/** - The certificate chain returned by the server as an array of PEM-formatted certificates. - */ -FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationCertificateChainNotificationKey; - -/** - The entry within the SSL pinning configuration that was used as the pinning policy for the server being validated. It will be the same as the `kTSKValidationServerHostnameNotificationKey` entry unless the server is a subdomain of a domain configured in the pinning policy with `kTSKIncludeSubdomains` enabled. The corresponding pinning configuration that was used for validation can be retrieved using: - - NSString *notedHostname = userInfo[kTSKValidationNotedHostnameNotificationKey]; - NSDictionary *hostnameConfiguration = [TrustKit configuration][kTSKPinnedDomains][notedHostname]; - */ -FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationNotedHostnameNotificationKey; - -/** - The hostname of the server SSL pinning validation was performed against. - */ -FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationServerHostnameNotificationKey; - - -/** - `TrustKit` is a class for programmatically configuring the global SSL pinning policy within an App. - - The policy can be set either by adding it to the App's _Info.plist_ under the `TSKConfiguration` key, or by programmatically supplying it using the `TrustKit` class described here. Throughout the App's lifecycle, TrustKit can only be initialized once so only one of the two techniques should be used. - - A TrustKit pinning policy is a dictionary which contains some global, App-wide settings (of type `TSKGlobalConfigurationKey`) as well as domain-specific configuration keys (of type `TSKDomainConfigurationKey`) to be defined under the `kTSKPinnedDomains` entry. The following table shows the keys and the types of the corresponding values, and uses indentation to indicate structure: + A TrustKit pinning policy is a dictionary which contains some global, App-wide settings + (of type `TSKGlobalConfigurationKey`) as well as domain-specific configuration keys + (of type `TSKDomainConfigurationKey`) to be defined under the `kTSKPinnedDomains` entry. + The following table shows the keys and the types of the corresponding values, and uses + indentation to indicate structure: ``` | Key | Type | @@ -289,7 +278,8 @@ FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationServerHostnameN | ____ TSKDisableDefaultReportUri | Boolean | ``` - When setting the pinning policy programmatically, it has to be supplied to the `initializeWithConfiguration:` method as a dictionary. For example: + When setting the pinning policy programmatically, it has to be supplied to the + `initializeWithConfiguration:` method as a dictionary. For example: ``` NSDictionary *trustKitConfig = @@ -336,9 +326,13 @@ FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationServerHostnameN TrustKit.initialize(withConfiguration:trustKitConfig) ``` - The various configuration keys that can be specified in the policy are described in the "Constants" section of the documentation. + The various configuration keys that can be specified in the policy are described in the + "Constants" section of the documentation. - Lastly, once TrustKit has been initialized, `kTSKValidationCompletedNotification` notifications will be posted every time TrustKit validates the certificate chain of a server; these notifications provide some information about the validation that was done and can be used for example for performance measurement. + Lastly, once TrustKit has been initialized, `kTSKValidationCompletedNotification` notifications + will be posted every time TrustKit validates the certificate chain of a server; these + notifications provide some information about the validation that was done and can be used + for example for performance measurement. */ @interface TrustKit : NSObject @@ -360,10 +354,14 @@ FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationServerHostnameN /** Initialize the global SSL pinning policy with the supplied configuration. - This method should be called as early as possible in the App's lifecycle to ensure that the App's very first SSL connections are validated by TrustKit. Once TrustKit has been initialized, notifications will be posted for any SSL pinning validation performed. + This method should be called as early as possible in the App's lifecycle to ensure that + the App's very first SSL connections are validated by TrustKit. Once TrustKit has been + initialized, notifications will be posted for any SSL pinning validation performed. - @param trustKitConfig A dictionary containing various keys for configuring the global SSL pinning policy. - @exception NSException Thrown when the supplied configuration is invalid or TrustKit has already been initialized. + @param trustKitConfig A dictionary containing various keys for configuring the global SSL + pinning policy. + @exception NSException Thrown when the supplied configuration is invalid or TrustKit has + already been initialized. */ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig; @@ -376,27 +374,22 @@ FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationServerHostnameN /** Retrieve a copy of the global SSL pinning policy. - @return A dictionary with a copy of the current TrustKit configuration, or `nil` if TrustKit has not been initialized. + @return A dictionary with a copy of the current TrustKit configuration, or `nil` if + TrustKit has not been initialized. */ + (NSDictionary * _Nullable) configuration; -/** - Set the global logger. - - This method sets the global logger, used when TrustKit needs to display a message to the developer. - - If a global logger is not set, the default logger will be used, which will print TrustKit log messages (using `NSLog()`) when the App is built in Debug mode. If the App was built for Release, the default logger will not print any messages at all. - */ -+ (void)setLoggerBlock:(void (^ _Nullable)(NSString * _Nonnull))block; - #pragma mark Instance Methods /** Initialize a TrustKit instance with the supplied SSL pinning policy configuration. - This method should be called as early as possible in the App's lifecycle to ensure that the App's very first SSL connections are validated by TrustKit. Once TrustKit has been initialized, notifications will be posted for any SSL pinning validation performed. + This method should be called as early as possible in the App's lifecycle to ensure that + the App's very first SSL connections are validated by TrustKit. Once TrustKit has been + initialized, notifications will be posted for any SSL pinning validation performed. - @param trustKitConfig A dictionary containing various keys for configuring the global SSL pinning policy. + @param trustKitConfig A dictionary containing various keys for configuring the global + SSL pinning policy. */ - (instancetype)initWithConfiguration:(NSDictionary * _Nullable)trustKitConfig; @@ -408,17 +401,33 @@ FOUNDATION_EXPORT const TSKNotificationUserInfoKey kTSKValidationServerHostnameN @property (nonatomic, readonly, nullable) NSDictionary *configuration; /** - Set the logger used by this instance, used when TrustKit needs to display a message to the developer. - - If a global logger is not set, the default logger will be used, which will print TrustKit log messages (using `NSLog()`) when the App is built in Debug mode. If the App was built for Release, the default logger will not print any messages at all. + A pinning validator instance conforming to the configuration of this TrustKit instance. */ -@property (nonatomic, nullable) void(^loggerBlock)(NSString * _Nonnull msg); +@property (nonatomic, nonnull) TSKPinningValidator *pinningValidator; +/** + Register a block to be invoked for every request that is going through TrustKit's pinning + validation mechanism. + + Once TrustKit has been initialized, the callback will be invoked every time TrustKit validates + the certificate chain for a server configured in the SSL pinning policy; if the server's + hostname does not have an entry in the pinning policy, no invocations will result as no + pinning validation was performed. + + This callback can be used for performance measurement or to act upon any pinning validation + performed by TrustKit (for example to customize the reporting mechanism). The callback + provide the `TSKPinningValidatorResult` resulting from the validation. That instance provides + access to TrustKit's inner-workings which most Apps should not need. Hence, this callback + should not be set unless the App requires some advanced customization in regards to pinning + validation. Keep in mind that, if set, the callback may be invoked very frequently and is + not a suitable place for expensive tasks. + */ +@property (nonatomic, nullable) void(^validationDelegateCallback)(TSKPinningValidatorResult * _Nonnull result); /** - A pinning validator instance conforming to the configuration of this TrustKit instance. + Queue on which to invoke `validationDelegateCallback` (if set). Default value is main queue. */ -@property (nonatomic, nonnull) TSKPinningValidator *pinningValidator; +@property (nonatomic, null_resettable) dispatch_queue_t validationDelegateQueue; @end NS_ASSUME_NONNULL_END diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 59035027..0c0e9b0c 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -47,17 +47,6 @@ const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp256r1 = @"TSKAlgorithmEcDsaSecp256r1"; const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp384r1 = @"TSKAlgorithmEcDsaSecp384r1"; -#pragma mark Notification keys -NSString *kTSKValidationCompletedNotification = @"TSKValidationCompletedNotification"; - -const TSKNotificationUserInfoKey kTSKValidationDurationNotificationKey = @"TSKValidationDurationNotificationKey"; -const TSKNotificationUserInfoKey kTSKValidationResultNotificationKey = @"TSKValidationResultNotificationKey"; -const TSKNotificationUserInfoKey kTSKValidationDecisionNotificationKey = @"TSKValidationDecisionNotificationKey"; -const TSKNotificationUserInfoKey kTSKValidationCertificateChainNotificationKey = @"TSKValidationCertificateChainNotificationKey"; -const TSKNotificationUserInfoKey kTSKValidationNotedHostnameNotificationKey = @"TSKValidationNotedHostnameNotificationKey"; -const TSKNotificationUserInfoKey kTSKValidationServerHostnameNotificationKey = @"TSKValidationServerHostnameNotificationKey"; - - #pragma mark TrustKit Global State // Shared TrustKit singleton instance static TrustKit *sharedTrustKit = nil; @@ -68,29 +57,6 @@ // Email info@datatheorem.com if you need a free dashboard to see your App's reports static NSString * const kTSKDefaultReportUri = @"https://overmind.datatheorem.com/trustkit/report"; - -#pragma mark Default Logging Block - -// Default logger block: only log in debug builds and add TrustKit at the beginning of the line -void (^_loggerBlock)(NSString *) = ^void(NSString *message) -{ -#if DEBUG - NSLog(@"=== TrustKit: %@", message); -#endif -}; - - -// The logging function we use within TrustKit -void TSKLog(NSString *format, ...) -{ - va_list args; - va_start(args, format); - NSString *message = [[NSString alloc] initWithFormat: format arguments:args]; - va_end(args); - _loggerBlock(message); -} - - #pragma mark TrustKit Initialization Helper Functions @interface TrustKit () @@ -134,7 +100,6 @@ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig + (void)setLoggerBlock:(void (^)(NSString *))block { TrustKit *singleton = [self sharedInstance]; - singleton.loggerBlock = block; } + (NSDictionary * _Nullable)configuration @@ -157,6 +122,8 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo // Convert and store the SSL pins in our global variable _configuration = parseTrustKitConfiguration(trustKitConfig); + _validationDelegateQueue = dispatch_get_main_queue(); + // Create a dispatch queue for activating the reporter // We use a serial queue targetting the global default queue in order to ensure reports are sent one by one // even when a lot of pin failures are occuring, instead of spamming the global queue with events to process @@ -177,6 +144,16 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo ignorePinsForUserTrustAnchors:userTrustAnchorBypass validationResultQueue:_pinFailureReporterQueue validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { + + // Invoke client handler if set + void(^callback)(TSKPinningValidatorResult *) = self.validationDelegateCallback; + if (callback) { + dispatch_async(self.validationDelegateQueue, ^{ + callback(result); + }); + } + + // Send analytics report [weakSelf sendValidationReport:result]; }]; @@ -229,6 +206,11 @@ - (void)sendValidationReport:(TSKPinningValidatorResult *)result } } +- (void)setValidationDelegateQueue:(dispatch_queue_t)validationDelegateQueue +{ + _validationDelegateQueue = validationDelegateQueue ?: dispatch_get_main_queue(); +} + # pragma mark Private / Test Methods + (void)resetConfiguration diff --git a/TrustKitTests/TSKLoggerTests.m b/TrustKitTests/TSKLoggerTests.m index 78f45766..7bf51540 100644 --- a/TrustKitTests/TSKLoggerTests.m +++ b/TrustKitTests/TSKLoggerTests.m @@ -6,44 +6,51 @@ // Copyright © 2016 TrustKit. All rights reserved. // -#import -#import "../TrustKit/TrustKit+Private.h" - - -@interface TSKLoggerTests : XCTestCase - -@end - -@implementation TSKLoggerTests - -- (void)setUp -{ - [super setUp]; -} - -- (void)tearDown -{ - [super tearDown]; -} - -- (void)testDefaultLoggerBlock -{ - TSKLog(@"test %@", @"test"); -} - - -- (void)testSetLoggerBlock -{ - __block bool wasBlockCalled = false; - void (^loggerBlock)(NSString *) = ^void(NSString *message) - { - XCTAssert(message, @"test test"); - wasBlockCalled = true; - }; - - [TrustKit setLoggerBlock:loggerBlock]; - TSKLog(@"test %@", @"test"); - XCTAssertTrue(wasBlockCalled); -} - -@end +//#import +//#import "../TrustKit/TrustKit.h" +// +// +//extern void _TSKLog(NSString *format, ...); +// +//@interface TSKLoggerTests : XCTestCase +// +//@end +// +//@implementation TSKLoggerTests +// +//- (void)testDefaultLoggerBlock +//{ +// _TSKLog(@"test %@", @"test"); +//} +// +// +//- (void)testSetLoggerBlock +//{ +// __block bool wasBlockCalled = false; +// void (^loggerBlock)(NSString *) = ^void(NSString *message) +// { +// XCTAssert(message, @"test test"); +// wasBlockCalled = true; +// }; +// +// TrustKit *tk = [TrustKit new]; +// tk.loggerBlock = loggerBlock; +// TSKLog(@"test %@", @"test"); +// XCTAssertTrue(wasBlockCalled); +//} +// +//- (void)testSetLoggerBlock_singleton +//{ +// __block bool wasBlockCalled = false; +// void (^loggerBlock)(NSString *) = ^void(NSString *message) +// { +// XCTAssert(message, @"test test"); +// wasBlockCalled = true; +// }; +// +// [TrustKit setLoggerBlock:loggerBlock]; +// TSKLog(@"test %@", @"test"); +// XCTAssertTrue(wasBlockCalled); +//} +// +//@end diff --git a/TrustKitTests/TSKNSURLConnectionTests.m b/TrustKitTests/TSKNSURLConnectionTests.m index 599570fd..114d7807 100644 --- a/TrustKitTests/TSKNSURLConnectionTests.m +++ b/TrustKitTests/TSKNSURLConnectionTests.m @@ -23,7 +23,7 @@ +(void)resetLastTrustDecision; #pragma mark Test NSURLConnection delegate with no auth handler @interface TestNSURLConnectionDelegateNoAuthHandler : NSObject { - XCTestExpectation *testExpectation; + XCTestExpectation *_testExpectation; } @property NSError *lastError; @@ -46,9 +46,8 @@ @implementation TestNSURLConnectionDelegateNoAuthHandler { - (instancetype)initWithExpectation:(XCTestExpectation *)expectation { self = [super init]; - if (self) - { - testExpectation = expectation; + if (self) { + _testExpectation = expectation; } return self; } @@ -59,7 +58,7 @@ - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)err { NSLog(@"Received error, %@", error); _lastError = error; - [testExpectation fulfill]; + [_testExpectation fulfill]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data @@ -70,7 +69,7 @@ - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { _lastResponse = response; - [testExpectation fulfill]; + [_testExpectation fulfill]; } diff --git a/TrustKitTests/TSKPinConfigurationTests.m b/TrustKitTests/TSKPinConfigurationTests.m index 42dd7964..4c9e62de 100644 --- a/TrustKitTests/TSKPinConfigurationTests.m +++ b/TrustKitTests/TSKPinConfigurationTests.m @@ -12,7 +12,7 @@ #import #import "../TrustKit/TrustKit+Private.h" #import "../TrustKit/Pinning/ssl_pin_verifier.h" -#import "../TrustKit/Pinning/public_key_utils.h" +//#import "../TrustKit/Pinning/TSKSPKIHashCache.h" #import "../TrustKit/parse_configuration.h" diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 6e446945..29ac0472 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -14,7 +14,7 @@ #import "../TrustKit/parse_configuration.h" #import "../TrustKit/Pinning/ssl_pin_verifier.h" -#import "../TrustKit/Pinning/public_key_utils.h" +#import "../TrustKit/Pinning/TSKSPKIHashCache.h" #import "../TrustKit/Reporting/reporting_utils.h" @@ -24,7 +24,7 @@ @interface TSKPinningValidatorTests : XCTestCase { - + TSKSPKIHashCache *spkiCache; } @end @@ -41,6 +41,7 @@ @implementation TSKPinningValidatorTests - (void)setUp { [super setUp]; + // Create our certificate objects _rootCertificate = [TSKCertificateUtils createCertificateFromDer:@"GoodRootCA"]; _intermediateCertificate = [TSKCertificateUtils createCertificateFromDer:@"GoodIntermediateCA"]; @@ -48,16 +49,25 @@ - (void)setUp _selfSignedCertificate = [TSKCertificateUtils createCertificateFromDer:@"www.good.com.selfsigned"]; _globalsignRootCertificate = [TSKCertificateUtils createCertificateFromDer:@"GlobalSignRootCA"]; - [TrustKit resetConfiguration]; + spkiCache = [TSKSPKIHashCache new]; } - (void)tearDown { - [TrustKit resetConfiguration]; CFRelease(_rootCertificate); CFRelease(_intermediateCertificate); + CFRelease(_selfSignedCertificate); CFRelease(_leafCertificate); + CFRelease(_globalsignRootCertificate); + + _rootCertificate = nil; + _intermediateCertificate = nil; + _leafCertificate = nil; + _selfSignedCertificate = nil; + _globalsignRootCertificate = nil; + + spkiCache = nil; [super tearDown]; } @@ -86,16 +96,19 @@ - (void)testVerifyAgainstAnyPublicKey ]}}}; // Ensure the SPKI cache was on the filesystem is empty - XCTAssert([getSpkiCacheFromFileSystem()[@1] count] == 0, @"SPKI cache for RSA 4096 must be empty before the test"); + NSDictionary *fsCache = [spkiCache getSpkiCacheFromFileSystem]; + XCTAssert([fsCache[@(TSKPublicKeyAlgorithmRsa4096)] count] == 0, @"SPKI cache for RSA 4096 must be empty before the test"); // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); + NSDictionary *domainConfig = parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"]; TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; verificationResult = verifyPublicKeyPin(trust, @"www.good.com", - parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], - parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes]); + domainConfig[kTSKPublicKeyAlgorithms], + domainConfig[kTSKPublicKeyHashes], + spkiCache); XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); @@ -134,7 +147,8 @@ - (void)testVerifyAgainstAnyPublicKey [[NSNotificationCenter defaultCenter] removeObserver:observerId]; // Ensure the SPKI cache was persisted to the filesystem - XCTAssert([getSpkiCacheFromFileSystem()[@1] count] == 1, @"SPKI cache for RSA 4096 must be persisted to the file system"); + fsCache = [spkiCache getSpkiCacheFromFileSystem]; + XCTAssert([fsCache[@1] count] == 1, @"SPKI cache for RSA 4096 must be persisted to the file system"); CFRelease(trust); } @@ -161,7 +175,8 @@ - (void)testVerifyAgainstIntermediateCAPublicKey ]}}}; // Ensure the SPKI cache was on the filesystem is empty - XCTAssert([getSpkiCacheFromFileSystem()[@1] count] == 0, @"SPKI cache for RSA 4096 must be empty before the test"); + NSDictionary *fsCache = [spkiCache getSpkiCacheFromFileSystem]; + XCTAssert([fsCache[@1] count] == 0, @"SPKI cache for RSA 4096 must be empty before the test"); // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); @@ -170,7 +185,8 @@ - (void)testVerifyAgainstIntermediateCAPublicKey verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], - parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes]); + parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes], + spkiCache); XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); @@ -182,7 +198,8 @@ - (void)testVerifyAgainstIntermediateCAPublicKey // Ensure the SPKI cache was persisted to the filesystem - XCTAssert([getSpkiCacheFromFileSystem()[@1] count] == 2, @"SPKI cache for RSA 4096 must be persisted to the file system"); + fsCache = [spkiCache getSpkiCacheFromFileSystem]; + XCTAssert([fsCache[@1] count] == 2, @"SPKI cache for RSA 4096 must be persisted to the file system"); CFRelease(trust); } @@ -215,7 +232,8 @@ - (void)testVerifyAgainstCAPublicKey verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], - parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes]); + parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes], + spkiCache); XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); @@ -255,7 +273,8 @@ - (void)testVerifyAgainstLeafPublicKey verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], - parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes]); + parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes], + spkiCache); XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); @@ -295,7 +314,8 @@ - (void)testVerifyAgainstBadPublicKey verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], - parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes]); + parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes], + spkiCache); XCTAssert(verificationResult == TSKPinValidationResultFailed, @"Validation must fail against bad public key pins"); @@ -405,7 +425,8 @@ - (void)testVerifyAgainstBadPublicKeyPinningNotEnforced verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], - parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes]); + parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes], + spkiCache); XCTAssert(verificationResult == TSKPinValidationResultFailed, @"Validation must fail against bad public key pins"); @@ -472,7 +493,8 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], - parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes]); + parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes], + spkiCache); XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); @@ -513,7 +535,8 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], - parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes]); + parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes], + spkiCache); XCTAssert(verificationResult == TSKPinValidationResultFailedCertificateChainNotTrusted, @"Validation must fail against bad certificate chain"); @@ -581,7 +604,8 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname verificationResult = verifyPublicKeyPin(trust, @"www.bad.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.bad.com"][kTSKPublicKeyAlgorithms], - parsedTrustKitConfig[kTSKPinnedDomains][@"www.bad.com"][kTSKPublicKeyHashes]); + parsedTrustKitConfig[kTSKPinnedDomains][@"www.bad.com"][kTSKPublicKeyHashes], + spkiCache); XCTAssert(verificationResult == TSKPinValidationResultFailedCertificateChainNotTrusted, @"Validation must fail against bad hostname"); @@ -622,7 +646,8 @@ - (void)testVerifyAgainstInjectedCaPublicKey verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], - parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes]); + parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes], + spkiCache); XCTAssert(verificationResult == TSKTrustDecisionShouldBlockConnection, @"Validation must fail against injected pinned CA"); diff --git a/TrustKitTests/TSKPublicKeyAlgorithmTests.m b/TrustKitTests/TSKPublicKeyAlgorithmTests.m index 63126c67..dcc468e5 100644 --- a/TrustKitTests/TSKPublicKeyAlgorithmTests.m +++ b/TrustKitTests/TSKPublicKeyAlgorithmTests.m @@ -14,7 +14,7 @@ #import "../TrustKit/parse_configuration.h" #import "../TrustKit/Pinning/ssl_pin_verifier.h" -#import "../TrustKit/Pinning/public_key_utils.h" +#import "../TrustKit/Pinning/TSKSPKIHashCache.h" #import "../TrustKit/Reporting/reporting_utils.h" #import "TSKCertificateUtils.h" @@ -22,7 +22,7 @@ @interface TSKPublicKeyAlgorithmTests : XCTestCase { - + TSKSPKIHashCache *spkiCache; } @end @@ -31,12 +31,12 @@ @implementation TSKPublicKeyAlgorithmTests - (void)setUp { [super setUp]; - initializeSubjectPublicKeyInfoCache(); + spkiCache = [TSKSPKIHashCache new]; } - (void)tearDown { - resetSubjectPublicKeyInfoCache(); + spkiCache = nil; [super tearDown]; } @@ -46,7 +46,7 @@ - (void)testExtractRsa2048 // Ensure a RSA 2048 key is properly extracted from its certificate SecCertificateRef certificate = [TSKCertificateUtils createCertificateFromDer:@"www.globalsign.com"]; - NSData *spkiHash = hashSubjectPublicKeyInfoFromCertificate(certificate, TSKPublicKeyAlgorithmRsa2048); + NSData *spkiHash = [spkiCache hashSubjectPublicKeyInfoFromCertificate:certificate publicKeyAlgorithm:TSKPublicKeyAlgorithmRsa2048]; NSString *spkiPin = [spkiHash base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; XCTAssert([spkiPin isEqualToString:@"NDCIt6TrQnfOk+lquunrmlPQB3K/7CLOCmSS5kW+KCc="]); @@ -59,7 +59,7 @@ - (void)testExtractRsa4096 // Ensure a RSA 4096 key is properly extracted from its certificate SecCertificateRef certificate = [TSKCertificateUtils createCertificateFromDer:@"www.good.com"]; - NSData *spkiHash = hashSubjectPublicKeyInfoFromCertificate(certificate, TSKPublicKeyAlgorithmRsa4096); + NSData *spkiHash = [spkiCache hashSubjectPublicKeyInfoFromCertificate:certificate publicKeyAlgorithm:TSKPublicKeyAlgorithmRsa4096]; NSString *spkiPin = [spkiHash base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; XCTAssert([spkiPin isEqualToString:@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY="]); @@ -72,7 +72,7 @@ - (void)testExtractEcDsaSecp256r1 // Ensure a secp256r1 key is properly extracted from its certificate SecCertificateRef certificate = [TSKCertificateUtils createCertificateFromDer:@"www.cloudflare.com"]; - NSData *spkiHash = hashSubjectPublicKeyInfoFromCertificate(certificate, TSKPublicKeyAlgorithmEcDsaSecp256r1); + NSData *spkiHash = [spkiCache hashSubjectPublicKeyInfoFromCertificate:certificate publicKeyAlgorithm:TSKPublicKeyAlgorithmEcDsaSecp256r1]; NSString *spkiPin = [spkiHash base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; XCTAssert([spkiPin isEqualToString:@"Gc7EN2acfkbE0dUOAd34tr1XLr+JdkTiTrMAfhESQHI="]); @@ -85,7 +85,7 @@ - (void)testExtractEcDsaSecp384r1 // Ensure a secp384r1 key is properly extracted from its certificate SecCertificateRef certificate = [TSKCertificateUtils createCertificateFromDer:@"GeoTrust_Primary_CA_G2_ECC"]; - NSData *spkiHash = hashSubjectPublicKeyInfoFromCertificate(certificate, TSKPublicKeyAlgorithmEcDsaSecp384r1); + NSData *spkiHash = [spkiCache hashSubjectPublicKeyInfoFromCertificate:certificate publicKeyAlgorithm:TSKPublicKeyAlgorithmEcDsaSecp384r1]; NSString *spkiPin = [spkiHash base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; XCTAssert([spkiPin isEqualToString:@"vPtEqrmtAhAVcGtBIep2HIHJ6IlnWQ9vlK50TciLePs="]); @@ -119,19 +119,19 @@ - (void)testVerifyMultipleAlgorithms kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", // Server Key @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}); - - XCTAssert([getSpkiCache()[@0] count] == 0, @"SPKI cache must be empty"); - XCTAssert([getSpkiCache()[@1] count] == 0, @"SPKI cache must be empty"); + XCTAssert([spkiCache.getSpkiCache[@0] count] == 0, @"SPKI cache must be empty"); + XCTAssert([spkiCache.getSpkiCache[@1] count] == 0, @"SPKI cache must be empty"); TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], - trustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes]); + trustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes], + spkiCache); // Ensure the SPKI cache was used; the full certificate chain is three certs and we have to go through all of them to get to the pinned leaf - XCTAssert([getSpkiCache()[@0] count] == 3, @"SPKI cache must have been used"); - XCTAssert([getSpkiCache()[@1] count] == 3, @"SPKI cache must have been used"); + XCTAssert([spkiCache.getSpkiCache[@0] count] == 3, @"SPKI cache must have been used"); + XCTAssert([spkiCache.getSpkiCache[@1] count] == 3, @"SPKI cache must have been used"); CFRelease(trust); CFRelease(leafCertificate); diff --git a/TrustKitTests/TSKTestURLSessionDelegate.h b/TrustKitTests/TSKTestURLSessionDelegate.h new file mode 100644 index 00000000..263f9ce5 --- /dev/null +++ b/TrustKitTests/TSKTestURLSessionDelegate.h @@ -0,0 +1,13 @@ +// +// TSKTestURLSessionDelegate.h +// TrustKit +// +// Created by Adam Kaplan on 3/18/17. +// Copyright © 2017 TrustKit. All rights reserved. +// + +#import + +@interface TSKTestURLSessionDelegate : NSObject + +@end diff --git a/TrustKitTests/TSKTestURLSessionDelegate.m b/TrustKitTests/TSKTestURLSessionDelegate.m new file mode 100644 index 00000000..392deb54 --- /dev/null +++ b/TrustKitTests/TSKTestURLSessionDelegate.m @@ -0,0 +1,13 @@ +// +// TSKTestURLSessionDelegate.m +// TrustKit +// +// Created by Adam Kaplan on 3/18/17. +// Copyright © 2017 TrustKit. All rights reserved. +// + +#import "TSKTestURLSessionDelegate.h" + +@implementation TSKTestURLSessionDelegate + +@end From d51ec6f6bba8bc5f6f8284295bdf2e8933ab45df Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 23 Mar 2017 18:07:53 -0400 Subject: [PATCH 003/126] Added comprehensive unit tests for NSURLConnectionDelegateProxy. Swizzling tests are NOT present, only the regular methods. --- TrustKit.xcodeproj/project.pbxproj | 18 +- .../TSKNSURLConnectionDelegateProxy.h | 3 - .../TSKNSURLConnectionDelegateProxy.m | 72 +-- TrustKit/TSKPinningValidator.m | 16 +- TrustKit/TSKPinningValidatorResult.h | 20 +- TrustKit/TSKPinningValidatorResult.m | 25 + TrustKit/TrustKit+Private.h | 4 + TrustKit/TrustKit.m | 5 - TrustKitTests/TSKNSURLConnectionTests.m | 481 ++++++++++++------ TrustKitTests/TSKNSURLSessionTests.m | 3 +- TrustKitTests/TSKPinningValidatorTests.m | 3 +- TrustKitTests/TSKReporterTests.m | 110 ++-- 12 files changed, 497 insertions(+), 263 deletions(-) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index d1e6475b..b2f9ef58 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -531,10 +531,11 @@ isa = PBXGroup; children = ( 8C0C90111E3C196A003851A8 /* module.modulemap */, - 8CA6CC571BAF60DA00BDA419 /* Swizzling */, 8CE919281AEA0F61002B29AE /* Dependencies */, + 8CA6CC571BAF60DA00BDA419 /* Swizzling */, 8CE919201AEA0745002B29AE /* Reporting */, 8CE9191B1AEA072A002B29AE /* Pinning */, + FC663F551E8421BE008E2EFE /* Configuration */, 8C84804C1A896EE30017C155 /* TrustKit.h */, 8C84806C1A896F660017C155 /* TrustKit.m */, 2FA286123F801C437F35D240 /* TrustKit+Private.h */, @@ -543,10 +544,6 @@ FC1A08FD1E579F630055B12C /* TSKPinValidatorResult.h */, FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */, FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */, - 8C5D98B21CEFF079008E654B /* parse_configuration.m */, - 8C5D98B61CEFF103008E654B /* parse_configuration.h */, - 8C4346D41E5B894A008023F9 /* configuration_utils.h */, - 8C4346D51E5B894A008023F9 /* configuration_utils.m */, 8C84804A1A896EE30017C155 /* Supporting Files */, ); path = TrustKit; @@ -725,6 +722,17 @@ name = Dependencies; sourceTree = ""; }; + FC663F551E8421BE008E2EFE /* Configuration */ = { + isa = PBXGroup; + children = ( + 8C5D98B21CEFF079008E654B /* parse_configuration.m */, + 8C5D98B61CEFF103008E654B /* parse_configuration.h */, + 8C4346D41E5B894A008023F9 /* configuration_utils.h */, + 8C4346D51E5B894A008023F9 /* configuration_utils.m */, + ); + name = Configuration; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h index 1a7812a0..c51127b8 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h @@ -10,9 +10,6 @@ @interface TSKNSURLConnectionDelegateProxy : NSObject -{ - id originalDelegate; // The NSURLConnectionDelegate we're going to proxy -} // Initalize our hooks + (void)swizzleNSURLConnectionConstructors; diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m index 6c66ed26..b7a29915 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m @@ -10,34 +10,15 @@ #import "../TrustKit+Private.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" - - typedef void (^AsyncCompletionHandler)(NSURLResponse *response, NSData *data, NSError *connectionError); - -@interface TSKNSURLConnectionDelegateProxy(Private) --(BOOL)forwardToOriginalDelegateAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge forConnection:(NSURLConnection *)connection; +@interface TSKNSURLConnectionDelegateProxy () +@property (nonatomic) id originalDelegate; // The NSURLConnectionDelegate we're going to proxy +@property (nonatomic) TSKTrustDecision lastTrustDecision; @end - @implementation TSKNSURLConnectionDelegateProxy - -#pragma mark Private methods used for tests - -static TSKTrustDecision _lastTrustDecision = (TSKTrustDecision)-1; - -+(void)resetLastTrustDecision -{ - _lastTrustDecision = (TSKTrustDecision)-1; -} - -+(TSKTrustDecision)getLastTrustDecision -{ - return _lastTrustDecision; -} - - #pragma mark Public methods + (void)swizzleNSURLConnectionConstructors @@ -126,19 +107,21 @@ + (void)swizzleNSURLConnectionConstructors } +#pragma mark Instance Constructors + - (instancetype)initWithDelegate:(id)delegate { self = [super init]; if (self) { - originalDelegate = delegate; + _originalDelegate = delegate; + _lastTrustDecision = (TSKTrustDecision)-1; } TSKLog(@"Proxy-ing NSURLConnectionDelegate: %@", NSStringFromClass([delegate class])); return self; } - -#pragma mark Delegate methods +#pragma mark NSObject overrides - (BOOL)respondsToSelector:(SEL)aSelector { @@ -150,7 +133,7 @@ - (BOOL)respondsToSelector:(SEL)aSelector else { // The delegate proxy should mirror the original delegate's methods so that it doesn't change the app flow - return [originalDelegate respondsToSelector:aSelector]; + return [_originalDelegate respondsToSelector:aSelector]; } } @@ -158,38 +141,33 @@ - (BOOL)respondsToSelector:(SEL)aSelector - (id)forwardingTargetForSelector:(SEL)sel { // Forward messages to the original delegate if the proxy doesn't implement the method - return originalDelegate; + return _originalDelegate; } +#pragma mark Instance methods -// NSURLConnection is deprecated in iOS 9 #pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" --(BOOL)forwardToOriginalDelegateAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge forConnection:(NSURLConnection *)connection +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // NSURLConnection is deprecated in iOS 9 +- (BOOL)forwardToOriginalDelegateAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge forConnection:(NSURLConnection *)connection { - BOOL wasChallengeHandled = NO; - // Can the original delegate handle this challenge ? - if ([originalDelegate respondsToSelector:@selector(connection:willSendRequestForAuthenticationChallenge:)]) + if ([_originalDelegate respondsToSelector:@selector(connection:willSendRequestForAuthenticationChallenge:)]) { // Yes - forward the challenge to the original delegate - wasChallengeHandled = YES; - [originalDelegate connection:connection willSendRequestForAuthenticationChallenge:challenge]; + [_originalDelegate connection:connection willSendRequestForAuthenticationChallenge:challenge]; + return YES; } - else if ([originalDelegate respondsToSelector:@selector(connection:canAuthenticateAgainstProtectionSpace:)]) + + if ([_originalDelegate respondsToSelector:@selector(connection:canAuthenticateAgainstProtectionSpace:)] + && [_originalDelegate connection:connection canAuthenticateAgainstProtectionSpace:challenge.protectionSpace]) { - if ([originalDelegate connection:connection canAuthenticateAgainstProtectionSpace:challenge.protectionSpace]) - { - // Yes - forward the challenge to the original delegate - wasChallengeHandled = YES; - [originalDelegate connection:connection didReceiveAuthenticationChallenge:challenge]; - } + // Yes - forward the challenge to the original delegate + [_originalDelegate connection:connection didReceiveAuthenticationChallenge:challenge]; + return YES; } - return wasChallengeHandled; + return NO; } -#pragma GCC diagnostic pop - - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { @@ -214,7 +192,7 @@ - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticatio } // Forward all challenges (including client auth challenges) to the original delegate - if (wasChallengeHandled == NO) + if (!wasChallengeHandled) { // We will also get here if the pinning validation succeeded or the domain was not pinned if ([self forwardToOriginalDelegateAuthenticationChallenge:challenge forConnection:connection] == NO) @@ -224,6 +202,6 @@ - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticatio } } } - +#pragma GCC diagnostic pop @end diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index c376aaca..a148bc18 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -140,15 +140,15 @@ - (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname } // Send a notification after all validation is done; this will also trigger a report if pin validation failed if (self.validationResultQueue && self.validationResultHandler) { + NSTimeInterval validationDuration = [NSDate timeIntervalSinceReferenceDate] - validationStartTime; + TSKPinningValidatorResult *result = [[TSKPinningValidatorResult alloc] initWithServerHostname:serverHostname + serverTrust:serverTrust + notedHostname:domainConfigKey + validationResult:validationResult + finalTrustDecision:finalTrustDecision + validationDuration:validationDuration + certificateChain:nil]; dispatch_async(self.validationResultQueue, ^{ - TSKPinningValidatorResult *result = [TSKPinningValidatorResult new]; - result.serverHostname = serverHostname; - result.serverTrust = serverTrust; - result.notedHostname = domainConfigKey; - result.validationResult = validationResult; - result.finalTrustDecision = finalTrustDecision; - result.validationDuration = [NSDate timeIntervalSinceReferenceDate] - validationStartTime; - self.validationResultHandler(result); }); } diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h index 7b6c68a1..435e7af8 100644 --- a/TrustKit/TSKPinningValidatorResult.h +++ b/TrustKit/TSKPinningValidatorResult.h @@ -17,12 +17,12 @@ /** The hostname of the server SSL pinning validation was performed against. */ -@property (nonatomic, nonnull) NSString *serverHostname; +@property (nonatomic, readonly, nonnull) NSString *serverHostname; /** The original SecTrustRef that validation was performed against. */ -@property (nonatomic, nonnull) SecTrustRef serverTrust; +@property (nonatomic, readonly, nonnull) SecTrustRef serverTrust; /** The entry within the SSL pinning configuration that was used as the pinning policy for the @@ -34,14 +34,14 @@ NSString *notedHostname = userInfo[kTSKValidationNotedHostnameNotificationKey]; NSDictionary *hostnameConfiguration = [TrustKit configuration][kTSKPinnedDomains][notedHostname]; */ -@property (nonatomic, nonnull) NSString *notedHostname; +@property (nonatomic, readonly, nonnull) NSString *notedHostname; /** The `TSKPinValidationResult` returned when validating the server's certificate chain, which represents the result of evaluating the certificate chain against the configured SSL pins for this server. */ -@property (nonatomic) TSKPinValidationResult validationResult; +@property (nonatomic, readonly) TSKPinValidationResult validationResult; /** The `TSKTrustDecision` returned when validating the certificate's chain, which describes @@ -53,16 +53,24 @@ but the policy might be set to ignore pinning validation failures for this server, thereby returning `TSKTrustDecisionShouldAllowConnection`. */ -@property (nonatomic) TSKTrustDecision finalTrustDecision; +@property (nonatomic, readonly) TSKTrustDecision finalTrustDecision; /** The time in seconds it took for the SSL pinning validation to be performed. */ -@property (nonatomic) NSTimeInterval validationDuration; +@property (nonatomic, readonly) NSTimeInterval validationDuration; /** The certificate chain returned by the server as an array of PEM-formatted certificates. */ @property (nonatomic, readonly, nullable) NSArray *certificateChain; +- (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHostname + serverTrust:(SecTrustRef _Nonnull)serverTrust + notedHostname:(NSString * _Nonnull)notedHostname + validationResult:(TSKPinValidationResult)validationResult + finalTrustDecision:(TSKTrustDecision)finalTrustDecision + validationDuration:(NSTimeInterval)validationDuration + certificateChain:(NSArray * _Nullable)certificateChain; + @end diff --git a/TrustKit/TSKPinningValidatorResult.m b/TrustKit/TSKPinningValidatorResult.m index a6ae217c..65a799a0 100644 --- a/TrustKit/TSKPinningValidatorResult.m +++ b/TrustKit/TSKPinningValidatorResult.m @@ -26,4 +26,29 @@ - (NSArray * _Nullable)certificateChain return _certificateChain; } +- (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHostname + serverTrust:(SecTrustRef _Nonnull)serverTrust + notedHostname:(NSString * _Nonnull)notedHostname + validationResult:(TSKPinValidationResult)validationResult + finalTrustDecision:(TSKTrustDecision)finalTrustDecision + validationDuration:(NSTimeInterval)validationDuration + certificateChain:(NSArray * _Nullable)certificateChain +{ + NSParameterAssert(serverHostname); + NSParameterAssert(serverTrust); + NSParameterAssert(notedHostname); + + self = [super init]; + if (self) { + _serverHostname = serverHostname; + _serverTrust = serverTrust; + _notedHostname = notedHostname; + _validationResult = validationResult; + _finalTrustDecision = finalTrustDecision; + _validationDuration = validationDuration; + _certificateChain = certificateChain; + } + return self; +} + @end diff --git a/TrustKit/TrustKit+Private.h b/TrustKit/TrustKit+Private.h index 8fe4bc09..bd934e45 100644 --- a/TrustKit/TrustKit+Private.h +++ b/TrustKit/TrustKit+Private.h @@ -27,6 +27,10 @@ @interface TrustKit(Private) +@property (nonatomic) TSKBackgroundReporter *pinFailureReporter; + +- (void)sendValidationReport:(TSKPinningValidatorResult *)result; + + (void) resetConfiguration; + (BOOL) wasTrustKitInitialized; + (NSString *) getDefaultReportUri; diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 0c0e9b0c..c749789c 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -97,11 +97,6 @@ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig }); } -+ (void)setLoggerBlock:(void (^)(NSString *))block -{ - TrustKit *singleton = [self sharedInstance]; -} - + (NSDictionary * _Nullable)configuration { TrustKit *singleton = [self sharedInstance]; diff --git a/TrustKitTests/TSKNSURLConnectionTests.m b/TrustKitTests/TSKNSURLConnectionTests.m index 114d7807..a321e052 100644 --- a/TrustKitTests/TSKNSURLConnectionTests.m +++ b/TrustKitTests/TSKNSURLConnectionTests.m @@ -8,46 +8,37 @@ #import #import "../TrustKit/TrustKit+Private.h" +#import "../TrustKit/TSKPinningValidatorResult.h" #import "../TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h" +#import - -#pragma mark Private test methods -@interface TSKNSURLConnectionDelegateProxy(Private) - -+(TSKPinValidationResult)getLastTrustDecision; -+(void)resetLastTrustDecision; - +@interface TSKNSURLConnectionDelegateProxy (TestSupport) +@property (nonatomic) TSKPinValidationResult lastTrustDecision; +-(BOOL)forwardToOriginalDelegateAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge forConnection:(NSURLConnection *)connection; @end - +/* #pragma mark Test NSURLConnection delegate with no auth handler @interface TestNSURLConnectionDelegateNoAuthHandler : NSObject { XCTestExpectation *_testExpectation; } +@property TrustKit *trustKit; @property NSError *lastError; @property NSURLResponse *lastResponse; - (instancetype)initWithExpectation:(XCTestExpectation *)expectation; -- (void)connectionDidFinishLoading:(NSURLConnection *)connection; -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response; -- (NSURLRequest *)connection:(NSURLConnection *)connection - willSendRequest:(NSURLRequest *)request - redirectResponse:(NSURLResponse *)redirectResponse; - @end +@implementation TestNSURLConnectionDelegateNoAuthHandler -@implementation TestNSURLConnectionDelegateNoAuthHandler { -} - -- (instancetype)initWithExpectation:(XCTestExpectation *)expectation +- (instancetype)initWithExpectation:(XCTestExpectation *)expectation trustKit:(TrustKit *)trustKit { self = [super init]; if (self) { _testExpectation = expectation; + _trustKit = trustKit; } return self; } @@ -61,10 +52,7 @@ - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)err [_testExpectation fulfill]; } -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data -{ -} - +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { @@ -93,51 +81,88 @@ - (NSURLRequest *)connection:(NSURLConnection *)connection } @end + + + @implementation TestNSURLConnectionDelegateDidReceiveAuth + - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge + { + _wasAuthHandlerCalled = YES; + [_testExpectation fulfill]; + } + + - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace + { + return YES; + } + @end + + + + #pragma mark Test NSURLConnection delegate with connection:didReceiveAuthenticationChallenge: + @interface TestNSURLConnectionDelegateDidReceiveAuth : TestNSURLConnectionDelegateNoAuthHandler + + @property BOOL wasAuthHandlerCalled; // Used to validate that the delegate's auth handler was called + + - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; + - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace; + @end + + + #pragma mark Test NSURLConnection delegate with connection:willSendRequestForAuthenticationChallenge: + @interface TestNSURLConnectionDelegateWillSendRequestForAuth : TestNSURLConnectionDelegateNoAuthHandler + + @property BOOL wasAuthHandlerCalled; // Used to validate that the delegate's auth handler was called + + - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; + @end + + + @implementation TestNSURLConnectionDelegateWillSendRequestForAuth + - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; + { + _wasAuthHandlerCalled = YES; + [_testExpectation fulfill]; + } + + @end +*/ +///// These are classes that respond to specific combinations of methods. Mock the methods +///// as needed for the tests. -#pragma mark Test NSURLConnection delegate with connection:didReceiveAuthenticationChallenge: -@interface TestNSURLConnectionDelegateDidReceiveAuth : TestNSURLConnectionDelegateNoAuthHandler - -@property BOOL wasAuthHandlerCalled; // Used to validate that the delegate's auth handler was called - -- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; -- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace; +@interface TestModeADelegate : NSObject @end +@implementation TestModeADelegate +- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { } +- (void)fakeMethod { } // used in some tests -@implementation TestNSURLConnectionDelegateDidReceiveAuth -- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge -{ - _wasAuthHandlerCalled = YES; - [testExpectation fulfill]; -} - -- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace -{ - return YES; -} +// Protocol requirements +- (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} +- (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} +- (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} +- (void)performDefaultHandlingForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} @end - - -#pragma mark Test NSURLConnection delegate with connection:willSendRequestForAuthenticationChallenge: -@interface TestNSURLConnectionDelegateWillSendRequestForAuth : TestNSURLConnectionDelegateNoAuthHandler - -@property BOOL wasAuthHandlerCalled; // Used to validate that the delegate's auth handler was called - -- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; +@interface TestModeBDelegate : NSObject +@property (nonatomic) BOOL shouldAuthenticate; // the value returned by `canAuthenticate...:` @end - -@implementation TestNSURLConnectionDelegateWillSendRequestForAuth -- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; -{ - _wasAuthHandlerCalled = YES; - [testExpectation fulfill]; +@implementation TestModeBDelegate +- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { + return self.shouldAuthenticate; } +- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { } +// Protocol requirements +- (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} +- (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} +- (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} +- (void)performDefaultHandlingForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} @end +///// + #pragma mark Test suite @@ -150,26 +175,213 @@ - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticatio // WARNING 2: If the domain sends a redirection, two pinning validation will occur, thereby setting the // lastTrustDecision to an unexpected value -@interface TSKNSURLConnectionTests : XCTestCase - +@interface TSKNSURLConnectionTests : XCTestCase { + +} @end @implementation TSKNSURLConnectionTests - (void)setUp { [super setUp]; - [TrustKit resetConfiguration]; - [TSKNSURLConnectionDelegateProxy resetLastTrustDecision]; - [[NSURLCache sharedURLCache] removeAllCachedResponses]; } - (void)tearDown { [super tearDown]; } -// NSURLConnection is deprecated - disable Xcode warnings #pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // NSURLConnection is deprecated - disable Xcode warnings + +#pragma mark - respondsToSelector override + +- (void)test_respondsToSelector_alwaysTrueForWillSendRequest +{ + TSKNSURLConnectionDelegateProxy *proxy; + + proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:[TestModeADelegate new]]; + XCTAssertTrue([proxy respondsToSelector:@selector(connection:willSendRequestForAuthenticationChallenge:)]); + + proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:[TestModeBDelegate new]]; + XCTAssertTrue([proxy respondsToSelector:@selector(connection:willSendRequestForAuthenticationChallenge:)]); +} + +- (void)respondsToSelector_trueForOriginalMethods +{ + TSKNSURLConnectionDelegateProxy *proxy; + + proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:[TestModeADelegate new]]; + XCTAssertTrue([proxy respondsToSelector:@selector(fakeMethod)]); + + proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:[TestModeBDelegate new]]; + XCTAssertFalse([proxy respondsToSelector:@selector(fakeMethod)]); +} + +- (void)test_respondsToSelector_falseForUnimplementedMethods +{ + TSKNSURLConnectionDelegateProxy *proxy; + + proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:[TestModeADelegate new]]; + XCTAssertFalse([proxy respondsToSelector:NSSelectorFromString(@"argle:bargle:")]); +} + +#pragma mark - forwardingTargetForSelector override + + +- (void)test_respondsToSelector_forwardsTargetForSelector +{ + TestModeADelegate *delegate = OCMStrictClassMock([TestModeADelegate class]); + OCMExpect([delegate fakeMethod]); + + TSKNSURLConnectionDelegateProxy *proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]; + [(id)proxy fakeMethod]; + + OCMVerifyAll((id)delegate); + [(id)delegate stopMocking]; +} + +#pragma mark - forwardToOriginalDelegateAuthenticationChallenge + +- (void)test_forwardToOriginalDelegateAuthenticationChallenge_respondsToWillSend +{ + NSURLConnection *cnxn = [[NSURLConnection alloc] init]; + NSURLAuthenticationChallenge *challenge = [[NSURLAuthenticationChallenge alloc] init]; + + TestModeADelegate *delegate = OCMStrictClassMock([TestModeADelegate class]); + OCMExpect([delegate connection:cnxn willSendRequestForAuthenticationChallenge:challenge]); + + TSKNSURLConnectionDelegateProxy *proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]; + [(id)proxy forwardToOriginalDelegateAuthenticationChallenge:challenge forConnection:cnxn]; + + OCMVerifyAll((id)delegate); + [(id)delegate stopMocking]; +} + +- (void)test_forwardToOriginalDelegateAuthenticationChallenge_respondsToCanAuthenticate +{ + TestModeBDelegate *delegate = OCMStrictClassMock([TestModeBDelegate class]); + + NSURLConnection *cnxn = [[NSURLConnection alloc] init]; + NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] init]; + NSURLAuthenticationChallenge *challenge = [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:space + proposedCredential:nil + previousFailureCount:0 + failureResponse:nil + error:nil + sender:delegate]; + OCMExpect([delegate connection:cnxn canAuthenticateAgainstProtectionSpace:space]).andReturn(YES); + OCMExpect([delegate connection:cnxn didReceiveAuthenticationChallenge:challenge]); + + TSKNSURLConnectionDelegateProxy *proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]; + [(id)proxy forwardToOriginalDelegateAuthenticationChallenge:challenge forConnection:cnxn]; + + OCMVerifyAll((id)delegate); + [(id)delegate stopMocking]; +} + +#pragma mark - connection:willSendRequestForAuthenticationChallenge: + +- (void)test_connectionWillSendRequestForAuthenticationChallenge_notServerTrust +{ + TestModeADelegate *delegate = OCMStrictClassMock([TestModeADelegate class]); + TSKNSURLConnectionDelegateProxy *proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]; + + NSURLConnection *cnxn = [[NSURLConnection alloc] init]; + NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"host" port:0 protocol:nil realm:nil + authenticationMethod:NSURLAuthenticationMethodHTTPBasic]; + NSURLAuthenticationChallenge *challenge = [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:space + proposedCredential:nil + previousFailureCount:0 + failureResponse:nil + error:nil + sender:delegate]; + // Expect fallthrough because this connection was not blocked. + OCMExpect([delegate connection:cnxn willSendRequestForAuthenticationChallenge:challenge]); + [proxy connection:cnxn willSendRequestForAuthenticationChallenge:challenge]; + OCMVerifyAll((id)delegate); +} + +- (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustA +{ + TestModeADelegate *delegate = OCMStrictClassMock([TestModeADelegate class]); + + TSKPinningValidator *validator = OCMStrictClassMock([TSKPinningValidator class]); + [TrustKit initializeWithConfiguration:@{}]; + [TrustKit sharedInstance].pinningValidator = validator; + + NSURLConnection *cnxn = [[NSURLConnection alloc] init]; + NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"hostname" port:0 protocol:nil realm:nil + authenticationMethod:NSURLAuthenticationMethodServerTrust]; + NSURLAuthenticationChallenge *challenge = [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:space + proposedCredential:nil + previousFailureCount:0 + failureResponse:nil + error:nil + sender:delegate]; + + TSKNSURLConnectionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]); + + OCMExpect([validator evaluateTrust:space.serverTrust forHostname:@"hostname"]).andReturn(TSKTrustDecisionShouldAllowConnection); + OCMExpect([proxy forwardToOriginalDelegateAuthenticationChallenge:challenge forConnection:cnxn]); + OCMStub([proxy connection:cnxn willSendRequestForAuthenticationChallenge:challenge]).andForwardToRealObject(); + OCMExpect([delegate performDefaultHandlingForAuthenticationChallenge:challenge]); + + [proxy connection:cnxn willSendRequestForAuthenticationChallenge:challenge]; + + OCMVerifyAll((id)delegate); + OCMVerifyAll((id)validator); + OCMVerifyAll((id)proxy); + + [TrustKit sharedInstance].pinningValidator = [TSKPinningValidator new]; + [(id)validator stopMocking]; + [(id)delegate stopMocking]; + [(id)proxy stopMocking]; +} + +- (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustB +{ + TestModeBDelegate *delegate = OCMPartialMock([TestModeBDelegate new]); + + TSKPinningValidator *validator = OCMStrictClassMock([TSKPinningValidator class]); + [TrustKit initializeWithConfiguration:@{}]; + [TrustKit sharedInstance].pinningValidator = validator; + + NSURLConnection *cnxn = [[NSURLConnection alloc] init]; + NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"hostname" port:0 protocol:nil realm:nil + authenticationMethod:NSURLAuthenticationMethodServerTrust]; + NSURLAuthenticationChallenge *challenge = [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:space + proposedCredential:nil + previousFailureCount:0 + failureResponse:nil + error:nil + sender:delegate]; + + TSKNSURLConnectionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]); + + OCMExpect([validator evaluateTrust:space.serverTrust forHostname:@"hostname"]).andReturn(TSKTrustDecisionShouldAllowConnection); + OCMStub([proxy connection:cnxn willSendRequestForAuthenticationChallenge:challenge]).andForwardToRealObject(); + // Test that the forward method was called – that method was tested in it's own unit tests. + OCMExpect([proxy forwardToOriginalDelegateAuthenticationChallenge:challenge forConnection:cnxn]).andReturn(NO); + OCMExpect([delegate performDefaultHandlingForAuthenticationChallenge:challenge]); + + [proxy connection:cnxn willSendRequestForAuthenticationChallenge:challenge]; + + OCMVerifyAll((id)delegate); + OCMVerifyAll((id)validator); + OCMVerifyAll((id)proxy); + + [TrustKit sharedInstance].pinningValidator = [TSKPinningValidator new]; + [(id)validator stopMocking]; + [(id)delegate stopMocking]; + [(id)proxy stopMocking]; +} + + +// TODO: add swizzling tests to ensure the above tested methods are properly invoked. + + +// LEGACY BELOW + // Disable auto-swizzling and ensure TrustKit does not get called @@ -211,7 +423,7 @@ - (void)testSwizzleNetworkDelegatesDiabled } */ - +/* // Tests a secure connection to https://www.yahoo.com and forces validation to fail by providing a fake hash - (void)testPinningValidationFailed { @@ -227,25 +439,21 @@ - (void)testPinningValidationFailed @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Fake key 2 ]}}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; // Configure notification listener XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - NSDictionary *userInfo = [note userInfo]; - // Notification received, check the userInfo - XCTAssertEqualObjects(userInfo[kTSKValidationDecisionNotificationKey], @(TSKTrustDecisionShouldBlockConnection)); - XCTAssertEqualObjects(userInfo[kTSKValidationResultNotificationKey], @(TSKPinValidationResultFailed)); - XCTAssertEqualObjects(userInfo[kTSKValidationNotedHostnameNotificationKey], @"www.yahoo.com"); - XCTAssertEqualObjects(userInfo[kTSKValidationServerHostnameNotificationKey], @"www.yahoo.com"); - XCTAssertGreaterThan([userInfo[kTSKValidationCertificateChainNotificationKey] count], (unsigned long)1); - XCTAssertGreaterThan([userInfo[kTSKValidationDurationNotificationKey] doubleValue], 0); - [notifReceivedExpectation fulfill]; - }]; - + trustKit.validationDelegateCallback = ^(TSKPinningValidatorResult *result) { + // Notification received, check the userInfo + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); + XCTAssertEqual(result.validationResult, TSKPinValidationResultFailed); + XCTAssertEqualObjects(result.notedHostname, @"www.yahoo.com"); + XCTAssertEqualObjects(result.serverHostname, @"www.yahoo.com"); + XCTAssertGreaterThan(result.certificateChain.count, (unsigned long)1); + XCTAssertGreaterThan(result.validationDuration, 0); + + [notifReceivedExpectation fulfill]; + }; XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegate"]; TestNSURLConnectionDelegateNoAuthHandler *delegate = [[TestNSURLConnectionDelegateNoAuthHandler alloc] initWithExpectation:expectation]; @@ -267,8 +475,6 @@ - (void)testPinningValidationFailed XCTAssert(([TSKNSURLConnectionDelegateProxy getLastTrustDecision] == TSKTrustDecisionShouldBlockConnection), @"TrustKit accepted an invalid certificate"); XCTAssertNotNil(delegate.lastError, @"TrustKit did not trigger an error"); XCTAssertNil(delegate.lastResponse, @"TrustKit returned a response although pin validation failed"); - - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; } @@ -288,7 +494,8 @@ - (void)testPinningValidationFailedChainNotTrusted @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegate"]; TestNSURLConnectionDelegateNoAuthHandler *delegate = [[TestNSURLConnectionDelegateNoAuthHandler alloc] initWithExpectation:expectation]; @@ -329,16 +536,14 @@ - (void)testPinningValidationFailedChainNotTrustedAndNotPinned @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; // Configure notification listener - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - // Ensure a validation notification was NOT posted - XCTFail(@"kTSKValidationCompletedNotification should not have been posted"); - }]; + trustKit.validationDelegateCallback = ^(TSKPinningValidatorResult *result) { + // Ensure a validation notification was NOT posted + XCTFail(@"kTSKValidationCompletedNotification should not have been posted"); + }; XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegate"]; TestNSURLConnectionDelegateNoAuthHandler *delegate = [[TestNSURLConnectionDelegateNoAuthHandler alloc] initWithExpectation:expectation]; @@ -359,8 +564,6 @@ - (void)testPinningValidationFailedChainNotTrustedAndNotPinned XCTAssert(([TSKNSURLConnectionDelegateProxy getLastTrustDecision] == TSKTrustDecisionDomainNotPinned), @"TrustKit accepted an invalid certificate"); XCTAssertNotNil(delegate.lastError, @"TrustKit did not trigger an error"); XCTAssertNil(delegate.lastResponse, @"TrustKit returned a response although the server's certificate is invalid"); - - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; } @@ -378,24 +581,20 @@ - (void)testPinningValidationSucceeded @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; - // Configure notification listener XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - NSDictionary *userInfo = [note userInfo]; - // Notification received, check the userInfo - XCTAssertEqualObjects(userInfo[kTSKValidationDecisionNotificationKey], @(TSKTrustDecisionShouldAllowConnection)); - XCTAssertEqualObjects(userInfo[kTSKValidationResultNotificationKey], @(TSKPinValidationResultSuccess)); - XCTAssertEqualObjects(userInfo[kTSKValidationNotedHostnameNotificationKey], @"www.twitter.com"); - XCTAssertEqualObjects(userInfo[kTSKValidationServerHostnameNotificationKey], @"www.twitter.com"); - XCTAssertGreaterThan([userInfo[kTSKValidationCertificateChainNotificationKey] count], (unsigned long)1); - XCTAssertGreaterThan([userInfo[kTSKValidationDurationNotificationKey] doubleValue], 0); - [notifReceivedExpectation fulfill]; - }]; + trustKit.validationDelegateCallback = ^(TSKPinningValidatorResult *result) { + // Notification received, check the userInfo + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); + XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + XCTAssertEqualObjects(result.notedHostname, @"www.twitter.com"); + XCTAssertEqualObjects(result.serverHostname, @"www.twitter.com"); + XCTAssertGreaterThan(result.certificateChain.count, (unsigned long)1); + XCTAssertGreaterThan(result.validationDuration, 0); + + [notifReceivedExpectation fulfill]; + }; XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegate"]; TestNSURLConnectionDelegateNoAuthHandler *delegate = [[TestNSURLConnectionDelegateNoAuthHandler alloc] initWithExpectation:expectation]; @@ -420,8 +619,6 @@ - (void)testPinningValidationSucceeded XCTAssertNil(delegate.lastError, @"TrustKit triggered an error"); XCTAssertNotNil(delegate.lastResponse, @"TrustKit prevented a response from being returned"); XCTAssert([(NSHTTPURLResponse *)delegate.lastResponse statusCode] == 301, @"TrustKit prevented a response from being returned"); - - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; } @@ -460,6 +657,8 @@ - (void)testNoDelegateWarnings NSLog(@"Timeout Error: %@", error); } }]; + + [TrustKit resetConfiguration]; } @@ -478,26 +677,23 @@ - (void)testDidReceiveAuthHandlerGetsCalled @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; // Configure notification listener XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - NSDictionary *userInfo = [note userInfo]; - // Notification received, check the userInfo - XCTAssertEqualObjects(userInfo[kTSKValidationDecisionNotificationKey], @(TSKTrustDecisionShouldAllowConnection)); - XCTAssertEqualObjects(userInfo[kTSKValidationResultNotificationKey], @(TSKPinValidationResultSuccess)); - XCTAssertEqualObjects(userInfo[kTSKValidationNotedHostnameNotificationKey], @"www.apple.com"); - XCTAssertEqualObjects(userInfo[kTSKValidationServerHostnameNotificationKey], @"www.apple.com"); - XCTAssertGreaterThan([userInfo[kTSKValidationCertificateChainNotificationKey] count], (unsigned long)1); - XCTAssertGreaterThan([userInfo[kTSKValidationDurationNotificationKey] doubleValue], 0); - [notifReceivedExpectation fulfill]; - }]; - + trustKit.validationDelegateCallback = ^(TSKPinningValidatorResult *result) { + // Notification received, check the userInfo + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); + XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + XCTAssertEqualObjects(result.notedHostname, @"www.apple.com"); + XCTAssertEqualObjects(result.serverHostname, @"www.apple.com"); + XCTAssertGreaterThan(result.certificateChain.count, (unsigned long)1); + XCTAssertGreaterThan(result.validationDuration, 0); + + [notifReceivedExpectation fulfill]; + }; + // Configure notification listener XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegateDidReceiveAuth"]; TestNSURLConnectionDelegateDidReceiveAuth *delegate = [[TestNSURLConnectionDelegateDidReceiveAuth alloc] initWithExpectation:expectation]; NSURLConnection *connection = [[NSURLConnection alloc] @@ -514,8 +710,6 @@ - (void)testDidReceiveAuthHandlerGetsCalled } }]; XCTAssert(delegate.wasAuthHandlerCalled, @"TrustKit prevented the original delegate's auth handler from being called."); - - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; } @@ -534,24 +728,21 @@ - (void)testWillSendRequestForAuthHandlerGetsCalled @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; // Configure notification listener XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - NSDictionary *userInfo = [note userInfo]; - // Notification received, check the userInfo - XCTAssertEqualObjects(userInfo[kTSKValidationDecisionNotificationKey], @(TSKTrustDecisionShouldAllowConnection)); - XCTAssertEqualObjects(userInfo[kTSKValidationResultNotificationKey], @(TSKPinValidationResultSuccess)); - XCTAssertEqualObjects(userInfo[kTSKValidationNotedHostnameNotificationKey], @"www.fastmail.fm"); - XCTAssertEqualObjects(userInfo[kTSKValidationServerHostnameNotificationKey], @"www.fastmail.fm"); - XCTAssertGreaterThan([userInfo[kTSKValidationCertificateChainNotificationKey] count], (unsigned long)1); - XCTAssertGreaterThan([userInfo[kTSKValidationDurationNotificationKey] doubleValue], 0); - [notifReceivedExpectation fulfill]; - }]; + trustKit.validationDelegateCallback = ^(TSKPinningValidatorResult *result) { + // Notification received, check the userInfo + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); + XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + XCTAssertEqualObjects(result.notedHostname, @"www.fastmail.fm"); + XCTAssertEqualObjects(result.serverHostname, @"www.fastmail.fm"); + XCTAssertGreaterThan(result.certificateChain.count, (unsigned long)1); + XCTAssertGreaterThan(result.validationDuration, 0); + + [notifReceivedExpectation fulfill]; + }; XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegateDidReceiveAuth"]; TestNSURLConnectionDelegateWillSendRequestForAuth *delegate = [[TestNSURLConnectionDelegateWillSendRequestForAuth alloc] initWithExpectation:expectation]; @@ -569,10 +760,8 @@ - (void)testWillSendRequestForAuthHandlerGetsCalled } }]; XCTAssert(delegate.wasAuthHandlerCalled, @"TrustKit prevented the original delegate's auth handler from being called."); - - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; } - +*/ #pragma GCC diagnostic pop @end diff --git a/TrustKitTests/TSKNSURLSessionTests.m b/TrustKitTests/TSKNSURLSessionTests.m index f52a4769..df09af97 100644 --- a/TrustKitTests/TSKNSURLSessionTests.m +++ b/TrustKitTests/TSKNSURLSessionTests.m @@ -10,7 +10,7 @@ #import "../TrustKit/TrustKit+Private.h" #import "../TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h" - +/* #pragma mark Private test methods @interface TSKNSURLSessionDelegateProxy(Private) @@ -519,3 +519,4 @@ - (void)testSessionDidReceiveChallengeGetsCalled } @end +*/ diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 29ac0472..b01ae531 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -21,7 +21,7 @@ #import "TSKCertificateUtils.h" #import - +/* @interface TSKPinningValidatorTests : XCTestCase { TSKSPKIHashCache *spkiCache; @@ -930,3 +930,4 @@ - (void)testExcludedSubdomain } @end +*/ diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index 94ff4598..957920fd 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -13,6 +13,7 @@ #import "../TrustKit/TrustKit+Private.h" +#import "../TrustKit/TSKPinningValidatorResult.h" #import "../TrustKit/Reporting/TSKBackgroundReporter.h" #import "../TrustKit/Reporting/TSKPinFailureReport.h" #import "../TrustKit/Reporting/reporting_utils.h" @@ -31,6 +32,7 @@ @interface TSKReporterTests : XCTestCase @implementation TSKReporterTests { + TrustKit *_trustKit; SecTrustRef _testTrust; SecCertificateRef _rootCertificate; SecCertificateRef _intermediateCertificate; @@ -62,11 +64,13 @@ - (void)tearDown CFRelease(_intermediateCertificate); CFRelease(_leafCertificate); CFRelease(_testTrust); + _trustKit = nil; [super tearDown]; } -- (void)testSendReportFromNotificationBlock +// NOTE: this is more of a test of TrustKit.m +- (void)testSendReportFromValidationReport { // Ensure that a pin validation notification triggers the upload of a report if the validation failed // Initialize TrustKit so the reporter block is ready to receive notifications @@ -82,12 +86,8 @@ - (void)testSendReportFromNotificationBlock kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Fake key 2 ]}}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; - // Setup mocking of the reporter - TSKBackgroundReporter *defaultReporter = [TrustKit getGlobalPinFailureReporter]; - id pinFailureReporterMock = [OCMockObject mockForClass:[TSKBackgroundReporter class]]; - [TrustKit setGlobalPinFailureReporter: pinFailureReporterMock]; + _trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; // Expect a report to be sent out when a notification is posted NSSet *knownPins = [NSSet setWithArray:@[[[NSData alloc]initWithBase64EncodedString:@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" @@ -100,51 +100,79 @@ - (void)testSendReportFromNotificationBlock [dateFormat setDateFormat:@"yyyy-MM-dd"]; NSDate *expirationDate = [dateFormat dateFromString:expirationDateStr]; - [[pinFailureReporterMock expect] pinValidationFailedForHostname:@"www.test.com" - port:nil - certificateChain:_testCertificateChain + TSKPinningValidatorResult *res; + + // TEST FAILURE + // Setup mocking of the reporter + id pinReporterMock = OCMClassMock([TSKBackgroundReporter class]); + _trustKit.pinFailureReporter = pinReporterMock; + + OCMExpect([pinReporterMock pinValidationFailedForHostname:@"www.test.com" + port:nil + certificateChain:_testCertificateChain + notedHostname:@"www.test.com" + reportURIs:@[[NSURL URLWithString:@"https://overmind.datatheorem.com/trustkit/report"]] + includeSubdomains:NO + enforcePinning:YES + knownPins:knownPins + validationResult:TSKPinValidationResultErrorCouldNotGenerateSpkiHash + expirationDate:expirationDate]); + + res = [[TSKPinningValidatorResult alloc] initWithServerHostname:@"www.test.com" + serverTrust:_testTrust notedHostname:@"www.test.com" - reportURIs:@[[NSURL URLWithString:@"https://overmind.datatheorem.com/trustkit/report"]] - includeSubdomains:NO - enforcePinning:YES - knownPins:knownPins validationResult:TSKPinValidationResultErrorCouldNotGenerateSpkiHash - expirationDate:expirationDate]; + finalTrustDecision:TSKTrustDecisionShouldBlockConnection + validationDuration:1.0 + certificateChain:_testCertificateChain]; + [_trustKit sendValidationReport:res]; - // Create a notification - [[NSNotificationCenter defaultCenter] postNotificationName:kTSKValidationCompletedNotification - object:nil - userInfo:@{kTSKValidationDurationNotificationKey: @(1), - kTSKValidationDecisionNotificationKey: @(1), - kTSKValidationResultNotificationKey: @(TSKPinValidationResultErrorCouldNotGenerateSpkiHash), - kTSKValidationCertificateChainNotificationKey: _testCertificateChain, - kTSKValidationNotedHostnameNotificationKey: @"www.test.com", - kTSKValidationServerHostnameNotificationKey: @"www.test.com"}]; // Ensure that the reporter was called - [pinFailureReporterMock verify]; + [pinReporterMock verify]; + [pinReporterMock stopMocking]; + _trustKit.pinFailureReporter = nil; + // TEST CA SUCCESS // Send a notification for a successful validation and ensure no report gets sent - id pinSuccessReporterMock = [OCMockObject mockForClass:[TSKBackgroundReporter class]]; - [TrustKit setGlobalPinFailureReporter: pinSuccessReporterMock]; - [[NSNotificationCenter defaultCenter] postNotificationName:kTSKValidationCompletedNotification - object:nil - userInfo:@{kTSKValidationResultNotificationKey: @(TSKPinValidationResultSuccess)}]; + pinReporterMock = OCMClassMock([TSKBackgroundReporter class]); + _trustKit.pinFailureReporter = pinReporterMock; + + res = [[TSKPinningValidatorResult alloc] initWithServerHostname:@"www.test.com" + serverTrust:_testTrust + notedHostname:@"www.test.com" + validationResult:TSKPinValidationResultSuccess + finalTrustDecision:TSKTrustDecisionShouldAllowConnection + validationDuration:1.0 + certificateChain:_testCertificateChain]; + // Ensure that the reporter was NOT called - [pinSuccessReporterMock verify]; + [_trustKit sendValidationReport:res]; + [pinReporterMock verify]; + + [pinReporterMock stopMocking]; + _trustKit.pinFailureReporter = nil; #if !TARGET_OS_IPHONE - // OS X - Send a notification for a failed validation due to a custom CA and ensure no report gets sent - [[NSNotificationCenter defaultCenter] postNotificationName:kTSKValidationCompletedNotification - object:nil - userInfo:@{kTSKValidationResultNotificationKey: @(TSKPinValidationResultFailedUserDefinedTrustAnchor)}]; + // TEST USER-DEFINED SUCCESS + // Send a notification for a successful validation and ensure no report gets sent + pinReporterMock = OCMClassMock([TSKBackgroundReporter class]); + _trustKit.pinFailureReporter = pinReporterMock; + + res = [[TSKPinningValidatorResult alloc] initWithServerHostname:@"www.test.com" + serverTrust:_testTrust + notedHostname:@"www.test.com" + validationResult:TSKPinValidationResultFailedUserDefinedTrustAnchor + finalTrustDecision:TSKTrustDecisionShouldAllowConnection + validationDuration:1.0 + certificateChain:_testCertificateChain]; + // Ensure that the reporter was NOT called - [pinSuccessReporterMock verify]; + [_trustKit sendValidationReport:res]; + [pinReporterMock verify]; + [pinReporterMock stopMocking]; + pinReporterMock = nil; #endif - - // Cleanup - [TrustKit setGlobalPinFailureReporter: defaultReporter]; - [TrustKit resetConfiguration]; } @@ -167,7 +195,7 @@ - (void)testReporter validationResult:TSKPinValidationResultFailed expirationDate:[NSDate date]]; - [NSThread sleepForTimeInterval:2.0]; + [NSThread sleepForTimeInterval:0.1]; } - (void)testReporterNilExpirationDate @@ -189,7 +217,7 @@ - (void)testReporterNilExpirationDate validationResult:TSKPinValidationResultFailed expirationDate:nil]; - [NSThread sleepForTimeInterval:2.0]; + [NSThread sleepForTimeInterval:0.1]; } From 91301671aea1e98f00cef8853984b52f11e100f0 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Sun, 26 Mar 2017 14:09:58 -0400 Subject: [PATCH 004/126] Added partial test for NSURLSession - Tests respondsToSelector - Initial refactor of NSURLSession proxy class - Additional code cleanup - Removed some unused methods --- .../TSKNSURLConnectionDelegateProxy.h | 12 +- .../TSKNSURLConnectionDelegateProxy.m | 22 +-- .../Swizzling/TSKNSURLSessionDelegateProxy.h | 32 ++-- .../Swizzling/TSKNSURLSessionDelegateProxy.m | 170 ++++++------------ TrustKitTests/TSKNSURLConnectionTests.m | 68 +++++-- TrustKitTests/TSKNSURLSessionTests.m | 123 +++++++++++++ 6 files changed, 252 insertions(+), 175 deletions(-) diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h index c51127b8..a102e13e 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h @@ -8,21 +8,17 @@ #import +NS_ASSUME_NONNULL_BEGIN @interface TSKNSURLConnectionDelegateProxy : NSObject // Initalize our hooks + (void)swizzleNSURLConnectionConstructors; -- (instancetype)initWithDelegate:(id)delegate; - -// Mirror the original delegate's list of implemented methods -- (BOOL)respondsToSelector:(SEL)aSelector ; - -// Forward messages to the original delegate if the proxy doesn't implement the method -- (id)forwardingTargetForSelector:(SEL)sel; +- (instancetype _Nullable)initWithDelegate:(id)delegate; - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; - @end + +NS_ASSUME_NONNULL_END diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m index b7a29915..a0699353 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m @@ -137,13 +137,6 @@ - (BOOL)respondsToSelector:(SEL)aSelector } } - -- (id)forwardingTargetForSelector:(SEL)sel -{ - // Forward messages to the original delegate if the proxy doesn't implement the method - return _originalDelegate; -} - #pragma mark Instance methods #pragma GCC diagnostic push @@ -171,8 +164,6 @@ - (BOOL)forwardToOriginalDelegateAuthenticationChallenge:(NSURLAuthenticationCha - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { - BOOL wasChallengeHandled = NO; - // For SSL pinning we only care about server authentication if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { @@ -186,20 +177,17 @@ - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticatio if (trustDecision == TSKTrustDecisionShouldBlockConnection) { // Pinning validation failed - block the connection - wasChallengeHandled = YES; [challenge.sender cancelAuthenticationChallenge:challenge]; + return; } } // Forward all challenges (including client auth challenges) to the original delegate - if (!wasChallengeHandled) + // We will also get here if the pinning validation succeeded or the domain was not pinned + if ([self forwardToOriginalDelegateAuthenticationChallenge:challenge forConnection:connection] == NO) { - // We will also get here if the pinning validation succeeded or the domain was not pinned - if ([self forwardToOriginalDelegateAuthenticationChallenge:challenge forConnection:connection] == NO) - { - // The original delegate could not handle the challenge; use the default handler - [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge]; - } + // The original delegate could not handle the challenge; use the default handler + [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge]; } } #pragma GCC diagnostic pop diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h index dd973755..c7a58cd0 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h @@ -8,31 +8,25 @@ #import +NS_ASSUME_NONNULL_BEGIN + +typedef void(^TSKURLSessionAuthChallengeCallback)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential); @interface TSKNSURLSessionDelegateProxy : NSObject -{ - id originalDelegate; // The NSURLSessionDelegate we're going to proxy -} + (void)swizzleNSURLSessionConstructors; -- (_Nullable instancetype)initWithDelegate:(_Nonnull id)delegate; - -// Mirror the original delegate's list of implemented methods -- (BOOL)respondsToSelector:(_Nonnull SEL)aSelector; +- (instancetype _Nullable)initWithDelegate:(id)delegate; -// Forward messages to the original delegate if the proxy doesn't implement the method -- (_Nonnull id)forwardingTargetForSelector:(_Nonnull SEL)sel; +- (void)URLSession:(NSURLSession *)session +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(TSKURLSessionAuthChallengeCallback)completionHandler; -- (void)URLSession:(NSURLSession * _Nonnull)session -didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge - completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential * _Nullable credential))completionHandler; - -- (void)URLSession:(NSURLSession * _Nonnull)session - task:(NSURLSessionTask * _Nonnull)task -didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge - completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential * _Nullable credential))completionHandler; +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(TSKURLSessionAuthChallengeCallback)completionHandler; @end + +NS_ASSUME_NONNULL_END diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m index 7d7a2534..2d0b17f5 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m @@ -10,52 +10,34 @@ #import "../Dependencies/RSSwizzle/RSSwizzle.h" #import "../TrustKit+Private.h" +@interface TSKNSURLSessionDelegateProxy () +/* The NSURLSessionDelegate we're going to proxy */ +@property (nonatomic) id originalDelegate; +@property (nonatomic, getter=getLastTrustDecision) TSKTrustDecision lastTrustDecision; +@end @implementation TSKNSURLSessionDelegateProxy - -#pragma mark Private methods used for tests - -static TSKTrustDecision _lastTrustDecision = (TSKTrustDecision)-1; - -+(void)resetLastTrustDecision -{ - _lastTrustDecision = (TSKTrustDecision)-1; -} - -+(TSKTrustDecision)getLastTrustDecision -{ - return _lastTrustDecision; -} - - #pragma mark Public methods + (void)swizzleNSURLSessionConstructors { // Figure out NSURLSession's "real" class - NSString *NSURLSessionClass; - if (NSClassFromString(@"NSURLSession") != nil) - { - // iOS 8+ - NSURLSessionClass = @"NSURLSession"; - } - else if (NSClassFromString(@"__NSCFURLSession") != nil) - { - // Pre iOS 8, for some reason hooking NSURLSession doesn't work. We need to use the real/private class __NSCFURLSession - NSURLSessionClass = @"__NSCFURLSession"; - } - else + // Pre iOS 8, for some reason hooking NSURLSession doesn't work. We need to use the real/private class __NSCFURLSession + // TODO: Remove support for iOS < 8? + Class NSURLSessionClass = (NSClassFromString(@"NSURLSession") /* iOS 8+ */ + ?: NSClassFromString(@"__NSCFURLSession") /* iOS <8 */); + if (NSURLSessionClass == nil) { + NSAssert(false, @"ERROR: Could not find NSURLSession's class"); TSKLog(@"ERROR: Could not find NSURLSession's class"); return; } - // + sessionWithConfiguration:delegate:delegateQueue: #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wshadow" - RSSwizzleClassMethod(NSClassFromString(NSURLSessionClass), + RSSwizzleClassMethod(NSURLSessionClass, @selector(sessionWithConfiguration:delegate:delegateQueue:), RSSWReturnType(NSURLSession *), RSSWArguments(NSURLSessionConfiguration * _Nonnull configuration, id _Nullable delegate, NSOperationQueue * _Nullable queue), @@ -87,24 +69,24 @@ + (void)swizzleNSURLSessionConstructors // Not hooking the following methods as they end up calling +sessionWithConfiguration:delegate:delegateQueue: // +sessionWithConfiguration: // +sharedSession - #pragma clang diagnostic pop } - - (instancetype)initWithDelegate:(id)delegate { + NSParameterAssert(delegate); + self = [super init]; if (self) { - originalDelegate = delegate; + _originalDelegate = delegate; + _lastTrustDecision = (TSKTrustDecision)-1; } TSKLog(@"Proxy-ing NSURLSessionDelegate: %@", NSStringFromClass([delegate class])); return self; } - #pragma mark Delegate methods - (BOOL)respondsToSelector:(SEL)aSelector @@ -112,131 +94,89 @@ - (BOOL)respondsToSelector:(SEL)aSelector if (aSelector == @selector(URLSession:task:didReceiveChallenge:completionHandler:)) { // For the task-level handler, mirror the delegate - return [originalDelegate respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)]; + return [_originalDelegate respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)]; } else if (aSelector == @selector(URLSession:didReceiveChallenge:completionHandler:)) { - if ([originalDelegate respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)] == YES) + if ([_originalDelegate respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)] == YES) + { + return YES; + } + else if ([_originalDelegate respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)] == NO) { + // If the task-level handler is not implemented in the delegate, we need to implement the session-level handler + // regardless of what the delegate implements, to ensure we get to handle auth challenges so we can do pinning validation return YES; } else { - if ([originalDelegate respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)] == NO) - { - // If the task-level handler is not implemented in the delegate, we need to implement the session-level handler - // regardless of what the delegate implements, to ensure we get to handle auth challenges so we can do pinning validation - return YES; - } - else - { - // Let the task-level handler handle auth challenges - return NO; - } + // Let the task-level handler handle auth challenges + return NO; } } else { // The delegate proxy should mirror the original delegate's methods so that it doesn't change the app flow - return [originalDelegate respondsToSelector:aSelector]; + return [_originalDelegate respondsToSelector:aSelector]; } } - -- (id)forwardingTargetForSelector:(SEL)sel +- (BOOL)forwardToOriginalDelegateAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(TSKURLSessionAuthChallengeCallback)completionHandler + forSession:(NSURLSession * _Nonnull)session { - // Forward messages to the original delegate if the proxy doesn't implement the method - return originalDelegate; -} - - --(BOOL)forwardToOriginalDelegateAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge - completionHandler:(void (^ _Nonnull) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler - forSession:(NSURLSession * _Nonnull)session -{ - BOOL wasChallengeHandled = NO; - // Can the original delegate handle this challenge ? - if ([originalDelegate respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)]) + if ([_originalDelegate respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)]) { // Yes - forward the challenge to the original delegate - wasChallengeHandled = YES; - [originalDelegate URLSession:session didReceiveChallenge:challenge completionHandler:completionHandler]; + [_originalDelegate URLSession:session didReceiveChallenge:challenge completionHandler:completionHandler]; + return YES; } - return wasChallengeHandled; + return NO; } - -- (void)URLSession:(NSURLSession * _Nonnull)session -didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge - completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential * _Nullable credential))completionHandler +- (void)common_URLSession:(NSURLSession * _Nonnull)session + challenge:(NSURLAuthenticationChallenge * _Nonnull)challenge + completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential * _Nullable credential))completionHandler { - BOOL wasChallengeHandled = NO; - // For SSL pinning we only care about server authentication if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { - TSKTrustDecision trustDecision = TSKTrustDecisionShouldBlockConnection; - SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; - NSString *serverHostname = challenge.protectionSpace.host; - // Check the trust object against the pinning policy - trustDecision = [TSKPinningValidator evaluateTrust:serverTrust forHostname:serverHostname]; - _lastTrustDecision = trustDecision; - if (trustDecision == TSKTrustDecisionShouldBlockConnection) + _lastTrustDecision = [TSKPinningValidator evaluateTrust:challenge.protectionSpace.serverTrust + forHostname:challenge.protectionSpace.host]; + if (_lastTrustDecision == TSKTrustDecisionShouldBlockConnection) { // Pinning validation failed - block the connection - wasChallengeHandled = YES; completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, NULL); + return; // Challenge handled (blocked), stop here! } } - + // Forward all challenges (including client auth challenges) to the original delegate - if (wasChallengeHandled == NO) + // We will also get here if the pinning validation succeeded or the domain was not pinned + if ([self forwardToOriginalDelegateAuthenticationChallenge:challenge completionHandler:completionHandler forSession:session] == NO) { - // We will also get here if the pinning validation succeeded or the domain was not pinned - if ([self forwardToOriginalDelegateAuthenticationChallenge:challenge completionHandler:completionHandler forSession:session] == NO) - { - // The original delegate could not handle the challenge; use the default handler - completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, NULL); - } + // The original delegate could not handle the challenge; use the default handler + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, NULL); } } +- (void)URLSession:(NSURLSession * _Nonnull)session +didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge + completionHandler:(TSKURLSessionAuthChallengeCallback)completionHandler +{ + [self common_URLSession:session challenge:challenge completionHandler:completionHandler]; +} + - (void)URLSession:(NSURLSession * _Nonnull)session task:(NSURLSessionTask * _Nonnull)task didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler { - BOOL wasChallengeHandled = NO; - - // For SSL pinning we only care about server authentication - if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) - { - TSKTrustDecision trustDecision = TSKTrustDecisionShouldBlockConnection; - - // Check the trust object against the pinning policy - trustDecision = [TSKPinningValidator evaluateTrust:challenge.protectionSpace.serverTrust - forHostname:challenge.protectionSpace.host]; - _lastTrustDecision = trustDecision; - if (trustDecision == TSKTrustDecisionShouldBlockConnection) - { - // Pinning validation failed - block the connection - wasChallengeHandled = YES; - completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, NULL); - } - } - - // Forward all challenges (including client auth challenges) to the original delegate - if (wasChallengeHandled == NO) - { - // We will also get here if the pinning validation succeeded or the domain was not pinned - // If we're in this delegate method (and not URLSession:didReceiveChallenge:completionHandler:) - // it means the delegate definitely implements the handler method so we can call it directly - [originalDelegate URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler]; - } + [self common_URLSession:session challenge:challenge completionHandler:completionHandler]; } @end diff --git a/TrustKitTests/TSKNSURLConnectionTests.m b/TrustKitTests/TSKNSURLConnectionTests.m index a321e052..42cd4c78 100644 --- a/TrustKitTests/TSKNSURLConnectionTests.m +++ b/TrustKitTests/TSKNSURLConnectionTests.m @@ -225,20 +225,19 @@ - (void)test_respondsToSelector_falseForUnimplementedMethods XCTAssertFalse([proxy respondsToSelector:NSSelectorFromString(@"argle:bargle:")]); } -#pragma mark - forwardingTargetForSelector override - - -- (void)test_respondsToSelector_forwardsTargetForSelector -{ - TestModeADelegate *delegate = OCMStrictClassMock([TestModeADelegate class]); - OCMExpect([delegate fakeMethod]); - - TSKNSURLConnectionDelegateProxy *proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]; - [(id)proxy fakeMethod]; - - OCMVerifyAll((id)delegate); - [(id)delegate stopMocking]; -} +//#pragma mark - forwardingTargetForSelector override +// +//- (void)test_respondsToSelector_forwardsTargetForSelector +//{ +// TestModeADelegate *delegate = OCMStrictClassMock([TestModeADelegate class]); +// OCMExpect([delegate fakeMethod]); +// +// TSKNSURLConnectionDelegateProxy *proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]; +// [(id)proxy fakeMethod]; +// +// OCMVerifyAll((id)delegate); +// [(id)delegate stopMocking]; +//} #pragma mark - forwardToOriginalDelegateAuthenticationChallenge @@ -301,7 +300,7 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_notServerTrust OCMVerifyAll((id)delegate); } -- (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustA +- (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustA_allow { TestModeADelegate *delegate = OCMStrictClassMock([TestModeADelegate class]); @@ -338,7 +337,7 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustA [(id)proxy stopMocking]; } -- (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustB +- (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustB_allow { TestModeBDelegate *delegate = OCMPartialMock([TestModeBDelegate new]); @@ -376,6 +375,43 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustB [(id)proxy stopMocking]; } +// Test the block case: only need to test for one because the failure handling is identical +- (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustB_block +{ + TestModeBDelegate *delegate = OCMPartialMock([TestModeBDelegate new]); + + TSKPinningValidator *validator = OCMStrictClassMock([TSKPinningValidator class]); + [TrustKit initializeWithConfiguration:@{}]; + [TrustKit sharedInstance].pinningValidator = validator; + + NSURLConnection *cnxn = [[NSURLConnection alloc] init]; + NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"hostname" port:0 protocol:nil realm:nil + authenticationMethod:NSURLAuthenticationMethodServerTrust]; + NSURLAuthenticationChallenge *challenge = [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:space + proposedCredential:nil + previousFailureCount:0 + failureResponse:nil + error:nil + sender:delegate]; + + TSKNSURLConnectionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]); + + OCMExpect([validator evaluateTrust:space.serverTrust forHostname:@"hostname"]).andReturn(TSKTrustDecisionShouldBlockConnection); + OCMExpect([delegate cancelAuthenticationChallenge:challenge]); + + [proxy connection:cnxn willSendRequestForAuthenticationChallenge:challenge]; + + OCMVerifyAll((id)delegate); + OCMVerifyAll((id)validator); + OCMVerifyAll((id)proxy); + + [TrustKit sharedInstance].pinningValidator = [TSKPinningValidator new]; + [(id)validator stopMocking]; + [(id)delegate stopMocking]; + [(id)proxy stopMocking]; +} + + // TODO: add swizzling tests to ensure the above tested methods are properly invoked. diff --git a/TrustKitTests/TSKNSURLSessionTests.m b/TrustKitTests/TSKNSURLSessionTests.m index df09af97..34939603 100644 --- a/TrustKitTests/TSKNSURLSessionTests.m +++ b/TrustKitTests/TSKNSURLSessionTests.m @@ -10,6 +10,15 @@ #import "../TrustKit/TrustKit+Private.h" #import "../TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h" +@interface TSKNSURLSessionDelegateProxy (TestSupport) +@property (nonatomic) id originalDelegate; +@property (nonatomic) TSKPinValidationResult lastTrustDecision; + +- (BOOL)forwardToOriginalDelegateAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(TSKURLSessionAuthChallengeCallback)completionHandler + forSession:(NSURLSession * _Nonnull)session; +@end + /* #pragma mark Private test methods @interface TSKNSURLSessionDelegateProxy(Private) @@ -145,8 +154,122 @@ - (void)URLSession:(NSURLSession * _Nonnull)session } @end +*/ + +// An NSURLSessionDelegate +@interface SessionDelegate : NSObject +@end +@implementation SessionDelegate + +- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler +{ } + +- (void)fakeMethod { } + +@end + +// An NSURLSession and Task delegate +@interface TaskAndSessionDelegate : SessionDelegate +@end +@implementation TaskAndSessionDelegate + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler +{ } + +- (void)fakeMethod { } + +@end + +// An NSURLSessionTask delegate (only, no NSURLSessionDelegate methods) +@interface TaskDelegate : NSObject @end +@implementation TaskDelegate + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler +{ } +- (void)fakeMethod { } +@end + +// A delegate that doesn't actually implement any of the optional handlers +@interface NoOptionalsDelegate : NSObject @end +@implementation NoOptionalsDelegate +- (void)fakeMethod { } +@end + +#pragma mark - Test suite + +@interface TSKNSURLSessionTests : XCTestCase + +@end + +@implementation TSKNSURLSessionTests + +- (void)setUp { + [super setUp]; +} + +- (void)tearDown { + [super tearDown]; +} + +#pragma mark respondsToSelector override + +- (void)test_respondsToSelector_sessionDelegate +{ + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:[SessionDelegate new]]; + + XCTAssertTrue([proxy respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)]); + + XCTAssertFalse([proxy respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)]); + + XCTAssertTrue([proxy respondsToSelector:@selector(fakeMethod)]); + + XCTAssertFalse([proxy respondsToSelector:NSSelectorFromString(@"unimplementedMethod")]); +} + +- (void)test_respondsToSelector_taskDelegate +{ + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:[TaskDelegate new]]; + + XCTAssertFalse([proxy respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)]); + + XCTAssertTrue([proxy respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)]); + + XCTAssertTrue([proxy respondsToSelector:@selector(fakeMethod)]); + + XCTAssertFalse([proxy respondsToSelector:NSSelectorFromString(@"unimplementedMethod")]); +} + +- (void)test_respondsToSelector_taskAndSessionDelegate +{ + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:[TaskAndSessionDelegate new]]; + + XCTAssertTrue([proxy respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)]); + + XCTAssertTrue([proxy respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)]); + + XCTAssertTrue([proxy respondsToSelector:@selector(fakeMethod)]); + + XCTAssertFalse([proxy respondsToSelector:NSSelectorFromString(@"unimplementedMethod")]); +} + +- (void)test_respondsToSelector_noOptionalsDelegate +{ + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:[NoOptionalsDelegate new]]; + + XCTAssertTrue([proxy respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)]); + + XCTAssertFalse([proxy respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)]); + + XCTAssertTrue([proxy respondsToSelector:@selector(fakeMethod)]); + + XCTAssertFalse([proxy respondsToSelector:NSSelectorFromString(@"unimplementedMethod")]); +} + +@end + +/* #pragma mark Test suite @interface TSKNSURLSessionTests : XCTestCase From 86d78fe5e6ad89a92ca1359b30e08acd789ec499 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Tue, 28 Mar 2017 00:05:48 -0700 Subject: [PATCH 005/126] All tests pass (still did not re-add swizzling tests) --- TrustKit.xcodeproj/project.pbxproj | 2 +- TrustKit/Pinning/TSKSPKIHashCache.m | 1 + .../TSKNSURLConnectionDelegateProxy.m | 7 +- .../Swizzling/TSKNSURLSessionDelegateProxy.m | 8 +- TrustKit/TSKPinningValidator.m | 2 +- TrustKit/TrustKit.h | 8 +- TrustKit/TrustKit.m | 2 +- TrustKitTests/TSKNSURLSessionTests.m | 263 ++++++++- TrustKitTests/TSKPinningValidatorTests.m | 538 +++++++++++------- TrustKitTests/TSKPublicKeyAlgorithmTests.m | 20 +- 10 files changed, 619 insertions(+), 232 deletions(-) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index b2f9ef58..20c9cdc2 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -561,12 +561,12 @@ 8C8480561A896EE30017C155 /* TrustKitTests */ = { isa = PBXGroup; children = ( + 8C8480571A896EE30017C155 /* Supporting Files */, 8C5AB4631CF26A0300234B30 /* Dependencies */, 070868B31ADFF68200E5AFDC /* Certificates */, 8CD5F7371BCB02A7005801D8 /* TSKNSURLConnectionTests.m */, 8CD5F7561BCB7219005801D8 /* TSKNSURLSessionTests.m */, 2FA2868CAFECA46ADE0B6E3E /* TSKPinningValidatorTests.m */, - 8C8480571A896EE30017C155 /* Supporting Files */, 6B032D3F1AF1AEB600EAFA69 /* TSKReporterTests.m */, 8C15F9A31B17564400F06C0E /* TSKPinConfigurationTests.m */, 8CC78B1E1B1B586F00523A25 /* TSKCertificateUtils.h */, diff --git a/TrustKit/Pinning/TSKSPKIHashCache.m b/TrustKit/Pinning/TSKSPKIHashCache.m index 639b7358..e9e98676 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -100,6 +100,7 @@ - (instancetype)init if (self) { // Initialize our cache of SPKI hashes // First try to load a cached version from the filesystem + _spkiCacheFilename = @"spki-hash.cache"; _subjectPublicKeyInfoHashesCache = [self getSpkiCacheFromFileSystem]; TSKLog(@"Loaded %lu SPKI cache entries from the filesystem", (unsigned long)_subjectPublicKeyInfoHashesCache.count); diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m index a0699353..e7adba86 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m @@ -14,7 +14,6 @@ @interface TSKNSURLConnectionDelegateProxy () @property (nonatomic) id originalDelegate; // The NSURLConnectionDelegate we're going to proxy -@property (nonatomic) TSKTrustDecision lastTrustDecision; @end @implementation TSKNSURLConnectionDelegateProxy @@ -115,7 +114,6 @@ - (instancetype)initWithDelegate:(id)delegate if (self) { _originalDelegate = delegate; - _lastTrustDecision = (TSKTrustDecision)-1; } TSKLog(@"Proxy-ing NSURLConnectionDelegate: %@", NSStringFromClass([delegate class])); return self; @@ -167,13 +165,12 @@ - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticatio // For SSL pinning we only care about server authentication if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { - TSKTrustDecision trustDecision = TSKTrustDecisionShouldBlockConnection; SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; NSString *serverHostname = challenge.protectionSpace.host; // Check the trust object against the pinning policy - trustDecision = [TSKPinningValidator evaluateTrust:serverTrust forHostname:serverHostname]; - _lastTrustDecision = trustDecision; + TSKTrustDecision trustDecision = [TSKPinningValidator evaluateTrust:serverTrust + forHostname:serverHostname]; if (trustDecision == TSKTrustDecisionShouldBlockConnection) { // Pinning validation failed - block the connection diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m index 2d0b17f5..f0925767 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m @@ -13,7 +13,6 @@ @interface TSKNSURLSessionDelegateProxy () /* The NSURLSessionDelegate we're going to proxy */ @property (nonatomic) id originalDelegate; -@property (nonatomic, getter=getLastTrustDecision) TSKTrustDecision lastTrustDecision; @end @implementation TSKNSURLSessionDelegateProxy @@ -81,7 +80,6 @@ - (instancetype)initWithDelegate:(id)delegate if (self) { _originalDelegate = delegate; - _lastTrustDecision = (TSKTrustDecision)-1; } TSKLog(@"Proxy-ing NSURLSessionDelegate: %@", NSStringFromClass([delegate class])); return self; @@ -144,9 +142,9 @@ - (void)common_URLSession:(NSURLSession * _Nonnull)session if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { // Check the trust object against the pinning policy - _lastTrustDecision = [TSKPinningValidator evaluateTrust:challenge.protectionSpace.serverTrust - forHostname:challenge.protectionSpace.host]; - if (_lastTrustDecision == TSKTrustDecisionShouldBlockConnection) + TSKTrustDecision trustDecision = [TSKPinningValidator evaluateTrust:challenge.protectionSpace.serverTrust + forHostname:challenge.protectionSpace.host]; + if (trustDecision == TSKTrustDecisionShouldBlockConnection) { // Pinning validation failed - block the connection completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, NULL); diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index a148bc18..2352e210 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -77,7 +77,7 @@ - (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname else { // This domain has a pinning policy - NSDictionary *domainConfig = self.pinnedDomains[domainConfigKey]; + NSDictionary *domainConfig = self.pinnedDomains[kTSKPinnedDomains][domainConfigKey]; // Has the pinning policy expired? NSDate *expirationDate = domainConfig[kTSKExpirationDate]; diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 6aa51ed1..c0b1926d 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -326,14 +326,8 @@ FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp384r1; TrustKit.initialize(withConfiguration:trustKitConfig) ``` - The various configuration keys that can be specified in the policy are described in the + The various configuration keys that can be specified in the policy are described in the "Constants" section of the documentation. - - Lastly, once TrustKit has been initialized, `kTSKValidationCompletedNotification` notifications - will be posted every time TrustKit validates the certificate chain of a server; these - notifications provide some information about the validation that was done and can be used - for example for performance measurement. - */ @interface TrustKit : NSObject diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index c749789c..f43a01d4 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -135,7 +135,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo BOOL userTrustAnchorBypass = [_configuration[kTSKIgnorePinningForUserDefinedTrustAnchors] boolValue]; #endif __weak typeof(self) weakSelf = self; - _pinningValidator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:_configuration[kTSKPinnedDomains] + _pinningValidator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:_configuration ignorePinsForUserTrustAnchors:userTrustAnchorBypass validationResultQueue:_pinFailureReporterQueue validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { diff --git a/TrustKitTests/TSKNSURLSessionTests.m b/TrustKitTests/TSKNSURLSessionTests.m index 34939603..fbf3a69f 100644 --- a/TrustKitTests/TSKNSURLSessionTests.m +++ b/TrustKitTests/TSKNSURLSessionTests.m @@ -10,6 +10,8 @@ #import "../TrustKit/TrustKit+Private.h" #import "../TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h" +#import + @interface TSKNSURLSessionDelegateProxy (TestSupport) @property (nonatomic) id originalDelegate; @property (nonatomic) TSKPinValidationResult lastTrustDecision; @@ -17,6 +19,12 @@ @interface TSKNSURLSessionDelegateProxy (TestSupport) - (BOOL)forwardToOriginalDelegateAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(TSKURLSessionAuthChallengeCallback)completionHandler forSession:(NSURLSession * _Nonnull)session; + +- (void)common_URLSession:(NSURLSession * _Nonnull)session + challenge:(NSURLAuthenticationChallenge * _Nonnull)challenge + completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential * _Nullable credential))completionHandler; + @end /* @@ -157,15 +165,23 @@ - (void)URLSession:(NSURLSession * _Nonnull)session */ // An NSURLSessionDelegate -@interface SessionDelegate : NSObject +@interface SessionDelegate : NSObject @end @implementation SessionDelegate - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler -{ } +{ + completionHandler(NSURLSessionAuthChallengeUseCredential, challenge.proposedCredential); +} - (void)fakeMethod { } +- (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} + +- (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} + +- (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} + @end // An NSURLSession and Task delegate @@ -267,6 +283,249 @@ - (void)test_respondsToSelector_noOptionalsDelegate XCTAssertFalse([proxy respondsToSelector:NSSelectorFromString(@"unimplementedMethod")]); } +#pragma mark forwardToOriginalDelegateAuthenticationChallenge + +// Test session delegate that implements @selector(URLSession:didReceiveChallenge:completionHandler:) +- (void)test_forwardToOriginalDelegateAuthenticationChallenge_implements +{ + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:[SessionDelegate new]]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; + NSURLAuthenticationChallenge *challenge = [NSURLAuthenticationChallenge new]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"CallbackInvoked"]; + + BOOL result = [proxy forwardToOriginalDelegateAuthenticationChallenge:challenge + completionHandler:^(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) { + [expectation fulfill]; + } forSession:session]; + + XCTAssertTrue(result); + + [self waitForExpectationsWithTimeout:5.0 handler:nil]; +} + +// Test task delegate that doesn't implement @selector(URLSession:didReceiveChallenge:completionHandler:) +- (void)test_forwardToOriginalDelegateAuthenticationChallenge_doesNotImplement +{ + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:[TaskDelegate new]]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; + NSURLAuthenticationChallenge *challenge = [NSURLAuthenticationChallenge new]; + + BOOL result = [proxy forwardToOriginalDelegateAuthenticationChallenge:challenge + completionHandler:^(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) { + XCTFail(@"Should not be invoked"); + } forSession:session]; + + XCTAssertFalse(result); +} + +#pragma mark common_URLSession:challenge:challenge:completionHandler: + +- (void)test_common_URLSession_invalidAuthMethod_session +{ + SessionDelegate *delegate = [SessionDelegate new]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:delegate]; + + NSURLSession *session = [NSURLSession new]; + NSURLAuthenticationChallenge *challenge = ({ + NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"" + port:443 + protocol:@"" + realm:@"" + authenticationMethod:NSURLAuthenticationMethodHTTPBasic]; + [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:space + proposedCredential:nil + previousFailureCount:0 + failureResponse:nil + error:nil + sender:delegate]; + }); + + [proxy common_URLSession:session + challenge:challenge + completionHandler:^(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential *credential) { + XCTAssertEqual(disposition, NSURLSessionAuthChallengeUseCredential); + XCTAssertEqual(credential, challenge.proposedCredential); + }]; +} + +- (void)test_common_URLSession_invalidAuthMethod_task +{ + TaskDelegate *delegate = [TaskDelegate new]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:delegate]; + + NSURLSession *session = [NSURLSession new]; + NSURLAuthenticationChallenge *challenge = ({ + NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"" + port:443 + protocol:@"" + realm:@"" + authenticationMethod:NSURLAuthenticationMethodHTTPBasic]; + [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:space + proposedCredential:nil + previousFailureCount:0 + failureResponse:nil + error:nil + sender:[SessionDelegate new]]; + }); + + [proxy common_URLSession:session + challenge:challenge + completionHandler:^(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential *credential) { + XCTAssertEqual(disposition, NSURLSessionAuthChallengePerformDefaultHandling); + XCTAssertEqual(credential, challenge.proposedCredential); + }]; +} + +- (void)test_common_URLSession_session_pinFailed +{ + SessionDelegate *delegate = [SessionDelegate new]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:delegate]; + + NSURLSession *session = [NSURLSession new]; + NSURLAuthenticationChallenge *challenge = ({ + NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"hostname" + port:443 + protocol:@"" + realm:@"" + authenticationMethod:NSURLAuthenticationMethodServerTrust]; + [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:space + proposedCredential:nil + previousFailureCount:0 + failureResponse:nil + error:nil + sender:delegate]; + }); + + TSKPinningValidator *validator = OCMStrictClassMock([TSKPinningValidator class]); + [TrustKit initializeWithConfiguration:@{}]; + [TrustKit sharedInstance].pinningValidator = validator; + + OCMExpect([validator evaluateTrust:challenge.protectionSpace.serverTrust forHostname:@"hostname"]).andReturn(TSKTrustDecisionShouldBlockConnection); + + [proxy common_URLSession:session + challenge:challenge + completionHandler:^(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential *credential) { + XCTAssertEqual(disposition, NSURLSessionAuthChallengeCancelAuthenticationChallenge); + XCTAssertEqual(credential, challenge.proposedCredential); + }]; + + [(id)validator stopMocking]; + [TrustKit sharedInstance].pinningValidator = [TSKPinningValidator new]; +} + +- (void)test_common_URLSession_session_pinSuccess +{ + SessionDelegate *delegate = [SessionDelegate new]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:delegate]; + + NSURLSession *session = [NSURLSession new]; + NSURLAuthenticationChallenge *challenge = ({ + NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"hostname" + port:443 + protocol:@"" + realm:@"" + authenticationMethod:NSURLAuthenticationMethodServerTrust]; + [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:space + proposedCredential:nil + previousFailureCount:0 + failureResponse:nil + error:nil + sender:delegate]; + }); + + TSKPinningValidator *validator = OCMStrictClassMock([TSKPinningValidator class]); + [TrustKit initializeWithConfiguration:@{}]; + [TrustKit sharedInstance].pinningValidator = validator; + + OCMExpect([validator evaluateTrust:challenge.protectionSpace.serverTrust forHostname:@"hostname"]).andReturn(TSKTrustDecisionShouldAllowConnection); + + [proxy common_URLSession:session + challenge:challenge + completionHandler:^(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential *credential) { + XCTAssertEqual(disposition, NSURLSessionAuthChallengeUseCredential); + XCTAssertEqual(credential, challenge.proposedCredential); + }]; + + [(id)validator stopMocking]; + [TrustKit sharedInstance].pinningValidator = [TSKPinningValidator new]; +} + +#pragma mark URLSession:didReceiveChallenge:challenge:completionHandler: + +- (void)test_urlSessionChallengeDelegate +{ + SessionDelegate *delegate = [SessionDelegate new]; + TSKNSURLSessionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:delegate]); + + NSURLSession *session = [NSURLSession new]; + NSURLAuthenticationChallenge *challenge = ({ + NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"hostname" + port:443 + protocol:@"" + realm:@"" + authenticationMethod:NSURLAuthenticationMethodServerTrust]; + [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:space + proposedCredential:nil + previousFailureCount:0 + failureResponse:nil + error:nil + sender:delegate]; + }); + + TSKURLSessionAuthChallengeCallback completionHandler = ^(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) {}; + + OCMExpect([proxy common_URLSession:session + challenge:challenge + completionHandler:completionHandler]).andDo(^(NSInvocation *i){}); + + [proxy URLSession:session didReceiveChallenge:challenge completionHandler:completionHandler]; + + OCMVerifyAll((id)proxy); + [(id)proxy stopMocking]; +} + +#pragma mark URLSession:task:didReceiveChallenge:completionHandler: + +- (void)test_urlSessionTaskChallengeDelegate +{ + SessionDelegate *delegate = [SessionDelegate new]; + TSKNSURLSessionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:delegate]); + + NSURLSession *session = [NSURLSession new]; + NSURLAuthenticationChallenge *challenge = ({ + NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"hostname" + port:443 + protocol:@"" + realm:@"" + authenticationMethod:NSURLAuthenticationMethodServerTrust]; + [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:space + proposedCredential:nil + previousFailureCount:0 + failureResponse:nil + error:nil + sender:delegate]; + }); + + TSKURLSessionAuthChallengeCallback completionHandler = ^(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) {}; + + OCMExpect([proxy common_URLSession:session + challenge:challenge + completionHandler:completionHandler]).andDo(^(NSInvocation *i){}); + + [proxy URLSession:session + task:[NSURLSessionTask new] + didReceiveChallenge:challenge + completionHandler:completionHandler]; + + OCMVerifyAll((id)proxy); + [(id)proxy stopMocking]; +} + @end /* diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index b01ae531..5142aac1 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -12,6 +12,7 @@ #import #import "../TrustKit/TrustKit+Private.h" #import "../TrustKit/parse_configuration.h" +#import "../TrustKit/TSKPinningValidatorResult.h" #import "../TrustKit/Pinning/ssl_pin_verifier.h" #import "../TrustKit/Pinning/TSKSPKIHashCache.h" @@ -21,11 +22,20 @@ #import "TSKCertificateUtils.h" #import -/* +@interface TestAuthSender : NSObject +@end +@implementation TestAuthSender +- (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} +- (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} +- (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {} +@end + +@interface TSKSPKIHashCache (TestSupport) +- (void)resetSubjectPublicKeyInfoDiskCache; +@end + + @interface TSKPinningValidatorTests : XCTestCase -{ - TSKSPKIHashCache *spkiCache; -} @end @implementation TSKPinningValidatorTests @@ -35,6 +45,8 @@ @implementation TSKPinningValidatorTests SecCertificateRef _selfSignedCertificate; SecCertificateRef _leafCertificate; SecCertificateRef _globalsignRootCertificate; + + TSKSPKIHashCache *spkiCache; } @@ -50,6 +62,7 @@ - (void)setUp _globalsignRootCertificate = [TSKCertificateUtils createCertificateFromDer:@"GlobalSignRootCA"]; spkiCache = [TSKSPKIHashCache new]; + [spkiCache resetSubjectPublicKeyInfoDiskCache]; } @@ -97,58 +110,48 @@ - (void)testVerifyAgainstAnyPublicKey // Ensure the SPKI cache was on the filesystem is empty NSDictionary *fsCache = [spkiCache getSpkiCacheFromFileSystem]; - XCTAssert([fsCache[@(TSKPublicKeyAlgorithmRsa4096)] count] == 0, @"SPKI cache for RSA 4096 must be empty before the test"); + XCTAssert([fsCache[@1] count] == 0, @"SPKI cache for RSA 4096 must be empty before the test"); // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); NSDictionary *domainConfig = parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"]; - TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; - verificationResult = verifyPublicKeyPin(trust, - @"www.good.com", - domainConfig[kTSKPublicKeyAlgorithms], - domainConfig[kTSKPublicKeyHashes], - spkiCache); - + TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, + @"www.good.com", + domainConfig[kTSKPublicKeyAlgorithms], + domainConfig[kTSKPublicKeyHashes], + spkiCache); - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); - - - // Then test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; - - // Configure notification listener - // weak to work around fullfill being called multiple times - // http://stackoverflow.com/questions/27555499/xctestexpectation-how-to-avoid-calling-the-fulfill-method-after-the-wait-contex - __weak XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - NSDictionary *userInfo = [note userInfo]; - // Notification received, check the userInfo - XCTAssertEqualObjects(userInfo[kTSKValidationDecisionNotificationKey], @(TSKTrustDecisionShouldAllowConnection)); - XCTAssertEqualObjects(userInfo[kTSKValidationResultNotificationKey], @(TSKPinValidationResultSuccess)); - XCTAssertEqualObjects(userInfo[kTSKValidationCertificateChainNotificationKey], convertTrustToPemArray(trust)); - XCTAssertEqualObjects(userInfo[kTSKValidationNotedHostnameNotificationKey], @"www.good.com"); - [notifReceivedExpectation fulfill]; - }]; + XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, + @"Validation must pass against valid public key pins"); + + XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; + TSKPinningValidator *validator; + validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + ignorePinsForUserTrustAnchors:NO + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *result) { + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); + + XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + + XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); + + XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + + [expectation fulfill]; + }]; // Call TSKPinningValidator - TSKTrustDecision result = [TSKPinningValidator evaluateTrust:trust forHostname:@"www.good.com"]; - XCTAssert(result == TSKTrustDecisionShouldAllowConnection); + TSKTrustDecision result = [validator evaluateTrust:trust forHostname:@"www.good.com"]; + XCTAssertEqual(result, TSKTrustDecisionShouldAllowConnection); // Ensure a validation notification was posted - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { - if (error) { - NSLog(@"Expectation timeout Error: %@", error); - } - }]; - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; + [self waitForExpectationsWithTimeout:2.0 handler:nil]; // Ensure the SPKI cache was persisted to the filesystem fsCache = [spkiCache getSpkiCacheFromFileSystem]; - XCTAssert([fsCache[@1] count] == 1, @"SPKI cache for RSA 4096 must be persisted to the file system"); + XCTAssertEqual([fsCache[@1] count], 1UL, @"SPKI cache for RSA 4096 must be persisted to the file system"); CFRelease(trust); } @@ -176,7 +179,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey // Ensure the SPKI cache was on the filesystem is empty NSDictionary *fsCache = [spkiCache getSpkiCacheFromFileSystem]; - XCTAssert([fsCache[@1] count] == 0, @"SPKI cache for RSA 4096 must be empty before the test"); + XCTAssertEqual([fsCache[@1] count], 0UL, @"SPKI cache for RSA 4096 must be empty before the test"); // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); @@ -189,17 +192,36 @@ - (void)testVerifyAgainstIntermediateCAPublicKey spkiCache); - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); + XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, + @"Validation must pass against valid public key pins"); // Then test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; - XCTAssert([TSKPinningValidator evaluateTrust:trust forHostname:@"www.good.com"] == TSKTrustDecisionShouldAllowConnection); - + XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; + TSKPinningValidator *validator; + validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + ignorePinsForUserTrustAnchors:NO + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *result) { + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); + + XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + + XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); + + XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + + [expectation fulfill]; + }]; + + TSKTrustDecision result = [validator evaluateTrust:trust forHostname:@"www.good.com"]; + XCTAssertEqual(result, TSKTrustDecisionShouldAllowConnection); + + [self waitForExpectationsWithTimeout:2.0 handler:nil]; // Ensure the SPKI cache was persisted to the filesystem fsCache = [spkiCache getSpkiCacheFromFileSystem]; - XCTAssert([fsCache[@1] count] == 2, @"SPKI cache for RSA 4096 must be persisted to the file system"); + XCTAssertEqual([fsCache[@1] count], 2UL, @"SPKI cache for RSA 4096 must be persisted to the file system"); CFRelease(trust); } @@ -236,12 +258,33 @@ - (void)testVerifyAgainstCAPublicKey spkiCache); - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); + XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, + @"Validation must pass against valid public key pins"); // Then test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; - XCTAssert([TSKPinningValidator evaluateTrust:trust forHostname:@"www.good.com"] == TSKTrustDecisionShouldAllowConnection); + XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; + TSKPinningValidator *validator; + validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + ignorePinsForUserTrustAnchors:NO + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *result) { + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); + + XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + + XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); + + XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + + [expectation fulfill]; + }]; + + XCTAssertEqual([validator evaluateTrust:trust forHostname:@"www.good.com"], + TSKTrustDecisionShouldAllowConnection); + + [self waitForExpectationsWithTimeout:2.0 handler:nil]; + CFRelease(trust); } @@ -277,12 +320,33 @@ - (void)testVerifyAgainstLeafPublicKey spkiCache); - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); + XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, + @"Validation must pass against valid public key pins"); // Then test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; - XCTAssert([TSKPinningValidator evaluateTrust:trust forHostname:@"www.good.com"] == TSKTrustDecisionShouldAllowConnection); + XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; + TSKPinningValidator *validator; + validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + ignorePinsForUserTrustAnchors:NO + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *result) { + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); + + XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + + XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); + + XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + + [expectation fulfill]; + }]; + + XCTAssertEqual([validator evaluateTrust:trust forHostname:@"www.good.com"], + TSKTrustDecisionShouldAllowConnection); + + [self waitForExpectationsWithTimeout:2.0 handler:nil]; + CFRelease(trust); } @@ -318,38 +382,34 @@ - (void)testVerifyAgainstBadPublicKey spkiCache); - XCTAssert(verificationResult == TSKPinValidationResultFailed, @"Validation must fail against bad public key pins"); + XCTAssertEqual(verificationResult, TSKPinValidationResultFailed, + @"Validation must fail against bad public key pins"); // Then test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; - - // Configure notification listener - __weak XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - NSDictionary *userInfo = [note userInfo]; - // Notification received, check the userInfo - XCTAssertEqualObjects(userInfo[kTSKValidationDecisionNotificationKey], @(TSKTrustDecisionShouldBlockConnection)); - XCTAssertEqualObjects(userInfo[kTSKValidationResultNotificationKey], @(TSKPinValidationResultFailed)); - XCTAssertEqualObjects(userInfo[kTSKValidationCertificateChainNotificationKey], convertTrustToPemArray(trust)); - XCTAssertEqualObjects(userInfo[kTSKValidationNotedHostnameNotificationKey], @"www.good.com"); - [notifReceivedExpectation fulfill]; - }]; + XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; + TSKPinningValidator *validator; + validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + ignorePinsForUserTrustAnchors:NO + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *result) { + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); + + XCTAssertEqual(result.validationResult, TSKPinValidationResultFailed); + + XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); + + XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + + [expectation fulfill]; + }]; // Call TSKPinningValidator - TSKTrustDecision result = [TSKPinningValidator evaluateTrust:trust forHostname:@"www.good.com"]; - XCTAssert(result == TSKTrustDecisionShouldBlockConnection); + TSKTrustDecision result = [validator evaluateTrust:trust forHostname:@"www.good.com"]; + XCTAssertEqual(result, TSKTrustDecisionShouldBlockConnection); // Ensure a validation notification was posted - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { - if (error) { - NSLog(@"Expectation timeout Error: %@", error); - } - }]; - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; + [self waitForExpectationsWithTimeout:2.0 handler:nil]; CFRelease(trust); } @@ -371,7 +431,7 @@ - (void)testVerifyAgainstBadPublicKeyPinsExpired kTSKPinnedDomains : @{@"www.good.com" : @{ // Totally expired - kTSKExpirationDate: @"2014-01-01", + kTSKExpirationDate: [NSDate dateWithTimeIntervalSinceReferenceDate:0], kTSKEnforcePinning: @YES, kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Bad Key @@ -379,21 +439,18 @@ - (void)testVerifyAgainstBadPublicKeyPinsExpired ]}}}; // Test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; + TSKPinningValidator *validator; + validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig + ignorePinsForUserTrustAnchors:NO + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *result) { + XCTFail(@"Should not be invoked"); + }]; - // Configure notification listener - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - // Ensure a validation notification was NOT posted - XCTFail(@"kTSKValidationCompletedNotification should not have been posted"); - }]; // Call TSKPinningValidator - TSKTrustDecision result = [TSKPinningValidator evaluateTrust:trust forHostname:@"www.good.com"]; - XCTAssert(result == TSKTrustDecisionDomainNotPinned); + TSKTrustDecision result = [validator evaluateTrust:trust forHostname:@"www.good.com"]; + XCTAssertEqual(result, TSKTrustDecisionDomainNotPinned); - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; CFRelease(trust); } @@ -428,39 +485,32 @@ - (void)testVerifyAgainstBadPublicKeyPinningNotEnforced parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes], spkiCache); - - XCTAssert(verificationResult == TSKPinValidationResultFailed, @"Validation must fail against bad public key pins"); - + XCTAssertEqual(verificationResult, TSKPinValidationResultFailed, @"Validation must fail against bad public key pins"); // Then test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; - - // Configure notification listener - __weak XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - NSDictionary *userInfo = [note userInfo]; - // Notification received, check the userInfo - XCTAssertEqualObjects(userInfo[kTSKValidationDecisionNotificationKey], @(TSKTrustDecisionShouldAllowConnection)); - XCTAssertEqualObjects(userInfo[kTSKValidationResultNotificationKey], @(TSKPinValidationResultFailed)); - XCTAssertEqualObjects(userInfo[kTSKValidationCertificateChainNotificationKey], convertTrustToPemArray(trust)); - XCTAssertEqualObjects(userInfo[kTSKValidationNotedHostnameNotificationKey], @"www.good.com"); - [notifReceivedExpectation fulfill]; - }]; + XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; + TSKPinningValidator *validator; + validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + ignorePinsForUserTrustAnchors:NO + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *result) { + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); + + XCTAssertEqual(result.validationResult, TSKPinValidationResultFailed); + + XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); + + XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + + [expectation fulfill]; + }]; // Call TSKPinningValidator - TSKTrustDecision result = [TSKPinningValidator evaluateTrust:trust forHostname:@"www.good.com"]; - XCTAssert(result == TSKTrustDecisionShouldAllowConnection); + TSKTrustDecision result = [validator evaluateTrust:trust forHostname:@"www.good.com"]; + XCTAssertEqual(result, TSKTrustDecisionShouldAllowConnection); // Ensure a validation notification was posted - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { - if (error) { - NSLog(@"Expectation timeout Error: %@", error); - } - }]; - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; + [self waitForExpectationsWithTimeout:2.0 handler:nil]; CFRelease(trust); } @@ -497,12 +547,34 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey spkiCache); - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); + XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, + @"Validation must pass against valid public key pins"); // Then test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; - XCTAssert([TSKPinningValidator evaluateTrust:trust forHostname:@"www.good.com"] == TSKTrustDecisionShouldAllowConnection); + XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; + TSKPinningValidator *validator; + validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + ignorePinsForUserTrustAnchors:NO + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *result) { + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); + + XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + + XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); + + XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + + [expectation fulfill]; + }]; + + XCTAssertEqual([validator evaluateTrust:trust forHostname:@"www.good.com"], + TSKTrustDecisionShouldAllowConnection); + + // Ensure a validation notification was posted + [self waitForExpectationsWithTimeout:2.0 handler:nil]; + CFRelease(trust); } @@ -539,38 +611,34 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain spkiCache); - XCTAssert(verificationResult == TSKPinValidationResultFailedCertificateChainNotTrusted, @"Validation must fail against bad certificate chain"); + XCTAssertEqual(verificationResult, TSKPinValidationResultFailedCertificateChainNotTrusted, + @"Validation must fail against bad certificate chain"); // Then test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; - - // Configure notification listener - __weak XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - NSDictionary *userInfo = [note userInfo]; - // Notification received, check the userInfo - XCTAssertEqualObjects(userInfo[kTSKValidationDecisionNotificationKey], @(TSKTrustDecisionShouldBlockConnection)); - XCTAssertEqualObjects(userInfo[kTSKValidationResultNotificationKey], @(TSKPinValidationResultFailedCertificateChainNotTrusted)); - XCTAssertEqualObjects(userInfo[kTSKValidationCertificateChainNotificationKey], convertTrustToPemArray(trust)); - XCTAssertEqualObjects(userInfo[kTSKValidationNotedHostnameNotificationKey], @"www.good.com"); - [notifReceivedExpectation fulfill]; - }]; + XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; + TSKPinningValidator *validator; + validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + ignorePinsForUserTrustAnchors:NO + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *result) { + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); + + XCTAssertEqual(result.validationResult, TSKPinValidationResultFailedCertificateChainNotTrusted); + + XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); + + XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + + [expectation fulfill]; + }]; // Call TSKPinningValidator - TSKTrustDecision result = [TSKPinningValidator evaluateTrust:trust forHostname:@"www.good.com"]; - XCTAssert(result == TSKTrustDecisionShouldBlockConnection); + XCTAssertEqual([validator evaluateTrust:trust forHostname:@"www.good.com"], + TSKTrustDecisionShouldBlockConnection); // Ensure a validation notification was posted - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { - if (error) { - NSLog(@"Expectation timeout Error: %@", error); - } - }]; - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; + [self waitForExpectationsWithTimeout:2.0 handler:nil]; CFRelease(trust); } @@ -608,12 +676,34 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname spkiCache); - XCTAssert(verificationResult == TSKPinValidationResultFailedCertificateChainNotTrusted, @"Validation must fail against bad hostname"); + XCTAssertEqual(verificationResult, TSKPinValidationResultFailedCertificateChainNotTrusted, + @"Validation must fail against bad hostname"); // Then test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; - XCTAssert([TSKPinningValidator evaluateTrust:trust forHostname:@"www.bad.com"] == TSKTrustDecisionShouldBlockConnection); + XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; + TSKPinningValidator *validator; + validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + ignorePinsForUserTrustAnchors:NO + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *result) { + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); + + XCTAssertEqual(result.validationResult, TSKPinValidationResultFailedCertificateChainNotTrusted); + + XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); + + XCTAssertEqualObjects(result.notedHostname, @"www.bad.com"); + + [expectation fulfill]; + }]; + + XCTAssertEqual([validator evaluateTrust:trust forHostname:@"www.bad.com"], + TSKTrustDecisionShouldBlockConnection); + + // Ensure a validation notification was posted + [self waitForExpectationsWithTimeout:2.0 handler:nil]; + CFRelease(trust); } @@ -650,12 +740,34 @@ - (void)testVerifyAgainstInjectedCaPublicKey spkiCache); - XCTAssert(verificationResult == TSKTrustDecisionShouldBlockConnection, @"Validation must fail against injected pinned CA"); + XCTAssertEqual(verificationResult, TSKTrustDecisionShouldBlockConnection, + @"Validation must fail against injected pinned CA"); // Then test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; - XCTAssert([TSKPinningValidator evaluateTrust:trust forHostname:@"www.good.com"] == TSKTrustDecisionShouldBlockConnection); + XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; + TSKPinningValidator *validator; + validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + ignorePinsForUserTrustAnchors:NO + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *result) { + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); + + XCTAssertEqual(result.validationResult, TSKPinValidationResultFailed); + + XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); + + XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + + [expectation fulfill]; + }]; + + XCTAssertEqual([validator evaluateTrust:trust forHostname:@"www.good.com"], + TSKTrustDecisionShouldBlockConnection); + + // Ensure a validation notification was posted + [self waitForExpectationsWithTimeout:2.0 handler:nil]; + CFRelease(trust); } @@ -682,21 +794,18 @@ - (void)testDomainNotPinned // Then test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; + TSKPinningValidator *validator; + validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig + ignorePinsForUserTrustAnchors:NO + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *result) { + XCTFail(@"Should not invoke callback"); + }]; - // Configure notification listener - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - // Ensure a validation notification was NOT posted - XCTFail(@"kTSKValidationCompletedNotification should not have been posted"); - }]; // Call TSKPinningValidator - TSKTrustDecision result = [TSKPinningValidator evaluateTrust:trust forHostname:@"www.nonpinned.com"]; - XCTAssert(result == TSKTrustDecisionDomainNotPinned); + XCTAssertEqual([validator evaluateTrust:trust forHostname:@"www.nonpinned.com"], + TSKTrustDecisionDomainNotPinned); - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; CFRelease(trust); } @@ -729,7 +838,7 @@ -(void) testHandleChallengeCompletionHandlerDomainNotPinned void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) { // For a non-pinned domain, we expect the default SSL validation to be called - XCTAssert(disposition == NSURLSessionAuthChallengePerformDefaultHandling); + XCTAssertEqual(disposition, NSURLSessionAuthChallengePerformDefaultHandling); XCTAssertNil(credential); wasHandlerCalled = YES; }; @@ -747,8 +856,8 @@ -(void) testHandleChallengeCompletionHandlerDomainNotPinned // Test the helper method BOOL wasChallengeHandled = [TSKPinningValidator handleChallenge:challengeMock completionHandler:completionHandler]; - XCTAssert(wasChallengeHandled == YES); - XCTAssert(wasHandlerCalled == YES); + XCTAssertTrue(wasChallengeHandled); + XCTAssertTrue(wasHandlerCalled); CFRelease(trust); } @@ -768,20 +877,17 @@ -(void) testHandleChallengeCompletionHandlerPinningFailed NSDictionary *trustKitConfig = @{kTSKSwizzleNetworkDelegates: @NO, kTSKPinnedDomains : @{@"www.good.com" : @{ + kTSKEnforcePinning: @YES, kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", //Fake Key @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Fake key ]}}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; - - __block BOOL wasHandlerCalled = NO; - void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) - { - // For a pinning failure, we expect the authentication challenge to be cancelled - XCTAssert(disposition == NSURLSessionAuthChallengeCancelAuthenticationChallenge); - XCTAssertNil(credential); - wasHandlerCalled = YES; - }; + TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig + ignorePinsForUserTrustAnchors:YES + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { + // + }]; // Mock a protection space id protectionSpaceMock = [OCMockObject mockForClass:[NSURLProtectionSpace class]]; @@ -793,11 +899,21 @@ -(void) testHandleChallengeCompletionHandlerPinningFailed id challengeMock = [OCMockObject mockForClass:[NSURLAuthenticationChallenge class]]; OCMStub([challengeMock protectionSpace]).andReturn(protectionSpaceMock); + + __block BOOL wasHandlerCalled = NO; + void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) + { + // For a pinning failure, we expect the authentication challenge to be cancelled + XCTAssertEqual(disposition, NSURLSessionAuthChallengeCancelAuthenticationChallenge); + XCTAssertNil(credential); + wasHandlerCalled = YES; + }; + // Test the helper method - BOOL wasChallengeHandled = [TSKPinningValidator handleChallenge:challengeMock completionHandler:completionHandler]; + BOOL wasChallengeHandled = [validator handleChallenge:challengeMock completionHandler:completionHandler]; - XCTAssert(wasChallengeHandled == YES); - XCTAssert(wasHandlerCalled == YES); + XCTAssertTrue(wasChallengeHandled); + XCTAssertTrue(wasHandlerCalled); CFRelease(trust); } @@ -821,32 +937,46 @@ -(void) testHandleChallengeCompletionHandlerPinningSuccessful kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=", // CA Key @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Fake key ]}}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + + TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig + ignorePinsForUserTrustAnchors:YES + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { + // + }]; + + // Mock a protection space + NSURLProtectionSpace *protectionSpace = OCMPartialMock([[NSURLProtectionSpace alloc] initWithHost:@"www.good.com" + port:443 + protocol:NSURLProtectionSpaceHTTPS + realm:nil + authenticationMethod:NSURLAuthenticationMethodServerTrust]); + + NSURLCredential *credential = [NSURLCredential credentialForTrust:trust]; + OCMStub([protectionSpace serverTrust]).andReturn(trust); + + // Mock an authentication challenge + NSURLAuthenticationChallenge *challenge = [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:protectionSpace + proposedCredential:credential + previousFailureCount:0 + failureResponse:nil + error:nil + sender:[TestAuthSender new]]; __block BOOL wasHandlerCalled = NO; - void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) + void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable gotCredential) { // For a pinning success, we expect the authentication challenge to use the supplied credential - XCTAssert(disposition == NSURLSessionAuthChallengeUseCredential); - XCTAssertTrue([credential isEqual:[NSURLCredential credentialForTrust:trust]]); + XCTAssertEqual(disposition, NSURLSessionAuthChallengeUseCredential); + XCTAssertEqualObjects(gotCredential, credential); wasHandlerCalled = YES; }; - // Mock a protection space - id protectionSpaceMock = [OCMockObject mockForClass:[NSURLProtectionSpace class]]; - OCMStub([protectionSpaceMock authenticationMethod]).andReturn(NSURLAuthenticationMethodServerTrust); - OCMStub([protectionSpaceMock host]).andReturn(@"www.good.com"); - OCMStub([protectionSpaceMock serverTrust]).andReturn(trust); - - // Mock an authentication challenge - id challengeMock = [OCMockObject mockForClass:[NSURLAuthenticationChallenge class]]; - OCMStub([challengeMock protectionSpace]).andReturn(protectionSpaceMock); - // Test the helper method - BOOL wasChallengeHandled = [TSKPinningValidator handleChallenge:challengeMock completionHandler:completionHandler]; + BOOL wasChallengeHandled = [validator handleChallenge:challenge completionHandler:completionHandler]; - XCTAssert(wasChallengeHandled == YES); - XCTAssert(wasHandlerCalled == YES); + XCTAssertTrue(wasChallengeHandled); + XCTAssertTrue(wasHandlerCalled); CFRelease(trust); } @@ -893,8 +1023,8 @@ -(void) testHandleChallengeCompletionHandlerNotServerTrustAuthenticationMethod // Test the helper method BOOL wasChallengeHandled = [TSKPinningValidator handleChallenge:challengeMock completionHandler:completionHandler]; - XCTAssert(wasChallengeHandled == NO); - XCTAssert(wasHandlerCalled == NO); + XCTAssertFalse(wasChallengeHandled); + XCTAssertFalse(wasHandlerCalled); CFRelease(trust); } @@ -925,9 +1055,11 @@ - (void)testExcludedSubdomain // Then test TSKPinningValidator [TrustKit initializeWithConfiguration:trustKitConfig]; - XCTAssert([TSKPinningValidator evaluateTrust:trust forHostname:@"unsecured.good.com"] == TSKTrustDecisionDomainNotPinned); + + XCTAssertEqual([TSKPinningValidator evaluateTrust:trust forHostname:@"unsecured.good.com"], + TSKTrustDecisionDomainNotPinned); + CFRelease(trust); } @end -*/ diff --git a/TrustKitTests/TSKPublicKeyAlgorithmTests.m b/TrustKitTests/TSKPublicKeyAlgorithmTests.m index dcc468e5..bc882c7f 100644 --- a/TrustKitTests/TSKPublicKeyAlgorithmTests.m +++ b/TrustKitTests/TSKPublicKeyAlgorithmTests.m @@ -20,18 +20,24 @@ #import "TSKCertificateUtils.h" +@interface TSKSPKIHashCache (TestSupport) +- (void)resetSubjectPublicKeyInfoDiskCache; +@end + + @interface TSKPublicKeyAlgorithmTests : XCTestCase -{ - TSKSPKIHashCache *spkiCache; -} @end @implementation TSKPublicKeyAlgorithmTests +{ + TSKSPKIHashCache *spkiCache; +} - (void)setUp { [super setUp]; spkiCache = [TSKSPKIHashCache new]; + [spkiCache resetSubjectPublicKeyInfoDiskCache]; } - (void)tearDown @@ -119,8 +125,8 @@ - (void)testVerifyMultipleAlgorithms kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", // Server Key @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}); - XCTAssert([spkiCache.getSpkiCache[@0] count] == 0, @"SPKI cache must be empty"); - XCTAssert([spkiCache.getSpkiCache[@1] count] == 0, @"SPKI cache must be empty"); + XCTAssertEqual([spkiCache.getSpkiCache[@0] count], 0UL, @"SPKI cache must be empty"); + XCTAssertEqual([spkiCache.getSpkiCache[@1] count], 0UL, @"SPKI cache must be empty"); TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; verificationResult = verifyPublicKeyPin(trust, @@ -130,8 +136,8 @@ - (void)testVerifyMultipleAlgorithms spkiCache); // Ensure the SPKI cache was used; the full certificate chain is three certs and we have to go through all of them to get to the pinned leaf - XCTAssert([spkiCache.getSpkiCache[@0] count] == 3, @"SPKI cache must have been used"); - XCTAssert([spkiCache.getSpkiCache[@1] count] == 3, @"SPKI cache must have been used"); + XCTAssertEqual([spkiCache.getSpkiCache[@0] count], 3UL, @"SPKI cache must have been used"); + XCTAssertEqual([spkiCache.getSpkiCache[@1] count], 3UL, @"SPKI cache must have been used"); CFRelease(trust); CFRelease(leafCertificate); From c75468953dcdd2c97b31ae478659f219fb5e4b81 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Mon, 3 Apr 2017 17:33:03 -0400 Subject: [PATCH 006/126] =?UTF-8?q?Optimize=20and=20modernize=20TSKPinFail?= =?UTF-8?q?ureReporter.m=20Date=20formatters=20are=20notoriously=20heavy,?= =?UTF-8?q?=20so=20I=20just=20made=20them=20init=20once=20=E2=80=93=C2=A0b?= =?UTF-8?q?ut=20then=20got=20distracted=20into=20modernizing=20the=20synta?= =?UTF-8?q?x=20of=20the=20rest=20of=20the=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TrustKit/Reporting/TSKPinFailureReport.m | 86 ++++++++++++++++-------- 1 file changed, 57 insertions(+), 29 deletions(-) diff --git a/TrustKit/Reporting/TSKPinFailureReport.m b/TrustKit/Reporting/TSKPinFailureReport.m index e429d4ed..2acca16f 100644 --- a/TrustKit/Reporting/TSKPinFailureReport.m +++ b/TrustKit/Reporting/TSKPinFailureReport.m @@ -57,47 +57,75 @@ - (nonnull instancetype) initWithAppBundleId:(nonnull NSString *)appBundleId - (nonnull NSData *)json; { - // Convert the date to a string - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + // NSDateFormatter (and NSNumberFormatter) is extremely expensive to initialize, doesn't + // change, and is listed as explicitely thread safe, so lets reuse the instance. + static NSDateFormatter *DateTimeFormatter = nil; + static NSDateFormatter *DateFormatter = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + /// Date AND time formatter for JSON + DateTimeFormatter = ({ + NSDateFormatter *df = [[NSDateFormatter alloc] init]; + + // Explicitely set the locale to avoid an iOS 8 bug + // http://stackoverflow.com/questions/29374181/nsdateformatter-hh-returning-am-pm-on-ios-8-device + df.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + + df.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'Z'"; + df.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; + df; + }); + + /// Date ONLY formatter + DateFormatter = ({ + NSDateFormatter *df = [[NSDateFormatter alloc] init]; + + // Explicitely set the locale to avoid an iOS 8 bug + // http://stackoverflow.com/questions/29374181/nsdateformatter-hh-returning-am-pm-on-ios-8-device + df.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + + df.dateFormat = @"yyyy-MM-dd"; + df.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; + df; + }); + }); - // Explicitely set the locale to avoid an iOS 8 bug - // http://stackoverflow.com/questions/29374181/nsdateformatter-hh-returning-am-pm-on-ios-8-device - [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]]; - - [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"]; - [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; - NSString *currentTimeStr = [dateFormatter stringFromDate: self.dateTime]; + id dateStr = [NSNull null]; + if (self.dateTime) + { + dateStr = [DateTimeFormatter stringFromDate:self.dateTime] ?: [NSNull null]; + } id expirationDateStr = [NSNull null]; if (self.knownPinsExpirationDate) { // For the expiration date, only return the expiration day, as specified in the pinning policy - [dateFormatter setDateFormat:@"yyyy-MM-dd"]; - expirationDateStr = [dateFormatter stringFromDate:self.knownPinsExpirationDate]; + expirationDateStr = [DateFormatter stringFromDate:self.knownPinsExpirationDate] ?: [NSNull null]; } // Create the dictionary NSDictionary *requestData = @{ - @"app-bundle-id" : self.appBundleId, - @"app-version" : self.appVersion, - @"app-platform" : self.appPlatform, - @"app-platform-version" : self.appPlatformVersion, - @"app-vendor-id" : self.appVendorId, - @"trustkit-version" : self.trustkitVersion, - @"date-time" : currentTimeStr, - @"hostname" : self.hostname, - @"port" : self.port, - @"noted-hostname" : self.notedHostname, - @"include-subdomains" : [NSNumber numberWithBool:self.includeSubdomains], - @"enforce-pinning" : [NSNumber numberWithBool:self.enforcePinning], - @"validated-certificate-chain" : self.validatedCertificateChain, - @"known-pins" : self.knownPins, - @"validation-result": [NSNumber numberWithInt:self.validationResult], - @"known-pins-expiration-date": expirationDateStr + @"app-bundle-id": self.appBundleId, + @"app-version": self.appVersion, + @"app-platform": self.appPlatform, + @"app-platform-version": self.appPlatformVersion, + @"app-vendor-id": self.appVendorId, + @"trustkit-version": self.trustkitVersion, + @"date-time": dateStr, + @"hostname": self.hostname, + @"port": self.port, + @"noted-hostname": self.notedHostname, + @"include-subdomains": @(self.includeSubdomains), + @"enforce-pinning": @(self.enforcePinning), + @"validated-certificate-chain": self.validatedCertificateChain, + @"known-pins": self.knownPins, + @"validation-result": @(self.validationResult), + @"known-pins-expiration-date": expirationDateStr }; NSError *error; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:requestData options:(NSJSONWritingOptions)0 error:&error]; + // FIXME: error is unhandled. return jsonData; } @@ -105,9 +133,9 @@ - (nonnull NSData *)json; - (nonnull NSMutableURLRequest *)requestToUri:(NSURL *)reportUri { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:reportUri]; - [request setHTTPMethod:@"POST"]; + request.HTTPMethod = @"POST"; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; - [request setHTTPBody:[self json]]; + request.HTTPBody = [self json]; return request; } From f47aa0ed2abe0ad2f2a3aa0b0abf1e563055e30c Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Wed, 5 Apr 2017 18:00:51 -0400 Subject: [PATCH 007/126] Refactored TSKReportsRateLimiter: no singleton - Removed all locks, totally non-locking implementation with no app induced kernel calls - Pulled the rate limiter tests into their own test suite and a few more unit tests - Simplified the public API of rate limiter. --- TrustKit.xcodeproj/project.pbxproj | 14 +- TrustKit/Reporting/TSKBackgroundReporter.m | 16 +- TrustKit/Reporting/TSKReportsRateLimiter.h | 9 +- TrustKit/Reporting/TSKReportsRateLimiter.m | 108 ++++++------ TrustKitTests/TSKReporterTests.m | 106 +----------- TrustKitTests/TSKReportsRateLimiterTests.m | 186 +++++++++++++++++++++ 6 files changed, 263 insertions(+), 176 deletions(-) create mode 100644 TrustKitTests/TSKReportsRateLimiterTests.m diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 20c9cdc2..ab397826 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -241,6 +241,7 @@ FC1A090F1E57AC450055B12C /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */; }; FC1A09101E57AC450055B12C /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */; }; FC1A09111E57AC450055B12C /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */; }; + FC4CAC7B1E958E0500DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -389,6 +390,7 @@ FC1A09081E57AC450055B12C /* TSKSPKIHashCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKSPKIHashCache.h; path = Pinning/TSKSPKIHashCache.h; sourceTree = ""; }; FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKSPKIHashCache.m; path = Pinning/TSKSPKIHashCache.m; sourceTree = ""; }; FC1A09121E57C6820055B12C /* TSKPublicKeyAlgorithm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TSKPublicKeyAlgorithm.h; path = Pinning/TSKPublicKeyAlgorithm.h; sourceTree = ""; }; + FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKReportsRateLimiterTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -561,13 +563,13 @@ 8C8480561A896EE30017C155 /* TrustKitTests */ = { isa = PBXGroup; children = ( + FC4CAC791E958DC600DAC41E /* Reporting */, 8C8480571A896EE30017C155 /* Supporting Files */, 8C5AB4631CF26A0300234B30 /* Dependencies */, 070868B31ADFF68200E5AFDC /* Certificates */, 8CD5F7371BCB02A7005801D8 /* TSKNSURLConnectionTests.m */, 8CD5F7561BCB7219005801D8 /* TSKNSURLSessionTests.m */, 2FA2868CAFECA46ADE0B6E3E /* TSKPinningValidatorTests.m */, - 6B032D3F1AF1AEB600EAFA69 /* TSKReporterTests.m */, 8C15F9A31B17564400F06C0E /* TSKPinConfigurationTests.m */, 8CC78B1E1B1B586F00523A25 /* TSKCertificateUtils.h */, 8CC78B1F1B1B586F00523A25 /* TSKCertificateUtils.m */, @@ -722,6 +724,15 @@ name = Dependencies; sourceTree = ""; }; + FC4CAC791E958DC600DAC41E /* Reporting */ = { + isa = PBXGroup; + children = ( + 6B032D3F1AF1AEB600EAFA69 /* TSKReporterTests.m */, + FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */, + ); + name = Reporting; + sourceTree = ""; + }; FC663F551E8421BE008E2EFE /* Configuration */ = { isa = PBXGroup; children = ( @@ -1163,6 +1174,7 @@ 8CC78B251B1B616500523A25 /* TSKPublicKeyAlgorithmTests.m in Sources */, 6B032D401AF1AEC200EAFA69 /* TSKReporterTests.m in Sources */, 8CD5F7571BCB7219005801D8 /* TSKNSURLSessionTests.m in Sources */, + FC4CAC7B1E958E0500DAC41E /* TSKReportsRateLimiterTests.m in Sources */, 8CC78B201B1B586F00523A25 /* TSKCertificateUtils.m in Sources */, FC06FE821E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.m in Sources */, ); diff --git a/TrustKit/Reporting/TSKBackgroundReporter.m b/TrustKit/Reporting/TSKBackgroundReporter.m index c122932e..44888efa 100644 --- a/TrustKit/Reporting/TSKBackgroundReporter.m +++ b/TrustKit/Reporting/TSKBackgroundReporter.m @@ -26,12 +26,13 @@ @interface TSKBackgroundReporter() -@property (nonatomic, strong, nonnull) NSString *appBundleId; -@property (nonatomic, strong, nonnull) NSString *appVersion; -@property (nonatomic, strong, nonnull) NSString *appVendorId; -@property (nonatomic, strong, nonnull) NSString *appPlatform; -@property (nonatomic, strong, nonnull) NSString *appPlatformVersion; -@property BOOL shouldRateLimitReports; +@property (nonatomic, nonnull) NSString *appBundleId; +@property (nonatomic, nonnull) NSString *appVersion; +@property (nonatomic, nonnull) NSString *appVendorId; +@property (nonatomic, nonnull) NSString *appPlatform; +@property (nonatomic, nonnull) NSString *appPlatformVersion; +@property (nonatomic, nonnull) TSKReportsRateLimiter *rateLimiter; +@property (nonatomic) BOOL shouldRateLimitReports; @end @@ -46,6 +47,7 @@ - (nonnull instancetype)initAndRateLimitReports:(BOOL)shouldRateLimitReports; if (self) { _shouldRateLimitReports = shouldRateLimitReports; + _rateLimiter = [TSKReportsRateLimiter new]; // Retrieve the App and device's information #if TARGET_OS_IPHONE @@ -217,7 +219,7 @@ - (void) pinValidationFailedForHostname:(nonnull NSString *) serverHostname expirationDate:knownPinsExpirationDate]; // Should we rate-limit this report? - if (_shouldRateLimitReports && [TSKReportsRateLimiter shouldRateLimitReport:report]) + if (_shouldRateLimitReports && [self.rateLimiter shouldRateLimitReport:report]) { // We recently sent the exact same report; do not send this report TSKLog(@"Pin failure report for %@ was not sent due to rate-limiting", serverHostname); diff --git a/TrustKit/Reporting/TSKReportsRateLimiter.h b/TrustKit/Reporting/TSKReportsRateLimiter.h index 697dbf10..c9010841 100644 --- a/TrustKit/Reporting/TSKReportsRateLimiter.h +++ b/TrustKit/Reporting/TSKReportsRateLimiter.h @@ -21,13 +21,6 @@ */ @interface TSKReportsRateLimiter : NSObject -+ (BOOL) shouldRateLimitReport:(TSKPinFailureReport *)report; +- (BOOL)shouldRateLimitReport:(TSKPinFailureReport * _Nonnull)report; @end - - - -@interface TSKReportsRateLimiter(Private) -// Helper method for running tests -+ (void) setLastReportsCacheResetDate:(NSDate *)date; -@end diff --git a/TrustKit/Reporting/TSKReportsRateLimiter.m b/TrustKit/Reporting/TSKReportsRateLimiter.m index 6ae2f3eb..942fd2a4 100644 --- a/TrustKit/Reporting/TSKReportsRateLimiter.m +++ b/TrustKit/Reporting/TSKReportsRateLimiter.m @@ -10,83 +10,79 @@ */ #import "TSKReportsRateLimiter.h" -#include #import "reporting_utils.h" +static const NSTimeInterval kIntervalBetweenReportsCacheReset = 3600 * 24; -// Variables to rate-limit the number of pin failure reports that get sent -static dispatch_once_t _dispatchOnceInit; -static NSMutableSet *_reportsCache = nil; -static pthread_mutex_t _reportsCacheLock; -// We reset the reports cache every 24 hours to ensure identical reports are only sent once per day -#define INTERVAL_BETWEEN_REPORTS_CACHE_RESET 3600*24 -static NSDate *_lastReportsCacheResetDate = nil; +@interface TSKReportsRateLimiter () +/** Cache to rate-limit the number of pin failure reports that get sent */ +@property (nonatomic) NSMutableSet *reportsCache; +/** We reset the reports cache every 24 hours to ensure identical reports are only sent once per day */ +@property (nonatomic) NSDate *lastReportsCacheResetDate; +/** Concurrent queue for multi-reader, single-writer to the reports cache using dispatch barriers */ +@property (nonatomic) dispatch_queue_t reportsCacheQueue; + +@end @implementation TSKReportsRateLimiter -+ (BOOL) shouldRateLimitReport:(TSKPinFailureReport *)report +- (instancetype)init { - // Initialize all the internal state for rate-limiting report uploads - dispatch_once(&_dispatchOnceInit, ^ - { - // Initialize state for rate-limiting - pthread_mutex_init(&_reportsCacheLock, NULL); - _lastReportsCacheResetDate = [NSDate date]; - _reportsCache = [NSMutableSet set]; - }); - - - // Check if we need to clear the reports cache for rate-limiting - NSDate *currentDate = [NSDate date]; - NSTimeInterval secondsSinceCacheReset = [currentDate timeIntervalSinceDate:_lastReportsCacheResetDate]; - if (secondsSinceCacheReset > INTERVAL_BETWEEN_REPORTS_CACHE_RESET) - { - // Reset the cache - pthread_mutex_lock(&_reportsCacheLock); - { - [_reportsCache removeAllObjects]; - _lastReportsCacheResetDate = currentDate; - } - pthread_mutex_unlock(&_reportsCacheLock); + self = [super init]; + if (self) { + // Initialize all the internal state for rate-limiting report uploads + _reportsCache = [NSMutableSet set]; + _lastReportsCacheResetDate = [NSDate date]; + _reportsCacheQueue = dispatch_queue_create("TSKReportsRateLimiter", DISPATCH_QUEUE_SERIAL); } + return self; +} + +- (BOOL)shouldRateLimitReport:(TSKPinFailureReport *)report +{ + NSParameterAssert(report); + // Check if we need to clear the reports cache for rate-limiting + NSTimeInterval secondsSinceCacheReset = -[self.lastReportsCacheResetDate timeIntervalSinceNow]; // Create an array containg the gist of the pin failure report; do not include the dates - NSArray *pinFailureInfo = @[report.notedHostname, report.hostname, report.port, report.validatedCertificateChain, report.knownPins, [NSNumber numberWithInt:report.validationResult]]; - + NSArray *pinFailureInfo = @[ report.notedHostname, + report.hostname, + report.port, + report.validatedCertificateChain, + report.knownPins, + @(report.validationResult) ]; - // Check if the exact same report has already been sent recently - BOOL shouldRateLimitReport = NO; - pthread_mutex_lock(&_reportsCacheLock); - { - shouldRateLimitReport = [_reportsCache containsObject:pinFailureInfo]; - } - pthread_mutex_unlock(&_reportsCacheLock); - - if (shouldRateLimitReport == NO) - { - // An identical report has NOT been sent recently - // Add this report to the cache for rate-limiting - pthread_mutex_lock(&_reportsCacheLock); + __block BOOL shouldRateLimitReport = NO; + __weak typeof(self) weakSelf = self; + dispatch_sync(self.reportsCacheQueue, ^{ + if (secondsSinceCacheReset > kIntervalBetweenReportsCacheReset) { - [_reportsCache addObject:pinFailureInfo]; + // Reset the cache + [weakSelf.reportsCache removeAllObjects]; + weakSelf.lastReportsCacheResetDate = [NSDate date]; } - pthread_mutex_unlock(&_reportsCacheLock); - } + + // Check if the exact same report has already been sent recently + shouldRateLimitReport = [weakSelf.reportsCache containsObject:pinFailureInfo]; + if (shouldRateLimitReport == NO) + { + // An identical report has NOT been sent recently + // Add this report to the cache for rate-limiting + [weakSelf.reportsCache addObject:pinFailureInfo]; + } + }); + return shouldRateLimitReport; } - -+ (void) setLastReportsCacheResetDate:(NSDate *)date +- (void)setLastReportsCacheResetDate:(NSDate *)lastReportsCacheResetDate { - pthread_mutex_lock(&_reportsCacheLock); - { - _lastReportsCacheResetDate = date; - } - pthread_mutex_unlock(&_reportsCacheLock); + NSParameterAssert(lastReportsCacheResetDate); + _lastReportsCacheResetDate = lastReportsCacheResetDate; } @end diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index 957920fd..d783b66a 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -17,13 +17,11 @@ #import "../TrustKit/Reporting/TSKBackgroundReporter.h" #import "../TrustKit/Reporting/TSKPinFailureReport.h" #import "../TrustKit/Reporting/reporting_utils.h" -#import "../TrustKit/Reporting/TSKReportsRateLimiter.h" #import #import "../TrustKit/Reporting/vendor_identifier.h" #import "TSKCertificateUtils.h" - #pragma mark Test suite @interface TSKReporterTests : XCTestCase @@ -33,6 +31,7 @@ @interface TSKReporterTests : XCTestCase @implementation TSKReporterTests { TrustKit *_trustKit; + //TSKPinFailureReport *_testReporter; SecTrustRef _testTrust; SecCertificateRef _rootCertificate; SecCertificateRef _intermediateCertificate; @@ -65,6 +64,7 @@ - (void)tearDown CFRelease(_leafCertificate); CFRelease(_testTrust); _trustKit = nil; + //_testReporter = nil; [super tearDown]; } @@ -220,108 +220,6 @@ - (void)testReporterNilExpirationDate [NSThread sleepForTimeInterval:0.1]; } - -- (void)testReportsRateLimiter -{ - // Create the pin validation failure report - NSArray *certificateChain = convertTrustToPemArray(_testTrust); - NSArray *formattedPins = convertPinsToHpkpPins([NSSet setWithArray:@[[[NSData alloc]initWithBase64EncodedString:@"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=" - options:(NSDataBase64DecodingOptions)0], - [[NSData alloc]initWithBase64EncodedString:@"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" - options:(NSDataBase64DecodingOptions)0]] - ]); - - - TSKPinFailureReport *report = [[TSKPinFailureReport alloc] initWithAppBundleId:@"test" - appVersion:@"1.2.3" - appPlatform:@"IOS" - appPlatformVersion:@"9.0.0" - appVendorId:@"test" - trustkitVersion:@"4.3.2.1" - hostname:@"mail.example.com" - port:[NSNumber numberWithInt:443] - dateTime:[NSDate date] - notedHostname:@"example.com" - includeSubdomains:NO - enforcePinning:NO - validatedCertificateChain:certificateChain - knownPins:formattedPins - validationResult:TSKPinValidationResultFailedCertificateChainNotTrusted - expirationDate:[NSDate date]]; - - // Ensure the same report will not be sent twice in a row - XCTAssert([TSKReportsRateLimiter shouldRateLimitReport:report] == NO, @"Wrongly rate-limited a new report"); - XCTAssert([TSKReportsRateLimiter shouldRateLimitReport:report] == YES, @"Did not rate-limit an identical report"); - - // Set the last time the cache was reset to more than 24 hours ago and ensure the report is sent again - [TSKReportsRateLimiter setLastReportsCacheResetDate:[[NSDate date] dateByAddingTimeInterval:-3700*24]]; - XCTAssert([TSKReportsRateLimiter shouldRateLimitReport:report] == NO, @"Reports cache was not properly reset after 24 hours"); - - - // Ensure the same report with a different validation result will be sent - report = [[TSKPinFailureReport alloc] initWithAppBundleId:@"test" - appVersion:@"1.2.3" - appPlatform:@"IOS" - appPlatformVersion:@"9.0.0" - appVendorId:@"test" - trustkitVersion:@"4.3.2.1" - hostname:@"mail.example.com" - port:[NSNumber numberWithInt:443] - dateTime:[NSDate date] - notedHostname:@"example.com" - includeSubdomains:NO - enforcePinning:NO - validatedCertificateChain:certificateChain - knownPins:formattedPins - validationResult:TSKPinValidationResultFailed - expirationDate:[NSDate date]]; - XCTAssert([TSKReportsRateLimiter shouldRateLimitReport:report] == NO, @"Wrongly rate-limited a new report"); - XCTAssert([TSKReportsRateLimiter shouldRateLimitReport:report] == YES, @"Did not rate-limit an identical report"); - - - // Ensure the same report with a different hostname will be sent - report = [[TSKPinFailureReport alloc] initWithAppBundleId:@"test" - appVersion:@"1.2.3" - appPlatform:@"IOS" - appPlatformVersion:@"9.0.0" - appVendorId:@"test" - trustkitVersion:@"4.3.2.1" - hostname:@"other.example.com" - port:[NSNumber numberWithInt:443] - dateTime:[NSDate date] - notedHostname:@"example.com" - includeSubdomains:NO - enforcePinning:NO - validatedCertificateChain:certificateChain - knownPins:formattedPins - validationResult:TSKPinValidationResultFailedCertificateChainNotTrusted - expirationDate:[NSDate date]]; - XCTAssert([TSKReportsRateLimiter shouldRateLimitReport:report] == NO, @"Wrongly rate-limited a new report"); - XCTAssert([TSKReportsRateLimiter shouldRateLimitReport:report] == YES, @"Did not rate-limit an identical report"); - - - // Ensure the same report with a different certificate chain will be sent - report = [[TSKPinFailureReport alloc] initWithAppBundleId:@"test" - appVersion:@"1.2.3" - appPlatform:@"IOS" - appPlatformVersion:@"9.0.0" - appVendorId:@"test" - trustkitVersion:@"4.3.2.1" - hostname:@"mail.example.com" - port:[NSNumber numberWithInt:443] - dateTime:[NSDate date] - notedHostname:@"example.com" - includeSubdomains:NO - enforcePinning:NO - validatedCertificateChain:[certificateChain subarrayWithRange:NSMakeRange(1, 2)] - knownPins:formattedPins - validationResult:TSKPinValidationResultFailedCertificateChainNotTrusted - expirationDate:[NSDate date]]; - XCTAssert([TSKReportsRateLimiter shouldRateLimitReport:report] == NO, @"Wrongly rate-limited a new report"); - XCTAssert([TSKReportsRateLimiter shouldRateLimitReport:report] == YES, @"Did not rate-limit an identical report"); -} - - - (void)testIdentifierForVendor { NSString *idfv = identifier_for_vendor(); diff --git a/TrustKitTests/TSKReportsRateLimiterTests.m b/TrustKitTests/TSKReportsRateLimiterTests.m new file mode 100644 index 00000000..a06b1a39 --- /dev/null +++ b/TrustKitTests/TSKReportsRateLimiterTests.m @@ -0,0 +1,186 @@ +// +// TSKReportsRateLimiterTests.m +// TrustKit +// +// Created by Adam Kaplan on 4/5/17. +// Copyright © 2017 TrustKit. All rights reserved. +// + +#import + +#import "../TrustKit/Reporting/TSKPinFailureReport.h" +#import "../TrustKit/Reporting/TSKReportsRateLimiter.m" +#import "TSKCertificateUtils.h" + +#import + +@interface TSKReportsRateLimiter (ExposeTests) +@property (nonatomic) NSDate *lastReportsCacheResetDate; +@end + +@interface TSKReportsRateLimiterTests : XCTestCase +@property (nonatomic, readonly) TSKReportsRateLimiter *rateLimiter; +@property (nonatomic, readonly) SecTrustRef testTrust; +@property (nonatomic, readonly) NSArray *testCertificateChain; +@property (nonatomic, readonly) TSKPinFailureReport *testReport; +@end + +@implementation TSKReportsRateLimiterTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. + _rateLimiter = [TSKReportsRateLimiter new]; + + SecCertificateRef rootCertificate = [TSKCertificateUtils createCertificateFromDer:@"GoodRootCA"]; + SecCertificateRef intermediateCertificate = [TSKCertificateUtils createCertificateFromDer:@"GoodIntermediateCA"]; + SecCertificateRef leafCertificate = [TSKCertificateUtils createCertificateFromDer:@"www.good.com"]; + + SecCertificateRef certChainArray[2] = { leafCertificate, intermediateCertificate }; + SecCertificateRef trustStoreArray[1] = { rootCertificate }; + + _testTrust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) + anchorCertificates:(const void **)trustStoreArray + arrayLength:sizeof(trustStoreArray)/sizeof(trustStoreArray[0])]; + _testCertificateChain = convertTrustToPemArray(self.testTrust); + + // Create the pin validation failure report + NSArray *certificateChain = convertTrustToPemArray(self.testTrust); + NSArray *formattedPins = convertPinsToHpkpPins([NSSet setWithArray:@[[[NSData alloc]initWithBase64EncodedString:@"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=" + options:(NSDataBase64DecodingOptions)0], + [[NSData alloc]initWithBase64EncodedString:@"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" + options:(NSDataBase64DecodingOptions)0]] + ]); + + _testReport = [[TSKPinFailureReport alloc] initWithAppBundleId:@"test" + appVersion:@"1.2.3" + appPlatform:@"IOS" + appPlatformVersion:@"9.0.0" + appVendorId:@"test" + trustkitVersion:@"4.3.2.1" + hostname:@"mail.example.com" + port:@443 + dateTime:[NSDate dateWithTimeIntervalSinceReferenceDate:0] + notedHostname:@"example.com" + includeSubdomains:NO + enforcePinning:NO + validatedCertificateChain:certificateChain + knownPins:formattedPins + validationResult:TSKPinValidationResultFailedCertificateChainNotTrusted + expirationDate:[NSDate dateWithTimeIntervalSinceReferenceDate:0]]; + +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +// Ensure a new report will be sent +- (void)test_noRateLimitOnFirstReport +{ + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:self.testReport], @"Wrongly rate-limited a new report"); +} + +// Ensure the same report will not be sent twice in a row +- (void)test_rateLimitDuplicateReport +{ + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:self.testReport], @"Wrongly rate-limited a new report"); + XCTAssertTrue([self.rateLimiter shouldRateLimitReport:self.testReport], @"Failed to rate-limit a repeated report"); + // And sanity check, why not? + XCTAssertTrue([self.rateLimiter shouldRateLimitReport:self.testReport], @"Failed to rate-limit a repeated report x2"); +} + +// Ensure the same report will not be sent twice in a row +- (void)test_rateLimitAllowDuplicateAfter24Hours +{ + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:self.testReport], @"Wrongly rate-limited a new report"); + XCTAssertTrue([self.rateLimiter shouldRateLimitReport:self.testReport], @"Failed to rate-limit a repeated report"); + + // Force update the cache timer to replicate a day passing, then re-run the tests + self.rateLimiter.lastReportsCacheResetDate = [NSDate dateWithTimeIntervalSinceNow:-3601 * 24]; + + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:self.testReport], @"Wrongly rate-limited a duplicate report after 24 hours"); + XCTAssertTrue([self.rateLimiter shouldRateLimitReport:self.testReport], @"Failed to rate-limit a repeated report"); +} + +// Hashing test: noted hostname +- (void)test_rateLimitHashing_notedHostname +{ + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:self.testReport], @"Wrongly rate-limited a new report"); + + TSKPinFailureReport *mockReport = OCMPartialMock(self.testReport); + OCMStub(mockReport.notedHostname).andReturn(@"flargle.blargle.com"); + + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:mockReport], @"Wrongly rate-limited a new report"); + + [(id)mockReport stopMocking]; +} + +// Hashing test: hostname +- (void)test_rateLimitHashing_hostname +{ + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:self.testReport], @"Wrongly rate-limited a new report"); + + TSKPinFailureReport *mockReport = OCMPartialMock(self.testReport); + OCMStub(mockReport.hostname).andReturn(@"flargle.blargle.com"); + + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:mockReport], @"Wrongly rate-limited a new report"); + + [(id)mockReport stopMocking]; +} + +// Hashing test: noted port +- (void)test_rateLimitHashing_port +{ + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:self.testReport], @"Wrongly rate-limited a new report"); + + TSKPinFailureReport *mockReport = OCMPartialMock(self.testReport); + OCMStub(mockReport.port).andReturn(@9090); + + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:mockReport], @"Wrongly rate-limited a new report"); + + [(id)mockReport stopMocking]; +} + +// Hashing test: certificate chain +- (void)test_rateLimitHashing_validatedCertificateChain +{ + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:self.testReport], @"Wrongly rate-limited a new report"); + + TSKPinFailureReport *mockReport = OCMPartialMock(self.testReport); + OCMStub(mockReport.validatedCertificateChain).andReturn(@[]); + + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:mockReport], @"Wrongly rate-limited a new report"); + + [(id)mockReport stopMocking]; +} + +// Hashing test: known pins +- (void)test_rateLimitHashing_knownPins +{ + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:self.testReport], @"Wrongly rate-limited a new report"); + + TSKPinFailureReport *mockReport = OCMPartialMock(self.testReport); + OCMStub(mockReport.knownPins).andReturn(@[]); + + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:mockReport], @"Wrongly rate-limited a new report"); + + [(id)mockReport stopMocking]; +} + +// Hashing test: validation result +- (void)test_rateLimitHashing_validationResult +{ + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:self.testReport], @"Wrongly rate-limited a new report"); + + TSKPinFailureReport *mockReport = OCMPartialMock(self.testReport); + OCMStub(mockReport.validationResult).andReturn(TSKPinValidationResultErrorCouldNotGenerateSpkiHash); + + XCTAssertFalse([self.rateLimiter shouldRateLimitReport:mockReport], @"Wrongly rate-limited a new report"); + + [(id)mockReport stopMocking]; +} + +@end From 7d29aa7a72b43e040c0c4165ecf076a40e497860 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 6 Apr 2017 10:28:00 -0400 Subject: [PATCH 008/126] Improved test expressiveness by switching out the last remaining tests that used comparison methods with regular XCTAssert, for the more suitable XCT test method --- TrustKit.xcodeproj/project.pbxproj | 8 +----- TrustKitTests/TSKPinConfigurationTests.m | 31 ++++++++++------------ TrustKitTests/TSKPublicKeyAlgorithmTests.m | 10 +++---- TrustKitTests/TSKReporterTests.m | 4 +-- TrustKitTests/TSKTestURLSessionDelegate.h | 13 --------- TrustKitTests/TSKTestURLSessionDelegate.m | 13 --------- 6 files changed, 22 insertions(+), 57 deletions(-) delete mode 100644 TrustKitTests/TSKTestURLSessionDelegate.h delete mode 100644 TrustKitTests/TSKTestURLSessionDelegate.m diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index ab397826..c6ca3a3f 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -224,7 +224,6 @@ 8CE919221AEA077F002B29AE /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */; }; 8CE919251AEA07C5002B29AE /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */; }; 8CE9192D1AEA0F7E002B29AE /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919291AEA0F7E002B29AE /* domain_registry.h */; }; - FC06FE821E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FC06FE811E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.m */; }; FC1A09001E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; FC1A09011E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; FC1A09021E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; @@ -382,8 +381,6 @@ 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ssl_pin_verifier.h; path = Pinning/ssl_pin_verifier.h; sourceTree = ""; }; 8CE919291AEA0F7E002B29AE /* domain_registry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = domain_registry.h; path = Dependencies/domain_registry/domain_registry.h; sourceTree = ""; }; FC06FE7F1E7C3FFA00AF0B4D /* TrustKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TrustKit-Prefix.pch"; sourceTree = ""; }; - FC06FE801E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKTestURLSessionDelegate.h; sourceTree = ""; }; - FC06FE811E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKTestURLSessionDelegate.m; sourceTree = ""; }; FC1A08FD1E579F630055B12C /* TSKPinValidatorResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinValidatorResult.h; sourceTree = ""; }; FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; @@ -563,9 +560,9 @@ 8C8480561A896EE30017C155 /* TrustKitTests */ = { isa = PBXGroup; children = ( - FC4CAC791E958DC600DAC41E /* Reporting */, 8C8480571A896EE30017C155 /* Supporting Files */, 8C5AB4631CF26A0300234B30 /* Dependencies */, + FC4CAC791E958DC600DAC41E /* Reporting */, 070868B31ADFF68200E5AFDC /* Certificates */, 8CD5F7371BCB02A7005801D8 /* TSKNSURLConnectionTests.m */, 8CD5F7561BCB7219005801D8 /* TSKNSURLSessionTests.m */, @@ -575,8 +572,6 @@ 8CC78B1F1B1B586F00523A25 /* TSKCertificateUtils.m */, 8CC78B241B1B616500523A25 /* TSKPublicKeyAlgorithmTests.m */, 8C80249C1D750D0100678959 /* TSKLoggerTests.m */, - FC06FE801E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.h */, - FC06FE811E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.m */, ); path = TrustKitTests; sourceTree = ""; @@ -1176,7 +1171,6 @@ 8CD5F7571BCB7219005801D8 /* TSKNSURLSessionTests.m in Sources */, FC4CAC7B1E958E0500DAC41E /* TSKReportsRateLimiterTests.m in Sources */, 8CC78B201B1B586F00523A25 /* TSKCertificateUtils.m in Sources */, - FC06FE821E7DF3B800AF0B4D /* TSKTestURLSessionDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/TrustKitTests/TSKPinConfigurationTests.m b/TrustKitTests/TSKPinConfigurationTests.m index 4c9e62de..06c31e31 100644 --- a/TrustKitTests/TSKPinConfigurationTests.m +++ b/TrustKitTests/TSKPinConfigurationTests.m @@ -12,15 +12,10 @@ #import #import "../TrustKit/TrustKit+Private.h" #import "../TrustKit/Pinning/ssl_pin_verifier.h" -//#import "../TrustKit/Pinning/TSKSPKIHashCache.h" #import "../TrustKit/parse_configuration.h" - @interface TSKPinConfigurationTests : XCTestCase -{ - -} @end @implementation TSKPinConfigurationTests @@ -69,7 +64,7 @@ - (void)testDisablePinningForSubdomainAndNoPublicKey }); NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"unsecured.good.com", trustKitConfig); - XCTAssert([serverConfigKey isEqualToString:@"unsecured.good.com"], @"Did not receive a configuration for pinned subdomain"); + XCTAssertEqualObjects(serverConfigKey, @"unsecured.good.com", @"Did not receive a configuration for pinned subdomain"); } @@ -138,7 +133,7 @@ - (void)testGetConfigurationPinningEnabled ]}}}); NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig); - XCTAssert([serverConfigKey isEqualToString:@"www.good.com"], @"Did not receive a configuration for a pinned domain"); + XCTAssertEqualObjects(serverConfigKey, @"www.good.com", @"Did not receive a configuration for a pinned domain"); // Validate the content of the config NSDictionary *serverConfig = trustKitConfig[kTSKPinnedDomains][serverConfigKey]; @@ -162,7 +157,7 @@ - (void)testGetConfigurationPinningEnabledWithExpirationDate ]}}}); NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig); - XCTAssert([serverConfigKey isEqualToString:@"www.good.com"], @"Did not receive a configuration for a pinned domain"); + XCTAssertEqualObjects(serverConfigKey, @"www.good.com", @"Did not receive a configuration for a pinned domain"); // Validate the content of the config NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; @@ -170,9 +165,9 @@ - (void)testGetConfigurationPinningEnabledWithExpirationDate NSDate *expirationDate = [dateFormat dateFromString:expirationDateStr]; NSDictionary *serverConfig = trustKitConfig[kTSKPinnedDomains][serverConfigKey]; - XCTAssert([expirationDate isEqualToDate:serverConfig[kTSKExpirationDate]]); + XCTAssertEqualObjects(expirationDate, serverConfig[kTSKExpirationDate]); XCTAssertEqual(serverConfig[kTSKPublicKeyAlgorithms][0], @(TSKPublicKeyAlgorithmEcDsaSecp384r1)); - XCTAssertEqual([serverConfig[kTSKPublicKeyHashes] count], (unsigned long) 2); + XCTAssertEqual([serverConfig[kTSKPublicKeyHashes] count], (unsigned long)2); } @@ -189,7 +184,7 @@ - (void)testGetConfigurationPinningDisabled // Ensure www.datatheorem.com gets no configuration NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.datatheorem.com", trustKitConfig); - XCTAssert(serverConfigKey == nil, @"Received a configuration a non-pinned domain"); + XCTAssertNil(serverConfigKey, @"Received a configuration a non-pinned domain"); } @@ -207,7 +202,7 @@ - (void)testIncludeSubdomainsEnabled // Ensure www.good.com gets the configuration set for good.com as includeSubdomains is enabled NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig); - XCTAssert([serverConfigKey isEqualToString:@"good.com"], @"IncludeSubdomains did not work"); + XCTAssertEqualObjects(serverConfigKey, @"good.com", @"IncludeSubdomains did not work"); } @@ -224,7 +219,7 @@ - (void)testIncludeSubdomainsEnabledSameDomain // Ensure good.com gets the configuration set for good.com as includeSubdomains is enabled NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"good.com", trustKitConfig); - XCTAssert([serverConfigKey isEqualToString:@"good.com"], @"IncludeSubdomains did not work"); + XCTAssertEqualObjects(serverConfigKey, @"good.com", @"IncludeSubdomains did not work"); } @@ -241,7 +236,7 @@ - (void)testIncludeSubdomainsEnabledSubSubDomain ]}}}); NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"sub.www.good.com.www.good.com", trustKitConfig); - XCTAssert([serverConfigKey isEqualToString:@"www.good.com"], @"IncludeSubdomains did not work"); + XCTAssertEqualObjects(serverConfigKey, @"www.good.com", @"IncludeSubdomains did not work"); } @@ -291,7 +286,7 @@ - (void)testIncludeSubdomainsDisabled // Ensure www.good.com does not get the configuration set for good.com NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig); - XCTAssert(serverConfigKey == nil, @"IncludeSubdomains did not work"); + XCTAssertNil(serverConfigKey, @"IncludeSubdomains did not work"); } @@ -314,7 +309,8 @@ - (void)testIncludeSubdomainsEnabledAndSpecificConfiguration // Ensure the configuration specific to www.good.com takes precedence over the more general config for good.com NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig); - XCTAssert([serverConfigKey isEqualToString:@"www.good.com"], @"IncludeSubdomains took precedence over a more specialized configuration"); + XCTAssertEqualObjects(serverConfigKey, @"www.good.com", + @"IncludeSubdomains took precedence over a more specialized configuration"); } @@ -338,7 +334,8 @@ - (void)testGlobalSettings ]}}}); // Ensure the kTSKSwizzleNetworkDelegates setting was saved - XCTAssert([trustKitConfig[kTSKSwizzleNetworkDelegates] boolValue] == NO, @"kTSKSwizzleNetworkDelegates was not saved in the configuration"); + XCTAssertFalse([trustKitConfig[kTSKSwizzleNetworkDelegates] boolValue], + @"kTSKSwizzleNetworkDelegates was not saved in the configuration"); } @end diff --git a/TrustKitTests/TSKPublicKeyAlgorithmTests.m b/TrustKitTests/TSKPublicKeyAlgorithmTests.m index bc882c7f..4aff1c5f 100644 --- a/TrustKitTests/TSKPublicKeyAlgorithmTests.m +++ b/TrustKitTests/TSKPublicKeyAlgorithmTests.m @@ -55,7 +55,7 @@ - (void)testExtractRsa2048 NSData *spkiHash = [spkiCache hashSubjectPublicKeyInfoFromCertificate:certificate publicKeyAlgorithm:TSKPublicKeyAlgorithmRsa2048]; NSString *spkiPin = [spkiHash base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; - XCTAssert([spkiPin isEqualToString:@"NDCIt6TrQnfOk+lquunrmlPQB3K/7CLOCmSS5kW+KCc="]); + XCTAssertEqualObjects(spkiPin, @"NDCIt6TrQnfOk+lquunrmlPQB3K/7CLOCmSS5kW+KCc="); CFRelease(certificate); } @@ -68,7 +68,7 @@ - (void)testExtractRsa4096 NSData *spkiHash = [spkiCache hashSubjectPublicKeyInfoFromCertificate:certificate publicKeyAlgorithm:TSKPublicKeyAlgorithmRsa4096]; NSString *spkiPin = [spkiHash base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; - XCTAssert([spkiPin isEqualToString:@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY="]); + XCTAssertEqualObjects(spkiPin, @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY="); CFRelease(certificate); } @@ -81,7 +81,7 @@ - (void)testExtractEcDsaSecp256r1 NSData *spkiHash = [spkiCache hashSubjectPublicKeyInfoFromCertificate:certificate publicKeyAlgorithm:TSKPublicKeyAlgorithmEcDsaSecp256r1]; NSString *spkiPin = [spkiHash base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; - XCTAssert([spkiPin isEqualToString:@"Gc7EN2acfkbE0dUOAd34tr1XLr+JdkTiTrMAfhESQHI="]); + XCTAssertEqualObjects(spkiPin, @"Gc7EN2acfkbE0dUOAd34tr1XLr+JdkTiTrMAfhESQHI="); CFRelease(certificate); } @@ -94,7 +94,7 @@ - (void)testExtractEcDsaSecp384r1 NSData *spkiHash = [spkiCache hashSubjectPublicKeyInfoFromCertificate:certificate publicKeyAlgorithm:TSKPublicKeyAlgorithmEcDsaSecp384r1]; NSString *spkiPin = [spkiHash base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; - XCTAssert([spkiPin isEqualToString:@"vPtEqrmtAhAVcGtBIep2HIHJ6IlnWQ9vlK50TciLePs="]); + XCTAssertEqualObjects(spkiPin, @"vPtEqrmtAhAVcGtBIep2HIHJ6IlnWQ9vlK50TciLePs="); CFRelease(certificate); } @@ -144,7 +144,7 @@ - (void)testVerifyMultipleAlgorithms CFRelease(intermediateCertificate); CFRelease(rootCertificate); - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins with multiple algorithms"); + XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins with multiple algorithms"); } diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index d783b66a..688d31a6 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -47,8 +47,8 @@ - (void)setUp { _intermediateCertificate = [TSKCertificateUtils createCertificateFromDer:@"GoodIntermediateCA"]; _leafCertificate = [TSKCertificateUtils createCertificateFromDer:@"www.good.com"]; - SecCertificateRef certChainArray[2] = {_leafCertificate, _intermediateCertificate}; - SecCertificateRef trustStoreArray[1] = {_rootCertificate}; + SecCertificateRef certChainArray[2] = { _leafCertificate, _intermediateCertificate }; + SecCertificateRef trustStoreArray[1] = { _rootCertificate }; _testTrust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) diff --git a/TrustKitTests/TSKTestURLSessionDelegate.h b/TrustKitTests/TSKTestURLSessionDelegate.h deleted file mode 100644 index 263f9ce5..00000000 --- a/TrustKitTests/TSKTestURLSessionDelegate.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// TSKTestURLSessionDelegate.h -// TrustKit -// -// Created by Adam Kaplan on 3/18/17. -// Copyright © 2017 TrustKit. All rights reserved. -// - -#import - -@interface TSKTestURLSessionDelegate : NSObject - -@end diff --git a/TrustKitTests/TSKTestURLSessionDelegate.m b/TrustKitTests/TSKTestURLSessionDelegate.m deleted file mode 100644 index 392deb54..00000000 --- a/TrustKitTests/TSKTestURLSessionDelegate.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// TSKTestURLSessionDelegate.m -// TrustKit -// -// Created by Adam Kaplan on 3/18/17. -// Copyright © 2017 TrustKit. All rights reserved. -// - -#import "TSKTestURLSessionDelegate.h" - -@implementation TSKTestURLSessionDelegate - -@end From d73d2e19c743963f102e744d55cac7dece5360e3 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 6 Apr 2017 12:58:27 -0400 Subject: [PATCH 009/126] Lots of small changes to fix annoying warnings/errors - Fixed the random but frequent error about "Redefinition of module TrustKit" because the tests were also configured to create the app's module map. Whoops! In the process of finding that, changed lots of other things: - Removed the TrustKit+Private.h header. There is no more private header for test support. Test support is entirely within the test target. - Moved the #defines out of the precompiled header and into TSKCommon.h. This didn't end up helping any issue but i think its cleaner anyway. - Added const to all statics - Removed the shared background session and greatly simplified the background session creation. --- TrustKit.xcodeproj/project.pbxproj | 26 +- .../xcshareddata/xcschemes/TrustKit.xcscheme | 3 +- TrustKit/Dependencies/RSSwizzle/RSSwizzle.h | 2 - TrustKit/Pinning/TSKSPKIHashCache.h | 1 - TrustKit/Pinning/TSKSPKIHashCache.m | 13 +- TrustKit/Pinning/ssl_pin_verifier.h | 2 - TrustKit/Pinning/ssl_pin_verifier.m | 1 - TrustKit/Reporting/TSKBackgroundReporter.h | 1 - TrustKit/Reporting/TSKBackgroundReporter.m | 99 +++---- TrustKit/Reporting/TSKPinFailureReport.h | 1 - TrustKit/Reporting/TSKReportsRateLimiter.h | 2 - TrustKit/Reporting/reporting_utils.m | 2 - TrustKit/Reporting/vendor_identifier.h | 3 - TrustKit/Reporting/vendor_identifier.m | 3 +- .../TSKNSURLConnectionDelegateProxy.h | 2 - .../TSKNSURLConnectionDelegateProxy.m | 2 +- .../Swizzling/TSKNSURLSessionDelegateProxy.h | 2 - .../Swizzling/TSKNSURLSessionDelegateProxy.m | 2 +- TrustKit/TSKCommon.h | 21 ++ TrustKit/TSKPinningValidator.h | 1 - TrustKit/TSKPinningValidator.m | 6 +- TrustKit/TSKPinningValidatorResult.h | 1 - TrustKit/TSKTrustKit-Umbrella.h | 15 ++ TrustKit/TSKTrustKitConfig.h | 246 ++++++++++++++++++ TrustKit/TrustKit+Private.h | 43 --- TrustKit/TrustKit-Prefix.pch | 2 + TrustKit/TrustKit.h | 235 +---------------- TrustKit/TrustKit.m | 2 +- TrustKit/TrustKit.modulemap | 6 + TrustKit/configuration_utils.h | 1 - TrustKit/configuration_utils.m | 2 +- TrustKit/module.modulemap | 6 - TrustKit/parse_configuration.m | 1 - TrustKitTests/TSKCertificateUtils.h | 2 +- TrustKitTests/TSKNSURLConnectionTests.m | 7 +- TrustKitTests/TSKNSURLSessionTests.m | 6 +- TrustKitTests/TSKPinConfigurationTests.m | 2 +- TrustKitTests/TSKPinningValidatorTests.m | 3 +- TrustKitTests/TSKPublicKeyAlgorithmTests.m | 2 +- TrustKitTests/TSKReporterTests.m | 8 +- 40 files changed, 392 insertions(+), 393 deletions(-) create mode 100644 TrustKit/TSKCommon.h create mode 100644 TrustKit/TSKTrustKit-Umbrella.h create mode 100644 TrustKit/TSKTrustKitConfig.h delete mode 100644 TrustKit/TrustKit+Private.h create mode 100644 TrustKit/TrustKit.modulemap delete mode 100644 TrustKit/module.modulemap diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index c6ca3a3f..31e49973 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -241,6 +241,8 @@ FC1A09101E57AC450055B12C /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */; }; FC1A09111E57AC450055B12C /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */; }; FC4CAC7B1E958E0500DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; + FC4CAC7C1E96891A00DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; + FC4CAC7D1E96891B00DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -310,12 +312,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 2FA286123F801C437F35D240 /* TrustKit+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TrustKit+Private.h"; sourceTree = ""; }; 2FA2868CAFECA46ADE0B6E3E /* TSKPinningValidatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorTests.m; sourceTree = ""; }; 6B032D3F1AF1AEB600EAFA69 /* TSKReporterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TSKReporterTests.m; sourceTree = ""; }; 6B2B06AC1B05154A00FC749E /* TSKBackgroundReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKBackgroundReporter.h; path = Reporting/TSKBackgroundReporter.h; sourceTree = ""; }; 6B2B06AE1B05157400FC749E /* TSKBackgroundReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKBackgroundReporter.m; path = Reporting/TSKBackgroundReporter.m; sourceTree = ""; }; - 8C0C90111E3C196A003851A8 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; + 8C0C90111E3C196A003851A8 /* TrustKit.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = TrustKit.modulemap; sourceTree = ""; }; 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidator.h; sourceTree = ""; }; 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidator.m; sourceTree = ""; }; 8C15F99E1B16094D00F06C0E /* TSKPinFailureReport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinFailureReport.h; path = Reporting/TSKPinFailureReport.h; sourceTree = ""; }; @@ -388,6 +389,9 @@ FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKSPKIHashCache.m; path = Pinning/TSKSPKIHashCache.m; sourceTree = ""; }; FC1A09121E57C6820055B12C /* TSKPublicKeyAlgorithm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TSKPublicKeyAlgorithm.h; path = Pinning/TSKPublicKeyAlgorithm.h; sourceTree = ""; }; FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKReportsRateLimiterTests.m; sourceTree = ""; }; + FC4CAC7E1E96917B00DAC41E /* TSKTrustKitConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustKitConfig.h; sourceTree = ""; }; + FC4CAC7F1E96A2D600DAC41E /* TSKCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKCommon.h; sourceTree = ""; }; + FC4CAC801E96A95D00DAC41E /* TSKTrustKit-Umbrella.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TSKTrustKit-Umbrella.h"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -529,7 +533,7 @@ 8C8480491A896EE30017C155 /* TrustKit */ = { isa = PBXGroup; children = ( - 8C0C90111E3C196A003851A8 /* module.modulemap */, + 8C0C90111E3C196A003851A8 /* TrustKit.modulemap */, 8CE919281AEA0F61002B29AE /* Dependencies */, 8CA6CC571BAF60DA00BDA419 /* Swizzling */, 8CE919201AEA0745002B29AE /* Reporting */, @@ -537,7 +541,8 @@ FC663F551E8421BE008E2EFE /* Configuration */, 8C84804C1A896EE30017C155 /* TrustKit.h */, 8C84806C1A896F660017C155 /* TrustKit.m */, - 2FA286123F801C437F35D240 /* TrustKit+Private.h */, + FC4CAC7F1E96A2D600DAC41E /* TSKCommon.h */, + FC4CAC7E1E96917B00DAC41E /* TSKTrustKitConfig.h */, 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */, 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */, FC1A08FD1E579F630055B12C /* TSKPinValidatorResult.h */, @@ -552,6 +557,7 @@ isa = PBXGroup; children = ( 8C84804B1A896EE30017C155 /* Info.plist */, + FC4CAC801E96A95D00DAC41E /* TSKTrustKit-Umbrella.h */, FC06FE7F1E7C3FFA00AF0B4D /* TrustKit-Prefix.pch */, ); name = "Supporting Files"; @@ -1206,6 +1212,7 @@ files = ( 8C80249F1D750D0100678959 /* TSKLoggerTests.m in Sources */, 8C84CBC31D6E1718009B3E7D /* TSKNSURLConnectionTests.m in Sources */, + FC4CAC7D1E96891B00DAC41E /* TSKReportsRateLimiterTests.m in Sources */, 8C84CBC41D6E1718009B3E7D /* TSKPinConfigurationTests.m in Sources */, 8C84CBC51D6E1718009B3E7D /* TSKPinningValidatorTests.m in Sources */, 8C84CBC61D6E1718009B3E7D /* TSKPublicKeyAlgorithmTests.m in Sources */, @@ -1271,6 +1278,7 @@ files = ( 8C80249E1D750D0100678959 /* TSKLoggerTests.m in Sources */, 8CD5F7391BCB02A7005801D8 /* TSKNSURLConnectionTests.m in Sources */, + FC4CAC7C1E96891A00DAC41E /* TSKReportsRateLimiterTests.m in Sources */, 8CA6CC3A1BAE2C7C00BDA419 /* TSKReporterTests.m in Sources */, 8CA6CC391BAE2C7200BDA419 /* TSKPinningValidatorTests.m in Sources */, 8CA6CC3C1BAE2C8100BDA419 /* TSKPublicKeyAlgorithmTests.m in Sources */, @@ -1381,7 +1389,7 @@ GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MODULEMAP_FILE = "$(SRCROOT)/TrustKit/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/TrustKit/TrustKit.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -1440,7 +1448,7 @@ GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MODULEMAP_FILE = "$(SRCROOT)/TrustKit/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/TrustKit/TrustKit.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; @@ -1531,6 +1539,7 @@ INFOPLIST_FILE = TrustKitTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @loader_path/../Frameworks @executable_path/../Frameworks"; + MODULEMAP_FILE = ""; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1556,6 +1565,7 @@ INFOPLIST_FILE = TrustKitTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @loader_path/../Frameworks @executable_path/../Frameworks"; + MODULEMAP_FILE = ""; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1645,6 +1655,7 @@ GCC_TREAT_WARNINGS_AS_ERRORS = YES; INFOPLIST_FILE = TrustKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @loader_path/../Frameworks @executable_path/../Frameworks"; + MODULEMAP_FILE = ""; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1671,6 +1682,7 @@ GCC_TREAT_WARNINGS_AS_ERRORS = YES; INFOPLIST_FILE = TrustKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @loader_path/../Frameworks @executable_path/../Frameworks"; + MODULEMAP_FILE = ""; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1795,6 +1807,7 @@ INFOPLIST_FILE = TrustKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.9; + MODULEMAP_FILE = ""; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.TrustKit-OS-XTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1819,6 +1832,7 @@ INFOPLIST_FILE = TrustKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.9; + MODULEMAP_FILE = ""; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.TrustKit-OS-XTests"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit.xcscheme b/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit.xcscheme index 380aae95..461288fc 100644 --- a/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit.xcscheme +++ b/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit.xcscheme @@ -40,7 +40,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/TrustKit/Dependencies/RSSwizzle/RSSwizzle.h b/TrustKit/Dependencies/RSSwizzle/RSSwizzle.h index 9b91ace3..2c85155e 100755 --- a/TrustKit/Dependencies/RSSwizzle/RSSwizzle.h +++ b/TrustKit/Dependencies/RSSwizzle/RSSwizzle.h @@ -6,8 +6,6 @@ // // -#import - #pragma mark - Macros Based API /// A macro for wrapping the return type of the swizzled method. diff --git a/TrustKit/Pinning/TSKSPKIHashCache.h b/TrustKit/Pinning/TSKSPKIHashCache.h index cb52dfcd..43434f6b 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.h +++ b/TrustKit/Pinning/TSKSPKIHashCache.h @@ -9,7 +9,6 @@ */ -#import #import "TSKPublicKeyAlgorithm.h" @import Security; diff --git a/TrustKit/Pinning/TSKSPKIHashCache.m b/TrustKit/Pinning/TSKSPKIHashCache.m index e9e98676..9154e321 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -10,7 +10,6 @@ */ #import "TSKSPKIHashCache.h" -#import "../TrustKit+Private.h" #import #if TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED <= 100000 @@ -35,31 +34,31 @@ #pragma mark Missing ASN1 SPKI Headers // These are the ASN1 headers for the Subject Public Key Info section of a certificate -static unsigned char rsa2048Asn1Header[] = { +static const unsigned char rsa2048Asn1Header[] = { 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00 }; -static unsigned char rsa4096Asn1Header[] = { +static const unsigned char rsa4096Asn1Header[] = { 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00 }; -static unsigned char ecDsaSecp256r1Asn1Header[] = { +static const unsigned char ecDsaSecp256r1Asn1Header[] = { 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00 }; -static unsigned char ecDsaSecp384r1Asn1Header[] = { +static const unsigned char ecDsaSecp384r1Asn1Header[] = { 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00 }; // Careful with the order... must match how TSKPublicKeyAlgorithm is defined -static unsigned char *asn1HeaderBytes[4] = { rsa2048Asn1Header, rsa4096Asn1Header, +static const unsigned char *asn1HeaderBytes[4] = { rsa2048Asn1Header, rsa4096Asn1Header, ecDsaSecp256r1Asn1Header, ecDsaSecp384r1Asn1Header }; -static unsigned int asn1HeaderSizes[4] = { sizeof(rsa2048Asn1Header), sizeof(rsa4096Asn1Header), +static const unsigned int asn1HeaderSizes[4] = { sizeof(rsa2048Asn1Header), sizeof(rsa4096Asn1Header), sizeof(ecDsaSecp256r1Asn1Header), sizeof(ecDsaSecp384r1Asn1Header) }; diff --git a/TrustKit/Pinning/ssl_pin_verifier.h b/TrustKit/Pinning/ssl_pin_verifier.h index 58960c8d..32c25b60 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.h +++ b/TrustKit/Pinning/ssl_pin_verifier.h @@ -9,8 +9,6 @@ */ - -#import #import "../TSKPinValidatorResult.h" @class TSKSPKIHashCache; diff --git a/TrustKit/Pinning/ssl_pin_verifier.m b/TrustKit/Pinning/ssl_pin_verifier.m index 7e45bb31..2435c321 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.m +++ b/TrustKit/Pinning/ssl_pin_verifier.m @@ -12,7 +12,6 @@ #import "ssl_pin_verifier.h" #import "TSKSPKIHashCache.h" #import "../Dependencies/domain_registry/domain_registry.h" -#import "../TrustKit+Private.h" #import "../configuration_utils.h" diff --git a/TrustKit/Reporting/TSKBackgroundReporter.h b/TrustKit/Reporting/TSKBackgroundReporter.h index aed823df..04b0ba42 100644 --- a/TrustKit/Reporting/TSKBackgroundReporter.h +++ b/TrustKit/Reporting/TSKBackgroundReporter.h @@ -9,7 +9,6 @@ */ -#import #import "../Pinning/ssl_pin_verifier.h" /** diff --git a/TrustKit/Reporting/TSKBackgroundReporter.m b/TrustKit/Reporting/TSKBackgroundReporter.m index 44888efa..ca4a351c 100644 --- a/TrustKit/Reporting/TSKBackgroundReporter.m +++ b/TrustKit/Reporting/TSKBackgroundReporter.m @@ -10,22 +10,19 @@ */ #import "TSKBackgroundReporter.h" -#import "../TrustKit+Private.h" +#import "../TSKTrustKitConfig.h" #import "TSKPinFailureReport.h" #import "reporting_utils.h" #import "TSKReportsRateLimiter.h" #import "vendor_identifier.h" -#import // Session identifier for background uploads: .TSKBackgroundReporter -static NSString *kTSKBackgroundSessionIdentifierFormat = @"%@.TSKBackgroundReporter"; -static NSURLSession *_backgroundSession = nil; -static dispatch_once_t dispatchOnceBackgroundSession; - +static NSString * const kTSKBackgroundSessionIdentifierFormat = @"%@.TSKBackgroundReporter.%@"; @interface TSKBackgroundReporter() +@property (nonatomic, nonnull) NSURLSession *backgroundSession; @property (nonatomic, nonnull) NSString *appBundleId; @property (nonatomic, nonnull) NSString *appVersion; @property (nonatomic, nonnull) NSString *appVendorId; @@ -103,72 +100,60 @@ - (nonnull instancetype)initAndRateLimitReports:(BOOL)shouldRateLimitReports; if (_appBundleId == nil) { // The bundle ID we get is nil if we're running tests on Travis. If the bundle ID is nil, background sessions can't be used - // backgroundSessionConfigurationWithIdentifier: will throw an exception within dispatch_once() which can't be handled - // Use a regular session instead + // backgroundSessionConfigurationWithIdentifier: will throw an exception TSKLog(@"Null bundle ID: we are running the test suite; falling back to a normal session."); _appBundleId = @"N/A"; _appVendorId = @"unit-tests"; - dispatch_once(&dispatchOnceBackgroundSession, ^{ - _backgroundSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] - delegate:self - delegateQueue:nil]; - }); + _backgroundSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] + delegate:self + delegateQueue:nil]; } else { // Get the vendor identifier _appVendorId = identifier_for_vendor(); - + // We're not running unit tests - use a background session - /* - Using dispatch_once here ensures that multiple background sessions with the same identifier are not created - in this instance of the application. If you want to support multiple background sessions within a single process, - you should create each session with its own identifier. - */ - dispatch_once(&dispatchOnceBackgroundSession, ^{ - NSURLSessionConfiguration *backgroundConfiguration = nil; - - // The API for creating background sessions changed between iOS 7 and iOS 8 and OS X 10.9 and 10.10 -#if (TARGET_OS_IPHONE &&__IPHONE_OS_VERSION_MAX_ALLOWED < 80000) || (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MAX_ALLOWED < 1100) - // iOS 7 or OS X 10.9 as the max SDK: awlays use the deprecated/iOS 7 API - backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:[NSString stringWithFormat:kTSKBackgroundSessionIdentifierFormat, _appBundleId]]; -#else - // iOS 8+ or OS X 10.10+ as the max SDK -#if (TARGET_OS_IPHONE &&__IPHONE_OS_VERSION_MIN_REQUIRED < 80000) || (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED < 1100) - // iOS 7 or OS X 10.9 as the min SDK - // Try to use the new API if available at runtime - if (![NSURLSessionConfiguration respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]) - { - // Device runs on iOS 7 or OS X 10.9 - backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:[NSString stringWithFormat:kTSKBackgroundSessionIdentifierFormat, _appBundleId]]; - } - else -#endif - { - // Device runs on iOS 8+ or OS X 10.10+ or min SDK is iOS 8+ or OS X 10.10+ - backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: [NSString stringWithFormat:kTSKBackgroundSessionIdentifierFormat, _appBundleId]]; - } -#endif - - - + // AppleDoc (currently 10.3) state that multiple background sessions with the same + // identifier should never be created, so ensure that cannot happen by creating + // a unique ID per instance. + NSString *backgroundSessionId = [NSString stringWithFormat:kTSKBackgroundSessionIdentifierFormat, + _appBundleId, @""]; + + // The API for creating background sessions changed between iOS 7 and iOS 8 and OS X 10.9 and 10.10 + // Try to use the new API if available at runtime + NSURLSessionConfiguration *backgroundConfiguration; + if ([NSURLSessionConfiguration respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]) + { + // Device runs on iOS 8+ or OS X 10.10+ or min SDK is iOS 8+ or OS X 10.10+ + backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:backgroundSessionId]; + } + else + { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // Device runs on iOS 7 or OS X 10.9 + backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:backgroundSessionId]; +#pragma clang diagnostic pop + } + + #if TARGET_OS_IPHONE - // iOS-only settings - // Do not wake up the App after completing the upload - backgroundConfiguration.sessionSendsLaunchEvents = NO; + // iOS-only settings + // Do not wake up the App after completing the upload + backgroundConfiguration.sessionSendsLaunchEvents = NO; #endif - + #if (TARGET_OS_IPHONE) || ((!TARGET_OS_IPHONE) && (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1100)) - // On OS X discretionary is only available on 10.10 - backgroundConfiguration.discretionary = YES; + // On OS X discretionary is only available on 10.10 + backgroundConfiguration.discretionary = YES; #endif - // We have to use a delegate as background sessions can't use completion handlers - _backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfiguration - delegate:self - delegateQueue:nil]; - }); + // We have to use a delegate as background sessions can't use completion handlers + _backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfiguration + delegate:self + delegateQueue:nil]; } } return self; diff --git a/TrustKit/Reporting/TSKPinFailureReport.h b/TrustKit/Reporting/TSKPinFailureReport.h index 0469c152..f687c09f 100644 --- a/TrustKit/Reporting/TSKPinFailureReport.h +++ b/TrustKit/Reporting/TSKPinFailureReport.h @@ -9,7 +9,6 @@ */ -#import #import "../Pinning/ssl_pin_verifier.h" diff --git a/TrustKit/Reporting/TSKReportsRateLimiter.h b/TrustKit/Reporting/TSKReportsRateLimiter.h index c9010841..f446c335 100644 --- a/TrustKit/Reporting/TSKReportsRateLimiter.h +++ b/TrustKit/Reporting/TSKReportsRateLimiter.h @@ -10,8 +10,6 @@ */ #import "TSKPinFailureReport.h" -#import - /* * Simple helper class which caches reports for 24 hours to prevent identical reports from being sent twice diff --git a/TrustKit/Reporting/reporting_utils.m b/TrustKit/Reporting/reporting_utils.m index ab880bc2..9199127d 100644 --- a/TrustKit/Reporting/reporting_utils.m +++ b/TrustKit/Reporting/reporting_utils.m @@ -9,8 +9,6 @@ */ -#import - #import "reporting_utils.h" diff --git a/TrustKit/Reporting/vendor_identifier.h b/TrustKit/Reporting/vendor_identifier.h index fe8ad295..808a403c 100644 --- a/TrustKit/Reporting/vendor_identifier.h +++ b/TrustKit/Reporting/vendor_identifier.h @@ -6,8 +6,5 @@ // Copyright © 2016 TrustKit. All rights reserved. // -#import - - // Will return the IDFV on platforms that support it (iOS, tvOS) and a randomly generated UUID on other platforms (macOS, watchOS) NSString *identifier_for_vendor(void); diff --git a/TrustKit/Reporting/vendor_identifier.m b/TrustKit/Reporting/vendor_identifier.m index c4c4e843..695a8b2c 100644 --- a/TrustKit/Reporting/vendor_identifier.m +++ b/TrustKit/Reporting/vendor_identifier.m @@ -8,12 +8,11 @@ #import "vendor_identifier.h" - #if TARGET_OS_IPHONE && !TARGET_OS_WATCH #pragma mark Vendor identifier - macOS, tvOS -@import UIKit; // For accessing the IDFV +@import UIKit; // for accessing IDFV NSString *identifier_for_vendor(void) { diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h index a102e13e..0f6da000 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h @@ -6,8 +6,6 @@ // Copyright © 2015 TrustKit. All rights reserved. // -#import - NS_ASSUME_NONNULL_BEGIN @interface TSKNSURLConnectionDelegateProxy : NSObject diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m index e7adba86..608c7f8a 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m @@ -7,7 +7,7 @@ // #import "TSKNSURLConnectionDelegateProxy.h" -#import "../TrustKit+Private.h" +#import "../TrustKit.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" typedef void (^AsyncCompletionHandler)(NSURLResponse *response, NSData *data, NSError *connectionError); diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h index c7a58cd0..6c7da849 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h @@ -6,8 +6,6 @@ // Copyright © 2015 TrustKit. All rights reserved. // -#import - NS_ASSUME_NONNULL_BEGIN typedef void(^TSKURLSessionAuthChallengeCallback)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential); diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m index f0925767..21e223b4 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m @@ -8,7 +8,7 @@ #import "TSKNSURLSessionDelegateProxy.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" -#import "../TrustKit+Private.h" +#import "../TrustKit.h" @interface TSKNSURLSessionDelegateProxy () /* The NSURLSessionDelegate we're going to proxy */ diff --git a/TrustKit/TSKCommon.h b/TrustKit/TSKCommon.h new file mode 100644 index 00000000..3f6cb6b5 --- /dev/null +++ b/TrustKit/TSKCommon.h @@ -0,0 +1,21 @@ +// +// TSKCommon.h +// TrustKit +// +// Created by Adam Kaplan on 4/6/17. +// Copyright © 2017 TrustKit. All rights reserved. +// + +// Common header with internal constants and defines. + +#ifndef TSKCommon_h +#define TSKCommon_h + +// The logging function we use within TrustKit +#ifdef DEBUG +#define TSKLog(format, ...) NSLog(@"=== TrustKit: " format, ##__VA_ARGS__); +#else +#define TSKLog(format, ...) +#endif + +#endif /* TSKCommon_h */ diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index d546c6a8..75bdd70b 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -9,7 +9,6 @@ */ -#import #import "TSKPinValidatorResult.h" #import "Pinning/TSKPublicKeyAlgorithm.h" diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index 2352e210..beeb068d 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -9,11 +9,13 @@ */ -#import "TrustKit+Private.h" +#import "TSKPinningValidator.h" +#import "TSKTrustKitConfig.h" #import "TSKPinValidatorResult.h" #import "TSKPinningValidatorResult.h" #import "Pinning/TSKSPKIHashCache.h" - +#import "Pinning/ssl_pin_verifier.h" +#import "TrustKit.h" @interface TSKPinningValidator () @property (nonatomic) TSKSPKIHashCache *spkiHashCache; diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h index 435e7af8..6c98ac77 100644 --- a/TrustKit/TSKPinningValidatorResult.h +++ b/TrustKit/TSKPinningValidatorResult.h @@ -9,7 +9,6 @@ */ -#import #import "TSKPinValidatorResult.h" @interface TSKPinningValidatorResult : NSObject diff --git a/TrustKit/TSKTrustKit-Umbrella.h b/TrustKit/TSKTrustKit-Umbrella.h new file mode 100644 index 00000000..3a2fca41 --- /dev/null +++ b/TrustKit/TSKTrustKit-Umbrella.h @@ -0,0 +1,15 @@ +// +// TSKTrustKit-Umbrella.h +// TrustKit +// +// Created by Adam Kaplan on 4/6/17. +// Copyright © 2017 TrustKit. All rights reserved. +// + +#ifndef TSKTrustKit_Umbrella_h +#define TSKTrustKit_Umbrella_h + +#import +#import "TSKCommon.h" + +#endif /* TSKTrustKit_Umbrella_h */ diff --git a/TrustKit/TSKTrustKitConfig.h b/TrustKit/TSKTrustKitConfig.h new file mode 100644 index 00000000..fd3cefa0 --- /dev/null +++ b/TrustKit/TSKTrustKitConfig.h @@ -0,0 +1,246 @@ +// +// TSKTrustKitConfig.h +// TrustKit +// +// Created by Adam Kaplan on 4/6/17. +// Copyright © 2017 TrustKit. All rights reserved. +// + +// These externs are currently fulfilled in TrustKit.m. They are here to prevent reverse +// includes since both TrustKit instances and low level internal C API need the definitions + +#ifndef TSKTrustKitConfig_h +#define TSKTrustKitConfig_h + +#pragma mark TrustKit Version Number + +/** + The version of TrustKit, such as "1.4.0". + */ +FOUNDATION_EXPORT NSString * const TrustKitVersion; + + +#pragma mark Configuration Keys + + +/** + A global, App-wide configuration key that can be set in the pinning policy. + */ +typedef NSString *TSKGlobalConfigurationKey; + + +/** + A domain-specific configuration key (to defined for a domain under the `kTSKPinnedDomains` + key) that can be set in the pinning policy. + */ +typedef NSString *TSKDomainConfigurationKey; + + +#pragma mark Global Configuration Keys - Required + + +/** + A boolean. If set to `YES`, TrustKit will perform method swizzling on the App's + `NSURLConnection` and `NSURLSession` delegates in order to automatically add SSL pinning + validation to the App's connections. + + Swizzling allows enabling pinning within an App without having to find and modify each + and every instance of `NSURLConnection` or `NSURLSession` delegates. + However, it should only be enabled for simple Apps, as it may not work properly in several + scenarios including: + + * Apps with complex connection delegates, for example to handle client authentication + via certificates or basic authentication. + * Apps where method swizzling of the connection delegates is already performed by another + module or library (such as Analytics SDKs). + * Apps that do no use `NSURLSession` or `NSURLConnection` for their connections. + + In such scenarios or if the developer wants a tigher control on the App's networking + behavior, `kTSKSwizzleNetworkDelegates` should be set to `NO`; the developer should then + manually add pinning validation to the App's authentication handlers. + + See the `TSKPinningValidator` class for instructions on how to do so. + */ +FOUNDATION_EXPORT const TSKGlobalConfigurationKey kTSKSwizzleNetworkDelegates; + + +/** + A dictionary with domains (such as _www.domain.com_) as keys and dictionaries as values. + + Each entry should contain domain-specific settings for performing pinning validation when + connecting to the domain, including for example the domain's public key hashes. A list of + all domain-specific keys is available in the "Domain-specific Keys" sections. + */ +FOUNDATION_EXPORT const TSKGlobalConfigurationKey kTSKPinnedDomains; + + + +#pragma mark Global Configuration Keys - Optional + + +/** + A boolean. If set to `YES`, pinning validation will be skipped if the server's certificate + chain terminates at a user-defined trust anchor (such as a root CA that isn't part of OS X's + default trust store) and no pin failure reports will be sent; default value is `YES`. + + This is useful for allowing SSL connections through corporate proxies or firewalls. See + "How does key pinning interact with local proxies and filters?" within the Chromium security + FAQ at https://www.chromium.org/Home/chromium-security/security-faq for more information. + + Only available on macOS. + */ +FOUNDATION_EXPORT const TSKGlobalConfigurationKey kTSKIgnorePinningForUserDefinedTrustAnchors NS_AVAILABLE_MAC(10_9); + + +#pragma mark Domain-Specific Configuration Keys - Required + +/** + An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate's + Subject Public Key Info. + + TrustKit will verify that at least one of the specified pins is found in the server's + evaluated certificate chain. + */ +FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKPublicKeyHashes; + + +/** + An array of `TSKSupportedAlgorithm` constants to specify the public key algorithms for the + keys to be pinned. + + TrustKit requires this information in order to compute SSL pins when validating a server's + certificate chain, because the `Security` framework does not provide APIs to extract the + key's algorithm from an SSL certificate. To minimize the performance impact of Trustkit, + only one algorithm should be enabled. + */ +FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKPublicKeyAlgorithms; + + +#pragma mark Domain-Specific Configuration Keys - Optional + +/** + A boolean. If set to `NO`, TrustKit will not block SSL connections that caused a pin or + certificate validation error; default value is `YES`. + + When a pinning failure occurs, pin failure reports will always be sent to the configured + report URIs regardless of the value of `kTSKEnforcePinning`. + */ +FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKEnforcePinning; + + +/** + A boolean. If set to `YES`, also pin all the subdomains of the specified domain; default + value is `NO`. + */ +FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKIncludeSubdomains; + + +/** + A boolean. If set to `YES`, TrustKit will not pin this specific domain if `kTSKIncludeSubdomains` + was set for this domain's parent domain. + + This allows excluding specific subdomains from a pinning policy that was applied to a + parent domain. + */ +FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKExcludeSubdomainFromParentPolicy; + + +/** + An array of URLs to which pin validation failures should be reported. + + To minimize the performance impact of sending reports on each validation failure, the reports + are uploaded using the background transfer service and are also rate-limited to one per day + and per type of failure. For HTTPS report URLs, the HTTPS connections will ignore the SSL + pinning policy and use the default certificate validation mechanisms, in order to maximize + the chance of the reports reaching the server. The format of the reports is similar to the + one described in RFC 7469 for the HPKP specification: + + { + "app-bundle-id": "com.datatheorem.testtrustkit2", + "app-version": "1", + "app-vendor-id": "599F9C00-92DC-4B5C-9464-7971F01F8370", + "app-platform": "IOS", + "app-platform-version": "10.2.0", + "trustkit-version": "1.3.1", + "hostname": "www.datatheorem.com", + "port": 0, + "noted-hostname": "datatheorem.com", + "include-subdomains": true, + "enforce-pinning": true, + "validated-certificate-chain": [ + pem1, ... pemN + ], + "known-pins": [ + "pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\"", + "pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"" + ], + "validation-result":1 + } + */ +FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKReportUris; + + +/** + A boolean. If set to `YES`, the default report URL for sending pin failure reports will + be disabled; default value is `NO`. + + By default, pin failure reports are sent to a report server hosted by Data Theorem, for + detecting potential CA compromises and man-in-the-middle attacks, as well as providing a + free dashboard for developers; email info@datatheorem.com if you'd like a dashboard for + your App. Only pin failure reports are sent, which contain the App's bundle ID, the IDFV, + and the server's hostname and certificate chain that failed validation. + */ +FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKDisableDefaultReportUri; + + +/** + A string containing the date, in yyyy-MM-dd format, on which the domain's configured SSL + pins expire, thus disabling pinning validation. If the key is not set, then the pins do + not expire. + + Expiration helps prevent connectivity issues in Apps which do not get updates to their + pin set, such as when the user disables App updates. + */ +FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKExpirationDate; + + + +#pragma mark Supported Public Key Algorithm Keys + + +/** + A public key algorithm supported by TrustKit for computing SSL pins: + + * `kTSKAlgorithmRsa2048` + * `kTSKAlgorithmRsa4096` + * `kTSKAlgorithmEcDsaSecp256r1` + * `kTSKAlgorithmEcDsaSecp384r1` + + */ +typedef NSString *TSKSupportedAlgorithm; + + +/** + RSA 2048. + */ +FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmRsa2048; + + +/** + RSA 4096. + */ +FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmRsa4096; + + +/** + ECDSA with secp256r1 curve. + */ +FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp256r1; + + +/** + ECDSA with secp384r1 curve. + */ +FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp384r1; + +#endif /* TSKTrustKitConfig_h */ diff --git a/TrustKit/TrustKit+Private.h b/TrustKit/TrustKit+Private.h deleted file mode 100644 index bd934e45..00000000 --- a/TrustKit/TrustKit+Private.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - - TrustKit+Private.h - TrustKit - - Copyright 2015 The TrustKit Project Authors - Licensed under the MIT license, see associated LICENSE file for terms. - See AUTHORS file for the list of project authors. - - */ - -#ifndef TrustKit_TrustKit_Private____FILEEXTENSION___ -#define TrustKit_TrustKit_Private____FILEEXTENSION___ - -#import "TrustKit.h" -#import "Pinning/ssl_pin_verifier.h" -#import "Reporting/TSKBackgroundReporter.h" - -// The logging function we use within TrustKit -#ifdef DEBUG -#define TSKLog(format, ...) NSLog(@"=== TrustKit: " format, ##__VA_ARGS__); -#else -#define TSKLog(format, ...) -#endif - -#pragma mark Methods for the unit tests - -@interface TrustKit(Private) - -@property (nonatomic) TSKBackgroundReporter *pinFailureReporter; - -- (void)sendValidationReport:(TSKPinningValidatorResult *)result; - -+ (void) resetConfiguration; -+ (BOOL) wasTrustKitInitialized; -+ (NSString *) getDefaultReportUri; -+ (TSKBackgroundReporter *) getGlobalPinFailureReporter; -+ (void) setGlobalPinFailureReporter:(TSKBackgroundReporter *) reporter; - -@end - - -#endif diff --git a/TrustKit/TrustKit-Prefix.pch b/TrustKit/TrustKit-Prefix.pch index ce8fc2bf..8716c6fe 100644 --- a/TrustKit/TrustKit-Prefix.pch +++ b/TrustKit/TrustKit-Prefix.pch @@ -1,5 +1,7 @@ // Copyright © 2017 TrustKit. All rights reserved. +#import "TSKCommon.h" + #ifdef __OBJC__ #import diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index c0b1926d..ed78904f 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -9,244 +9,11 @@ */ - -#import #import "TSKPinningValidator.h" +#import "TSKTrustKitConfig.h" NS_ASSUME_NONNULL_BEGIN - -#pragma mark TrustKit Version Number - -/** - The version of TrustKit, such as "1.4.0". - */ -FOUNDATION_EXPORT NSString * const TrustKitVersion; - - -#pragma mark Configuration Keys - - -/** - A global, App-wide configuration key that can be set in the pinning policy. - */ -typedef NSString *TSKGlobalConfigurationKey; - - -/** - A domain-specific configuration key (to defined for a domain under the `kTSKPinnedDomains` - key) that can be set in the pinning policy. - */ -typedef NSString *TSKDomainConfigurationKey; - - -#pragma mark Global Configuration Keys - Required - - -/** - A boolean. If set to `YES`, TrustKit will perform method swizzling on the App's - `NSURLConnection` and `NSURLSession` delegates in order to automatically add SSL pinning - validation to the App's connections. - - Swizzling allows enabling pinning within an App without having to find and modify each - and every instance of `NSURLConnection` or `NSURLSession` delegates. - However, it should only be enabled for simple Apps, as it may not work properly in several - scenarios including: - - * Apps with complex connection delegates, for example to handle client authentication - via certificates or basic authentication. - * Apps where method swizzling of the connection delegates is already performed by another - module or library (such as Analytics SDKs). - * Apps that do no use `NSURLSession` or `NSURLConnection` for their connections. - - In such scenarios or if the developer wants a tigher control on the App's networking - behavior, `kTSKSwizzleNetworkDelegates` should be set to `NO`; the developer should then - manually add pinning validation to the App's authentication handlers. - - See the `TSKPinningValidator` class for instructions on how to do so. - */ -FOUNDATION_EXPORT const TSKGlobalConfigurationKey kTSKSwizzleNetworkDelegates; - - -/** - A dictionary with domains (such as _www.domain.com_) as keys and dictionaries as values. - - Each entry should contain domain-specific settings for performing pinning validation when - connecting to the domain, including for example the domain's public key hashes. A list of - all domain-specific keys is available in the "Domain-specific Keys" sections. - */ -FOUNDATION_EXPORT const TSKGlobalConfigurationKey kTSKPinnedDomains; - - - -#pragma mark Global Configuration Keys - Optional - - -/** - A boolean. If set to `YES`, pinning validation will be skipped if the server's certificate - chain terminates at a user-defined trust anchor (such as a root CA that isn't part of OS X's - default trust store) and no pin failure reports will be sent; default value is `YES`. - - This is useful for allowing SSL connections through corporate proxies or firewalls. See - "How does key pinning interact with local proxies and filters?" within the Chromium security - FAQ at https://www.chromium.org/Home/chromium-security/security-faq for more information. - - Only available on macOS. - */ -FOUNDATION_EXPORT const TSKGlobalConfigurationKey kTSKIgnorePinningForUserDefinedTrustAnchors NS_AVAILABLE_MAC(10_9); - - -#pragma mark Domain-Specific Configuration Keys - Required - -/** - An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate's - Subject Public Key Info. - - TrustKit will verify that at least one of the specified pins is found in the server's - evaluated certificate chain. - */ -FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKPublicKeyHashes; - - -/** - An array of `TSKSupportedAlgorithm` constants to specify the public key algorithms for the - keys to be pinned. - - TrustKit requires this information in order to compute SSL pins when validating a server's - certificate chain, because the `Security` framework does not provide APIs to extract the - key's algorithm from an SSL certificate. To minimize the performance impact of Trustkit, - only one algorithm should be enabled. -*/ -FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKPublicKeyAlgorithms; - - -#pragma mark Domain-Specific Configuration Keys - Optional - -/** - A boolean. If set to `NO`, TrustKit will not block SSL connections that caused a pin or - certificate validation error; default value is `YES`. - - When a pinning failure occurs, pin failure reports will always be sent to the configured - report URIs regardless of the value of `kTSKEnforcePinning`. - */ -FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKEnforcePinning; - - -/** - A boolean. If set to `YES`, also pin all the subdomains of the specified domain; default - value is `NO`. - */ -FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKIncludeSubdomains; - - -/** - A boolean. If set to `YES`, TrustKit will not pin this specific domain if `kTSKIncludeSubdomains` - was set for this domain's parent domain. - - This allows excluding specific subdomains from a pinning policy that was applied to a - parent domain. - */ -FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKExcludeSubdomainFromParentPolicy; - - -/** - An array of URLs to which pin validation failures should be reported. - - To minimize the performance impact of sending reports on each validation failure, the reports - are uploaded using the background transfer service and are also rate-limited to one per day - and per type of failure. For HTTPS report URLs, the HTTPS connections will ignore the SSL - pinning policy and use the default certificate validation mechanisms, in order to maximize - the chance of the reports reaching the server. The format of the reports is similar to the - one described in RFC 7469 for the HPKP specification: - - { - "app-bundle-id": "com.datatheorem.testtrustkit2", - "app-version": "1", - "app-vendor-id": "599F9C00-92DC-4B5C-9464-7971F01F8370", - "app-platform": "IOS", - "app-platform-version": "10.2.0", - "trustkit-version": "1.3.1", - "hostname": "www.datatheorem.com", - "port": 0, - "noted-hostname": "datatheorem.com", - "include-subdomains": true, - "enforce-pinning": true, - "validated-certificate-chain": [ - pem1, ... pemN - ], - "known-pins": [ - "pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\"", - "pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"" - ], - "validation-result":1 - } - */ -FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKReportUris; - - -/** - A boolean. If set to `YES`, the default report URL for sending pin failure reports will - be disabled; default value is `NO`. - - By default, pin failure reports are sent to a report server hosted by Data Theorem, for - detecting potential CA compromises and man-in-the-middle attacks, as well as providing a - free dashboard for developers; email info@datatheorem.com if you'd like a dashboard for - your App. Only pin failure reports are sent, which contain the App's bundle ID, the IDFV, - and the server's hostname and certificate chain that failed validation. - */ -FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKDisableDefaultReportUri; - - -/** - A string containing the date, in yyyy-MM-dd format, on which the domain's configured SSL - pins expire, thus disabling pinning validation. If the key is not set, then the pins do - not expire. - - Expiration helps prevent connectivity issues in Apps which do not get updates to their - pin set, such as when the user disables App updates. - */ -FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKExpirationDate; - - - -#pragma mark Supported Public Key Algorithm Keys - - -/** - A public key algorithm supported by TrustKit for computing SSL pins: - - * `kTSKAlgorithmRsa2048` - * `kTSKAlgorithmRsa4096` - * `kTSKAlgorithmEcDsaSecp256r1` - * `kTSKAlgorithmEcDsaSecp384r1` - - */ -typedef NSString *TSKSupportedAlgorithm; - - -/** - RSA 2048. - */ -FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmRsa2048; - - -/** - RSA 4096. - */ -FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmRsa4096; - - -/** - ECDSA with secp256r1 curve. - */ -FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp256r1; - - -/** - ECDSA with secp384r1 curve. - */ -FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp384r1; - /** `TrustKit` is a class for programmatically configuring the global SSL pinning policy within an App. diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index f43a01d4..8cff7778 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -9,7 +9,7 @@ */ -#import "TrustKit+Private.h" +#import "TrustKit.h" #import "Reporting/TSKBackgroundReporter.h" #import "Swizzling/TSKNSURLConnectionDelegateProxy.h" #import "Swizzling/TSKNSURLSessionDelegateProxy.h" diff --git a/TrustKit/TrustKit.modulemap b/TrustKit/TrustKit.modulemap new file mode 100644 index 00000000..b685e43a --- /dev/null +++ b/TrustKit/TrustKit.modulemap @@ -0,0 +1,6 @@ +framework module TrustKit { + umbrella header "TSKTrustKit-Umbrella.h" + + //export * + //module * { export * } +} diff --git a/TrustKit/configuration_utils.h b/TrustKit/configuration_utils.h index 48b82dbd..8e9ad4f9 100644 --- a/TrustKit/configuration_utils.h +++ b/TrustKit/configuration_utils.h @@ -6,6 +6,5 @@ // Copyright © 2017 TrustKit. All rights reserved. // -#import NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *trustKitConfiguration); diff --git a/TrustKit/configuration_utils.m b/TrustKit/configuration_utils.m index d1982694..dac7e093 100644 --- a/TrustKit/configuration_utils.m +++ b/TrustKit/configuration_utils.m @@ -7,8 +7,8 @@ // #import "configuration_utils.h" +#import "TSKTrustKitConfig.h" #import "Dependencies/domain_registry/domain_registry.h" -#import "TrustKit+Private.h" static BOOL isSubdomain(NSString *domain, NSString *subdomain) diff --git a/TrustKit/module.modulemap b/TrustKit/module.modulemap deleted file mode 100644 index e1dc96fe..00000000 --- a/TrustKit/module.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module TrustKit { - umbrella header "TrustKit.h" - - export * - module * { export * } -} diff --git a/TrustKit/parse_configuration.m b/TrustKit/parse_configuration.m index 3afea06b..94d44959 100644 --- a/TrustKit/parse_configuration.m +++ b/TrustKit/parse_configuration.m @@ -6,7 +6,6 @@ // Copyright © 2016 TrustKit. All rights reserved. // -#import #import "TrustKit.h" #import "Dependencies/domain_registry/domain_registry.h" #import "parse_configuration.h" diff --git a/TrustKitTests/TSKCertificateUtils.h b/TrustKitTests/TSKCertificateUtils.h index a519c32c..1e5d9d83 100644 --- a/TrustKitTests/TSKCertificateUtils.h +++ b/TrustKitTests/TSKCertificateUtils.h @@ -9,7 +9,7 @@ */ -#import +@import Foundation; @interface TSKCertificateUtils : NSObject diff --git a/TrustKitTests/TSKNSURLConnectionTests.m b/TrustKitTests/TSKNSURLConnectionTests.m index 42cd4c78..ef3fc9ae 100644 --- a/TrustKitTests/TSKNSURLConnectionTests.m +++ b/TrustKitTests/TSKNSURLConnectionTests.m @@ -7,8 +7,7 @@ // #import -#import "../TrustKit/TrustKit+Private.h" -#import "../TrustKit/TSKPinningValidatorResult.h" +#import "../TrustKit/TrustKit.h" #import "../TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h" #import @@ -17,6 +16,10 @@ @interface TSKNSURLConnectionDelegateProxy (TestSupport) -(BOOL)forwardToOriginalDelegateAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge forConnection:(NSURLConnection *)connection; @end +@interface TrustKit (TestSupport) ++ (void)resetConfiguration; +@end + /* #pragma mark Test NSURLConnection delegate with no auth handler @interface TestNSURLConnectionDelegateNoAuthHandler : NSObject diff --git a/TrustKitTests/TSKNSURLSessionTests.m b/TrustKitTests/TSKNSURLSessionTests.m index fbf3a69f..c334db62 100644 --- a/TrustKitTests/TSKNSURLSessionTests.m +++ b/TrustKitTests/TSKNSURLSessionTests.m @@ -7,11 +7,15 @@ // #import -#import "../TrustKit/TrustKit+Private.h" +#import "../TrustKit/TrustKit.h" #import "../TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h" #import +@interface TrustKit (TestSupport) ++ (void)resetConfiguration; +@end + @interface TSKNSURLSessionDelegateProxy (TestSupport) @property (nonatomic) id originalDelegate; @property (nonatomic) TSKPinValidationResult lastTrustDecision; diff --git a/TrustKitTests/TSKPinConfigurationTests.m b/TrustKitTests/TSKPinConfigurationTests.m index 06c31e31..7433f9f7 100644 --- a/TrustKitTests/TSKPinConfigurationTests.m +++ b/TrustKitTests/TSKPinConfigurationTests.m @@ -10,7 +10,7 @@ */ #import -#import "../TrustKit/TrustKit+Private.h" +#import "../TrustKit/TrustKit.h" #import "../TrustKit/Pinning/ssl_pin_verifier.h" #import "../TrustKit/parse_configuration.h" diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 5142aac1..2e70c6ed 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -10,7 +10,8 @@ */ #import -#import "../TrustKit/TrustKit+Private.h" +#import "../TrustKit/TrustKit.h" +#import "../TrustKit/TSKTrustKitConfig.h" #import "../TrustKit/parse_configuration.h" #import "../TrustKit/TSKPinningValidatorResult.h" diff --git a/TrustKitTests/TSKPublicKeyAlgorithmTests.m b/TrustKitTests/TSKPublicKeyAlgorithmTests.m index 4aff1c5f..259fab42 100644 --- a/TrustKitTests/TSKPublicKeyAlgorithmTests.m +++ b/TrustKitTests/TSKPublicKeyAlgorithmTests.m @@ -10,7 +10,7 @@ */ #import -#import "../TrustKit/TrustKit+Private.h" +#import "../TrustKit/TSKTrustKitConfig.h" #import "../TrustKit/parse_configuration.h" #import "../TrustKit/Pinning/ssl_pin_verifier.h" diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index 688d31a6..cbba0ec3 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -11,7 +11,7 @@ #import -#import "../TrustKit/TrustKit+Private.h" +#import "../TrustKit/TrustKit.h" #import "../TrustKit/TSKPinningValidatorResult.h" #import "../TrustKit/Reporting/TSKBackgroundReporter.h" @@ -24,6 +24,12 @@ #pragma mark Test suite +@interface TrustKit (TestSupport) +@property (nonatomic) TSKBackgroundReporter *pinFailureReporter; ++ (NSString *)getDefaultReportUri; +- (void)sendValidationReport:(TSKPinningValidatorResult *)result; +@end + @interface TSKReporterTests : XCTestCase @end From df02a8776861a3cd663115f9b1be9764ae0aac3f Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 6 Apr 2017 14:32:13 -0400 Subject: [PATCH 010/126] Removed excess imports from TrustKit.h These were not needed to include TrustKit.h, but very common so I want to explore moving them to something like the PCH or ModuleMap --- TrustKit.xcodeproj/project.pbxproj | 17 ++++++++++++----- TrustKit/Reporting/TSKBackgroundReporter.m | 4 +--- .../Swizzling/TSKNSURLConnectionDelegateProxy.m | 3 ++- .../Swizzling/TSKNSURLSessionDelegateProxy.m | 3 ++- TrustKit/TrustKit-Prefix.pch | 8 +++++++- TrustKit/TrustKit.h | 4 +++- TrustKit/TrustKit.m | 1 + TrustKit/parse_configuration.m | 2 +- TrustKitTests/TSKNSURLConnectionTests.m | 2 ++ TrustKitTests/TSKNSURLSessionTests.m | 2 ++ TrustKitTests/TSKPinConfigurationTests.m | 2 +- TrustKitTests/TSKPinningValidatorTests.m | 1 + 12 files changed, 35 insertions(+), 14 deletions(-) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 31e49973..4a4ce720 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -392,6 +392,7 @@ FC4CAC7E1E96917B00DAC41E /* TSKTrustKitConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustKitConfig.h; sourceTree = ""; }; FC4CAC7F1E96A2D600DAC41E /* TSKCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKCommon.h; sourceTree = ""; }; FC4CAC801E96A95D00DAC41E /* TSKTrustKit-Umbrella.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TSKTrustKit-Umbrella.h"; sourceTree = ""; }; + FCD0CC031E96ADF00076D431 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -512,6 +513,7 @@ 8C8480491A896EE30017C155 /* TrustKit */, 8C8480561A896EE30017C155 /* TrustKitTests */, 8C8480481A896EE30017C155 /* Products */, + FCD0CC021E96ADF00076D431 /* Frameworks */, ); sourceTree = ""; }; @@ -745,6 +747,14 @@ name = Configuration; sourceTree = ""; }; + FCD0CC021E96ADF00076D431 /* Frameworks */ = { + isa = PBXGroup; + children = ( + FCD0CC031E96ADF00076D431 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1373,7 +1383,6 @@ "DEBUG=1", "$(inherited)", ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; @@ -1389,13 +1398,11 @@ GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MODULEMAP_FILE = "$(SRCROOT)/TrustKit/TrustKit.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; TARGETED_DEVICE_FAMILY = "1,2"; - USE_HEADERMAP = NO; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1448,12 +1455,10 @@ GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MODULEMAP_FILE = "$(SRCROOT)/TrustKit/TrustKit.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; TARGETED_DEVICE_FAMILY = "1,2"; - USE_HEADERMAP = NO; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -1474,6 +1479,7 @@ ENABLE_BITCODE = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "TrustKit/TrustKit-Prefix.pch"; + GENERATE_PKGINFO_FILE = YES; INFOPLIST_FILE = TrustKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -1504,6 +1510,7 @@ ENABLE_BITCODE = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "TrustKit/TrustKit-Prefix.pch"; + GENERATE_PKGINFO_FILE = YES; INFOPLIST_FILE = TrustKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 10.0; diff --git a/TrustKit/Reporting/TSKBackgroundReporter.m b/TrustKit/Reporting/TSKBackgroundReporter.m index ca4a351c..5e3f977f 100644 --- a/TrustKit/Reporting/TSKBackgroundReporter.m +++ b/TrustKit/Reporting/TSKBackgroundReporter.m @@ -16,7 +16,6 @@ #import "TSKReportsRateLimiter.h" #import "vendor_identifier.h" - // Session identifier for background uploads: .TSKBackgroundReporter static NSString * const kTSKBackgroundSessionIdentifierFormat = @"%@.TSKBackgroundReporter.%@"; @@ -33,7 +32,6 @@ @interface TSKBackgroundReporter() @end - @implementation TSKBackgroundReporter #pragma mark Public methods @@ -120,7 +118,7 @@ - (nonnull instancetype)initAndRateLimitReports:(BOOL)shouldRateLimitReports; // identifier should never be created, so ensure that cannot happen by creating // a unique ID per instance. NSString *backgroundSessionId = [NSString stringWithFormat:kTSKBackgroundSessionIdentifierFormat, - _appBundleId, @""]; + _appBundleId, [[NSUUID UUID] UUIDString]]; // The API for creating background sessions changed between iOS 7 and iOS 8 and OS X 10.9 and 10.10 // Try to use the new API if available at runtime diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m index 608c7f8a..7e8938fb 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m @@ -7,7 +7,8 @@ // #import "TSKNSURLConnectionDelegateProxy.h" -#import "../TrustKit.h" +#import "../TSKPinValidatorResult.h" +#import "../TSKPinningValidator.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" typedef void (^AsyncCompletionHandler)(NSURLResponse *response, NSData *data, NSError *connectionError); diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m index 21e223b4..d737ccf0 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m @@ -7,8 +7,9 @@ // #import "TSKNSURLSessionDelegateProxy.h" +#import "../TSKPinValidatorResult.h" +#import "../TSKPinningValidator.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" -#import "../TrustKit.h" @interface TSKNSURLSessionDelegateProxy () /* The NSURLSessionDelegate we're going to proxy */ diff --git a/TrustKit/TrustKit-Prefix.pch b/TrustKit/TrustKit-Prefix.pch index 8716c6fe..33f1db1b 100644 --- a/TrustKit/TrustKit-Prefix.pch +++ b/TrustKit/TrustKit-Prefix.pch @@ -1,9 +1,15 @@ // Copyright © 2017 TrustKit. All rights reserved. +#import #import "TSKCommon.h" -#ifdef __OBJC__ +#ifndef PrefixHeader_pch +#define PrefixHeader_pch +#ifdef __OBJC__ +// Include any system framework and library headers here that should be included in all compilation units. +// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. #import +#endif #endif diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index ed78904f..fd21de19 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -9,9 +9,11 @@ */ -#import "TSKPinningValidator.h" #import "TSKTrustKitConfig.h" +@class TSKPinningValidator; +@class TSKPinningValidatorResult; + NS_ASSUME_NONNULL_BEGIN /** diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 8cff7778..47bb8e0c 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -14,6 +14,7 @@ #import "Swizzling/TSKNSURLConnectionDelegateProxy.h" #import "Swizzling/TSKNSURLSessionDelegateProxy.h" #import "parse_configuration.h" +#import "TSKPinningValidator.h" #import "TSKPinningValidatorResult.h" diff --git a/TrustKit/parse_configuration.m b/TrustKit/parse_configuration.m index 94d44959..f0e86e6b 100644 --- a/TrustKit/parse_configuration.m +++ b/TrustKit/parse_configuration.m @@ -6,7 +6,7 @@ // Copyright © 2016 TrustKit. All rights reserved. // -#import "TrustKit.h" +#import "TSKTrustKitConfig.h" #import "Dependencies/domain_registry/domain_registry.h" #import "parse_configuration.h" #import "Pinning/TSKPublicKeyAlgorithm.h" diff --git a/TrustKitTests/TSKNSURLConnectionTests.m b/TrustKitTests/TSKNSURLConnectionTests.m index ef3fc9ae..5d138b38 100644 --- a/TrustKitTests/TSKNSURLConnectionTests.m +++ b/TrustKitTests/TSKNSURLConnectionTests.m @@ -8,6 +8,8 @@ #import #import "../TrustKit/TrustKit.h" +#import "../TrustKit/TSKPinningValidator.h" +#import "../TrustKit/TSKPinningValidatorResult.h" #import "../TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h" #import diff --git a/TrustKitTests/TSKNSURLSessionTests.m b/TrustKitTests/TSKNSURLSessionTests.m index c334db62..bc4fa174 100644 --- a/TrustKitTests/TSKNSURLSessionTests.m +++ b/TrustKitTests/TSKNSURLSessionTests.m @@ -8,6 +8,8 @@ #import #import "../TrustKit/TrustKit.h" +#import "../TrustKit/TSKPinningValidator.h" +#import "../TrustKit/TSKPinningValidatorResult.h" #import "../TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h" #import diff --git a/TrustKitTests/TSKPinConfigurationTests.m b/TrustKitTests/TSKPinConfigurationTests.m index 7433f9f7..32e9b22d 100644 --- a/TrustKitTests/TSKPinConfigurationTests.m +++ b/TrustKitTests/TSKPinConfigurationTests.m @@ -12,9 +12,9 @@ #import #import "../TrustKit/TrustKit.h" #import "../TrustKit/Pinning/ssl_pin_verifier.h" +#import "../TrustKit/Pinning/TSKPublicKeyAlgorithm.h" #import "../TrustKit/parse_configuration.h" - @interface TSKPinConfigurationTests : XCTestCase @end diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 2e70c6ed..12b2295c 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -11,6 +11,7 @@ #import #import "../TrustKit/TrustKit.h" +#import "../TrustKit/TSKPinningValidator.h" #import "../TrustKit/TSKTrustKitConfig.h" #import "../TrustKit/parse_configuration.h" #import "../TrustKit/TSKPinningValidatorResult.h" From fa42ae3bba676941e7ebacd9e38ec07f58f63d19 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 6 Apr 2017 17:05:13 -0400 Subject: [PATCH 011/126] Re-added Foundation imports because I just cannot get PCH files to work properly in this project. They compile OK, but autocomplete is dead (tried everything, clearing DerivedData, etc) --- TrustKit.xcodeproj/project.pbxproj | 4 +- TrustKit/Pinning/TSKSPKIHashCache.h | 1 + TrustKit/Pinning/ssl_pin_verifier.h | 1 + TrustKit/Reporting/TSKBackgroundReporter.h | 1 + TrustKit/Reporting/TSKPinFailureReport.h | 2 +- TrustKit/Reporting/TSKPinFailureReport.m | 1 - TrustKit/Reporting/TSKReportsRateLimiter.h | 1 + TrustKit/Reporting/reporting_utils.h | 2 + TrustKit/Reporting/vendor_identifier.h | 2 + TrustKit/Reporting/vendor_identifier.m | 6 +- .../TSKNSURLConnectionDelegateProxy.h | 6 +- .../TSKNSURLConnectionDelegateProxy.m | 17 ++++-- .../Swizzling/TSKNSURLSessionDelegateProxy.h | 8 ++- .../Swizzling/TSKNSURLSessionDelegateProxy.m | 17 +++--- TrustKit/TSKPinValidatorResult.h | 1 + TrustKit/TSKPinningValidator.h | 56 +----------------- TrustKit/TSKPinningValidator.m | 14 ----- TrustKit/TSKPinningValidatorResult.h | 1 + TrustKit/TrustKit-Prefix.pch | 9 +-- TrustKit/TrustKit.h | 1 + TrustKit/TrustKit.m | 4 +- TrustKit/TrustKit.modulemap | 4 +- TrustKit/configuration_utils.h | 1 + TrustKit/parse_configuration.h | 2 + TrustKitTests/TSKNSURLConnectionTests.m | 59 ++++++++++--------- TrustKitTests/TSKNSURLSessionTests.m | 47 +++++++++------ TrustKitTests/TSKPinningValidatorTests.m | 13 ++-- 27 files changed, 126 insertions(+), 155 deletions(-) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 4a4ce720..782a4a56 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -739,8 +739,8 @@ FC663F551E8421BE008E2EFE /* Configuration */ = { isa = PBXGroup; children = ( - 8C5D98B21CEFF079008E654B /* parse_configuration.m */, 8C5D98B61CEFF103008E654B /* parse_configuration.h */, + 8C5D98B21CEFF079008E654B /* parse_configuration.m */, 8C4346D41E5B894A008023F9 /* configuration_utils.h */, 8C4346D51E5B894A008023F9 /* configuration_utils.m */, ); @@ -1398,6 +1398,7 @@ GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + MODULEMAP_FILE = "$(SRCROOT)/TrustKit/TrustKit.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -1455,6 +1456,7 @@ GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + MODULEMAP_FILE = "$(SRCROOT)/TrustKit/TrustKit.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; diff --git a/TrustKit/Pinning/TSKSPKIHashCache.h b/TrustKit/Pinning/TSKSPKIHashCache.h index 43434f6b..bbb345f9 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.h +++ b/TrustKit/Pinning/TSKSPKIHashCache.h @@ -11,6 +11,7 @@ #import "TSKPublicKeyAlgorithm.h" +@import Foundation; @import Security; // Each key is a raw certificate data (for easy lookup) and each value is the certificate's raw SPKI data diff --git a/TrustKit/Pinning/ssl_pin_verifier.h b/TrustKit/Pinning/ssl_pin_verifier.h index 32c25b60..3c656f9f 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.h +++ b/TrustKit/Pinning/ssl_pin_verifier.h @@ -10,6 +10,7 @@ */ #import "../TSKPinValidatorResult.h" +@import Foundation; @class TSKSPKIHashCache; diff --git a/TrustKit/Reporting/TSKBackgroundReporter.h b/TrustKit/Reporting/TSKBackgroundReporter.h index 04b0ba42..b1ee974c 100644 --- a/TrustKit/Reporting/TSKBackgroundReporter.h +++ b/TrustKit/Reporting/TSKBackgroundReporter.h @@ -10,6 +10,7 @@ */ #import "../Pinning/ssl_pin_verifier.h" +@import Foundation; /** `TSKSimpleBackgroundReporter` is a class for uploading pin failure reports using the background transfer service. diff --git a/TrustKit/Reporting/TSKPinFailureReport.h b/TrustKit/Reporting/TSKPinFailureReport.h index f687c09f..867f3c74 100644 --- a/TrustKit/Reporting/TSKPinFailureReport.h +++ b/TrustKit/Reporting/TSKPinFailureReport.h @@ -10,7 +10,7 @@ */ #import "../Pinning/ssl_pin_verifier.h" - +@import Foundation; @interface TSKPinFailureReport : NSObject diff --git a/TrustKit/Reporting/TSKPinFailureReport.m b/TrustKit/Reporting/TSKPinFailureReport.m index 2acca16f..945ed254 100644 --- a/TrustKit/Reporting/TSKPinFailureReport.m +++ b/TrustKit/Reporting/TSKPinFailureReport.m @@ -13,7 +13,6 @@ @implementation TSKPinFailureReport - - (nonnull instancetype) initWithAppBundleId:(nonnull NSString *)appBundleId appVersion:(nonnull NSString *)appVersion appPlatform:(nonnull NSString *)appPlatform diff --git a/TrustKit/Reporting/TSKReportsRateLimiter.h b/TrustKit/Reporting/TSKReportsRateLimiter.h index f446c335..1d374cf9 100644 --- a/TrustKit/Reporting/TSKReportsRateLimiter.h +++ b/TrustKit/Reporting/TSKReportsRateLimiter.h @@ -10,6 +10,7 @@ */ #import "TSKPinFailureReport.h" +@import Foundation; /* * Simple helper class which caches reports for 24 hours to prevent identical reports from being sent twice diff --git a/TrustKit/Reporting/reporting_utils.h b/TrustKit/Reporting/reporting_utils.h index 643654a4..bf0d762b 100644 --- a/TrustKit/Reporting/reporting_utils.h +++ b/TrustKit/Reporting/reporting_utils.h @@ -9,6 +9,8 @@ */ +@import Foundation; + #ifndef TrustKit_reporting_utils_h #define TrustKit_reporting_utils_h diff --git a/TrustKit/Reporting/vendor_identifier.h b/TrustKit/Reporting/vendor_identifier.h index 808a403c..c61ef6db 100644 --- a/TrustKit/Reporting/vendor_identifier.h +++ b/TrustKit/Reporting/vendor_identifier.h @@ -6,5 +6,7 @@ // Copyright © 2016 TrustKit. All rights reserved. // +@import Foundation; + // Will return the IDFV on platforms that support it (iOS, tvOS) and a randomly generated UUID on other platforms (macOS, watchOS) NSString *identifier_for_vendor(void); diff --git a/TrustKit/Reporting/vendor_identifier.m b/TrustKit/Reporting/vendor_identifier.m index 695a8b2c..44c3cf5a 100644 --- a/TrustKit/Reporting/vendor_identifier.m +++ b/TrustKit/Reporting/vendor_identifier.m @@ -10,13 +10,13 @@ #if TARGET_OS_IPHONE && !TARGET_OS_WATCH -#pragma mark Vendor identifier - macOS, tvOS +#pragma mark Vendor identifier - iOS, tvOS -@import UIKit; // for accessing IDFV +@import UIKit; // for accessing the IDFV NSString *identifier_for_vendor(void) { - return [[[UIDevice currentDevice] identifierForVendor]UUIDString]; + return [[[UIDevice currentDevice] identifierForVendor] UUIDString]; } #else diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h index 0f6da000..1c07809d 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h @@ -8,12 +8,14 @@ NS_ASSUME_NONNULL_BEGIN +@class TrustKit; + @interface TSKNSURLConnectionDelegateProxy : NSObject // Initalize our hooks -+ (void)swizzleNSURLConnectionConstructors; ++ (void)swizzleNSURLConnectionConstructors:(TrustKit *)trustKit; -- (instancetype _Nullable)initWithDelegate:(id)delegate; +- (instancetype _Nullable)initWithTrustKit:(TrustKit *)trustKit connectionDelegate:(id _Nullable)delegate; - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m index 7e8938fb..3c3cb162 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m @@ -7,6 +7,7 @@ // #import "TSKNSURLConnectionDelegateProxy.h" +#import "../TrustKit.h" #import "../TSKPinValidatorResult.h" #import "../TSKPinningValidator.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" @@ -15,13 +16,14 @@ @interface TSKNSURLConnectionDelegateProxy () @property (nonatomic) id originalDelegate; // The NSURLConnectionDelegate we're going to proxy +@property (nonatomic) TrustKit *trustKit; @end @implementation TSKNSURLConnectionDelegateProxy #pragma mark Public methods -+ (void)swizzleNSURLConnectionConstructors ++ (void)swizzleNSURLConnectionConstructors:(TrustKit *)trustKit { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wshadow" @@ -42,7 +44,8 @@ + (void)swizzleNSURLConnectionConstructors else { // Replace the delegate with our own so we can intercept and handle authentication challenges - TSKNSURLConnectionDelegateProxy *swizzledDelegate = [[TSKNSURLConnectionDelegateProxy alloc]initWithDelegate:delegate]; + TSKNSURLConnectionDelegateProxy *swizzledDelegate = [[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:trustKit + connectionDelegate:delegate]; connection = RSSWCallOriginal(request, swizzledDelegate); } return connection; @@ -67,7 +70,8 @@ + (void)swizzleNSURLConnectionConstructors else { // Replace the delegate with our own so we can intercept and handle authentication challenges - TSKNSURLConnectionDelegateProxy *swizzledDelegate = [[TSKNSURLConnectionDelegateProxy alloc]initWithDelegate:delegate]; + TSKNSURLConnectionDelegateProxy *swizzledDelegate = [[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:trustKit + connectionDelegate:delegate]; connection = RSSWCallOriginal(request, swizzledDelegate, startImmediately); } return connection; @@ -109,12 +113,13 @@ + (void)swizzleNSURLConnectionConstructors #pragma mark Instance Constructors -- (instancetype)initWithDelegate:(id)delegate +- (instancetype)initWithTrustKit:(TrustKit *)trustKit connectionDelegate:(id)delegate { self = [super init]; if (self) { _originalDelegate = delegate; + _trustKit = trustKit; } TSKLog(@"Proxy-ing NSURLConnectionDelegate: %@", NSStringFromClass([delegate class])); return self; @@ -170,8 +175,8 @@ - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticatio NSString *serverHostname = challenge.protectionSpace.host; // Check the trust object against the pinning policy - TSKTrustDecision trustDecision = [TSKPinningValidator evaluateTrust:serverTrust - forHostname:serverHostname]; + TSKTrustDecision trustDecision = [self.trustKit.pinningValidator evaluateTrust:serverTrust + forHostname:serverHostname]; if (trustDecision == TSKTrustDecisionShouldBlockConnection) { // Pinning validation failed - block the connection diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h index 6c7da849..8cb2a22d 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h @@ -6,15 +6,19 @@ // Copyright © 2015 TrustKit. All rights reserved. // +@import Foundation; + NS_ASSUME_NONNULL_BEGIN +@class TrustKit; + typedef void(^TSKURLSessionAuthChallengeCallback)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential); @interface TSKNSURLSessionDelegateProxy : NSObject -+ (void)swizzleNSURLSessionConstructors; ++ (void)swizzleNSURLSessionConstructors:(TrustKit *)trustKit; -- (instancetype _Nullable)initWithDelegate:(id)delegate; +- (instancetype _Nullable)initWithTrustKit:(TrustKit *)trustKit sessionDelegate:(id)delegate; - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m index d737ccf0..850efdd4 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m @@ -7,20 +7,22 @@ // #import "TSKNSURLSessionDelegateProxy.h" +#import "../TrustKit.h" #import "../TSKPinValidatorResult.h" #import "../TSKPinningValidator.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" @interface TSKNSURLSessionDelegateProxy () /* The NSURLSessionDelegate we're going to proxy */ -@property (nonatomic) id originalDelegate; +@property (nonatomic) id originalDelegate; +@property (nonatomic) TrustKit *trustKit; @end @implementation TSKNSURLSessionDelegateProxy #pragma mark Public methods -+ (void)swizzleNSURLSessionConstructors ++ (void)swizzleNSURLSessionConstructors:(TrustKit *)trustKit { // Figure out NSURLSession's "real" class // Pre iOS 8, for some reason hooking NSURLSession doesn't work. We need to use the real/private class __NSCFURLSession @@ -60,7 +62,8 @@ + (void)swizzleNSURLSessionConstructors else { // Replace the delegate with our own so we can intercept and handle authentication challenges - TSKNSURLSessionDelegateProxy *swizzledDelegate = [[TSKNSURLSessionDelegateProxy alloc]initWithDelegate:delegate]; + TSKNSURLSessionDelegateProxy *swizzledDelegate = [[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:trustKit + sessionDelegate:delegate]; session = RSSWCallOriginal(configuration, swizzledDelegate, queue); } @@ -72,8 +75,7 @@ + (void)swizzleNSURLSessionConstructors #pragma clang diagnostic pop } - -- (instancetype)initWithDelegate:(id)delegate +- (instancetype _Nullable)initWithTrustKit:(TrustKit *)trustKit sessionDelegate:(id)delegate { NSParameterAssert(delegate); @@ -81,6 +83,7 @@ - (instancetype)initWithDelegate:(id)delegate if (self) { _originalDelegate = delegate; + _trustKit = trustKit; } TSKLog(@"Proxy-ing NSURLSessionDelegate: %@", NSStringFromClass([delegate class])); return self; @@ -143,8 +146,8 @@ - (void)common_URLSession:(NSURLSession * _Nonnull)session if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { // Check the trust object against the pinning policy - TSKTrustDecision trustDecision = [TSKPinningValidator evaluateTrust:challenge.protectionSpace.serverTrust - forHostname:challenge.protectionSpace.host]; + TSKTrustDecision trustDecision = [self.trustKit.pinningValidator evaluateTrust:challenge.protectionSpace.serverTrust + forHostname:challenge.protectionSpace.host]; if (trustDecision == TSKTrustDecisionShouldBlockConnection) { // Pinning validation failed - block the connection diff --git a/TrustKit/TSKPinValidatorResult.h b/TrustKit/TSKPinValidatorResult.h index 463870fd..bd37de39 100644 --- a/TrustKit/TSKPinValidatorResult.h +++ b/TrustKit/TSKPinValidatorResult.h @@ -8,6 +8,7 @@ See AUTHORS file for the list of project authors. */ +@import Foundation; /** Possible return values when verifying a server's identity. diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 75bdd70b..28daa2ab 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -11,6 +11,7 @@ #import "TSKPinValidatorResult.h" #import "Pinning/TSKPublicKeyAlgorithm.h" +@import Foundation; @class TSKPinningValidatorResult; @@ -99,58 +100,3 @@ typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef cert NSURLCredential * _Nullable credential))completionHandler; @end - -#pragma mark Global Singleton-Based Methods Category - -@interface TSKPinningValidator (GlobalTrustKit) - -/** - Evaluate the supplied server trust against the global SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent. - - When using the `NSURLSession` or `WKWebView` network APIs, the `handleChallenge:completionHandler:` method should be called instead, as it is simpler to use. - - When using low-level network APIs (such as `NSStream`), instructions on how to retrieve the connection's `serverTrust` are available at https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html . - - @param serverTrust The trust object representing the server's certificate chain. The trust's evaluation policy is always overridden using `SecTrustSetPolicies()` to ensure all the proper SSL checks (expiration, hostname validation, etc.) are enabled. - - @param serverHostname The hostname of the server whose identity is being validated. - - @return A `TSKTrustDecision` which describes whether the SSL connection should be allowed or blocked, based on the global pinning policy. - - @warning If no SSL pinning policy was configured for the supplied _serverHostname_, this method has no effect and will return `TSKTrustDecisionDomainNotPinned` without validating the supplied _serverTrust_ at all. This means that the server's _serverTrust_ object __must__ be verified against the device's trust store using `SecTrustEvaluate()`. Failing to do so will __disable SSL certificate validation__. - - @exception NSException Thrown when TrustKit has not been initialized with a pinning policy. - */ -+ (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname:(NSString * _Nonnull)serverHostname; - - -/** - Helper method for handling authentication challenges received within a `NSURLSessionDelegate`, `NSURLSessionTaskDelegate` or `WKNavigationDelegate`. - - This method will evaluate the server trust within the authentication challenge against the global SSL pinning policy previously configured, and then call the `completionHandler` with the corresponding `disposition` and `credential`. For example, this method can be leveraged in a `WKNavigationDelegate` challenge handler method: - - - (void)webView:(WKWebView *)webView - didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge - completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential *credential))completionHandler - { - if (![TSKPinningValidator handleChallenge:challenge completionHandler:completionHandler]) - { - // TrustKit did not handle this challenge: perhaps it was not for server trust - // or the domain was not pinned. Fall back to the default behavior - completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); - } - } - - @param challenge The authentication challenge, supplied by the URL loading system to the delegate's challenge handler method. - - @param completionHandler A block to invoke to respond to the challenge, supplied by the URL loading system to the delegate's challenge handler method. - - @return `YES` if the challenge was handled and the `completionHandler` was successfuly invoked. `NO` if the challenge could not be handled because it was not for server certificate validation (ie. the challenge's `authenticationMethod` was not `NSURLAuthenticationMethodServerTrust`). - - @exception NSException Thrown when TrustKit has not been initialized with a pinning policy. - */ -+ (BOOL) handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge - completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential * _Nullable credential))completionHandler; -@end diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index beeb068d..64b23e79 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -23,20 +23,6 @@ @interface TSKPinningValidator () @implementation TSKPinningValidator -#pragma mark Class Methods For Shared Singleton - -+ (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname:(NSString * _Nonnull)serverHostname -{ - TrustKit *trustKit = [TrustKit sharedInstance]; - return [trustKit.pinningValidator evaluateTrust:serverTrust forHostname:serverHostname]; -} - -+ (BOOL) handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler -{ - TrustKit *trustKit = [TrustKit sharedInstance]; - return [trustKit.pinningValidator handleChallenge:challenge completionHandler:completionHandler]; -} - #pragma mark Instance Methods - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h index 6c98ac77..88c416fb 100644 --- a/TrustKit/TSKPinningValidatorResult.h +++ b/TrustKit/TSKPinningValidatorResult.h @@ -10,6 +10,7 @@ */ #import "TSKPinValidatorResult.h" +@import Foundation; @interface TSKPinningValidatorResult : NSObject diff --git a/TrustKit/TrustKit-Prefix.pch b/TrustKit/TrustKit-Prefix.pch index 33f1db1b..b0feb885 100644 --- a/TrustKit/TrustKit-Prefix.pch +++ b/TrustKit/TrustKit-Prefix.pch @@ -1,15 +1,10 @@ // Copyright © 2017 TrustKit. All rights reserved. -#import -#import "TSKCommon.h" - -#ifndef PrefixHeader_pch -#define PrefixHeader_pch - #ifdef __OBJC__ // Include any system framework and library headers here that should be included in all compilation units. // You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. +#import #import -#endif +#import "TSKCommon.h" #endif diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index fd21de19..952c2d97 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -10,6 +10,7 @@ */ #import "TSKTrustKitConfig.h" +@import Foundation; @class TSKPinningValidator; @class TSKPinningValidatorResult; diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 47bb8e0c..f8de0fa2 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -90,10 +90,10 @@ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig // Hook network APIs if needed if ([sharedTrustKit.configuration[kTSKSwizzleNetworkDelegates] boolValue]) { // NSURLConnection - [TSKNSURLConnectionDelegateProxy swizzleNSURLConnectionConstructors]; + [TSKNSURLConnectionDelegateProxy swizzleNSURLConnectionConstructors:sharedTrustKit]; // NSURLSession - [TSKNSURLSessionDelegateProxy swizzleNSURLSessionConstructors]; + [TSKNSURLSessionDelegateProxy swizzleNSURLSessionConstructors:sharedTrustKit]; } }); } diff --git a/TrustKit/TrustKit.modulemap b/TrustKit/TrustKit.modulemap index b685e43a..add7afd4 100644 --- a/TrustKit/TrustKit.modulemap +++ b/TrustKit/TrustKit.modulemap @@ -1,6 +1,6 @@ framework module TrustKit { umbrella header "TSKTrustKit-Umbrella.h" - //export * - //module * { export * } + export * + module * { export * } } diff --git a/TrustKit/configuration_utils.h b/TrustKit/configuration_utils.h index 8e9ad4f9..891ba018 100644 --- a/TrustKit/configuration_utils.h +++ b/TrustKit/configuration_utils.h @@ -6,5 +6,6 @@ // Copyright © 2017 TrustKit. All rights reserved. // +@import Foundation; NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *trustKitConfiguration); diff --git a/TrustKit/parse_configuration.h b/TrustKit/parse_configuration.h index 56a94513..633dec36 100644 --- a/TrustKit/parse_configuration.h +++ b/TrustKit/parse_configuration.h @@ -9,6 +9,8 @@ #ifndef parse_configuration_h #define parse_configuration_h +@import Foundation; + NSDictionary *parseTrustKitConfiguration(NSDictionary *TrustKitArguments); #endif /* parse_configuration_h */ diff --git a/TrustKitTests/TSKNSURLConnectionTests.m b/TrustKitTests/TSKNSURLConnectionTests.m index 5d138b38..a0a6e013 100644 --- a/TrustKitTests/TSKNSURLConnectionTests.m +++ b/TrustKitTests/TSKNSURLConnectionTests.m @@ -180,15 +180,15 @@ - (void)performDefaultHandlingForAuthenticationChallenge:(NSURLAuthenticationCha // WARNING 2: If the domain sends a redirection, two pinning validation will occur, thereby setting the // lastTrustDecision to an unexpected value -@interface TSKNSURLConnectionTests : XCTestCase { - -} +@interface TSKNSURLConnectionTests : XCTestCase +@property (nonatomic) TrustKit *trustKit; @end @implementation TSKNSURLConnectionTests - (void)setUp { [super setUp]; + _trustKit = [[TrustKit alloc] initWithConfiguration:@{ }]; } - (void)tearDown { @@ -204,10 +204,12 @@ - (void)test_respondsToSelector_alwaysTrueForWillSendRequest { TSKNSURLConnectionDelegateProxy *proxy; - proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:[TestModeADelegate new]]; + proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:self.trustKit + connectionDelegate:[TestModeADelegate new]]; XCTAssertTrue([proxy respondsToSelector:@selector(connection:willSendRequestForAuthenticationChallenge:)]); - - proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:[TestModeBDelegate new]]; + + proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:self.trustKit + connectionDelegate:[TestModeBDelegate new]]; XCTAssertTrue([proxy respondsToSelector:@selector(connection:willSendRequestForAuthenticationChallenge:)]); } @@ -215,18 +217,21 @@ - (void)respondsToSelector_trueForOriginalMethods { TSKNSURLConnectionDelegateProxy *proxy; - proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:[TestModeADelegate new]]; + proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:self.trustKit + connectionDelegate:[TestModeADelegate new]]; XCTAssertTrue([proxy respondsToSelector:@selector(fakeMethod)]); - - proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:[TestModeBDelegate new]]; + + proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:self.trustKit + connectionDelegate:[TestModeBDelegate new]]; XCTAssertFalse([proxy respondsToSelector:@selector(fakeMethod)]); } - (void)test_respondsToSelector_falseForUnimplementedMethods { TSKNSURLConnectionDelegateProxy *proxy; - - proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:[TestModeADelegate new]]; + + proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:self.trustKit + connectionDelegate:[TestModeADelegate new]]; XCTAssertFalse([proxy respondsToSelector:NSSelectorFromString(@"argle:bargle:")]); } @@ -254,7 +259,8 @@ - (void)test_forwardToOriginalDelegateAuthenticationChallenge_respondsToWillSend TestModeADelegate *delegate = OCMStrictClassMock([TestModeADelegate class]); OCMExpect([delegate connection:cnxn willSendRequestForAuthenticationChallenge:challenge]); - TSKNSURLConnectionDelegateProxy *proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]; + TSKNSURLConnectionDelegateProxy *proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:self.trustKit + connectionDelegate:delegate]; [(id)proxy forwardToOriginalDelegateAuthenticationChallenge:challenge forConnection:cnxn]; OCMVerifyAll((id)delegate); @@ -276,7 +282,8 @@ - (void)test_forwardToOriginalDelegateAuthenticationChallenge_respondsToCanAuthe OCMExpect([delegate connection:cnxn canAuthenticateAgainstProtectionSpace:space]).andReturn(YES); OCMExpect([delegate connection:cnxn didReceiveAuthenticationChallenge:challenge]); - TSKNSURLConnectionDelegateProxy *proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]; + TSKNSURLConnectionDelegateProxy *proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:self.trustKit + connectionDelegate:delegate]; [(id)proxy forwardToOriginalDelegateAuthenticationChallenge:challenge forConnection:cnxn]; OCMVerifyAll((id)delegate); @@ -288,7 +295,8 @@ - (void)test_forwardToOriginalDelegateAuthenticationChallenge_respondsToCanAuthe - (void)test_connectionWillSendRequestForAuthenticationChallenge_notServerTrust { TestModeADelegate *delegate = OCMStrictClassMock([TestModeADelegate class]); - TSKNSURLConnectionDelegateProxy *proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]; + TSKNSURLConnectionDelegateProxy *proxy = [[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:self.trustKit + connectionDelegate:delegate]; NSURLConnection *cnxn = [[NSURLConnection alloc] init]; NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"host" port:0 protocol:nil realm:nil @@ -308,10 +316,9 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_notServerTrust - (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustA_allow { TestModeADelegate *delegate = OCMStrictClassMock([TestModeADelegate class]); - + TSKPinningValidator *validator = OCMStrictClassMock([TSKPinningValidator class]); - [TrustKit initializeWithConfiguration:@{}]; - [TrustKit sharedInstance].pinningValidator = validator; + self.trustKit.pinningValidator = validator; NSURLConnection *cnxn = [[NSURLConnection alloc] init]; NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"hostname" port:0 protocol:nil realm:nil @@ -323,7 +330,8 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustA_al error:nil sender:delegate]; - TSKNSURLConnectionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]); + TSKNSURLConnectionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:self.trustKit + connectionDelegate:delegate]); OCMExpect([validator evaluateTrust:space.serverTrust forHostname:@"hostname"]).andReturn(TSKTrustDecisionShouldAllowConnection); OCMExpect([proxy forwardToOriginalDelegateAuthenticationChallenge:challenge forConnection:cnxn]); @@ -336,7 +344,6 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustA_al OCMVerifyAll((id)validator); OCMVerifyAll((id)proxy); - [TrustKit sharedInstance].pinningValidator = [TSKPinningValidator new]; [(id)validator stopMocking]; [(id)delegate stopMocking]; [(id)proxy stopMocking]; @@ -347,8 +354,7 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustB_al TestModeBDelegate *delegate = OCMPartialMock([TestModeBDelegate new]); TSKPinningValidator *validator = OCMStrictClassMock([TSKPinningValidator class]); - [TrustKit initializeWithConfiguration:@{}]; - [TrustKit sharedInstance].pinningValidator = validator; + self.trustKit.pinningValidator = validator; NSURLConnection *cnxn = [[NSURLConnection alloc] init]; NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"hostname" port:0 protocol:nil realm:nil @@ -360,7 +366,8 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustB_al error:nil sender:delegate]; - TSKNSURLConnectionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]); + TSKNSURLConnectionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:self.trustKit + connectionDelegate:delegate]); OCMExpect([validator evaluateTrust:space.serverTrust forHostname:@"hostname"]).andReturn(TSKTrustDecisionShouldAllowConnection); OCMStub([proxy connection:cnxn willSendRequestForAuthenticationChallenge:challenge]).andForwardToRealObject(); @@ -374,7 +381,6 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustB_al OCMVerifyAll((id)validator); OCMVerifyAll((id)proxy); - [TrustKit sharedInstance].pinningValidator = [TSKPinningValidator new]; [(id)validator stopMocking]; [(id)delegate stopMocking]; [(id)proxy stopMocking]; @@ -386,8 +392,7 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustB_bl TestModeBDelegate *delegate = OCMPartialMock([TestModeBDelegate new]); TSKPinningValidator *validator = OCMStrictClassMock([TSKPinningValidator class]); - [TrustKit initializeWithConfiguration:@{}]; - [TrustKit sharedInstance].pinningValidator = validator; + self.trustKit.pinningValidator = validator; NSURLConnection *cnxn = [[NSURLConnection alloc] init]; NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"hostname" port:0 protocol:nil realm:nil @@ -399,7 +404,8 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustB_bl error:nil sender:delegate]; - TSKNSURLConnectionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLConnectionDelegateProxy alloc] initWithDelegate:delegate]); + TSKNSURLConnectionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLConnectionDelegateProxy alloc] initWithTrustKit:self.trustKit + connectionDelegate:delegate]); OCMExpect([validator evaluateTrust:space.serverTrust forHostname:@"hostname"]).andReturn(TSKTrustDecisionShouldBlockConnection); OCMExpect([delegate cancelAuthenticationChallenge:challenge]); @@ -410,7 +416,6 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustB_bl OCMVerifyAll((id)validator); OCMVerifyAll((id)proxy); - [TrustKit sharedInstance].pinningValidator = [TSKPinningValidator new]; [(id)validator stopMocking]; [(id)delegate stopMocking]; [(id)proxy stopMocking]; diff --git a/TrustKitTests/TSKNSURLSessionTests.m b/TrustKitTests/TSKNSURLSessionTests.m index bc4fa174..d675799b 100644 --- a/TrustKitTests/TSKNSURLSessionTests.m +++ b/TrustKitTests/TSKNSURLSessionTests.m @@ -222,13 +222,14 @@ - (void)fakeMethod { } #pragma mark - Test suite @interface TSKNSURLSessionTests : XCTestCase - +@property (nonatomic) TrustKit *trustKit; @end @implementation TSKNSURLSessionTests - (void)setUp { [super setUp]; + _trustKit = [[TrustKit alloc] initWithConfiguration:@{ }]; } - (void)tearDown { @@ -239,7 +240,8 @@ - (void)tearDown { - (void)test_respondsToSelector_sessionDelegate { - TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:[SessionDelegate new]]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:self.trustKit + sessionDelegate:[SessionDelegate new]]; XCTAssertTrue([proxy respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)]); @@ -252,7 +254,8 @@ - (void)test_respondsToSelector_sessionDelegate - (void)test_respondsToSelector_taskDelegate { - TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:[TaskDelegate new]]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:self.trustKit + sessionDelegate:[TaskDelegate new]]; XCTAssertFalse([proxy respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)]); @@ -265,7 +268,8 @@ - (void)test_respondsToSelector_taskDelegate - (void)test_respondsToSelector_taskAndSessionDelegate { - TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:[TaskAndSessionDelegate new]]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:self.trustKit + sessionDelegate:[TaskAndSessionDelegate new]]; XCTAssertTrue([proxy respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)]); @@ -278,7 +282,8 @@ - (void)test_respondsToSelector_taskAndSessionDelegate - (void)test_respondsToSelector_noOptionalsDelegate { - TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:[NoOptionalsDelegate new]]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:self.trustKit + sessionDelegate:[NoOptionalsDelegate new]]; XCTAssertTrue([proxy respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)]); @@ -294,7 +299,8 @@ - (void)test_respondsToSelector_noOptionalsDelegate // Test session delegate that implements @selector(URLSession:didReceiveChallenge:completionHandler:) - (void)test_forwardToOriginalDelegateAuthenticationChallenge_implements { - TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:[SessionDelegate new]]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:self.trustKit + sessionDelegate:[SessionDelegate new]]; NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; NSURLAuthenticationChallenge *challenge = [NSURLAuthenticationChallenge new]; @@ -313,7 +319,8 @@ - (void)test_forwardToOriginalDelegateAuthenticationChallenge_implements // Test task delegate that doesn't implement @selector(URLSession:didReceiveChallenge:completionHandler:) - (void)test_forwardToOriginalDelegateAuthenticationChallenge_doesNotImplement { - TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:[TaskDelegate new]]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:self.trustKit + sessionDelegate:[TaskDelegate new]]; NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; NSURLAuthenticationChallenge *challenge = [NSURLAuthenticationChallenge new]; @@ -330,7 +337,8 @@ - (void)test_forwardToOriginalDelegateAuthenticationChallenge_doesNotImplement - (void)test_common_URLSession_invalidAuthMethod_session { SessionDelegate *delegate = [SessionDelegate new]; - TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:delegate]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:self.trustKit + sessionDelegate:delegate]; NSURLSession *session = [NSURLSession new]; NSURLAuthenticationChallenge *challenge = ({ @@ -359,7 +367,8 @@ - (void)test_common_URLSession_invalidAuthMethod_session - (void)test_common_URLSession_invalidAuthMethod_task { TaskDelegate *delegate = [TaskDelegate new]; - TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:delegate]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:self.trustKit + sessionDelegate:delegate]; NSURLSession *session = [NSURLSession new]; NSURLAuthenticationChallenge *challenge = ({ @@ -388,7 +397,8 @@ - (void)test_common_URLSession_invalidAuthMethod_task - (void)test_common_URLSession_session_pinFailed { SessionDelegate *delegate = [SessionDelegate new]; - TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:delegate]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:self.trustKit + sessionDelegate:delegate]; NSURLSession *session = [NSURLSession new]; NSURLAuthenticationChallenge *challenge = ({ @@ -406,8 +416,7 @@ - (void)test_common_URLSession_session_pinFailed }); TSKPinningValidator *validator = OCMStrictClassMock([TSKPinningValidator class]); - [TrustKit initializeWithConfiguration:@{}]; - [TrustKit sharedInstance].pinningValidator = validator; + self.trustKit.pinningValidator = validator; OCMExpect([validator evaluateTrust:challenge.protectionSpace.serverTrust forHostname:@"hostname"]).andReturn(TSKTrustDecisionShouldBlockConnection); @@ -420,13 +429,13 @@ - (void)test_common_URLSession_session_pinFailed }]; [(id)validator stopMocking]; - [TrustKit sharedInstance].pinningValidator = [TSKPinningValidator new]; } - (void)test_common_URLSession_session_pinSuccess { SessionDelegate *delegate = [SessionDelegate new]; - TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:delegate]; + TSKNSURLSessionDelegateProxy *proxy = [[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:self.trustKit + sessionDelegate:delegate]; NSURLSession *session = [NSURLSession new]; NSURLAuthenticationChallenge *challenge = ({ @@ -444,8 +453,7 @@ - (void)test_common_URLSession_session_pinSuccess }); TSKPinningValidator *validator = OCMStrictClassMock([TSKPinningValidator class]); - [TrustKit initializeWithConfiguration:@{}]; - [TrustKit sharedInstance].pinningValidator = validator; + self.trustKit.pinningValidator = validator; OCMExpect([validator evaluateTrust:challenge.protectionSpace.serverTrust forHostname:@"hostname"]).andReturn(TSKTrustDecisionShouldAllowConnection); @@ -458,7 +466,6 @@ - (void)test_common_URLSession_session_pinSuccess }]; [(id)validator stopMocking]; - [TrustKit sharedInstance].pinningValidator = [TSKPinningValidator new]; } #pragma mark URLSession:didReceiveChallenge:challenge:completionHandler: @@ -466,7 +473,8 @@ - (void)test_common_URLSession_session_pinSuccess - (void)test_urlSessionChallengeDelegate { SessionDelegate *delegate = [SessionDelegate new]; - TSKNSURLSessionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:delegate]); + TSKNSURLSessionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:self.trustKit + sessionDelegate:delegate]); NSURLSession *session = [NSURLSession new]; NSURLAuthenticationChallenge *challenge = ({ @@ -500,7 +508,8 @@ - (void)test_urlSessionChallengeDelegate - (void)test_urlSessionTaskChallengeDelegate { SessionDelegate *delegate = [SessionDelegate new]; - TSKNSURLSessionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLSessionDelegateProxy alloc] initWithDelegate:delegate]); + TSKNSURLSessionDelegateProxy *proxy = OCMPartialMock([[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:self.trustKit + sessionDelegate:delegate]); NSURLSession *session = [NSURLSession new]; NSURLAuthenticationChallenge *challenge = ({ diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 12b2295c..20b520b5 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -834,7 +834,7 @@ -(void) testHandleChallengeCompletionHandlerDomainNotPinned kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=", // CA Key @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; __block BOOL wasHandlerCalled = NO; void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) @@ -856,7 +856,7 @@ -(void) testHandleChallengeCompletionHandlerDomainNotPinned OCMStub([challengeMock protectionSpace]).andReturn(protectionSpaceMock); // Test the helper method - BOOL wasChallengeHandled = [TSKPinningValidator handleChallenge:challengeMock completionHandler:completionHandler]; + BOOL wasChallengeHandled = [tk.pinningValidator handleChallenge:challengeMock completionHandler:completionHandler]; XCTAssertTrue(wasChallengeHandled); XCTAssertTrue(wasHandlerCalled); @@ -884,6 +884,7 @@ -(void) testHandleChallengeCompletionHandlerPinningFailed kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", //Fake Key @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Fake key ]}}}; + TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() @@ -1002,7 +1003,7 @@ -(void) testHandleChallengeCompletionHandlerNotServerTrustAuthenticationMethod kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=", // CA Key @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Fake key ]}}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; __block BOOL wasHandlerCalled = NO; void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) @@ -1023,7 +1024,7 @@ -(void) testHandleChallengeCompletionHandlerNotServerTrustAuthenticationMethod OCMStub([challengeMock protectionSpace]).andReturn(protectionSpaceMock); // Test the helper method - BOOL wasChallengeHandled = [TSKPinningValidator handleChallenge:challengeMock completionHandler:completionHandler]; + BOOL wasChallengeHandled = [tk.pinningValidator handleChallenge:challengeMock completionHandler:completionHandler]; XCTAssertFalse(wasChallengeHandled); XCTAssertFalse(wasHandlerCalled); @@ -1056,9 +1057,9 @@ - (void)testExcludedSubdomain }}; // Then test TSKPinningValidator - [TrustKit initializeWithConfiguration:trustKitConfig]; + TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; - XCTAssertEqual([TSKPinningValidator evaluateTrust:trust forHostname:@"unsecured.good.com"], + XCTAssertEqual([tk.pinningValidator evaluateTrust:trust forHostname:@"unsecured.good.com"], TSKTrustDecisionDomainNotPinned); CFRelease(trust); From 386562ab48f39185249dad3455bfab6819623f31 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 6 Apr 2017 18:20:40 -0400 Subject: [PATCH 012/126] Removed the damn PCH file --- TrustKit.xcodeproj/project.pbxproj | 10 ++-------- TrustKit/Dependencies/RSSwizzle/RSSwizzle.h | 2 ++ TrustKit/Pinning/TSKPublicKeyAlgorithm.h | 2 ++ TrustKit/Pinning/TSKSPKIHashCache.m | 1 + TrustKit/Pinning/ssl_pin_verifier.m | 1 + TrustKit/Reporting/TSKBackgroundReporter.m | 2 ++ TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h | 2 ++ TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m | 1 + TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m | 1 + TrustKit/{TSKCommon.h => TSKLog.h} | 0 TrustKit/TSKPinningValidator.m | 1 + TrustKit/TSKTrustKitConfig.h | 5 +---- TrustKit/TrustKit-Prefix.pch | 10 ---------- TrustKit/TrustKit.m | 2 +- TrustKit/configuration_utils.m | 1 + 15 files changed, 18 insertions(+), 23 deletions(-) rename TrustKit/{TSKCommon.h => TSKLog.h} (100%) delete mode 100644 TrustKit/TrustKit-Prefix.pch diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 2ae840d3..dca1a0d1 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -381,7 +381,6 @@ 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ssl_pin_verifier.m; path = Pinning/ssl_pin_verifier.m; sourceTree = ""; }; 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ssl_pin_verifier.h; path = Pinning/ssl_pin_verifier.h; sourceTree = ""; }; 8CE919291AEA0F7E002B29AE /* domain_registry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = domain_registry.h; path = Dependencies/domain_registry/domain_registry.h; sourceTree = ""; }; - FC06FE7F1E7C3FFA00AF0B4D /* TrustKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TrustKit-Prefix.pch"; sourceTree = ""; }; FC1A08FD1E579F630055B12C /* TSKPinValidatorResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinValidatorResult.h; sourceTree = ""; }; FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; @@ -390,7 +389,7 @@ FC1A09121E57C6820055B12C /* TSKPublicKeyAlgorithm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TSKPublicKeyAlgorithm.h; path = Pinning/TSKPublicKeyAlgorithm.h; sourceTree = ""; }; FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKReportsRateLimiterTests.m; sourceTree = ""; }; FC4CAC7E1E96917B00DAC41E /* TSKTrustKitConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustKitConfig.h; sourceTree = ""; }; - FC4CAC7F1E96A2D600DAC41E /* TSKCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKCommon.h; sourceTree = ""; }; + FC4CAC7F1E96A2D600DAC41E /* TSKLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKLog.h; sourceTree = ""; }; FC4CAC801E96A95D00DAC41E /* TSKTrustKit-Umbrella.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TSKTrustKit-Umbrella.h"; sourceTree = ""; }; FCD0CC031E96ADF00076D431 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; /* End PBXFileReference section */ @@ -542,7 +541,7 @@ FC663F551E8421BE008E2EFE /* Configuration */, 8C84804C1A896EE30017C155 /* TrustKit.h */, 8C84806C1A896F660017C155 /* TrustKit.m */, - FC4CAC7F1E96A2D600DAC41E /* TSKCommon.h */, + FC4CAC7F1E96A2D600DAC41E /* TSKLog.h */, FC4CAC7E1E96917B00DAC41E /* TSKTrustKitConfig.h */, 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */, 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */, @@ -560,7 +559,6 @@ 8C0C90111E3C196A003851A8 /* TrustKit.modulemap */, 8C84804B1A896EE30017C155 /* Info.plist */, FC4CAC801E96A95D00DAC41E /* TSKTrustKit-Umbrella.h */, - FC06FE7F1E7C3FFA00AF0B4D /* TrustKit-Prefix.pch */, ); name = "Supporting Files"; sourceTree = ""; @@ -1479,8 +1477,6 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "TrustKit/TrustKit-Prefix.pch"; GENERATE_PKGINFO_FILE = YES; INFOPLIST_FILE = TrustKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -1511,8 +1507,6 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "TrustKit/TrustKit-Prefix.pch"; GENERATE_PKGINFO_FILE = YES; INFOPLIST_FILE = TrustKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; diff --git a/TrustKit/Dependencies/RSSwizzle/RSSwizzle.h b/TrustKit/Dependencies/RSSwizzle/RSSwizzle.h index 2c85155e..885cc588 100755 --- a/TrustKit/Dependencies/RSSwizzle/RSSwizzle.h +++ b/TrustKit/Dependencies/RSSwizzle/RSSwizzle.h @@ -6,6 +6,8 @@ // // +@import Foundation; + #pragma mark - Macros Based API /// A macro for wrapping the return type of the swizzled method. diff --git a/TrustKit/Pinning/TSKPublicKeyAlgorithm.h b/TrustKit/Pinning/TSKPublicKeyAlgorithm.h index 1bf43b87..d806a57d 100644 --- a/TrustKit/Pinning/TSKPublicKeyAlgorithm.h +++ b/TrustKit/Pinning/TSKPublicKeyAlgorithm.h @@ -12,6 +12,8 @@ #ifndef TSKPublicKeyAlgorithm_h #define TSKPublicKeyAlgorithm_h +@import Foundation; + typedef NS_ENUM(NSInteger, TSKPublicKeyAlgorithm) { // Some assumptions are made about this specific ordering in public_key_utils.m diff --git a/TrustKit/Pinning/TSKSPKIHashCache.m b/TrustKit/Pinning/TSKSPKIHashCache.m index 9154e321..c55b3304 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -10,6 +10,7 @@ */ #import "TSKSPKIHashCache.h" +#import "../TSKLog.h" #import #if TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED <= 100000 diff --git a/TrustKit/Pinning/ssl_pin_verifier.m b/TrustKit/Pinning/ssl_pin_verifier.m index 2435c321..5470ee10 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.m +++ b/TrustKit/Pinning/ssl_pin_verifier.m @@ -13,6 +13,7 @@ #import "TSKSPKIHashCache.h" #import "../Dependencies/domain_registry/domain_registry.h" #import "../configuration_utils.h" +#import "../TSKLog.h" #pragma mark SSL Pin Verifier diff --git a/TrustKit/Reporting/TSKBackgroundReporter.m b/TrustKit/Reporting/TSKBackgroundReporter.m index 5e3f977f..203f7159 100644 --- a/TrustKit/Reporting/TSKBackgroundReporter.m +++ b/TrustKit/Reporting/TSKBackgroundReporter.m @@ -11,11 +11,13 @@ #import "TSKBackgroundReporter.h" #import "../TSKTrustKitConfig.h" +#import "../TSKLog.h" #import "TSKPinFailureReport.h" #import "reporting_utils.h" #import "TSKReportsRateLimiter.h" #import "vendor_identifier.h" + // Session identifier for background uploads: .TSKBackgroundReporter static NSString * const kTSKBackgroundSessionIdentifierFormat = @"%@.TSKBackgroundReporter.%@"; diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h index 1c07809d..f40e70d6 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h @@ -6,6 +6,8 @@ // Copyright © 2015 TrustKit. All rights reserved. // +@import Foundation; + NS_ASSUME_NONNULL_BEGIN @class TrustKit; diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m index 3c3cb162..1228d5a7 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m @@ -8,6 +8,7 @@ #import "TSKNSURLConnectionDelegateProxy.h" #import "../TrustKit.h" +#import "../TSKLog.h" #import "../TSKPinValidatorResult.h" #import "../TSKPinningValidator.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m index 850efdd4..c447c19c 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m @@ -8,6 +8,7 @@ #import "TSKNSURLSessionDelegateProxy.h" #import "../TrustKit.h" +#import "../TSKLog.h" #import "../TSKPinValidatorResult.h" #import "../TSKPinningValidator.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" diff --git a/TrustKit/TSKCommon.h b/TrustKit/TSKLog.h similarity index 100% rename from TrustKit/TSKCommon.h rename to TrustKit/TSKLog.h diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index 64b23e79..90ffdbe1 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -16,6 +16,7 @@ #import "Pinning/TSKSPKIHashCache.h" #import "Pinning/ssl_pin_verifier.h" #import "TrustKit.h" +#import "TSKLog.h" @interface TSKPinningValidator () @property (nonatomic) TSKSPKIHashCache *spkiHashCache; diff --git a/TrustKit/TSKTrustKitConfig.h b/TrustKit/TSKTrustKitConfig.h index fd3cefa0..f7b4e0bf 100644 --- a/TrustKit/TSKTrustKitConfig.h +++ b/TrustKit/TSKTrustKitConfig.h @@ -9,8 +9,7 @@ // These externs are currently fulfilled in TrustKit.m. They are here to prevent reverse // includes since both TrustKit instances and low level internal C API need the definitions -#ifndef TSKTrustKitConfig_h -#define TSKTrustKitConfig_h +@import Foundation; #pragma mark TrustKit Version Number @@ -242,5 +241,3 @@ FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp256r1; ECDSA with secp384r1 curve. */ FOUNDATION_EXPORT const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp384r1; - -#endif /* TSKTrustKitConfig_h */ diff --git a/TrustKit/TrustKit-Prefix.pch b/TrustKit/TrustKit-Prefix.pch deleted file mode 100644 index b0feb885..00000000 --- a/TrustKit/TrustKit-Prefix.pch +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © 2017 TrustKit. All rights reserved. - -#ifdef __OBJC__ -// Include any system framework and library headers here that should be included in all compilation units. -// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. -#import -#import -#import "TSKCommon.h" - -#endif diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index f8de0fa2..5069e2ef 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -16,7 +16,7 @@ #import "parse_configuration.h" #import "TSKPinningValidator.h" #import "TSKPinningValidatorResult.h" - +#import "TSKLog.h" NSString * const TrustKitVersion = @"1.4.2"; diff --git a/TrustKit/configuration_utils.m b/TrustKit/configuration_utils.m index dac7e093..61e690b5 100644 --- a/TrustKit/configuration_utils.m +++ b/TrustKit/configuration_utils.m @@ -9,6 +9,7 @@ #import "configuration_utils.h" #import "TSKTrustKitConfig.h" #import "Dependencies/domain_registry/domain_registry.h" +#import "TSKLog.h" static BOOL isSubdomain(NSString *domain, NSString *subdomain) From c972b130acdac1ba1c8e082908046425cfdac797 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Fri, 7 Apr 2017 13:29:22 -0400 Subject: [PATCH 013/126] Hash cache now requires a unique label for the cache files. Prevents multiple instances from stepping on each other. --- TrustKit.xcodeproj/project.pbxproj | 8 +++--- TrustKit/Pinning/TSKSPKIHashCache.h | 18 ++++++++++++ TrustKit/Pinning/TSKSPKIHashCache.m | 28 +++++++++--------- TrustKit/TSKPinningValidator.h | 9 +++--- TrustKit/TSKPinningValidator.m | 7 +++-- TrustKit/TrustKit.h | 7 +++-- TrustKit/TrustKit.m | 10 +++++-- TrustKitTests/TSKNSURLConnectionTests.m | 2 +- TrustKitTests/TSKNSURLSessionTests.m | 2 +- TrustKitTests/TSKPinningValidatorTests.m | 33 ++++++++++++++++------ TrustKitTests/TSKPublicKeyAlgorithmTests.m | 3 +- TrustKitTests/TSKReporterTests.m | 2 +- 12 files changed, 87 insertions(+), 42 deletions(-) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index dca1a0d1..c8539b12 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -312,13 +312,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 2FA2868CAFECA46ADE0B6E3E /* TSKPinningValidatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorTests.m; sourceTree = ""; }; + 2FA2868CAFECA46ADE0B6E3E /* TSKPinningValidatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = TSKPinningValidatorTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 6B032D3F1AF1AEB600EAFA69 /* TSKReporterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TSKReporterTests.m; sourceTree = ""; }; 6B2B06AC1B05154A00FC749E /* TSKBackgroundReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKBackgroundReporter.h; path = Reporting/TSKBackgroundReporter.h; sourceTree = ""; }; 6B2B06AE1B05157400FC749E /* TSKBackgroundReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKBackgroundReporter.m; path = Reporting/TSKBackgroundReporter.m; sourceTree = ""; }; 8C0C90111E3C196A003851A8 /* TrustKit.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = TrustKit.modulemap; sourceTree = ""; }; - 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidator.h; sourceTree = ""; }; - 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidator.m; sourceTree = ""; }; + 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = TSKPinningValidator.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = TSKPinningValidator.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 8C15F99E1B16094D00F06C0E /* TSKPinFailureReport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinFailureReport.h; path = Reporting/TSKPinFailureReport.h; sourceTree = ""; }; 8C15F99F1B16094D00F06C0E /* TSKPinFailureReport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKPinFailureReport.m; path = Reporting/TSKPinFailureReport.m; sourceTree = ""; }; 8C15F9A31B17564400F06C0E /* TSKPinConfigurationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinConfigurationTests.m; sourceTree = ""; }; @@ -333,7 +333,7 @@ 8C84804C1A896EE30017C155 /* TrustKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TrustKit.h; sourceTree = ""; }; 8C8480521A896EE30017C155 /* TrustKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TrustKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8C8480581A896EE30017C155 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 8C84806C1A896F660017C155 /* TrustKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TrustKit.m; sourceTree = ""; }; + 8C84806C1A896F660017C155 /* TrustKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = TrustKit.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 8C84CBB21D6E0981009B3E7D /* TrustKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TrustKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C84CBDD1D6E1718009B3E7D /* TrustKit tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "TrustKit tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 8C84CC071D6E3C67009B3E7D /* vendor_identifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vendor_identifier.h; path = Reporting/vendor_identifier.h; sourceTree = ""; }; diff --git a/TrustKit/Pinning/TSKSPKIHashCache.h b/TrustKit/Pinning/TSKSPKIHashCache.h index bbb345f9..a47a7bf4 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.h +++ b/TrustKit/Pinning/TSKSPKIHashCache.h @@ -14,14 +14,32 @@ @import Foundation; @import Security; +NS_ASSUME_NONNULL_BEGIN + // Each key is a raw certificate data (for easy lookup) and each value is the certificate's raw SPKI data typedef NSMutableDictionary SpkiCacheDictionnary; @interface TSKSPKIHashCache : NSObject +- (instancetype)new NS_UNAVAILABLE; +- (instancetype)init NS_UNAVAILABLE; + +/** + Create a new cache of SPKI hashes. The identifier is required to ensure that multiple cache + instances do not attempt to use the same file on disk for persistence. If nil, persistence + will be disabled (not recommended). + + @param uniqueIdentifier A unique identifier that is stable across app launches/instance creation + @return An initialized hash cache. + */ +- (instancetype _Nullable)initWithIdentifier:(NSString * _Nullable)uniqueIdentifier NS_DESIGNATED_INITIALIZER; + - (NSData *)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certificate publicKeyAlgorithm:(TSKPublicKeyAlgorithm)publicKeyAlgorithm; - (NSMutableDictionary *)getSpkiCache; + - (NSMutableDictionary *)getSpkiCacheFromFileSystem; @end + +NS_ASSUME_NONNULL_END diff --git a/TrustKit/Pinning/TSKSPKIHashCache.m b/TrustKit/Pinning/TSKSPKIHashCache.m index c55b3304..e822b5ee 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -94,16 +94,18 @@ - (NSData *)getPublicKeyDataFromCertificate_unified:(SecCertificateRef)certifica @implementation TSKSPKIHashCache -- (instancetype)init +- (instancetype)initWithIdentifier:(NSString *)uniqueIdentifier { self = [super init]; if (self) { - // Initialize our cache of SPKI hashes + // Initialize our locks + _lockQueue = dispatch_queue_create("TSKSPKIHashLock", DISPATCH_QUEUE_CONCURRENT); + + _spkiCacheFilename = uniqueIdentifier; // if this value is nil, persistence will always fail. + // First try to load a cached version from the filesystem - _spkiCacheFilename = @"spki-hash.cache"; _subjectPublicKeyInfoHashesCache = [self getSpkiCacheFromFileSystem]; TSKLog(@"Loaded %lu SPKI cache entries from the filesystem", (unsigned long)_subjectPublicKeyInfoHashesCache.count); - if (_subjectPublicKeyInfoHashesCache == nil) { _subjectPublicKeyInfoHashesCache = [NSMutableDictionary new]; @@ -120,9 +122,6 @@ - (instancetype)init } - // Initialize our locks - _lockQueue = dispatch_queue_create("TSKSPKIHashLock", DISPATCH_QUEUE_CONCURRENT); - #if LEGACY_IOS_KEY_EXTRACTION _keychainQueue = dispatch_queue_create("TSKSPKIKeychainLock", DISPATCH_QUEUE_SERIAL); // Cleanup the Keychain in case the App previously crashed @@ -187,12 +186,15 @@ - (NSData *)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certifica }); // Update the cache on the filesystem - NSString *spkiCachePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] - stringByAppendingPathComponent:self.spkiCacheFilename]; - NSData *serializedSpkiCache = [NSKeyedArchiver archivedDataWithRootObject:_subjectPublicKeyInfoHashesCache]; - if ([serializedSpkiCache writeToFile:spkiCachePath atomically:YES] == NO) - { - TSKLog(@"Could not persist SPKI cache to the filesystem"); + if (self.spkiCacheFilename.length > 0) { + NSURL *cachesDirUrl = [[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] firstObject]; + NSURL *cacheUrl = [cachesDirUrl URLByAppendingPathComponent:self.spkiCacheFilename]; + NSData *serializedSpkiCache = [NSKeyedArchiver archivedDataWithRootObject:_subjectPublicKeyInfoHashesCache]; + if ([serializedSpkiCache writeToURL:cacheUrl atomically:YES] == NO) + { + NSAssert(false, @"Failed to write cache"); + TSKLog(@"Could not persist SPKI cache to the filesystem"); + } } return subjectPublicKeyInfoHash; diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 28daa2ab..4c3cd014 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -47,6 +47,7 @@ typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef cert @property (nonatomic, readonly, nonnull) void(^validationResultHandler)(TSKPinningValidatorResult * _Nonnull result); - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains + identifier:(NSString *_Nullable)name ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler; @@ -68,7 +69,7 @@ typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef cert @exception NSException Thrown when TrustKit has not been initialized with a pinning policy. */ -- (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname:(NSString * _Nonnull)serverHostname; +- (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname:(NSString * _Nonnull)serverHostname; /** @@ -95,8 +96,8 @@ typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef cert @exception NSException Thrown when TrustKit has not been initialized with a pinning policy. */ -- (BOOL) handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge - completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential * _Nullable credential))completionHandler; +- (BOOL)handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge + completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential * _Nullable credential))completionHandler; @end diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index 90ffdbe1..dc015b79 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -27,6 +27,7 @@ @implementation TSKPinningValidator #pragma mark Instance Methods - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains + identifier:(NSString *)name ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler @@ -37,12 +38,12 @@ - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)p _ignorePinsForUserTrustAnchors = ignorePinsForUserTrustAnchors; _validationResultQueue = validationResultQueue; _validationResultHandler = validationResultHandler; - _spkiHashCache = [TSKSPKIHashCache new]; + _spkiHashCache = [[TSKSPKIHashCache alloc] initWithIdentifier:name]; } return self; } -- (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname:(NSString * _Nonnull)serverHostname +- (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname:(NSString * _Nonnull)serverHostname { TSKTrustDecision finalTrustDecision = TSKTrustDecisionShouldBlockConnection; @@ -149,7 +150,7 @@ - (TSKTrustDecision) evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname } -- (BOOL) handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler +- (BOOL)handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler { BOOL wasChallengeHandled = NO; if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 952c2d97..c082ac94 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -153,9 +153,12 @@ NS_ASSUME_NONNULL_BEGIN initialized, notifications will be posted for any SSL pinning validation performed. @param trustKitConfig A dictionary containing various keys for configuring the global - SSL pinning policy. + SSL pinning policy. + @param uniqueIdentifier An identifier for this instance. It is required if you want the + pin to be persisted to disk. */ -- (instancetype)initWithConfiguration:(NSDictionary * _Nullable)trustKitConfig; +- (instancetype)initWithConfiguration:(NSDictionary * _Nullable)trustKitConfig + identifier:(NSString * _Nullable)uniqueIdentifier; /** Retrieve the SSL pinning policy for this instance. diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 5069e2ef..93882960 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -58,6 +58,9 @@ // Email info@datatheorem.com if you need a free dashboard to see your App's reports static NSString * const kTSKDefaultReportUri = @"https://overmind.datatheorem.com/trustkit/report"; +// Internal SPKI hash cache file name – we'll use as identifier +static NSString * const kTSKSharedInstanceIdentifier = @"spki-hash.cache"; + #pragma mark TrustKit Initialization Helper Functions @interface TrustKit () @@ -85,7 +88,8 @@ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - sharedTrustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + sharedTrustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig + identifier:kTSKSharedInstanceIdentifier]; // Hook network APIs if needed if ([sharedTrustKit.configuration[kTSKSwizzleNetworkDelegates] boolValue]) { @@ -106,7 +110,7 @@ + (NSDictionary * _Nullable)configuration #pragma mark Instance -- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig +- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig identifier:(NSString *)uniqueIdentifier { NSParameterAssert(trustKitConfig); if (!trustKitConfig) { @@ -137,6 +141,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo #endif __weak typeof(self) weakSelf = self; _pinningValidator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:_configuration + identifier:uniqueIdentifier ignorePinsForUserTrustAnchors:userTrustAnchorBypass validationResultQueue:_pinFailureReporterQueue validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { @@ -214,7 +219,6 @@ + (void)resetConfiguration //sharedTrustKitOnceToken = 0; // Reset is only available/used for tests //resetSubjectPublicKeyInfoCache(); - //_spkiHashCache = [TSKSPKIHashCache new]; //_configuration = nil; // _isTrustKitInitialized = NO; } diff --git a/TrustKitTests/TSKNSURLConnectionTests.m b/TrustKitTests/TSKNSURLConnectionTests.m index a0a6e013..3f4f39b1 100644 --- a/TrustKitTests/TSKNSURLConnectionTests.m +++ b/TrustKitTests/TSKNSURLConnectionTests.m @@ -188,7 +188,7 @@ @implementation TSKNSURLConnectionTests - (void)setUp { [super setUp]; - _trustKit = [[TrustKit alloc] initWithConfiguration:@{ }]; + _trustKit = [[TrustKit alloc] initWithConfiguration:@{ } identifier:nil]; } - (void)tearDown { diff --git a/TrustKitTests/TSKNSURLSessionTests.m b/TrustKitTests/TSKNSURLSessionTests.m index d675799b..a9536822 100644 --- a/TrustKitTests/TSKNSURLSessionTests.m +++ b/TrustKitTests/TSKNSURLSessionTests.m @@ -229,7 +229,7 @@ @implementation TSKNSURLSessionTests - (void)setUp { [super setUp]; - _trustKit = [[TrustKit alloc] initWithConfiguration:@{ }]; + _trustKit = [[TrustKit alloc] initWithConfiguration:@{ } identifier:nil]; } - (void)tearDown { diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 20b520b5..48d170fc 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -63,8 +63,8 @@ - (void)setUp _selfSignedCertificate = [TSKCertificateUtils createCertificateFromDer:@"www.good.com.selfsigned"]; _globalsignRootCertificate = [TSKCertificateUtils createCertificateFromDer:@"GlobalSignRootCA"]; - spkiCache = [TSKSPKIHashCache new]; [spkiCache resetSubjectPublicKeyInfoDiskCache]; + spkiCache = [[TSKSPKIHashCache alloc] initWithIdentifier:@"test"]; } @@ -82,6 +82,7 @@ - (void)tearDown _selfSignedCertificate = nil; _globalsignRootCertificate = nil; + [spkiCache resetSubjectPublicKeyInfoDiskCache]; spkiCache = nil; [super tearDown]; } @@ -126,10 +127,11 @@ - (void)testVerifyAgainstAnyPublicKey XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); - + XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -202,6 +204,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -268,6 +271,7 @@ - (void)testVerifyAgainstCAPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -330,6 +334,7 @@ - (void)testVerifyAgainstLeafPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -392,6 +397,7 @@ - (void)testVerifyAgainstBadPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -443,6 +449,7 @@ - (void)testVerifyAgainstBadPublicKeyPinsExpired // Test TSKPinningValidator TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -493,6 +500,7 @@ - (void)testVerifyAgainstBadPublicKeyPinningNotEnforced XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -557,6 +565,7 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -621,6 +630,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -686,6 +696,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -705,7 +716,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname // Ensure a validation notification was posted [self waitForExpectationsWithTimeout:2.0 handler:nil]; - + CFRelease(trust); } @@ -750,6 +761,7 @@ - (void)testVerifyAgainstInjectedCaPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -769,7 +781,7 @@ - (void)testVerifyAgainstInjectedCaPublicKey // Ensure a validation notification was posted [self waitForExpectationsWithTimeout:2.0 handler:nil]; - + CFRelease(trust); } @@ -798,6 +810,7 @@ - (void)testDomainNotPinned // Then test TSKPinningValidator TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -834,7 +847,7 @@ -(void) testHandleChallengeCompletionHandlerDomainNotPinned kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=", // CA Key @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}; - TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:nil]; __block BOOL wasHandlerCalled = NO; void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) @@ -857,7 +870,7 @@ -(void) testHandleChallengeCompletionHandlerDomainNotPinned // Test the helper method BOOL wasChallengeHandled = [tk.pinningValidator handleChallenge:challengeMock completionHandler:completionHandler]; - + XCTAssertTrue(wasChallengeHandled); XCTAssertTrue(wasHandlerCalled); @@ -886,6 +899,7 @@ -(void) testHandleChallengeCompletionHandlerPinningFailed ]}}}; TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { @@ -942,6 +956,7 @@ -(void) testHandleChallengeCompletionHandlerPinningSuccessful ]}}}; TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig + identifier:nil ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { @@ -1003,7 +1018,7 @@ -(void) testHandleChallengeCompletionHandlerNotServerTrustAuthenticationMethod kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=", // CA Key @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Fake key ]}}}; - TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:nil]; __block BOOL wasHandlerCalled = NO; void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) @@ -1055,9 +1070,9 @@ - (void)testExcludedSubdomain kTSKExcludeSubdomainFromParentPolicy: @YES } }}; - + // Then test TSKPinningValidator - TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:nil]; XCTAssertEqual([tk.pinningValidator evaluateTrust:trust forHostname:@"unsecured.good.com"], TSKTrustDecisionDomainNotPinned); diff --git a/TrustKitTests/TSKPublicKeyAlgorithmTests.m b/TrustKitTests/TSKPublicKeyAlgorithmTests.m index 259fab42..fe392b56 100644 --- a/TrustKitTests/TSKPublicKeyAlgorithmTests.m +++ b/TrustKitTests/TSKPublicKeyAlgorithmTests.m @@ -36,12 +36,13 @@ @implementation TSKPublicKeyAlgorithmTests - (void)setUp { [super setUp]; - spkiCache = [TSKSPKIHashCache new]; [spkiCache resetSubjectPublicKeyInfoDiskCache]; + spkiCache = [[TSKSPKIHashCache alloc] initWithIdentifier:@"test"]; } - (void)tearDown { + [spkiCache resetSubjectPublicKeyInfoDiskCache]; spkiCache = nil; [super tearDown]; } diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index cbba0ec3..2ac4a934 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -93,7 +93,7 @@ - (void)testSendReportFromValidationReport @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Fake key 2 ]}}}; - _trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + _trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:nil]; // Expect a report to be sent out when a notification is posted NSSet *knownPins = [NSSet setWithArray:@[[[NSData alloc]initWithBase64EncodedString:@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" From 9e462f1a9e798fec57993363a36be9a92058e0b1 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Fri, 7 Apr 2017 14:06:06 -0400 Subject: [PATCH 014/126] Remove logger test file which was commented out --- TrustKit.xcodeproj/project.pbxproj | 17 +++++---- TrustKitTests/TSKLoggerTests.m | 56 ------------------------------ 2 files changed, 8 insertions(+), 65 deletions(-) delete mode 100644 TrustKitTests/TSKLoggerTests.m diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index c8539b12..8b3cc612 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -38,9 +38,6 @@ 8C5D98B31CEFF079008E654B /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C5D98B21CEFF079008E654B /* parse_configuration.m */; }; 8C5D98B41CEFF079008E654B /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C5D98B21CEFF079008E654B /* parse_configuration.m */; }; 8C5D98B51CEFF079008E654B /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C5D98B21CEFF079008E654B /* parse_configuration.m */; }; - 8C80249D1D750D0100678959 /* TSKLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C80249C1D750D0100678959 /* TSKLoggerTests.m */; }; - 8C80249E1D750D0100678959 /* TSKLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C80249C1D750D0100678959 /* TSKLoggerTests.m */; }; - 8C80249F1D750D0100678959 /* TSKLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C80249C1D750D0100678959 /* TSKLoggerTests.m */; }; 8C84804D1A896EE30017C155 /* TrustKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C84804C1A896EE30017C155 /* TrustKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8C84806D1A896F660017C155 /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C84806C1A896F660017C155 /* TrustKit.m */; }; 8C84CB911D6E0981009B3E7D /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */; }; @@ -327,7 +324,6 @@ 8C5AB4671CF26A2900234B30 /* OCMock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OCMock.framework; path = Dependencies/OCMock/iOS/OCMock.framework; sourceTree = ""; }; 8C5D98B21CEFF079008E654B /* parse_configuration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = parse_configuration.m; sourceTree = ""; }; 8C5D98B61CEFF103008E654B /* parse_configuration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = parse_configuration.h; sourceTree = ""; }; - 8C80249C1D750D0100678959 /* TSKLoggerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKLoggerTests.m; sourceTree = ""; }; 8C8480471A896EE30017C155 /* TrustKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TrustKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C84804B1A896EE30017C155 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8C84804C1A896EE30017C155 /* TrustKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TrustKit.h; sourceTree = ""; }; @@ -577,7 +573,6 @@ 8CC78B1E1B1B586F00523A25 /* TSKCertificateUtils.h */, 8CC78B1F1B1B586F00523A25 /* TSKCertificateUtils.m */, 8CC78B241B1B616500523A25 /* TSKPublicKeyAlgorithmTests.m */, - 8C80249C1D750D0100678959 /* TSKLoggerTests.m */, ); path = TrustKitTests; sourceTree = ""; @@ -1176,7 +1171,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8C80249D1D750D0100678959 /* TSKLoggerTests.m in Sources */, 8CD5F7381BCB02A7005801D8 /* TSKNSURLConnectionTests.m in Sources */, 8C15F9A41B17564400F06C0E /* TSKPinConfigurationTests.m in Sources */, 075AA1091AC985FD00178223 /* TSKPinningValidatorTests.m in Sources */, @@ -1218,7 +1212,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8C80249F1D750D0100678959 /* TSKLoggerTests.m in Sources */, 8C84CBC31D6E1718009B3E7D /* TSKNSURLConnectionTests.m in Sources */, FC4CAC7D1E96891B00DAC41E /* TSKReportsRateLimiterTests.m in Sources */, 8C84CBC41D6E1718009B3E7D /* TSKPinConfigurationTests.m in Sources */, @@ -1284,7 +1277,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8C80249E1D750D0100678959 /* TSKLoggerTests.m in Sources */, 8CD5F7391BCB02A7005801D8 /* TSKNSURLConnectionTests.m in Sources */, FC4CAC7C1E96891A00DAC41E /* TSKReportsRateLimiterTests.m in Sources */, 8CA6CC3A1BAE2C7C00BDA419 /* TSKReporterTests.m in Sources */, @@ -1374,7 +1366,6 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -1477,6 +1468,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; GENERATE_PKGINFO_FILE = YES; INFOPLIST_FILE = TrustKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -1507,6 +1499,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; GENERATE_PKGINFO_FILE = YES; INFOPLIST_FILE = TrustKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -1593,6 +1586,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; @@ -1624,6 +1618,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; @@ -1754,6 +1749,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = NO; FRAMEWORK_VERSION = A; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -1782,6 +1778,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = NO; FRAMEWORK_VERSION = A; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -1859,6 +1856,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; @@ -1890,6 +1888,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; diff --git a/TrustKitTests/TSKLoggerTests.m b/TrustKitTests/TSKLoggerTests.m deleted file mode 100644 index 7bf51540..00000000 --- a/TrustKitTests/TSKLoggerTests.m +++ /dev/null @@ -1,56 +0,0 @@ -// -// TSKLoggerTests.m -// TrustKit -// -// Created by Alban Diquet on 8/29/16. -// Copyright © 2016 TrustKit. All rights reserved. -// - -//#import -//#import "../TrustKit/TrustKit.h" -// -// -//extern void _TSKLog(NSString *format, ...); -// -//@interface TSKLoggerTests : XCTestCase -// -//@end -// -//@implementation TSKLoggerTests -// -//- (void)testDefaultLoggerBlock -//{ -// _TSKLog(@"test %@", @"test"); -//} -// -// -//- (void)testSetLoggerBlock -//{ -// __block bool wasBlockCalled = false; -// void (^loggerBlock)(NSString *) = ^void(NSString *message) -// { -// XCTAssert(message, @"test test"); -// wasBlockCalled = true; -// }; -// -// TrustKit *tk = [TrustKit new]; -// tk.loggerBlock = loggerBlock; -// TSKLog(@"test %@", @"test"); -// XCTAssertTrue(wasBlockCalled); -//} -// -//- (void)testSetLoggerBlock_singleton -//{ -// __block bool wasBlockCalled = false; -// void (^loggerBlock)(NSString *) = ^void(NSString *message) -// { -// XCTAssert(message, @"test test"); -// wasBlockCalled = true; -// }; -// -// [TrustKit setLoggerBlock:loggerBlock]; -// TSKLog(@"test %@", @"test"); -// XCTAssertTrue(wasBlockCalled); -//} -// -//@end From f2f15c96e7b3e69ca7384ed4c9ad76fd0be9d04a Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Tue, 11 Apr 2017 10:56:05 -0400 Subject: [PATCH 015/126] Add some initializer annotations --- TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h | 4 +++- TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h index f40e70d6..68ef9465 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h @@ -17,7 +17,9 @@ NS_ASSUME_NONNULL_BEGIN // Initalize our hooks + (void)swizzleNSURLConnectionConstructors:(TrustKit *)trustKit; -- (instancetype _Nullable)initWithTrustKit:(TrustKit *)trustKit connectionDelegate:(id _Nullable)delegate; +- (instancetype)init NS_UNAVAILABLE; + +- (instancetype _Nullable)initWithTrustKit:(TrustKit *)trustKit connectionDelegate:(id _Nullable)delegate NS_DESIGNATED_INITIALIZER; - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h index 8cb2a22d..7bf1b3dc 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h @@ -18,7 +18,9 @@ typedef void(^TSKURLSessionAuthChallengeCallback)(NSURLSessionAuthChallengeDispo + (void)swizzleNSURLSessionConstructors:(TrustKit *)trustKit; -- (instancetype _Nullable)initWithTrustKit:(TrustKit *)trustKit sessionDelegate:(id)delegate; +- (instancetype)init NS_UNAVAILABLE; + +- (instancetype _Nullable)initWithTrustKit:(TrustKit *)trustKit sessionDelegate:(id)delegate NS_DESIGNATED_INITIALIZER; - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge From c1d5f73bd5fd348064be5afe2d14d85999dde116 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Wed, 12 Apr 2017 15:14:53 -0400 Subject: [PATCH 016/126] Update demo app to use the singleton/swizzling --- TrustKit.xcodeproj/project.pbxproj | 4 +- TrustKit/TSKPinningValidator.m | 3 +- TrustKitDemo/TrustKitDemo/AppDelegate.m | 68 +++++++--------------- TrustKitDemo/TrustKitDemo/ViewController.h | 8 --- TrustKitDemo/TrustKitDemo/ViewController.m | 13 +++-- 5 files changed, 32 insertions(+), 64 deletions(-) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 8b3cc612..9ff7dad9 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -221,7 +221,7 @@ 8CE919221AEA077F002B29AE /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */; }; 8CE919251AEA07C5002B29AE /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */; }; 8CE9192D1AEA0F7E002B29AE /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919291AEA0F7E002B29AE /* domain_registry.h */; }; - FC1A09001E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; + FC1A09001E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; FC1A09011E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; FC1A09021E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; FC1A09031E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; @@ -756,6 +756,7 @@ buildActionMask = 2147483647; files = ( 8C84CCDA1D6E5D5A009B3E7D /* registry_types.h in Headers */, + FC1A09001E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, 8C0C904E1E3C4210003851A8 /* TSKPinningValidator.h in Headers */, 8CE9192D1AEA0F7E002B29AE /* domain_registry.h in Headers */, 8CD5F7491BCB535E005801D8 /* TSKNSURLSessionDelegateProxy.h in Headers */, @@ -770,7 +771,6 @@ 8C9EBE021B619BBE00CA7EE0 /* TSKReportsRateLimiter.h in Headers */, 8C84CCEC1D6E5D5A009B3E7D /* trie_search.h in Headers */, 8CD5F7421BCB06F4005801D8 /* RSSwizzle.h in Headers */, - FC1A09001E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, 8C9492F61B2379A100F5DF38 /* reporting_utils.h in Headers */, 8C84804D1A896EE30017C155 /* TrustKit.h in Headers */, 8C15F9A01B16094D00F06C0E /* TSKPinFailureReport.h in Headers */, diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index dc015b79..682cd746 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -71,11 +71,10 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: // Has the pinning policy expired? NSDate *expirationDate = domainConfig[kTSKExpirationDate]; - if ((expirationDate != nil) && ([expirationDate compare:[NSDate date]] == NSOrderedAscending)) + if (expirationDate != nil && [expirationDate compare:[NSDate date]] == NSOrderedAscending) { // Yes the policy has expired finalTrustDecision = TSKTrustDecisionDomainNotPinned; - } else if ([domainConfig[kTSKExcludeSubdomainFromParentPolicy] boolValue]) { diff --git a/TrustKitDemo/TrustKitDemo/AppDelegate.m b/TrustKitDemo/TrustKitDemo/AppDelegate.m index 022836b5..fddc7d01 100644 --- a/TrustKitDemo/TrustKitDemo/AppDelegate.m +++ b/TrustKitDemo/TrustKitDemo/AppDelegate.m @@ -11,6 +11,8 @@ #import "AppDelegate.h" #import +#import +#import @interface AppDelegate () @@ -18,16 +20,16 @@ @interface AppDelegate () @implementation AppDelegate - - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. // Override TrustKit's logger method - void (^loggerBlock)(NSString *) = ^void(NSString *message) - { - NSLog(@"TrustKit log: %@", message); - }; - [TrustKit setLoggerBlock:loggerBlock]; + // FIXME: logger +// void (^loggerBlock)(NSString *) = ^void(NSString *message) +// { +// NSLog(@"TrustKit log: %@", message); +// }; +// [TrustKit setLoggerBlock:loggerBlock]; // Initialize TrustKit @@ -39,13 +41,13 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( kTSKPinnedDomains: @{ // Pin invalid SPKI hashes to *.yahoo.com to demonstrate pinning failures - @"yahoo.com" : @{ - kTSKEnforcePinning:@YES, - kTSKIncludeSubdomains:@YES, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + @"yahoo.com": @{ + kTSKEnforcePinning: @YES, + kTSKIncludeSubdomains: @YES, + kTSKPublicKeyAlgorithms: @[kTSKAlgorithmRsa2048], // Wrong SPKI hashes to demonstrate pinning failure - kTSKPublicKeyHashes : @[ + kTSKPublicKeyHashes: @[ @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" ], @@ -69,44 +71,18 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( }}}; [TrustKit initializeWithConfiguration:trustKitConfig]; - - + // Demonstrate how to receive pin validation notifications (only useful for performance/metrics) - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; - [center addObserverForName:kTSKValidationCompletedNotification object:nil - queue:mainQueue usingBlock:^(NSNotification *note) { - NSDictionary* userInfo = [note userInfo]; - NSLog(@"Received pinning validation notification:\n Duration: %@\n Decision: %@\n Result: %@\n Hostname: %@", - userInfo[kTSKValidationDurationNotificationKey], - userInfo[kTSKValidationDecisionNotificationKey], - userInfo[kTSKValidationResultNotificationKey], - userInfo[kTSKValidationServerHostnameNotificationKey]); - }]; + [TrustKit sharedInstance].validationDelegateQueue =dispatch_get_main_queue(); + [TrustKit sharedInstance].validationDelegateCallback = ^(TSKPinningValidatorResult * _Nonnull result) { + NSLog(@"Received pinning validation notification:\n\tDuration: %0.4f\n\tDecision: %ld\n\tResult: %ld\n\tHostname: %@", + result.validationDuration, + (long)result.finalTrustDecision, + (long)result.validationResult, + result.serverHostname); + }; return YES; } -- (void)applicationWillResignActive:(UIApplication *)application { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. -} - -- (void)applicationDidEnterBackground:(UIApplication *)application { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. -} - -- (void)applicationWillEnterForeground:(UIApplication *)application { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. -} - -- (void)applicationDidBecomeActive:(UIApplication *)application { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. -} - -- (void)applicationWillTerminate:(UIApplication *)application { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. -} - @end diff --git a/TrustKitDemo/TrustKitDemo/ViewController.h b/TrustKitDemo/TrustKitDemo/ViewController.h index 3ae96856..23dc3d60 100644 --- a/TrustKitDemo/TrustKitDemo/ViewController.h +++ b/TrustKitDemo/TrustKitDemo/ViewController.h @@ -13,13 +13,5 @@ @interface ViewController : UIViewController -- (void)URLSession:(NSURLSession * _Nonnull)session - task:(NSURLSessionTask * _Nonnull)task -didCompleteWithError:(NSError * _Nullable)error; - -- (void)URLSession:(NSURLSession * _Nonnull)session - dataTask:(NSURLSessionDataTask * _Nonnull)dataTask - didReceiveData:(NSData * _Nonnull)data; - @end diff --git a/TrustKitDemo/TrustKitDemo/ViewController.m b/TrustKitDemo/TrustKitDemo/ViewController.m index 06da1482..6b8cbb11 100644 --- a/TrustKitDemo/TrustKitDemo/ViewController.m +++ b/TrustKitDemo/TrustKitDemo/ViewController.m @@ -11,6 +11,7 @@ #import "ViewController.h" #import +#import @interface ViewController () @@ -25,13 +26,13 @@ @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; - self.destinationWebView.delegate = self; - NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] - delegate:self - delegateQueue:nil]; - self.session = session; + self.destinationWebView.delegate = self; + self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] + delegate:self + delegateQueue:nil]; + // First demonstrate pinning failure [self loadUrlWithPinningFailure]; } @@ -62,7 +63,7 @@ - (void)URLSession:(NSURLSession *)session completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { // Call into TrustKit here to do pinning validation - if (![TSKPinningValidator handleChallenge:challenge completionHandler:completionHandler]) + if (![[TrustKit sharedInstance].pinningValidator handleChallenge:challenge completionHandler:completionHandler]) { // TrustKit did not handle this challenge: perhaps it was not for server trust // or the domain was not pinned. Fall back to the default behavior From a3f06927aa628ea7f03025d6d7379b5cb122877b Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 13 Apr 2017 11:15:41 -0400 Subject: [PATCH 017/126] Fixed #if related compile error for MacOS --- TrustKit/Pinning/TSKSPKIHashCache.m | 1 + TrustKit/TSKPinningValidator.m | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/TrustKit/Pinning/TSKSPKIHashCache.m b/TrustKit/Pinning/TSKSPKIHashCache.m index e822b5ee..58fa93a1 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -84,6 +84,7 @@ - (NSData *)getPublicKeyDataFromCertificate_legacy_ios:(SecCertificateRef)certif #if LEGACY_MACOS_KEY_EXTRACTION @interface TSKSPKIHashCache (LegacyMacOS) - (NSData *)getPublicKeyDataFromCertificate_legacy_macos:(SecCertificateRef)certificate; +@end #endif #if UNIFIED_KEY_EXTRACTION diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index 682cd746..f903f2b7 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -98,7 +98,7 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: TSKLog(@"Pin validation failed for %@", serverHostname); #if !TARGET_OS_IPHONE if ((validationResult == TSKPinValidationResultFailedUserDefinedTrustAnchor) - && (self.ignorePinsForUserTrustAnchors) + && (self.ignorePinsForUserTrustAnchors)) { // OS-X only: user-defined trust anchors can be whitelisted (for corporate proxies, etc.) so don't send reports TSKLog(@"Ignoring pinning failure due to user-defined trust anchor for %@", serverHostname); From 5bb2ec22d180cde1b8d032cce6463daa9f66efc7 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 24 May 2017 11:09:33 -0700 Subject: [PATCH 018/126] Lower deployment target --- TrustKit.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 9ff7dad9..421be7fb 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -1472,7 +1472,7 @@ GENERATE_PKGINFO_FILE = YES; INFOPLIST_FILE = TrustKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; MODULEMAP_FILE = "$(SRCROOT)/TrustKit/TrustKit.modulemap"; @@ -1503,7 +1503,7 @@ GENERATE_PKGINFO_FILE = YES; INFOPLIST_FILE = TrustKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; MODULEMAP_FILE = "$(SRCROOT)/TrustKit/TrustKit.modulemap"; From 7fd91ce180913235c24045033207016a4ea98d0d Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Mon, 5 Jun 2017 18:32:00 -0400 Subject: [PATCH 019/126] PR comments from #109. A bunch of minor code cleanup and de-duplication. --- TrustKit/Pinning/TSKSPKIHashCache.h | 20 +++++++-- TrustKit/Pinning/TSKSPKIHashCache.m | 50 ++++++++++++---------- TrustKit/Reporting/TSKReportsRateLimiter.h | 7 +++ TrustKit/Reporting/TSKReportsRateLimiter.m | 10 +++-- TrustKit/Reporting/vendor_identifier.m | 8 ++-- TrustKit/TSKLog.h | 17 +++++--- TrustKit/TSKPinningValidator.h | 30 ++++++++++++- TrustKitTests/TSKPinningValidatorTests.m | 42 +++++++++--------- TrustKitTests/TSKPublicKeyAlgorithmTests.m | 8 ++-- TrustKitTests/TSKReporterTests.m | 2 +- 10 files changed, 126 insertions(+), 68 deletions(-) diff --git a/TrustKit/Pinning/TSKSPKIHashCache.h b/TrustKit/Pinning/TSKSPKIHashCache.h index a47a7bf4..e866073f 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.h +++ b/TrustKit/Pinning/TSKSPKIHashCache.h @@ -17,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN // Each key is a raw certificate data (for easy lookup) and each value is the certificate's raw SPKI data -typedef NSMutableDictionary SpkiCacheDictionnary; +typedef NSMutableDictionary SPKICacheDictionnary; @interface TSKSPKIHashCache : NSObject @@ -34,11 +34,25 @@ typedef NSMutableDictionary SpkiCacheDictionnary; */ - (instancetype _Nullable)initWithIdentifier:(NSString * _Nullable)uniqueIdentifier NS_DESIGNATED_INITIALIZER; +/** + Get a pin cache for the provided certificate and public key algorithm. The pins + are cached so + + @param certificate The certificate containing the public key that will be hashed + @param publicKeyAlgorithm The public algorithm to expect was used in this certificate + @return The hash of the public key assuming it used the provided algorithm + */ - (NSData *)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certificate publicKeyAlgorithm:(TSKPublicKeyAlgorithm)publicKeyAlgorithm; -- (NSMutableDictionary *)getSpkiCache; +/** + Obtain the current cache used by this instance. + */ +@property (nonatomic, nullable, readonly) NSMutableDictionary *SPKICache; -- (NSMutableDictionary *)getSpkiCacheFromFileSystem; +/** + Load the SPKI cache from the filesystem. This triggers blocking file I/O. + */ +@property (nonatomic, nullable, readonly) NSMutableDictionary *SPKICacheFromFileSystem; @end diff --git a/TrustKit/Pinning/TSKSPKIHashCache.m b/TrustKit/Pinning/TSKSPKIHashCache.m index 58fa93a1..dc968db8 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -28,10 +28,6 @@ #define UNIFIED_KEY_EXTRACTION 1 #endif - -// Each key is a raw certificate data (for easy lookup) and each value is the certificate's raw SPKI data -typedef NSMutableDictionary SpkiCacheDictionnary; - #pragma mark Missing ASN1 SPKI Headers // These are the ASN1 headers for the Subject Public Key Info section of a certificate @@ -57,16 +53,25 @@ }; // Careful with the order... must match how TSKPublicKeyAlgorithm is defined -static const unsigned char *asn1HeaderBytes[4] = { rsa2048Asn1Header, rsa4096Asn1Header, - ecDsaSecp256r1Asn1Header, ecDsaSecp384r1Asn1Header }; -static const unsigned int asn1HeaderSizes[4] = { sizeof(rsa2048Asn1Header), sizeof(rsa4096Asn1Header), - sizeof(ecDsaSecp256r1Asn1Header), sizeof(ecDsaSecp384r1Asn1Header) }; +static const unsigned char *asn1HeaderBytes[4] = { + rsa2048Asn1Header, + rsa4096Asn1Header, + ecDsaSecp256r1Asn1Header, + ecDsaSecp384r1Asn1Header +}; + +static const unsigned int asn1HeaderSizes[4] = { + sizeof(rsa2048Asn1Header), + sizeof(rsa4096Asn1Header), + sizeof(ecDsaSecp256r1Asn1Header), + sizeof(ecDsaSecp384r1Asn1Header) +}; @interface TSKSPKIHashCache () // Dictionnary to cache SPKI hashes instead of having to compute them on every connection // We store one cache dictionnary per TSKPublicKeyAlgorithm we support -@property (nonatomic) NSMutableDictionary *subjectPublicKeyInfoHashesCache; +@property (nonatomic) NSMutableDictionary *subjectPublicKeyInfoHashesCache; @property (nonatomic) dispatch_queue_t lockQueue; @property (nonatomic) NSString *spkiCacheFilename; @end @@ -105,7 +110,7 @@ - (instancetype)initWithIdentifier:(NSString *)uniqueIdentifier _spkiCacheFilename = uniqueIdentifier; // if this value is nil, persistence will always fail. // First try to load a cached version from the filesystem - _subjectPublicKeyInfoHashesCache = [self getSpkiCacheFromFileSystem]; + _subjectPublicKeyInfoHashesCache = self.SPKICacheFromFileSystem; TSKLog(@"Loaded %lu SPKI cache entries from the filesystem", (unsigned long)_subjectPublicKeyInfoHashesCache.count); if (_subjectPublicKeyInfoHashesCache == nil) { @@ -188,10 +193,8 @@ - (NSData *)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certifica // Update the cache on the filesystem if (self.spkiCacheFilename.length > 0) { - NSURL *cachesDirUrl = [[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] firstObject]; - NSURL *cacheUrl = [cachesDirUrl URLByAppendingPathComponent:self.spkiCacheFilename]; NSData *serializedSpkiCache = [NSKeyedArchiver archivedDataWithRootObject:_subjectPublicKeyInfoHashesCache]; - if ([serializedSpkiCache writeToURL:cacheUrl atomically:YES] == NO) + if ([serializedSpkiCache writeToURL:[self SPKICachePath] atomically:YES] == NO) { NSAssert(false, @"Failed to write cache"); TSKLog(@"Could not persist SPKI cache to the filesystem"); @@ -201,19 +204,17 @@ - (NSData *)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certifica return subjectPublicKeyInfoHash; } -- (NSMutableDictionary *)getSpkiCacheFromFileSystem +- (NSMutableDictionary *)SPKICacheFromFileSystem { NSMutableDictionary *spkiCache; - NSString *spkiCachePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] - stringByAppendingPathComponent:self.spkiCacheFilename]; - NSData *serializedSpkiCache = [NSData dataWithContentsOfFile:spkiCachePath]; + NSData *serializedSpkiCache = [NSData dataWithContentsOfURL:[self SPKICachePath]]; if (serializedSpkiCache) { spkiCache = [NSKeyedUnarchiver unarchiveObjectWithData:serializedSpkiCache]; } return spkiCache; } -- (NSMutableDictionary *)getSpkiCache +- (NSMutableDictionary *)SPKICache { return _subjectPublicKeyInfoHashesCache; } @@ -267,6 +268,14 @@ - (NSData *)getPublicKeyDataFromCertificate:(SecCertificateRef)certificate #endif } +- (NSURL *)SPKICachePath +{ + NSAssert(self.spkiCacheFilename, @"SPKI filename should not be nil"); + NSURL *cachesDirUrl = [NSFileManager.defaultManager URLsForDirectory:NSCachesDirectory + inDomains:NSUserDomainMask].firstObject; + return [cachesDirUrl URLByAppendingPathComponent:self.spkiCacheFilename]; +} + @end @@ -400,10 +409,7 @@ @implementation TSKSPKIHashCache (TestSupport) - (void)resetSubjectPublicKeyInfoDiskCache { // Discard SPKI cache - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSString *spkiCachePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] - stringByAppendingPathComponent:self.spkiCacheFilename]; - [fileManager removeItemAtPath:spkiCachePath error:nil]; + [NSFileManager.defaultManager removeItemAtURL:[self SPKICachePath] error:nil]; } @end diff --git a/TrustKit/Reporting/TSKReportsRateLimiter.h b/TrustKit/Reporting/TSKReportsRateLimiter.h index 1d374cf9..6bfa460d 100644 --- a/TrustKit/Reporting/TSKReportsRateLimiter.h +++ b/TrustKit/Reporting/TSKReportsRateLimiter.h @@ -20,6 +20,13 @@ */ @interface TSKReportsRateLimiter : NSObject +/** + Determine if the report should be reported or ignored due to the rate limiting policy. + + @param report The report to check whether or not to rate limit + @return True if the report should be ignored under the rate-limiting policy that + is in effect. + */ - (BOOL)shouldRateLimitReport:(TSKPinFailureReport * _Nonnull)report; @end diff --git a/TrustKit/Reporting/TSKReportsRateLimiter.m b/TrustKit/Reporting/TSKReportsRateLimiter.m index 942fd2a4..1d5588fc 100644 --- a/TrustKit/Reporting/TSKReportsRateLimiter.m +++ b/TrustKit/Reporting/TSKReportsRateLimiter.m @@ -59,20 +59,22 @@ - (BOOL)shouldRateLimitReport:(TSKPinFailureReport *)report __block BOOL shouldRateLimitReport = NO; __weak typeof(self) weakSelf = self; dispatch_sync(self.reportsCacheQueue, ^{ + typeof(self) strongSelf = weakSelf; + if (secondsSinceCacheReset > kIntervalBetweenReportsCacheReset) { // Reset the cache - [weakSelf.reportsCache removeAllObjects]; - weakSelf.lastReportsCacheResetDate = [NSDate date]; + [strongSelf.reportsCache removeAllObjects]; + strongSelf.lastReportsCacheResetDate = [NSDate date]; } // Check if the exact same report has already been sent recently - shouldRateLimitReport = [weakSelf.reportsCache containsObject:pinFailureInfo]; + shouldRateLimitReport = [strongSelf.reportsCache containsObject:pinFailureInfo]; if (shouldRateLimitReport == NO) { // An identical report has NOT been sent recently // Add this report to the cache for rate-limiting - [weakSelf.reportsCache addObject:pinFailureInfo]; + [strongSelf.reportsCache addObject:pinFailureInfo]; } }); diff --git a/TrustKit/Reporting/vendor_identifier.m b/TrustKit/Reporting/vendor_identifier.m index 44c3cf5a..feadbe68 100644 --- a/TrustKit/Reporting/vendor_identifier.m +++ b/TrustKit/Reporting/vendor_identifier.m @@ -16,7 +16,7 @@ NSString *identifier_for_vendor(void) { - return [[[UIDevice currentDevice] identifierForVendor] UUIDString]; + return UIDevice.currentDevice.identifierForVendor.UUIDString; } #else @@ -31,14 +31,12 @@ NSString *identifier_for_vendor(void) { // Try to retrieve the vendor ID from the preferences - NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; + NSUserDefaults *preferences = NSUserDefaults.standardUserDefaults; NSString *vendorId = [preferences stringForKey:kTSKVendorIdentifierKey]; if (vendorId == nil) { // Generate and store a new UUID - vendorId = [[NSUUID UUID] UUIDString]; - - [preferences setObject:vendorId forKey:kTSKVendorIdentifierKey]; + [preferences setObject:NSUUID.UUID.UUIDString forKey:kTSKVendorIdentifierKey]; [preferences synchronize]; } return vendorId; diff --git a/TrustKit/TSKLog.h b/TrustKit/TSKLog.h index 3f6cb6b5..0c394048 100644 --- a/TrustKit/TSKLog.h +++ b/TrustKit/TSKLog.h @@ -1,10 +1,13 @@ -// -// TSKCommon.h -// TrustKit -// -// Created by Adam Kaplan on 4/6/17. -// Copyright © 2017 TrustKit. All rights reserved. -// +/* + + TSKCommon.h + TrustKit + + Copyright 2015 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ // Common header with internal constants and defines. diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 4c3cd014..e30a4eea 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -41,13 +41,41 @@ typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef cert /// @name Manual SSL Pinning Validation ///------------------------------------ + +/** + Domain pinning configuration, typically obtained by parseTrustKitConfiguration() + */ @property (nonatomic, readonly, nullable) NSDictionary *pinnedDomains; + +/** + Set to true to ignore the trust anchors in the user trust store. Only applicable + to platforms that support a user trust store (Mac OS). + */ @property (nonatomic, readonly) BOOL ignorePinsForUserTrustAnchors; + +/** + The queue use when invoking the validationResultHandler + */ @property (nonatomic, readonly, nonnull) dispatch_queue_t validationResultQueue; + +/** + The callback invoked with validation results + */ @property (nonatomic, readonly, nonnull) void(^validationResultHandler)(TSKPinningValidatorResult * _Nonnull result); +/** + Initialize an instance of TSKPinningValidatorResult + + @param pinnedDomains Domain pinning configuration, typically obtained by parseTrustKitConfiguration() + @param name A name for this instance. It should be unique to differentiate insstances + in logs and cache files. + @param ignorePinsForUserTrustAnchors Set to true to ignore the trust anchors in the user trust store + @param validationResultQueue The queue use when invoking the validationResultHandler + @param validationResultHandler The callback invoked with validation results + @return Initialized instance + */ - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains - identifier:(NSString *_Nullable)name + identifier:(NSString * _Nonnull)name ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler; diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 48d170fc..525761c5 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -112,7 +112,7 @@ - (void)testVerifyAgainstAnyPublicKey ]}}}; // Ensure the SPKI cache was on the filesystem is empty - NSDictionary *fsCache = [spkiCache getSpkiCacheFromFileSystem]; + NSDictionary *fsCache = spkiCache.SPKICacheFromFileSystem; XCTAssert([fsCache[@1] count] == 0, @"SPKI cache for RSA 4096 must be empty before the test"); // First test the verifyPublicKeyPin() function @@ -131,7 +131,7 @@ - (void)testVerifyAgainstAnyPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -154,7 +154,7 @@ - (void)testVerifyAgainstAnyPublicKey [self waitForExpectationsWithTimeout:2.0 handler:nil]; // Ensure the SPKI cache was persisted to the filesystem - fsCache = [spkiCache getSpkiCacheFromFileSystem]; + fsCache = spkiCache.SPKICacheFromFileSystem; XCTAssertEqual([fsCache[@1] count], 1UL, @"SPKI cache for RSA 4096 must be persisted to the file system"); CFRelease(trust); @@ -182,7 +182,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey ]}}}; // Ensure the SPKI cache was on the filesystem is empty - NSDictionary *fsCache = [spkiCache getSpkiCacheFromFileSystem]; + NSDictionary *fsCache = spkiCache.SPKICacheFromFileSystem; XCTAssertEqual([fsCache[@1] count], 0UL, @"SPKI cache for RSA 4096 must be empty before the test"); // First test the verifyPublicKeyPin() function @@ -204,7 +204,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -225,7 +225,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey [self waitForExpectationsWithTimeout:2.0 handler:nil]; // Ensure the SPKI cache was persisted to the filesystem - fsCache = [spkiCache getSpkiCacheFromFileSystem]; + fsCache = spkiCache.SPKICacheFromFileSystem; XCTAssertEqual([fsCache[@1] count], 2UL, @"SPKI cache for RSA 4096 must be persisted to the file system"); CFRelease(trust); @@ -271,7 +271,7 @@ - (void)testVerifyAgainstCAPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -334,7 +334,7 @@ - (void)testVerifyAgainstLeafPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -397,7 +397,7 @@ - (void)testVerifyAgainstBadPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -449,7 +449,7 @@ - (void)testVerifyAgainstBadPublicKeyPinsExpired // Test TSKPinningValidator TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -500,7 +500,7 @@ - (void)testVerifyAgainstBadPublicKeyPinningNotEnforced XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -565,7 +565,7 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -630,7 +630,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -696,7 +696,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -761,7 +761,7 @@ - (void)testVerifyAgainstInjectedCaPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -810,7 +810,7 @@ - (void)testDomainNotPinned // Then test TSKPinningValidator TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -847,7 +847,7 @@ -(void) testHandleChallengeCompletionHandlerDomainNotPinned kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=", // CA Key @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}; - TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:nil]; + TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:@"test"]; __block BOOL wasHandlerCalled = NO; void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) @@ -899,7 +899,7 @@ -(void) testHandleChallengeCompletionHandlerPinningFailed ]}}}; TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { @@ -956,7 +956,7 @@ -(void) testHandleChallengeCompletionHandlerPinningSuccessful ]}}}; TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig - identifier:nil + identifier:@"test" ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { @@ -1018,7 +1018,7 @@ -(void) testHandleChallengeCompletionHandlerNotServerTrustAuthenticationMethod kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=", // CA Key @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Fake key ]}}}; - TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:nil]; + TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:@"test"]; __block BOOL wasHandlerCalled = NO; void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) @@ -1072,7 +1072,7 @@ - (void)testExcludedSubdomain }}; // Then test TSKPinningValidator - TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:nil]; + TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:@"test"]; XCTAssertEqual([tk.pinningValidator evaluateTrust:trust forHostname:@"unsecured.good.com"], TSKTrustDecisionDomainNotPinned); diff --git a/TrustKitTests/TSKPublicKeyAlgorithmTests.m b/TrustKitTests/TSKPublicKeyAlgorithmTests.m index fe392b56..798648d7 100644 --- a/TrustKitTests/TSKPublicKeyAlgorithmTests.m +++ b/TrustKitTests/TSKPublicKeyAlgorithmTests.m @@ -126,8 +126,8 @@ - (void)testVerifyMultipleAlgorithms kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", // Server Key @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}); - XCTAssertEqual([spkiCache.getSpkiCache[@0] count], 0UL, @"SPKI cache must be empty"); - XCTAssertEqual([spkiCache.getSpkiCache[@1] count], 0UL, @"SPKI cache must be empty"); + XCTAssertEqual([spkiCache.SPKICache[@0] count], 0UL, @"SPKI cache must be empty"); + XCTAssertEqual([spkiCache.SPKICache[@1] count], 0UL, @"SPKI cache must be empty"); TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; verificationResult = verifyPublicKeyPin(trust, @@ -137,8 +137,8 @@ - (void)testVerifyMultipleAlgorithms spkiCache); // Ensure the SPKI cache was used; the full certificate chain is three certs and we have to go through all of them to get to the pinned leaf - XCTAssertEqual([spkiCache.getSpkiCache[@0] count], 3UL, @"SPKI cache must have been used"); - XCTAssertEqual([spkiCache.getSpkiCache[@1] count], 3UL, @"SPKI cache must have been used"); + XCTAssertEqual([spkiCache.SPKICache[@0] count], 3UL, @"SPKI cache must have been used"); + XCTAssertEqual([spkiCache.SPKICache[@1] count], 3UL, @"SPKI cache must have been used"); CFRelease(trust); CFRelease(leafCertificate); diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index 2ac4a934..e86dbbe4 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -93,7 +93,7 @@ - (void)testSendReportFromValidationReport @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Fake key 2 ]}}}; - _trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:nil]; + _trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:@"test"]; // Expect a report to be sent out when a notification is posted NSSet *knownPins = [NSSet setWithArray:@[[[NSData alloc]initWithBase64EncodedString:@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" From 554931274bcdad523f950f1bec7f5e6a491e67f7 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Mon, 5 Jun 2017 19:18:53 -0400 Subject: [PATCH 020/126] Finish comment --- TrustKit/Pinning/TSKSPKIHashCache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TrustKit/Pinning/TSKSPKIHashCache.h b/TrustKit/Pinning/TSKSPKIHashCache.h index e866073f..97f7d779 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.h +++ b/TrustKit/Pinning/TSKSPKIHashCache.h @@ -36,7 +36,7 @@ typedef NSMutableDictionary SPKICacheDictionnary; /** Get a pin cache for the provided certificate and public key algorithm. The pins - are cached so + are cached so subsequent calls will be faster than the initial call. @param certificate The certificate containing the public key that will be hashed @param publicKeyAlgorithm The public algorithm to expect was used in this certificate From b861a126821eb9fdce31e8e51125991827b8c8eb Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Mon, 5 Jun 2017 19:29:01 -0400 Subject: [PATCH 021/126] By default TrustKit constructors all use a shared global hash cache. Existing thread-safety queue system should continue to provide safe operation. --- TrustKit/TSKPinningValidator.h | 6 ++--- TrustKit/TSKPinningValidator.m | 4 ++-- TrustKit/TrustKit.m | 12 +++++++++- TrustKitTests/TSKPinningValidatorTests.m | 29 ++++++++++++------------ 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index e30a4eea..666b2409 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -14,6 +14,7 @@ @import Foundation; @class TSKPinningValidatorResult; +@class TSKSPKIHashCache; typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef certificate, TSKPublicKeyAlgorithm algorithm); @@ -67,15 +68,14 @@ typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef cert Initialize an instance of TSKPinningValidatorResult @param pinnedDomains Domain pinning configuration, typically obtained by parseTrustKitConfiguration() - @param name A name for this instance. It should be unique to differentiate insstances - in logs and cache files. + @param hashCache The hash cache to use. If nil, no caching is performed, performance may suffer. @param ignorePinsForUserTrustAnchors Set to true to ignore the trust anchors in the user trust store @param validationResultQueue The queue use when invoking the validationResultHandler @param validationResultHandler The callback invoked with validation results @return Initialized instance */ - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains - identifier:(NSString * _Nonnull)name + hashCache:(TSKSPKIHashCache * _Nullable)hashCache ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler; diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index f903f2b7..fea59cc9 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -27,7 +27,7 @@ @implementation TSKPinningValidator #pragma mark Instance Methods - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains - identifier:(NSString *)name + hashCache:(TSKSPKIHashCache * _Nullable)hashCache ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler @@ -38,7 +38,7 @@ - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)p _ignorePinsForUserTrustAnchors = ignorePinsForUserTrustAnchors; _validationResultQueue = validationResultQueue; _validationResultHandler = validationResultHandler; - _spkiHashCache = [[TSKSPKIHashCache alloc] initWithIdentifier:name]; + _spkiHashCache = hashCache; } return self; } diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 93882960..1bc6fe8d 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -13,6 +13,7 @@ #import "Reporting/TSKBackgroundReporter.h" #import "Swizzling/TSKNSURLConnectionDelegateProxy.h" #import "Swizzling/TSKNSURLSessionDelegateProxy.h" +#import "Pinning/TSKSPKIHashCache.h" #import "parse_configuration.h" #import "TSKPinningValidator.h" #import "TSKPinningValidatorResult.h" @@ -52,6 +53,9 @@ // Shared TrustKit singleton instance static TrustKit *sharedTrustKit = nil; +// A shared hash cache for use by all TrustKit instances +static TSKSPKIHashCache *sharedHashCache; + static char kTSKPinFailureReporterQueueLabel[] = "com.datatheorem.trustkit.reporterqueue"; // Default report URI - can be disabled with TSKDisableDefaultReportUri @@ -139,9 +143,15 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo #else BOOL userTrustAnchorBypass = [_configuration[kTSKIgnorePinningForUserDefinedTrustAnchors] boolValue]; #endif + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedHashCache = [[TSKSPKIHashCache alloc] initWithIdentifier:@"trustkit"]; + }); + __weak typeof(self) weakSelf = self; _pinningValidator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:_configuration - identifier:uniqueIdentifier + hashCache:sharedHashCache ignorePinsForUserTrustAnchors:userTrustAnchorBypass validationResultQueue:_pinFailureReporterQueue validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 525761c5..7676b907 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -15,6 +15,7 @@ #import "../TrustKit/TSKTrustKitConfig.h" #import "../TrustKit/parse_configuration.h" #import "../TrustKit/TSKPinningValidatorResult.h" +#import "../TrustKit/Pinning/TSKSPKIHashCache.h" #import "../TrustKit/Pinning/ssl_pin_verifier.h" #import "../TrustKit/Pinning/TSKSPKIHashCache.h" @@ -131,7 +132,7 @@ - (void)testVerifyAgainstAnyPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -204,7 +205,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -271,7 +272,7 @@ - (void)testVerifyAgainstCAPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -334,7 +335,7 @@ - (void)testVerifyAgainstLeafPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -397,7 +398,7 @@ - (void)testVerifyAgainstBadPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -449,7 +450,7 @@ - (void)testVerifyAgainstBadPublicKeyPinsExpired // Test TSKPinningValidator TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -500,7 +501,7 @@ - (void)testVerifyAgainstBadPublicKeyPinningNotEnforced XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -565,7 +566,7 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -630,7 +631,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -696,7 +697,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -761,7 +762,7 @@ - (void)testVerifyAgainstInjectedCaPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -810,7 +811,7 @@ - (void)testDomainNotPinned // Then test TSKPinningValidator TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -899,7 +900,7 @@ -(void) testHandleChallengeCompletionHandlerPinningFailed ]}}}; TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { @@ -956,7 +957,7 @@ -(void) testHandleChallengeCompletionHandlerPinningSuccessful ]}}}; TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig - identifier:@"test" + hashCache:spkiCache ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { From 6714bc17fe86bacb6c719dbc4bec70603e4e01c9 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Mon, 5 Jun 2017 19:33:24 -0400 Subject: [PATCH 022/126] Typo fix --- TrustKit/TSKPinningValidator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index e30a4eea..14387011 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -70,7 +70,7 @@ typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef cert @param name A name for this instance. It should be unique to differentiate insstances in logs and cache files. @param ignorePinsForUserTrustAnchors Set to true to ignore the trust anchors in the user trust store - @param validationResultQueue The queue use when invoking the validationResultHandler + @param validationResultQueue The queue used when invoking the validationResultHandler @param validationResultHandler The callback invoked with validation results @return Initialized instance */ From 3df18cac31933691e280b4f767e98900c1b4de2e Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Tue, 6 Jun 2017 12:16:44 -0400 Subject: [PATCH 023/126] Re-enable dylib initialization support --- TrustKit/TrustKit.m | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 1bc6fe8d..5a661d49 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -258,20 +258,20 @@ + (void)setGlobalPinFailureReporter:(TSKBackgroundReporter *) reporter // TRUSTKIT_SKIP_LIB_INITIALIZATION define allows consumers to opt out of the dylib constructor. // This might be useful to mitigate integration risks, if the consumer doens't wish to use // plist file, and wants to initialize lib manually later on. -//#ifndef TRUSTKIT_SKIP_LIB_INITIALIZATION -// -//__attribute__((constructor)) static void initializeWithInfoPlist(int argc, const char **argv) -//{ -// // TrustKit just got started in the App -// CFBundleRef appBundle = CFBundleGetMainBundle(); -// -// // Retrieve the configuration from the App's Info.plist file -// NSDictionary *trustKitConfigFromInfoPlist = (__bridge NSDictionary *)CFBundleGetValueForInfoDictionaryKey(appBundle, (__bridge CFStringRef)kTSKConfiguration); -// if (trustKitConfigFromInfoPlist) -// { -// TSKLog(@"Configuration supplied via the App's Info.plist"); -// initializeTrustKit(trustKitConfigFromInfoPlist); -// } -//} -// -//#endif +#ifndef TRUSTKIT_SKIP_LIB_INITIALIZATION + +__attribute__((constructor)) static void initializeWithInfoPlist(int argc, const char **argv) +{ + // TrustKit just got started in the App + CFBundleRef appBundle = CFBundleGetMainBundle(); + + // Retrieve the configuration from the App's Info.plist file + NSDictionary *trustKitConfigFromInfoPlist = (__bridge NSDictionary *)CFBundleGetValueForInfoDictionaryKey(appBundle, (__bridge CFStringRef)kTSKConfiguration); + if (trustKitConfigFromInfoPlist) + { + TSKLog(@"Configuration supplied via the App's Info.plist"); + [TrustKit initializeWithConfiguration:trustKitConfigFromInfoPlist]; + } +} + +#endif From a4c85bcdb874a43b7a4721da4066c20fb95d75f4 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Tue, 6 Jun 2017 14:09:41 -0400 Subject: [PATCH 024/126] - Expose default reporting URI constant in TrustKit.h - this removes some test support - Moved default cache name constant way down into the hash cache class where it applies - Removed several unneeded test support APIs --- TrustKit/Pinning/TSKSPKIHashCache.h | 4 + TrustKit/TSKPinningValidator.h | 3 +- TrustKit/TSKPinningValidator.m | 2 +- TrustKit/TrustKit.h | 5 + TrustKit/TrustKit.m | 38 +- TrustKitTests/TSKNSURLConnectionTests.m | 496 ---------------------- TrustKitTests/TSKNSURLSessionTests.m | 511 ----------------------- TrustKitTests/TSKPinningValidatorTests.m | 26 +- TrustKitTests/TSKReporterTests.m | 5 +- 9 files changed, 30 insertions(+), 1060 deletions(-) diff --git a/TrustKit/Pinning/TSKSPKIHashCache.h b/TrustKit/Pinning/TSKSPKIHashCache.h index 97f7d779..aae7852e 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.h +++ b/TrustKit/Pinning/TSKSPKIHashCache.h @@ -16,6 +16,10 @@ NS_ASSUME_NONNULL_BEGIN +// The identifier used for the default shared hash cache. Use this identifier +// in the TSKSPKIHashCache constructor to use the shared cache. +static NSString * const kTSKSPKISharedHashCacheIdentifier = @"spki-hash.cache"; + // Each key is a raw certificate data (for easy lookup) and each value is the certificate's raw SPKI data typedef NSMutableDictionary SPKICacheDictionnary; diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 45737e29..bceb6f51 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -35,7 +35,6 @@ typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef cert * `handleChallenge:completionHandler:` a helper method to be used for implementing pinning validation in challenge handler methods within `NSURLSession` and `WKWebView` delegates. */ - @interface TSKPinningValidator : NSObject ///------------------------------------ @@ -75,7 +74,7 @@ typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef cert @return Initialized instance */ - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains - hashCache:(TSKSPKIHashCache * _Nullable)hashCache + hashCache:(TSKSPKIHashCache * _Nonnull)hashCache ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler; diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index fea59cc9..0f965e54 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -27,7 +27,7 @@ @implementation TSKPinningValidator #pragma mark Instance Methods - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains - hashCache:(TSKSPKIHashCache * _Nullable)hashCache + hashCache:(TSKSPKIHashCache * _Nonnull)hashCache ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index c082ac94..4f72aa17 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -17,6 +17,11 @@ NS_ASSUME_NONNULL_BEGIN +/** The default URI – maintained by DataTheorem – used for pinning failure reports + if none is specified in the configuration. + */ +extern NSString * const kTSKDefaultReportUri; + /** `TrustKit` is a class for programmatically configuring the global SSL pinning policy within an App. diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 5a661d49..10529247 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -11,6 +11,7 @@ #import "TrustKit.h" #import "Reporting/TSKBackgroundReporter.h" +#import "Pinning/TSKSPKIHashCache.h" #import "Swizzling/TSKNSURLConnectionDelegateProxy.h" #import "Swizzling/TSKNSURLSessionDelegateProxy.h" #import "Pinning/TSKSPKIHashCache.h" @@ -60,10 +61,7 @@ // Default report URI - can be disabled with TSKDisableDefaultReportUri // Email info@datatheorem.com if you need a free dashboard to see your App's reports -static NSString * const kTSKDefaultReportUri = @"https://overmind.datatheorem.com/trustkit/report"; - -// Internal SPKI hash cache file name – we'll use as identifier -static NSString * const kTSKSharedInstanceIdentifier = @"spki-hash.cache"; +NSString * const kTSKDefaultReportUri = @"https://overmind.datatheorem.com/trustkit/report"; #pragma mark TrustKit Initialization Helper Functions @@ -93,7 +91,7 @@ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedTrustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig - identifier:kTSKSharedInstanceIdentifier]; + identifier:@"TrustKitShared"]; // Hook network APIs if needed if ([sharedTrustKit.configuration[kTSKSwizzleNetworkDelegates] boolValue]) { @@ -151,7 +149,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo __weak typeof(self) weakSelf = self; _pinningValidator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:_configuration - hashCache:sharedHashCache + hashCache:[[TSKSPKIHashCache alloc] initWithIdentifier:kTSKSPKISharedHashCacheIdentifier] ignorePinsForUserTrustAnchors:userTrustAnchorBypass validationResultQueue:_pinFailureReporterQueue validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { @@ -222,34 +220,6 @@ - (void)setValidationDelegateQueue:(dispatch_queue_t)validationDelegateQueue _validationDelegateQueue = validationDelegateQueue ?: dispatch_get_main_queue(); } -# pragma mark Private / Test Methods - -+ (void)resetConfiguration -{ - //sharedTrustKitOnceToken = 0; - // Reset is only available/used for tests - //resetSubjectPublicKeyInfoCache(); - //_configuration = nil; -// _isTrustKitInitialized = NO; -} - -+ (NSString *)getDefaultReportUri -{ - return kTSKDefaultReportUri; -} - -+ (TSKBackgroundReporter *)getGlobalPinFailureReporter -{ - TrustKit *singleton = [self sharedInstance]; - return singleton.pinFailureReporter; -} - -+ (void)setGlobalPinFailureReporter:(TSKBackgroundReporter *) reporter -{ - TrustKit *singleton = [self sharedInstance]; - singleton.pinFailureReporter = reporter; -} - @end diff --git a/TrustKitTests/TSKNSURLConnectionTests.m b/TrustKitTests/TSKNSURLConnectionTests.m index 3f4f39b1..c6e52966 100644 --- a/TrustKitTests/TSKNSURLConnectionTests.m +++ b/TrustKitTests/TSKNSURLConnectionTests.m @@ -22,116 +22,6 @@ @interface TrustKit (TestSupport) + (void)resetConfiguration; @end -/* -#pragma mark Test NSURLConnection delegate with no auth handler -@interface TestNSURLConnectionDelegateNoAuthHandler : NSObject -{ - XCTestExpectation *_testExpectation; -} - -@property TrustKit *trustKit; -@property NSError *lastError; -@property NSURLResponse *lastResponse; - -- (instancetype)initWithExpectation:(XCTestExpectation *)expectation; -@end - -@implementation TestNSURLConnectionDelegateNoAuthHandler - -- (instancetype)initWithExpectation:(XCTestExpectation *)expectation trustKit:(TrustKit *)trustKit -{ - self = [super init]; - if (self) { - _testExpectation = expectation; - _trustKit = trustKit; - } - return self; -} - -- (void)connectionDidFinishLoading:(NSURLConnection *)connection {} - -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error -{ - NSLog(@"Received error, %@", error); - _lastError = error; - [_testExpectation fulfill]; -} - -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { } - -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response -{ - _lastResponse = response; - [_testExpectation fulfill]; -} - - -- (NSURLRequest *)connection:(NSURLConnection *)connection - willSendRequest:(NSURLRequest *)request - redirectResponse:(NSURLResponse *)redirectResponse -{ - NSURLRequest *finalRequest; - if (redirectResponse == nil) - { - // URL canonicalization - not an actual redirection - finalRequest = request; - } - else - { - // Do not follow redirections as they cause two pinning validations, thereby changing the lastTrustDecision - finalRequest = nil; - NSLog(@"Received redirection %@", redirectResponse); - } - return finalRequest; -} - -@end - - - @implementation TestNSURLConnectionDelegateDidReceiveAuth - - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge - { - _wasAuthHandlerCalled = YES; - [_testExpectation fulfill]; - } - - - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace - { - return YES; - } - @end - - - - #pragma mark Test NSURLConnection delegate with connection:didReceiveAuthenticationChallenge: - @interface TestNSURLConnectionDelegateDidReceiveAuth : TestNSURLConnectionDelegateNoAuthHandler - - @property BOOL wasAuthHandlerCalled; // Used to validate that the delegate's auth handler was called - - - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; - - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace; - @end - - - #pragma mark Test NSURLConnection delegate with connection:willSendRequestForAuthenticationChallenge: - @interface TestNSURLConnectionDelegateWillSendRequestForAuth : TestNSURLConnectionDelegateNoAuthHandler - - @property BOOL wasAuthHandlerCalled; // Used to validate that the delegate's auth handler was called - - - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; - @end - - - @implementation TestNSURLConnectionDelegateWillSendRequestForAuth - - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; - { - _wasAuthHandlerCalled = YES; - [_testExpectation fulfill]; - } - - @end -*/ - ///// These are classes that respond to specific combinations of methods. Mock the methods ///// as needed for the tests. @@ -421,393 +311,7 @@ - (void)test_connectionWillSendRequestForAuthenticationChallenge_serverTrustB_bl [(id)proxy stopMocking]; } - - // TODO: add swizzling tests to ensure the above tested methods are properly invoked. - -// LEGACY BELOW - - - -// Disable auto-swizzling and ensure TrustKit does not get called -// Disabling this test for now as there are no ways to reset the swizzling across different tests -/* -- (void)testSwizzleNetworkDelegatesDiabled -{ - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : - @{ - @"www.reddit.com" : @{ - kTSKEnforcePinning : @YES, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key - ]}}}; - - [TrustKit initializeWithConfiguration:trustKitConfig]; - - - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegate"]; - TestNSURLConnectionDelegate* delegate = [[TestNSURLConnectionDelegate alloc] initWithExpectation:expectation]; - NSURLConnection *connection = [[NSURLConnection alloc] - initWithRequest:[NSURLRequest requestWithURL: - [NSURL URLWithString:@"https://www.yahoo.com/"]] - delegate:delegate]; - [connection start]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error) { - NSLog(@"Timeout Error: %@", error); - } - }]; - - // The initial value for getLastTrustKitValidationResult is -1 when no pinning validation has been done yet - XCTAssert(([TSKNSURLConnectionDelegateProxy getLastTrustKitValidationResult] < 0), @"TrustKit was called although swizzling was disabled"); -} -*/ - -/* -// Tests a secure connection to https://www.yahoo.com and forces validation to fail by providing a fake hash -- (void)testPinningValidationFailed -{ - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"www.yahoo.com" : @{ - kTSKEnforcePinning : @YES, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key - @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Fake key 2 - ]}}}; - - TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; - - // Configure notification listener - XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - trustKit.validationDelegateCallback = ^(TSKPinningValidatorResult *result) { - // Notification received, check the userInfo - XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultFailed); - XCTAssertEqualObjects(result.notedHostname, @"www.yahoo.com"); - XCTAssertEqualObjects(result.serverHostname, @"www.yahoo.com"); - XCTAssertGreaterThan(result.certificateChain.count, (unsigned long)1); - XCTAssertGreaterThan(result.validationDuration, 0); - - [notifReceivedExpectation fulfill]; - }; - - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegate"]; - TestNSURLConnectionDelegateNoAuthHandler *delegate = [[TestNSURLConnectionDelegateNoAuthHandler alloc] initWithExpectation:expectation]; - // Use -initWithRequest:delegate: - NSURLConnection *connection = [[NSURLConnection alloc] - initWithRequest:[NSURLRequest requestWithURL: - [NSURL URLWithString:@"https://www.yahoo.com/"]] - delegate:delegate]; - [connection start]; - - // Wait for the connection to complete and ensure a validation notification was posted - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) - { - if (error) - { - NSLog(@"Timeout Error: %@", error); - } - }]; - XCTAssert(([TSKNSURLConnectionDelegateProxy getLastTrustDecision] == TSKTrustDecisionShouldBlockConnection), @"TrustKit accepted an invalid certificate"); - XCTAssertNotNil(delegate.lastError, @"TrustKit did not trigger an error"); - XCTAssertNil(delegate.lastResponse, @"TrustKit returned a response although pin validation failed"); -} - - -// Tests a secure connection to https://self-signed.badssl.com with an invalid certificate chain and ensure that TrustKit is not disabling default certificate validation -- (void)testPinningValidationFailedChainNotTrusted -{ - // This is not needed but to ensure TrustKit does get initialized - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"self-signed.badssl.com" : @{ - kTSKEnforcePinning : @NO, // Do not enforce pinning to ensure default SSL validation is still enabled - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"9SLklscvzMYj8f+52lp5ze/hY0CFHyLSPQzSpYYIBm8=", // Leaf key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key - ]}}}; - - - TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegate"]; - TestNSURLConnectionDelegateNoAuthHandler *delegate = [[TestNSURLConnectionDelegateNoAuthHandler alloc] initWithExpectation:expectation]; - // Use -initWithRequest:delegate: - NSURLConnection *connection = [[NSURLConnection alloc] - initWithRequest:[NSURLRequest requestWithURL: - [NSURL URLWithString:@"https://self-signed.badssl.com/"]] - delegate:delegate]; - [connection start]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) - { - if (error) - { - NSLog(@"Timeout Error: %@", error); - } - }]; - XCTAssert(([TSKNSURLConnectionDelegateProxy getLastTrustDecision] == TSKTrustDecisionShouldBlockConnection), @"TrustKit accepted an invalid certificate"); - XCTAssertNotNil(delegate.lastError, @"TrustKit did not trigger an error"); - XCTAssertNil(delegate.lastResponse, @"TrustKit returned a response although the server's certificate is invalid"); -} - - -// Tests a secure connection to https://self-signed.badssl.com with an invalid certificate chain and ensure that TrustKit is not disabling default certificate validation for domains that are not even pinned -- (void)testPinningValidationFailedChainNotTrustedAndNotPinned -{ - // This is not needed but to ensure TrustKit does get initialized - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - // Different domain than the one we are connecting to - @"www.yahoo.com" : @{ - kTSKEnforcePinning : @NO, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"9SLklscvzMYj8f+52lp5ze/hY0CFHyLSPQzSpYYIBm8=", // Leaf key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key - ]}}}; - - - TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; - - // Configure notification listener - trustKit.validationDelegateCallback = ^(TSKPinningValidatorResult *result) { - // Ensure a validation notification was NOT posted - XCTFail(@"kTSKValidationCompletedNotification should not have been posted"); - }; - - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegate"]; - TestNSURLConnectionDelegateNoAuthHandler *delegate = [[TestNSURLConnectionDelegateNoAuthHandler alloc] initWithExpectation:expectation]; - // Use -initWithRequest:delegate: - NSURLConnection *connection = [[NSURLConnection alloc] - initWithRequest:[NSURLRequest requestWithURL: - [NSURL URLWithString:@"https://self-signed.badssl.com/"]] - delegate:delegate]; - [connection start]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) - { - if (error) - { - NSLog(@"Timeout Error: %@", error); - } - }]; - XCTAssert(([TSKNSURLConnectionDelegateProxy getLastTrustDecision] == TSKTrustDecisionDomainNotPinned), @"TrustKit accepted an invalid certificate"); - XCTAssertNotNil(delegate.lastError, @"TrustKit did not trigger an error"); - XCTAssertNil(delegate.lastResponse, @"TrustKit returned a response although the server's certificate is invalid"); -} - - -// Tests a secure connection to https://www.twitter.com by pinning only to the CA public key -- (void)testPinningValidationSucceeded -{ - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"www.twitter.com" : @{ - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"RRM1dGqnDFsCJXBTHky16vi1obOlCgFFn/yOhI/y+ho=", // CA key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key - ]}}}; - - TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; - - XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - trustKit.validationDelegateCallback = ^(TSKPinningValidatorResult *result) { - // Notification received, check the userInfo - XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); - XCTAssertEqualObjects(result.notedHostname, @"www.twitter.com"); - XCTAssertEqualObjects(result.serverHostname, @"www.twitter.com"); - XCTAssertGreaterThan(result.certificateChain.count, (unsigned long)1); - XCTAssertGreaterThan(result.validationDuration, 0); - - [notifReceivedExpectation fulfill]; - }; - - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegate"]; - TestNSURLConnectionDelegateNoAuthHandler *delegate = [[TestNSURLConnectionDelegateNoAuthHandler alloc] initWithExpectation:expectation]; - // Use -initWithRequest:delegate:startstartImmediately: - NSURLConnection *connection = [[NSURLConnection alloc] - initWithRequest:[NSURLRequest requestWithURL: - [NSURL URLWithString:@"https://www.twitter.com/"]] - delegate:delegate - startImmediately:YES]; - [connection start]; - - // Wait for the connection to finish and ensure a notification was sent - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) - { - if (error) - { - NSLog(@"Timeout Error: %@", error); - } - }]; - - XCTAssert(([TSKNSURLConnectionDelegateProxy getLastTrustDecision] == TSKTrustDecisionShouldAllowConnection), @"TrustKit rejected a valid certificate"); - XCTAssertNil(delegate.lastError, @"TrustKit triggered an error"); - XCTAssertNotNil(delegate.lastResponse, @"TrustKit prevented a response from being returned"); - XCTAssert([(NSHTTPURLResponse *)delegate.lastResponse statusCode] == 301, @"TrustKit prevented a response from being returned"); -} - - -- (void)testNoDelegateWarnings -{ - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"www.google.com" : @{ - kTSKEnforcePinning : @YES, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key - @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Fake key 2 - ]}}}; - - [TrustKit initializeWithConfiguration:trustKitConfig]; - - // Run other NSURLConnection methods that we swizzle to display a warning, to ensure they don't crash - XCTestExpectation *expectation = [self expectationWithDescription:@"Asynchronous request"]; - [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL: - [NSURL URLWithString:@"https://www.google.com/test"]] - queue:[NSOperationQueue mainQueue] - completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { - [expectation fulfill]; - }]; - - [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL: - [NSURL URLWithString:@"https://www.google.com/test"]] - returningResponse:nil error:nil]; - - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error) { - NSLog(@"Timeout Error: %@", error); - } - }]; - - [TrustKit resetConfiguration]; -} - - -// Ensure that if the original delegate has an auth handler, it also gets called when pinning validation succeed -// so that we don't disrupt the App's usual flow because of TrustKit's swizzling -- (void)testDidReceiveAuthHandlerGetsCalled -{ - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"www.apple.com" : @{ - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"gMxWOrX4PMQesK9qFNbYBxjBfjUvlkn/vN1n+L9lE5E=", // CA key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key - ]}}}; - - TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; - - // Configure notification listener - XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - trustKit.validationDelegateCallback = ^(TSKPinningValidatorResult *result) { - // Notification received, check the userInfo - XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); - XCTAssertEqualObjects(result.notedHostname, @"www.apple.com"); - XCTAssertEqualObjects(result.serverHostname, @"www.apple.com"); - XCTAssertGreaterThan(result.certificateChain.count, (unsigned long)1); - XCTAssertGreaterThan(result.validationDuration, 0); - - [notifReceivedExpectation fulfill]; - }; - - // Configure notification listener - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegateDidReceiveAuth"]; - TestNSURLConnectionDelegateDidReceiveAuth *delegate = [[TestNSURLConnectionDelegateDidReceiveAuth alloc] initWithExpectation:expectation]; - NSURLConnection *connection = [[NSURLConnection alloc] - initWithRequest:[NSURLRequest requestWithURL: - [NSURL URLWithString:@"https://www.apple.com/"]] - delegate:delegate]; - [connection start]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) - { - if (error) - { - NSLog(@"Timeout Error: %@", error); - } - }]; - XCTAssert(delegate.wasAuthHandlerCalled, @"TrustKit prevented the original delegate's auth handler from being called."); -} - - -// Ensure that if the original delegate has an auth handler, it also gets called when pinning validation succeed -// so that we don't disrupt the App's usual flow because of TrustKit's swizzling -- (void)testWillSendRequestForAuthHandlerGetsCalled -{ - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"www.fastmail.fm" : @{ - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"5kJvNEMw0KjrCAu7eXY5HZdvyCS13BbA0VJG1RSP91w=", // CA key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key - ]}}}; - - TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; - - // Configure notification listener - XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - trustKit.validationDelegateCallback = ^(TSKPinningValidatorResult *result) { - // Notification received, check the userInfo - XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); - XCTAssertEqualObjects(result.notedHostname, @"www.fastmail.fm"); - XCTAssertEqualObjects(result.serverHostname, @"www.fastmail.fm"); - XCTAssertGreaterThan(result.certificateChain.count, (unsigned long)1); - XCTAssertGreaterThan(result.validationDuration, 0); - - [notifReceivedExpectation fulfill]; - }; - - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLConnectionDelegateDidReceiveAuth"]; - TestNSURLConnectionDelegateWillSendRequestForAuth *delegate = [[TestNSURLConnectionDelegateWillSendRequestForAuth alloc] initWithExpectation:expectation]; - NSURLConnection *connection = [[NSURLConnection alloc] - initWithRequest:[NSURLRequest requestWithURL: - [NSURL URLWithString:@"https://www.fastmail.fm/"]] - delegate:delegate]; - [connection start]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) - { - if (error) - { - NSLog(@"Timeout Error: %@", error); - } - }]; - XCTAssert(delegate.wasAuthHandlerCalled, @"TrustKit prevented the original delegate's auth handler from being called."); -} - -*/ #pragma GCC diagnostic pop @end diff --git a/TrustKitTests/TSKNSURLSessionTests.m b/TrustKitTests/TSKNSURLSessionTests.m index a9536822..d388fbd4 100644 --- a/TrustKitTests/TSKNSURLSessionTests.m +++ b/TrustKitTests/TSKNSURLSessionTests.m @@ -33,142 +33,6 @@ - (void)common_URLSession:(NSURLSession * _Nonnull)session @end -/* -#pragma mark Private test methods -@interface TSKNSURLSessionDelegateProxy(Private) - -+(TSKPinValidationResult)getLastTrustDecision; -+(void)resetLastTrustDecision; - -@end - - -#pragma mark Test NSURLSession delegate with no auth handler -@interface TestNSURLSessionDelegate : NSObject -{ - XCTestExpectation *testExpectation; -} -@property NSError *lastError; -@property NSURLResponse *lastResponse; - - -- (instancetype)initWithExpectation:(XCTestExpectation *)expectation; - -- (void)URLSession:(NSURLSession * _Nonnull)session - task:(NSURLSessionTask * _Nonnull)task -didCompleteWithError:(NSError * _Nullable)error; - -- (void)URLSession:(NSURLSession * _Nonnull)session - dataTask:(NSURLSessionDataTask * _Nonnull)dataTask -didReceiveResponse:(NSURLResponse * _Nonnull)response - completionHandler:(void (^ _Nonnull)(NSURLSessionResponseDisposition disposition))completionHandler; - -- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler; - -@end - - -@implementation TestNSURLSessionDelegate - -- (instancetype)initWithExpectation:(XCTestExpectation *)expectation -{ - self = [super init]; - if (self) - { - testExpectation = expectation; - } - return self; -} - -- (void)URLSession:(NSURLSession * _Nonnull)session - task:(NSURLSessionTask * _Nonnull)task -didCompleteWithError:(NSError * _Nullable)error -{ - NSLog(@"Received error, %@", error); - _lastError = error; - [testExpectation fulfill]; -} - -- (void)URLSession:(NSURLSession * _Nonnull)session - dataTask:(NSURLSessionDataTask * _Nonnull)dataTask -didReceiveResponse:(NSURLResponse * _Nonnull)response - completionHandler:(void (^ _Nonnull)(NSURLSessionResponseDisposition disposition))completionHandler -{ - _lastResponse = response; - [testExpectation fulfill]; -} - -- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler -{ - - NSLog(@"Received redirection"); - [testExpectation fulfill]; - - // Do not follow redirections as they cause two pinning validations, thereby changing the lastTrustDecision - if (completionHandler) - { - completionHandler(nil); - } -} - -@end - - -#pragma mark Test NSURLSession delegate with URLSession:task:didReceiveChallenge:completionHandler: -@interface TestNSURLSessionDelegateTaskDidReceiveChallenge : TestNSURLSessionDelegate - -@property BOOL wasAuthHandlerCalled; // Used to validate that the delegate's auth handler was called - -- (void)URLSession:(NSURLSession * _Nonnull)session - task:(NSURLSessionTask * _Nonnull)task -didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge - completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential * _Nullable credential))completionHandler; - -@end - - -@implementation TestNSURLSessionDelegateTaskDidReceiveChallenge - -- (void)URLSession:(NSURLSession * _Nonnull)session - task:(NSURLSessionTask * _Nonnull)task -didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge - completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential * _Nullable credential))completionHandler -{ - _wasAuthHandlerCalled = YES; - [testExpectation fulfill]; -} - -@end - - -#pragma mark Test NSURLSession delegate with -URLSession:didReceiveChallenge:completionHandler: -@interface TestNSURLSessionDelegateSessionDidReceiveChallenge : TestNSURLSessionDelegate - -@property BOOL wasAuthHandlerCalled; // Used to validate that the delegate's auth handler was called - -- (void)URLSession:(NSURLSession * _Nonnull)session -didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge - completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential * _Nullable credential))completionHandler; - -@end - - -@implementation TestNSURLSessionDelegateSessionDidReceiveChallenge - -- (void)URLSession:(NSURLSession * _Nonnull)session -didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge - completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential * _Nullable credential))completionHandler -{ - _wasAuthHandlerCalled = YES; - [testExpectation fulfill]; -} - -@end -*/ // An NSURLSessionDelegate @interface SessionDelegate : NSObject @@ -542,378 +406,3 @@ - (void)test_urlSessionTaskChallengeDelegate } @end - -/* -#pragma mark Test suite -@interface TSKNSURLSessionTests : XCTestCase - -@end - -@implementation TSKNSURLSessionTests - -- (void)setUp { - [super setUp]; - [TrustKit resetConfiguration]; - [TSKNSURLSessionDelegateProxy resetLastTrustDecision]; - [[NSURLCache sharedURLCache] removeAllCachedResponses]; -} - -- (void)tearDown { - [super tearDown]; -} - -- (void)testPinningValidationFailed -{ - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"www.yahoo.com" : @{ - kTSKEnforcePinning : @YES, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key - @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Fake key 2 - ]}}}; - - [TrustKit initializeWithConfiguration:trustKitConfig]; - - // Configure notification listener - XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - NSDictionary *userInfo = [note userInfo]; - // Notification received, check the userInfo - XCTAssertEqualObjects(userInfo[kTSKValidationDecisionNotificationKey], @(TSKTrustDecisionShouldBlockConnection)); - XCTAssertEqualObjects(userInfo[kTSKValidationResultNotificationKey], @(TSKPinValidationResultFailed)); - XCTAssertEqualObjects(userInfo[kTSKValidationNotedHostnameNotificationKey], @"www.yahoo.com"); - XCTAssertEqualObjects(userInfo[kTSKValidationServerHostnameNotificationKey], @"www.yahoo.com"); - XCTAssertGreaterThan([userInfo[kTSKValidationCertificateChainNotificationKey] count], (unsigned long)1); - XCTAssertGreaterThan([userInfo[kTSKValidationDurationNotificationKey] doubleValue], 0); - [notifReceivedExpectation fulfill]; - }]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLSessionTaskDelegate"]; - TestNSURLSessionDelegate* delegate = [[TestNSURLSessionDelegate alloc] initWithExpectation:expectation]; - - NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] - delegate:delegate - delegateQueue:nil]; - - NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.yahoo.com/"]]; - [task resume]; - - // Wait for the connection to succeed and ensure a notification was posted - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) - { - if (error) - { - NSLog(@"Timeout Error: %@", error); - } - }]; - XCTAssert(([TSKNSURLSessionDelegateProxy getLastTrustDecision] == TSKTrustDecisionShouldBlockConnection), @"TrustKit accepted an invalid certificate"); - XCTAssertNotNil(delegate.lastError, @"TrustKit did not trigger an error"); - XCTAssertNil(delegate.lastResponse, @"TrustKit returned a response although pin validation failed"); - - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; -} - - -// Tests a secure connection to https://self-signed.badssl.com with an invalid certificate chain and ensure that TrustKit is not disabling default certificate validation -- (void)testPinningValidationFailedChainNotTrusted -{ - // This is not needed but to ensure TrustKit does get initialized - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"self-signed.badssl.com" : @{ - kTSKEnforcePinning : @NO, // Do not enforce pinning to ensure default SSL validation is still enabled - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"9SLklscvzMYj8f+52lp5ze/hY0CFHyLSPQzSpYYIBm8=", // Leaf key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key - ]}}}; - - [TrustKit initializeWithConfiguration:trustKitConfig]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLSessionTaskDelegate"]; - TestNSURLSessionDelegate* delegate = [[TestNSURLSessionDelegate alloc] initWithExpectation:expectation]; - - NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] - delegate:delegate - delegateQueue:nil]; - - NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://self-signed.badssl.com/"]]; - [task resume]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) - { - if (error) - { - NSLog(@"Timeout Error: %@", error); - } - }]; - XCTAssert(([TSKNSURLSessionDelegateProxy getLastTrustDecision] == TSKTrustDecisionShouldBlockConnection), @"TrustKit accepted an invalid certificate"); - XCTAssertNotNil(delegate.lastError, @"TrustKit did not trigger an error"); - XCTAssertNil(delegate.lastResponse, @"TrustKit returned a response although pin validation failed"); -} - - -// Tests a secure connection to https://self-signed.badssl.com with an invalid certificate chain and ensure that TrustKit is not disabling default certificate validation for domains that are not even pinned -- (void)testPinningValidationFailedChainNotTrustedAndNotPinned -{ - // This is not needed but to ensure TrustKit does get initialized - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"www.yahoo.com" : @{ - kTSKEnforcePinning : @NO, // Do not enforce pinning to ensure default SSL validation is still enabled - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"9SLklscvzMYj8f+52lp5ze/hY0CFHyLSPQzSpYYIBm8=", // Leaf key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key - ]}}}; - - [TrustKit initializeWithConfiguration:trustKitConfig]; - - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - // Ensure a validation notification was NOT posted - XCTFail(@"kTSKValidationCompletedNotification should not have been posted"); - }]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLSessionTaskDelegate"]; - TestNSURLSessionDelegate* delegate = [[TestNSURLSessionDelegate alloc] initWithExpectation:expectation]; - - NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] - delegate:delegate - delegateQueue:nil]; - - NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://self-signed.badssl.com/"]]; - [task resume]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) - { - if (error) - { - NSLog(@"Timeout Error: %@", error); - } - }]; - XCTAssert(([TSKNSURLSessionDelegateProxy getLastTrustDecision] == TSKTrustDecisionDomainNotPinned), @"TrustKit accepted an invalid certificate"); - XCTAssertNotNil(delegate.lastError, @"TrustKit did not trigger an error"); - XCTAssertNil(delegate.lastResponse, @"TrustKit returned a response although pin validation failed"); - - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; -} - - -- (void)testPinningValidationSucceeded -{ - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"www.datatheorem.com" : @{ - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"cGuxAXyFXFkWm61cF4HPWX8S0srS9j0aSqN0k4AP+4A=", // CA key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key - ]}}}; - - [TrustKit initializeWithConfiguration:trustKitConfig]; - - // Configure notification listener - XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - NSDictionary *userInfo = [note userInfo]; - // Notification received, check the userInfo - XCTAssertEqualObjects(userInfo[kTSKValidationDecisionNotificationKey], @(TSKTrustDecisionShouldAllowConnection)); - XCTAssertEqualObjects(userInfo[kTSKValidationResultNotificationKey], @(TSKPinValidationResultSuccess)); - XCTAssertEqualObjects(userInfo[kTSKValidationNotedHostnameNotificationKey], @"www.datatheorem.com"); - XCTAssertEqualObjects(userInfo[kTSKValidationServerHostnameNotificationKey], @"www.datatheorem.com"); - XCTAssertGreaterThan([userInfo[kTSKValidationCertificateChainNotificationKey] count], (unsigned long)1); - XCTAssertGreaterThan([userInfo[kTSKValidationDurationNotificationKey] doubleValue], 0); - [notifReceivedExpectation fulfill]; - }]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLSession"]; - TestNSURLSessionDelegate *delegate = [[TestNSURLSessionDelegate alloc] initWithExpectation:expectation]; - - NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] - delegate:delegate - delegateQueue:nil]; - - NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.datatheorem.com/"]]; - [task resume]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error) { - NSLog(@"Timeout Error: %@", error); - } - }]; - XCTAssert(([TSKNSURLSessionDelegateProxy getLastTrustDecision] == TSKTrustDecisionShouldAllowConnection), @"TrustKit rejected a valid certificate"); - XCTAssertNil(delegate.lastError, @"TrustKit triggered an error"); - XCTAssertNotNil(delegate.lastResponse, @"TrustKit prevented a response from being returned"); - XCTAssert([(NSHTTPURLResponse *)delegate.lastResponse statusCode] == 200, @"TrustKit prevented a response from being returned"); - - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; -} - - -- (void)testNoDelegateWarnings -{ - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"www.google.com" : @{ - kTSKEnforcePinning : @YES, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key - @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Fake key 2 - ]}}}; - - [TrustKit initializeWithConfiguration:trustKitConfig]; - - // Run other NSURLSession methods that we swizzle to display a warning, to ensure they don't crash - - // Start a session with a nil delegate - NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] - delegate:nil - delegateQueue:nil]; - NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.google.com/"]]; - [task resume]; - - // Start a session with +sessionWithConfiguration: - NSURLSession *session2 = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; - NSURLSessionDataTask *task2 = [session2 dataTaskWithURL:[NSURL URLWithString:@"https://www.google.com/"]]; - [task2 resume]; - - // Start a session with +sharedSession - NSURLSession *session3 = [NSURLSession sharedSession]; - NSURLSessionDataTask *task3 = [session3 dataTaskWithURL:[NSURL URLWithString:@"https://www.yahoo.com/"]]; - [task3 resume]; -} - - -// Ensure that if the original delegate has an auth handler, it also gets called when pinning validation succeed -// so that we don't disrupt the App's usual flow because of TrustKit's swizzling -- (void)testTaskDidReceiveChallengeGetsCalled -{ - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"www.apple.com" : @{ - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"gMxWOrX4PMQesK9qFNbYBxjBfjUvlkn/vN1n+L9lE5E=", // CA key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key - ]}}}; - - [TrustKit initializeWithConfiguration:trustKitConfig]; - - // Configure notification listener - XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - NSDictionary *userInfo = [note userInfo]; - // Notification received, check the userInfo - XCTAssertEqualObjects(userInfo[kTSKValidationDecisionNotificationKey], @(TSKTrustDecisionShouldAllowConnection)); - XCTAssertEqualObjects(userInfo[kTSKValidationResultNotificationKey], @(TSKPinValidationResultSuccess)); - XCTAssertEqualObjects(userInfo[kTSKValidationNotedHostnameNotificationKey], @"www.apple.com"); - XCTAssertEqualObjects(userInfo[kTSKValidationServerHostnameNotificationKey], @"www.apple.com"); - XCTAssertGreaterThan([userInfo[kTSKValidationCertificateChainNotificationKey] count], (unsigned long)1); - XCTAssertGreaterThan([userInfo[kTSKValidationDurationNotificationKey] doubleValue], 0); - [notifReceivedExpectation fulfill]; - }]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLSession"]; - TestNSURLSessionDelegateTaskDidReceiveChallenge *delegate = [[TestNSURLSessionDelegateTaskDidReceiveChallenge alloc] initWithExpectation:expectation]; - - NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] - delegate:delegate - delegateQueue:nil]; - - NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com/"]]; - [task resume]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error) { - NSLog(@"Timeout Error: %@", error); - } - }]; - XCTAssert(delegate.wasAuthHandlerCalled, @"TrustKit prevented the original delegate's auth handler from being called"); - - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; -} - - -// Ensure that if the original delegate has an auth handler, it also gets called when pinning validation succeed -// so that we don't disrupt the App's usual flow because of TrustKit's swizzling -- (void)testSessionDidReceiveChallengeGetsCalled -{ - NSDictionary *trustKitConfig = - @{ - kTSKSwizzleNetworkDelegates: @YES, - kTSKPinnedDomains : - @{ - @"www.fastmail.fm" : @{ - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"5kJvNEMw0KjrCAu7eXY5HZdvyCS13BbA0VJG1RSP91w=", // CA key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key - ]}}}; - - [TrustKit initializeWithConfiguration:trustKitConfig]; - - // Configure notification listener - XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; - id observerId = [[NSNotificationCenter defaultCenter] addObserverForName:kTSKValidationCompletedNotification - object:nil - queue:nil - usingBlock:^(NSNotification * _Nonnull note) { - NSDictionary *userInfo = [note userInfo]; - // Notification received, check the userInfo - XCTAssertEqualObjects(userInfo[kTSKValidationDecisionNotificationKey], @(TSKTrustDecisionShouldAllowConnection)); - XCTAssertEqualObjects(userInfo[kTSKValidationResultNotificationKey], @(TSKPinValidationResultSuccess)); - XCTAssertEqualObjects(userInfo[kTSKValidationNotedHostnameNotificationKey], @"www.fastmail.fm"); - XCTAssertEqualObjects(userInfo[kTSKValidationServerHostnameNotificationKey], @"www.fastmail.fm"); - XCTAssertGreaterThan([userInfo[kTSKValidationCertificateChainNotificationKey] count], (unsigned long)1); - XCTAssertGreaterThan([userInfo[kTSKValidationDurationNotificationKey] doubleValue], 0); - [notifReceivedExpectation fulfill]; - }]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLSession"]; - TestNSURLSessionDelegateSessionDidReceiveChallenge *delegate = [[TestNSURLSessionDelegateSessionDidReceiveChallenge alloc] initWithExpectation:expectation]; - - NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] - delegate:delegate - delegateQueue:nil]; - - NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.fastmail.fm/"]]; - [task resume]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error) { - NSLog(@"Timeout Error: %@", error); - } - }]; - XCTAssert(delegate.wasAuthHandlerCalled, @"TrustKit prevented the original delegate's auth handler from being called"); - - [[NSNotificationCenter defaultCenter] removeObserver:observerId]; -} - -@end -*/ diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 7676b907..7295b88c 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -205,7 +205,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -272,7 +272,7 @@ - (void)testVerifyAgainstCAPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -335,7 +335,7 @@ - (void)testVerifyAgainstLeafPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -398,7 +398,7 @@ - (void)testVerifyAgainstBadPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -450,7 +450,7 @@ - (void)testVerifyAgainstBadPublicKeyPinsExpired // Test TSKPinningValidator TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -501,7 +501,7 @@ - (void)testVerifyAgainstBadPublicKeyPinningNotEnforced XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -566,7 +566,7 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -631,7 +631,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -697,7 +697,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -762,7 +762,7 @@ - (void)testVerifyAgainstInjectedCaPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -811,7 +811,7 @@ - (void)testDomainNotPinned // Then test TSKPinningValidator TSKPinningValidator *validator; validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *result) { @@ -900,7 +900,7 @@ -(void) testHandleChallengeCompletionHandlerPinningFailed ]}}}; TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { @@ -957,7 +957,7 @@ -(void) testHandleChallengeCompletionHandlerPinningSuccessful ]}}}; TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig - hashCache:spkiCache + hashCache:spkiCache ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index e86dbbe4..d8af95b7 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -26,7 +26,6 @@ @interface TrustKit (TestSupport) @property (nonatomic) TSKBackgroundReporter *pinFailureReporter; -+ (NSString *)getDefaultReportUri; - (void)sendValidationReport:(TSKPinningValidatorResult *)result; @end @@ -190,7 +189,7 @@ - (void)testReporter port:[NSNumber numberWithInt:443] certificateChain:_testCertificateChain notedHostname:@"example.com" - reportURIs:@[[NSURL URLWithString:[TrustKit getDefaultReportUri]]] + reportURIs:@[[NSURL URLWithString:kTSKDefaultReportUri]] includeSubdomains:YES enforcePinning:YES knownPins:[NSSet setWithArray:@[ @@ -212,7 +211,7 @@ - (void)testReporterNilExpirationDate port:[NSNumber numberWithInt:443] certificateChain:_testCertificateChain notedHostname:@"example.com" - reportURIs:@[[NSURL URLWithString:[TrustKit getDefaultReportUri]]] + reportURIs:@[[NSURL URLWithString:kTSKDefaultReportUri]] includeSubdomains:YES enforcePinning:YES knownPins:[NSSet setWithArray:@[ From edd94fd8fca6f8787c0b9928c12f8d85b39bc43b Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Tue, 6 Jun 2017 15:05:35 -0400 Subject: [PATCH 025/126] Drop support for iOS 8 and macOS 10.10. Add project metadata files to xcode for easier editing. --- TrustKit.xcodeproj/project.pbxproj | 24 ++++++- TrustKit/Pinning/TSKSPKIHashCache.m | 17 +++-- TrustKit/Reporting/TSKBackgroundReporter.m | 75 ++++++---------------- 3 files changed, 51 insertions(+), 65 deletions(-) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 421be7fb..bc084f20 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -383,6 +383,12 @@ FC1A09081E57AC450055B12C /* TSKSPKIHashCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKSPKIHashCache.h; path = Pinning/TSKSPKIHashCache.h; sourceTree = ""; }; FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKSPKIHashCache.m; path = Pinning/TSKSPKIHashCache.m; sourceTree = ""; }; FC1A09121E57C6820055B12C /* TSKPublicKeyAlgorithm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TSKPublicKeyAlgorithm.h; path = Pinning/TSKPublicKeyAlgorithm.h; sourceTree = ""; }; + FC23F6731EE72F6300397646 /* TrustKit.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TrustKit.podspec; sourceTree = ""; }; + FC23F6741EE72F6300397646 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + FC23F6751EE72F6300397646 /* ATTRIBUTIONS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ATTRIBUTIONS; sourceTree = ""; }; + FC23F6761EE72F6300397646 /* AUTHORS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS; sourceTree = ""; }; + FC23F6771EE72F6300397646 /* circle.yml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = circle.yml; sourceTree = ""; }; + FC23F6781EE72F6300397646 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKReportsRateLimiterTests.m; sourceTree = ""; }; FC4CAC7E1E96917B00DAC41E /* TSKTrustKitConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustKitConfig.h; sourceTree = ""; }; FC4CAC7F1E96A2D600DAC41E /* TSKLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKLog.h; sourceTree = ""; }; @@ -530,6 +536,7 @@ 8C8480491A896EE30017C155 /* TrustKit */ = { isa = PBXGroup; children = ( + FC23F6721EE72F2A00397646 /* Project Metadata */, 8CE919281AEA0F61002B29AE /* Dependencies */, 8CA6CC571BAF60DA00BDA419 /* Swizzling */, 8CE919201AEA0745002B29AE /* Reporting */, @@ -720,6 +727,19 @@ name = Dependencies; sourceTree = ""; }; + FC23F6721EE72F2A00397646 /* Project Metadata */ = { + isa = PBXGroup; + children = ( + FC23F6731EE72F6300397646 /* TrustKit.podspec */, + FC23F6741EE72F6300397646 /* README.md */, + FC23F6751EE72F6300397646 /* ATTRIBUTIONS */, + FC23F6761EE72F6300397646 /* AUTHORS */, + FC23F6771EE72F6300397646 /* circle.yml */, + FC23F6781EE72F6300397646 /* LICENSE */, + ); + name = "Project Metadata"; + sourceTree = ""; + }; FC4CAC791E958DC600DAC41E /* Reporting */ = { isa = PBXGroup; children = ( @@ -1755,7 +1775,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 10.10; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = com.datatheorem.TrustKit; PRODUCT_NAME = TrustKit; @@ -1784,7 +1804,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 10.10; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = com.datatheorem.TrustKit; PRODUCT_NAME = TrustKit; diff --git a/TrustKit/Pinning/TSKSPKIHashCache.m b/TrustKit/Pinning/TSKSPKIHashCache.m index dc968db8..d38dee11 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -223,32 +223,35 @@ - (NSData *)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certifica - (NSData *)getPublicKeyDataFromCertificate:(SecCertificateRef)certificate { + // ****** TvOS or WatchOS ****** #if TARGET_OS_WATCH || TARGET_OS_TV // watchOS 3+ or tvOS 10+ return [self getPublicKeyDataFromCertificate_unified:certificate]; #elif TARGET_OS_IOS - // iOS 7+ + // ****** iOS ****** #if __IPHONE_OS_VERSION_MAX_ALLOWED < 100000 - // Base SDK is iOS 7, 8 or 9 + // Base SDK is iOS 8 or 9 return [self getPublicKeyDataFromCertificate_legacy_ios:certificate ]; #else // Base SDK is iOS 10+ - try to use the unified Security APIs if available NSProcessInfo *processInfo = [NSProcessInfo processInfo]; - if ([processInfo respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)] && [processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10, 0, 0}]) + if ([processInfo respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)] + && [processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10, 0, 0}]) { // iOS 10+ return [self getPublicKeyDataFromCertificate_unified:certificate]; } else { - // iOS 7, 8, 9 + // iOS 8 or 9 return [self getPublicKeyDataFromCertificate_legacy_ios:certificate]; } #endif + // ****** macOS ****** #else - // macOS 10.9+ + // macOS 10.10+ #if LEGACY_MACOS_KEY_EXTRACTION - // Base SDK is macOS 10.9, 10.10 or 10.11 + // Base SDK is macOS 10.10 or 10.11 return [self getPublicKeyDataFromCertificate_legacy_macos:certificate]; #else // Base SDK is macOS 10.12 - try to use the unified Security APIs if available @@ -261,7 +264,7 @@ - (NSData *)getPublicKeyDataFromCertificate:(SecCertificateRef)certificate } else { - // macOS 10.9, 10.10, 10.11 + // macOS 10.10 or 10.11 return [self getPublicKeyDataFromCertificate_legacy_macos:certificate]; } #endif diff --git a/TrustKit/Reporting/TSKBackgroundReporter.m b/TrustKit/Reporting/TSKBackgroundReporter.m index 203f7159..bd3ea3a0 100644 --- a/TrustKit/Reporting/TSKBackgroundReporter.m +++ b/TrustKit/Reporting/TSKBackgroundReporter.m @@ -54,38 +54,19 @@ - (nonnull instancetype)initAndRateLimitReports:(BOOL)shouldRateLimitReports; _appPlatform = @"WATCHOS"; #else _appPlatform = @"IOS"; - - // Before iOS 8 we need to build the OS version manually - // The number will not be perfectly accurate as we can't detect the patch version - if (NSFoundationVersionNumber == NSFoundationVersionNumber_iOS_7_0) - { - _appPlatformVersion = @"7.0.0"; - } - else if (NSFoundationVersionNumber == NSFoundationVersionNumber_iOS_7_1) - { - _appPlatformVersion = @"7.1.0"; - } #endif #else _appPlatform = @"MACOS"; - - // Before macOS 10.10 we need to build the OS version manually - // The number will not be perfectly accurate as we can't detect the patch version - if (NSFoundationVersionNumber == NSFoundationVersionNumber10_9) - { - _appPlatformVersion = @"10.9.0"; - } - else if (NSFoundationVersionNumber == NSFoundationVersionNumber10_9_2) - { - _appPlatformVersion = @"10.9.2"; - } #endif // If we don't have the OS version yet, we are on a device that provides the operatingSystemVersion method if (_appPlatformVersion == nil) { NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion]; - _appPlatformVersion = [NSString stringWithFormat:@"%ld.%ld.%ld", (long)version.majorVersion, (long)version.minorVersion, (long)version.patchVersion]; + _appPlatformVersion = [NSString stringWithFormat:@"%ld.%ld.%ld", + (long)version.majorVersion, + (long)version.minorVersion, + (long)version.patchVersion]; } @@ -122,23 +103,8 @@ - (nonnull instancetype)initAndRateLimitReports:(BOOL)shouldRateLimitReports; NSString *backgroundSessionId = [NSString stringWithFormat:kTSKBackgroundSessionIdentifierFormat, _appBundleId, [[NSUUID UUID] UUIDString]]; - // The API for creating background sessions changed between iOS 7 and iOS 8 and OS X 10.9 and 10.10 - // Try to use the new API if available at runtime - NSURLSessionConfiguration *backgroundConfiguration; - if ([NSURLSessionConfiguration respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]) - { - // Device runs on iOS 8+ or OS X 10.10+ or min SDK is iOS 8+ or OS X 10.10+ - backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:backgroundSessionId]; - } - else - { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - // Device runs on iOS 7 or OS X 10.9 - backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:backgroundSessionId]; -#pragma clang diagnostic pop - } - + NSURLSessionConfiguration *backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:backgroundSessionId]; + backgroundConfiguration.discretionary = YES; #if TARGET_OS_IPHONE // iOS-only settings @@ -146,10 +112,6 @@ - (nonnull instancetype)initAndRateLimitReports:(BOOL)shouldRateLimitReports; backgroundConfiguration.sessionSendsLaunchEvents = NO; #endif -#if (TARGET_OS_IPHONE) || ((!TARGET_OS_IPHONE) && (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1100)) - // On OS X discretionary is only available on 10.10 - backgroundConfiguration.discretionary = YES; -#endif // We have to use a delegate as background sessions can't use completion handlers _backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfiguration delegate:self @@ -160,21 +122,21 @@ - (nonnull instancetype)initAndRateLimitReports:(BOOL)shouldRateLimitReports; } -- (void) pinValidationFailedForHostname:(nonnull NSString *) serverHostname - port:(nullable NSNumber *) serverPort - certificateChain:(nonnull NSArray *) certificateChain - notedHostname:(nonnull NSString *) notedHostname - reportURIs:(nonnull NSArray *) reportURIs - includeSubdomains:(BOOL) includeSubdomains - enforcePinning:(BOOL) enforcePinning - knownPins:(nonnull NSSet *) knownPins - validationResult:(TSKPinValidationResult) validationResult +- (void) pinValidationFailedForHostname:(nonnull NSString *)serverHostname + port:(nullable NSNumber *)serverPort + certificateChain:(nonnull NSArray *)certificateChain + notedHostname:(nonnull NSString *)notedHostname + reportURIs:(nonnull NSArray *)reportURIs + includeSubdomains:(BOOL)includeSubdomains + enforcePinning:(BOOL)enforcePinning + knownPins:(nonnull NSSet *)knownPins + validationResult:(TSKPinValidationResult)validationResult expirationDate:(nullable NSDate *)knownPinsExpirationDate { // Default port to 0 if not specified if (serverPort == nil) { - serverPort = [NSNumber numberWithInt:0]; + serverPort = @(0); } if (reportURIs == nil) @@ -194,7 +156,7 @@ - (void) pinValidationFailedForHostname:(nonnull NSString *) serverHostname trustkitVersion:TrustKitVersion hostname:serverHostname port:serverPort - dateTime:[NSDate date] // Use the current time + dateTime:[NSDate date] // current date & time notedHostname:notedHostname includeSubdomains:includeSubdomains enforcePinning:enforcePinning @@ -213,7 +175,8 @@ - (void) pinValidationFailedForHostname:(nonnull NSString *) serverHostname // Create a temporary file for storing the JSON data in ~/tmp NSURL *tmpDirURL = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES]; - NSURL *tmpFileURL = [[tmpDirURL URLByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]] URLByAppendingPathExtension:@"tsk-report"]; + NSURL *tmpFileURL = [[tmpDirURL URLByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]] + URLByAppendingPathExtension:@"tsk-report"]; // Write the JSON report data to the temporary file NSError *error; From a42517f5d60ad918df6b28d558171f5798de891c Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Tue, 6 Jun 2017 15:38:26 -0400 Subject: [PATCH 026/126] Corrected versions in README and comments --- README.md | 2 +- TrustKit.podspec | 6 ++++-- TrustKit.xcodeproj/project.pbxproj | 26 ++++++++++++++------------ 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2c0313ea..d70213c3 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ TrustKit [![Build Status](https://circleci.com/gh/datatheorem/TrustKit.svg?style=svg)](https://circleci.com/gh/datatheorem/TrustKit) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Version Status](https://img.shields.io/cocoapods/v/TrustKit.svg?style=flat)](https://cocoapods.org/pods/TrustKit) [![Platform](https://img.shields.io/cocoapods/p/TrustKit.svg?style=flat)](https://cocoapods.org/pods/TrustKit) [![License MIT](https://img.shields.io/cocoapods/l/TrustKit.svg?style=flat)](https://en.wikipedia.org/wiki/MIT_License) -**TrustKit** is an open source framework that makes it easy to deploy SSL public key pinning and reporting in any iOS 7+, macOS 10.9+, tvOS 10+ or watchOS 3+ App; it supports both Swift and Objective-C Apps. +**TrustKit** is an open source framework that makes it easy to deploy SSL public key pinning and reporting in any iOS 8+, macOS 10.10+, tvOS 10+ or watchOS 3+ App; it supports both Swift and Objective-C Apps. If you need SSL pinning/reporting in your Android App. we have also released **TrustKit for Android** at [https://github.com/datatheorem/TrustKit-Android](https://github.com/datatheorem/TrustKit-Android). diff --git a/TrustKit.podspec b/TrustKit.podspec index b7477b81..da9ac81e 100644 --- a/TrustKit.podspec +++ b/TrustKit.podspec @@ -7,10 +7,12 @@ Pod::Spec.new do |s| s.license = { :type => 'MIT', :file => 'LICENSE' } s.authors = 'Alban Diquet', 'Angela Chow', 'Eric Castro' s.source = { :git => "https://github.com/datatheorem/TrustKit.git", :tag => "#{s.version}" } - s.ios.deployment_target = '7.0' - s.osx.deployment_target = '10.9' + + s.ios.deployment_target = '8.0' + s.osx.deployment_target = '10.10' s.tvos.deployment_target = '10.0' s.watchos.deployment_target = '3.0' + s.source_files = 'TrustKit', 'TrustKit/**/*.{h,m,c}' s.header_mappings_dir = 'TrustKit' s.public_header_files = 'TrustKit/TrustKit.h', 'TrustKit/TSKPinningValidator.h' diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index bc084f20..e84f5a42 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -383,12 +383,13 @@ FC1A09081E57AC450055B12C /* TSKSPKIHashCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKSPKIHashCache.h; path = Pinning/TSKSPKIHashCache.h; sourceTree = ""; }; FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKSPKIHashCache.m; path = Pinning/TSKSPKIHashCache.m; sourceTree = ""; }; FC1A09121E57C6820055B12C /* TSKPublicKeyAlgorithm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TSKPublicKeyAlgorithm.h; path = Pinning/TSKPublicKeyAlgorithm.h; sourceTree = ""; }; - FC23F6731EE72F6300397646 /* TrustKit.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TrustKit.podspec; sourceTree = ""; }; - FC23F6741EE72F6300397646 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - FC23F6751EE72F6300397646 /* ATTRIBUTIONS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ATTRIBUTIONS; sourceTree = ""; }; - FC23F6761EE72F6300397646 /* AUTHORS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS; sourceTree = ""; }; - FC23F6771EE72F6300397646 /* circle.yml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = circle.yml; sourceTree = ""; }; - FC23F6781EE72F6300397646 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + FC23F68C1EE73BE600397646 /* TrustKit.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TrustKit.podspec; sourceTree = SOURCE_ROOT; }; + FC23F68D1EE73BE600397646 /* generate_documentation.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = generate_documentation.sh; sourceTree = SOURCE_ROOT; }; + FC23F68E1EE73BE600397646 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; }; + FC23F68F1EE73BE600397646 /* ATTRIBUTIONS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ATTRIBUTIONS; sourceTree = SOURCE_ROOT; }; + FC23F6901EE73BE600397646 /* AUTHORS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS; sourceTree = SOURCE_ROOT; }; + FC23F6911EE73BE600397646 /* circle.yml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = circle.yml; sourceTree = SOURCE_ROOT; }; + FC23F6921EE73BE600397646 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = SOURCE_ROOT; }; FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKReportsRateLimiterTests.m; sourceTree = ""; }; FC4CAC7E1E96917B00DAC41E /* TSKTrustKitConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustKitConfig.h; sourceTree = ""; }; FC4CAC7F1E96A2D600DAC41E /* TSKLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKLog.h; sourceTree = ""; }; @@ -730,12 +731,13 @@ FC23F6721EE72F2A00397646 /* Project Metadata */ = { isa = PBXGroup; children = ( - FC23F6731EE72F6300397646 /* TrustKit.podspec */, - FC23F6741EE72F6300397646 /* README.md */, - FC23F6751EE72F6300397646 /* ATTRIBUTIONS */, - FC23F6761EE72F6300397646 /* AUTHORS */, - FC23F6771EE72F6300397646 /* circle.yml */, - FC23F6781EE72F6300397646 /* LICENSE */, + FC23F68C1EE73BE600397646 /* TrustKit.podspec */, + FC23F68D1EE73BE600397646 /* generate_documentation.sh */, + FC23F68E1EE73BE600397646 /* README.md */, + FC23F68F1EE73BE600397646 /* ATTRIBUTIONS */, + FC23F6901EE73BE600397646 /* AUTHORS */, + FC23F6911EE73BE600397646 /* circle.yml */, + FC23F6921EE73BE600397646 /* LICENSE */, ); name = "Project Metadata"; sourceTree = ""; From 998db1964f8e45c97044b94b8f271f0a7b723e08 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 8 Jun 2017 16:49:34 -0400 Subject: [PATCH 027/126] Added nullability annotations to configuration_utils.h and ssl_pin_verifier.h as well as a few development asserts --- TrustKit/Pinning/ssl_pin_verifier.h | 9 +++++---- TrustKit/Pinning/ssl_pin_verifier.m | 3 +++ TrustKit/configuration_utils.h | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/TrustKit/Pinning/ssl_pin_verifier.h b/TrustKit/Pinning/ssl_pin_verifier.h index 3c656f9f..e632d6a5 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.h +++ b/TrustKit/Pinning/ssl_pin_verifier.h @@ -14,8 +14,9 @@ @class TSKSPKIHashCache; -// Figure out if a specific domain is pinned and retrieve this domain's configuration key; returns nil if no configuration was found -NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *trustKitConfiguration); - // Validate that the server trust contains at least one of the know/expected pins -TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *serverHostname, NSArray *supportedAlgorithms, NSSet *knownPins, TSKSPKIHashCache *hashCache); +TSKPinValidationResult verifyPublicKeyPin(SecTrustRef _Nonnull serverTrust, + NSString * _Nonnull serverHostname, + NSArray * _Nonnull supportedAlgorithms, + NSSet * _Nonnull knownPins, + TSKSPKIHashCache * _Nullable hashCache); diff --git a/TrustKit/Pinning/ssl_pin_verifier.m b/TrustKit/Pinning/ssl_pin_verifier.m index 5470ee10..4b488784 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.m +++ b/TrustKit/Pinning/ssl_pin_verifier.m @@ -20,6 +20,9 @@ TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *serverHostname, NSArray *supportedAlgorithms, NSSet *knownPins, TSKSPKIHashCache *hashCache) { + NSCParameterAssert(serverTrust); + NSCParameterAssert(supportedAlgorithms); + NSCParameterAssert(knownPins); if ((serverTrust == NULL) || (supportedAlgorithms == nil) || (knownPins == nil)) { TSKLog(@"Invalid pinning parameters for %@", serverHostname); diff --git a/TrustKit/configuration_utils.h b/TrustKit/configuration_utils.h index 891ba018..163350cd 100644 --- a/TrustKit/configuration_utils.h +++ b/TrustKit/configuration_utils.h @@ -8,4 +8,5 @@ @import Foundation; -NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *trustKitConfiguration); +// Figure out if a specific domain is pinned and retrieve this domain's configuration key; returns nil if no configuration was found +NSString * _Nullable getPinningConfigurationKeyForDomain(NSString * _Nonnull hostname, NSDictionary * _Nonnull trustKitConfiguration); From 806ec32b561c9ae1930f799336ef218fef9c160c Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 8 Jun 2017 17:22:35 -0400 Subject: [PATCH 028/126] Clean up import chain and support @import umbrella header. - Moved constants declared in TSKTrustKitConfig.h into TSKTrustKitConfig.m, instead of TrustKit.m. - Bump version to 1.5 --- TrustKit.podspec | 14 +++++++--- TrustKit.xcodeproj/project.pbxproj | 18 ++++++++++--- TrustKit/Reporting/TSKBackgroundReporter.m | 1 + TrustKit/TSKTrustKit-Umbrella.h | 5 +++- TrustKit/TSKTrustKitConfig.h | 10 ------- TrustKit/TSKTrustKitConfig.m | 31 ++++++++++++++++++++++ TrustKit/TrustKit.h | 8 ++++-- TrustKit/TrustKit.m | 28 ++----------------- TrustKitTests/TSKPinConfigurationTests.m | 2 ++ TrustKitTests/TSKReporterTests.m | 1 + 10 files changed, 72 insertions(+), 46 deletions(-) create mode 100644 TrustKit/TSKTrustKitConfig.m diff --git a/TrustKit.podspec b/TrustKit.podspec index da9ac81e..805e209f 100644 --- a/TrustKit.podspec +++ b/TrustKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "TrustKit" - s.version = "1.4.2" + s.version = "1.5.0" s.summary = 'TrustKit is an open source framework that makes it easy to deploy SSL pinning in any iOS, macOS, tvOS or watchOS App.' s.homepage = "https://datatheorem.github.io/TrustKit" s.documentation_url = 'https://datatheorem.github.io/TrustKit/documentation/' @@ -13,9 +13,15 @@ Pod::Spec.new do |s| s.tvos.deployment_target = '10.0' s.watchos.deployment_target = '3.0' - s.source_files = 'TrustKit', 'TrustKit/**/*.{h,m,c}' + s.source_files = ['TrustKit', 'TrustKit/**/*.{h,m,c}'] s.header_mappings_dir = 'TrustKit' - s.public_header_files = 'TrustKit/TrustKit.h', 'TrustKit/TSKPinningValidator.h' - s.frameworks = 'Foundation', 'Security' + s.public_header_files = [ + 'TrustKit/TrustKit.h', + 'TrustKit/TSKTrustKitConfig.h', + 'TrustKit/TSKPinningValidator.h', + 'TrustKit/TSKPinValidatorResult.h', + 'TrustKit/Pinning/TSKPublicKeyAlgorithm.h' + ] + s.frameworks = ['Foundation', 'Security'] s.requires_arc = true end diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index e84f5a42..f8f22fd3 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -240,6 +240,11 @@ FC4CAC7B1E958E0500DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; FC4CAC7C1E96891A00DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; FC4CAC7D1E96891B00DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; + FCE7D62D1EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */; }; + FCE7D62E1EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */; }; + FCE7D62F1EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */; }; + FCE7D6301EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */; }; + FCE7D6311EE9F66A0081EEEF /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -314,8 +319,8 @@ 6B2B06AC1B05154A00FC749E /* TSKBackgroundReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKBackgroundReporter.h; path = Reporting/TSKBackgroundReporter.h; sourceTree = ""; }; 6B2B06AE1B05157400FC749E /* TSKBackgroundReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKBackgroundReporter.m; path = Reporting/TSKBackgroundReporter.m; sourceTree = ""; }; 8C0C90111E3C196A003851A8 /* TrustKit.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = TrustKit.modulemap; sourceTree = ""; }; - 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = TSKPinningValidator.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; - 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = TSKPinningValidator.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = TSKPinningValidator.h; sourceTree = ""; }; + 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = TSKPinningValidator.m; sourceTree = ""; }; 8C15F99E1B16094D00F06C0E /* TSKPinFailureReport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinFailureReport.h; path = Reporting/TSKPinFailureReport.h; sourceTree = ""; }; 8C15F99F1B16094D00F06C0E /* TSKPinFailureReport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKPinFailureReport.m; path = Reporting/TSKPinFailureReport.m; sourceTree = ""; }; 8C15F9A31B17564400F06C0E /* TSKPinConfigurationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinConfigurationTests.m; sourceTree = ""; }; @@ -329,7 +334,7 @@ 8C84804C1A896EE30017C155 /* TrustKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TrustKit.h; sourceTree = ""; }; 8C8480521A896EE30017C155 /* TrustKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TrustKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8C8480581A896EE30017C155 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 8C84806C1A896F660017C155 /* TrustKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = TrustKit.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 8C84806C1A896F660017C155 /* TrustKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = TrustKit.m; sourceTree = ""; }; 8C84CBB21D6E0981009B3E7D /* TrustKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TrustKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C84CBDD1D6E1718009B3E7D /* TrustKit tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "TrustKit tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 8C84CC071D6E3C67009B3E7D /* vendor_identifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vendor_identifier.h; path = Reporting/vendor_identifier.h; sourceTree = ""; }; @@ -395,6 +400,7 @@ FC4CAC7F1E96A2D600DAC41E /* TSKLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKLog.h; sourceTree = ""; }; FC4CAC801E96A95D00DAC41E /* TSKTrustKit-Umbrella.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TSKTrustKit-Umbrella.h"; sourceTree = ""; }; FCD0CC031E96ADF00076D431 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKTrustKitConfig.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -547,6 +553,7 @@ 8C84806C1A896F660017C155 /* TrustKit.m */, FC4CAC7F1E96A2D600DAC41E /* TSKLog.h */, FC4CAC7E1E96917B00DAC41E /* TSKTrustKitConfig.h */, + FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */, 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */, 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */, FC1A08FD1E579F630055B12C /* TSKPinValidatorResult.h */, @@ -1167,6 +1174,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FCE7D62D1EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */, 8C84CCD11D6E5D5A009B3E7D /* init_registry_tables.c in Sources */, 8CE919221AEA077F002B29AE /* ssl_pin_verifier.m in Sources */, 8C84CC0C1D6E3C67009B3E7D /* vendor_identifier.m in Sources */, @@ -1208,6 +1216,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FCE7D62F1EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */, 8C84CCD31D6E5D5A009B3E7D /* init_registry_tables.c in Sources */, 8C84CB911D6E0981009B3E7D /* ssl_pin_verifier.m in Sources */, 8C84CC0F1D6E3C67009B3E7D /* vendor_identifier.m in Sources */, @@ -1251,6 +1260,7 @@ files = ( 0DB3B67D1DA3B26700DA730D /* assert.c in Sources */, 0DB3B67E1DA3B26700DA730D /* registry_search.c in Sources */, + FCE7D6311EE9F66A0081EEEF /* TSKTrustKitConfig.m in Sources */, 0DB3B67F1DA3B26700DA730D /* trie_search.c in Sources */, 0DB3B67C1DA3B24100DA730D /* init_registry_tables.c in Sources */, 8C84CC0D1D6E3C67009B3E7D /* vendor_identifier.m in Sources */, @@ -1273,6 +1283,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FCE7D62E1EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */, 8C84CCD21D6E5D5A009B3E7D /* init_registry_tables.c in Sources */, 8C84CC0E1D6E3C67009B3E7D /* vendor_identifier.m in Sources */, FC1A090F1E57AC450055B12C /* TSKSPKIHashCache.m in Sources */, @@ -1314,6 +1325,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FCE7D6301EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */, 8CC5D2241D6E64D10074F515 /* init_registry_tables.c in Sources */, 8CC5D2251D6E64D10074F515 /* ssl_pin_verifier.m in Sources */, 8CC5D2261D6E64D10074F515 /* vendor_identifier.m in Sources */, diff --git a/TrustKit/Reporting/TSKBackgroundReporter.m b/TrustKit/Reporting/TSKBackgroundReporter.m index bd3ea3a0..59f7b709 100644 --- a/TrustKit/Reporting/TSKBackgroundReporter.m +++ b/TrustKit/Reporting/TSKBackgroundReporter.m @@ -10,6 +10,7 @@ */ #import "TSKBackgroundReporter.h" +#import "../TrustKit.h" #import "../TSKTrustKitConfig.h" #import "../TSKLog.h" #import "TSKPinFailureReport.h" diff --git a/TrustKit/TSKTrustKit-Umbrella.h b/TrustKit/TSKTrustKit-Umbrella.h index 3a2fca41..d9a12f7b 100644 --- a/TrustKit/TSKTrustKit-Umbrella.h +++ b/TrustKit/TSKTrustKit-Umbrella.h @@ -10,6 +10,9 @@ #define TSKTrustKit_Umbrella_h #import -#import "TSKCommon.h" + +#import "TSKTrustKitConfig.h" +#import "TSKPinValidatorResult.h" +#import "Pinning/TSKPublicKeyAlgorithm.h" #endif /* TSKTrustKit_Umbrella_h */ diff --git a/TrustKit/TSKTrustKitConfig.h b/TrustKit/TSKTrustKitConfig.h index f7b4e0bf..03446288 100644 --- a/TrustKit/TSKTrustKitConfig.h +++ b/TrustKit/TSKTrustKitConfig.h @@ -11,14 +11,6 @@ @import Foundation; -#pragma mark TrustKit Version Number - -/** - The version of TrustKit, such as "1.4.0". - */ -FOUNDATION_EXPORT NSString * const TrustKitVersion; - - #pragma mark Configuration Keys @@ -202,8 +194,6 @@ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKDisableDefaultReportUri; */ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKExpirationDate; - - #pragma mark Supported Public Key Algorithm Keys diff --git a/TrustKit/TSKTrustKitConfig.m b/TrustKit/TSKTrustKitConfig.m new file mode 100644 index 00000000..a3641a4f --- /dev/null +++ b/TrustKit/TSKTrustKitConfig.m @@ -0,0 +1,31 @@ +// +// TSKTrustKitConfig.h +// TrustKit +// +// Created by Adam Kaplan on 4/6/17. +// Copyright © 2017 TrustKit. All rights reserved. +// +#import "TSKTrustKitConfig.h" + +// General keys +const TSKGlobalConfigurationKey kTSKSwizzleNetworkDelegates = @"TSKSwizzleNetworkDelegates"; +const TSKGlobalConfigurationKey kTSKPinnedDomains = @"TSKPinnedDomains"; + +const TSKGlobalConfigurationKey kTSKIgnorePinningForUserDefinedTrustAnchors = @"TSKIgnorePinningForUserDefinedTrustAnchors"; + +// Keys for each domain within the TSKPinnedDomains entry +const TSKDomainConfigurationKey kTSKPublicKeyHashes = @"TSKPublicKeyHashes"; +const TSKDomainConfigurationKey kTSKEnforcePinning = @"TSKEnforcePinning"; +const TSKDomainConfigurationKey kTSKExcludeSubdomainFromParentPolicy = @"kSKExcludeSubdomainFromParentPolicy"; + +const TSKDomainConfigurationKey kTSKIncludeSubdomains = @"TSKIncludeSubdomains"; +const TSKDomainConfigurationKey kTSKPublicKeyAlgorithms = @"TSKPublicKeyAlgorithms"; +const TSKDomainConfigurationKey kTSKReportUris = @"TSKReportUris"; +const TSKDomainConfigurationKey kTSKDisableDefaultReportUri = @"TSKDisableDefaultReportUri"; +const TSKDomainConfigurationKey kTSKExpirationDate = @"TSKExpirationDate"; + +#pragma mark Public key Algorithms Constants +const TSKSupportedAlgorithm kTSKAlgorithmRsa2048 = @"TSKAlgorithmRsa2048"; +const TSKSupportedAlgorithm kTSKAlgorithmRsa4096 = @"TSKAlgorithmRsa4096"; +const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp256r1 = @"TSKAlgorithmEcDsaSecp256r1"; +const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp384r1 = @"TSKAlgorithmEcDsaSecp384r1"; diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 4f72aa17..d674bf40 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -9,7 +9,6 @@ */ -#import "TSKTrustKitConfig.h" @import Foundation; @class TSKPinningValidator; @@ -17,10 +16,15 @@ NS_ASSUME_NONNULL_BEGIN +/** + The version of TrustKit, such as "1.4.0". + */ +FOUNDATION_EXPORT NSString * const TrustKitVersion; + /** The default URI – maintained by DataTheorem – used for pinning failure reports if none is specified in the configuration. */ -extern NSString * const kTSKDefaultReportUri; +FOUNDATION_EXPORT NSString * const kTSKDefaultReportUri; /** `TrustKit` is a class for programmatically configuring the global SSL pinning policy diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 10529247..63f7c5fe 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -18,38 +18,14 @@ #import "parse_configuration.h" #import "TSKPinningValidator.h" #import "TSKPinningValidatorResult.h" +#import "TSKTrustKitConfig.h" #import "TSKLog.h" -NSString * const TrustKitVersion = @"1.4.2"; - -#pragma mark Configuration Constants +NSString * const TrustKitVersion = @"1.5.0"; // Info.plist key we read the public key hashes from static const NSString *kTSKConfiguration = @"TSKConfiguration"; -// General keys -const TSKGlobalConfigurationKey kTSKSwizzleNetworkDelegates = @"TSKSwizzleNetworkDelegates"; -const TSKGlobalConfigurationKey kTSKPinnedDomains = @"TSKPinnedDomains"; - -const TSKGlobalConfigurationKey kTSKIgnorePinningForUserDefinedTrustAnchors = @"TSKIgnorePinningForUserDefinedTrustAnchors"; - -// Keys for each domain within the TSKPinnedDomains entry -const TSKDomainConfigurationKey kTSKPublicKeyHashes = @"TSKPublicKeyHashes"; -const TSKDomainConfigurationKey kTSKEnforcePinning = @"TSKEnforcePinning"; -const TSKDomainConfigurationKey kTSKExcludeSubdomainFromParentPolicy = @"kSKExcludeSubdomainFromParentPolicy"; - -const TSKDomainConfigurationKey kTSKIncludeSubdomains = @"TSKIncludeSubdomains"; -const TSKDomainConfigurationKey kTSKPublicKeyAlgorithms = @"TSKPublicKeyAlgorithms"; -const TSKDomainConfigurationKey kTSKReportUris = @"TSKReportUris"; -const TSKDomainConfigurationKey kTSKDisableDefaultReportUri = @"TSKDisableDefaultReportUri"; -const TSKDomainConfigurationKey kTSKExpirationDate = @"TSKExpirationDate"; - -#pragma mark Public key Algorithms Constants -const TSKSupportedAlgorithm kTSKAlgorithmRsa2048 = @"TSKAlgorithmRsa2048"; -const TSKSupportedAlgorithm kTSKAlgorithmRsa4096 = @"TSKAlgorithmRsa4096"; -const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp256r1 = @"TSKAlgorithmEcDsaSecp256r1"; -const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp384r1 = @"TSKAlgorithmEcDsaSecp384r1"; - #pragma mark TrustKit Global State // Shared TrustKit singleton instance static TrustKit *sharedTrustKit = nil; diff --git a/TrustKitTests/TSKPinConfigurationTests.m b/TrustKitTests/TSKPinConfigurationTests.m index 32e9b22d..a0039a58 100644 --- a/TrustKitTests/TSKPinConfigurationTests.m +++ b/TrustKitTests/TSKPinConfigurationTests.m @@ -10,7 +10,9 @@ */ #import + #import "../TrustKit/TrustKit.h" +#import "../TrustKit/TSKTrustKitConfig.h" #import "../TrustKit/Pinning/ssl_pin_verifier.h" #import "../TrustKit/Pinning/TSKPublicKeyAlgorithm.h" #import "../TrustKit/parse_configuration.h" diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index d8af95b7..5e3bc81d 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -12,6 +12,7 @@ #import #import "../TrustKit/TrustKit.h" +#import "../TrustKit/TSKTrustKitConfig.h" #import "../TrustKit/TSKPinningValidatorResult.h" #import "../TrustKit/Reporting/TSKBackgroundReporter.h" From ffd63b1f381cb1ceeecf2aebe1bb016037225745 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 8 Jun 2017 18:50:00 -0400 Subject: [PATCH 029/126] Reliable support for use as both Framework and static library via Cocoapods. Fixed umbrella header and non-modular includes. Removed excess configs from the project file around module packaging. --- TrustKit.podspec | 1 - TrustKit.xcodeproj/project.pbxproj | 80 ++++++++++----------- TrustKit/{ => Framework}/Info.plist | 0 TrustKit/Framework/TrustKit-umbrella.h | 17 +++++ TrustKit/{ => Framework}/TrustKit.modulemap | 2 +- TrustKit/Reporting/TSKBackgroundReporter.m | 1 - TrustKit/TSKLog.h | 8 +-- TrustKit/TSKPinningValidator.h | 2 +- TrustKit/TSKTrustKit-Umbrella.h | 18 ----- TrustKit/TSKTrustKitConfig.m | 2 + TrustKit/TrustKit.h | 14 ++-- TrustKit/TrustKit.m | 4 -- 12 files changed, 72 insertions(+), 77 deletions(-) rename TrustKit/{ => Framework}/Info.plist (100%) create mode 100644 TrustKit/Framework/TrustKit-umbrella.h rename TrustKit/{ => Framework}/TrustKit.modulemap (61%) delete mode 100644 TrustKit/TSKTrustKit-Umbrella.h diff --git a/TrustKit.podspec b/TrustKit.podspec index 805e209f..9cc4d2b8 100644 --- a/TrustKit.podspec +++ b/TrustKit.podspec @@ -14,7 +14,6 @@ Pod::Spec.new do |s| s.watchos.deployment_target = '3.0' s.source_files = ['TrustKit', 'TrustKit/**/*.{h,m,c}'] - s.header_mappings_dir = 'TrustKit' s.public_header_files = [ 'TrustKit/TrustKit.h', 'TrustKit/TSKTrustKitConfig.h', diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index f8f22fd3..606715e2 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -222,9 +222,9 @@ 8CE919251AEA07C5002B29AE /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */; }; 8CE9192D1AEA0F7E002B29AE /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919291AEA0F7E002B29AE /* domain_registry.h */; }; FC1A09001E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; - FC1A09011E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; - FC1A09021E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; - FC1A09031E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; }; + FC1A09011E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FC1A09021E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FC1A09031E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; FC1A09041E57A4BB0055B12C /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */; }; FC1A09051E57A4BB0055B12C /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */; }; FC1A09061E57A4BB0055B12C /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */; }; @@ -245,6 +245,10 @@ FCE7D62F1EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */; }; FCE7D6301EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */; }; FCE7D6311EE9F66A0081EEEF /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */; }; + FCE7D6321EE9FDFD0081EEEF /* TSKPublicKeyAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A09121E57C6820055B12C /* TSKPublicKeyAlgorithm.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FCE7D6331EE9FE080081EEEF /* TSKPublicKeyAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A09121E57C6820055B12C /* TSKPublicKeyAlgorithm.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FCE7D6341EE9FE260081EEEF /* TSKPublicKeyAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A09121E57C6820055B12C /* TSKPublicKeyAlgorithm.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FCE7D6351EE9FE2F0081EEEF /* TSKPublicKeyAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A09121E57C6820055B12C /* TSKPublicKeyAlgorithm.h */; settings = {ATTRIBUTES = (Public, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -318,7 +322,6 @@ 6B032D3F1AF1AEB600EAFA69 /* TSKReporterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TSKReporterTests.m; sourceTree = ""; }; 6B2B06AC1B05154A00FC749E /* TSKBackgroundReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKBackgroundReporter.h; path = Reporting/TSKBackgroundReporter.h; sourceTree = ""; }; 6B2B06AE1B05157400FC749E /* TSKBackgroundReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKBackgroundReporter.m; path = Reporting/TSKBackgroundReporter.m; sourceTree = ""; }; - 8C0C90111E3C196A003851A8 /* TrustKit.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = TrustKit.modulemap; sourceTree = ""; }; 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = TSKPinningValidator.h; sourceTree = ""; }; 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = TSKPinningValidator.m; sourceTree = ""; }; 8C15F99E1B16094D00F06C0E /* TSKPinFailureReport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinFailureReport.h; path = Reporting/TSKPinFailureReport.h; sourceTree = ""; }; @@ -330,7 +333,6 @@ 8C5D98B21CEFF079008E654B /* parse_configuration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = parse_configuration.m; sourceTree = ""; }; 8C5D98B61CEFF103008E654B /* parse_configuration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = parse_configuration.h; sourceTree = ""; }; 8C8480471A896EE30017C155 /* TrustKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TrustKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8C84804B1A896EE30017C155 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8C84804C1A896EE30017C155 /* TrustKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TrustKit.h; sourceTree = ""; }; 8C8480521A896EE30017C155 /* TrustKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TrustKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8C8480581A896EE30017C155 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -398,9 +400,11 @@ FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKReportsRateLimiterTests.m; sourceTree = ""; }; FC4CAC7E1E96917B00DAC41E /* TSKTrustKitConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustKitConfig.h; sourceTree = ""; }; FC4CAC7F1E96A2D600DAC41E /* TSKLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKLog.h; sourceTree = ""; }; - FC4CAC801E96A95D00DAC41E /* TSKTrustKit-Umbrella.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TSKTrustKit-Umbrella.h"; sourceTree = ""; }; FCD0CC031E96ADF00076D431 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKTrustKitConfig.m; sourceTree = ""; }; + FCE7D6381EEA04180081EEEF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + FCE7D6391EEA04180081EEEF /* TrustKit-umbrella.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TrustKit-umbrella.h"; sourceTree = ""; }; + FCE7D63A1EEA04180081EEEF /* TrustKit.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = TrustKit.modulemap; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -559,21 +563,11 @@ FC1A08FD1E579F630055B12C /* TSKPinValidatorResult.h */, FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */, FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */, - 8C84804A1A896EE30017C155 /* Supporting Files */, + FCE7D6371EEA04180081EEEF /* Framework */, ); path = TrustKit; sourceTree = ""; }; - 8C84804A1A896EE30017C155 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 8C0C90111E3C196A003851A8 /* TrustKit.modulemap */, - 8C84804B1A896EE30017C155 /* Info.plist */, - FC4CAC801E96A95D00DAC41E /* TSKTrustKit-Umbrella.h */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; 8C8480561A896EE30017C155 /* TrustKitTests */ = { isa = PBXGroup; children = ( @@ -777,6 +771,16 @@ name = Frameworks; sourceTree = ""; }; + FCE7D6371EEA04180081EEEF /* Framework */ = { + isa = PBXGroup; + children = ( + FCE7D6381EEA04180081EEEF /* Info.plist */, + FCE7D6391EEA04180081EEEF /* TrustKit-umbrella.h */, + FCE7D63A1EEA04180081EEEF /* TrustKit.modulemap */, + ); + path = Framework; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -786,6 +790,7 @@ files = ( 8C84CCDA1D6E5D5A009B3E7D /* registry_types.h in Headers */, FC1A09001E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, + FCE7D6321EE9FDFD0081EEEF /* TSKPublicKeyAlgorithm.h in Headers */, 8C0C904E1E3C4210003851A8 /* TSKPinningValidator.h in Headers */, 8CE9192D1AEA0F7E002B29AE /* domain_registry.h in Headers */, 8CD5F7491BCB535E005801D8 /* TSKNSURLSessionDelegateProxy.h in Headers */, @@ -811,7 +816,9 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + FC1A09021E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, 8C84CCDC1D6E5D5A009B3E7D /* registry_types.h in Headers */, + FCE7D6341EE9FE260081EEEF /* TSKPublicKeyAlgorithm.h in Headers */, 8C0C90501E3C4212003851A8 /* TSKPinningValidator.h in Headers */, 8C84CBA21D6E0981009B3E7D /* domain_registry.h in Headers */, 8C84CBA41D6E0981009B3E7D /* TSKNSURLSessionDelegateProxy.h in Headers */, @@ -826,7 +833,6 @@ 8C84CBA81D6E0981009B3E7D /* TSKReportsRateLimiter.h in Headers */, 8C84CCEE1D6E5D5A009B3E7D /* trie_search.h in Headers */, 8C84CBA91D6E0981009B3E7D /* RSSwizzle.h in Headers */, - FC1A09021E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, 8C84CBAA1D6E0981009B3E7D /* reporting_utils.h in Headers */, 8C84CBAB1D6E0981009B3E7D /* TrustKit.h in Headers */, 8C84CBAC1D6E0981009B3E7D /* TSKPinFailureReport.h in Headers */, @@ -838,8 +844,10 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + FC1A09011E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, 8C84CCDB1D6E5D5A009B3E7D /* registry_types.h in Headers */, 8C0C904F1E3C4212003851A8 /* TSKPinningValidator.h in Headers */, + FCE7D6331EE9FE080081EEEF /* TSKPublicKeyAlgorithm.h in Headers */, 8CA6CC251BAE2B6A00BDA419 /* TrustKit.h in Headers */, 8CD5F74A1BCB535E005801D8 /* TSKNSURLSessionDelegateProxy.h in Headers */, 8C4346D71E5B894A008023F9 /* configuration_utils.h in Headers */, @@ -855,7 +863,6 @@ 8CA6CC211BAE2B6A00BDA419 /* ssl_pin_verifier.h in Headers */, 8C84CCED1D6E5D5A009B3E7D /* trie_search.h in Headers */, 8CD5F7431BCB06F4005801D8 /* RSSwizzle.h in Headers */, - FC1A09011E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, 8CA6CC191BAE2B6600BDA419 /* TSKBackgroundReporter.h in Headers */, 8CA6CC271BAE2B7000BDA419 /* domain_registry.h in Headers */, 8CA6CC1D1BAE2B6600BDA419 /* reporting_utils.h in Headers */, @@ -866,7 +873,9 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + FC1A09031E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, 8CC5D2371D6E64D10074F515 /* registry_types.h in Headers */, + FCE7D6351EE9FE2F0081EEEF /* TSKPublicKeyAlgorithm.h in Headers */, 8C0C90511E3C4213003851A8 /* TSKPinningValidator.h in Headers */, 8CC5D2381D6E64D10074F515 /* domain_registry.h in Headers */, 8CC5D23A1D6E64D10074F515 /* TSKNSURLSessionDelegateProxy.h in Headers */, @@ -881,7 +890,6 @@ 8CC5D2431D6E64D10074F515 /* TSKReportsRateLimiter.h in Headers */, 8CC5D2441D6E64D10074F515 /* trie_search.h in Headers */, 8CC5D2451D6E64D10074F515 /* RSSwizzle.h in Headers */, - FC1A09031E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, 8CC5D2461D6E64D10074F515 /* reporting_utils.h in Headers */, 8CC5D2471D6E64D10074F515 /* TrustKit.h in Headers */, 8CC5D2481D6E64D10074F515 /* TSKPinFailureReport.h in Headers */, @@ -1395,7 +1403,6 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; ENABLE_BITCODE = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -1421,7 +1428,8 @@ GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MODULEMAP_FILE = "$(SRCROOT)/TrustKit/TrustKit.modulemap"; + HEADER_SEARCH_PATHS = "$(SRCROOT)/**"; + MODULEMAP_FILE = "$(SRCROOT)/TrustKit/Framework/TrustKit.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -1458,7 +1466,6 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; ENABLE_BITCODE = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1479,7 +1486,8 @@ GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MODULEMAP_FILE = "$(SRCROOT)/TrustKit/TrustKit.modulemap"; + HEADER_SEARCH_PATHS = "$(SRCROOT)/**"; + MODULEMAP_FILE = "$(SRCROOT)/TrustKit/Framework/TrustKit.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; @@ -1504,12 +1512,11 @@ ENABLE_BITCODE = "$(inherited)"; GCC_GENERATE_TEST_COVERAGE_FILES = YES; GENERATE_PKGINFO_FILE = YES; - INFOPLIST_FILE = TrustKit/Info.plist; + INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Framework/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; - MODULEMAP_FILE = "$(SRCROOT)/TrustKit/TrustKit.modulemap"; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1535,12 +1542,11 @@ ENABLE_BITCODE = "$(inherited)"; GCC_GENERATE_TEST_COVERAGE_FILES = YES; GENERATE_PKGINFO_FILE = YES; - INFOPLIST_FILE = TrustKit/Info.plist; + INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Framework/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; - MODULEMAP_FILE = "$(SRCROOT)/TrustKit/TrustKit.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; @@ -1571,7 +1577,6 @@ INFOPLIST_FILE = TrustKitTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @loader_path/../Frameworks @executable_path/../Frameworks"; - MODULEMAP_FILE = ""; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1597,7 +1602,6 @@ INFOPLIST_FILE = TrustKitTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @loader_path/../Frameworks @executable_path/../Frameworks"; - MODULEMAP_FILE = ""; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1621,7 +1625,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; GCC_GENERATE_TEST_COVERAGE_FILES = YES; - INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Info.plist"; + INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Framework/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -1653,7 +1657,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; GCC_GENERATE_TEST_COVERAGE_FILES = YES; - INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Info.plist"; + INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Framework/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -1689,7 +1693,6 @@ GCC_TREAT_WARNINGS_AS_ERRORS = YES; INFOPLIST_FILE = TrustKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @loader_path/../Frameworks @executable_path/../Frameworks"; - MODULEMAP_FILE = ""; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1716,7 +1719,6 @@ GCC_TREAT_WARNINGS_AS_ERRORS = YES; INFOPLIST_FILE = TrustKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @loader_path/../Frameworks @executable_path/../Frameworks"; - MODULEMAP_FILE = ""; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1785,7 +1787,7 @@ FRAMEWORK_VERSION = A; GCC_GENERATE_TEST_COVERAGE_FILES = YES; GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Info.plist"; + INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Framework/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; @@ -1814,7 +1816,7 @@ FRAMEWORK_VERSION = A; GCC_GENERATE_TEST_COVERAGE_FILES = YES; GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Info.plist"; + INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Framework/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; @@ -1843,7 +1845,6 @@ INFOPLIST_FILE = TrustKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.9; - MODULEMAP_FILE = ""; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.TrustKit-OS-XTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1868,7 +1869,6 @@ INFOPLIST_FILE = TrustKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.9; - MODULEMAP_FILE = ""; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.TrustKit-OS-XTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1891,7 +1891,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; GCC_GENERATE_TEST_COVERAGE_FILES = YES; - INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Info.plist"; + INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Framework/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -1923,7 +1923,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = "$(inherited)"; GCC_GENERATE_TEST_COVERAGE_FILES = YES; - INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Info.plist"; + INFOPLIST_FILE = "$(SRCROOT)/TrustKit/Framework/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; diff --git a/TrustKit/Info.plist b/TrustKit/Framework/Info.plist similarity index 100% rename from TrustKit/Info.plist rename to TrustKit/Framework/Info.plist diff --git a/TrustKit/Framework/TrustKit-umbrella.h b/TrustKit/Framework/TrustKit-umbrella.h new file mode 100644 index 00000000..5809f4e2 --- /dev/null +++ b/TrustKit/Framework/TrustKit-umbrella.h @@ -0,0 +1,17 @@ +// +// TrustKit.h +// TrustKit +// +// Created by Adam Kaplan on 6/8/17. +// Copyright © 2017 TrustKit. All rights reserved. +// + +#ifndef _TRUSTKIT_ +#define _TRUSTKIT_ + +#import +#import +#import +#import + +#endif /* _TRUSTKIT_ */ diff --git a/TrustKit/TrustKit.modulemap b/TrustKit/Framework/TrustKit.modulemap similarity index 61% rename from TrustKit/TrustKit.modulemap rename to TrustKit/Framework/TrustKit.modulemap index add7afd4..0526b6f7 100644 --- a/TrustKit/TrustKit.modulemap +++ b/TrustKit/Framework/TrustKit.modulemap @@ -1,5 +1,5 @@ framework module TrustKit { - umbrella header "TSKTrustKit-Umbrella.h" + umbrella header "TrustKit-umbrella.h" export * module * { export * } diff --git a/TrustKit/Reporting/TSKBackgroundReporter.m b/TrustKit/Reporting/TSKBackgroundReporter.m index 59f7b709..bd3ea3a0 100644 --- a/TrustKit/Reporting/TSKBackgroundReporter.m +++ b/TrustKit/Reporting/TSKBackgroundReporter.m @@ -10,7 +10,6 @@ */ #import "TSKBackgroundReporter.h" -#import "../TrustKit.h" #import "../TSKTrustKitConfig.h" #import "../TSKLog.h" #import "TSKPinFailureReport.h" diff --git a/TrustKit/TSKLog.h b/TrustKit/TSKLog.h index 0c394048..38ed5031 100644 --- a/TrustKit/TSKLog.h +++ b/TrustKit/TSKLog.h @@ -1,6 +1,6 @@ /* - TSKCommon.h + TSKLog.h TrustKit Copyright 2015 The TrustKit Project Authors @@ -11,8 +11,8 @@ // Common header with internal constants and defines. -#ifndef TSKCommon_h -#define TSKCommon_h +#ifndef TSKLog_h +#define TSKLog_h // The logging function we use within TrustKit #ifdef DEBUG @@ -21,4 +21,4 @@ #define TSKLog(format, ...) #endif -#endif /* TSKCommon_h */ +#endif /* TSKLog_h */ diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index bceb6f51..8482abf9 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -10,7 +10,7 @@ */ #import "TSKPinValidatorResult.h" -#import "Pinning/TSKPublicKeyAlgorithm.h" +#import "TSKPublicKeyAlgorithm.h" @import Foundation; @class TSKPinningValidatorResult; diff --git a/TrustKit/TSKTrustKit-Umbrella.h b/TrustKit/TSKTrustKit-Umbrella.h deleted file mode 100644 index d9a12f7b..00000000 --- a/TrustKit/TSKTrustKit-Umbrella.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// TSKTrustKit-Umbrella.h -// TrustKit -// -// Created by Adam Kaplan on 4/6/17. -// Copyright © 2017 TrustKit. All rights reserved. -// - -#ifndef TSKTrustKit_Umbrella_h -#define TSKTrustKit_Umbrella_h - -#import - -#import "TSKTrustKitConfig.h" -#import "TSKPinValidatorResult.h" -#import "Pinning/TSKPublicKeyAlgorithm.h" - -#endif /* TSKTrustKit_Umbrella_h */ diff --git a/TrustKit/TSKTrustKitConfig.m b/TrustKit/TSKTrustKitConfig.m index a3641a4f..c815bbce 100644 --- a/TrustKit/TSKTrustKitConfig.m +++ b/TrustKit/TSKTrustKitConfig.m @@ -7,6 +7,8 @@ // #import "TSKTrustKitConfig.h" +NSString * const TrustKitVersion = @"1.5.0"; + // General keys const TSKGlobalConfigurationKey kTSKSwizzleNetworkDelegates = @"TSKSwizzleNetworkDelegates"; const TSKGlobalConfigurationKey kTSKPinnedDomains = @"TSKPinnedDomains"; diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index d674bf40..3a3d5369 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -11,16 +11,16 @@ @import Foundation; -@class TSKPinningValidator; -@class TSKPinningValidatorResult; +#ifndef _TRUSTKIT_ +#define _TRUSTKIT_ + #import "TSKTrustKitConfig.h" + #import "TSKPublicKeyAlgorithm.h" + #import "TSKPinningValidator.h" + #import "TSKPinValidatorResult.h" +#endif /* _TRUSTKIT_ */ NS_ASSUME_NONNULL_BEGIN -/** - The version of TrustKit, such as "1.4.0". - */ -FOUNDATION_EXPORT NSString * const TrustKitVersion; - /** The default URI – maintained by DataTheorem – used for pinning failure reports if none is specified in the configuration. */ diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 63f7c5fe..4072e20c 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -16,13 +16,9 @@ #import "Swizzling/TSKNSURLSessionDelegateProxy.h" #import "Pinning/TSKSPKIHashCache.h" #import "parse_configuration.h" -#import "TSKPinningValidator.h" #import "TSKPinningValidatorResult.h" -#import "TSKTrustKitConfig.h" #import "TSKLog.h" -NSString * const TrustKitVersion = @"1.5.0"; - // Info.plist key we read the public key hashes from static const NSString *kTSKConfiguration = @"TSKConfiguration"; From 859ed45173b8fdf69bf26161e539cb88465237d4 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 8 Jun 2017 18:53:07 -0400 Subject: [PATCH 030/126] Missed file from previous commit --- TrustKit/TSKTrustKitConfig.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TrustKit/TSKTrustKitConfig.h b/TrustKit/TSKTrustKitConfig.h index 03446288..48df3e61 100644 --- a/TrustKit/TSKTrustKitConfig.h +++ b/TrustKit/TSKTrustKitConfig.h @@ -11,6 +11,13 @@ @import Foundation; + +/** + The version of TrustKit, such as "1.4.0". + */ +FOUNDATION_EXPORT NSString * const TrustKitVersion; + + #pragma mark Configuration Keys From 0c7e1171e565fbed8937f045b6cef113c593dead Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 8 Jun 2017 19:09:45 -0400 Subject: [PATCH 031/126] Add support for bundling alternative certificate authorities that are not trusted by the operating system --- TrustKit/TSKPinningValidator.m | 29 ++++++++++++++++++++++++-- TrustKit/TSKTrustKitConfig.h | 14 +++++++++++++ TrustKit/TSKTrustKitConfig.m | 1 + TrustKit/parse_configuration.m | 38 ++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 2 deletions(-) diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index 0f965e54..613e1dd2 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -15,6 +15,7 @@ #import "TSKPinningValidatorResult.h" #import "Pinning/TSKSPKIHashCache.h" #import "Pinning/ssl_pin_verifier.h" +#import "configuration_utils.h" #import "TrustKit.h" #import "TSKLog.h" @@ -78,14 +79,37 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: } else if ([domainConfig[kTSKExcludeSubdomainFromParentPolicy] boolValue]) { - // This is a subdomain that was explicitely excluded from the parent domain's policy + // This is a subdomain that was explicitly excluded from the parent domain's policy finalTrustDecision = TSKTrustDecisionDomainNotPinned; } else { + // Add bundled trust anchors if specified in the configuration + NSArray *additionalTrustAnchors = domainConfig[kTSKAdditionalTrustAnchors]; + if (additionalTrustAnchors.count) + { + SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)additionalTrustAnchors); + SecTrustSetAnchorCertificatesOnly(serverTrust, false); // trust union of OS and user anchor certificate sets + } + // The domain has a pinning policy that has not expired // Look for one the configured public key pins in the server's evaluated certificate chain - TSKPinValidationResult validationResult = verifyPublicKeyPin(serverTrust, serverHostname, domainConfig[kTSKPublicKeyAlgorithms], domainConfig[kTSKPublicKeyHashes], self.spkiHashCache); + TSKPinValidationResult validationResult = verifyPublicKeyPin(serverTrust, + serverHostname, + domainConfig[kTSKPublicKeyAlgorithms], + domainConfig[kTSKPublicKeyHashes], + self.spkiHashCache); + + // Remove configured additional trust anchors + if (additionalTrustAnchors.count) + { + // SecTrustSetAnchorCertificates is documented to restore default anchor + // certs if NULL is passed as the second parameter, but that param is also + // annotated as non-null, causing a warning and confusion... + CFArrayRef nullArray = NULL; + SecTrustSetAnchorCertificates(serverTrust, nullArray); + } + if (validationResult == TSKPinValidationResultSuccess) { // Pin validation was successful @@ -127,6 +151,7 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: } } } + // Send a notification after all validation is done; this will also trigger a report if pin validation failed if (self.validationResultQueue && self.validationResultHandler) { NSTimeInterval validationDuration = [NSDate timeIntervalSinceReferenceDate] - validationStartTime; diff --git a/TrustKit/TSKTrustKitConfig.h b/TrustKit/TSKTrustKitConfig.h index 48df3e61..5717414c 100644 --- a/TrustKit/TSKTrustKitConfig.h +++ b/TrustKit/TSKTrustKitConfig.h @@ -201,6 +201,20 @@ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKDisableDefaultReportUri; */ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKExpirationDate; +/** + An NSArray of additional trust anchors that can be used for validating the trust + chain of pinned certificates that do not end in an OS trusted CA anchor. + + The entries in the array should each be a single PEM-encdoded public certificate. + + ~~ SECURITY WARNING ~~ + Misuse of this configuration option could potentially render your application + vulnerable to exploits since it bypasses the normal operating system trust store. + It is intended for enterprise scenarios where a company might be running their + own internal production-grade certificate authority for debugging purposes. + */ +FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKAdditionalTrustAnchors; + #pragma mark Supported Public Key Algorithm Keys diff --git a/TrustKit/TSKTrustKitConfig.m b/TrustKit/TSKTrustKitConfig.m index c815bbce..ef2269a0 100644 --- a/TrustKit/TSKTrustKitConfig.m +++ b/TrustKit/TSKTrustKitConfig.m @@ -25,6 +25,7 @@ const TSKDomainConfigurationKey kTSKReportUris = @"TSKReportUris"; const TSKDomainConfigurationKey kTSKDisableDefaultReportUri = @"TSKDisableDefaultReportUri"; const TSKDomainConfigurationKey kTSKExpirationDate = @"TSKExpirationDate"; +const TSKDomainConfigurationKey kTSKAdditionalTrustAnchors = @"TSKAdditionalTrustAnchors"; #pragma mark Public key Algorithms Constants const TSKSupportedAlgorithm kTSKAlgorithmRsa2048 = @"TSKAlgorithmRsa2048"; diff --git a/TrustKit/parse_configuration.m b/TrustKit/parse_configuration.m index f0e86e6b..966a23fb 100644 --- a/TrustKit/parse_configuration.m +++ b/TrustKit/parse_configuration.m @@ -13,6 +13,7 @@ #import #import "configuration_utils.h" +SecCertificateRef certificateFromPEM(NSString *pem); NSDictionary *parseTrustKitConfiguration(NSDictionary *TrustKitArguments) { @@ -166,6 +167,22 @@ domainFinalConfiguration[kTSKDisableDefaultReportUri] = @(NO); } + NSArray *additionalTrustAnchors = domainPinningPolicy[kTSKAdditionalTrustAnchors]; + if (additionalTrustAnchors) + { + CFMutableArrayRef anchorCerts = CFArrayCreateMutable(NULL, (CFIndex)additionalTrustAnchors.count, &kCFTypeArrayCallBacks); + NSInteger certIndex = 0; // used for logging error messages + for (NSString *pem in additionalTrustAnchors) { + SecCertificateRef cert = certificateFromPEM(pem); + if (cert == nil) { + [NSException raise:@"TrustKit configuration invalid" + format:@"Failed to parse PEM-encoded certificate at index %ld for domain %@", (long)certIndex, domainName]; + } + CFArrayAppendValue(anchorCerts, cert); + certIndex++; + } + domainFinalConfiguration[kTSKAdditionalTrustAnchors] = [(__bridge NSMutableArray *)anchorCerts copy]; + } // Extract the list of public key algorithms to support and convert them from string to the TSKPublicKeyAlgorithm type NSArray *publicKeyAlgsStr = domainPinningPolicy[kTSKPublicKeyAlgorithms]; @@ -272,4 +289,25 @@ return [finalConfiguration copy]; } +SecCertificateRef certificateFromPEM(NSString *pem) +{ + // NOTE: multiple certificate PEM is not supported since these are anchor certificates + // + // Strip PEM header and footers. We don't support multi-certificate PEM. + NSMutableString *pemMutable = pem.mutableCopy; + [pemMutable replaceOccurrencesOfString:@"-----BEGIN CERTIFICATE-----" + withString:@"" + options:(NSStringCompareOptions)(NSAnchoredSearch | NSLiteralSearch) + range:NSMakeRange(0, pemMutable.length)]; + + [pemMutable replaceOccurrencesOfString:@"-----END CERTIFICATE-----" + withString:@"" + options:(NSStringCompareOptions)(NSAnchoredSearch | NSBackwardsSearch | NSLiteralSearch) + range:NSMakeRange(0, pemMutable.length)]; + + NSData *pemData = [[NSData alloc] initWithBase64EncodedString:pemMutable + options:NSDataBase64DecodingIgnoreUnknownCharacters]; + return SecCertificateCreateWithData(NULL, (CFDataRef)pemData); + +} From ac25f4f2ed85fff00065f71a709056273d6fd513 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Fri, 9 Jun 2017 14:20:17 -0400 Subject: [PATCH 032/126] Additional trust anchors are only respected if build with -DDEBUG=1. Otherwise the unsafe certificates will need to be in the OS trust store (OSX Keychain or iOS General > About > Trust Store) to work. --- TrustKit/TSKPinningValidator.h | 9 +++++++++ TrustKit/TSKPinningValidator.m | 35 +++++++++++++++++++--------------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 8482abf9..358260b7 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -41,6 +41,15 @@ typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef cert /// @name Manual SSL Pinning Validation ///------------------------------------ +/** + If this property returns YES, pinning may include any additional trust anchors + provided in a domain configuration under the kTSKAdditionalTrustAnchors key. + + This property is YES only when the preprocessor flag DEBUG is set to 1 (the + default behavior for the "Debug" configuration of an Xcode project). Subclasses + may override this method – with extreme caution – to alter this behavior. + */ +@property (nonatomic, class, readonly) BOOL allowsAdditionalTrustAnchors; /** Domain pinning configuration, typically obtained by parseTrustKitConfiguration() diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index 613e1dd2..d9bf1b99 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -25,6 +25,18 @@ @interface TSKPinningValidator () @implementation TSKPinningValidator ++ (BOOL)allowsAdditionalTrustAnchors +{ + static const BOOL allowAdditionalTrustAnchors = { +#if DEBUG == 1 + YES +#else + NO +#endif + }; + return allowAdditionalTrustAnchors; +} + #pragma mark Instance Methods - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains @@ -85,11 +97,14 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: else { // Add bundled trust anchors if specified in the configuration - NSArray *additionalTrustAnchors = domainConfig[kTSKAdditionalTrustAnchors]; - if (additionalTrustAnchors.count) - { - SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)additionalTrustAnchors); - SecTrustSetAnchorCertificatesOnly(serverTrust, false); // trust union of OS and user anchor certificate sets + if (self.class.allowsAdditionalTrustAnchors) { + NSArray *additionalTrustAnchors = domainConfig[kTSKAdditionalTrustAnchors]; + if (additionalTrustAnchors.count) + { + TSKLog(@"Pin validation includes %ld potentially unsafe trust anchors", (long)additionalTrustAnchors.count); + SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)additionalTrustAnchors); + SecTrustSetAnchorCertificatesOnly(serverTrust, false); // trust union of OS and user anchor certificate sets + } } // The domain has a pinning policy that has not expired @@ -100,16 +115,6 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: domainConfig[kTSKPublicKeyHashes], self.spkiHashCache); - // Remove configured additional trust anchors - if (additionalTrustAnchors.count) - { - // SecTrustSetAnchorCertificates is documented to restore default anchor - // certs if NULL is passed as the second parameter, but that param is also - // annotated as non-null, causing a warning and confusion... - CFArrayRef nullArray = NULL; - SecTrustSetAnchorCertificates(serverTrust, nullArray); - } - if (validationResult == TSKPinValidationResultSuccess) { // Pin validation was successful From d993c6aaaa442dca9f1ab83d7f4879f4e10fccf1 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Fri, 9 Jun 2017 14:43:06 -0400 Subject: [PATCH 033/126] Apply recommended project settings for Xcode 9 --- TrustKit.xcodeproj/project.pbxproj | 10 +++++++++- .../xcshareddata/xcschemes/TrustKit OS X.xcscheme | 2 +- .../xcshareddata/xcschemes/TrustKit tvOS.xcscheme | 2 +- .../xcshareddata/xcschemes/TrustKit watchOS.xcscheme | 2 +- .../xcshareddata/xcschemes/TrustKit.xcscheme | 2 +- .../xcshareddata/xcschemes/TrustKit_Static.xcscheme | 2 +- 6 files changed, 14 insertions(+), 6 deletions(-) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 606715e2..8e1f5c06 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -1053,7 +1053,7 @@ isa = PBXProject; attributes = { CLASSPREFIX = TSK; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = TrustKit; TargetAttributes = { 8C8480461A896EE30017C155 = { @@ -1387,7 +1387,9 @@ CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_ASSIGN_ENUM = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; @@ -1396,6 +1398,8 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; @@ -1450,7 +1454,9 @@ CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_ASSIGN_ENUM = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; @@ -1459,6 +1465,8 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; diff --git a/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit OS X.xcscheme b/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit OS X.xcscheme index fbb234a1..cfd86637 100644 --- a/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit OS X.xcscheme +++ b/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit OS X.xcscheme @@ -1,6 +1,6 @@ Date: Fri, 9 Jun 2017 15:54:21 -0400 Subject: [PATCH 034/126] Add custom trust anchor section to docs Probably should go to it's own page... --- docs/getting-started.md | 128 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/docs/getting-started.md b/docs/getting-started.md index ac31a209..c5ad6b2b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -190,6 +190,134 @@ access). This will give you an idea of how many users would be blocked, if pin validation was to be enforced. +### Debugging with TrustKit + +SSL pinning can make it difficult for developers to develop network clients or +troubleshoot network requests. Common tools like Charles Proxy use self-signed +SSL certificates to effectively create an (untrusted) SSL proxy. This allows +Charles to decrypt and read SSL-protected information for debugging or reverse +engineering purposes. Since this configuration is identical to a malicious +man-in-the-middle attack, TrustKit will reject such proxied SSL connections. + +There are several options available to mitigate this issue. + +#### For Small Development Teams + +Create a self-signed SSL certificate authority for Charles to use instead of it's +default randomly generated CA. This team CA cert & private key can be shared among +teammates and set as the custom root CA in each instance of Charles. The root CA +certificate must be installed and trusted in each iOS Simulator and Device used +for development. + +For debugging in production, users will need to trust the self-signed root CA – +which is potentially dangerous and requires the owner of the device to enter their +passcode. + +#### For Large or Enterprise Development Teams + +If you're working in an enterprise environment where sharing a root CA certificate +and private key is impractical, issuing intermediate CA certs and keys might work +better. In this case, you create a self-signed root certificate authority which is +maintained in a production-secure fashion. This CA can issue intermediate CA certs +for use with Charles or other tools, as needed. + +In this case, your SSL proxy can use an intermediate CA to proxy SSL requests. That +intermediate certifiate would not be pinned by TrustKit, but the self-signed root CA +would be. It's optional but recommended for the intermedate CA have a short validity +window (short expiration) and include a personal identifier for the employee that +requested the certificate (i.e. employee email in intermediate CA email field). This +way, if a rogue employee were to issue an intermediate CA certificate and bypass +protections in TrustKit, you'd be able to trace any attacks back to a person. + +#### Leveraging additional trust anchors + +Tired of adding the Charles certificate to dozens of iOS Simulators and devices on +a daily basis? + +TrustKit supports custom trust anchors which bypass the OS trust store. This means +that you can pin your untrusted root debugging certificate from Charles, but not +specifically add it to the OS trust store on your simulators/devices/computers. + +To use custom trust anchors, add the certificate strings to a list under the +kTSKAdditionalTrustAnchors configuration key. Each entry should include ony one +certificate in PEM format, with no password (it's a public key, right?). Exmaple: + +``` + NSDictionary *trustKitConfig = + @{ + // The list of domains we want to pin and their configuration + kTSKPinnedDomains: @{ + + @"yahoo.com" : @{ + // The custom trust anchors to use. If a certificate trust chain + // ends in a certificate in this list, the OS trust store is not + // consulted if compiled with DEBUG=1. + // WARNING: potentially unsafe. See "Debugging with TrustKit" section + kTSKAdditionalTrustAnchors : @[ + @"-----BEGIN CERTIFICATE-----\ + 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\ + WcqquNULAx3uOWA5ZeO0yiOGgqc=\ + -----END CERTIFICATE-----" + ]; + +``` + +#### Production exploit safeguards (and how to bypass them) + +By default TrustKit is hard-coded to ignore the custom trust anchors created using +the above processes in a production build. TrustKit does this by checking a define +in TSKPinningValidator: custom anchors are ignored unless #if DEBUG == 1. + +The idea is that kTSKAdditionalTrustAnchors configuration is primarily intended for +use during development. It simplifies your workflow by not requiring the iOS Simulator +or iOS device to manually add a custom OS trust anchor (or added to Keychain for macOS). + +There may be practical applications for supporting untrusted trust anchors in production. +For example: +* if your team is skilled in running secure CAs, this would allow very easy +debugging. +* Supporting select instituational proxies would be possible with additional trust anchors. + +##### Bypassing Custom Trust Anchor Safeguards In Production + +- Add DEBUG=1 to "Preprocessor Macros" in the Xcode target build settings for your +distribution configuration (named "Release" by default). This will probably cause +issues in your app. +- Subclass TSKPinningValidator and override +allowsAdditionalTrustAnchors to return +true. + #### Other configuration settings The list of all the configuration keys is available in the From a2f87781842be7fa5badd5fbc36ce1a3c1fad778 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Sat, 10 Jun 2017 22:11:52 -0400 Subject: [PATCH 035/126] Unit tests for kTSKAdditionalTrustAnchors --- TrustKit.xcodeproj/project.pbxproj | 33 +++++++++ TrustKit/TSKTrustKitConfig.h | 13 +++- TrustKit/parse_configuration.m | 17 +++-- .../TrustAnchor/anchor-ca.cert.pem | 36 ++++++++++ .../anchor-fake.yahoo.com.cert.pem | 31 ++++++++ .../TrustAnchor/anchor-intermediate.cert.pem | 30 ++++++++ TrustKitTests/TSKCertificateUtils.h | 4 +- TrustKitTests/TSKCertificateUtils.m | 27 +++++-- TrustKitTests/TSKPinConfigurationTests.m | 44 ++++++++++++ TrustKitTests/TSKPinningValidatorTests.m | 72 +++++++++++++++++++ 10 files changed, 294 insertions(+), 13 deletions(-) create mode 100644 TrustKitTests/Certificates/TrustAnchor/anchor-ca.cert.pem create mode 100644 TrustKitTests/Certificates/TrustAnchor/anchor-fake.yahoo.com.cert.pem create mode 100644 TrustKitTests/Certificates/TrustAnchor/anchor-intermediate.cert.pem diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 8e1f5c06..93b39464 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -221,6 +221,12 @@ 8CE919221AEA077F002B29AE /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */; }; 8CE919251AEA07C5002B29AE /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */; }; 8CE9192D1AEA0F7E002B29AE /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919291AEA0F7E002B29AE /* domain_registry.h */; }; + FC049B3A1EECD1B000FDC5F4 /* anchor-ca.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD051EECD19E00AB3D81 /* anchor-ca.cert.pem */; }; + FC049B3B1EECD1B000FDC5F4 /* anchor-fake.yahoo.com.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD061EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem */; }; + FC049B3C1EECD1B000FDC5F4 /* anchor-intermediate.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD071EECD19E00AB3D81 /* anchor-intermediate.cert.pem */; }; + FC049B3D1EECD1B100FDC5F4 /* anchor-ca.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD051EECD19E00AB3D81 /* anchor-ca.cert.pem */; }; + FC049B3E1EECD1B100FDC5F4 /* anchor-fake.yahoo.com.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD061EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem */; }; + FC049B3F1EECD1B100FDC5F4 /* anchor-intermediate.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD071EECD19E00AB3D81 /* anchor-intermediate.cert.pem */; }; FC1A09001E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; FC1A09011E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; FC1A09021E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -240,6 +246,9 @@ FC4CAC7B1E958E0500DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; FC4CAC7C1E96891A00DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; FC4CAC7D1E96891B00DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; + FCC1DD081EECD19E00AB3D81 /* anchor-ca.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD051EECD19E00AB3D81 /* anchor-ca.cert.pem */; }; + FCC1DD091EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD061EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem */; }; + FCC1DD0A1EECD19E00AB3D81 /* anchor-intermediate.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD071EECD19E00AB3D81 /* anchor-intermediate.cert.pem */; }; FCE7D62D1EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */; }; FCE7D62E1EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */; }; FCE7D62F1EE9F6610081EEEF /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */; }; @@ -400,6 +409,9 @@ FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKReportsRateLimiterTests.m; sourceTree = ""; }; FC4CAC7E1E96917B00DAC41E /* TSKTrustKitConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustKitConfig.h; sourceTree = ""; }; FC4CAC7F1E96A2D600DAC41E /* TSKLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKLog.h; sourceTree = ""; }; + FCC1DD051EECD19E00AB3D81 /* anchor-ca.cert.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "anchor-ca.cert.pem"; sourceTree = ""; }; + FCC1DD061EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "anchor-fake.yahoo.com.cert.pem"; sourceTree = ""; }; + FCC1DD071EECD19E00AB3D81 /* anchor-intermediate.cert.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "anchor-intermediate.cert.pem"; sourceTree = ""; }; FCD0CC031E96ADF00076D431 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKTrustKitConfig.m; sourceTree = ""; }; FCE7D6381EEA04180081EEEF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -475,6 +487,7 @@ 070868B31ADFF68200E5AFDC /* Certificates */ = { isa = PBXGroup; children = ( + FCC1DD041EECD19E00AB3D81 /* TrustAnchor */, 8CBA05B31E28739D0045D8B3 /* ECDSA sec384r1 */, 8CC78B1B1B1B552100523A25 /* RSA 4096 */, 8CC78B1A1B1B551400523A25 /* RSA 2048 */, @@ -763,6 +776,17 @@ name = Configuration; sourceTree = ""; }; + FCC1DD041EECD19E00AB3D81 /* TrustAnchor */ = { + isa = PBXGroup; + children = ( + FCC1DD051EECD19E00AB3D81 /* anchor-ca.cert.pem */, + FCC1DD061EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem */, + FCC1DD071EECD19E00AB3D81 /* anchor-intermediate.cert.pem */, + ); + name = TrustAnchor; + path = Certificates/TrustAnchor; + sourceTree = ""; + }; FCD0CC021E96ADF00076D431 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1111,9 +1135,12 @@ buildActionMask = 2147483647; files = ( 8CBA05D81E28AAA40045D8B3 /* GoodIntermediateCA.der in Resources */, + FCC1DD091EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem in Resources */, 8CBA05E51E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der in Resources */, + FCC1DD0A1EECD19E00AB3D81 /* anchor-intermediate.cert.pem in Resources */, 8CBA05E11E28AAA40045D8B3 /* www.good.com.selfsigned.der in Resources */, 8CBA05C71E28AA6C0045D8B3 /* GlobalSignDomainValidationCA-SHA256-G2.der in Resources */, + FCC1DD081EECD19E00AB3D81 /* anchor-ca.cert.pem in Resources */, 8CBA05DB1E28AAA40045D8B3 /* GoodRootCA.der in Resources */, 8CBA05CD1E28AA6C0045D8B3 /* www.globalsign.com.der in Resources */, 8CBA05D11E28AA7B0045D8B3 /* www.cloudflare.com.der in Resources */, @@ -1134,9 +1161,12 @@ buildActionMask = 2147483647; files = ( 8CBA05DA1E28AAA40045D8B3 /* GoodIntermediateCA.der in Resources */, + FC049B3E1EECD1B100FDC5F4 /* anchor-fake.yahoo.com.cert.pem in Resources */, 8CBA05E71E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der in Resources */, + FC049B3F1EECD1B100FDC5F4 /* anchor-intermediate.cert.pem in Resources */, 8CBA05E31E28AAA40045D8B3 /* www.good.com.selfsigned.der in Resources */, 8CBA05C91E28AA6C0045D8B3 /* GlobalSignDomainValidationCA-SHA256-G2.der in Resources */, + FC049B3D1EECD1B100FDC5F4 /* anchor-ca.cert.pem in Resources */, 8CBA05DD1E28AAA40045D8B3 /* GoodRootCA.der in Resources */, 8CBA05CF1E28AA6C0045D8B3 /* www.globalsign.com.der in Resources */, 8CBA05D31E28AA7B0045D8B3 /* www.cloudflare.com.der in Resources */, @@ -1157,9 +1187,12 @@ buildActionMask = 2147483647; files = ( 8CBA05D91E28AAA40045D8B3 /* GoodIntermediateCA.der in Resources */, + FC049B3B1EECD1B000FDC5F4 /* anchor-fake.yahoo.com.cert.pem in Resources */, 8CBA05E61E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der in Resources */, + FC049B3C1EECD1B000FDC5F4 /* anchor-intermediate.cert.pem in Resources */, 8CBA05E21E28AAA40045D8B3 /* www.good.com.selfsigned.der in Resources */, 8CBA05C81E28AA6C0045D8B3 /* GlobalSignDomainValidationCA-SHA256-G2.der in Resources */, + FC049B3A1EECD1B000FDC5F4 /* anchor-ca.cert.pem in Resources */, 8CBA05DC1E28AAA40045D8B3 /* GoodRootCA.der in Resources */, 8CBA05CE1E28AA6C0045D8B3 /* www.globalsign.com.der in Resources */, 8CBA05D21E28AA7B0045D8B3 /* www.cloudflare.com.der in Resources */, diff --git a/TrustKit/TSKTrustKitConfig.h b/TrustKit/TSKTrustKitConfig.h index 5717414c..48b47720 100644 --- a/TrustKit/TSKTrustKitConfig.h +++ b/TrustKit/TSKTrustKitConfig.h @@ -202,10 +202,16 @@ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKDisableDefaultReportUri; FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKExpirationDate; /** - An NSArray of additional trust anchors that can be used for validating the trust - chain of pinned certificates that do not end in an OS trusted CA anchor. + An array of strings representing additional trust anchors usable for validating + the trust chain of pinned certificates that do not end in an OS trust anchor. - The entries in the array should each be a single PEM-encdoded public certificate. + The default behavior of TrustKit is to ignore these trust anchors unless compiled + in debug mode (pass -DDEBUG=1 to the preprocessor). This behavior can be modified + by subclassing TSKPinningValidator and overriding +allowsAdditionalTrustAnchors. + + The entries in the array should each be a single PEM-encoded public certificate. + Standard RFC-7468 PEM format is supported (see https://tools.ietf.org/html/rfc7468#section-2 ). + Note that the header, footer and any newlines are optional, but aid in readability. ~~ SECURITY WARNING ~~ Misuse of this configuration option could potentially render your application @@ -215,6 +221,7 @@ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKExpirationDate; */ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKAdditionalTrustAnchors; + #pragma mark Supported Public Key Algorithm Keys diff --git a/TrustKit/parse_configuration.m b/TrustKit/parse_configuration.m index 966a23fb..5bfb891a 100644 --- a/TrustKit/parse_configuration.m +++ b/TrustKit/parse_configuration.m @@ -291,10 +291,13 @@ SecCertificateRef certificateFromPEM(NSString *pem) { - // NOTE: multiple certificate PEM is not supported since these are anchor certificates - // + // NOTE: multi-certificate PEM is not supported since this is for individual + // trust anchor certificates. + // Strip PEM header and footers. We don't support multi-certificate PEM. - NSMutableString *pemMutable = pem.mutableCopy; + NSMutableString *pemMutable = [pem stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet].mutableCopy; + + // Strip PEM header and footer [pemMutable replaceOccurrencesOfString:@"-----BEGIN CERTIFICATE-----" withString:@"" options:(NSStringCompareOptions)(NSAnchoredSearch | NSLiteralSearch) @@ -307,7 +310,11 @@ SecCertificateRef certificateFromPEM(NSString *pem) NSData *pemData = [[NSData alloc] initWithBase64EncodedString:pemMutable options:NSDataBase64DecodingIgnoreUnknownCharacters]; - return SecCertificateCreateWithData(NULL, (CFDataRef)pemData); - + SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef)pemData); + if (!cert) + { + [NSException raise:@"TrustKit configuration invalid" format:@"Failed to parse PEM certificate"]; + } + return cert; } diff --git a/TrustKitTests/Certificates/TrustAnchor/anchor-ca.cert.pem b/TrustKitTests/Certificates/TrustAnchor/anchor-ca.cert.pem new file mode 100644 index 00000000..21d625d2 --- /dev/null +++ b/TrustKitTests/Certificates/TrustAnchor/anchor-ca.cert.pem @@ -0,0 +1,36 @@ +-----BEGIN CERTIFICATE----- +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 +WcqquNULAx3uOWA5ZeO0yiOGgqc= +-----END CERTIFICATE----- diff --git a/TrustKitTests/Certificates/TrustAnchor/anchor-fake.yahoo.com.cert.pem b/TrustKitTests/Certificates/TrustAnchor/anchor-fake.yahoo.com.cert.pem new file mode 100644 index 00000000..277dd405 --- /dev/null +++ b/TrustKitTests/Certificates/TrustAnchor/anchor-fake.yahoo.com.cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFSjCCBDKgAwIBAgICEEwwDQYJKoZIhvcNAQELBQAwgaoxCzAJBgNVBAYTAlVT +MRMwEQYDVQQIDApDYWxpZm9ybmlhMRMwEQYDVQQKDApZYWhvbywgSW5jMRswGQYD +VQQLDBJQdWJsaXNoZXIgUHJvZHVjdHMxLTArBgNVBAMMJFBpbmN1c2hpb24gRXBo +ZW1lcmFsIEludGVybWVkaWF0ZSBDQTElMCMGCSqGSIb3DQEJARYWbm8tcmVwbHlA +eWFob28taW5jLmNvbTAeFw0xNzA2MDkyMDQ1MTJaFw00MjA2MDMyMDQ1MTJaMIGo +MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU3Vu +bnl2YWxlMRMwEQYDVQQKDApZYWhvbywgSW5jMRswGQYDVQQLDBJQdWJsaXNoZXIg +UHJvZHVjdHMxFzAVBgNVBAMMDmZha2UueWFob28uY29tMSUwIwYJKoZIhvcNAQkB +FhZuby1yZXBseUB5YWhvby1pbmMuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA3tIexwPqDC4qRF3G1EtRZlEwOFhMFcsjp7Fhc+YbAqtGVZwSEqo7 +YJfTGh3byCwikoBy/ZZTSVxDocY5k8UIfEGQJEwxuRZSc+iMWJkqeblwFbHeR0lw +nH033o53E/n4Yj57cIl2/GQ+jZSetLoxLKnf4N7FOje7+qDOWaSGxNHD5KCaBG/j +cpF14PKI09MXza1KqOtxTnd+0cqtUNV2EsTEE6INiVC9f4L7QmLoZDnemQF250fe +jEfKyK+x2b+dcRKdNkD5Xmap3w++9Jd3dBWEIZv8l6518t/307z61ATL9dpaW9fn +xgh6nIhJ/tUlq8bU+BXFJZKJANNksifXJwIDAQABo4IBeDCCAXQwCQYDVR0TBAIw +ADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2Vu +ZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUxok2/uVwT+tXOG61 +D+CweINe/u8wgdoGA1UdIwSB0jCBz4AUNdswOI7W+txCQKery4t0kNkMQByhgbKk +ga8wgawxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQH +DAlTdW5ueXZhbGUxEzARBgNVBAoMCllhaG9vLCBJbmMxGzAZBgNVBAsMElB1Ymxp +c2hlciBQcm9kdWN0czEZMBcGA1UEAwwQUGluY3VzaGlvbiBEZWJ1ZzEnMCUGCSqG +SIb3DQEJARYYYWRhbWthcGxhbkB5YWhvby1pbmMuY29tggIQSjAOBgNVHQ8BAf8E +BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAJqo +iCLaILCV+qK18icv5UNUkeNKOtreWHTK0CSFAIZ02Xft2nAQhDwGMd4TPcDJEQR0 +yChiMciQWBXxpHE+iTgES/NXAtirU/2KkimisVw4lMJQ4+AYhCnBIUEbeFfW0gNy +xtbQ0p/qyOt1bKe998TObc+OPfCP0QmDQGFzpXebn0rtFNZOhBB2c+jZJ18yW+R3 +ZirArIhPhQqE/WXMxtkdTel9uHxHTDdEPvvysW6wVYSsIrOF9OCTGRF57UMv2Sxb +OhoqmGdfRicDH+PNnKsCTiBY7UuD1SEUZIJiS70mHPHKzmIZ2HUSIVoITm8v4hIA +25ZjY8e0wSkXyoSiBeY= +-----END CERTIFICATE----- diff --git a/TrustKitTests/Certificates/TrustAnchor/anchor-intermediate.cert.pem b/TrustKitTests/Certificates/TrustAnchor/anchor-intermediate.cert.pem new file mode 100644 index 00000000..5bdf58a8 --- /dev/null +++ b/TrustKitTests/Certificates/TrustAnchor/anchor-intermediate.cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFOjCCAyKgAwIBAgICEEowDQYJKoZIhvcNAQELBQAwgawxCzAJBgNVBAYTAlVT +MRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTdW5ueXZhbGUxEzARBgNV +BAoMCllhaG9vLCBJbmMxGzAZBgNVBAsMElB1Ymxpc2hlciBQcm9kdWN0czEZMBcG +A1UEAwwQUGluY3VzaGlvbiBEZWJ1ZzEnMCUGCSqGSIb3DQEJARYYYWRhbWthcGxh +bkB5YWhvby1pbmMuY29tMB4XDTE3MDYwOTIwMTcyNFoXDTM3MDYwOTIwMTcyNFow +gaoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRMwEQYDVQQKDApZ +YWhvbywgSW5jMRswGQYDVQQLDBJQdWJsaXNoZXIgUHJvZHVjdHMxLTArBgNVBAMM +JFBpbmN1c2hpb24gRXBoZW1lcmFsIEludGVybWVkaWF0ZSBDQTElMCMGCSqGSIb3 +DQEJARYWbm8tcmVwbHlAeWFob28taW5jLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMSCXZEEIWfbHEZg4NzjlY8J4GjxNaC9CQ+o/El/NFFAgTkc +BIxShkTOg2mwnHuenWhsvvH4hJOT/1aliLCvRX9x+EcbJGcmAO+Op+lrt+090MQG +RbsjvmdregsRtmFX8ScXAulSi345BzQFHSpATO8lEO7d9a6IC3q/lL2U2Y8EYYLV +xvvWOIkEUMj3t3HRBoZ/UJX7syfsrOOdeJJehIWmMhpudI1r3m7kasWWGRjFE1J7 +FVquRE6al4vHdh0RxJbuU2vX/eqWbr42I2xgnsIFkUO5aJj6ilZ/gNMp40Ct8ouk +nDlwb18t7EeF9PEDouUAY+SMn3r1BuuLSaoti+ECAwEAAaNmMGQwHQYDVR0OBBYE +FDXbMDiO1vrcQkCnq8uLdJDZDEAcMB8GA1UdIwQYMBaAFB5/kaZXVHpqBIhxQL6U +vIigkWEcMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqG +SIb3DQEBCwUAA4ICAQC3mEBzBnX294fFxYn1VNQq7B71zRIsgOfupT/w3SXk2LWf +utBcQmEIBKyCtX7fSFcQMDyU5X4b8jlRg8V7zehlFd4rp/4jtMyYLvHloOgJejtB +n2W00GUSgxfEgYzU7g/cWNopj2jxZjH3nP7rOHWw9m0XTYgYoyU7Zyuj/zMSN5SF +ArvlbYOYMTqmqWb2rHiNxiXwP+ldGCtOV24Enygc1QwKpHpU0PYTOl8eqes1RTb6 +VMT0MfkMwNG3QE9HU3CPssRfDT839DmVE/qIMSmg0DVJpKfvx5l6XAcG44AcR9Sc +fP2mLsueUqZz81B4X/5gMaRCPEjAYvczi/gaZHaMdY2UyngpcH9HsT7pa0RxB+a/ +4VO2NwtHtaerSD5ZrKhbXNskqtuI/T5N9C4zEriMjW1lcCD6wH3cZ9t7YioWc+1/ +PWEq7tDp3CWdghWp0aHo/BYhSdsl26soKoP5tCHekV8Mvpi+fHjj9JxGTJnrK5Bs +kaFMIsnBVl+eTPaQSN1zI00fck8hQ2z+8nv2tCwanHes7QyuPgLWiGMRZnSU6eaT +SQ8Jh4H+8JkcaMAYgnSAPjfI4rFFgjPAMdHrVSSASFI+C2ap6iFZ65TdSso638RT +KUfap5rkzQIPM1I0j5KEmeHYeMMssmQSkDMzTs013S27FzhADZCjgVBTF3DHDQ== +-----END CERTIFICATE----- diff --git a/TrustKitTests/TSKCertificateUtils.h b/TrustKitTests/TSKCertificateUtils.h index 1e5d9d83..8c0d5aad 100644 --- a/TrustKitTests/TSKCertificateUtils.h +++ b/TrustKitTests/TSKCertificateUtils.h @@ -13,7 +13,9 @@ @interface TSKCertificateUtils : NSObject -+ (SecCertificateRef)createCertificateFromDer:(NSString *)derCertiticatePath; ++ (SecCertificateRef)createCertificateFromPem:(NSString *)pemFilename; + ++ (SecCertificateRef)createCertificateFromDer:(NSString *)derFilename; + (SecTrustRef)createTrustWithCertificates:(const void **)certArray arrayLength:(NSInteger)certArrayLength diff --git a/TrustKitTests/TSKCertificateUtils.m b/TrustKitTests/TSKCertificateUtils.m index 9d496444..9e2d5119 100644 --- a/TrustKitTests/TSKCertificateUtils.m +++ b/TrustKitTests/TSKCertificateUtils.m @@ -13,13 +13,32 @@ @implementation TSKCertificateUtils -+ (SecCertificateRef)createCertificateFromDer:(NSString *)derCertiticatePath ++ (SecCertificateRef)createCertificateFromPem:(NSString *)pemFilename +{ + NSBundle *bundle = [NSBundle bundleForClass:self.class]; + NSString *pemFilepath = [bundle pathForResource:pemFilename ofType:@"pem"]; + NSString *pemString = [NSString stringWithContentsOfFile:pemFilepath usedEncoding:nil error:nil]; + pemString ?: [NSException raise:@"Test error" format:@"Could not open PEM file %@", pemFilename]; + + pemString = [pemString stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]; + pemString = [pemString stringByReplacingOccurrencesOfString:@"-----BEGIN CERTIFICATE-----" withString:@""]; + pemString = [pemString stringByReplacingOccurrencesOfString:@"-----END CERTIFICATE-----" withString:@""]; + + NSData *der = [[NSData alloc] initWithBase64EncodedString:pemString options:NSDataBase64DecodingIgnoreUnknownCharacters]; + der ?: [NSException raise:@"Test error" format:@"Could not convert PEM to DER from %@", pemFilename]; + + SecCertificateRef certificate = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)der); + return certificate; +} + + ++ (SecCertificateRef)createCertificateFromDer:(NSString *)derFilename { NSBundle *bundle = [NSBundle bundleForClass:[self class]]; - CFDataRef certData = (__bridge_retained CFDataRef)[NSData dataWithContentsOfFile:[bundle pathForResource:derCertiticatePath ofType:@"der"]]; + CFDataRef certData = (__bridge_retained CFDataRef)[NSData dataWithContentsOfFile:[bundle pathForResource:derFilename ofType:@"der"]]; if (!certData) { - [NSException raise:@"Test error" format:@"Could not open certificate at path %@", derCertiticatePath]; + [NSException raise:@"Test error" format:@"Could not open certificate at path %@", derFilename]; } SecCertificateRef certificate = SecCertificateCreateWithData(kCFAllocatorDefault, certData); CFRelease(certData); @@ -32,7 +51,7 @@ + (SecTrustRef)createTrustWithCertificates:(const void **)certArray anchorCertificates:(const void **)anchorCertificates arrayLength:(NSInteger)anchorArrayLength { - CFArrayRef certificateChain = CFArrayCreate(NULL, (const void **)certArray, certArrayLength, NULL); + CFArrayRef certificateChain = CFArrayCreate(NULL, (const void **)certArray, certArrayLength, &kCFTypeArrayCallBacks); SecPolicyRef policy = SecPolicyCreateSSL(true, NULL); SecTrustRef trust; diff --git a/TrustKitTests/TSKPinConfigurationTests.m b/TrustKitTests/TSKPinConfigurationTests.m index a0039a58..b926b333 100644 --- a/TrustKitTests/TSKPinConfigurationTests.m +++ b/TrustKitTests/TSKPinConfigurationTests.m @@ -16,6 +16,7 @@ #import "../TrustKit/Pinning/ssl_pin_verifier.h" #import "../TrustKit/Pinning/TSKPublicKeyAlgorithm.h" #import "../TrustKit/parse_configuration.h" +#import "TSKCertificateUtils.h" @interface TSKPinConfigurationTests : XCTestCase @end @@ -340,4 +341,47 @@ - (void)testGlobalSettings @"kTSKSwizzleNetworkDelegates was not saved in the configuration"); } +- (void)testAdditionalTrustAnchors +{ + // Load trust anchor + NSBundle *bundle = [NSBundle bundleForClass:self.class]; + NSString *base64pem = [NSString stringWithContentsOfFile:[bundle pathForResource:@"anchor-ca.cert" ofType:@"pem"] + encoding:NSUTF8StringEncoding + error:nil]; + XCTAssertNotNil(base64pem); + + NSDictionary *trustKitConfig; + trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, + kTSKPinnedDomains : @{ + @"fake.yahoo.com": @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" + ], + kTSKAdditionalTrustAnchors : @[ base64pem ] + }}}); + + NSArray *trustAnchors = trustKitConfig[kTSKPinnedDomains][@"fake.yahoo.com"][kTSKAdditionalTrustAnchors]; + XCTAssertEqual(trustAnchors.count, (NSUInteger)1, @"Expected one trust anchor"); + + // We'll check the data from the certificate. That data will be in DER format, + // so when we output it, we're getting a clean representation of the original + // PEM file contents. + // To get an appropriate comparison string, we'll have to parse the PEM into + // DER, essentially removing the header, footer, and base64-encoding. Then + // we'll re-output the fresh DER back into PEM to ensure the line breaks and + // other Foundation-specific formatting is applied. + SecCertificateRef expectCert = [TSKCertificateUtils createCertificateFromPem:@"anchor-ca.cert"]; + NSData *expectCertData = (__bridge_transfer NSData *)SecCertificateCopyData(expectCert); + NSString *expectPem = [expectCertData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; + CFRelease(expectCert); + + // Grab the certificate, extract the DER data and format as PEM (base64-encode + // with line breaks). + SecCertificateRef certificate = (__bridge SecCertificateRef)trustAnchors.firstObject; + NSData *certificateData = (__bridge_transfer NSData *)SecCertificateCopyData(certificate); + NSString *gotPem = [certificateData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; + XCTAssertEqualObjects(expectPem, gotPem); +} + @end diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 7295b88c..32090ea7 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -37,6 +37,16 @@ @interface TSKSPKIHashCache (TestSupport) - (void)resetSubjectPublicKeyInfoDiskCache; @end +static BOOL AllowsAdditionalTrustAnchors = YES; // toggle in tests if needed +@interface TestPinningValidator: TSKPinningValidator +@end +@implementation TestPinningValidator ++ (BOOL)allowsAdditionalTrustAnchors +{ + return AllowsAdditionalTrustAnchors; +} +@end + @interface TSKPinningValidatorTests : XCTestCase @end @@ -1081,4 +1091,66 @@ - (void)testExcludedSubdomain CFRelease(trust); } +- (void)testAdditionalTrustAnchors +{ + // Load and set custom trust anchor + SecCertificateRef anchor = [TSKCertificateUtils createCertificateFromPem:@"anchor-ca.cert"]; + CFArrayRef anchors = CFArrayCreate(NULL, (const void **)&anchor, 1, &kCFTypeArrayCallBacks); + CFRelease(anchor); + + NSDictionary *config = @{ kTSKPinnedDomains : @{ + @"fake.yahoo.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0="], + kTSKAdditionalTrustAnchors : (__bridge_transfer NSArray *)anchors } } }; + + TestPinningValidator *pinningValidator = [[TestPinningValidator alloc] initWithPinnedDomainConfig:config + hashCache:spkiCache + ignorePinsForUserTrustAnchors:YES + validationResultQueue:dispatch_get_main_queue() + validationResultHandler:^(TSKPinningValidatorResult *x) {}]; + XCTAssertTrue(TestPinningValidator.allowsAdditionalTrustAnchors); + + SecTrustRef(^createTestServerTrust)(void) = ^() { + // Create the server trust for this chain + SecCertificateRef intermediate = [TSKCertificateUtils createCertificateFromPem:@"anchor-intermediate.cert"]; + SecCertificateRef leaf = [TSKCertificateUtils createCertificateFromPem:@"anchor-fake.yahoo.com.cert"]; + SecCertificateRef certChainArray[2] = {leaf, intermediate}; + + SecTrustRef trust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:2 + anchorCertificates:(const void **)NULL + arrayLength:0]; + CFRelease(leaf); + CFRelease(intermediate); + + // Set a date in which these certificates are valid (any time after July 2017 is fine) + SecTrustSetVerifyDate(trust, (__bridge CFDateRef)[NSDate dateWithTimeIntervalSince1970:1497145990]); + return trust; + }; + + SecTrustRef serverTrust; + TSKTrustDecision decision; + + // Trust anchors should be applied only if the allowsAdditionalTrustAnchors + // class property returns true + { + AllowsAdditionalTrustAnchors = YES; + serverTrust = createTestServerTrust(); + decision = [pinningValidator evaluateTrust:serverTrust forHostname:@"fake.yahoo.com"]; + XCTAssertEqual(decision, TSKTrustDecisionShouldAllowConnection); + CFRelease(serverTrust); + } + + // Trust anchors should be ignored if the allowsAdditionalTrustAnchors class + // property returns false + { + AllowsAdditionalTrustAnchors = NO; + serverTrust = createTestServerTrust(); + decision = [pinningValidator evaluateTrust:serverTrust forHostname:@"fake.yahoo.com"]; + XCTAssertEqual(decision, TSKTrustDecisionShouldBlockConnection); + CFRelease(serverTrust); + } +} + @end From e33b0754fe8d3ae710384ebb20ea11b8fb4edf98 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Sat, 10 Jun 2017 22:17:35 -0400 Subject: [PATCH 036/126] Remove unneeded test assert --- TrustKitTests/TSKPinningValidatorTests.m | 1 - 1 file changed, 1 deletion(-) diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 32090ea7..8546b70a 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -1109,7 +1109,6 @@ - (void)testAdditionalTrustAnchors ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() validationResultHandler:^(TSKPinningValidatorResult *x) {}]; - XCTAssertTrue(TestPinningValidator.allowsAdditionalTrustAnchors); SecTrustRef(^createTestServerTrust)(void) = ^() { // Create the server trust for this chain From 4cc3f64a711941a13bf88961d366634bba643074 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 10:51:36 -0700 Subject: [PATCH 037/126] Update public suffix list --- .../registry_tables.h | 15486 ++++++++-------- 1 file changed, 7835 insertions(+), 7651 deletions(-) diff --git a/TrustKit/Dependencies/domain_registry/registry_tables_genfiles/registry_tables.h b/TrustKit/Dependencies/domain_registry/registry_tables_genfiles/registry_tables.h index 665abb41..bc207925 100644 --- a/TrustKit/Dependencies/domain_registry/registry_tables_genfiles/registry_tables.h +++ b/TrustKit/Dependencies/domain_registry/registry_tables_genfiles/registry_tables.h @@ -1,7 +1,7 @@ -/* Size of kStringTable 43554 */ -/* Size of kNodeTable 3549 */ +/* Size of kStringTable 44345 */ +/* Size of kNodeTable 3715 */ /* Size of kLeafNodeTable 4215 */ -/* Total size 73278 bytes */ +/* Total size 75065 bytes */ static const char kStringTable[] = "aaa\0" "aarp\0" "abarth\0" "abb\0" "abbott\0" "abbvie\0" "abc\0" @@ -24,7 +24,7 @@ static const char kStringTable[] = "bargains\0" "baseball\0" "basketball\0" "bauhaus\0" "bayern\0" "bbc\0" "bbt\0" "bbva\0" "bcg\0" "bcn\0" "xn--mgbab2bd\0" "kobe\0" "beats\0" "beauty\0" "servebeer\0" "bentley\0" "berlin\0" "best\0" -"bestbuy\0" "bet\0" "xn--mgb9awbf\0" "bg\0" "gmbh\0" "bharti\0" +"bestbuy\0" "bet\0" "xn--mgb9awbf\0" "cbg\0" "gmbh\0" "bharti\0" "sbi\0" "bible\0" "bid\0" "bike\0" "plumbing\0" "bingo\0" "bio\0" "ebiz\0" "bj\0" "black\0" "blackfriday\0" "blanco\0" "blockbuster\0" "serveblog\0" "bloomberg\0" "blue\0" "ibm\0" "bms\0" "bmw\0" "cbn\0" @@ -56,154 +56,154 @@ static const char kStringTable[] = "dance\0" "alwaysdata\0" "hakodate\0" "dating\0" "datsun\0" "today\0" "dclk\0" "dds\0" "iide\0" "deal\0" "dealer\0" "deals\0" "degree\0" "delivery\0" "dell\0" "deloitte\0" "delta\0" "is-a-democrat\0" "dental\0" -"dentist\0" "desi\0" "artanddesign\0" "dev\0" "dhl\0" "diamonds\0" +"dentist\0" "desi\0" "artanddesign\0" "awdev\0" "dhl\0" "diamonds\0" "diet\0" "digital\0" "nextdirect\0" "myactivedirectory\0" "discount\0" "discover\0" "dish\0" "diy\0" "dj\0" "tdk\0" "adm\0" "dnp\0" "godo\0" "docs\0" "is-a-doctor\0" "dodge\0" "dog\0" "doha\0" "domains\0" "dot\0" "download\0" "drive\0" "dtv\0" "dubai\0" "dunlop\0" "duns\0" -"dupont\0" "durban\0" "dvr\0" "dwg\0" "czeladz\0" "earth\0" "seat\0" -"rec\0" "iveco\0" "edeka\0" "edu\0" "arteducation\0" "tree\0" "leg\0" -"email\0" "emerck\0" "energy\0" "is-an-engineer\0" "engineering\0" -"enterprises\0" "epost\0" "epson\0" "farmequipment\0" "lier\0" "ericsson\0" -"terni\0" "neues\0" "esq\0" "realestate\0" "esurance\0" "fet\0" -"etisalat\0" "eu\0" "eurovision\0" "eus\0" "events\0" "everbank\0" -"serveexchange\0" "geometre-expert\0" "exposed\0" "orientexpress\0" -"extraspace\0" "fage\0" "fail\0" "fairwinds\0" "faith\0" "tuxfamily\0" -"mlbfan\0" "fans\0" "statefarm\0" "farmers\0" "fashion\0" "fast\0" -"fedex\0" "feedback\0" "ferrari\0" "ferrero\0" "sanofi\0" "fiat\0" -"fidelity\0" "fido\0" "film\0" "final\0" "finance\0" "lplfinancial\0" -"fire\0" "firestone\0" "firmdale\0" "fish\0" "fishing\0" "fit\0" -"fitness\0" "fj\0" "jfk\0" "flickr\0" "flights\0" "flir\0" "florist\0" -"flowers\0" "fly\0" "ifm\0" "ortsinfo\0" "foo\0" "food\0" "foodnetwork\0" -"football\0" "oxford\0" "forex\0" "forsale\0" "forum\0" "foundation\0" -"fox\0" "sfr\0" "dyndns-free\0" "fresenius\0" "frl\0" "frogans\0" -"frontdoor\0" "frontier\0" "ftr\0" "fujitsu\0" "fujixerox\0" "fun\0" -"fund\0" "furniture\0" "futbol\0" "fyi\0" "saga\0" "legal\0" "artgallery\0" -"gallo\0" "gallup\0" "marugame\0" "is-into-games\0" "gap\0" "usgarden\0" -"xn--sandnessjen-ogb\0" "gbiz\0" "gd\0" "gdn\0" "koge\0" "gea\0" -"gent\0" "genting\0" "george\0" "ggf\0" "gg\0" "ggee\0" "pittsburgh\0" -"nogi\0" "gift\0" "gifts\0" "gives\0" "giving\0" "gl\0" "glade\0" -"glass\0" "withgoogle\0" "global\0" "globo\0" "xn--fzys8d69uvgm\0" -"gmail\0" "gmo\0" "gmx\0" "bjugn\0" "godaddy\0" "gold\0" "goldpoint\0" -"golf\0" "goo\0" "goodhands\0" "goodyear\0" "goog\0" "gop\0" "forgot\0" -"chernigov\0" "gp\0" "gq\0" "agr\0" "grainger\0" "graphics\0" "gratis\0" -"is-a-green\0" "gripe\0" "grocery\0" "stcgroup\0" "vgs\0" "gt\0" -"ivgu\0" "theguardian\0" "gucci\0" "guge\0" "guide\0" "guitars\0" -"is-a-guru\0" "rzgw\0" "hair\0" "hamburg\0" "hangout\0" "hbo\0" -"hdfc\0" "hdfcbank\0" "health\0" "help\0" "helsinki\0" "thruhere\0" -"hermes\0" "hgtv\0" "hiphop\0" "hisamitsu\0" "hitachi\0" "chernihiv\0" -"nhk\0" "hkt\0" "hm\0" "stjohn\0" "hockey\0" "holdings\0" "holiday\0" -"homedepot\0" "homegoods\0" "homes\0" "homesense\0" "honda\0" "honeywell\0" -"horse\0" "hospital\0" "nfshost\0" "futurehosting\0" "hot\0" "hoteles\0" -"kerryhotels\0" "hotmail\0" "mulhouse\0" "show\0" "ruhr\0" "hsbc\0" -"recht\0" "htc\0" "sohu\0" "hughes\0" "hyatt\0" "hyundai\0" "icbc\0" -"venice\0" "fie\0" "ieee\0" "iinet\0" "ikano\0" "mil\0" "cim\0" -"imamat\0" "imdb\0" "immo\0" "immobilien\0" "fin\0" "industries\0" +"dupont\0" "durban\0" "dvr\0" "czeladz\0" "earth\0" "seat\0" "nec\0" +"iveco\0" "edeka\0" "edu\0" "arteducation\0" "tree\0" "leg\0" "email\0" +"emerck\0" "energy\0" "is-an-engineer\0" "engineering\0" "enterprises\0" +"epost\0" "epson\0" "farmequipment\0" "lier\0" "ericsson\0" "terni\0" +"neues\0" "esq\0" "realestate\0" "esurance\0" "fet\0" "etisalat\0" +"eu\0" "eurovision\0" "eus\0" "events\0" "everbank\0" "serveexchange\0" +"geometre-expert\0" "exposed\0" "extraspace\0" "fage\0" "fail\0" +"fairwinds\0" "faith\0" "tuxfamily\0" "mlbfan\0" "fans\0" "statefarm\0" +"farmers\0" "fashion\0" "fast\0" "fedex\0" "feedback\0" "ferrari\0" +"ferrero\0" "sanofi\0" "fiat\0" "fidelity\0" "fido\0" "film\0" "final\0" +"finance\0" "lplfinancial\0" "fire\0" "firestone\0" "firmdale\0" +"fish\0" "fishing\0" "fit\0" "fitness\0" "fj\0" "jfk\0" "flickr\0" +"flights\0" "flir\0" "florist\0" "flowers\0" "fly\0" "ifm\0" "ortsinfo\0" +"foo\0" "food\0" "foodnetwork\0" "football\0" "oxford\0" "forex\0" +"forsale\0" "forum\0" "foundation\0" "fox\0" "sfr\0" "dyndns-free\0" +"fresenius\0" "frl\0" "frogans\0" "frontdoor\0" "frontier\0" "ftr\0" +"fujitsu\0" "fujixerox\0" "fun\0" "fund\0" "furniture\0" "futbol\0" +"fyi\0" "saga\0" "legal\0" "artgallery\0" "gallo\0" "gallup\0" "marugame\0" +"is-into-games\0" "gap\0" "usgarden\0" "xn--sandnessjen-ogb\0" "gbiz\0" +"gd\0" "gdn\0" "koge\0" "gea\0" "gent\0" "genting\0" "george\0" +"ggf\0" "gg\0" "ggee\0" "pittsburgh\0" "nogi\0" "gift\0" "gifts\0" +"gives\0" "giving\0" "gl\0" "glade\0" "glass\0" "withgoogle\0" "global\0" +"globo\0" "xn--fzys8d69uvgm\0" "gmail\0" "gmo\0" "gmx\0" "bjugn\0" +"godaddy\0" "gold\0" "goldpoint\0" "golf\0" "goo\0" "goodhands\0" +"goodyear\0" "goog\0" "gop\0" "forgot\0" "chernigov\0" "gp\0" "gq\0" +"agr\0" "grainger\0" "graphics\0" "gratis\0" "is-a-green\0" "gripe\0" +"grocery\0" "stcgroup\0" "vgs\0" "gt\0" "ivgu\0" "guardian\0" "gucci\0" +"guge\0" "guide\0" "guitars\0" "is-a-guru\0" "rzgw\0" "hair\0" "hamburg\0" +"hangout\0" "hbo\0" "hdfc\0" "hdfcbank\0" "health\0" "help\0" "helsinki\0" +"thruhere\0" "hermes\0" "hgtv\0" "hiphop\0" "hisamitsu\0" "hitachi\0" +"chernihiv\0" "nhk\0" "hkt\0" "hm\0" "stjohn\0" "hockey\0" "holdings\0" +"holiday\0" "homedepot\0" "homegoods\0" "homes\0" "homesense\0" +"honda\0" "honeywell\0" "horse\0" "hospital\0" "nfshost\0" "flynnhosting\0" +"hot\0" "hoteles\0" "kerryhotels\0" "hotmail\0" "mulhouse\0" "show\0" +"ruhr\0" "hsbc\0" "recht\0" "htc\0" "sohu\0" "hughes\0" "hyatt\0" +"hyundai\0" "icbc\0" "venice\0" "fie\0" "ieee\0" "ikano\0" "mil\0" +"cim\0" "imamat\0" "imdb\0" "immo\0" "immobilien\0" "fin\0" "industries\0" "infiniti\0" "sling\0" "pink\0" "institute\0" "lifeinsurance\0" "insure\0" "mint\0" "intel\0" "international\0" "intuit\0" "investments\0" -"ipiranga\0" "iq\0" "irish\0" "iris\0" "iselect\0" "ismaili\0" "gist\0" -"istanbul\0" "itv\0" "iwc\0" "jaguar\0" "java\0" "jcb\0" "jcp\0" -"fedje\0" "jeep\0" "jetzt\0" "jewelry\0" "jio\0" "jlc\0" "jll\0" -"jm\0" "jmp\0" "jnj\0" "gujo\0" "jobs\0" "joburg\0" "jot\0" "joy\0" -"jp\0" "jpmorgan\0" "jprs\0" "juegos\0" "juniper\0" "kaufen\0" "kddi\0" -"wake\0" "kerrylogistics\0" "kerryproperties\0" "kfh\0" "kg\0" "kh\0" -"ski\0" "nokia\0" "askim\0" "kinder\0" "kindle\0" "kitchen\0" "kiwi\0" -"km\0" "bokn\0" "koeln\0" "komatsu\0" "kosher\0" "ostrowwlkp\0" -"kpmg\0" "kpn\0" "wskr\0" "krd\0" "kred\0" "kuokgroup\0" "kw\0" -"sky\0" "kyoto\0" "kz\0" "fla\0" "lacaixa\0" "ladbrokes\0" "lamborghini\0" -"lamer\0" "lancaster\0" "lancia\0" "lancome\0" "orland\0" "landrover\0" -"lanxess\0" "lasalle\0" "balat\0" "latino\0" "latrobe\0" "wroclaw\0" -"is-a-lawyer\0" "mlb\0" "plc\0" "mcdonalds\0" "lease\0" "leclerc\0" -"lefrak\0" "lego\0" "lexus\0" "lgbt\0" "suli\0" "liaison\0" "lidl\0" -"metlife\0" "lifestyle\0" "lighting\0" "like\0" "lilly\0" "limited\0" -"limo\0" "lincoln\0" "linde\0" "homelink\0" "lipsy\0" "live\0" "living\0" -"lixil\0" "elk\0" "loan\0" "loans\0" "locker\0" "locus\0" "loft\0" -"lol\0" "london\0" "lotte\0" "lotto\0" "love\0" "lpl\0" "lr\0" "mls\0" -"alt\0" "ltd\0" "ltda\0" "lu\0" "lundbeck\0" "lupin\0" "luxe\0" -"luxury\0" "malselv\0" "mma\0" "macys\0" "madrid\0" "maif\0" "est-a-la-maison\0" -"makeup\0" "itoman\0" "management\0" "mango\0" "map\0" "indianmarket\0" -"marketing\0" "markets\0" "marriott\0" "marshalls\0" "maserati\0" -"mattel\0" "gotemba\0" "xn--qcka1pmc\0" "mckinsey\0" "bmd\0" "wme\0" -"media\0" "meet\0" "melbourne\0" "meme\0" "memorial\0" "drammen\0" -"menu\0" "merckmsd\0" "xn--j1amh\0" "miami\0" "microsoft\0" "rimini\0" -"rmit\0" "mitsubishi\0" "mk\0" "ml\0" "0emm\0" "pmn\0" "mobi\0" -"azure-mobile\0" "mobily\0" "shimoda\0" "moe\0" "moi\0" "mom\0" -"monash\0" "money\0" "monster\0" "montblanc\0" "mopar\0" "mormon\0" -"mortgage\0" "moscow\0" "kumamoto\0" "motorcycles\0" "mov\0" "movie\0" -"movistar\0" "emp\0" "mq\0" "emr\0" "from-mt\0" "mtn\0" "mtpc\0" -"mtr\0" "oumu\0" "naturalhistorymuseum\0" "northwesternmutual\0" -"mutuelle\0" "mv\0" "sumy\0" "mz\0" "arna\0" "xn--rdy-0nab\0" "nadex\0" -"nagoya\0" "tokoname\0" "nationwide\0" "natura\0" "oldnavy\0" "pnc\0" -"etne\0" "nec\0" "netbank\0" "netflix\0" "neustar\0" "new\0" "newholland\0" +"ipiranga\0" "iq\0" "bir\0" "irish\0" "iris\0" "iselect\0" "ismaili\0" +"gist\0" "istanbul\0" "itv\0" "iwc\0" "jaguar\0" "java\0" "jcb\0" +"jcp\0" "fedje\0" "jeep\0" "jetzt\0" "jewelry\0" "jio\0" "jlc\0" +"jll\0" "jm\0" "jmp\0" "jnj\0" "gujo\0" "jobs\0" "joburg\0" "jot\0" +"joy\0" "jp\0" "jpmorgan\0" "jprs\0" "juegos\0" "juniper\0" "kaufen\0" +"kddi\0" "wake\0" "kerrylogistics\0" "kerryproperties\0" "kfh\0" +"kg\0" "kh\0" "ski\0" "nokia\0" "askim\0" "kinder\0" "kindle\0" +"kitchen\0" "kiwi\0" "km\0" "bokn\0" "koeln\0" "komatsu\0" "kosher\0" +"ostrowwlkp\0" "kpmg\0" "kpn\0" "wskr\0" "krd\0" "kred\0" "kuokgroup\0" +"kw\0" "sky\0" "kyoto\0" "kz\0" "fla\0" "lacaixa\0" "ladbrokes\0" +"lamborghini\0" "lamer\0" "lancaster\0" "lancia\0" "lancome\0" "orland\0" +"landrover\0" "lanxess\0" "lasalle\0" "balat\0" "latino\0" "latrobe\0" +"wroclaw\0" "is-a-lawyer\0" "mlb\0" "plc\0" "mcdonalds\0" "lease\0" +"leclerc\0" "lefrak\0" "lego\0" "lexus\0" "lgbt\0" "suli\0" "liaison\0" +"lidl\0" "metlife\0" "lifestyle\0" "lighting\0" "like\0" "lilly\0" +"limited\0" "limo\0" "lincoln\0" "linde\0" "homelink\0" "lipsy\0" +"live\0" "living\0" "lixil\0" "elk\0" "loan\0" "loans\0" "locker\0" +"locus\0" "loft\0" "lol\0" "london\0" "lotte\0" "lotto\0" "love\0" +"lpl\0" "lr\0" "mls\0" "alt\0" "ltd\0" "ltda\0" "lu\0" "lundbeck\0" +"lupin\0" "luxe\0" "luxury\0" "malselv\0" "mma\0" "macys\0" "madrid\0" +"maif\0" "est-a-la-maison\0" "makeup\0" "itoman\0" "management\0" +"mango\0" "map\0" "indianmarket\0" "marketing\0" "markets\0" "marriott\0" +"marshalls\0" "maserati\0" "mattel\0" "gotemba\0" "xn--qcka1pmc\0" +"mckinsey\0" "bmd\0" "wme\0" "media\0" "meet\0" "melbourne\0" "meme\0" +"memorial\0" "drammen\0" "menu\0" "merckmsd\0" "xn--j1amh\0" "miami\0" +"microsoft\0" "rimini\0" "rmit\0" "mitsubishi\0" "mk\0" "ml\0" "0emm\0" +"pmn\0" "mobi\0" "azure-mobile\0" "mobily\0" "shimoda\0" "moe\0" +"moi\0" "mom\0" "monash\0" "money\0" "monster\0" "montblanc\0" "mopar\0" +"mormon\0" "mortgage\0" "moscow\0" "kumamoto\0" "motorcycles\0" +"mov\0" "movie\0" "movistar\0" "emp\0" "mq\0" "emr\0" "from-mt\0" +"mtn\0" "mtpc\0" "mtr\0" "oumu\0" "naturalhistorymuseum\0" "northwesternmutual\0" +"mv\0" "sumy\0" "mz\0" "arna\0" "xn--rdy-0nab\0" "nadex\0" "nagoya\0" +"tokoname\0" "nationwide\0" "natura\0" "oldnavy\0" "pnc\0" "etne\0" +"exnet\0" "netbank\0" "netflix\0" "neustar\0" "new\0" "newholland\0" "news\0" "next\0" "nexus\0" "inf\0" "nfl\0" "cng\0" "nango\0" "dni\0" "nico\0" "nike\0" "nikon\0" "ninja\0" "nissan\0" "nissay\0" "ueno\0" "norton\0" "now\0" "nowruz\0" "nowtv\0" "bnr\0" "kanra\0" "nrw\0" "ntt\0" "rnu\0" "nyc\0" "linz\0" "observer\0" "off\0" "homeoffice\0" "okinawa\0" "olayan\0" "olayangroup\0" "ollo\0" "nom\0" "omega\0" -"phone\0" "song\0" "onl\0" "online\0" "onyourside\0" "ooo\0" "open\0" -"oracle\0" "orange\0" "sarpsborg\0" "eating-organic\0" "origins\0" -"kosaka\0" "morotsuka\0" "ovh\0" "page\0" "pamperedchef\0" "panasonic\0" -"panerai\0" "paris\0" "pars\0" "partners\0" "parts\0" "party\0" -"passagens\0" "pet\0" "pf\0" "pfizer\0" "ppg\0" "ph\0" "pharmacy\0" -"phd\0" "philips\0" "photo\0" "photography\0" "myphotos\0" "physio\0" -"piaget\0" "servepics\0" "pictet\0" "pictures\0" "pid\0" "shopping\0" -"pioneer\0" "pizza\0" "pk\0" "birthplace\0" "play\0" "playstation\0" -"ptplus\0" "pm\0" "pohl\0" "poker\0" "politie\0" "porn\0" "from-pr\0" -"pramerica\0" "praxi\0" "prime\0" "pro\0" "prod\0" "productions\0" -"prof\0" "progressive\0" "promo\0" "property\0" "protection\0" "pru\0" -"prudential\0" "ups\0" "pt\0" "pub\0" "pw\0" "pwc\0" "spy\0" "xn--bievt-0qa\0" -"qpon\0" "quebec\0" "quest\0" "qvc\0" "racing\0" "radio\0" "raid\0" -"kure\0" "stufftoread\0" "realtor\0" "realty\0" "recipes\0" "redstone\0" -"redumbrella\0" "rehab\0" "reise\0" "reisen\0" "reit\0" "reliance\0" -"turen\0" "space-to-rent\0" "rentals\0" "repair\0" "report\0" "is-a-republican\0" -"rest\0" "restaurant\0" "review\0" "reviews\0" "rexroth\0" "zuerich\0" -"richardli\0" "ricoh\0" "rightathome\0" "ril\0" "sondrio\0" "ditchyourip\0" -"rocher\0" "rocks\0" "rodeo\0" "rogers\0" "room\0" "rsvp\0" "run\0" -"rwe\0" "ryukyu\0" "wsa\0" "saarland\0" "safe\0" "safety\0" "sakura\0" -"salon\0" "samsclub\0" "samsung\0" "sandvik\0" "sandvikcoromant\0" -"sap\0" "sapo\0" "sarl\0" "sas\0" "save\0" "saxo\0" "sb\0" "sbs\0" -"psc\0" "sca\0" "scb\0" "schaeffler\0" "schmidt\0" "scholarships\0" -"school\0" "schule\0" "schwarz\0" "historyofscience\0" "scjohnson\0" -"scor\0" "scot\0" "from-sd\0" "nose\0" "cdn77-secure\0" "security\0" -"seek\0" "sener\0" "services\0" "seven\0" "sew\0" "essex\0" "sexy\0" -"xn--5su34j936bgsg\0" "shangrila\0" "sharp\0" "shaw\0" "shell\0" -"shia\0" "shiksha\0" "shoes\0" "workshop\0" "shouji\0" "showtime\0" -"shriram\0" "psi\0" "silk\0" "messina\0" "singles\0" "blogsite\0" -"sj\0" "msk\0" "skin\0" "skype\0" "qsl\0" "gsm\0" "smile\0" "asn\0" -"aso\0" "soccer\0" "social\0" "softbank\0" "software\0" "solar\0" -"solutions\0" "sony\0" "masoy\0" "spiegel\0" "appspot\0" "spreadbetting\0" -"sr\0" "srl\0" "srt\0" "fst\0" "stada\0" "staples\0" "starhub\0" -"statebank\0" "statoil\0" "stc\0" "stockholm\0" "storage\0" "store\0" -"stream\0" "studio\0" "study\0" "kusu\0" "sucks\0" "supplies\0" -"supply\0" "support\0" "surf\0" "surgery\0" "suzuki\0" "sv\0" "swatch\0" -"swiftcover\0" "swiss\0" "mypsx\0" "sydney\0" "symantec\0" "systems\0" -"pisz\0" "tab\0" "taipei\0" "elasticbeanstalk\0" "taobao\0" "target\0" -"tatamotors\0" "tatar\0" "tattoo\0" "taxi\0" "etc\0" "steam\0" "technology\0" -"hotel\0" "telecity\0" "telefonica\0" "temasek\0" "tennis\0" "teva\0" -"wtf\0" "tg\0" "ath\0" "thd\0" "theater\0" "theatre\0" "tiaa\0" -"tickets\0" "tienda\0" "tiffany\0" "tips\0" "tires\0" "trentinostirol\0" -"tj\0" "tjmaxx\0" "tjx\0" "tk\0" "tkmaxx\0" "intl\0" "atm\0" "tmall\0" +"phone\0" "song\0" "onion\0" "onl\0" "barsyonline\0" "onyourside\0" +"ooo\0" "open\0" "oracle\0" "orange\0" "sarpsborg\0" "eating-organic\0" +"origins\0" "kosaka\0" "morotsuka\0" "ovh\0" "page\0" "pamperedchef\0" +"panasonic\0" "panerai\0" "paris\0" "pars\0" "partners\0" "parts\0" +"party\0" "passagens\0" "pet\0" "pf\0" "pfizer\0" "ppg\0" "ph\0" +"pharmacy\0" "phd\0" "philips\0" "photo\0" "photography\0" "myphotos\0" +"physio\0" "piaget\0" "servepics\0" "pictet\0" "pictures\0" "pid\0" +"shopping\0" "pioneer\0" "pizza\0" "pk\0" "birthplace\0" "play\0" +"playstation\0" "ptplus\0" "pm\0" "pohl\0" "poker\0" "politie\0" +"porn\0" "from-pr\0" "pramerica\0" "praxi\0" "prime\0" "pro\0" "prod\0" +"productions\0" "prof\0" "progressive\0" "promo\0" "property\0" +"protection\0" "pru\0" "prudential\0" "ups\0" "pt\0" "pub\0" "pw\0" +"pwc\0" "spy\0" "xn--bievt-0qa\0" "qpon\0" "quebec\0" "quest\0" +"qvc\0" "racing\0" "radio\0" "raid\0" "kure\0" "stufftoread\0" "realtor\0" +"realty\0" "recipes\0" "redstone\0" "redumbrella\0" "rehab\0" "reise\0" +"reisen\0" "reit\0" "reliance\0" "turen\0" "space-to-rent\0" "rentals\0" +"repair\0" "report\0" "is-a-republican\0" "rest\0" "restaurant\0" +"review\0" "reviews\0" "rexroth\0" "zuerich\0" "richardli\0" "ricoh\0" +"rightathome\0" "ril\0" "sondrio\0" "ditchyourip\0" "rocher\0" "rocks\0" +"rodeo\0" "rogers\0" "room\0" "rsvp\0" "rugby\0" "run\0" "rwe\0" +"ryukyu\0" "wsa\0" "saarland\0" "safe\0" "safety\0" "sakura\0" "salon\0" +"samsclub\0" "samsung\0" "sandvik\0" "sandvikcoromant\0" "sap\0" +"sapo\0" "sarl\0" "sas\0" "save\0" "saxo\0" "sb\0" "sbs\0" "psc\0" +"sca\0" "scb\0" "schaeffler\0" "schmidt\0" "scholarships\0" "school\0" +"schule\0" "schwarz\0" "historyofscience\0" "scjohnson\0" "scor\0" +"scot\0" "from-sd\0" "nose\0" "cdn77-secure\0" "security\0" "seek\0" +"sener\0" "services\0" "seven\0" "sew\0" "essex\0" "sexy\0" "xn--5su34j936bgsg\0" +"shangrila\0" "sharp\0" "shaw\0" "shell\0" "shia\0" "shiksha\0" +"shoes\0" "workshop\0" "shouji\0" "showtime\0" "shriram\0" "psi\0" +"silk\0" "messina\0" "singles\0" "freesite\0" "sj\0" "msk\0" "skin\0" +"skype\0" "qsl\0" "gsm\0" "smile\0" "asn\0" "aso\0" "soccer\0" "social\0" +"softbank\0" "software\0" "solar\0" "solutions\0" "sony\0" "masoy\0" +"webspace\0" "spiegel\0" "appspot\0" "spreadbetting\0" "sr\0" "srl\0" +"srt\0" "fst\0" "stada\0" "staples\0" "starhub\0" "statebank\0" +"statoil\0" "stc\0" "stockholm\0" "storage\0" "store\0" "stream\0" +"studio\0" "study\0" "kusu\0" "sucks\0" "supplies\0" "supply\0" +"support\0" "surf\0" "surgery\0" "suzuki\0" "sv\0" "swatch\0" "swiftcover\0" +"swiss\0" "mypsx\0" "sydney\0" "symantec\0" "systems\0" "pisz\0" +"tab\0" "taipei\0" "elasticbeanstalk\0" "taobao\0" "target\0" "tatamotors\0" +"tatar\0" "tattoo\0" "taxi\0" "etc\0" "steam\0" "technology\0" "hotel\0" +"telecity\0" "telefonica\0" "temasek\0" "tennis\0" "teva\0" "wtf\0" +"tg\0" "ath\0" "thd\0" "theater\0" "theatre\0" "tiaa\0" "tickets\0" +"tienda\0" "tiffany\0" "tips\0" "tires\0" "trentinostirol\0" "tj\0" +"tjmaxx\0" "tjx\0" "tk\0" "tkmaxx\0" "intl\0" "atm\0" "tmall\0" "mito\0" "tokyo\0" "tools\0" "top\0" "toray\0" "toshiba\0" "total\0" "tours\0" "toyota\0" "toys\0" "ntr\0" "trade\0" "trading\0" "training\0" "travel\0" "travelers\0" "travelersinsurance\0" "trust\0" "trv\0" "withyoutube\0" "tui\0" "tunes\0" "tushu\0" "tvs\0" "tw\0" "myfritz\0" "padua\0" "ubs\0" "uconnect\0" "pug\0" "jeonbuk\0" "unicom\0" "university\0" -"kazuno\0" "uol\0" "jus\0" "tuva\0" "vacations\0" "vana\0" "vanguard\0" -"vegas\0" "ventures\0" "verisign\0" "versicherung\0" "skiptvet\0" -"fvg\0" "vi\0" "viajes\0" "video\0" "vig\0" "viking\0" "villas\0" -"granvin\0" "vip\0" "virgin\0" "visa\0" "television\0" "vista\0" -"vistaprint\0" "viva\0" "vivo\0" "vlaanderen\0" "koebenhavn\0" "vodka\0" -"volkswagen\0" "volvo\0" "vote\0" "voting\0" "voto\0" "voyage\0" -"vu\0" "vuelos\0" "wales\0" "walmart\0" "walter\0" "wang\0" "wanggou\0" -"warman\0" "watches\0" "weather\0" "weatherchannel\0" "weber\0" -"s3-website\0" "wed\0" "wedding\0" "weibo\0" "weir\0" "wf\0" "whoswho\0" -"wien\0" "dyndns-wiki\0" "williamhill\0" "win\0" "windows\0" "wine\0" -"winners\0" "wolterskluwer\0" "woodside\0" "works\0" "world\0" "wow\0" -"wtc\0" "xfinity\0" "xihuan\0" "xin\0" "xn--11b4c3d\0" "xn--1ck2e1b\0" -"xn--1qqw23a\0" "xn--30rr7y\0" "xn--3bst00m\0" "xn--3ds443g\0" "xn--3e0b707e\0" +"kazuno\0" "uol\0" "jus\0" "vacations\0" "vana\0" "vanguard\0" "vegas\0" +"ventures\0" "verisign\0" "versicherung\0" "skiptvet\0" "fvg\0" +"vi\0" "viajes\0" "video\0" "vig\0" "viking\0" "villas\0" "granvin\0" +"vip\0" "virgin\0" "visa\0" "television\0" "vista\0" "vistaprint\0" +"viva\0" "vivo\0" "vlaanderen\0" "koebenhavn\0" "vodka\0" "volkswagen\0" +"volvo\0" "vote\0" "voting\0" "voto\0" "voyage\0" "vu\0" "vuelos\0" +"wales\0" "walmart\0" "walter\0" "wang\0" "wanggou\0" "warman\0" +"watches\0" "weather\0" "weatherchannel\0" "weber\0" "s3-website\0" +"wed\0" "wedding\0" "weibo\0" "weir\0" "wf\0" "whoswho\0" "wien\0" +"dyndns-wiki\0" "williamhill\0" "win\0" "windows\0" "wine\0" "winners\0" +"wolterskluwer\0" "woodside\0" "works\0" "world\0" "wow\0" "wtc\0" +"xfinity\0" "xihuan\0" "xin\0" "xn--11b4c3d\0" "xn--1ck2e1b\0" "xn--1qqw23a\0" +"xn--30rr7y\0" "xn--3bst00m\0" "xn--3ds443g\0" "xn--3e0b707e\0" "xn--3oq18vl8pn36a\0" "xn--3pxu8k\0" "xn--42c2d9a\0" "xn--45brj9c\0" -"xn--45q11c\0" "xn--4gbrim\0" "xn--4gq48lf9j\0" "xn--54b7fta0cc\0" -"xn--55qw42g\0" "xn--55qx5d\0" "xn--5tzm5g\0" "xn--6frz82g\0" "xn--6qq986b3xl\0" +"xn--45q11c\0" "xn--4gbrim\0" "xn--54b7fta0cc\0" "xn--55qw42g\0" +"xn--55qx5d\0" "xn--5tzm5g\0" "xn--6frz82g\0" "xn--6qq986b3xl\0" "xn--80adxhks\0" "xn--80ao21a\0" "xn--80aqecdr1a\0" "xn--80asehdb\0" "xn--80aswg\0" "xn--8y0a063a\0" "xn--90a3ac\0" "xn--90ais\0" "xn--9dbq2a\0" "xn--9et52u\0" "xn--9krt00a\0" "xn--b4w605ferd\0" "xn--bck1b9a5dre4c\0" @@ -249,46 +249,49 @@ static const char kStringTable[] = "parachuting\0" "paragliding\0" "pilot\0" "production\0" "recreation\0" "repbody\0" "rotorcraft\0" "scientist\0" "skydiving\0" "is-a-student\0" "trader\0" "is-a-personaltrainer\0" "workinggroup\0" "fed\0" "gv\0" -"spb\0" "gob\0" "karikatur\0" "e164\0" "in-addr\0" "ip6\0" "heguri\0" -"urn\0" "cloudns\0" "futuremailing\0" "jor\0" "priv\0" "szex\0" -"kunden\0" "*\0" "conf\0" "nsw\0" "cnt\0" "wuoz\0" "qld\0" "uvic\0" -"sowa\0" "klepp\0" "transurl\0" "2000\0" "eu-1\0" "g12\0" "s3\0" -"e4\0" "5\0" "cdn77\0" "8\0" "9\0" "hb\0" "qc\0" "sf\0" "qh\0" "yk\0" -"jl\0" "cq\0" "js\0" "gx\0" "dscloud\0" "dyndns\0" "for-better\0" +"spb\0" "gob\0" "musica\0" "karikatur\0" "e164\0" "in-addr\0" "ip6\0" +"heguri\0" "urn\0" "cloudns\0" "12hp\0" "2ix\0" "4lima\0" "futurehosting\0" +"futuremailing\0" "lima-city\0" "jor\0" "priv\0" "szex\0" "kunden\0" +"*\0" "conf\0" "nsw\0" "cnt\0" "wuoz\0" "qld\0" "uvic\0" "sowa\0" +"klepp\0" "transurl\0" "2000\0" "eu-1\0" "g12\0" "s3\0" "e4\0" "5\0" +"1337\0" "8\0" "9\0" "hb\0" "barsy\0" "qc\0" "sf\0" "qh\0" "yk\0" +"jl\0" "cq\0" "rr\0" "js\0" "gx\0" "dscloud\0" "dyndns\0" "for-better\0" "here-for-more\0" "for-some\0" "for-the\0" "mmafan\0" "myftp\0" "no-ip\0" "selfip\0" "webhop\0" "campobasso\0" "barreau\0" "gouv\0" -"adv\0" "arq\0" "bato\0" "eng\0" "esp\0" "rieti\0" "flog\0" "fnd\0" -"fot\0" "imb\0" "ind\0" "lel\0" "mus\0" "not\0" "slg\0" "srv\0" -"teo\0" "tmp\0" "trd\0" "vlog\0" "zlg\0" "lecce\0" "idf\0" "togo\0" -"api\0" "rj\0" "rr\0" "gc\0" "winb\0" "rns\0" "toon\0" "fantasyleague\0" -"ftpaccess\0" "game-server\0" "scrapping\0" "gotdns\0" "presse\0" -"xn--aroport-bya\0" "!www\0" "magentosite\0" "myfusion\0" "statics\0" +"adv\0" "arq\0" "bato\0" "belem\0" "cri\0" "def\0" "eng\0" "esp\0" +"rieti\0" "flog\0" "floripa\0" "fnd\0" "fot\0" "imb\0" "ind\0" "jampa\0" +"lel\0" "mus\0" "not\0" "xn--rdal-poa\0" "disrec\0" "recife\0" "slg\0" +"srv\0" "teo\0" "tmp\0" "trd\0" "vix\0" "vlog\0" "zlg\0" "lecce\0" +"idf\0" "togo\0" "api\0" "storj\0" "gc\0" "winb\0" "rns\0" "toon\0" +"fantasyleague\0" "ftpaccess\0" "game-server\0" "scrapping\0" "twmail\0" +"gotdns\0" "square7\0" "presse\0" "xn--aroport-bya\0" "!www\0" "magentosite\0" +"myfusion\0" "sensiosite\0" "statics\0" "trafficplex\0" "vapor\0" "utah\0" "gz\0" "naha\0" "marche\0" "ohi\0" "from-nm\0" "manx\0" "xj\0" "xn--od0alg\0" "xz\0" "stryn\0" "zj\0" "cn-north-1\0" "compute\0" -"elb\0" "firm\0" "on-web\0" "1kapp\0" "3utilities\0" "xn--8pvr4u\0" +"elb\0" "firm\0" "nodum\0" "on-web\0" "1kapp\0" "3utilities\0" "xn--8pvr4u\0" "alpha-myqnapcloud\0" "appchizi\0" "applinzi\0" "betainabox\0" "blogdns\0" -"blogsyte\0" "bloxcms\0" "bounty-full\0" "cechire\0" "ciscofreak\0" -"cloudcontrolapp\0" "cloudcontrolled\0" "codespot\0" "damnserver\0" -"ddnsking\0" "dev-myqnapcloud\0" "dnsalias\0" "dnsdojo\0" "dnsiskinky\0" -"doesntexist\0" "dontexist\0" "doomdns\0" "dreamhosters\0" "dsmynas\0" -"dyn-o-saur\0" "dynalias\0" "dyndns-at-home\0" "dyndns-at-work\0" -"dyndns-blog\0" "dyndns-home\0" "dyndns-ip\0" "dyndns-mail\0" "dyndns-office\0" -"dyndns-pics\0" "dyndns-remote\0" "dyndns-server\0" "dyndns-web\0" -"dyndns-work\0" "dynns\0" "est-a-la-masion\0" "est-le-patron\0" -"est-mon-blogueur\0" "evennode\0" "familyds\0" "fbsbx\0" "firebaseapp\0" -"firewall-gateway\0" "flynnhub\0" "freebox-os\0" "freeboxos\0" "from-ak\0" -"from-al\0" "from-ar\0" "from-ca\0" "from-ct\0" "from-dc\0" "from-de\0" -"from-fl\0" "from-ga\0" "from-hi\0" "from-ia\0" "from-id\0" "from-il\0" -"from-in\0" "from-ks\0" "from-ky\0" "from-ma\0" "from-md\0" "from-mi\0" -"from-mn\0" "from-mo\0" "from-ms\0" "from-nc\0" "from-nd\0" "from-ne\0" -"from-nh\0" "from-nj\0" "from-nv\0" "from-oh\0" "from-ok\0" "from-or\0" -"from-pa\0" "from-ri\0" "from-sc\0" "from-tn\0" "from-tx\0" "from-ut\0" -"from-va\0" "from-vt\0" "from-wa\0" "from-wi\0" "from-wv\0" "from-wy\0" -"geekgalaxy\0" "getmyip\0" "githubcloud\0" "githubcloudusercontent\0" -"githubusercontent\0" "googleapis\0" "googlecode\0" "gotpantheon\0" -"health-carereform\0" "herokuapp\0" "herokussl\0" "hobby-site\0" -"homelinux\0" "homesecuritymac\0" "homesecuritypc\0" "homeunix\0" -"iamallama\0" "is-a-anarchist\0" "is-a-blogger\0" "is-a-bookkeeper\0" +"blogsyte\0" "bloxcms\0" "bounty-full\0" "bplaced\0" "cechire\0" +"ciscofreak\0" "cloudcontrolapp\0" "cloudcontrolled\0" "codespot\0" +"damnserver\0" "ddnsking\0" "dev-myqnapcloud\0" "dnsalias\0" "dnsdojo\0" +"dnsiskinky\0" "doesntexist\0" "dontexist\0" "doomdns\0" "drayddns\0" +"dreamhosters\0" "dsmynas\0" "dyn-o-saur\0" "dynalias\0" "dyndns-at-home\0" +"dyndns-at-work\0" "dyndns-blog\0" "dyndns-home\0" "dyndns-ip\0" +"dyndns-mail\0" "dyndns-office\0" "dyndns-pics\0" "dyndns-remote\0" +"dyndns-server\0" "dyndns-web\0" "dyndns-work\0" "dynns\0" "est-a-la-masion\0" +"est-le-patron\0" "est-mon-blogueur\0" "evennode\0" "familyds\0" +"fbsbx\0" "firebaseapp\0" "firewall-gateway\0" "flynnhub\0" "freebox-os\0" +"freeboxos\0" "from-ak\0" "from-al\0" "from-ar\0" "from-ca\0" "from-ct\0" +"from-dc\0" "from-de\0" "from-fl\0" "from-ga\0" "from-hi\0" "from-ia\0" +"from-id\0" "from-il\0" "from-in\0" "from-ks\0" "from-ky\0" "from-ma\0" +"from-md\0" "from-mi\0" "from-mn\0" "from-mo\0" "from-ms\0" "from-nc\0" +"from-nd\0" "from-ne\0" "from-nh\0" "from-nj\0" "from-nv\0" "from-oh\0" +"from-ok\0" "from-or\0" "from-pa\0" "from-ri\0" "from-sc\0" "from-tn\0" +"from-tx\0" "from-ut\0" "from-va\0" "from-vt\0" "from-wa\0" "from-wi\0" +"from-wv\0" "from-wy\0" "geekgalaxy\0" "getmyip\0" "githubcloud\0" +"githubcloudusercontent\0" "githubusercontent\0" "googleapis\0" +"googlecode\0" "gotpantheon\0" "health-carereform\0" "herokuapp\0" +"herokussl\0" "hobby-site\0" "homelinux\0" "homesecuritymac\0" "homesecuritypc\0" +"homeunix\0" "iamallama\0" "is-a-anarchist\0" "is-a-blogger\0" "is-a-bookkeeper\0" "is-a-bulls-fan\0" "is-a-caterer\0" "is-a-chef\0" "is-a-conservative\0" "is-a-cpa\0" "is-a-cubicle-slave\0" "is-a-designer\0" "is-a-financialadvisor\0" "is-a-geek\0" "is-a-hard-worker\0" "is-a-hunter\0" "is-a-landscaper\0" @@ -299,31 +302,34 @@ static const char kStringTable[] = "is-an-artist\0" "is-an-entertainer\0" "is-certified\0" "is-gone\0" "is-into-anime\0" "is-into-cartoons\0" "is-leet\0" "is-not-certified\0" "is-slick\0" "is-uberleet\0" "is-with-theband\0" "isa-geek\0" "isa-hockeynut\0" -"issmarterthanyou\0" "joyent\0" "jpn\0" "likes-pie\0" "likescandy\0" -"logoip\0" "meteorapp\0" "myasustor\0" "mydrobo\0" "myshopblocks\0" -"myvnc\0" "neat-url\0" "net-freaks\0" "on-aptible\0" "onthewifi\0" -"operaunite\0" "outsystemscloud\0" "ownprovider\0" "pagefrontapp\0" -"pagespeedmobilizer\0" "pgfog\0" "point2this\0" "prgmr\0" "publishproxy\0" -"qa2\0" "quicksytes\0" "rackmaze\0" "remotewd\0" "saves-the-whales\0" +"issmarterthanyou\0" "jdevcloud\0" "joyent\0" "jpn\0" "likes-pie\0" +"likescandy\0" "logoip\0" "meteorapp\0" "myasustor\0" "mydrobo\0" +"myshopblocks\0" "mytuleap\0" "myvnc\0" "neat-url\0" "net-freaks\0" +"on-aptible\0" "onthewifi\0" "operaunite\0" "outsystemscloud\0" +"ownprovider\0" "pagefrontapp\0" "pagespeedmobilizer\0" "pgfog\0" +"point2this\0" "prgmr\0" "publishproxy\0" "qa2\0" "quicksytes\0" +"quipelements\0" "rackmaze\0" "remotewd\0" "saves-the-whales\0" "securitytactics\0" "sells-for-less\0" "sells-for-u\0" "servebbs\0" "servecounterstrike\0" "serveftp\0" "servegame\0" "servehalflife\0" "servehttp\0" "servehumour\0" "serveirc\0" "servemp3\0" "servep2p\0" "servequake\0" "servesarcasm\0" "simple-url\0" "vipsinaapp\0" "townnews-staging\0" -"unusualperson\0" "workisboring\0" "writesthisblog\0" "yolasite\0" -"s3-ap-northeast-1\0" "s3-ap-northeast-2\0" "s3-ap-south-1\0" "s3-ap-southeast-1\0" -"s3-ap-southeast-2\0" "s3-ca-central-1\0" "compute-1\0" "s3-eu-central-1\0" -"s3-eu-west-1\0" "s3-external-1\0" "s3-fips-us-gov-west-1\0" "s3-sa-east-1\0" -"s3-us-east-2\0" "s3-us-gov-west-1\0" "s3-us-west-1\0" "s3-us-west-2\0" -"s3-website-ap-northeast-1\0" "s3-website-ap-southeast-1\0" "s3-website-ap-southeast-2\0" -"s3-website-eu-west-1\0" "s3-website-sa-east-1\0" "s3-website-us-east-1\0" -"s3-website-us-west-1\0" "s3-website-us-west-2\0" "dualstack\0" -"alpha\0" "beta\0" "eu-2\0" "us-1\0" "us-2\0" "apps\0" "cns\0" "xen\0" -"ekloges\0" "parliament\0" "realm\0" "cosidns\0" "dd-dns\0" "ddnss\0" -"dnshome\0" "dnsupdater\0" "dray-dns\0" "draydns\0" "dyn-ip24\0" -"dyn-vpn\0" "dynamisches-dns\0" "dyndns1\0" "dynvpn\0" "fuettertdasnetz\0" -"home-webserver\0" "internet-dns\0" "isteingeek\0" "istmein\0" "keymachine\0" -"l-o-g-i-n\0" "lebtimnetz\0" "leitungsen\0" "mein-vigor\0" "my-gateway\0" -"my-router\0" "my-vigor\0" "my-wan\0" "myhome-server\0" "spdns\0" +"unusualperson\0" "workisboring\0" "wpdevcloud\0" "writesthisblog\0" +"yolasite\0" "s3-ap-northeast-1\0" "s3-ap-northeast-2\0" "s3-ap-south-1\0" +"s3-ap-southeast-1\0" "s3-ap-southeast-2\0" "s3-ca-central-1\0" +"compute-1\0" "s3-eu-central-1\0" "s3-eu-west-1\0" "s3-eu-west-2\0" +"s3-external-1\0" "s3-fips-us-gov-west-1\0" "s3-sa-east-1\0" "s3-us-east-2\0" +"s3-us-gov-west-1\0" "s3-us-west-1\0" "s3-us-west-2\0" "s3-website-ap-northeast-1\0" +"s3-website-ap-southeast-1\0" "s3-website-ap-southeast-2\0" "s3-website-eu-west-1\0" +"s3-website-sa-east-1\0" "s3-website-us-east-1\0" "s3-website-us-west-1\0" +"s3-website-us-west-2\0" "dualstack\0" "alpha\0" "beta\0" "eu-2\0" +"eu-3\0" "us-1\0" "us-2\0" "us-3\0" "apps\0" "cns\0" "xen\0" "ekloges\0" +"parliament\0" "metacentrum\0" "realm\0" "custom\0" "cosidns\0" +"dd-dns\0" "ddnss\0" "dnshome\0" "dnsupdater\0" "dray-dns\0" "draydns\0" +"dyn-ip24\0" "dyn-vpn\0" "dynamisches-dns\0" "dyndns1\0" "dynvpn\0" +"fuettertdasnetz\0" "git-repos\0" "home-webserver\0" "internet-dns\0" +"isteingeek\0" "istmein\0" "keymachine\0" "l-o-g-i-n\0" "lcube-server\0" +"lebtimnetz\0" "leitungsen\0" "mein-vigor\0" "my-gateway\0" "my-router\0" +"my-vigor\0" "my-wan\0" "myhome-server\0" "spdns\0" "svn-repos\0" "syno-ds\0" "synology-diskstation\0" "synology-ds\0" "taifun-dns\0" "traeumtgerade\0" "dedyn\0" "reg\0" "sld\0" "nerdpol\0" "k12\0" "aip\0" "lib\0" "pri\0" "riik\0" "eun\0" "nieruchomosci\0" "mycd\0" @@ -331,39 +337,41 @@ static const char kStringTable[] = "niki\0" "aeroport\0" "assedic\0" "avoues\0" "chambagri\0" "chirurgiens-dentistes\0" "chirurgiens-dentistes-en-france\0" "experts-comptables\0" "fbx-os\0" "fbxos\0" "greta\0" "huissier-justice\0" "medecin\0" "notaires\0" -"pharmacien\0" "prd\0" "veterinaire\0" "pvt\0" "mod\0" "idv\0" "inc\0" -"xn--ciqpn\0" "xn--gmq050i\0" "xn--gmqw5a\0" "xn--lcvr32d\0" "xn--mk0axi\0" -"xn--od0aq3b\0" "xn--tn0ag\0" "xn--uc0atv\0" "xn--uc0ay4a\0" "xn--wcvs22d\0" -"xn--zf0avx\0" "opencraft\0" "from\0" "perso\0" "rel\0" "agrar\0" -"bolt\0" "erotica\0" "erotika\0" "ingatlan\0" "jogasz\0" "konyvelo\0" -"lakas\0" "reklam\0" "transport\0" "tozsde\0" "utazas\0" "odesa\0" -"muni\0" "bergen\0" "barrel-of-knowledge\0" "barrell-of-knowledge\0" -"dvrcam\0" "dynamic-dns\0" "for-our\0" "groks-the\0" "groks-this\0" -"knowsitall\0" "nsupdate\0" "backplaneapp\0" "boxfuse\0" "browsersafetymark\0" -"drud\0" "enonic\0" "github\0" "gitlab\0" "hasura-app\0" "hzc\0" -"lair\0" "ngrok\0" "nid\0" "pantheonsite\0" "protonet\0" "sandcats\0" -"shiftedit\0" "spacekit\0" "stolos\0" "customer\0" "cupcake\0" "abruzzo\0" -"agrigento\0" "alessandria\0" "trentinoalto-adige\0" "trentinoaltoadige\0" -"esan\0" "ancona\0" "andria-barletta-trani\0" "andria-trani-barletta\0" -"andriabarlettatrani\0" "andriatranibarletta\0" "valdaosta\0" "aosta-valley\0" -"aostavalley\0" "valleeaoste\0" "laquila\0" "arezzo\0" "ascoli-piceno\0" -"ascolipiceno\0" "asti\0" "av\0" "avellino\0" "balsan\0" "nabari\0" -"barletta-trani-andria\0" "barlettatraniandria\0" "basilicata\0" -"belluno\0" "benevento\0" "bergamo\0" "biella\0" "publ\0" "bologna\0" -"bolzano\0" "bozen\0" "brescia\0" "brindisi\0" "cagliari\0" "reggiocalabria\0" -"caltanissetta\0" "campania\0" "campidano-medio\0" "campidanomedio\0" -"carbonia-iglesias\0" "carboniaiglesias\0" "carrara-massa\0" "carraramassa\0" -"caserta\0" "catania\0" "catanzaro\0" "cesena-forli\0" "cesenaforli\0" -"chieti\0" "como\0" "cosenza\0" "cremona\0" "crotone\0" "acct\0" -"cuneo\0" "dell-ogliastra\0" "dellogliastra\0" "emilia-romagna\0" -"emiliaromagna\0" "ravenna\0" "fermo\0" "ferrara\0" "fg\0" "firenze\0" -"florence\0" "foggia\0" "forli-cesena\0" "forlicesena\0" "friuli-v-giulia\0" +"pharmacien\0" "prd\0" "veterinaire\0" "pvt\0" "cya\0" "mod\0" "idv\0" +"inc\0" "xn--ciqpn\0" "xn--gmq050i\0" "xn--gmqw5a\0" "xn--lcvr32d\0" +"xn--mk0axi\0" "xn--od0aq3b\0" "xn--tn0ag\0" "xn--uc0atv\0" "xn--uc0ay4a\0" +"xn--wcvs22d\0" "xn--zf0avx\0" "cloudaccess\0" "opencraft\0" "from\0" +"perso\0" "rel\0" "agrar\0" "bolt\0" "erotica\0" "erotika\0" "ingatlan\0" +"jogasz\0" "konyvelo\0" "lakas\0" "reklam\0" "transport\0" "tozsde\0" +"utazas\0" "odesa\0" "muni\0" "bergen\0" "barrel-of-knowledge\0" +"barrell-of-knowledge\0" "dvrcam\0" "dynamic-dns\0" "for-our\0" +"groks-the\0" "groks-this\0" "knowsitall\0" "nsupdate\0" "backplaneapp\0" +"boxfuse\0" "browsersafetymark\0" "definima\0" "drud\0" "enonic\0" +"github\0" "gitlab\0" "hasura-app\0" "hzc\0" "lair\0" "ngrok\0" +"nid\0" "pantheonsite\0" "protonet\0" "sandcats\0" "shiftedit\0" +"spacekit\0" "stolos\0" "thingdust\0" "vaporcloud\0" "wedeploy\0" +"customer\0" "testing\0" "cust\0" "cupcake\0" "abruzzo\0" "agrigento\0" +"alessandria\0" "trentinoalto-adige\0" "trentinoaltoadige\0" "esan\0" +"ancona\0" "andria-barletta-trani\0" "andria-trani-barletta\0" "andriabarlettatrani\0" +"andriatranibarletta\0" "valdaosta\0" "aosta-valley\0" "aostavalley\0" +"valleeaoste\0" "laquila\0" "arezzo\0" "ascoli-piceno\0" "ascolipiceno\0" +"asti\0" "av\0" "avellino\0" "balsan\0" "nabari\0" "barletta-trani-andria\0" +"barlettatraniandria\0" "basilicata\0" "belluno\0" "benevento\0" +"bergamo\0" "biella\0" "publ\0" "bologna\0" "bolzano\0" "bozen\0" +"brescia\0" "brindisi\0" "cagliari\0" "reggiocalabria\0" "caltanissetta\0" +"campania\0" "campidano-medio\0" "campidanomedio\0" "carbonia-iglesias\0" +"carboniaiglesias\0" "carrara-massa\0" "carraramassa\0" "caserta\0" +"catania\0" "catanzaro\0" "cesena-forli\0" "cesenaforli\0" "chieti\0" +"como\0" "cosenza\0" "cremona\0" "crotone\0" "acct\0" "cuneo\0" +"dell-ogliastra\0" "dellogliastra\0" "emilia-romagna\0" "emiliaromagna\0" +"ravenna\0" "fermo\0" "ferrara\0" "fg\0" "firenze\0" "florence\0" +"foggia\0" "forli-cesena\0" "forlicesena\0" "friuli-v-giulia\0" "friuli-ve-giulia\0" "friuli-vegiulia\0" "friuli-venezia-giulia\0" "friuli-veneziagiulia\0" "friuli-vgiulia\0" "friuliv-giulia\0" "friulive-giulia\0" "friulivegiulia\0" "friulivenezia-giulia\0" "friuliveneziagiulia\0" "friulivgiulia\0" "frosinone\0" "genoa\0" "genova\0" "gorizia\0" "grosseto\0" "iglesias-carbonia\0" "iglesiascarbonia\0" "imperia\0" -"isernia\0" "la-spezia\0" "laspezia\0" "latina\0" "lazio\0" "tele\0" +"isernia\0" "la-spezia\0" "laspezia\0" "latina\0" "lazio\0" "bale\0" "lecco\0" "lig\0" "liguria\0" "livorno\0" "plo\0" "lodi\0" "lom\0" "lombardia\0" "lombardy\0" "lucania\0" "lucca\0" "macerata\0" "mantova\0" "hamar\0" "massa-carrara\0" "massacarrara\0" "matera\0" "medio-campidano\0" @@ -629,17 +637,17 @@ static const char kStringTable[] = "chungnam\0" "daegu\0" "daejeon\0" "gangwon\0" "gwangju\0" "gyeongbuk\0" "gyeonggi\0" "gyeongnam\0" "fhs\0" "incheon\0" "jeju\0" "jeonnam\0" "seoul\0" "ulsan\0" "static\0" "azurewebsites\0" "cyon\0" "mypep\0" -"assn\0" "grp\0" "soc\0" "brasilia\0" "daplie\0" "ddns\0" "dnsfor\0" -"hopto\0" "i234\0" "loginto\0" "myds\0" "noip\0" "synology\0" "yombo\0" -"agriculture\0" "airguard\0" "alabama\0" "alaska\0" "amber\0" "nativeamerican\0" -"americana\0" "americanantiques\0" "americanart\0" "brand\0" "annefrank\0" -"anthro\0" "anthropology\0" "usantiques\0" "aquarium\0" "arboretum\0" -"archaeological\0" "archaeology\0" "architecture\0" "artdeco\0" -"artsandcrafts\0" "asmatart\0" "assassination\0" "assisi\0" "astronomy\0" -"atlanta\0" "austin\0" "australia\0" "automotive\0" "axis\0" "badajoz\0" -"eisenbahn\0" "bale\0" "baltimore\0" "basel\0" "baths\0" "bauern\0" -"beauxarts\0" "beeldengeluid\0" "bellevue\0" "bergbau\0" "berkeley\0" -"bern\0" "bilbao\0" "bill\0" "birdart\0" "bonn\0" "botanical\0" +"assn\0" "grp\0" "soc\0" "brasilia\0" "c66\0" "daplie\0" "dnsfor\0" +"filegear\0" "hopto\0" "i234\0" "loginto\0" "myds\0" "noip\0" "synology\0" +"yombo\0" "localhost\0" "agriculture\0" "airguard\0" "alabama\0" +"alaska\0" "amber\0" "nativeamerican\0" "americana\0" "americanantiques\0" +"americanart\0" "brand\0" "annefrank\0" "anthro\0" "anthropology\0" +"usantiques\0" "aquarium\0" "arboretum\0" "archaeological\0" "archaeology\0" +"architecture\0" "artdeco\0" "artsandcrafts\0" "asmatart\0" "assassination\0" +"assisi\0" "astronomy\0" "atlanta\0" "austin\0" "australia\0" "automotive\0" +"axis\0" "badajoz\0" "eisenbahn\0" "baltimore\0" "basel\0" "baths\0" +"bauern\0" "beauxarts\0" "beeldengeluid\0" "bellevue\0" "bergbau\0" +"berkeley\0" "bern\0" "bilbao\0" "bill\0" "birdart\0" "bonn\0" "botanical\0" "botanicalgarden\0" "botanicgarden\0" "botany\0" "brandywinevalley\0" "brasil\0" "bristol\0" "british\0" "britishcolumbia\0" "broadcast\0" "brunel\0" "brussel\0" "bruxelles\0" "building\0" "burghof\0" "bushey\0" @@ -692,47 +700,49 @@ static const char kStringTable[] = "palmsprings\0" "panama\0" "pasadena\0" "philadelphia\0" "philadelphiaarea\0" "philately\0" "phoenix\0" "pilots\0" "planetarium\0" "plantation\0" "plants\0" "plaza\0" "portal\0" "portland\0" "portlligat\0" "preservation\0" -"presidio\0" "project\0" "pubol\0" "railroad\0" "railway\0" "resistance\0" -"riodejaneiro\0" "rochester\0" "rockart\0" "russia\0" "saintlouis\0" -"salzburg\0" "sandiego\0" "santabarbara\0" "santacruz\0" "santafe\0" -"saskatchewan\0" "satx\0" "savannahga\0" "schlesisches\0" "schoenbrunn\0" -"schokoladen\0" "schweiz\0" "science-fiction\0" "scienceandhistory\0" -"scienceandindustry\0" "sciencecenter\0" "sciencecenters\0" "sciencehistory\0" -"sciencesnaturelles\0" "scotland\0" "seaport\0" "settlement\0" "settlers\0" -"sherbrooke\0" "sibenik\0" "skole\0" "sologne\0" "soundandvision\0" -"southcarolina\0" "southwest\0" "square\0" "stadt\0" "stalbans\0" -"starnberg\0" "steiermark\0" "stpetersburg\0" "stuttgart\0" "suisse\0" -"surgeonshall\0" "surrey\0" "svizzera\0" "sweden\0" "tank\0" "telekommunikation\0" -"texas\0" "textile\0" "timekeeping\0" "topology\0" "touch\0" "trolley\0" -"trustee\0" "ulm\0" "undersea\0" "usarts\0" "ushuaia\0" "versailles\0" -"village\0" "virginia\0" "virtual\0" "virtuel\0" "volkenkunde\0" -"wallonie\0" "washingtondc\0" "watch-and-clock\0" "western\0" "westfalen\0" -"whaling\0" "wildlife\0" "xn--9dbhblg6di\0" "xn--comunicaes-v6a2o\0" -"xn--correios-e-telecomunicaes-ghc29a\0" "xn--h1aegh\0" "xn--lns-qla\0" -"yorkshire\0" "yosemite\0" "youth\0" "zoological\0" "zoology\0" -"bounceme\0" "broke-it\0" "buyshouses\0" "cdn77-ssl\0" "cloudapp\0" -"cloudfront\0" "cloudfunctions\0" "cryptonomic\0" "does-it\0" "dynathome\0" -"dynv6\0" "endofinternet\0" "fastly\0" "feste-ip\0" "from-az\0" -"from-co\0" "from-la\0" "from-ny\0" "gets-it\0" "ham-radio-op\0" -"homeftp\0" "homeip\0" "kicks-ass\0" "knx-server\0" "mydissent\0" +"presidio\0" "fedoraproject\0" "pubol\0" "railroad\0" "railway\0" +"resistance\0" "riodejaneiro\0" "rochester\0" "rockart\0" "russia\0" +"saintlouis\0" "salzburg\0" "sandiego\0" "santabarbara\0" "santacruz\0" +"santafe\0" "saskatchewan\0" "satx\0" "savannahga\0" "schlesisches\0" +"schoenbrunn\0" "schokoladen\0" "schweiz\0" "science-fiction\0" +"scienceandhistory\0" "scienceandindustry\0" "sciencecenter\0" "sciencecenters\0" +"sciencehistory\0" "sciencesnaturelles\0" "scotland\0" "seaport\0" +"settlement\0" "settlers\0" "sherbrooke\0" "sibenik\0" "skole\0" +"sologne\0" "soundandvision\0" "southcarolina\0" "southwest\0" "square\0" +"stadt\0" "stalbans\0" "starnberg\0" "steiermark\0" "stpetersburg\0" +"stuttgart\0" "suisse\0" "surgeonshall\0" "surrey\0" "svizzera\0" +"sweden\0" "tank\0" "telekommunikation\0" "texas\0" "textile\0" +"timekeeping\0" "topology\0" "touch\0" "trolley\0" "trustee\0" "ulm\0" +"undersea\0" "usarts\0" "ushuaia\0" "versailles\0" "village\0" "virginia\0" +"virtual\0" "virtuel\0" "volkenkunde\0" "wallonie\0" "washingtondc\0" +"watch-and-clock\0" "western\0" "westfalen\0" "whaling\0" "wildlife\0" +"xn--9dbhblg6di\0" "xn--comunicaes-v6a2o\0" "xn--correios-e-telecomunicaes-ghc29a\0" +"xn--h1aegh\0" "xn--lns-qla\0" "yorkshire\0" "yosemite\0" "youth\0" +"zoological\0" "zoology\0" "bounceme\0" "broke-it\0" "buyshouses\0" +"cdn77\0" "cdn77-ssl\0" "cloudapp\0" "cloudfront\0" "cloudfunctions\0" +"cryptonomic\0" "does-it\0" "dynathome\0" "dynv6\0" "endofinternet\0" +"fastly\0" "fastlylb\0" "feste-ip\0" "from-az\0" "from-co\0" "from-la\0" +"from-ny\0" "gets-it\0" "ham-radio-op\0" "homeftp\0" "homeip\0" +"ipifony\0" "kicks-ass\0" "knx-server\0" "moonscale\0" "mydissent\0" "myeffect\0" "mymediapc\0" "nhlfan\0" "office-on-the\0" "pgafan\0" "privatizehealthinsurance\0" "redirectme\0" "scrapper-site\0" "sells-it\0" -"serveminecraft\0" "static-access\0" "t3l3p0rt\0" "alces\0" "virtueeldomein\0" -"aarborte\0" "aejrie\0" "kafjord\0" "agdenes\0" "akershus\0" "aknoluokta\0" -"akrehamn\0" "alaheadju\0" "alesund\0" "algard\0" "alstahaug\0" -"yalta\0" "alvdal\0" "amli\0" "amot\0" "andasuolo\0" "andebu\0" -"sandoy\0" "lardal\0" "aremark\0" "arendal\0" "aseral\0" "asker\0" -"askoy\0" "askvoll\0" "asnes\0" "audnedaln\0" "aukra\0" "aure\0" -"aurland\0" "aurskog-holand\0" "austevoll\0" "austrheim\0" "averoy\0" -"badaddja\0" "bahcavuotna\0" "bahccavuotna\0" "baidar\0" "bajddar\0" -"balestrand\0" "ballangen\0" "balsfjord\0" "bamble\0" "bardu\0" -"barum\0" "batsfjord\0" "bearalvahki\0" "beardu\0" "beiarn\0" "eidsberg\0" -"berlevag\0" "bievat\0" "bindal\0" "birkenes\0" "bjarkoy\0" "bjerkreim\0" -"bodo\0" "bomlo\0" "bremanger\0" "bronnoy\0" "bronnoysund\0" "brumunddal\0" -"bryne\0" "budejju\0" "buskerud\0" "bygland\0" "bykle\0" "cahcesuolo\0" -"davvenjarga\0" "deatnu\0" "dep\0" "dielddanuorri\0" "divtasvuodna\0" -"divttasvuotna\0" "dovre\0" "drangedal\0" "drobak\0" "dyroy\0" "egersund\0" -"hareid\0" "eidfjord\0" "eidskog\0" "eidsvoll\0" "eigersund\0" "elverum\0" +"serveminecraft\0" "static-access\0" "t3l3p0rt\0" "freetls\0" "alces\0" +"cistron\0" "demon\0" "virtueeldomein\0" "aarborte\0" "aejrie\0" +"kafjord\0" "agdenes\0" "akershus\0" "aknoluokta\0" "akrehamn\0" +"alaheadju\0" "alesund\0" "algard\0" "alstahaug\0" "yalta\0" "alvdal\0" +"amli\0" "amot\0" "andasuolo\0" "andebu\0" "sandoy\0" "lardal\0" +"aremark\0" "arendal\0" "aseral\0" "asker\0" "askoy\0" "askvoll\0" +"asnes\0" "audnedaln\0" "aukra\0" "aure\0" "aurland\0" "aurskog-holand\0" +"austevoll\0" "austrheim\0" "averoy\0" "badaddja\0" "bahcavuotna\0" +"bahccavuotna\0" "baidar\0" "bajddar\0" "balestrand\0" "ballangen\0" +"balsfjord\0" "bamble\0" "bardu\0" "barum\0" "batsfjord\0" "bearalvahki\0" +"beardu\0" "beiarn\0" "eidsberg\0" "berlevag\0" "bievat\0" "bindal\0" +"birkenes\0" "bjarkoy\0" "bjerkreim\0" "bodo\0" "bomlo\0" "bremanger\0" +"bronnoy\0" "bronnoysund\0" "brumunddal\0" "bryne\0" "budejju\0" +"buskerud\0" "bygland\0" "bykle\0" "cahcesuolo\0" "davvenjarga\0" +"deatnu\0" "dep\0" "dielddanuorri\0" "divtasvuodna\0" "divttasvuotna\0" +"dovre\0" "drangedal\0" "drobak\0" "dyroy\0" "egersund\0" "hareid\0" +"eidfjord\0" "eidskog\0" "eidsvoll\0" "eigersund\0" "elverum\0" "enebakk\0" "engerdal\0" "etnedal\0" "evenassi\0" "evenes\0" "evje-og-hornnes\0" "farsund\0" "fauske\0" "fetsund\0" "finnoy\0" "fitjar\0" "fjaler\0" "fjell\0" "flakstad\0" "flatanger\0" "flekkefjord\0" "flesberg\0" @@ -828,71 +838,77 @@ static const char kStringTable[] = "xn--mre-og-romsdal-qqb\0" "xn--msy-ula0h\0" "xn--mtta-vrjjat-k7af\0" "xn--muost-0qa\0" "xn--nry-yla5g\0" "xn--nttery-byae\0" "xn--nvuotna-hwa\0" "xn--oppegrd-ixa\0" "xn--ostery-fya\0" "xn--osyro-wua\0" "xn--porsgu-sta26f\0" -"xn--rady-ira\0" "xn--rdal-poa\0" "xn--rde-ula\0" "xn--rennesy-v1a\0" -"xn--rholt-mra\0" "xn--risa-5na\0" "xn--risr-ira\0" "xn--rland-uua\0" -"xn--rlingen-mxa\0" "xn--rmskog-bya\0" "xn--rros-gra\0" "xn--rskog-uua\0" -"xn--rst-0na\0" "xn--rsta-fra\0" "xn--ryken-vua\0" "xn--ryrvik-bya\0" -"xn--s-1fa\0" "xn--sandy-yua\0" "xn--seral-lra\0" "xn--sgne-gra\0" -"xn--skierv-uta\0" "xn--skjervy-v1a\0" "xn--skjk-soa\0" "xn--sknit-yqa\0" -"xn--sknland-fxa\0" "xn--slat-5na\0" "xn--slt-elab\0" "xn--smla-hra\0" -"xn--smna-gra\0" "xn--snase-nra\0" "xn--sndre-land-0cb\0" "xn--snes-poa\0" -"xn--snsa-roa\0" "xn--sr-aurdal-l8a\0" "xn--sr-fron-q1a\0" "xn--sr-odal-q1a\0" -"xn--sr-varanger-ggb\0" "xn--srfold-bya\0" "xn--srreisa-q1a\0" "xn--srum-gra\0" -"xn--stfold-9xa\0" "xn--stjrdal-s1a\0" "xn--stjrdalshalsen-sqb\0" -"xn--stre-toten-zcb\0" "xn--tjme-hra\0" "xn--tnsberg-q1a\0" "xn--trany-yua\0" -"xn--trgstad-r1a\0" "xn--trna-woa\0" "xn--troms-zua\0" "xn--tysvr-vra\0" -"xn--unjrga-rta\0" "xn--vads-jra\0" "xn--vard-jra\0" "xn--vegrshei-c0a\0" -"xn--vestvgy-ixa6o\0" "xn--vg-yiab\0" "xn--vgan-qoa\0" "xn--vgsy-qoa0j\0" -"xn--vre-eiker-k8a\0" "xn--vrggt-xqad\0" "xn--vry-yla5g\0" "xn--yer-zna\0" -"xn--ygarden-p1a\0" "xn--ystre-slidre-ujb\0" "wios\0" "xn--vler-qoa\0" -"heroy\0" "sande\0" "xn--b-5ga\0" "xn--hery-ira\0" "merseine\0" -"shacknet\0" "cri\0" "govt\0" "maori\0" "xn--mori-qsa\0" "amune\0" -"bmoattachments\0" "boldlygoingnowhere\0" "cable-modem\0" "certmgr\0" -"collegefan\0" "couchpotatofries\0" "duckdns\0" "dvrdns\0" "endoftheinternet\0" -"from-me\0" "game-host\0" "hepforge\0" "homedns\0" "is-a-bruinsfan\0" -"is-a-candidate\0" "is-a-celticsfan\0" "is-a-knight\0" "is-a-patsfan\0" -"is-a-soxfan\0" "is-found\0" "is-lost\0" "is-saved\0" "is-very-bad\0" -"is-very-evil\0" "is-very-good\0" "is-very-nice\0" "is-very-sweet\0" -"misconfused\0" "my-firewall\0" "myfirewall\0" "nflfan\0" "pimienta\0" -"poivron\0" "potager\0" "read-books\0" "readmyblog\0" "sellsyourhome\0" -"stuff-4-sale\0" "sweetpepper\0" "tunk\0" "ufcfan\0" "wmflabs\0" -"zapto\0" "rsc\0" "origin\0" "q-a\0" "gok\0" "agro\0" "augustow\0" -"babia-gora\0" "bedzin\0" "beep\0" "beskidy\0" "bialowieza\0" "bialystok\0" -"bielawa\0" "bieszczady\0" "boleslawiec\0" "bydgoszcz\0" "bytom\0" -"cieszyn\0" "czest\0" "elblag\0" "vologda\0" "gdansk\0" "gdynia\0" -"gliwice\0" "glogow\0" "gmina\0" "gniezno\0" "gorlice\0" "grajewo\0" -"ilawa\0" "jaworzno\0" "jelenia-gora\0" "jgora\0" "kalisz\0" "karpacz\0" -"kartuzy\0" "kaszuby\0" "katowice\0" "kazimierz-dolny\0" "kepno\0" -"ketrzyn\0" "klodzko\0" "kobierzyce\0" "kolobrzeg\0" "konin\0" "konskowola\0" -"krakow\0" "kutno\0" "lapy\0" "lebork\0" "legnica\0" "lezajsk\0" -"limanowa\0" "lomza\0" "lubin\0" "lukow\0" "malbork\0" "malopolska\0" -"mazowsze\0" "mazury\0" "miasta\0" "mielec\0" "mielno\0" "mragowo\0" -"naklo\0" "nowaruda\0" "nysa\0" "olawa\0" "olecko\0" "olkusz\0" -"olsztyn\0" "opoczno\0" "opole\0" "ostroda\0" "ostroleka\0" "ostrowiec\0" -"pila\0" "podhale\0" "podlasie\0" "polkowice\0" "pomorskie\0" "pomorze\0" -"powiat\0" "poznan\0" "prochowice\0" "pruszkow\0" "przeworsk\0" -"pulawy\0" "radom\0" "rawa-maz\0" "rybnik\0" "rzeszow\0" "sanok\0" -"sejny\0" "sklep\0" "skoczow\0" "slask\0" "slupsk\0" "sopot\0" "sosnowiec\0" -"stalowa-wola\0" "starachowice\0" "stargard\0" "suwalki\0" "swidnica\0" -"swiebodzin\0" "swinoujscie\0" "szczecin\0" "szczytno\0" "szkola\0" -"targi\0" "tarnobrzeg\0" "tgory\0" "tourism\0" "turek\0" "turystyka\0" -"tychy\0" "ustka\0" "walbrzych\0" "warmia\0" "warszawa\0" "wegrow\0" -"wielun\0" "wloclawek\0" "wodzislaw\0" "wolomin\0" "wroc\0" "zachpomor\0" -"zagan\0" "zakopane\0" "zarow\0" "zgora\0" "zgorzelec\0" "griw\0" -"kmpsp\0" "konsulat\0" "kppsp\0" "kwp\0" "kwpsp\0" "mup\0" "oirm\0" -"oum\0" "pinb\0" "piw\0" "psse\0" "pup\0" "sdn\0" "starostwo\0" -"ugim\0" "umig\0" "upow\0" "wzmiuw\0" "uzs\0" "wif\0" "wiih\0" "witd\0" -"wiw\0" "zp\0" "test\0" "isla\0" "jur\0" "belau\0" "fhsk\0" "fhv\0" -"komforb\0" "kommunalforbund\0" "komvux\0" "lanbib\0" "naturbruksgymn\0" -"parti\0" "hashbang\0" "platform\0" "univ\0" "stackspace\0" "consulado\0" -"embaixada\0" "principe\0" "adygeya\0" "arkhangelsk\0" "balashov\0" -"bashkiria\0" "bryansk\0" "dagestan\0" "grozny\0" "ivanovo\0" "kalmykia\0" -"kaluga\0" "karelia\0" "khakassia\0" "krasnodar\0" "kurgan\0" "lenug\0" -"mordovia\0" "murmansk\0" "nalchik\0" "nov\0" "obninsk\0" "penza\0" -"pokrovsk\0" "sochi\0" "togliatti\0" "troitsk\0" "tula\0" "vladikavkaz\0" -"vladimir\0" "knightpoint\0" "agrinet\0" "defense\0" "edunet\0" -"rnrt\0" "bel\0" "kep\0" "better-than\0" "on-the-web\0" "worse-than\0" -"xn--czrw28b\0" "xn--zf0ao64a\0" "cherkassy\0" "cherkasy\0" "chernivtsi\0" +"xn--rady-ira\0" "xn--rde-ula\0" "xn--rennesy-v1a\0" "xn--rholt-mra\0" +"xn--risa-5na\0" "xn--risr-ira\0" "xn--rland-uua\0" "xn--rlingen-mxa\0" +"xn--rmskog-bya\0" "xn--rros-gra\0" "xn--rskog-uua\0" "xn--rst-0na\0" +"xn--rsta-fra\0" "xn--ryken-vua\0" "xn--ryrvik-bya\0" "xn--s-1fa\0" +"xn--sandy-yua\0" "xn--seral-lra\0" "xn--sgne-gra\0" "xn--skierv-uta\0" +"xn--skjervy-v1a\0" "xn--skjk-soa\0" "xn--sknit-yqa\0" "xn--sknland-fxa\0" +"xn--slat-5na\0" "xn--slt-elab\0" "xn--smla-hra\0" "xn--smna-gra\0" +"xn--snase-nra\0" "xn--sndre-land-0cb\0" "xn--snes-poa\0" "xn--snsa-roa\0" +"xn--sr-aurdal-l8a\0" "xn--sr-fron-q1a\0" "xn--sr-odal-q1a\0" "xn--sr-varanger-ggb\0" +"xn--srfold-bya\0" "xn--srreisa-q1a\0" "xn--srum-gra\0" "xn--stfold-9xa\0" +"xn--stjrdal-s1a\0" "xn--stjrdalshalsen-sqb\0" "xn--stre-toten-zcb\0" +"xn--tjme-hra\0" "xn--tnsberg-q1a\0" "xn--trany-yua\0" "xn--trgstad-r1a\0" +"xn--trna-woa\0" "xn--troms-zua\0" "xn--tysvr-vra\0" "xn--unjrga-rta\0" +"xn--vads-jra\0" "xn--vard-jra\0" "xn--vegrshei-c0a\0" "xn--vestvgy-ixa6o\0" +"xn--vg-yiab\0" "xn--vgan-qoa\0" "xn--vgsy-qoa0j\0" "xn--vre-eiker-k8a\0" +"xn--vrggt-xqad\0" "xn--vry-yla5g\0" "xn--yer-zna\0" "xn--ygarden-p1a\0" +"xn--ystre-slidre-ujb\0" "wios\0" "xn--vler-qoa\0" "heroy\0" "sande\0" +"xn--b-5ga\0" "xn--hery-ira\0" "merseine\0" "shacknet\0" "govt\0" +"maori\0" "xn--mori-qsa\0" "amune\0" "blogsite\0" "bmoattachments\0" +"boldlygoingnowhere\0" "cable-modem\0" "certmgr\0" "collegefan\0" +"couchpotatofries\0" "duckdns\0" "dvrdns\0" "endoftheinternet\0" +"fedorainfracloud\0" "fedorapeople\0" "from-me\0" "game-host\0" +"hepforge\0" "homedns\0" "is-a-bruinsfan\0" "is-a-candidate\0" "is-a-celticsfan\0" +"is-a-knight\0" "is-a-patsfan\0" "is-a-soxfan\0" "is-found\0" "is-lost\0" +"is-saved\0" "is-very-bad\0" "is-very-evil\0" "is-very-good\0" "is-very-nice\0" +"is-very-sweet\0" "misconfused\0" "my-firewall\0" "myfirewall\0" +"nflfan\0" "pimienta\0" "poivron\0" "potager\0" "read-books\0" "readmyblog\0" +"sellsyourhome\0" "stuff-4-sale\0" "sweetpepper\0" "tunk\0" "ufcfan\0" +"wmflabs\0" "zapto\0" "tele\0" "rsc\0" "origin\0" "q-a\0" "gok\0" +"agro\0" "augustow\0" "babia-gora\0" "bedzin\0" "beep\0" "beskidy\0" +"bialowieza\0" "bialystok\0" "bielawa\0" "bieszczady\0" "boleslawiec\0" +"bydgoszcz\0" "bytom\0" "cieszyn\0" "czest\0" "elblag\0" "vologda\0" +"gdansk\0" "gdynia\0" "gliwice\0" "glogow\0" "gmina\0" "gniezno\0" +"gorlice\0" "grajewo\0" "ilawa\0" "jaworzno\0" "jelenia-gora\0" +"jgora\0" "kalisz\0" "karpacz\0" "kartuzy\0" "kaszuby\0" "katowice\0" +"kazimierz-dolny\0" "kepno\0" "ketrzyn\0" "klodzko\0" "kobierzyce\0" +"kolobrzeg\0" "konin\0" "konskowola\0" "krakow\0" "kutno\0" "lapy\0" +"lebork\0" "legnica\0" "lezajsk\0" "limanowa\0" "lomza\0" "lubin\0" +"lukow\0" "malbork\0" "malopolska\0" "mazowsze\0" "mazury\0" "miasta\0" +"mielec\0" "mielno\0" "mragowo\0" "naklo\0" "nowaruda\0" "nysa\0" +"olawa\0" "olecko\0" "olkusz\0" "olsztyn\0" "opoczno\0" "opole\0" +"ostroda\0" "ostroleka\0" "ostrowiec\0" "pila\0" "podhale\0" "podlasie\0" +"polkowice\0" "pomorskie\0" "pomorze\0" "powiat\0" "poznan\0" "prochowice\0" +"pruszkow\0" "przeworsk\0" "pulawy\0" "radom\0" "rawa-maz\0" "rybnik\0" +"rzeszow\0" "sanok\0" "sejny\0" "sklep\0" "skoczow\0" "slask\0" +"slupsk\0" "sopot\0" "sosnowiec\0" "stalowa-wola\0" "starachowice\0" +"stargard\0" "suwalki\0" "swidnica\0" "swiebodzin\0" "swinoujscie\0" +"szczecin\0" "szczytno\0" "szkola\0" "targi\0" "tarnobrzeg\0" "tgory\0" +"tourism\0" "turek\0" "turystyka\0" "tychy\0" "ustka\0" "walbrzych\0" +"warmia\0" "warszawa\0" "wegrow\0" "wielun\0" "wloclawek\0" "wodzislaw\0" +"wolomin\0" "wroc\0" "zachpomor\0" "zagan\0" "zakopane\0" "zarow\0" +"zgora\0" "zgorzelec\0" "griw\0" "kmpsp\0" "konsulat\0" "kppsp\0" +"kwp\0" "kwpsp\0" "mup\0" "oirm\0" "oum\0" "pinb\0" "piw\0" "psse\0" +"pup\0" "sdn\0" "starostwo\0" "ugim\0" "umig\0" "upow\0" "wzmiuw\0" +"uzs\0" "wif\0" "wiih\0" "witd\0" "wiw\0" "zp\0" "test\0" "isla\0" +"jur\0" "belau\0" "clan\0" "adygeya\0" "bashkiria\0" "cldmail\0" +"dagestan\0" "grozny\0" "kalmykia\0" "kustanai\0" "marine\0" "mordovia\0" +"mytis\0" "nalchik\0" "nov\0" "pyatigorsk\0" "vladikavkaz\0" "vladimir\0" +"fhsk\0" "fhv\0" "komforb\0" "kommunalforbund\0" "komvux\0" "lanbib\0" +"naturbruksgymn\0" "parti\0" "hashbang\0" "platform\0" "platformsh\0" +"univ\0" "stackspace\0" "uber\0" "xs4all\0" "consulado\0" "embaixada\0" +"principe\0" "abkhazia\0" "aktyubinsk\0" "arkhangelsk\0" "armenia\0" +"ashgabad\0" "azerbaijan\0" "balashov\0" "bryansk\0" "bukhara\0" +"chimkent\0" "east-kazakhstan\0" "ivanovo\0" "jambyl\0" "kaluga\0" +"karacol\0" "karaganda\0" "karelia\0" "khakassia\0" "krasnodar\0" +"kurgan\0" "lenug\0" "mangyshlak\0" "murmansk\0" "navoi\0" "north-kazakhstan\0" +"obninsk\0" "penza\0" "pokrovsk\0" "sochi\0" "tashkent\0" "termez\0" +"togliatti\0" "troitsk\0" "tselinograd\0" "tula\0" "tuva\0" "knightpoint\0" +"agrinet\0" "defense\0" "edunet\0" "rnrt\0" "vpnplus\0" "bel\0" +"kep\0" "better-than\0" "on-the-web\0" "worse-than\0" "xn--czrw28b\0" +"xn--zf0ao64a\0" "mymailer\0" "cherkassy\0" "cherkasy\0" "chernivtsi\0" "chernovtsy\0" "crimea\0" "dnepropetrovsk\0" "dnipropetrovsk\0" "dominic\0" "donetsk\0" "dp\0" "ivano-frankivsk\0" "kharkiv\0" "kharkov\0" "kherson\0" "khmelnitskiy\0" "khmelnytskyi\0" "kiev\0" "kirovograd\0" @@ -903,7 +919,9 @@ static const char kStringTable[] = "nhs\0" "police\0" "service\0" "golffan\0" "is-by\0" "land-4-sale\0" "nsn\0" "pointto\0" "chtr\0" "paroch\0" "gub\0" "e12\0" "mypets\0" "xn--80au\0" "xn--90azh\0" "xn--d1at\0" "xn--o1ac\0" "xn--o1ach\0" -"agric\0" "grondar\0" "triton\0" ""; +"xn--12c1fe0br\0" "xn--12cfi8ixb8l\0" "xn--12co0c3b4eva\0" "xn--h3cuzk1di\0" +"xn--m3ch0j3a\0" "xn--o3cyx2a\0" "agric\0" "grondar\0" "triton\0" +""; static const struct TrieNode kNodeTable[] = { { 0, 0, 0, 1 }, /* aaa */ @@ -916,7 +934,7 @@ static const struct TrieNode kNodeTable[] = { { 38, 0, 0, 1 }, /* able */ { 43, 0, 0, 1 }, /* abogado */ { 51, 0, 0, 1 }, /* abudhabi */ - { 62, 3549, 6, 1 }, /* ac */ + { 62, 3715, 6, 1 }, /* ac */ { 65, 0, 0, 1 }, /* academy */ { 73, 0, 0, 1 }, /* accenture */ { 89, 0, 0, 1 }, /* accountant */ @@ -924,29 +942,29 @@ static const struct TrieNode kNodeTable[] = { { 112, 0, 0, 1 }, /* aco */ { 121, 0, 0, 1 }, /* active */ { 134, 0, 0, 1 }, /* actor */ - { 141, 3555, 1, 1 }, /* ad */ + { 141, 3721, 1, 1 }, /* ad */ { 60, 0, 0, 1 }, /* adac */ { 144, 0, 0, 1 }, /* ads */ { 148, 0, 0, 1 }, /* adult */ - { 157, 3556, 8, 1 }, /* ae */ + { 157, 3722, 8, 1 }, /* ae */ { 160, 0, 0, 1 }, /* aeg */ - { 164, 3564, 87, 1 }, /* aero */ + { 164, 3730, 87, 1 }, /* aero */ { 169, 0, 0, 1 }, /* aetna */ - { 191, 3651, 5, 1 }, /* af */ + { 191, 3817, 5, 1 }, /* af */ { 194, 0, 0, 1 }, /* afamilycompany */ { 209, 0, 0, 1 }, /* afl */ { 217, 0, 0, 1 }, /* africa */ - { 226, 3656, 5, 1 }, /* ag */ + { 226, 3822, 5, 1 }, /* ag */ { 229, 0, 0, 1 }, /* agakhan */ { 237, 0, 0, 1 }, /* agency */ - { 247, 3661, 4, 1 }, /* ai */ + { 247, 3827, 4, 1 }, /* ai */ { 250, 0, 0, 1 }, /* aig */ { 255, 0, 0, 1 }, /* aigo */ { 260, 0, 0, 1 }, /* airbus */ { 267, 0, 0, 1 }, /* airforce */ { 276, 0, 0, 1 }, /* airtel */ { 283, 0, 0, 1 }, /* akdn */ - { 290, 3665, 7, 1 }, /* al */ + { 290, 3831, 7, 1 }, /* al */ { 293, 0, 0, 1 }, /* alfaromeo */ { 303, 0, 0, 1 }, /* alibaba */ { 311, 0, 0, 1 }, /* alipay */ @@ -955,7 +973,7 @@ static const struct TrieNode kNodeTable[] = { { 337, 0, 0, 1 }, /* ally */ { 342, 0, 0, 1 }, /* alsace */ { 349, 0, 0, 1 }, /* alstom */ - { 358, 3672, 1, 1 }, /* am */ + { 358, 3838, 1, 1 }, /* am */ { 361, 0, 0, 1 }, /* americanexpress */ { 377, 0, 0, 1 }, /* americanfamily */ { 395, 0, 0, 1 }, /* amex */ @@ -966,29 +984,29 @@ static const struct TrieNode kNodeTable[] = { { 432, 0, 0, 1 }, /* android */ { 440, 0, 0, 1 }, /* anquan */ { 324, 0, 0, 1 }, /* anz */ - { 448, 3673, 6, 1 }, /* ao */ + { 448, 3839, 6, 1 }, /* ao */ { 451, 0, 0, 1 }, /* aol */ { 455, 0, 0, 1 }, /* apartments */ { 468, 0, 0, 1 }, /* app */ { 472, 0, 0, 1 }, /* apple */ { 480, 0, 0, 1 }, /* aq */ { 483, 0, 0, 1 }, /* aquarelle */ - { 494, 1553, 9, 1 }, /* ar */ + { 494, 1549, 10, 1 }, /* ar */ { 497, 0, 0, 1 }, /* arab */ { 502, 0, 0, 1 }, /* aramco */ { 509, 0, 0, 1 }, /* archi */ { 515, 0, 0, 1 }, /* army */ - { 520, 3679, 6, 1 }, /* arpa */ + { 520, 3845, 6, 1 }, /* arpa */ { 527, 0, 0, 1 }, /* art */ { 531, 0, 0, 1 }, /* arte */ - { 537, 3685, 1, 1 }, /* as */ + { 537, 3851, 1, 1 }, /* as */ { 540, 0, 0, 1 }, /* asda */ - { 545, 3686, 1, 1 }, /* asia */ + { 545, 3852, 1, 1 }, /* asia */ { 550, 0, 0, 1 }, /* associates */ - { 562, 1562, 10, 1 }, /* at */ + { 562, 1559, 14, 1 }, /* at */ { 565, 0, 0, 1 }, /* athleta */ { 573, 0, 0, 1 }, /* attorney */ - { 584, 1574, 18, 1 }, /* au */ + { 584, 1575, 18, 1 }, /* au */ { 587, 0, 0, 1 }, /* auction */ { 595, 0, 0, 1 }, /* audi */ { 600, 0, 0, 1 }, /* audible */ @@ -998,13 +1016,13 @@ static const struct TrieNode kNodeTable[] = { { 629, 0, 0, 1 }, /* auto */ { 634, 0, 0, 1 }, /* autos */ { 640, 0, 0, 1 }, /* avianca */ - { 649, 3701, 1, 1 }, /* aw */ + { 649, 3867, 1, 1 }, /* aw */ { 658, 0, 0, 1 }, /* aws */ { 663, 0, 0, 1 }, /* ax */ { 666, 0, 0, 1 }, /* axa */ - { 671, 3702, 12, 1 }, /* az */ + { 671, 3868, 12, 1 }, /* az */ { 682, 0, 0, 1 }, /* azure */ - { 308, 3665, 7, 1 }, /* ba */ + { 308, 3831, 7, 1 }, /* ba */ { 692, 0, 0, 1 }, /* baby */ { 697, 0, 0, 1 }, /* baidu */ { 392, 0, 0, 1 }, /* banamex */ @@ -1021,14 +1039,14 @@ static const struct TrieNode kNodeTable[] = { { 798, 0, 0, 1 }, /* basketball */ { 809, 0, 0, 1 }, /* bauhaus */ { 817, 0, 0, 1 }, /* bayern */ - { 17, 3714, 10, 1 }, /* bb */ + { 17, 3880, 10, 1 }, /* bb */ { 824, 0, 0, 1 }, /* bbc */ { 828, 0, 0, 1 }, /* bbt */ { 832, 0, 0, 1 }, /* bbva */ { 837, 0, 0, 1 }, /* bcg */ { 841, 0, 0, 1 }, /* bcn */ - { 855, 3687, 1, 0 }, /* bd */ - { 860, 1592, 3, 1 }, /* be */ + { 855, 3853, 1, 0 }, /* bd */ + { 860, 1593, 3, 1 }, /* be */ { 863, 0, 0, 1 }, /* beats */ { 869, 0, 0, 1 }, /* beauty */ { 881, 0, 0, 1 }, /* beer */ @@ -1037,3615 +1055,3782 @@ static const struct TrieNode kNodeTable[] = { { 901, 0, 0, 1 }, /* best */ { 906, 0, 0, 1 }, /* bestbuy */ { 914, 0, 0, 1 }, /* bet */ - { 928, 3685, 1, 1 }, /* bf */ - { 931, 3724, 37, 1 }, /* bg */ - { 936, 3651, 5, 1 }, /* bh */ - { 939, 0, 0, 1 }, /* bharti */ - { 57, 3761, 5, 1 }, /* bi */ - { 950, 0, 0, 1 }, /* bible */ - { 956, 0, 0, 1 }, /* bid */ - { 960, 0, 0, 1 }, /* bike */ - { 969, 0, 0, 1 }, /* bing */ - { 974, 0, 0, 1 }, /* bingo */ - { 980, 0, 0, 1 }, /* bio */ - { 985, 3766, 12, 1 }, /* biz */ - { 989, 3778, 4, 1 }, /* bj */ - { 992, 0, 0, 1 }, /* black */ - { 998, 0, 0, 1 }, /* blackfriday */ - { 1010, 0, 0, 1 }, /* blanco */ - { 1017, 0, 0, 1 }, /* blockbuster */ - { 1034, 0, 0, 1 }, /* blog */ - { 1039, 0, 0, 1 }, /* bloomberg */ - { 1049, 0, 0, 1 }, /* blue */ - { 1055, 3651, 5, 1 }, /* bm */ - { 1058, 0, 0, 1 }, /* bms */ - { 1062, 0, 0, 1 }, /* bmw */ - { 1067, 3687, 1, 0 }, /* bn */ - { 1070, 0, 0, 1 }, /* bnl */ - { 1074, 0, 0, 1 }, /* bnpparibas */ - { 1086, 3782, 9, 1 }, /* bo */ - { 1089, 0, 0, 1 }, /* boats */ - { 1095, 0, 0, 1 }, /* boehringer */ - { 1106, 0, 0, 1 }, /* bofa */ - { 1111, 0, 0, 1 }, /* bom */ - { 1115, 0, 0, 1 }, /* bond */ - { 1120, 0, 0, 1 }, /* boo */ - { 1124, 0, 0, 1 }, /* book */ - { 1129, 0, 0, 1 }, /* booking */ - { 1137, 0, 0, 1 }, /* boots */ - { 1143, 0, 0, 1 }, /* bosch */ - { 1149, 0, 0, 1 }, /* bostik */ - { 1156, 0, 0, 1 }, /* boston */ - { 1163, 0, 0, 1 }, /* bot */ - { 1167, 0, 0, 1 }, /* boutique */ - { 1177, 0, 0, 1 }, /* box */ - { 1182, 1595, 70, 1 }, /* br */ - { 1185, 0, 0, 1 }, /* bradesco */ - { 1194, 0, 0, 1 }, /* bridgestone */ - { 1206, 0, 0, 1 }, /* broadway */ - { 1215, 0, 0, 1 }, /* broker */ - { 1222, 0, 0, 1 }, /* brother */ - { 1230, 0, 0, 1 }, /* brussels */ - { 1240, 3651, 5, 1 }, /* bs */ - { 829, 3651, 5, 1 }, /* bt */ - { 1243, 0, 0, 1 }, /* budapest */ - { 1252, 0, 0, 1 }, /* bugatti */ - { 1260, 0, 0, 1 }, /* build */ - { 1266, 0, 0, 1 }, /* builders */ - { 1275, 0, 0, 1 }, /* business */ + { 928, 3851, 1, 1 }, /* bf */ + { 932, 3890, 38, 1 }, /* bg */ + { 937, 3817, 5, 1 }, /* bh */ + { 940, 0, 0, 1 }, /* bharti */ + { 57, 3928, 5, 1 }, /* bi */ + { 951, 0, 0, 1 }, /* bible */ + { 957, 0, 0, 1 }, /* bid */ + { 961, 0, 0, 1 }, /* bike */ + { 970, 0, 0, 1 }, /* bing */ + { 975, 0, 0, 1 }, /* bingo */ + { 981, 0, 0, 1 }, /* bio */ + { 986, 3933, 12, 1 }, /* biz */ + { 990, 3945, 4, 1 }, /* bj */ + { 993, 0, 0, 1 }, /* black */ + { 999, 0, 0, 1 }, /* blackfriday */ + { 1011, 0, 0, 1 }, /* blanco */ + { 1018, 0, 0, 1 }, /* blockbuster */ + { 1035, 0, 0, 1 }, /* blog */ + { 1040, 0, 0, 1 }, /* bloomberg */ + { 1050, 0, 0, 1 }, /* blue */ + { 1056, 3817, 5, 1 }, /* bm */ + { 1059, 0, 0, 1 }, /* bms */ + { 1063, 0, 0, 1 }, /* bmw */ + { 1068, 3853, 1, 0 }, /* bn */ + { 1071, 0, 0, 1 }, /* bnl */ + { 1075, 0, 0, 1 }, /* bnpparibas */ + { 1087, 3949, 9, 1 }, /* bo */ + { 1090, 0, 0, 1 }, /* boats */ + { 1096, 0, 0, 1 }, /* boehringer */ + { 1107, 0, 0, 1 }, /* bofa */ + { 1112, 0, 0, 1 }, /* bom */ + { 1116, 0, 0, 1 }, /* bond */ + { 1121, 0, 0, 1 }, /* boo */ + { 1125, 0, 0, 1 }, /* book */ + { 1130, 0, 0, 1 }, /* booking */ + { 1138, 0, 0, 1 }, /* boots */ + { 1144, 0, 0, 1 }, /* bosch */ + { 1150, 0, 0, 1 }, /* bostik */ + { 1157, 0, 0, 1 }, /* boston */ + { 1164, 0, 0, 1 }, /* bot */ + { 1168, 0, 0, 1 }, /* boutique */ + { 1178, 0, 0, 1 }, /* box */ + { 1183, 1596, 78, 1 }, /* br */ + { 1186, 0, 0, 1 }, /* bradesco */ + { 1195, 0, 0, 1 }, /* bridgestone */ + { 1207, 0, 0, 1 }, /* broadway */ + { 1216, 0, 0, 1 }, /* broker */ + { 1223, 0, 0, 1 }, /* brother */ + { 1231, 0, 0, 1 }, /* brussels */ + { 1241, 3817, 5, 1 }, /* bs */ + { 829, 3817, 5, 1 }, /* bt */ + { 1244, 0, 0, 1 }, /* budapest */ + { 1253, 0, 0, 1 }, /* bugatti */ + { 1261, 0, 0, 1 }, /* build */ + { 1267, 0, 0, 1 }, /* builders */ + { 1276, 0, 0, 1 }, /* business */ { 910, 0, 0, 1 }, /* buy */ - { 1284, 0, 0, 1 }, /* buzz */ - { 1289, 0, 0, 1 }, /* bv */ - { 1292, 3818, 2, 1 }, /* bw */ - { 694, 1665, 4, 1 }, /* by */ - { 1295, 3820, 6, 1 }, /* bz */ - { 1298, 0, 0, 1 }, /* bzh */ - { 221, 3826, 18, 1 }, /* ca */ - { 1306, 0, 0, 1 }, /* cab */ - { 1310, 0, 0, 1 }, /* cafe */ - { 1319, 0, 0, 1 }, /* cal */ - { 1323, 0, 0, 1 }, /* call */ - { 1328, 0, 0, 1 }, /* calvinklein */ - { 1343, 0, 0, 1 }, /* cam */ - { 1357, 0, 0, 1 }, /* camera */ - { 1372, 0, 0, 1 }, /* camp */ - { 1377, 0, 0, 1 }, /* cancerresearch */ - { 1392, 0, 0, 1 }, /* canon */ - { 1398, 0, 0, 1 }, /* capetown */ - { 1407, 0, 0, 1 }, /* capital */ - { 1415, 0, 0, 1 }, /* capitalone */ - { 1426, 0, 0, 1 }, /* car */ - { 1430, 0, 0, 1 }, /* caravan */ - { 1438, 0, 0, 1 }, /* cards */ - { 1450, 0, 0, 1 }, /* care */ - { 1455, 0, 0, 1 }, /* career */ - { 1462, 0, 0, 1 }, /* careers */ - { 1478, 0, 0, 1 }, /* cars */ - { 1483, 0, 0, 1 }, /* cartier */ - { 1491, 0, 0, 1 }, /* casa */ - { 1496, 0, 0, 1 }, /* case */ - { 1501, 0, 0, 1 }, /* caseih */ - { 1508, 0, 0, 1 }, /* cash */ - { 1513, 0, 0, 1 }, /* casino */ - { 1523, 0, 0, 1 }, /* cat */ - { 1527, 0, 0, 1 }, /* catering */ - { 1536, 0, 0, 1 }, /* catholic */ - { 1563, 0, 0, 1 }, /* cba */ - { 1066, 0, 0, 1 }, /* cbn */ - { 1567, 0, 0, 1 }, /* cbre */ - { 1239, 0, 0, 1 }, /* cbs */ - { 1579, 3844, 6, 1 }, /* cc */ - { 1583, 3685, 1, 1 }, /* cd */ - { 1586, 0, 0, 1 }, /* ceb */ - { 1593, 0, 0, 1 }, /* center */ - { 1600, 0, 0, 1 }, /* ceo */ - { 1604, 0, 0, 1 }, /* cern */ - { 1611, 3672, 1, 1 }, /* cf */ - { 1614, 0, 0, 1 }, /* cfa */ - { 1618, 0, 0, 1 }, /* cfd */ + { 1285, 0, 0, 1 }, /* buzz */ + { 1290, 0, 0, 1 }, /* bv */ + { 1293, 3985, 2, 1 }, /* bw */ + { 694, 1674, 4, 1 }, /* by */ + { 1296, 3987, 6, 1 }, /* bz */ + { 1299, 0, 0, 1 }, /* bzh */ + { 221, 1678, 19, 1 }, /* ca */ + { 1307, 0, 0, 1 }, /* cab */ + { 1311, 0, 0, 1 }, /* cafe */ + { 1320, 0, 0, 1 }, /* cal */ + { 1324, 0, 0, 1 }, /* call */ + { 1329, 0, 0, 1 }, /* calvinklein */ + { 1344, 0, 0, 1 }, /* cam */ + { 1358, 0, 0, 1 }, /* camera */ + { 1373, 0, 0, 1 }, /* camp */ + { 1378, 0, 0, 1 }, /* cancerresearch */ + { 1393, 0, 0, 1 }, /* canon */ + { 1399, 0, 0, 1 }, /* capetown */ + { 1408, 0, 0, 1 }, /* capital */ + { 1416, 0, 0, 1 }, /* capitalone */ + { 1427, 0, 0, 1 }, /* car */ + { 1431, 0, 0, 1 }, /* caravan */ + { 1439, 0, 0, 1 }, /* cards */ + { 1451, 0, 0, 1 }, /* care */ + { 1456, 0, 0, 1 }, /* career */ + { 1463, 0, 0, 1 }, /* careers */ + { 1479, 0, 0, 1 }, /* cars */ + { 1484, 0, 0, 1 }, /* cartier */ + { 1492, 0, 0, 1 }, /* casa */ + { 1497, 0, 0, 1 }, /* case */ + { 1502, 0, 0, 1 }, /* caseih */ + { 1509, 0, 0, 1 }, /* cash */ + { 1514, 0, 0, 1 }, /* casino */ + { 1524, 0, 0, 1 }, /* cat */ + { 1528, 0, 0, 1 }, /* catering */ + { 1537, 0, 0, 1 }, /* catholic */ + { 1564, 0, 0, 1 }, /* cba */ + { 1067, 0, 0, 1 }, /* cbn */ + { 1568, 0, 0, 1 }, /* cbre */ + { 1240, 0, 0, 1 }, /* cbs */ + { 1580, 3993, 7, 1 }, /* cc */ + { 1584, 3851, 1, 1 }, /* cd */ + { 1587, 0, 0, 1 }, /* ceb */ + { 1594, 0, 0, 1 }, /* center */ + { 1601, 0, 0, 1 }, /* ceo */ + { 1605, 0, 0, 1 }, /* cern */ + { 1612, 3838, 1, 1 }, /* cf */ + { 1615, 0, 0, 1 }, /* cfa */ + { 1619, 0, 0, 1 }, /* cfd */ { 838, 0, 0, 1 }, /* cg */ - { 1146, 3850, 2, 1 }, /* ch */ - { 1627, 0, 0, 1 }, /* chanel */ - { 1640, 0, 0, 1 }, /* channel */ - { 1648, 0, 0, 1 }, /* chase */ - { 1654, 0, 0, 1 }, /* chat */ - { 1659, 0, 0, 1 }, /* cheap */ - { 1665, 0, 0, 1 }, /* chintai */ - { 1673, 0, 0, 1 }, /* chloe */ - { 1679, 0, 0, 1 }, /* christmas */ - { 1689, 0, 0, 1 }, /* chrome */ - { 1696, 0, 0, 1 }, /* chrysler */ - { 1705, 0, 0, 1 }, /* church */ - { 1713, 3852, 15, 1 }, /* ci */ - { 1716, 0, 0, 1 }, /* cipriani */ - { 1725, 0, 0, 1 }, /* circle */ - { 1739, 0, 0, 1 }, /* cisco */ - { 1745, 0, 0, 1 }, /* citadel */ - { 1753, 0, 0, 1 }, /* citi */ - { 1758, 0, 0, 1 }, /* citic */ - { 1765, 0, 0, 1 }, /* city */ - { 1770, 0, 0, 1 }, /* cityeats */ - { 995, 3867, 2, 0 }, /* ck */ - { 1787, 3869, 5, 1 }, /* cl */ - { 1790, 0, 0, 1 }, /* claims */ - { 1797, 0, 0, 1 }, /* cleaning */ - { 1806, 0, 0, 1 }, /* click */ - { 1812, 0, 0, 1 }, /* clinic */ - { 1819, 0, 0, 1 }, /* clinique */ - { 1828, 0, 0, 1 }, /* clothing */ - { 1839, 1669, 3, 1 }, /* cloud */ - { 1849, 3686, 1, 1 }, /* club */ - { 1854, 0, 0, 1 }, /* clubmed */ - { 1863, 3874, 4, 1 }, /* cm */ - { 842, 1672, 44, 1 }, /* cn */ - { 113, 1720, 13, 1 }, /* co */ - { 1870, 0, 0, 1 }, /* coach */ - { 1876, 0, 0, 1 }, /* codes */ - { 1882, 0, 0, 1 }, /* coffee */ - { 1894, 0, 0, 1 }, /* college */ - { 1902, 0, 0, 1 }, /* cologne */ - { 1913, 1733, 273, 1 }, /* com */ - { 1917, 0, 0, 1 }, /* comcast */ - { 1925, 0, 0, 1 }, /* commbank */ - { 1934, 0, 0, 1 }, /* community */ + { 1147, 4000, 7, 1 }, /* ch */ + { 1628, 0, 0, 1 }, /* chanel */ + { 1641, 0, 0, 1 }, /* channel */ + { 1649, 0, 0, 1 }, /* chase */ + { 1655, 0, 0, 1 }, /* chat */ + { 1660, 0, 0, 1 }, /* cheap */ + { 1666, 0, 0, 1 }, /* chintai */ + { 1674, 0, 0, 1 }, /* chloe */ + { 1680, 0, 0, 1 }, /* christmas */ + { 1690, 0, 0, 1 }, /* chrome */ + { 1697, 0, 0, 1 }, /* chrysler */ + { 1706, 0, 0, 1 }, /* church */ + { 1714, 4007, 15, 1 }, /* ci */ + { 1717, 0, 0, 1 }, /* cipriani */ + { 1726, 0, 0, 1 }, /* circle */ + { 1740, 0, 0, 1 }, /* cisco */ + { 1746, 0, 0, 1 }, /* citadel */ + { 1754, 0, 0, 1 }, /* citi */ + { 1759, 0, 0, 1 }, /* citic */ + { 1766, 0, 0, 1 }, /* city */ + { 1771, 0, 0, 1 }, /* cityeats */ + { 996, 4022, 2, 0 }, /* ck */ + { 1788, 4024, 5, 1 }, /* cl */ + { 1791, 0, 0, 1 }, /* claims */ + { 1798, 0, 0, 1 }, /* cleaning */ + { 1807, 0, 0, 1 }, /* click */ + { 1813, 0, 0, 1 }, /* clinic */ + { 1820, 0, 0, 1 }, /* clinique */ + { 1829, 0, 0, 1 }, /* clothing */ + { 1840, 1697, 6, 1 }, /* cloud */ + { 1850, 3852, 1, 1 }, /* club */ + { 1855, 0, 0, 1 }, /* clubmed */ + { 1864, 4029, 4, 1 }, /* cm */ + { 842, 1703, 44, 1 }, /* cn */ + { 113, 1751, 14, 1 }, /* co */ + { 1871, 0, 0, 1 }, /* coach */ + { 1877, 0, 0, 1 }, /* codes */ + { 1883, 0, 0, 1 }, /* coffee */ + { 1895, 0, 0, 1 }, /* college */ + { 1903, 0, 0, 1 }, /* cologne */ + { 1914, 1765, 280, 1 }, /* com */ + { 1918, 0, 0, 1 }, /* comcast */ + { 1926, 0, 0, 1 }, /* commbank */ + { 1935, 0, 0, 1 }, /* community */ { 201, 0, 0, 1 }, /* company */ - { 1944, 0, 0, 1 }, /* compare */ - { 1952, 0, 0, 1 }, /* computer */ - { 1961, 0, 0, 1 }, /* comsec */ - { 1968, 0, 0, 1 }, /* condos */ - { 1975, 0, 0, 1 }, /* construction */ - { 1988, 0, 0, 1 }, /* consulting */ - { 1999, 0, 0, 1 }, /* contact */ - { 2007, 0, 0, 1 }, /* contractors */ - { 2019, 0, 0, 1 }, /* cooking */ - { 2027, 0, 0, 1 }, /* cookingchannel */ - { 2042, 0, 0, 1 }, /* cool */ - { 2047, 0, 0, 1 }, /* coop */ - { 2052, 0, 0, 1 }, /* corsica */ - { 2060, 0, 0, 1 }, /* country */ - { 2068, 0, 0, 1 }, /* coupon */ - { 2075, 0, 0, 1 }, /* coupons */ - { 2083, 0, 0, 1 }, /* courses */ - { 2091, 3890, 7, 1 }, /* cr */ - { 2094, 0, 0, 1 }, /* credit */ - { 2101, 0, 0, 1 }, /* creditcard */ - { 2112, 0, 0, 1 }, /* creditunion */ - { 2124, 0, 0, 1 }, /* cricket */ - { 2132, 0, 0, 1 }, /* crown */ - { 2138, 0, 0, 1 }, /* crs */ - { 2142, 0, 0, 1 }, /* cruise */ - { 2149, 0, 0, 1 }, /* cruises */ - { 2157, 0, 0, 1 }, /* csc */ - { 2162, 3897, 6, 1 }, /* cu */ - { 2165, 0, 0, 1 }, /* cuisinella */ - { 2176, 3672, 1, 1 }, /* cv */ - { 2181, 3903, 4, 1 }, /* cw */ - { 2184, 3907, 2, 1 }, /* cx */ - { 241, 2069, 13, 1 }, /* cy */ - { 2187, 0, 0, 1 }, /* cymru */ - { 2193, 0, 0, 1 }, /* cyou */ - { 2202, 3909, 4, 1 }, /* cz */ - { 2205, 0, 0, 1 }, /* dabur */ - { 2215, 0, 0, 1 }, /* dad */ - { 2219, 0, 0, 1 }, /* dance */ - { 2231, 0, 0, 1 }, /* data */ - { 2240, 0, 0, 1 }, /* date */ - { 2245, 0, 0, 1 }, /* dating */ - { 2252, 0, 0, 1 }, /* datsun */ - { 1006, 0, 0, 1 }, /* day */ - { 2265, 0, 0, 1 }, /* dclk */ - { 2270, 0, 0, 1 }, /* dds */ - { 2276, 2082, 38, 1 }, /* de */ - { 2279, 0, 0, 1 }, /* deal */ - { 2284, 0, 0, 1 }, /* dealer */ - { 2291, 0, 0, 1 }, /* deals */ - { 2297, 0, 0, 1 }, /* degree */ - { 2304, 0, 0, 1 }, /* delivery */ - { 2313, 0, 0, 1 }, /* dell */ - { 2318, 0, 0, 1 }, /* deloitte */ - { 2327, 0, 0, 1 }, /* delta */ - { 2338, 0, 0, 1 }, /* democrat */ - { 2347, 0, 0, 1 }, /* dental */ - { 2354, 0, 0, 1 }, /* dentist */ - { 2362, 0, 0, 1 }, /* desi */ - { 2373, 0, 0, 1 }, /* design */ - { 2380, 0, 0, 1 }, /* dev */ - { 2384, 0, 0, 1 }, /* dhl */ - { 2388, 0, 0, 1 }, /* diamonds */ - { 2397, 0, 0, 1 }, /* diet */ - { 2402, 0, 0, 1 }, /* digital */ - { 2414, 0, 0, 1 }, /* direct */ - { 2429, 0, 0, 1 }, /* directory */ - { 2439, 0, 0, 1 }, /* discount */ - { 2448, 0, 0, 1 }, /* discover */ - { 2457, 0, 0, 1 }, /* dish */ - { 2462, 0, 0, 1 }, /* diy */ - { 2466, 0, 0, 1 }, /* dj */ - { 2470, 3916, 6, 1 }, /* dk */ - { 2474, 3651, 5, 1 }, /* dm */ - { 2477, 0, 0, 1 }, /* dnp */ - { 48, 3922, 10, 1 }, /* do */ - { 2486, 0, 0, 1 }, /* docs */ - { 2496, 0, 0, 1 }, /* doctor */ - { 2503, 0, 0, 1 }, /* dodge */ - { 2509, 0, 0, 1 }, /* dog */ - { 2513, 0, 0, 1 }, /* doha */ - { 2518, 0, 0, 1 }, /* domains */ - { 2526, 0, 0, 1 }, /* dot */ - { 2530, 0, 0, 1 }, /* download */ - { 2539, 0, 0, 1 }, /* drive */ - { 2545, 0, 0, 1 }, /* dtv */ - { 2549, 0, 0, 1 }, /* dubai */ - { 1779, 0, 0, 1 }, /* duck */ - { 2555, 0, 0, 1 }, /* dunlop */ - { 2562, 0, 0, 1 }, /* duns */ - { 2567, 0, 0, 1 }, /* dupont */ - { 2574, 0, 0, 1 }, /* durban */ + { 1945, 0, 0, 1 }, /* compare */ + { 1953, 0, 0, 1 }, /* computer */ + { 1962, 0, 0, 1 }, /* comsec */ + { 1969, 0, 0, 1 }, /* condos */ + { 1976, 0, 0, 1 }, /* construction */ + { 1989, 0, 0, 1 }, /* consulting */ + { 2000, 0, 0, 1 }, /* contact */ + { 2008, 0, 0, 1 }, /* contractors */ + { 2020, 0, 0, 1 }, /* cooking */ + { 2028, 0, 0, 1 }, /* cookingchannel */ + { 2043, 4047, 1, 1 }, /* cool */ + { 2048, 0, 0, 1 }, /* coop */ + { 2053, 0, 0, 1 }, /* corsica */ + { 2061, 0, 0, 1 }, /* country */ + { 2069, 0, 0, 1 }, /* coupon */ + { 2076, 0, 0, 1 }, /* coupons */ + { 2084, 0, 0, 1 }, /* courses */ + { 2092, 4048, 7, 1 }, /* cr */ + { 2095, 0, 0, 1 }, /* credit */ + { 2102, 0, 0, 1 }, /* creditcard */ + { 2113, 0, 0, 1 }, /* creditunion */ + { 2125, 0, 0, 1 }, /* cricket */ + { 2133, 0, 0, 1 }, /* crown */ + { 2139, 0, 0, 1 }, /* crs */ + { 2143, 0, 0, 1 }, /* cruise */ + { 2150, 0, 0, 1 }, /* cruises */ + { 2158, 0, 0, 1 }, /* csc */ + { 2163, 4055, 6, 1 }, /* cu */ + { 2166, 0, 0, 1 }, /* cuisinella */ + { 2177, 3838, 1, 1 }, /* cv */ + { 2182, 4061, 4, 1 }, /* cw */ + { 2185, 4065, 3, 1 }, /* cx */ + { 241, 2113, 13, 1 }, /* cy */ + { 2188, 0, 0, 1 }, /* cymru */ + { 2194, 0, 0, 1 }, /* cyou */ + { 2203, 2126, 5, 1 }, /* cz */ + { 2206, 0, 0, 1 }, /* dabur */ + { 2216, 0, 0, 1 }, /* dad */ + { 2220, 0, 0, 1 }, /* dance */ + { 2232, 0, 0, 1 }, /* data */ + { 2241, 0, 0, 1 }, /* date */ + { 2246, 0, 0, 1 }, /* dating */ + { 2253, 0, 0, 1 }, /* datsun */ + { 1007, 0, 0, 1 }, /* day */ + { 2266, 0, 0, 1 }, /* dclk */ + { 2271, 0, 0, 1 }, /* dds */ + { 2277, 2131, 48, 1 }, /* de */ + { 2280, 0, 0, 1 }, /* deal */ + { 2285, 0, 0, 1 }, /* dealer */ + { 2292, 0, 0, 1 }, /* deals */ + { 2298, 0, 0, 1 }, /* degree */ + { 2305, 0, 0, 1 }, /* delivery */ + { 2314, 0, 0, 1 }, /* dell */ + { 2319, 0, 0, 1 }, /* deloitte */ + { 2328, 0, 0, 1 }, /* delta */ + { 2339, 0, 0, 1 }, /* democrat */ + { 2348, 0, 0, 1 }, /* dental */ + { 2355, 0, 0, 1 }, /* dentist */ + { 2363, 0, 0, 1 }, /* desi */ + { 2374, 0, 0, 1 }, /* design */ + { 2383, 0, 0, 1 }, /* dev */ + { 2387, 0, 0, 1 }, /* dhl */ + { 2391, 0, 0, 1 }, /* diamonds */ + { 2400, 0, 0, 1 }, /* diet */ + { 2405, 0, 0, 1 }, /* digital */ + { 2417, 0, 0, 1 }, /* direct */ + { 2432, 0, 0, 1 }, /* directory */ + { 2442, 0, 0, 1 }, /* discount */ + { 2451, 0, 0, 1 }, /* discover */ + { 2460, 0, 0, 1 }, /* dish */ + { 2465, 0, 0, 1 }, /* diy */ + { 2469, 0, 0, 1 }, /* dj */ + { 2473, 4073, 6, 1 }, /* dk */ + { 2477, 3817, 5, 1 }, /* dm */ + { 2480, 0, 0, 1 }, /* dnp */ + { 48, 4079, 10, 1 }, /* do */ + { 2489, 0, 0, 1 }, /* docs */ + { 2499, 0, 0, 1 }, /* doctor */ + { 2506, 0, 0, 1 }, /* dodge */ + { 2512, 0, 0, 1 }, /* dog */ + { 2516, 0, 0, 1 }, /* doha */ + { 2521, 0, 0, 1 }, /* domains */ + { 2529, 0, 0, 1 }, /* dot */ + { 2533, 0, 0, 1 }, /* download */ + { 2542, 0, 0, 1 }, /* drive */ + { 2548, 0, 0, 1 }, /* dtv */ + { 2552, 0, 0, 1 }, /* dubai */ + { 1780, 0, 0, 1 }, /* duck */ + { 2558, 0, 0, 1 }, /* dunlop */ + { 2565, 0, 0, 1 }, /* duns */ + { 2570, 0, 0, 1 }, /* dupont */ + { 2577, 0, 0, 1 }, /* durban */ { 224, 0, 0, 1 }, /* dvag */ - { 2581, 0, 0, 1 }, /* dvr */ - { 2585, 0, 0, 1 }, /* dwg */ - { 2594, 3932, 8, 1 }, /* dz */ - { 2597, 0, 0, 1 }, /* earth */ - { 2604, 0, 0, 1 }, /* eat */ - { 1965, 3940, 12, 1 }, /* ec */ - { 2614, 0, 0, 1 }, /* eco */ - { 2618, 0, 0, 1 }, /* edeka */ - { 2624, 0, 0, 1 }, /* edu */ - { 2631, 0, 0, 1 }, /* education */ - { 1886, 2120, 10, 1 }, /* ee */ - { 161, 2130, 9, 1 }, /* eg */ - { 2650, 0, 0, 1 }, /* email */ - { 2656, 0, 0, 1 }, /* emerck */ - { 2663, 0, 0, 1 }, /* energy */ - { 2676, 0, 0, 1 }, /* engineer */ - { 2685, 0, 0, 1 }, /* engineering */ - { 2697, 0, 0, 1 }, /* enterprises */ - { 2709, 0, 0, 1 }, /* epost */ - { 2715, 0, 0, 1 }, /* epson */ - { 2725, 0, 0, 1 }, /* equipment */ - { 883, 3687, 1, 0 }, /* er */ - { 2740, 0, 0, 1 }, /* ericsson */ - { 2750, 0, 0, 1 }, /* erni */ - { 558, 2139, 5, 1 }, /* es */ - { 2761, 0, 0, 1 }, /* esq */ - { 2769, 2144, 1, 1 }, /* estate */ - { 2776, 0, 0, 1 }, /* esurance */ - { 915, 3952, 8, 1 }, /* et */ - { 2789, 0, 0, 1 }, /* etisalat */ - { 2798, 2145, 6, 1 }, /* eu */ - { 2801, 0, 0, 1 }, /* eurovision */ - { 2812, 2151, 1, 1 }, /* eus */ - { 2816, 0, 0, 1 }, /* events */ - { 2823, 0, 0, 1 }, /* everbank */ - { 2837, 0, 0, 1 }, /* exchange */ - { 2855, 0, 0, 1 }, /* expert */ - { 2862, 0, 0, 1 }, /* exposed */ + { 2584, 0, 0, 1 }, /* dvr */ + { 2593, 4089, 8, 1 }, /* dz */ + { 2596, 0, 0, 1 }, /* earth */ + { 2603, 0, 0, 1 }, /* eat */ + { 1966, 4097, 12, 1 }, /* ec */ + { 2613, 0, 0, 1 }, /* eco */ + { 2617, 0, 0, 1 }, /* edeka */ + { 2623, 0, 0, 1 }, /* edu */ + { 2630, 0, 0, 1 }, /* education */ + { 1887, 2179, 10, 1 }, /* ee */ + { 161, 2189, 9, 1 }, /* eg */ + { 2649, 0, 0, 1 }, /* email */ + { 2655, 0, 0, 1 }, /* emerck */ + { 2662, 0, 0, 1 }, /* energy */ + { 2675, 0, 0, 1 }, /* engineer */ + { 2684, 0, 0, 1 }, /* engineering */ + { 2696, 0, 0, 1 }, /* enterprises */ + { 2708, 0, 0, 1 }, /* epost */ + { 2714, 0, 0, 1 }, /* epson */ + { 2724, 0, 0, 1 }, /* equipment */ + { 883, 3853, 1, 0 }, /* er */ + { 2739, 0, 0, 1 }, /* ericsson */ + { 2749, 0, 0, 1 }, /* erni */ + { 558, 2198, 5, 1 }, /* es */ + { 2760, 0, 0, 1 }, /* esq */ + { 2768, 2203, 1, 1 }, /* estate */ + { 2775, 0, 0, 1 }, /* esurance */ + { 915, 4109, 8, 1 }, /* et */ + { 2788, 0, 0, 1 }, /* etisalat */ + { 2797, 2204, 7, 1 }, /* eu */ + { 2800, 0, 0, 1 }, /* eurovision */ + { 2811, 2211, 1, 1 }, /* eus */ + { 2815, 0, 0, 1 }, /* events */ + { 2822, 0, 0, 1 }, /* everbank */ + { 2836, 0, 0, 1 }, /* exchange */ + { 2854, 0, 0, 1 }, /* expert */ + { 2861, 0, 0, 1 }, /* exposed */ { 369, 0, 0, 1 }, /* express */ - { 2884, 0, 0, 1 }, /* extraspace */ - { 2895, 0, 0, 1 }, /* fage */ - { 2900, 0, 0, 1 }, /* fail */ - { 2905, 0, 0, 1 }, /* fairwinds */ - { 2915, 3961, 1, 1 }, /* faith */ + { 2869, 0, 0, 1 }, /* extraspace */ + { 2880, 0, 0, 1 }, /* fage */ + { 2885, 0, 0, 1 }, /* fail */ + { 2890, 0, 0, 1 }, /* fairwinds */ + { 2900, 4118, 1, 1 }, /* faith */ { 385, 0, 0, 1 }, /* family */ - { 2934, 0, 0, 1 }, /* fan */ - { 2938, 0, 0, 1 }, /* fans */ - { 2948, 0, 0, 1 }, /* farm */ - { 2953, 0, 0, 1 }, /* farmers */ - { 2961, 0, 0, 1 }, /* fashion */ - { 2969, 0, 0, 1 }, /* fast */ - { 2974, 0, 0, 1 }, /* fedex */ - { 2980, 0, 0, 1 }, /* feedback */ - { 2989, 0, 0, 1 }, /* ferrari */ - { 2997, 0, 0, 1 }, /* ferrero */ - { 3009, 3962, 4, 1 }, /* fi */ - { 3012, 0, 0, 1 }, /* fiat */ - { 3017, 0, 0, 1 }, /* fidelity */ - { 3026, 0, 0, 1 }, /* fido */ - { 3031, 0, 0, 1 }, /* film */ - { 3036, 0, 0, 1 }, /* final */ - { 3042, 0, 0, 1 }, /* finance */ - { 3053, 0, 0, 1 }, /* financial */ - { 3063, 0, 0, 1 }, /* fire */ - { 3068, 0, 0, 1 }, /* firestone */ - { 3078, 0, 0, 1 }, /* firmdale */ - { 3087, 0, 0, 1 }, /* fish */ - { 3092, 0, 0, 1 }, /* fishing */ - { 3100, 3966, 1, 1 }, /* fit */ - { 3104, 0, 0, 1 }, /* fitness */ - { 3112, 3687, 1, 0 }, /* fj */ - { 3116, 3687, 1, 0 }, /* fk */ - { 3119, 0, 0, 1 }, /* flickr */ - { 3126, 0, 0, 1 }, /* flights */ - { 3134, 0, 0, 1 }, /* flir */ - { 3139, 0, 0, 1 }, /* florist */ - { 3147, 0, 0, 1 }, /* flowers */ - { 3155, 0, 0, 1 }, /* fly */ - { 3160, 0, 0, 1 }, /* fm */ - { 3169, 0, 0, 1 }, /* fo */ - { 3172, 0, 0, 1 }, /* foo */ - { 3176, 0, 0, 1 }, /* food */ - { 3181, 0, 0, 1 }, /* foodnetwork */ - { 3193, 0, 0, 1 }, /* football */ - { 3204, 0, 0, 1 }, /* ford */ - { 3209, 0, 0, 1 }, /* forex */ - { 3215, 0, 0, 1 }, /* forsale */ - { 3223, 0, 0, 1 }, /* forum */ - { 3229, 0, 0, 1 }, /* foundation */ - { 3240, 0, 0, 1 }, /* fox */ - { 3245, 3967, 30, 1 }, /* fr */ - { 3255, 0, 0, 1 }, /* free */ - { 3260, 0, 0, 1 }, /* fresenius */ - { 3270, 0, 0, 1 }, /* frl */ - { 3274, 0, 0, 1 }, /* frogans */ - { 3282, 0, 0, 1 }, /* frontdoor */ - { 3292, 0, 0, 1 }, /* frontier */ - { 3301, 0, 0, 1 }, /* ftr */ - { 3305, 0, 0, 1 }, /* fujitsu */ - { 3313, 0, 0, 1 }, /* fujixerox */ - { 3323, 0, 0, 1 }, /* fun */ - { 3327, 0, 0, 1 }, /* fund */ - { 3332, 0, 0, 1 }, /* furniture */ - { 3342, 0, 0, 1 }, /* futbol */ - { 3349, 0, 0, 1 }, /* fyi */ - { 3355, 0, 0, 1 }, /* ga */ - { 3360, 0, 0, 1 }, /* gal */ - { 3367, 0, 0, 1 }, /* gallery */ - { 3375, 0, 0, 1 }, /* gallo */ - { 3381, 0, 0, 1 }, /* gallup */ - { 3392, 0, 0, 1 }, /* game */ - { 3405, 0, 0, 1 }, /* games */ - { 3411, 0, 0, 1 }, /* gap */ - { 3417, 0, 0, 1 }, /* garden */ - { 3441, 0, 0, 1 }, /* gb */ - { 3444, 0, 0, 1 }, /* gbiz */ - { 3449, 0, 0, 1 }, /* gd */ - { 3452, 0, 0, 1 }, /* gdn */ - { 1899, 3997, 7, 1 }, /* ge */ - { 3461, 0, 0, 1 }, /* gea */ - { 3465, 0, 0, 1 }, /* gent */ - { 3470, 0, 0, 1 }, /* genting */ - { 3478, 0, 0, 1 }, /* george */ - { 3486, 0, 0, 1 }, /* gf */ - { 3489, 4004, 3, 1 }, /* gg */ - { 3492, 0, 0, 1 }, /* ggee */ - { 3505, 4007, 5, 1 }, /* gh */ - { 3510, 4012, 6, 1 }, /* gi */ - { 3513, 0, 0, 1 }, /* gift */ - { 3518, 0, 0, 1 }, /* gifts */ - { 3524, 0, 0, 1 }, /* gives */ - { 3530, 0, 0, 1 }, /* giving */ - { 3537, 4018, 5, 1 }, /* gl */ - { 3540, 0, 0, 1 }, /* glade */ - { 3546, 0, 0, 1 }, /* glass */ - { 3559, 0, 0, 1 }, /* gle */ - { 3563, 0, 0, 1 }, /* global */ - { 3570, 0, 0, 1 }, /* globo */ - { 3590, 0, 0, 1 }, /* gm */ - { 3593, 0, 0, 1 }, /* gmail */ - { 934, 0, 0, 1 }, /* gmbh */ - { 3599, 0, 0, 1 }, /* gmo */ - { 3603, 0, 0, 1 }, /* gmx */ - { 2377, 4023, 6, 1 }, /* gn */ - { 3613, 0, 0, 1 }, /* godaddy */ - { 3621, 0, 0, 1 }, /* gold */ - { 3626, 0, 0, 1 }, /* goldpoint */ - { 3636, 0, 0, 1 }, /* golf */ - { 3641, 0, 0, 1 }, /* goo */ - { 3645, 0, 0, 1 }, /* goodhands */ - { 3655, 0, 0, 1 }, /* goodyear */ - { 3664, 0, 0, 1 }, /* goog */ - { 3556, 0, 0, 1 }, /* google */ - { 3669, 0, 0, 1 }, /* gop */ - { 3676, 0, 0, 1 }, /* got */ - { 3686, 0, 0, 1 }, /* gov */ - { 3690, 4029, 6, 1 }, /* gp */ - { 3693, 0, 0, 1 }, /* gq */ - { 3697, 4035, 6, 1 }, /* gr */ - { 3700, 0, 0, 1 }, /* grainger */ - { 3709, 0, 0, 1 }, /* graphics */ - { 3718, 0, 0, 1 }, /* gratis */ - { 3730, 0, 0, 1 }, /* green */ - { 3736, 0, 0, 1 }, /* gripe */ - { 3742, 0, 0, 1 }, /* grocery */ - { 3753, 0, 0, 1 }, /* group */ - { 3760, 0, 0, 1 }, /* gs */ - { 3763, 4041, 7, 1 }, /* gt */ - { 3768, 3687, 1, 0 }, /* gu */ - { 3774, 0, 0, 1 }, /* guardian */ - { 3783, 0, 0, 1 }, /* gucci */ - { 3789, 0, 0, 1 }, /* guge */ - { 3794, 0, 0, 1 }, /* guide */ - { 3800, 0, 0, 1 }, /* guitars */ - { 3813, 0, 0, 1 }, /* guru */ - { 3820, 0, 0, 1 }, /* gw */ - { 2667, 4048, 6, 1 }, /* gy */ - { 3823, 0, 0, 1 }, /* hair */ - { 3828, 0, 0, 1 }, /* hamburg */ - { 3836, 0, 0, 1 }, /* hangout */ + { 2919, 0, 0, 1 }, /* fan */ + { 2923, 0, 0, 1 }, /* fans */ + { 2933, 4119, 1, 1 }, /* farm */ + { 2938, 0, 0, 1 }, /* farmers */ + { 2946, 0, 0, 1 }, /* fashion */ + { 2954, 0, 0, 1 }, /* fast */ + { 2959, 0, 0, 1 }, /* fedex */ + { 2965, 0, 0, 1 }, /* feedback */ + { 2974, 0, 0, 1 }, /* ferrari */ + { 2982, 0, 0, 1 }, /* ferrero */ + { 2994, 4120, 4, 1 }, /* fi */ + { 2997, 0, 0, 1 }, /* fiat */ + { 3002, 0, 0, 1 }, /* fidelity */ + { 3011, 0, 0, 1 }, /* fido */ + { 3016, 0, 0, 1 }, /* film */ + { 3021, 0, 0, 1 }, /* final */ + { 3027, 0, 0, 1 }, /* finance */ + { 3038, 0, 0, 1 }, /* financial */ + { 3048, 0, 0, 1 }, /* fire */ + { 3053, 0, 0, 1 }, /* firestone */ + { 3063, 0, 0, 1 }, /* firmdale */ + { 3072, 0, 0, 1 }, /* fish */ + { 3077, 0, 0, 1 }, /* fishing */ + { 3085, 4124, 1, 1 }, /* fit */ + { 3089, 0, 0, 1 }, /* fitness */ + { 3097, 3853, 1, 0 }, /* fj */ + { 3101, 3853, 1, 0 }, /* fk */ + { 3104, 0, 0, 1 }, /* flickr */ + { 3111, 0, 0, 1 }, /* flights */ + { 3119, 0, 0, 1 }, /* flir */ + { 3124, 0, 0, 1 }, /* florist */ + { 3132, 0, 0, 1 }, /* flowers */ + { 3140, 0, 0, 1 }, /* fly */ + { 3145, 0, 0, 1 }, /* fm */ + { 3154, 0, 0, 1 }, /* fo */ + { 3157, 0, 0, 1 }, /* foo */ + { 3161, 0, 0, 1 }, /* food */ + { 3166, 0, 0, 1 }, /* foodnetwork */ + { 3178, 0, 0, 1 }, /* football */ + { 3189, 0, 0, 1 }, /* ford */ + { 3194, 0, 0, 1 }, /* forex */ + { 3200, 0, 0, 1 }, /* forsale */ + { 3208, 0, 0, 1 }, /* forum */ + { 3214, 0, 0, 1 }, /* foundation */ + { 3225, 0, 0, 1 }, /* fox */ + { 3230, 4125, 30, 1 }, /* fr */ + { 3240, 0, 0, 1 }, /* free */ + { 3245, 0, 0, 1 }, /* fresenius */ + { 3255, 0, 0, 1 }, /* frl */ + { 3259, 0, 0, 1 }, /* frogans */ + { 3267, 0, 0, 1 }, /* frontdoor */ + { 3277, 0, 0, 1 }, /* frontier */ + { 3286, 0, 0, 1 }, /* ftr */ + { 3290, 0, 0, 1 }, /* fujitsu */ + { 3298, 0, 0, 1 }, /* fujixerox */ + { 3308, 0, 0, 1 }, /* fun */ + { 3312, 0, 0, 1 }, /* fund */ + { 3317, 0, 0, 1 }, /* furniture */ + { 3327, 0, 0, 1 }, /* futbol */ + { 3334, 0, 0, 1 }, /* fyi */ + { 3340, 0, 0, 1 }, /* ga */ + { 3345, 0, 0, 1 }, /* gal */ + { 3352, 0, 0, 1 }, /* gallery */ + { 3360, 0, 0, 1 }, /* gallo */ + { 3366, 0, 0, 1 }, /* gallup */ + { 3377, 0, 0, 1 }, /* game */ + { 3390, 0, 0, 1 }, /* games */ + { 3396, 0, 0, 1 }, /* gap */ + { 3402, 0, 0, 1 }, /* garden */ + { 3426, 0, 0, 1 }, /* gb */ + { 3429, 0, 0, 1 }, /* gbiz */ + { 3434, 0, 0, 1 }, /* gd */ + { 3437, 0, 0, 1 }, /* gdn */ + { 1900, 4155, 7, 1 }, /* ge */ + { 3446, 0, 0, 1 }, /* gea */ + { 3450, 0, 0, 1 }, /* gent */ + { 3455, 0, 0, 1 }, /* genting */ + { 3463, 0, 0, 1 }, /* george */ + { 3471, 0, 0, 1 }, /* gf */ + { 3474, 4162, 4, 1 }, /* gg */ + { 3477, 0, 0, 1 }, /* ggee */ + { 3490, 4166, 5, 1 }, /* gh */ + { 3495, 4171, 6, 1 }, /* gi */ + { 3498, 0, 0, 1 }, /* gift */ + { 3503, 0, 0, 1 }, /* gifts */ + { 3509, 0, 0, 1 }, /* gives */ + { 3515, 0, 0, 1 }, /* giving */ + { 3522, 4177, 5, 1 }, /* gl */ + { 3525, 0, 0, 1 }, /* glade */ + { 3531, 0, 0, 1 }, /* glass */ + { 3544, 0, 0, 1 }, /* gle */ + { 3548, 0, 0, 1 }, /* global */ + { 3555, 0, 0, 1 }, /* globo */ + { 3575, 0, 0, 1 }, /* gm */ + { 3578, 0, 0, 1 }, /* gmail */ + { 935, 0, 0, 1 }, /* gmbh */ + { 3584, 0, 0, 1 }, /* gmo */ + { 3588, 0, 0, 1 }, /* gmx */ + { 2378, 4182, 6, 1 }, /* gn */ + { 3598, 0, 0, 1 }, /* godaddy */ + { 3606, 0, 0, 1 }, /* gold */ + { 3611, 0, 0, 1 }, /* goldpoint */ + { 3621, 0, 0, 1 }, /* golf */ + { 3626, 0, 0, 1 }, /* goo */ + { 3630, 0, 0, 1 }, /* goodhands */ + { 3640, 0, 0, 1 }, /* goodyear */ + { 3649, 4188, 1, 1 }, /* goog */ + { 3541, 0, 0, 1 }, /* google */ + { 3654, 0, 0, 1 }, /* gop */ + { 3661, 0, 0, 1 }, /* got */ + { 3671, 0, 0, 1 }, /* gov */ + { 3675, 4189, 6, 1 }, /* gp */ + { 3678, 0, 0, 1 }, /* gq */ + { 3682, 4195, 6, 1 }, /* gr */ + { 3685, 0, 0, 1 }, /* grainger */ + { 3694, 0, 0, 1 }, /* graphics */ + { 3703, 0, 0, 1 }, /* gratis */ + { 3715, 0, 0, 1 }, /* green */ + { 3721, 0, 0, 1 }, /* gripe */ + { 3727, 0, 0, 1 }, /* grocery */ + { 3738, 0, 0, 1 }, /* group */ + { 3745, 0, 0, 1 }, /* gs */ + { 3748, 4201, 7, 1 }, /* gt */ + { 3753, 3853, 1, 0 }, /* gu */ + { 3756, 0, 0, 1 }, /* guardian */ + { 3765, 0, 0, 1 }, /* gucci */ + { 3771, 0, 0, 1 }, /* guge */ + { 3776, 0, 0, 1 }, /* guide */ + { 3782, 0, 0, 1 }, /* guitars */ + { 3795, 0, 0, 1 }, /* guru */ + { 3802, 0, 0, 1 }, /* gw */ + { 2666, 4208, 6, 1 }, /* gy */ + { 3805, 0, 0, 1 }, /* hair */ + { 3810, 0, 0, 1 }, /* hamburg */ + { 3818, 0, 0, 1 }, /* hangout */ { 812, 0, 0, 1 }, /* haus */ - { 3844, 0, 0, 1 }, /* hbo */ - { 3848, 0, 0, 1 }, /* hdfc */ - { 3853, 0, 0, 1 }, /* hdfcbank */ - { 3862, 0, 0, 1 }, /* health */ - { 1444, 0, 0, 1 }, /* healthcare */ - { 3869, 0, 0, 1 }, /* help */ - { 3874, 0, 0, 1 }, /* helsinki */ - { 3887, 0, 0, 1 }, /* here */ - { 3892, 0, 0, 1 }, /* hermes */ - { 3899, 0, 0, 1 }, /* hgtv */ - { 3904, 0, 0, 1 }, /* hiphop */ - { 3911, 0, 0, 1 }, /* hisamitsu */ - { 3921, 0, 0, 1 }, /* hitachi */ - { 3935, 0, 0, 1 }, /* hiv */ - { 3940, 4054, 24, 1 }, /* hk */ - { 3943, 0, 0, 1 }, /* hkt */ - { 3947, 0, 0, 1 }, /* hm */ - { 3954, 4078, 6, 1 }, /* hn */ - { 3957, 0, 0, 1 }, /* hockey */ - { 3964, 0, 0, 1 }, /* holdings */ - { 3973, 0, 0, 1 }, /* holiday */ - { 3981, 0, 0, 1 }, /* homedepot */ - { 3991, 0, 0, 1 }, /* homegoods */ - { 4001, 0, 0, 1 }, /* homes */ - { 4007, 0, 0, 1 }, /* homesense */ - { 4017, 0, 0, 1 }, /* honda */ - { 4023, 0, 0, 1 }, /* honeywell */ - { 4033, 0, 0, 1 }, /* horse */ - { 4039, 0, 0, 1 }, /* hospital */ - { 4051, 0, 0, 1 }, /* host */ - { 4062, 4084, 1, 1 }, /* hosting */ - { 4070, 0, 0, 1 }, /* hot */ - { 4074, 0, 0, 1 }, /* hoteles */ - { 4087, 0, 0, 1 }, /* hotels */ - { 4094, 0, 0, 1 }, /* hotmail */ - { 4105, 0, 0, 1 }, /* house */ - { 4112, 0, 0, 1 }, /* how */ - { 4118, 4085, 5, 1 }, /* hr */ - { 4121, 0, 0, 1 }, /* hsbc */ - { 4129, 4090, 17, 1 }, /* ht */ - { 4132, 0, 0, 1 }, /* htc */ - { 4138, 4107, 32, 1 }, /* hu */ - { 4141, 0, 0, 1 }, /* hughes */ - { 4148, 0, 0, 1 }, /* hyatt */ - { 4154, 0, 0, 1 }, /* hyundai */ - { 1054, 0, 0, 1 }, /* ibm */ - { 4162, 0, 0, 1 }, /* icbc */ - { 4170, 0, 0, 1 }, /* ice */ - { 2161, 0, 0, 1 }, /* icu */ - { 437, 2152, 11, 1 }, /* id */ - { 31, 4139, 2, 1 }, /* ie */ - { 4178, 0, 0, 1 }, /* ieee */ - { 3159, 0, 0, 1 }, /* ifm */ - { 4183, 0, 0, 1 }, /* iinet */ - { 4189, 0, 0, 1 }, /* ikano */ - { 2653, 2163, 8, 1 }, /* il */ - { 4200, 2171, 8, 1 }, /* im */ - { 4203, 0, 0, 1 }, /* imamat */ - { 4210, 0, 0, 1 }, /* imdb */ - { 4215, 0, 0, 1 }, /* immo */ - { 4220, 0, 0, 1 }, /* immobilien */ - { 898, 4143, 14, 1 }, /* in */ - { 4235, 0, 0, 1 }, /* industries */ - { 4246, 0, 0, 1 }, /* infiniti */ - { 3167, 4157, 16, 1 }, /* info */ - { 970, 0, 0, 1 }, /* ing */ - { 4262, 0, 0, 1 }, /* ink */ - { 4266, 0, 0, 1 }, /* institute */ - { 4280, 0, 0, 1 }, /* insurance */ - { 4290, 0, 0, 1 }, /* insure */ - { 3632, 3888, 1, 1 }, /* int */ - { 4302, 0, 0, 1 }, /* intel */ - { 4308, 0, 0, 1 }, /* international */ - { 4322, 0, 0, 1 }, /* intuit */ - { 4329, 0, 0, 1 }, /* investments */ - { 611, 2179, 20, 1 }, /* io */ - { 4341, 0, 0, 1 }, /* ipiranga */ - { 4350, 3549, 6, 1 }, /* iq */ - { 3136, 4174, 9, 1 }, /* ir */ - { 4353, 0, 0, 1 }, /* irish */ - { 3722, 4183, 8, 1 }, /* is */ - { 4364, 0, 0, 1 }, /* iselect */ - { 4372, 0, 0, 1 }, /* ismaili */ - { 2358, 0, 0, 1 }, /* ist */ - { 4385, 0, 0, 1 }, /* istanbul */ - { 2098, 4191, 369, 1 }, /* it */ + { 3826, 0, 0, 1 }, /* hbo */ + { 3830, 0, 0, 1 }, /* hdfc */ + { 3835, 0, 0, 1 }, /* hdfcbank */ + { 3844, 0, 0, 1 }, /* health */ + { 1445, 0, 0, 1 }, /* healthcare */ + { 3851, 0, 0, 1 }, /* help */ + { 3856, 0, 0, 1 }, /* helsinki */ + { 3869, 0, 0, 1 }, /* here */ + { 3874, 0, 0, 1 }, /* hermes */ + { 3881, 0, 0, 1 }, /* hgtv */ + { 3886, 0, 0, 1 }, /* hiphop */ + { 3893, 0, 0, 1 }, /* hisamitsu */ + { 3903, 0, 0, 1 }, /* hitachi */ + { 3917, 0, 0, 1 }, /* hiv */ + { 3922, 4214, 24, 1 }, /* hk */ + { 3925, 0, 0, 1 }, /* hkt */ + { 3929, 0, 0, 1 }, /* hm */ + { 3936, 4238, 6, 1 }, /* hn */ + { 3939, 0, 0, 1 }, /* hockey */ + { 3946, 0, 0, 1 }, /* holdings */ + { 3955, 0, 0, 1 }, /* holiday */ + { 3963, 0, 0, 1 }, /* homedepot */ + { 3973, 0, 0, 1 }, /* homegoods */ + { 3983, 0, 0, 1 }, /* homes */ + { 3989, 0, 0, 1 }, /* homesense */ + { 3999, 0, 0, 1 }, /* honda */ + { 4005, 0, 0, 1 }, /* honeywell */ + { 4015, 0, 0, 1 }, /* horse */ + { 4021, 0, 0, 1 }, /* hospital */ + { 4033, 4244, 2, 1 }, /* host */ + { 4043, 4246, 1, 1 }, /* hosting */ + { 4051, 0, 0, 1 }, /* hot */ + { 4055, 0, 0, 1 }, /* hoteles */ + { 4068, 0, 0, 1 }, /* hotels */ + { 4075, 0, 0, 1 }, /* hotmail */ + { 4086, 0, 0, 1 }, /* house */ + { 4093, 0, 0, 1 }, /* how */ + { 4099, 4247, 5, 1 }, /* hr */ + { 4102, 0, 0, 1 }, /* hsbc */ + { 4110, 4252, 17, 1 }, /* ht */ + { 4113, 0, 0, 1 }, /* htc */ + { 4119, 4269, 32, 1 }, /* hu */ + { 4122, 0, 0, 1 }, /* hughes */ + { 4129, 0, 0, 1 }, /* hyatt */ + { 4135, 0, 0, 1 }, /* hyundai */ + { 1055, 0, 0, 1 }, /* ibm */ + { 4143, 0, 0, 1 }, /* icbc */ + { 4151, 0, 0, 1 }, /* ice */ + { 2162, 0, 0, 1 }, /* icu */ + { 437, 2212, 11, 1 }, /* id */ + { 31, 4301, 2, 1 }, /* ie */ + { 4159, 0, 0, 1 }, /* ieee */ + { 3144, 0, 0, 1 }, /* ifm */ + { 4164, 0, 0, 1 }, /* ikano */ + { 2652, 2223, 8, 1 }, /* il */ + { 4175, 2231, 8, 1 }, /* im */ + { 4178, 0, 0, 1 }, /* imamat */ + { 4185, 0, 0, 1 }, /* imdb */ + { 4190, 0, 0, 1 }, /* immo */ + { 4195, 0, 0, 1 }, /* immobilien */ + { 898, 4305, 15, 1 }, /* in */ + { 4210, 0, 0, 1 }, /* industries */ + { 4221, 0, 0, 1 }, /* infiniti */ + { 3152, 4320, 16, 1 }, /* info */ + { 971, 0, 0, 1 }, /* ing */ + { 4237, 0, 0, 1 }, /* ink */ + { 4241, 0, 0, 1 }, /* institute */ + { 4255, 0, 0, 1 }, /* insurance */ + { 4265, 0, 0, 1 }, /* insure */ + { 3617, 4045, 1, 1 }, /* int */ + { 4277, 0, 0, 1 }, /* intel */ + { 4283, 0, 0, 1 }, /* international */ + { 4297, 0, 0, 1 }, /* intuit */ + { 4304, 0, 0, 1 }, /* investments */ + { 611, 2239, 25, 1 }, /* io */ + { 4316, 0, 0, 1 }, /* ipiranga */ + { 4325, 3715, 6, 1 }, /* iq */ + { 3121, 4338, 9, 1 }, /* ir */ + { 4332, 0, 0, 1 }, /* irish */ + { 3707, 4347, 8, 1 }, /* is */ + { 4343, 0, 0, 1 }, /* iselect */ + { 4351, 0, 0, 1 }, /* ismaili */ + { 2359, 0, 0, 1 }, /* ist */ + { 4364, 0, 0, 1 }, /* istanbul */ + { 2099, 4355, 369, 1 }, /* it */ { 582, 0, 0, 1 }, /* itau */ - { 4394, 0, 0, 1 }, /* itv */ - { 2612, 0, 0, 1 }, /* iveco */ - { 4398, 0, 0, 1 }, /* iwc */ - { 4402, 0, 0, 1 }, /* jaguar */ - { 4409, 0, 0, 1 }, /* java */ - { 4414, 0, 0, 1 }, /* jcb */ - { 4418, 0, 0, 1 }, /* jcp */ - { 4425, 4004, 3, 1 }, /* je */ - { 4428, 0, 0, 1 }, /* jeep */ - { 4433, 0, 0, 1 }, /* jetzt */ - { 4439, 0, 0, 1 }, /* jewelry */ - { 4447, 0, 0, 1 }, /* jio */ - { 4451, 0, 0, 1 }, /* jlc */ - { 4455, 0, 0, 1 }, /* jll */ - { 4459, 3687, 1, 0 }, /* jm */ - { 4462, 0, 0, 1 }, /* jmp */ - { 4466, 0, 0, 1 }, /* jnj */ - { 4472, 4560, 8, 1 }, /* jo */ - { 4475, 0, 0, 1 }, /* jobs */ - { 4480, 0, 0, 1 }, /* joburg */ - { 4487, 0, 0, 1 }, /* jot */ - { 4491, 0, 0, 1 }, /* joy */ - { 4495, 2199, 111, 1 }, /* jp */ - { 4498, 0, 0, 1 }, /* jpmorgan */ - { 4507, 0, 0, 1 }, /* jprs */ - { 4512, 0, 0, 1 }, /* juegos */ - { 4519, 0, 0, 1 }, /* juniper */ - { 4527, 0, 0, 1 }, /* kaufen */ - { 4534, 0, 0, 1 }, /* kddi */ - { 962, 2310, 2, 0 }, /* ke */ - { 4082, 0, 0, 1 }, /* kerryhotels */ - { 4544, 0, 0, 1 }, /* kerrylogistics */ - { 4559, 0, 0, 1 }, /* kerryproperties */ - { 4575, 0, 0, 1 }, /* kfh */ - { 4579, 3549, 6, 1 }, /* kg */ - { 4582, 3687, 1, 0 }, /* kh */ - { 3880, 6243, 7, 1 }, /* ki */ - { 4591, 0, 0, 1 }, /* kia */ - { 4597, 0, 0, 1 }, /* kim */ - { 4601, 0, 0, 1 }, /* kinder */ - { 4608, 0, 0, 1 }, /* kindle */ - { 4615, 0, 0, 1 }, /* kitchen */ - { 4623, 0, 0, 1 }, /* kiwi */ - { 4628, 6250, 17, 1 }, /* km */ - { 4633, 6267, 4, 1 }, /* kn */ - { 4636, 0, 0, 1 }, /* koeln */ - { 4642, 0, 0, 1 }, /* komatsu */ - { 4650, 0, 0, 1 }, /* kosher */ - { 4665, 6271, 6, 1 }, /* kp */ - { 4668, 0, 0, 1 }, /* kpmg */ - { 4673, 0, 0, 1 }, /* kpn */ - { 3123, 6277, 30, 1 }, /* kr */ - { 4682, 6307, 2, 1 }, /* krd */ - { 4686, 0, 0, 1 }, /* kred */ - { 4691, 0, 0, 1 }, /* kuokgroup */ - { 4701, 3687, 1, 0 }, /* kw */ - { 4705, 3651, 5, 1 }, /* ky */ - { 4708, 0, 0, 1 }, /* kyoto */ - { 4714, 3549, 6, 1 }, /* kz */ - { 2173, 6309, 10, 1 }, /* la */ - { 4721, 0, 0, 1 }, /* lacaixa */ - { 4729, 0, 0, 1 }, /* ladbrokes */ - { 4739, 0, 0, 1 }, /* lamborghini */ - { 4751, 0, 0, 1 }, /* lamer */ - { 4757, 0, 0, 1 }, /* lancaster */ - { 4767, 0, 0, 1 }, /* lancia */ - { 4774, 0, 0, 1 }, /* lancome */ - { 4784, 2312, 1, 1 }, /* land */ - { 4789, 0, 0, 1 }, /* landrover */ - { 4799, 0, 0, 1 }, /* lanxess */ - { 4807, 0, 0, 1 }, /* lasalle */ - { 2794, 0, 0, 1 }, /* lat */ - { 4821, 0, 0, 1 }, /* latino */ - { 4828, 0, 0, 1 }, /* latrobe */ - { 4840, 0, 0, 1 }, /* law */ - { 4849, 0, 0, 1 }, /* lawyer */ - { 4857, 3651, 5, 1 }, /* lb */ - { 4452, 6321, 7, 1 }, /* lc */ - { 4870, 0, 0, 1 }, /* lds */ - { 4874, 0, 0, 1 }, /* lease */ - { 4880, 0, 0, 1 }, /* leclerc */ - { 4888, 0, 0, 1 }, /* lefrak */ - { 3358, 0, 0, 1 }, /* legal */ - { 4895, 0, 0, 1 }, /* lego */ - { 4900, 0, 0, 1 }, /* lexus */ - { 4906, 0, 0, 1 }, /* lgbt */ - { 4377, 3672, 1, 1 }, /* li */ - { 4916, 0, 0, 1 }, /* liaison */ - { 4924, 0, 0, 1 }, /* lidl */ - { 4932, 0, 0, 1 }, /* life */ - { 4276, 0, 0, 1 }, /* lifeinsurance */ - { 4937, 0, 0, 1 }, /* lifestyle */ - { 4947, 0, 0, 1 }, /* lighting */ - { 4956, 0, 0, 1 }, /* like */ - { 4961, 0, 0, 1 }, /* lilly */ - { 4967, 0, 0, 1 }, /* limited */ - { 4975, 0, 0, 1 }, /* limo */ - { 4980, 0, 0, 1 }, /* lincoln */ - { 4988, 0, 0, 1 }, /* linde */ - { 4998, 6328, 2, 1 }, /* link */ - { 5003, 0, 0, 1 }, /* lipsy */ - { 5009, 0, 0, 1 }, /* live */ - { 5014, 0, 0, 1 }, /* living */ - { 5021, 0, 0, 1 }, /* lixil */ - { 2267, 6330, 15, 1 }, /* lk */ - { 5031, 0, 0, 1 }, /* loan */ - { 5036, 0, 0, 1 }, /* loans */ - { 5042, 0, 0, 1 }, /* locker */ - { 5049, 0, 0, 1 }, /* locus */ - { 5055, 0, 0, 1 }, /* loft */ - { 5060, 0, 0, 1 }, /* lol */ - { 5064, 0, 0, 1 }, /* london */ - { 5071, 0, 0, 1 }, /* lotte */ - { 5077, 0, 0, 1 }, /* lotto */ - { 5083, 0, 0, 1 }, /* love */ - { 5088, 0, 0, 1 }, /* lpl */ - { 3050, 0, 0, 1 }, /* lplfinancial */ - { 5092, 3651, 5, 1 }, /* lr */ - { 1236, 3818, 2, 1 }, /* ls */ - { 151, 4139, 2, 1 }, /* lt */ - { 5103, 0, 0, 1 }, /* ltd */ - { 5107, 0, 0, 1 }, /* ltda */ - { 5112, 3672, 1, 1 }, /* lu */ - { 5115, 0, 0, 1 }, /* lundbeck */ - { 5124, 0, 0, 1 }, /* lupin */ - { 5130, 0, 0, 1 }, /* luxe */ - { 5135, 0, 0, 1 }, /* luxury */ - { 5147, 6345, 9, 1 }, /* lv */ - { 339, 6354, 9, 1 }, /* ly */ - { 5151, 6363, 6, 1 }, /* ma */ - { 5154, 0, 0, 1 }, /* macys */ - { 5160, 0, 0, 1 }, /* madrid */ - { 5167, 0, 0, 1 }, /* maif */ - { 5181, 0, 0, 1 }, /* maison */ - { 5188, 0, 0, 1 }, /* makeup */ - { 5198, 0, 0, 1 }, /* man */ - { 5202, 6369, 1, 1 }, /* management */ - { 5213, 0, 0, 1 }, /* mango */ - { 5219, 0, 0, 1 }, /* map */ - { 5229, 0, 0, 1 }, /* market */ - { 5236, 0, 0, 1 }, /* marketing */ - { 5246, 0, 0, 1 }, /* markets */ - { 5254, 0, 0, 1 }, /* marriott */ - { 5263, 0, 0, 1 }, /* marshalls */ - { 5273, 0, 0, 1 }, /* maserati */ - { 5282, 0, 0, 1 }, /* mattel */ - { 5293, 0, 0, 1 }, /* mba */ - { 5307, 6370, 2, 1 }, /* mc */ - { 1582, 0, 0, 1 }, /* mcd */ - { 4864, 0, 0, 1 }, /* mcdonalds */ - { 5310, 0, 0, 1 }, /* mckinsey */ - { 5320, 3672, 1, 1 }, /* md */ - { 1693, 6372, 22, 1 }, /* me */ - { 1858, 0, 0, 1 }, /* med */ - { 5327, 0, 0, 1 }, /* media */ - { 5333, 0, 0, 1 }, /* meet */ - { 5338, 0, 0, 1 }, /* melbourne */ - { 5348, 0, 0, 1 }, /* meme */ - { 5353, 0, 0, 1 }, /* memorial */ - { 5366, 0, 0, 1 }, /* men */ - { 5370, 0, 0, 1 }, /* menu */ + { 4373, 0, 0, 1 }, /* itv */ + { 2611, 0, 0, 1 }, /* iveco */ + { 4377, 0, 0, 1 }, /* iwc */ + { 4381, 0, 0, 1 }, /* jaguar */ + { 4388, 0, 0, 1 }, /* java */ + { 4393, 0, 0, 1 }, /* jcb */ + { 4397, 0, 0, 1 }, /* jcp */ + { 4404, 4724, 3, 1 }, /* je */ + { 4407, 0, 0, 1 }, /* jeep */ + { 4412, 0, 0, 1 }, /* jetzt */ + { 4418, 0, 0, 1 }, /* jewelry */ + { 4426, 0, 0, 1 }, /* jio */ + { 4430, 0, 0, 1 }, /* jlc */ + { 4434, 0, 0, 1 }, /* jll */ + { 4438, 3853, 1, 0 }, /* jm */ + { 4441, 0, 0, 1 }, /* jmp */ + { 4445, 0, 0, 1 }, /* jnj */ + { 4451, 4727, 8, 1 }, /* jo */ + { 4454, 0, 0, 1 }, /* jobs */ + { 4459, 0, 0, 1 }, /* joburg */ + { 4466, 0, 0, 1 }, /* jot */ + { 4470, 0, 0, 1 }, /* joy */ + { 4474, 2268, 111, 1 }, /* jp */ + { 4477, 0, 0, 1 }, /* jpmorgan */ + { 4486, 0, 0, 1 }, /* jprs */ + { 4491, 0, 0, 1 }, /* juegos */ + { 4498, 0, 0, 1 }, /* juniper */ + { 4506, 0, 0, 1 }, /* kaufen */ + { 4513, 0, 0, 1 }, /* kddi */ + { 963, 2379, 2, 0 }, /* ke */ + { 4063, 0, 0, 1 }, /* kerryhotels */ + { 4523, 0, 0, 1 }, /* kerrylogistics */ + { 4538, 0, 0, 1 }, /* kerryproperties */ + { 4554, 0, 0, 1 }, /* kfh */ + { 4558, 3715, 6, 1 }, /* kg */ + { 4561, 3853, 1, 0 }, /* kh */ + { 3862, 6410, 7, 1 }, /* ki */ + { 4570, 0, 0, 1 }, /* kia */ + { 4576, 0, 0, 1 }, /* kim */ + { 4580, 0, 0, 1 }, /* kinder */ + { 4587, 0, 0, 1 }, /* kindle */ + { 4594, 0, 0, 1 }, /* kitchen */ + { 4602, 0, 0, 1 }, /* kiwi */ + { 4607, 6417, 17, 1 }, /* km */ + { 4612, 6434, 4, 1 }, /* kn */ + { 4615, 0, 0, 1 }, /* koeln */ + { 4621, 0, 0, 1 }, /* komatsu */ + { 4629, 0, 0, 1 }, /* kosher */ + { 4644, 6438, 6, 1 }, /* kp */ + { 4647, 0, 0, 1 }, /* kpmg */ + { 4652, 0, 0, 1 }, /* kpn */ + { 3108, 6444, 30, 1 }, /* kr */ + { 4661, 6474, 2, 1 }, /* krd */ + { 4665, 0, 0, 1 }, /* kred */ + { 4670, 0, 0, 1 }, /* kuokgroup */ + { 4680, 3853, 1, 0 }, /* kw */ + { 4684, 3817, 5, 1 }, /* ky */ + { 4687, 0, 0, 1 }, /* kyoto */ + { 4693, 3715, 6, 1 }, /* kz */ + { 2174, 6476, 10, 1 }, /* la */ + { 4700, 0, 0, 1 }, /* lacaixa */ + { 4708, 0, 0, 1 }, /* ladbrokes */ + { 4718, 0, 0, 1 }, /* lamborghini */ + { 4730, 0, 0, 1 }, /* lamer */ + { 4736, 0, 0, 1 }, /* lancaster */ + { 4746, 0, 0, 1 }, /* lancia */ + { 4753, 0, 0, 1 }, /* lancome */ + { 4763, 2381, 1, 1 }, /* land */ + { 4768, 0, 0, 1 }, /* landrover */ + { 4778, 0, 0, 1 }, /* lanxess */ + { 4786, 0, 0, 1 }, /* lasalle */ + { 2793, 0, 0, 1 }, /* lat */ + { 4800, 0, 0, 1 }, /* latino */ + { 4807, 0, 0, 1 }, /* latrobe */ + { 4819, 0, 0, 1 }, /* law */ + { 4828, 0, 0, 1 }, /* lawyer */ + { 4836, 3817, 5, 1 }, /* lb */ + { 4431, 6488, 7, 1 }, /* lc */ + { 4849, 0, 0, 1 }, /* lds */ + { 4853, 0, 0, 1 }, /* lease */ + { 4859, 0, 0, 1 }, /* leclerc */ + { 4867, 0, 0, 1 }, /* lefrak */ + { 3343, 0, 0, 1 }, /* legal */ + { 4874, 0, 0, 1 }, /* lego */ + { 4879, 0, 0, 1 }, /* lexus */ + { 4885, 0, 0, 1 }, /* lgbt */ + { 4356, 3838, 1, 1 }, /* li */ + { 4895, 0, 0, 1 }, /* liaison */ + { 4903, 0, 0, 1 }, /* lidl */ + { 4911, 0, 0, 1 }, /* life */ + { 4251, 0, 0, 1 }, /* lifeinsurance */ + { 4916, 0, 0, 1 }, /* lifestyle */ + { 4926, 0, 0, 1 }, /* lighting */ + { 4935, 0, 0, 1 }, /* like */ + { 4940, 0, 0, 1 }, /* lilly */ + { 4946, 0, 0, 1 }, /* limited */ + { 4954, 0, 0, 1 }, /* limo */ + { 4959, 0, 0, 1 }, /* lincoln */ + { 4967, 0, 0, 1 }, /* linde */ + { 4977, 6495, 2, 1 }, /* link */ + { 4982, 0, 0, 1 }, /* lipsy */ + { 4988, 0, 0, 1 }, /* live */ + { 4993, 0, 0, 1 }, /* living */ + { 5000, 0, 0, 1 }, /* lixil */ + { 2268, 6497, 15, 1 }, /* lk */ + { 5010, 0, 0, 1 }, /* loan */ + { 5015, 0, 0, 1 }, /* loans */ + { 5021, 0, 0, 1 }, /* locker */ + { 5028, 0, 0, 1 }, /* locus */ + { 5034, 0, 0, 1 }, /* loft */ + { 5039, 0, 0, 1 }, /* lol */ + { 5043, 0, 0, 1 }, /* london */ + { 5050, 0, 0, 1 }, /* lotte */ + { 5056, 0, 0, 1 }, /* lotto */ + { 5062, 0, 0, 1 }, /* love */ + { 5067, 0, 0, 1 }, /* lpl */ + { 3035, 0, 0, 1 }, /* lplfinancial */ + { 5071, 3817, 5, 1 }, /* lr */ + { 1237, 3985, 2, 1 }, /* ls */ + { 151, 4301, 2, 1 }, /* lt */ + { 5082, 0, 0, 1 }, /* ltd */ + { 5086, 0, 0, 1 }, /* ltda */ + { 5091, 3838, 1, 1 }, /* lu */ + { 5094, 0, 0, 1 }, /* lundbeck */ + { 5103, 0, 0, 1 }, /* lupin */ + { 5109, 0, 0, 1 }, /* luxe */ + { 5114, 0, 0, 1 }, /* luxury */ + { 5126, 6512, 9, 1 }, /* lv */ + { 339, 6521, 9, 1 }, /* ly */ + { 5130, 6530, 6, 1 }, /* ma */ + { 5133, 0, 0, 1 }, /* macys */ + { 5139, 0, 0, 1 }, /* madrid */ + { 5146, 0, 0, 1 }, /* maif */ + { 5160, 0, 0, 1 }, /* maison */ + { 5167, 0, 0, 1 }, /* makeup */ + { 5177, 0, 0, 1 }, /* man */ + { 5181, 6536, 1, 1 }, /* management */ + { 5192, 0, 0, 1 }, /* mango */ + { 5198, 0, 0, 1 }, /* map */ + { 5208, 0, 0, 1 }, /* market */ + { 5215, 0, 0, 1 }, /* marketing */ + { 5225, 0, 0, 1 }, /* markets */ + { 5233, 0, 0, 1 }, /* marriott */ + { 5242, 0, 0, 1 }, /* marshalls */ + { 5252, 0, 0, 1 }, /* maserati */ + { 5261, 0, 0, 1 }, /* mattel */ + { 5272, 0, 0, 1 }, /* mba */ + { 5286, 6537, 2, 1 }, /* mc */ + { 1583, 0, 0, 1 }, /* mcd */ + { 4843, 0, 0, 1 }, /* mcdonalds */ + { 5289, 0, 0, 1 }, /* mckinsey */ + { 5299, 3838, 1, 1 }, /* md */ + { 1694, 2382, 25, 1 }, /* me */ + { 1859, 0, 0, 1 }, /* med */ + { 5306, 0, 0, 1 }, /* media */ + { 5312, 0, 0, 1 }, /* meet */ + { 5317, 0, 0, 1 }, /* melbourne */ + { 5327, 0, 0, 1 }, /* meme */ + { 5332, 0, 0, 1 }, /* memorial */ + { 5345, 0, 0, 1 }, /* men */ + { 5349, 0, 0, 1 }, /* menu */ { 299, 0, 0, 1 }, /* meo */ - { 5375, 0, 0, 1 }, /* merckmsd */ - { 4929, 0, 0, 1 }, /* metlife */ - { 4670, 6394, 9, 1 }, /* mg */ - { 5391, 0, 0, 1 }, /* mh */ - { 5394, 0, 0, 1 }, /* miami */ - { 5400, 0, 0, 1 }, /* microsoft */ - { 4195, 0, 0, 1 }, /* mil */ - { 5412, 0, 0, 1 }, /* mini */ - { 4297, 0, 0, 1 }, /* mint */ - { 5418, 0, 0, 1 }, /* mit */ - { 5422, 0, 0, 1 }, /* mitsubishi */ - { 5433, 6403, 8, 1 }, /* mk */ - { 5436, 6411, 7, 1 }, /* ml */ - { 4856, 0, 0, 1 }, /* mlb */ - { 5095, 0, 0, 1 }, /* mls */ - { 5441, 3687, 1, 0 }, /* mm */ - { 5150, 0, 0, 1 }, /* mma */ - { 5445, 6418, 4, 1 }, /* mn */ - { 3600, 3651, 5, 1 }, /* mo */ - { 5448, 6422, 1, 1 }, /* mobi */ - { 5459, 0, 0, 1 }, /* mobile */ - { 5466, 0, 0, 1 }, /* mobily */ - { 5476, 0, 0, 1 }, /* moda */ - { 5481, 0, 0, 1 }, /* moe */ - { 5485, 0, 0, 1 }, /* moi */ - { 5489, 0, 0, 1 }, /* mom */ - { 5493, 0, 0, 1 }, /* monash */ - { 5500, 0, 0, 1 }, /* money */ - { 5506, 0, 0, 1 }, /* monster */ - { 5514, 0, 0, 1 }, /* montblanc */ - { 5524, 0, 0, 1 }, /* mopar */ - { 5530, 0, 0, 1 }, /* mormon */ - { 5537, 0, 0, 1 }, /* mortgage */ - { 5546, 0, 0, 1 }, /* moscow */ - { 5557, 0, 0, 1 }, /* moto */ - { 5562, 0, 0, 1 }, /* motorcycles */ - { 5574, 0, 0, 1 }, /* mov */ - { 5578, 0, 0, 1 }, /* movie */ - { 5584, 0, 0, 1 }, /* movistar */ - { 1374, 0, 0, 1 }, /* mp */ - { 5597, 0, 0, 1 }, /* mq */ - { 5601, 4139, 2, 1 }, /* mr */ - { 1059, 3651, 5, 1 }, /* ms */ - { 5380, 0, 0, 1 }, /* msd */ - { 5609, 2313, 4, 1 }, /* mt */ - { 5612, 0, 0, 1 }, /* mtn */ - { 5616, 0, 0, 1 }, /* mtpc */ - { 5621, 0, 0, 1 }, /* mtr */ - { 5627, 6423, 7, 1 }, /* mu */ - { 5644, 6430, 548, 1 }, /* museum */ - { 5663, 0, 0, 1 }, /* mutual */ - { 5670, 0, 0, 1 }, /* mutuelle */ - { 5679, 6978, 14, 1 }, /* mv */ - { 1063, 6992, 11, 1 }, /* mw */ - { 3604, 7003, 6, 1 }, /* mx */ - { 70, 7009, 8, 1 }, /* my */ - { 5687, 7017, 8, 1 }, /* mz */ - { 172, 7025, 17, 1 }, /* na */ - { 5704, 0, 0, 1 }, /* nab */ - { 5708, 0, 0, 1 }, /* nadex */ - { 5714, 0, 0, 1 }, /* nagoya */ - { 5725, 2317, 2, 1 }, /* name */ - { 5730, 0, 0, 1 }, /* nationwide */ - { 5741, 0, 0, 1 }, /* natura */ - { 5751, 0, 0, 1 }, /* navy */ + { 5354, 0, 0, 1 }, /* merckmsd */ + { 4908, 0, 0, 1 }, /* metlife */ + { 4649, 6540, 9, 1 }, /* mg */ + { 5370, 0, 0, 1 }, /* mh */ + { 5373, 0, 0, 1 }, /* miami */ + { 5379, 0, 0, 1 }, /* microsoft */ + { 4170, 0, 0, 1 }, /* mil */ + { 5391, 0, 0, 1 }, /* mini */ + { 4272, 0, 0, 1 }, /* mint */ + { 5397, 0, 0, 1 }, /* mit */ + { 5401, 0, 0, 1 }, /* mitsubishi */ + { 5412, 6549, 8, 1 }, /* mk */ + { 5415, 6557, 7, 1 }, /* ml */ + { 4835, 0, 0, 1 }, /* mlb */ + { 5074, 0, 0, 1 }, /* mls */ + { 5420, 3853, 1, 0 }, /* mm */ + { 5129, 0, 0, 1 }, /* mma */ + { 5424, 6564, 4, 1 }, /* mn */ + { 3585, 3817, 5, 1 }, /* mo */ + { 5427, 6568, 1, 1 }, /* mobi */ + { 5438, 0, 0, 1 }, /* mobile */ + { 5445, 0, 0, 1 }, /* mobily */ + { 5455, 0, 0, 1 }, /* moda */ + { 5460, 0, 0, 1 }, /* moe */ + { 5464, 0, 0, 1 }, /* moi */ + { 5468, 0, 0, 1 }, /* mom */ + { 5472, 0, 0, 1 }, /* monash */ + { 5479, 0, 0, 1 }, /* money */ + { 5485, 0, 0, 1 }, /* monster */ + { 5493, 0, 0, 1 }, /* montblanc */ + { 5503, 0, 0, 1 }, /* mopar */ + { 5509, 0, 0, 1 }, /* mormon */ + { 5516, 0, 0, 1 }, /* mortgage */ + { 5525, 0, 0, 1 }, /* moscow */ + { 5536, 0, 0, 1 }, /* moto */ + { 5541, 0, 0, 1 }, /* motorcycles */ + { 5553, 0, 0, 1 }, /* mov */ + { 5557, 0, 0, 1 }, /* movie */ + { 5563, 0, 0, 1 }, /* movistar */ + { 1375, 0, 0, 1 }, /* mp */ + { 5576, 0, 0, 1 }, /* mq */ + { 5580, 4301, 2, 1 }, /* mr */ + { 1060, 3817, 5, 1 }, /* ms */ + { 5359, 0, 0, 1 }, /* msd */ + { 5588, 2407, 4, 1 }, /* mt */ + { 5591, 0, 0, 1 }, /* mtn */ + { 5595, 0, 0, 1 }, /* mtpc */ + { 5600, 0, 0, 1 }, /* mtr */ + { 5606, 6569, 7, 1 }, /* mu */ + { 5623, 6576, 548, 1 }, /* museum */ + { 5642, 0, 0, 1 }, /* mutual */ + { 5649, 7124, 14, 1 }, /* mv */ + { 1064, 7138, 11, 1 }, /* mw */ + { 3589, 7149, 6, 1 }, /* mx */ + { 70, 7155, 8, 1 }, /* my */ + { 5657, 7163, 8, 1 }, /* mz */ + { 172, 7171, 17, 1 }, /* na */ + { 5674, 0, 0, 1 }, /* nab */ + { 5678, 0, 0, 1 }, /* nadex */ + { 5684, 0, 0, 1 }, /* nagoya */ + { 5695, 2411, 2, 1 }, /* name */ + { 5700, 0, 0, 1 }, /* nationwide */ + { 5711, 0, 0, 1 }, /* natura */ + { 5721, 0, 0, 1 }, /* navy */ { 688, 0, 0, 1 }, /* nba */ - { 5521, 7043, 1, 1 }, /* nc */ - { 1203, 0, 0, 1 }, /* ne */ - { 5765, 0, 0, 1 }, /* nec */ - { 4185, 2319, 78, 1 }, /* net */ - { 5769, 0, 0, 1 }, /* netbank */ - { 5777, 0, 0, 1 }, /* netflix */ - { 3185, 2399, 1, 1 }, /* network */ - { 5785, 0, 0, 1 }, /* neustar */ - { 5793, 0, 0, 1 }, /* new */ - { 5797, 0, 0, 1 }, /* newholland */ - { 5808, 0, 0, 1 }, /* news */ - { 5813, 0, 0, 1 }, /* next */ - { 2410, 0, 0, 1 }, /* nextdirect */ - { 5818, 0, 0, 1 }, /* nexus */ - { 5825, 7050, 10, 1 }, /* nf */ - { 5828, 0, 0, 1 }, /* nfl */ - { 971, 2400, 10, 1 }, /* ng */ - { 976, 0, 0, 1 }, /* ngo */ - { 3939, 0, 0, 1 }, /* nhk */ - { 1722, 7060, 14, 1 }, /* ni */ - { 5846, 0, 0, 1 }, /* nico */ - { 5851, 0, 0, 1 }, /* nike */ - { 5856, 0, 0, 1 }, /* nikon */ - { 5862, 0, 0, 1 }, /* ninja */ - { 5868, 0, 0, 1 }, /* nissan */ - { 5875, 0, 0, 1 }, /* nissay */ - { 1071, 2410, 5, 1 }, /* nl */ - { 1517, 2415, 726, 1 }, /* no */ - { 4589, 0, 0, 1 }, /* nokia */ - { 5651, 0, 0, 1 }, /* northwesternmutual */ - { 5887, 0, 0, 1 }, /* norton */ - { 5894, 0, 0, 1 }, /* now */ - { 5898, 0, 0, 1 }, /* nowruz */ - { 5905, 0, 0, 1 }, /* nowtv */ - { 2478, 3687, 1, 0 }, /* np */ - { 5912, 6243, 7, 1 }, /* nr */ - { 5917, 0, 0, 1 }, /* nra */ - { 5921, 0, 0, 1 }, /* nrw */ - { 5925, 0, 0, 1 }, /* ntt */ - { 5372, 7093, 3, 1 }, /* nu */ - { 5933, 0, 0, 1 }, /* nyc */ - { 325, 3141, 16, 1 }, /* nz */ - { 5449, 0, 0, 1 }, /* obi */ - { 5942, 0, 0, 1 }, /* observer */ - { 5951, 0, 0, 1 }, /* off */ - { 5959, 0, 0, 1 }, /* office */ - { 5966, 0, 0, 1 }, /* okinawa */ - { 5974, 0, 0, 1 }, /* olayan */ - { 5981, 0, 0, 1 }, /* olayangroup */ - { 5748, 0, 0, 1 }, /* oldnavy */ - { 5993, 0, 0, 1 }, /* ollo */ - { 353, 7096, 9, 1 }, /* om */ - { 6002, 0, 0, 1 }, /* omega */ - { 1202, 7105, 1, 1 }, /* one */ - { 6015, 0, 0, 1 }, /* ong */ - { 6019, 0, 0, 1 }, /* onl */ - { 6023, 0, 0, 1 }, /* online */ - { 6030, 0, 0, 1 }, /* onyourside */ - { 6041, 0, 0, 1 }, /* ooo */ - { 6045, 0, 0, 1 }, /* open */ - { 6050, 0, 0, 1 }, /* oracle */ - { 6057, 0, 0, 1 }, /* orange */ - { 6070, 3157, 90, 1 }, /* org */ - { 6081, 0, 0, 1 }, /* organic */ - { 2870, 0, 0, 1 }, /* orientexpress */ - { 6089, 0, 0, 1 }, /* origins */ - { 6098, 0, 0, 1 }, /* osaka */ - { 6107, 0, 0, 1 }, /* otsuka */ + { 5500, 7189, 2, 1 }, /* nc */ + { 1204, 0, 0, 1 }, /* ne */ + { 2607, 0, 0, 1 }, /* nec */ + { 5737, 2413, 89, 1 }, /* net */ + { 5741, 0, 0, 1 }, /* netbank */ + { 5749, 0, 0, 1 }, /* netflix */ + { 3170, 2506, 1, 1 }, /* network */ + { 5757, 0, 0, 1 }, /* neustar */ + { 5765, 0, 0, 1 }, /* new */ + { 5769, 0, 0, 1 }, /* newholland */ + { 5780, 0, 0, 1 }, /* news */ + { 5785, 0, 0, 1 }, /* next */ + { 2413, 0, 0, 1 }, /* nextdirect */ + { 5790, 0, 0, 1 }, /* nexus */ + { 5797, 7198, 10, 1 }, /* nf */ + { 5800, 0, 0, 1 }, /* nfl */ + { 972, 2507, 10, 1 }, /* ng */ + { 977, 0, 0, 1 }, /* ngo */ + { 3921, 0, 0, 1 }, /* nhk */ + { 1723, 7208, 14, 1 }, /* ni */ + { 5818, 0, 0, 1 }, /* nico */ + { 5823, 0, 0, 1 }, /* nike */ + { 5828, 0, 0, 1 }, /* nikon */ + { 5834, 0, 0, 1 }, /* ninja */ + { 5840, 0, 0, 1 }, /* nissan */ + { 5847, 0, 0, 1 }, /* nissay */ + { 1072, 2517, 7, 1 }, /* nl */ + { 1518, 2524, 726, 1 }, /* no */ + { 4568, 0, 0, 1 }, /* nokia */ + { 5630, 0, 0, 1 }, /* northwesternmutual */ + { 5859, 0, 0, 1 }, /* norton */ + { 5866, 0, 0, 1 }, /* now */ + { 5870, 0, 0, 1 }, /* nowruz */ + { 5877, 0, 0, 1 }, /* nowtv */ + { 2481, 3853, 1, 0 }, /* np */ + { 5884, 6410, 7, 1 }, /* nr */ + { 5889, 0, 0, 1 }, /* nra */ + { 5893, 0, 0, 1 }, /* nrw */ + { 5897, 0, 0, 1 }, /* ntt */ + { 5351, 7241, 3, 1 }, /* nu */ + { 5905, 0, 0, 1 }, /* nyc */ + { 325, 3250, 16, 1 }, /* nz */ + { 5428, 0, 0, 1 }, /* obi */ + { 5914, 0, 0, 1 }, /* observer */ + { 5923, 0, 0, 1 }, /* off */ + { 5931, 0, 0, 1 }, /* office */ + { 5938, 0, 0, 1 }, /* okinawa */ + { 5946, 0, 0, 1 }, /* olayan */ + { 5953, 0, 0, 1 }, /* olayangroup */ + { 5718, 0, 0, 1 }, /* oldnavy */ + { 5965, 0, 0, 1 }, /* ollo */ + { 353, 7244, 9, 1 }, /* om */ + { 5974, 0, 0, 1 }, /* omega */ + { 1203, 7253, 1, 1 }, /* one */ + { 5987, 0, 0, 1 }, /* ong */ + { 5991, 0, 0, 1 }, /* onion */ + { 5997, 0, 0, 1 }, /* onl */ + { 6006, 7254, 1, 1 }, /* online */ + { 6013, 0, 0, 1 }, /* onyourside */ + { 6024, 0, 0, 1 }, /* ooo */ + { 6028, 0, 0, 1 }, /* open */ + { 6033, 0, 0, 1 }, /* oracle */ + { 6040, 0, 0, 1 }, /* orange */ + { 6053, 3266, 94, 1 }, /* org */ + { 6064, 0, 0, 1 }, /* organic */ + { 6072, 0, 0, 1 }, /* origins */ + { 6081, 0, 0, 1 }, /* osaka */ + { 6090, 0, 0, 1 }, /* otsuka */ { 23, 0, 0, 1 }, /* ott */ - { 6114, 7167, 1, 1 }, /* ovh */ - { 522, 7168, 11, 1 }, /* pa */ - { 6118, 0, 0, 1 }, /* page */ - { 6123, 0, 0, 1 }, /* pamperedchef */ - { 6136, 0, 0, 1 }, /* panasonic */ - { 6146, 0, 0, 1 }, /* panerai */ - { 6154, 0, 0, 1 }, /* paris */ - { 6160, 0, 0, 1 }, /* pars */ - { 6165, 0, 0, 1 }, /* partners */ - { 6174, 0, 0, 1 }, /* parts */ - { 6180, 3961, 1, 1 }, /* party */ - { 6186, 0, 0, 1 }, /* passagens */ + { 6097, 7316, 1, 1 }, /* ovh */ + { 522, 7317, 11, 1 }, /* pa */ + { 6101, 0, 0, 1 }, /* page */ + { 6106, 0, 0, 1 }, /* pamperedchef */ + { 6119, 0, 0, 1 }, /* panasonic */ + { 6129, 0, 0, 1 }, /* panerai */ + { 6137, 0, 0, 1 }, /* paris */ + { 6143, 0, 0, 1 }, /* pars */ + { 6148, 0, 0, 1 }, /* partners */ + { 6157, 0, 0, 1 }, /* parts */ + { 6163, 4118, 1, 1 }, /* party */ + { 6169, 0, 0, 1 }, /* passagens */ { 314, 0, 0, 1 }, /* pay */ - { 2179, 0, 0, 1 }, /* pccw */ - { 3739, 7179, 8, 1 }, /* pe */ - { 6196, 0, 0, 1 }, /* pet */ - { 6200, 7187, 3, 1 }, /* pf */ - { 6203, 0, 0, 1 }, /* pfizer */ - { 6211, 3687, 1, 0 }, /* pg */ - { 6214, 7190, 8, 1 }, /* ph */ - { 6217, 0, 0, 1 }, /* pharmacy */ - { 6226, 0, 0, 1 }, /* phd */ - { 6230, 0, 0, 1 }, /* philips */ - { 6008, 0, 0, 1 }, /* phone */ - { 6238, 0, 0, 1 }, /* photo */ - { 6244, 0, 0, 1 }, /* photography */ - { 6258, 0, 0, 1 }, /* photos */ - { 6265, 0, 0, 1 }, /* physio */ - { 6272, 0, 0, 1 }, /* piaget */ - { 6284, 0, 0, 1 }, /* pics */ - { 6289, 0, 0, 1 }, /* pictet */ - { 6296, 0, 0, 1 }, /* pictures */ - { 6305, 0, 0, 1 }, /* pid */ - { 5126, 0, 0, 1 }, /* pin */ - { 6313, 0, 0, 1 }, /* ping */ - { 4261, 0, 0, 1 }, /* pink */ - { 6318, 0, 0, 1 }, /* pioneer */ - { 6326, 0, 0, 1 }, /* pizza */ - { 6332, 7198, 14, 1 }, /* pk */ - { 5089, 3248, 166, 1 }, /* pl */ - { 6340, 0, 0, 1 }, /* place */ - { 6346, 0, 0, 1 }, /* play */ - { 6351, 0, 0, 1 }, /* playstation */ - { 965, 0, 0, 1 }, /* plumbing */ - { 6365, 0, 0, 1 }, /* plus */ - { 6370, 0, 0, 1 }, /* pm */ - { 4674, 7259, 5, 1 }, /* pn */ - { 5756, 0, 0, 1 }, /* pnc */ - { 6373, 0, 0, 1 }, /* pohl */ - { 6378, 0, 0, 1 }, /* poker */ - { 6384, 0, 0, 1 }, /* politie */ - { 6392, 0, 0, 1 }, /* porn */ + { 2180, 0, 0, 1 }, /* pccw */ + { 3724, 7328, 8, 1 }, /* pe */ + { 6179, 0, 0, 1 }, /* pet */ + { 6183, 7336, 3, 1 }, /* pf */ + { 6186, 0, 0, 1 }, /* pfizer */ + { 6194, 3853, 1, 0 }, /* pg */ + { 6197, 7339, 8, 1 }, /* ph */ + { 6200, 0, 0, 1 }, /* pharmacy */ + { 6209, 0, 0, 1 }, /* phd */ + { 6213, 0, 0, 1 }, /* philips */ + { 5980, 0, 0, 1 }, /* phone */ + { 6221, 0, 0, 1 }, /* photo */ + { 6227, 0, 0, 1 }, /* photography */ + { 6241, 0, 0, 1 }, /* photos */ + { 6248, 0, 0, 1 }, /* physio */ + { 6255, 0, 0, 1 }, /* piaget */ + { 6267, 0, 0, 1 }, /* pics */ + { 6272, 0, 0, 1 }, /* pictet */ + { 6279, 7347, 1, 1 }, /* pictures */ + { 6288, 0, 0, 1 }, /* pid */ + { 5105, 0, 0, 1 }, /* pin */ + { 6296, 0, 0, 1 }, /* ping */ + { 4236, 0, 0, 1 }, /* pink */ + { 6301, 0, 0, 1 }, /* pioneer */ + { 6309, 0, 0, 1 }, /* pizza */ + { 6315, 7348, 14, 1 }, /* pk */ + { 5068, 3361, 166, 1 }, /* pl */ + { 6323, 0, 0, 1 }, /* place */ + { 6329, 0, 0, 1 }, /* play */ + { 6334, 0, 0, 1 }, /* playstation */ + { 966, 0, 0, 1 }, /* plumbing */ + { 6348, 0, 0, 1 }, /* plus */ + { 6353, 0, 0, 1 }, /* pm */ + { 4653, 7409, 5, 1 }, /* pn */ + { 5726, 0, 0, 1 }, /* pnc */ + { 6356, 0, 0, 1 }, /* pohl */ + { 6361, 0, 0, 1 }, /* poker */ + { 6367, 0, 0, 1 }, /* politie */ + { 6375, 0, 0, 1 }, /* porn */ { 617, 0, 0, 1 }, /* post */ - { 6402, 7264, 13, 1 }, /* pr */ - { 6405, 0, 0, 1 }, /* pramerica */ - { 6415, 0, 0, 1 }, /* praxi */ + { 6385, 7414, 13, 1 }, /* pr */ + { 6388, 0, 0, 1 }, /* pramerica */ + { 6398, 0, 0, 1 }, /* praxi */ { 371, 0, 0, 1 }, /* press */ - { 6421, 0, 0, 1 }, /* prime */ - { 6427, 7277, 12, 1 }, /* pro */ - { 6431, 0, 0, 1 }, /* prod */ - { 6436, 0, 0, 1 }, /* productions */ - { 6448, 0, 0, 1 }, /* prof */ - { 6453, 0, 0, 1 }, /* progressive */ - { 6465, 0, 0, 1 }, /* promo */ - { 4564, 0, 0, 1 }, /* properties */ - { 6471, 0, 0, 1 }, /* property */ - { 6480, 0, 0, 1 }, /* protection */ - { 6491, 0, 0, 1 }, /* pru */ - { 6495, 0, 0, 1 }, /* prudential */ - { 6235, 7289, 7, 1 }, /* ps */ - { 6510, 7296, 9, 1 }, /* pt */ - { 6513, 0, 0, 1 }, /* pub */ - { 6517, 7305, 7, 1 }, /* pw */ - { 6520, 0, 0, 1 }, /* pwc */ - { 6525, 7312, 7, 1 }, /* py */ - { 6539, 7319, 9, 1 }, /* qa */ - { 6542, 0, 0, 1 }, /* qpon */ - { 6547, 0, 0, 1 }, /* quebec */ - { 6554, 0, 0, 1 }, /* quest */ - { 6560, 0, 0, 1 }, /* qvc */ - { 6564, 0, 0, 1 }, /* racing */ - { 6571, 0, 0, 1 }, /* radio */ - { 6577, 0, 0, 1 }, /* raid */ - { 80, 7328, 4, 1 }, /* re */ - { 6594, 0, 0, 1 }, /* read */ - { 2765, 0, 0, 1 }, /* realestate */ - { 6599, 0, 0, 1 }, /* realtor */ - { 6607, 0, 0, 1 }, /* realty */ - { 6614, 0, 0, 1 }, /* recipes */ - { 4687, 0, 0, 1 }, /* red */ - { 6622, 0, 0, 1 }, /* redstone */ - { 6631, 0, 0, 1 }, /* redumbrella */ - { 6643, 0, 0, 1 }, /* rehab */ - { 6649, 0, 0, 1 }, /* reise */ - { 6655, 0, 0, 1 }, /* reisen */ - { 6662, 0, 0, 1 }, /* reit */ - { 6667, 0, 0, 1 }, /* reliance */ - { 6678, 0, 0, 1 }, /* ren */ - { 6691, 0, 0, 1 }, /* rent */ - { 6696, 0, 0, 1 }, /* rentals */ - { 6704, 0, 0, 1 }, /* repair */ - { 6711, 0, 0, 1 }, /* report */ - { 6723, 0, 0, 1 }, /* republican */ - { 6734, 0, 0, 1 }, /* rest */ - { 6739, 0, 0, 1 }, /* restaurant */ - { 6750, 3961, 1, 1 }, /* review */ - { 6757, 0, 0, 1 }, /* reviews */ - { 6765, 0, 0, 1 }, /* rexroth */ - { 6776, 0, 0, 1 }, /* rich */ - { 6781, 0, 0, 1 }, /* richardli */ - { 6791, 0, 0, 1 }, /* ricoh */ - { 6797, 0, 0, 1 }, /* rightathome */ - { 6809, 0, 0, 1 }, /* ril */ - { 6817, 0, 0, 1 }, /* rio */ - { 6829, 0, 0, 1 }, /* rip */ - { 5417, 0, 0, 1 }, /* rmit */ - { 166, 7332, 13, 1 }, /* ro */ - { 6833, 0, 0, 1 }, /* rocher */ - { 6840, 0, 0, 1 }, /* rocks */ - { 6846, 0, 0, 1 }, /* rodeo */ - { 6852, 0, 0, 1 }, /* rogers */ - { 6859, 0, 0, 1 }, /* room */ - { 1272, 7345, 7, 1 }, /* rs */ - { 6864, 0, 0, 1 }, /* rsvp */ - { 2190, 7352, 7, 1 }, /* ru */ - { 4116, 0, 0, 1 }, /* ruhr */ - { 6869, 0, 0, 1 }, /* run */ - { 5922, 7359, 9, 1 }, /* rw */ - { 6873, 0, 0, 1 }, /* rwe */ - { 6877, 0, 0, 1 }, /* ryukyu */ - { 1493, 7368, 8, 1 }, /* sa */ - { 6888, 0, 0, 1 }, /* saarland */ - { 6897, 0, 0, 1 }, /* safe */ - { 6902, 0, 0, 1 }, /* safety */ - { 6909, 0, 0, 1 }, /* sakura */ - { 3218, 0, 0, 1 }, /* sale */ - { 6916, 0, 0, 1 }, /* salon */ - { 6922, 0, 0, 1 }, /* samsclub */ - { 6931, 0, 0, 1 }, /* samsung */ - { 6939, 0, 0, 1 }, /* sandvik */ - { 6947, 0, 0, 1 }, /* sandvikcoromant */ - { 3005, 0, 0, 1 }, /* sanofi */ - { 6963, 0, 0, 1 }, /* sap */ - { 6967, 0, 0, 1 }, /* sapo */ - { 6972, 0, 0, 1 }, /* sarl */ - { 6977, 0, 0, 1 }, /* sas */ - { 6981, 0, 0, 1 }, /* save */ - { 6986, 0, 0, 1 }, /* saxo */ - { 6991, 3651, 5, 1 }, /* sb */ - { 946, 0, 0, 1 }, /* sbi */ - { 6994, 0, 0, 1 }, /* sbs */ - { 2158, 3651, 5, 1 }, /* sc */ - { 7002, 0, 0, 1 }, /* sca */ - { 7006, 0, 0, 1 }, /* scb */ - { 7010, 0, 0, 1 }, /* schaeffler */ - { 7021, 0, 0, 1 }, /* schmidt */ - { 7029, 0, 0, 1 }, /* scholarships */ - { 7042, 0, 0, 1 }, /* school */ - { 7049, 0, 0, 1 }, /* schule */ - { 7056, 0, 0, 1 }, /* schwarz */ - { 7073, 3961, 1, 1 }, /* science */ - { 7081, 0, 0, 1 }, /* scjohnson */ - { 7091, 0, 0, 1 }, /* scor */ - { 7096, 0, 0, 1 }, /* scot */ - { 5381, 7376, 8, 1 }, /* sd */ - { 1498, 7384, 41, 1 }, /* se */ - { 1385, 0, 0, 1 }, /* search */ - { 2603, 0, 0, 1 }, /* seat */ - { 7120, 0, 0, 1 }, /* secure */ - { 7127, 0, 0, 1 }, /* security */ - { 7136, 0, 0, 1 }, /* seek */ - { 4365, 0, 0, 1 }, /* select */ - { 7141, 0, 0, 1 }, /* sener */ - { 7147, 0, 0, 1 }, /* services */ - { 2087, 0, 0, 1 }, /* ses */ - { 7156, 0, 0, 1 }, /* seven */ - { 7162, 0, 0, 1 }, /* sew */ - { 7168, 0, 0, 1 }, /* sex */ - { 7172, 0, 0, 1 }, /* sexy */ - { 3244, 0, 0, 1 }, /* sfr */ - { 7192, 7425, 7, 1 }, /* sg */ - { 1510, 3414, 8, 1 }, /* sh */ - { 7195, 0, 0, 1 }, /* shangrila */ - { 7205, 0, 0, 1 }, /* sharp */ - { 7211, 0, 0, 1 }, /* shaw */ - { 7216, 0, 0, 1 }, /* shell */ - { 7222, 0, 0, 1 }, /* shia */ - { 7227, 0, 0, 1 }, /* shiksha */ - { 7235, 0, 0, 1 }, /* shoes */ - { 7245, 0, 0, 1 }, /* shop */ - { 6309, 0, 0, 1 }, /* shopping */ - { 7250, 0, 0, 1 }, /* shouji */ - { 4111, 0, 0, 1 }, /* show */ - { 7257, 0, 0, 1 }, /* showtime */ - { 7266, 0, 0, 1 }, /* shriram */ - { 2364, 3672, 1, 1 }, /* si */ - { 7278, 0, 0, 1 }, /* silk */ - { 7286, 0, 0, 1 }, /* sina */ - { 7291, 0, 0, 1 }, /* singles */ - { 7303, 7432, 1, 1 }, /* site */ - { 7308, 0, 0, 1 }, /* sj */ - { 7312, 3672, 1, 1 }, /* sk */ - { 4585, 0, 0, 1 }, /* ski */ - { 7315, 0, 0, 1 }, /* skin */ - { 4704, 0, 0, 1 }, /* sky */ - { 7320, 0, 0, 1 }, /* skype */ - { 7327, 3651, 5, 1 }, /* sl */ - { 4255, 0, 0, 1 }, /* sling */ - { 7331, 0, 0, 1 }, /* sm */ + { 6404, 0, 0, 1 }, /* prime */ + { 6410, 7427, 12, 1 }, /* pro */ + { 6414, 0, 0, 1 }, /* prod */ + { 6419, 0, 0, 1 }, /* productions */ + { 6431, 0, 0, 1 }, /* prof */ + { 6436, 0, 0, 1 }, /* progressive */ + { 6448, 0, 0, 1 }, /* promo */ + { 4543, 0, 0, 1 }, /* properties */ + { 6454, 0, 0, 1 }, /* property */ + { 6463, 0, 0, 1 }, /* protection */ + { 6474, 0, 0, 1 }, /* pru */ + { 6478, 0, 0, 1 }, /* prudential */ + { 6218, 7439, 7, 1 }, /* ps */ + { 6493, 7446, 9, 1 }, /* pt */ + { 6496, 0, 0, 1 }, /* pub */ + { 6500, 7455, 7, 1 }, /* pw */ + { 6503, 0, 0, 1 }, /* pwc */ + { 6508, 7462, 7, 1 }, /* py */ + { 6522, 7469, 9, 1 }, /* qa */ + { 6525, 0, 0, 1 }, /* qpon */ + { 6530, 0, 0, 1 }, /* quebec */ + { 6537, 0, 0, 1 }, /* quest */ + { 6543, 0, 0, 1 }, /* qvc */ + { 6547, 0, 0, 1 }, /* racing */ + { 6554, 0, 0, 1 }, /* radio */ + { 6560, 0, 0, 1 }, /* raid */ + { 80, 7478, 4, 1 }, /* re */ + { 6577, 0, 0, 1 }, /* read */ + { 2764, 0, 0, 1 }, /* realestate */ + { 6582, 0, 0, 1 }, /* realtor */ + { 6590, 0, 0, 1 }, /* realty */ + { 6597, 0, 0, 1 }, /* recipes */ + { 4666, 0, 0, 1 }, /* red */ + { 6605, 0, 0, 1 }, /* redstone */ + { 6614, 0, 0, 1 }, /* redumbrella */ + { 6626, 0, 0, 1 }, /* rehab */ + { 6632, 0, 0, 1 }, /* reise */ + { 6638, 0, 0, 1 }, /* reisen */ + { 6645, 0, 0, 1 }, /* reit */ + { 6650, 0, 0, 1 }, /* reliance */ + { 6661, 0, 0, 1 }, /* ren */ + { 6674, 0, 0, 1 }, /* rent */ + { 6679, 0, 0, 1 }, /* rentals */ + { 6687, 0, 0, 1 }, /* repair */ + { 6694, 0, 0, 1 }, /* report */ + { 6706, 0, 0, 1 }, /* republican */ + { 6717, 0, 0, 1 }, /* rest */ + { 6722, 0, 0, 1 }, /* restaurant */ + { 6733, 4118, 1, 1 }, /* review */ + { 6740, 0, 0, 1 }, /* reviews */ + { 6748, 0, 0, 1 }, /* rexroth */ + { 6759, 0, 0, 1 }, /* rich */ + { 6764, 0, 0, 1 }, /* richardli */ + { 6774, 0, 0, 1 }, /* ricoh */ + { 6780, 0, 0, 1 }, /* rightathome */ + { 6792, 0, 0, 1 }, /* ril */ + { 6800, 0, 0, 1 }, /* rio */ + { 6812, 7482, 1, 1 }, /* rip */ + { 5396, 0, 0, 1 }, /* rmit */ + { 166, 7483, 13, 1 }, /* ro */ + { 6816, 0, 0, 1 }, /* rocher */ + { 6823, 7496, 2, 1 }, /* rocks */ + { 6829, 0, 0, 1 }, /* rodeo */ + { 6835, 0, 0, 1 }, /* rogers */ + { 6842, 0, 0, 1 }, /* room */ + { 1273, 7498, 7, 1 }, /* rs */ + { 6847, 0, 0, 1 }, /* rsvp */ + { 2191, 3527, 27, 1 }, /* ru */ + { 6852, 0, 0, 1 }, /* rugby */ + { 4097, 0, 0, 1 }, /* ruhr */ + { 6858, 0, 0, 1 }, /* run */ + { 5894, 7506, 9, 1 }, /* rw */ + { 6862, 0, 0, 1 }, /* rwe */ + { 6866, 0, 0, 1 }, /* ryukyu */ + { 1494, 7515, 8, 1 }, /* sa */ + { 6877, 0, 0, 1 }, /* saarland */ + { 6886, 0, 0, 1 }, /* safe */ + { 6891, 0, 0, 1 }, /* safety */ + { 6898, 0, 0, 1 }, /* sakura */ + { 3203, 0, 0, 1 }, /* sale */ + { 6905, 0, 0, 1 }, /* salon */ + { 6911, 0, 0, 1 }, /* samsclub */ + { 6920, 0, 0, 1 }, /* samsung */ + { 6928, 0, 0, 1 }, /* sandvik */ + { 6936, 0, 0, 1 }, /* sandvikcoromant */ + { 2990, 0, 0, 1 }, /* sanofi */ + { 6952, 0, 0, 1 }, /* sap */ + { 6956, 0, 0, 1 }, /* sapo */ + { 6961, 0, 0, 1 }, /* sarl */ + { 6966, 0, 0, 1 }, /* sas */ + { 6970, 0, 0, 1 }, /* save */ + { 6975, 0, 0, 1 }, /* saxo */ + { 6980, 3817, 5, 1 }, /* sb */ + { 947, 0, 0, 1 }, /* sbi */ + { 6983, 0, 0, 1 }, /* sbs */ + { 2159, 3817, 5, 1 }, /* sc */ + { 6991, 0, 0, 1 }, /* sca */ + { 6995, 0, 0, 1 }, /* scb */ + { 6999, 0, 0, 1 }, /* schaeffler */ + { 7010, 0, 0, 1 }, /* schmidt */ + { 7018, 0, 0, 1 }, /* scholarships */ + { 7031, 0, 0, 1 }, /* school */ + { 7038, 0, 0, 1 }, /* schule */ + { 7045, 0, 0, 1 }, /* schwarz */ + { 7062, 4118, 1, 1 }, /* science */ + { 7070, 0, 0, 1 }, /* scjohnson */ + { 7080, 0, 0, 1 }, /* scor */ + { 7085, 0, 0, 1 }, /* scot */ + { 5360, 7523, 8, 1 }, /* sd */ + { 1499, 7531, 41, 1 }, /* se */ + { 1386, 0, 0, 1 }, /* search */ + { 2602, 0, 0, 1 }, /* seat */ + { 7109, 0, 0, 1 }, /* secure */ + { 7116, 0, 0, 1 }, /* security */ + { 7125, 0, 0, 1 }, /* seek */ + { 4344, 0, 0, 1 }, /* select */ + { 7130, 0, 0, 1 }, /* sener */ + { 7136, 0, 0, 1 }, /* services */ + { 2088, 0, 0, 1 }, /* ses */ + { 7145, 0, 0, 1 }, /* seven */ + { 7151, 0, 0, 1 }, /* sew */ + { 7157, 0, 0, 1 }, /* sex */ + { 7161, 0, 0, 1 }, /* sexy */ + { 3229, 0, 0, 1 }, /* sfr */ + { 7181, 7572, 7, 1 }, /* sg */ + { 1511, 3554, 8, 1 }, /* sh */ + { 7184, 0, 0, 1 }, /* shangrila */ + { 7194, 0, 0, 1 }, /* sharp */ + { 7200, 0, 0, 1 }, /* shaw */ + { 7205, 0, 0, 1 }, /* shell */ + { 7211, 0, 0, 1 }, /* shia */ + { 7216, 0, 0, 1 }, /* shiksha */ + { 7224, 0, 0, 1 }, /* shoes */ + { 7234, 0, 0, 1 }, /* shop */ + { 6292, 0, 0, 1 }, /* shopping */ + { 7239, 0, 0, 1 }, /* shouji */ + { 4092, 0, 0, 1 }, /* show */ + { 7246, 0, 0, 1 }, /* showtime */ + { 7255, 0, 0, 1 }, /* shriram */ + { 2365, 3838, 1, 1 }, /* si */ + { 7267, 0, 0, 1 }, /* silk */ + { 7275, 0, 0, 1 }, /* sina */ + { 7280, 0, 0, 1 }, /* singles */ + { 7292, 3562, 2, 1 }, /* site */ + { 7297, 0, 0, 1 }, /* sj */ + { 7301, 3838, 1, 1 }, /* sk */ + { 4564, 0, 0, 1 }, /* ski */ + { 7304, 0, 0, 1 }, /* skin */ + { 4683, 0, 0, 1 }, /* sky */ + { 7309, 0, 0, 1 }, /* skype */ + { 7316, 3817, 5, 1 }, /* sl */ + { 4230, 0, 0, 1 }, /* sling */ + { 7320, 0, 0, 1 }, /* sm */ { 525, 0, 0, 1 }, /* smart */ - { 7334, 0, 0, 1 }, /* smile */ - { 7341, 7433, 8, 1 }, /* sn */ - { 1609, 0, 0, 1 }, /* sncf */ - { 7345, 7441, 3, 1 }, /* so */ - { 7348, 0, 0, 1 }, /* soccer */ - { 7355, 0, 0, 1 }, /* social */ - { 7362, 0, 0, 1 }, /* softbank */ - { 7371, 0, 0, 1 }, /* software */ - { 4136, 0, 0, 1 }, /* sohu */ - { 7380, 0, 0, 1 }, /* solar */ - { 7386, 0, 0, 1 }, /* solutions */ - { 6014, 0, 0, 1 }, /* song */ - { 7396, 0, 0, 1 }, /* sony */ - { 7403, 0, 0, 1 }, /* soy */ - { 2889, 7444, 1, 1 }, /* space */ - { 7407, 0, 0, 1 }, /* spiegel */ - { 7418, 0, 0, 1 }, /* spot */ - { 7423, 0, 0, 1 }, /* spreadbetting */ - { 7437, 0, 0, 1 }, /* sr */ - { 7440, 0, 0, 1 }, /* srl */ - { 7444, 0, 0, 1 }, /* srt */ - { 619, 7445, 12, 1 }, /* st */ - { 7452, 0, 0, 1 }, /* stada */ - { 7458, 0, 0, 1 }, /* staples */ - { 5588, 0, 0, 1 }, /* star */ - { 7466, 0, 0, 1 }, /* starhub */ - { 7474, 0, 0, 1 }, /* statebank */ - { 2943, 0, 0, 1 }, /* statefarm */ - { 7484, 0, 0, 1 }, /* statoil */ - { 7492, 0, 0, 1 }, /* stc */ - { 3750, 0, 0, 1 }, /* stcgroup */ - { 7496, 0, 0, 1 }, /* stockholm */ - { 7506, 0, 0, 1 }, /* storage */ - { 7514, 0, 0, 1 }, /* store */ - { 7520, 0, 0, 1 }, /* stream */ - { 7527, 0, 0, 1 }, /* studio */ - { 7534, 0, 0, 1 }, /* study */ - { 4941, 0, 0, 1 }, /* style */ - { 3310, 7457, 32, 1 }, /* su */ - { 7545, 0, 0, 1 }, /* sucks */ - { 7551, 0, 0, 1 }, /* supplies */ - { 7560, 0, 0, 1 }, /* supply */ - { 7567, 0, 0, 1 }, /* support */ - { 7575, 0, 0, 1 }, /* surf */ - { 7580, 0, 0, 1 }, /* surgery */ - { 7588, 0, 0, 1 }, /* suzuki */ - { 7595, 7489, 5, 1 }, /* sv */ - { 7598, 0, 0, 1 }, /* swatch */ - { 7605, 0, 0, 1 }, /* swiftcover */ - { 7616, 0, 0, 1 }, /* swiss */ - { 7625, 3685, 1, 1 }, /* sx */ - { 5006, 3549, 6, 1 }, /* sy */ - { 7628, 0, 0, 1 }, /* sydney */ - { 7635, 0, 0, 1 }, /* symantec */ - { 7644, 7494, 1, 1 }, /* systems */ - { 7654, 7495, 3, 1 }, /* sz */ - { 7657, 0, 0, 1 }, /* tab */ - { 7661, 0, 0, 1 }, /* taipei */ - { 7680, 0, 0, 1 }, /* talk */ - { 7685, 0, 0, 1 }, /* taobao */ - { 7692, 0, 0, 1 }, /* target */ - { 7699, 0, 0, 1 }, /* tatamotors */ - { 7710, 0, 0, 1 }, /* tatar */ - { 7716, 0, 0, 1 }, /* tattoo */ + { 7323, 0, 0, 1 }, /* smile */ + { 7330, 7579, 8, 1 }, /* sn */ + { 1610, 0, 0, 1 }, /* sncf */ + { 7334, 7587, 3, 1 }, /* so */ + { 7337, 0, 0, 1 }, /* soccer */ + { 7344, 0, 0, 1 }, /* social */ + { 7351, 0, 0, 1 }, /* softbank */ + { 7360, 0, 0, 1 }, /* software */ + { 4117, 0, 0, 1 }, /* sohu */ + { 7369, 0, 0, 1 }, /* solar */ + { 7375, 0, 0, 1 }, /* solutions */ + { 5986, 0, 0, 1 }, /* song */ + { 7385, 0, 0, 1 }, /* sony */ + { 7392, 0, 0, 1 }, /* soy */ + { 2874, 7590, 3, 1 }, /* space */ + { 7405, 0, 0, 1 }, /* spiegel */ + { 7416, 0, 0, 1 }, /* spot */ + { 7421, 0, 0, 1 }, /* spreadbetting */ + { 7435, 0, 0, 1 }, /* sr */ + { 7438, 0, 0, 1 }, /* srl */ + { 7442, 0, 0, 1 }, /* srt */ + { 619, 7593, 12, 1 }, /* st */ + { 7450, 0, 0, 1 }, /* stada */ + { 7456, 0, 0, 1 }, /* staples */ + { 5567, 0, 0, 1 }, /* star */ + { 7464, 0, 0, 1 }, /* starhub */ + { 7472, 0, 0, 1 }, /* statebank */ + { 2928, 0, 0, 1 }, /* statefarm */ + { 7482, 0, 0, 1 }, /* statoil */ + { 7490, 0, 0, 1 }, /* stc */ + { 3735, 0, 0, 1 }, /* stcgroup */ + { 7494, 0, 0, 1 }, /* stockholm */ + { 7504, 0, 0, 1 }, /* storage */ + { 7512, 0, 0, 1 }, /* store */ + { 7518, 0, 0, 1 }, /* stream */ + { 7525, 0, 0, 1 }, /* studio */ + { 7532, 0, 0, 1 }, /* study */ + { 4920, 0, 0, 1 }, /* style */ + { 3295, 7605, 52, 1 }, /* su */ + { 7543, 0, 0, 1 }, /* sucks */ + { 7549, 0, 0, 1 }, /* supplies */ + { 7558, 0, 0, 1 }, /* supply */ + { 7565, 7254, 1, 1 }, /* support */ + { 7573, 0, 0, 1 }, /* surf */ + { 7578, 0, 0, 1 }, /* surgery */ + { 7586, 0, 0, 1 }, /* suzuki */ + { 7593, 7657, 5, 1 }, /* sv */ + { 7596, 0, 0, 1 }, /* swatch */ + { 7603, 0, 0, 1 }, /* swiftcover */ + { 7614, 0, 0, 1 }, /* swiss */ + { 7623, 3851, 1, 1 }, /* sx */ + { 4985, 3715, 6, 1 }, /* sy */ + { 7626, 0, 0, 1 }, /* sydney */ + { 7633, 0, 0, 1 }, /* symantec */ + { 7642, 7662, 1, 1 }, /* systems */ + { 7652, 7663, 3, 1 }, /* sz */ + { 7655, 0, 0, 1 }, /* tab */ + { 7659, 0, 0, 1 }, /* taipei */ + { 7678, 0, 0, 1 }, /* talk */ + { 7683, 0, 0, 1 }, /* taobao */ + { 7690, 0, 0, 1 }, /* target */ + { 7697, 0, 0, 1 }, /* tatamotors */ + { 7708, 0, 0, 1 }, /* tatar */ + { 7714, 0, 0, 1 }, /* tattoo */ { 662, 0, 0, 1 }, /* tax */ - { 7723, 0, 0, 1 }, /* taxi */ - { 4133, 0, 0, 1 }, /* tc */ - { 1712, 0, 0, 1 }, /* tci */ - { 5104, 3672, 1, 1 }, /* td */ - { 2469, 0, 0, 1 }, /* tdk */ - { 7733, 0, 0, 1 }, /* team */ - { 1622, 0, 0, 1 }, /* tech */ - { 7738, 0, 0, 1 }, /* technology */ + { 7721, 0, 0, 1 }, /* taxi */ + { 4114, 0, 0, 1 }, /* tc */ + { 1713, 0, 0, 1 }, /* tci */ + { 5083, 3838, 1, 1 }, /* td */ + { 2472, 0, 0, 1 }, /* tdk */ + { 7731, 0, 0, 1 }, /* team */ + { 1623, 0, 0, 1 }, /* tech */ + { 7736, 0, 0, 1 }, /* technology */ { 279, 0, 0, 1 }, /* tel */ - { 7755, 0, 0, 1 }, /* telecity */ - { 7764, 0, 0, 1 }, /* telefonica */ - { 7775, 0, 0, 1 }, /* temasek */ - { 7783, 0, 0, 1 }, /* tennis */ - { 7790, 0, 0, 1 }, /* teva */ - { 7796, 0, 0, 1 }, /* tf */ - { 7799, 0, 0, 1 }, /* tg */ - { 13, 7498, 7, 1 }, /* th */ - { 7806, 0, 0, 1 }, /* thd */ - { 7810, 0, 0, 1 }, /* theater */ - { 7818, 0, 0, 1 }, /* theatre */ - { 3771, 0, 0, 1 }, /* theguardian */ - { 7826, 0, 0, 1 }, /* tiaa */ - { 7831, 0, 0, 1 }, /* tickets */ - { 7839, 0, 0, 1 }, /* tienda */ - { 7846, 0, 0, 1 }, /* tiffany */ - { 7854, 0, 0, 1 }, /* tips */ - { 7859, 0, 0, 1 }, /* tires */ - { 7874, 0, 0, 1 }, /* tirol */ - { 7880, 7505, 15, 1 }, /* tj */ - { 7883, 0, 0, 1 }, /* tjmaxx */ - { 7890, 0, 0, 1 }, /* tjx */ - { 7894, 0, 0, 1 }, /* tk */ - { 7897, 0, 0, 1 }, /* tkmaxx */ - { 7906, 3685, 1, 1 }, /* tl */ - { 7910, 7520, 8, 1 }, /* tm */ - { 7913, 0, 0, 1 }, /* tmall */ - { 5613, 7528, 20, 1 }, /* tn */ - { 631, 3549, 6, 1 }, /* to */ - { 2259, 0, 0, 1 }, /* today */ - { 7924, 0, 0, 1 }, /* tokyo */ - { 7930, 0, 0, 1 }, /* tools */ - { 7936, 0, 0, 1 }, /* top */ - { 7940, 0, 0, 1 }, /* toray */ - { 7946, 0, 0, 1 }, /* toshiba */ - { 7954, 0, 0, 1 }, /* total */ - { 7960, 0, 0, 1 }, /* tours */ - { 1402, 0, 0, 1 }, /* town */ - { 7966, 0, 0, 1 }, /* toyota */ - { 7973, 0, 0, 1 }, /* toys */ - { 3302, 3422, 21, 1 }, /* tr */ - { 7982, 3961, 1, 1 }, /* trade */ - { 7988, 0, 0, 1 }, /* trading */ - { 7996, 0, 0, 1 }, /* training */ - { 8005, 0, 0, 1 }, /* travel */ - { 1634, 0, 0, 1 }, /* travelchannel */ - { 8012, 0, 0, 1 }, /* travelers */ - { 8022, 0, 0, 1 }, /* travelersinsurance */ - { 8041, 0, 0, 1 }, /* trust */ - { 8047, 0, 0, 1 }, /* trv */ - { 24, 7548, 17, 1 }, /* tt */ - { 8058, 0, 0, 1 }, /* tube */ - { 8063, 0, 0, 1 }, /* tui */ - { 8067, 0, 0, 1 }, /* tunes */ - { 8073, 0, 0, 1 }, /* tushu */ - { 2546, 7565, 4, 1 }, /* tv */ - { 8079, 0, 0, 1 }, /* tvs */ - { 8083, 7569, 14, 1 }, /* tw */ - { 8091, 7583, 12, 1 }, /* tz */ - { 8097, 7595, 82, 1 }, /* ua */ + { 7753, 0, 0, 1 }, /* telecity */ + { 7762, 0, 0, 1 }, /* telefonica */ + { 7773, 0, 0, 1 }, /* temasek */ + { 7781, 0, 0, 1 }, /* tennis */ + { 7788, 0, 0, 1 }, /* teva */ + { 7794, 0, 0, 1 }, /* tf */ + { 7797, 0, 0, 1 }, /* tg */ + { 13, 7666, 7, 1 }, /* th */ + { 7804, 0, 0, 1 }, /* thd */ + { 7808, 0, 0, 1 }, /* theater */ + { 7816, 0, 0, 1 }, /* theatre */ + { 7824, 0, 0, 1 }, /* tiaa */ + { 7829, 0, 0, 1 }, /* tickets */ + { 7837, 0, 0, 1 }, /* tienda */ + { 7844, 0, 0, 1 }, /* tiffany */ + { 7852, 0, 0, 1 }, /* tips */ + { 7857, 0, 0, 1 }, /* tires */ + { 7872, 0, 0, 1 }, /* tirol */ + { 7878, 7673, 15, 1 }, /* tj */ + { 7881, 0, 0, 1 }, /* tjmaxx */ + { 7888, 0, 0, 1 }, /* tjx */ + { 7892, 0, 0, 1 }, /* tk */ + { 7895, 0, 0, 1 }, /* tkmaxx */ + { 7904, 3851, 1, 1 }, /* tl */ + { 7908, 7688, 8, 1 }, /* tm */ + { 7911, 0, 0, 1 }, /* tmall */ + { 5592, 7696, 20, 1 }, /* tn */ + { 631, 7716, 7, 1 }, /* to */ + { 2260, 0, 0, 1 }, /* today */ + { 7922, 0, 0, 1 }, /* tokyo */ + { 7928, 0, 0, 1 }, /* tools */ + { 7934, 0, 0, 1 }, /* top */ + { 7938, 0, 0, 1 }, /* toray */ + { 7944, 0, 0, 1 }, /* toshiba */ + { 7952, 0, 0, 1 }, /* total */ + { 7958, 0, 0, 1 }, /* tours */ + { 1403, 0, 0, 1 }, /* town */ + { 7964, 0, 0, 1 }, /* toyota */ + { 7971, 0, 0, 1 }, /* toys */ + { 3287, 3564, 21, 1 }, /* tr */ + { 7980, 4118, 1, 1 }, /* trade */ + { 7986, 0, 0, 1 }, /* trading */ + { 7994, 0, 0, 1 }, /* training */ + { 8003, 0, 0, 1 }, /* travel */ + { 1635, 0, 0, 1 }, /* travelchannel */ + { 8010, 0, 0, 1 }, /* travelers */ + { 8020, 0, 0, 1 }, /* travelersinsurance */ + { 8039, 0, 0, 1 }, /* trust */ + { 8045, 0, 0, 1 }, /* trv */ + { 24, 7723, 17, 1 }, /* tt */ + { 8056, 0, 0, 1 }, /* tube */ + { 8061, 0, 0, 1 }, /* tui */ + { 8065, 0, 0, 1 }, /* tunes */ + { 8071, 0, 0, 1 }, /* tushu */ + { 2549, 7740, 4, 1 }, /* tv */ + { 8077, 0, 0, 1 }, /* tvs */ + { 8081, 3585, 15, 1 }, /* tw */ + { 8089, 7745, 12, 1 }, /* tz */ + { 8095, 7757, 82, 1 }, /* ua */ { 730, 0, 0, 1 }, /* ubank */ - { 8100, 0, 0, 1 }, /* ubs */ - { 8104, 0, 0, 1 }, /* uconnect */ - { 8114, 7677, 9, 1 }, /* ug */ - { 8122, 3443, 11, 1 }, /* uk */ - { 8125, 0, 0, 1 }, /* unicom */ - { 8132, 0, 0, 1 }, /* university */ - { 8146, 0, 0, 1 }, /* uno */ - { 8150, 0, 0, 1 }, /* uol */ - { 6506, 0, 0, 1 }, /* ups */ - { 264, 3454, 68, 1 }, /* us */ - { 911, 3525, 6, 1 }, /* uy */ - { 5902, 7700, 4, 1 }, /* uz */ + { 8098, 0, 0, 1 }, /* ubs */ + { 8102, 0, 0, 1 }, /* uconnect */ + { 8112, 7839, 9, 1 }, /* ug */ + { 8120, 3600, 11, 1 }, /* uk */ + { 8123, 0, 0, 1 }, /* unicom */ + { 8130, 0, 0, 1 }, /* university */ + { 8144, 0, 0, 1 }, /* uno */ + { 8148, 0, 0, 1 }, /* uol */ + { 6489, 0, 0, 1 }, /* ups */ + { 264, 3611, 68, 1 }, /* us */ + { 911, 3682, 6, 1 }, /* uy */ + { 5874, 7862, 4, 1 }, /* uz */ { 834, 0, 0, 1 }, /* va */ - { 8163, 0, 0, 1 }, /* vacations */ - { 8173, 0, 0, 1 }, /* vana */ - { 8178, 0, 0, 1 }, /* vanguard */ - { 6561, 3549, 6, 1 }, /* vc */ - { 125, 7704, 17, 1 }, /* ve */ - { 8187, 0, 0, 1 }, /* vegas */ - { 8193, 0, 0, 1 }, /* ventures */ - { 8202, 0, 0, 1 }, /* verisign */ - { 8211, 0, 0, 1 }, /* versicherung */ - { 8229, 0, 0, 1 }, /* vet */ - { 8234, 0, 0, 1 }, /* vg */ - { 8237, 7721, 5, 1 }, /* vi */ - { 8240, 0, 0, 1 }, /* viajes */ - { 8247, 0, 0, 1 }, /* video */ - { 8253, 0, 0, 1 }, /* vig */ - { 8257, 0, 0, 1 }, /* viking */ - { 8264, 0, 0, 1 }, /* villas */ - { 8275, 0, 0, 1 }, /* vin */ - { 8279, 0, 0, 1 }, /* vip */ - { 8283, 0, 0, 1 }, /* virgin */ - { 8290, 0, 0, 1 }, /* visa */ - { 2805, 0, 0, 1 }, /* vision */ - { 8306, 0, 0, 1 }, /* vista */ - { 8312, 0, 0, 1 }, /* vistaprint */ - { 8323, 0, 0, 1 }, /* viva */ - { 8328, 0, 0, 1 }, /* vivo */ - { 8333, 0, 0, 1 }, /* vlaanderen */ - { 8352, 7726, 13, 1 }, /* vn */ - { 8355, 0, 0, 1 }, /* vodka */ - { 8361, 0, 0, 1 }, /* volkswagen */ - { 8372, 0, 0, 1 }, /* volvo */ - { 8378, 0, 0, 1 }, /* vote */ - { 8383, 0, 0, 1 }, /* voting */ - { 8390, 0, 0, 1 }, /* voto */ - { 8395, 0, 0, 1 }, /* voyage */ - { 8402, 3903, 4, 1 }, /* vu */ - { 8405, 0, 0, 1 }, /* vuelos */ - { 8412, 0, 0, 1 }, /* wales */ - { 8418, 0, 0, 1 }, /* walmart */ - { 8426, 0, 0, 1 }, /* walter */ - { 8433, 0, 0, 1 }, /* wang */ - { 8438, 0, 0, 1 }, /* wanggou */ - { 8446, 0, 0, 1 }, /* warman */ - { 7599, 0, 0, 1 }, /* watch */ - { 8453, 0, 0, 1 }, /* watches */ - { 8461, 0, 0, 1 }, /* weather */ - { 8469, 0, 0, 1 }, /* weatherchannel */ - { 1340, 0, 0, 1 }, /* webcam */ - { 8484, 0, 0, 1 }, /* weber */ - { 8493, 0, 0, 1 }, /* website */ - { 8501, 0, 0, 1 }, /* wed */ - { 8505, 0, 0, 1 }, /* wedding */ - { 8513, 0, 0, 1 }, /* weibo */ - { 8519, 0, 0, 1 }, /* weir */ - { 8524, 0, 0, 1 }, /* wf */ - { 8527, 0, 0, 1 }, /* whoswho */ - { 8535, 0, 0, 1 }, /* wien */ - { 8547, 0, 0, 1 }, /* wiki */ - { 8552, 0, 0, 1 }, /* williamhill */ - { 8564, 0, 0, 1 }, /* win */ - { 8568, 0, 0, 1 }, /* windows */ - { 8576, 0, 0, 1 }, /* wine */ - { 8581, 0, 0, 1 }, /* winners */ - { 5323, 0, 0, 1 }, /* wme */ - { 8589, 0, 0, 1 }, /* wolterskluwer */ - { 8603, 0, 0, 1 }, /* woodside */ - { 3188, 0, 0, 1 }, /* work */ - { 8612, 0, 0, 1 }, /* works */ - { 8618, 0, 0, 1 }, /* world */ - { 8624, 0, 0, 1 }, /* wow */ - { 659, 7739, 7, 1 }, /* ws */ - { 8628, 0, 0, 1 }, /* wtc */ - { 7795, 0, 0, 1 }, /* wtf */ - { 1176, 0, 0, 1 }, /* xbox */ - { 3317, 0, 0, 1 }, /* xerox */ - { 8632, 0, 0, 1 }, /* xfinity */ - { 8640, 0, 0, 1 }, /* xihuan */ - { 8647, 0, 0, 1 }, /* xin */ - { 8651, 0, 0, 1 }, /* xn--11b4c3d */ - { 8663, 0, 0, 1 }, /* xn--1ck2e1b */ - { 8675, 0, 0, 1 }, /* xn--1qqw23a */ - { 8687, 0, 0, 1 }, /* xn--30rr7y */ - { 8698, 0, 0, 1 }, /* xn--3bst00m */ - { 8710, 0, 0, 1 }, /* xn--3ds443g */ - { 8722, 0, 0, 1 }, /* xn--3e0b707e */ - { 8735, 0, 0, 1 }, /* xn--3oq18vl8pn36a */ - { 8753, 0, 0, 1 }, /* xn--3pxu8k */ - { 8764, 0, 0, 1 }, /* xn--42c2d9a */ - { 8776, 0, 0, 1 }, /* xn--45brj9c */ - { 8788, 0, 0, 1 }, /* xn--45q11c */ - { 8799, 0, 0, 1 }, /* xn--4gbrim */ - { 8810, 0, 0, 1 }, /* xn--4gq48lf9j */ - { 8824, 0, 0, 1 }, /* xn--54b7fta0cc */ - { 8839, 0, 0, 1 }, /* xn--55qw42g */ - { 8851, 0, 0, 1 }, /* xn--55qx5d */ - { 7177, 0, 0, 1 }, /* xn--5su34j936bgsg */ - { 8862, 0, 0, 1 }, /* xn--5tzm5g */ - { 8873, 0, 0, 1 }, /* xn--6frz82g */ - { 8885, 0, 0, 1 }, /* xn--6qq986b3xl */ - { 8900, 0, 0, 1 }, /* xn--80adxhks */ - { 8913, 0, 0, 1 }, /* xn--80ao21a */ - { 8925, 0, 0, 1 }, /* xn--80aqecdr1a */ - { 8940, 0, 0, 1 }, /* xn--80asehdb */ - { 8953, 0, 0, 1 }, /* xn--80aswg */ - { 8964, 0, 0, 1 }, /* xn--8y0a063a */ - { 8977, 7746, 6, 1 }, /* xn--90a3ac */ - { 8988, 0, 0, 1 }, /* xn--90ais */ - { 8998, 0, 0, 1 }, /* xn--9dbq2a */ - { 9009, 0, 0, 1 }, /* xn--9et52u */ - { 9020, 0, 0, 1 }, /* xn--9krt00a */ - { 9032, 0, 0, 1 }, /* xn--b4w605ferd */ - { 9047, 0, 0, 1 }, /* xn--bck1b9a5dre4c */ - { 9065, 0, 0, 1 }, /* xn--c1avg */ - { 9075, 0, 0, 1 }, /* xn--c2br7g */ - { 9086, 0, 0, 1 }, /* xn--cck2b3b */ - { 9098, 0, 0, 1 }, /* xn--cg4bki */ - { 9109, 0, 0, 1 }, /* xn--clchc0ea0b2g2a9gcd */ - { 9132, 0, 0, 1 }, /* xn--czr694b */ - { 9144, 0, 0, 1 }, /* xn--czrs0t */ - { 9155, 0, 0, 1 }, /* xn--czru2d */ - { 9166, 0, 0, 1 }, /* xn--d1acj3b */ - { 9178, 0, 0, 1 }, /* xn--d1alf */ - { 9188, 0, 0, 1 }, /* xn--e1a4c */ - { 9198, 0, 0, 1 }, /* xn--eckvdtc9d */ - { 9212, 0, 0, 1 }, /* xn--efvy88h */ - { 9224, 0, 0, 1 }, /* xn--estv75g */ - { 9236, 0, 0, 1 }, /* xn--fct429k */ - { 9248, 0, 0, 1 }, /* xn--fhbei */ - { 9258, 0, 0, 1 }, /* xn--fiq228c5hs */ - { 9273, 0, 0, 1 }, /* xn--fiq64b */ - { 9284, 0, 0, 1 }, /* xn--fiqs8s */ - { 9295, 0, 0, 1 }, /* xn--fiqz9s */ - { 9306, 0, 0, 1 }, /* xn--fjq720a */ - { 9318, 0, 0, 1 }, /* xn--flw351e */ - { 9330, 0, 0, 1 }, /* xn--fpcrj9c3d */ - { 9344, 0, 0, 1 }, /* xn--fzc2c9e2c */ - { 3576, 0, 0, 1 }, /* xn--fzys8d69uvgm */ - { 9358, 0, 0, 1 }, /* xn--g2xx48c */ - { 9370, 0, 0, 1 }, /* xn--gckr3f0f */ - { 9383, 0, 0, 1 }, /* xn--gecrj9c */ - { 9395, 0, 0, 1 }, /* xn--gk3at1e */ - { 9407, 0, 0, 1 }, /* xn--h2brj9c */ - { 9419, 0, 0, 1 }, /* xn--hxt814e */ - { 9431, 0, 0, 1 }, /* xn--i1b6b1a6a2e */ - { 9447, 0, 0, 1 }, /* xn--imr513n */ - { 9459, 0, 0, 1 }, /* xn--io0a7i */ - { 9470, 0, 0, 1 }, /* xn--j1aef */ - { 5384, 0, 0, 1 }, /* xn--j1amh */ - { 9480, 0, 0, 1 }, /* xn--j6w193g */ - { 9492, 0, 0, 1 }, /* xn--jlq61u9w7b */ - { 9507, 0, 0, 1 }, /* xn--jvr189m */ - { 9519, 0, 0, 1 }, /* xn--kcrx77d1x4a */ - { 9535, 0, 0, 1 }, /* xn--kprw13d */ - { 9547, 0, 0, 1 }, /* xn--kpry57d */ - { 9559, 0, 0, 1 }, /* xn--kpu716f */ - { 9571, 0, 0, 1 }, /* xn--kput3i */ - { 1572, 0, 0, 1 }, /* xn--l1acc */ - { 9582, 0, 0, 1 }, /* xn--lgbbat1ad8j */ - { 9598, 0, 0, 1 }, /* xn--mgb2ddes */ + { 8156, 0, 0, 1 }, /* vacations */ + { 8166, 0, 0, 1 }, /* vana */ + { 8171, 0, 0, 1 }, /* vanguard */ + { 6544, 3715, 6, 1 }, /* vc */ + { 125, 7866, 17, 1 }, /* ve */ + { 8180, 0, 0, 1 }, /* vegas */ + { 8186, 0, 0, 1 }, /* ventures */ + { 8195, 0, 0, 1 }, /* verisign */ + { 8204, 0, 0, 1 }, /* versicherung */ + { 8222, 0, 0, 1 }, /* vet */ + { 8227, 0, 0, 1 }, /* vg */ + { 8230, 7883, 5, 1 }, /* vi */ + { 8233, 0, 0, 1 }, /* viajes */ + { 8240, 0, 0, 1 }, /* video */ + { 8246, 0, 0, 1 }, /* vig */ + { 8250, 0, 0, 1 }, /* viking */ + { 8257, 0, 0, 1 }, /* villas */ + { 8268, 0, 0, 1 }, /* vin */ + { 8272, 0, 0, 1 }, /* vip */ + { 8276, 0, 0, 1 }, /* virgin */ + { 8283, 0, 0, 1 }, /* visa */ + { 2804, 0, 0, 1 }, /* vision */ + { 8299, 0, 0, 1 }, /* vista */ + { 8305, 0, 0, 1 }, /* vistaprint */ + { 8316, 0, 0, 1 }, /* viva */ + { 8321, 0, 0, 1 }, /* vivo */ + { 8326, 0, 0, 1 }, /* vlaanderen */ + { 8345, 7888, 13, 1 }, /* vn */ + { 8348, 0, 0, 1 }, /* vodka */ + { 8354, 0, 0, 1 }, /* volkswagen */ + { 8365, 0, 0, 1 }, /* volvo */ + { 8371, 0, 0, 1 }, /* vote */ + { 8376, 0, 0, 1 }, /* voting */ + { 8383, 0, 0, 1 }, /* voto */ + { 8388, 0, 0, 1 }, /* voyage */ + { 8395, 4061, 4, 1 }, /* vu */ + { 8398, 0, 0, 1 }, /* vuelos */ + { 8405, 0, 0, 1 }, /* wales */ + { 8411, 0, 0, 1 }, /* walmart */ + { 8419, 0, 0, 1 }, /* walter */ + { 8426, 0, 0, 1 }, /* wang */ + { 8431, 0, 0, 1 }, /* wanggou */ + { 8439, 0, 0, 1 }, /* warman */ + { 7597, 0, 0, 1 }, /* watch */ + { 8446, 0, 0, 1 }, /* watches */ + { 8454, 0, 0, 1 }, /* weather */ + { 8462, 0, 0, 1 }, /* weatherchannel */ + { 1341, 0, 0, 1 }, /* webcam */ + { 8477, 0, 0, 1 }, /* weber */ + { 8486, 0, 0, 1 }, /* website */ + { 8494, 0, 0, 1 }, /* wed */ + { 8498, 0, 0, 1 }, /* wedding */ + { 8506, 0, 0, 1 }, /* weibo */ + { 8512, 0, 0, 1 }, /* weir */ + { 8517, 0, 0, 1 }, /* wf */ + { 8520, 0, 0, 1 }, /* whoswho */ + { 8528, 0, 0, 1 }, /* wien */ + { 8540, 0, 0, 1 }, /* wiki */ + { 8545, 0, 0, 1 }, /* williamhill */ + { 8557, 0, 0, 1 }, /* win */ + { 8561, 0, 0, 1 }, /* windows */ + { 8569, 0, 0, 1 }, /* wine */ + { 8574, 0, 0, 1 }, /* winners */ + { 5302, 0, 0, 1 }, /* wme */ + { 8582, 0, 0, 1 }, /* wolterskluwer */ + { 8596, 0, 0, 1 }, /* woodside */ + { 3173, 0, 0, 1 }, /* work */ + { 8605, 0, 0, 1 }, /* works */ + { 8611, 0, 0, 1 }, /* world */ + { 8617, 0, 0, 1 }, /* wow */ + { 659, 3688, 8, 1 }, /* ws */ + { 8621, 0, 0, 1 }, /* wtc */ + { 7793, 0, 0, 1 }, /* wtf */ + { 1177, 0, 0, 1 }, /* xbox */ + { 3302, 0, 0, 1 }, /* xerox */ + { 8625, 0, 0, 1 }, /* xfinity */ + { 8633, 0, 0, 1 }, /* xihuan */ + { 8640, 0, 0, 1 }, /* xin */ + { 8644, 0, 0, 1 }, /* xn--11b4c3d */ + { 8656, 0, 0, 1 }, /* xn--1ck2e1b */ + { 8668, 0, 0, 1 }, /* xn--1qqw23a */ + { 8680, 0, 0, 1 }, /* xn--30rr7y */ + { 8691, 0, 0, 1 }, /* xn--3bst00m */ + { 8703, 0, 0, 1 }, /* xn--3ds443g */ + { 8715, 0, 0, 1 }, /* xn--3e0b707e */ + { 8728, 0, 0, 1 }, /* xn--3oq18vl8pn36a */ + { 8746, 0, 0, 1 }, /* xn--3pxu8k */ + { 8757, 0, 0, 1 }, /* xn--42c2d9a */ + { 8769, 0, 0, 1 }, /* xn--45brj9c */ + { 8781, 0, 0, 1 }, /* xn--45q11c */ + { 8792, 0, 0, 1 }, /* xn--4gbrim */ + { 8803, 0, 0, 1 }, /* xn--54b7fta0cc */ + { 8818, 0, 0, 1 }, /* xn--55qw42g */ + { 8830, 0, 0, 1 }, /* xn--55qx5d */ + { 7166, 0, 0, 1 }, /* xn--5su34j936bgsg */ + { 8841, 0, 0, 1 }, /* xn--5tzm5g */ + { 8852, 0, 0, 1 }, /* xn--6frz82g */ + { 8864, 0, 0, 1 }, /* xn--6qq986b3xl */ + { 8879, 0, 0, 1 }, /* xn--80adxhks */ + { 8892, 0, 0, 1 }, /* xn--80ao21a */ + { 8904, 0, 0, 1 }, /* xn--80aqecdr1a */ + { 8919, 0, 0, 1 }, /* xn--80asehdb */ + { 8932, 0, 0, 1 }, /* xn--80aswg */ + { 8943, 0, 0, 1 }, /* xn--8y0a063a */ + { 8956, 7901, 6, 1 }, /* xn--90a3ac */ + { 8967, 0, 0, 1 }, /* xn--90ais */ + { 8977, 0, 0, 1 }, /* xn--9dbq2a */ + { 8988, 0, 0, 1 }, /* xn--9et52u */ + { 8999, 0, 0, 1 }, /* xn--9krt00a */ + { 9011, 0, 0, 1 }, /* xn--b4w605ferd */ + { 9026, 0, 0, 1 }, /* xn--bck1b9a5dre4c */ + { 9044, 0, 0, 1 }, /* xn--c1avg */ + { 9054, 0, 0, 1 }, /* xn--c2br7g */ + { 9065, 0, 0, 1 }, /* xn--cck2b3b */ + { 9077, 0, 0, 1 }, /* xn--cg4bki */ + { 9088, 0, 0, 1 }, /* xn--clchc0ea0b2g2a9gcd */ + { 9111, 0, 0, 1 }, /* xn--czr694b */ + { 9123, 0, 0, 1 }, /* xn--czrs0t */ + { 9134, 0, 0, 1 }, /* xn--czru2d */ + { 9145, 0, 0, 1 }, /* xn--d1acj3b */ + { 9157, 0, 0, 1 }, /* xn--d1alf */ + { 9167, 0, 0, 1 }, /* xn--e1a4c */ + { 9177, 0, 0, 1 }, /* xn--eckvdtc9d */ + { 9191, 0, 0, 1 }, /* xn--efvy88h */ + { 9203, 0, 0, 1 }, /* xn--estv75g */ + { 9215, 0, 0, 1 }, /* xn--fct429k */ + { 9227, 0, 0, 1 }, /* xn--fhbei */ + { 9237, 0, 0, 1 }, /* xn--fiq228c5hs */ + { 9252, 0, 0, 1 }, /* xn--fiq64b */ + { 9263, 0, 0, 1 }, /* xn--fiqs8s */ + { 9274, 0, 0, 1 }, /* xn--fiqz9s */ + { 9285, 0, 0, 1 }, /* xn--fjq720a */ + { 9297, 0, 0, 1 }, /* xn--flw351e */ + { 9309, 0, 0, 1 }, /* xn--fpcrj9c3d */ + { 9323, 0, 0, 1 }, /* xn--fzc2c9e2c */ + { 3561, 0, 0, 1 }, /* xn--fzys8d69uvgm */ + { 9337, 0, 0, 1 }, /* xn--g2xx48c */ + { 9349, 0, 0, 1 }, /* xn--gckr3f0f */ + { 9362, 0, 0, 1 }, /* xn--gecrj9c */ + { 9374, 0, 0, 1 }, /* xn--gk3at1e */ + { 9386, 0, 0, 1 }, /* xn--h2brj9c */ + { 9398, 0, 0, 1 }, /* xn--hxt814e */ + { 9410, 0, 0, 1 }, /* xn--i1b6b1a6a2e */ + { 9426, 0, 0, 1 }, /* xn--imr513n */ + { 9438, 0, 0, 1 }, /* xn--io0a7i */ + { 9449, 0, 0, 1 }, /* xn--j1aef */ + { 5363, 0, 0, 1 }, /* xn--j1amh */ + { 9459, 0, 0, 1 }, /* xn--j6w193g */ + { 9471, 0, 0, 1 }, /* xn--jlq61u9w7b */ + { 9486, 0, 0, 1 }, /* xn--jvr189m */ + { 9498, 0, 0, 1 }, /* xn--kcrx77d1x4a */ + { 9514, 0, 0, 1 }, /* xn--kprw13d */ + { 9526, 0, 0, 1 }, /* xn--kpry57d */ + { 9538, 0, 0, 1 }, /* xn--kpu716f */ + { 9550, 0, 0, 1 }, /* xn--kput3i */ + { 1573, 0, 0, 1 }, /* xn--l1acc */ + { 9561, 0, 0, 1 }, /* xn--lgbbat1ad8j */ + { 9577, 0, 0, 1 }, /* xn--mgb2ddes */ { 918, 0, 0, 1 }, /* xn--mgb9awbf */ - { 9611, 0, 0, 1 }, /* xn--mgba3a3ejt */ - { 9626, 0, 0, 1 }, /* xn--mgba3a4f16a */ - { 9642, 0, 0, 1 }, /* xn--mgba3a4fra */ - { 9657, 0, 0, 1 }, /* xn--mgba7c0bbn0a */ - { 9674, 0, 0, 1 }, /* xn--mgbaakc7dvf */ - { 9690, 0, 0, 1 }, /* xn--mgbaam7a8h */ + { 9590, 0, 0, 1 }, /* xn--mgba3a3ejt */ + { 9605, 0, 0, 1 }, /* xn--mgba3a4f16a */ + { 9621, 0, 0, 1 }, /* xn--mgba3a4fra */ + { 9636, 0, 0, 1 }, /* xn--mgba7c0bbn0a */ + { 9653, 0, 0, 1 }, /* xn--mgbaakc7dvf */ + { 9669, 0, 0, 1 }, /* xn--mgbaam7a8h */ { 845, 0, 0, 1 }, /* xn--mgbab2bd */ - { 9705, 0, 0, 1 }, /* xn--mgbai9a5eva00b */ - { 9724, 0, 0, 1 }, /* xn--mgbai9azgqp6j */ - { 9742, 0, 0, 1 }, /* xn--mgbayh7gpa */ - { 9757, 0, 0, 1 }, /* xn--mgbb9fbpob */ - { 9772, 0, 0, 1 }, /* xn--mgbbh1a71e */ - { 9787, 0, 0, 1 }, /* xn--mgbc0a9azcg */ - { 9803, 0, 0, 1 }, /* xn--mgbca7dzdo */ - { 9818, 0, 0, 1 }, /* xn--mgberp4a5d4a87g */ - { 9838, 0, 0, 1 }, /* xn--mgberp4a5d4ar */ - { 9856, 0, 0, 1 }, /* xn--mgbi4ecexp */ - { 9871, 0, 0, 1 }, /* xn--mgbpl2fh */ - { 9884, 0, 0, 1 }, /* xn--mgbqly7c0a67fbc */ - { 9904, 0, 0, 1 }, /* xn--mgbqly7cvafr */ - { 9921, 0, 0, 1 }, /* xn--mgbt3dhd */ - { 9934, 0, 0, 1 }, /* xn--mgbtf8fl */ - { 9947, 0, 0, 1 }, /* xn--mgbtx2b */ - { 9959, 0, 0, 1 }, /* xn--mgbx4cd0ab */ - { 9974, 0, 0, 1 }, /* xn--mix082f */ - { 9986, 0, 0, 1 }, /* xn--mix891f */ - { 9998, 0, 0, 1 }, /* xn--mk1bu44c */ - { 10011, 0, 0, 1 }, /* xn--mxtq1m */ - { 10022, 0, 0, 1 }, /* xn--ngbc5azd */ - { 10035, 0, 0, 1 }, /* xn--ngbe9e0a */ - { 10048, 0, 0, 1 }, /* xn--ngbrx */ - { 10058, 0, 0, 1 }, /* xn--nnx388a */ - { 10070, 0, 0, 1 }, /* xn--node */ - { 10079, 0, 0, 1 }, /* xn--nqv7f */ - { 10089, 0, 0, 1 }, /* xn--nqv7fs00ema */ - { 10105, 0, 0, 1 }, /* xn--nyqy26a */ - { 10117, 0, 0, 1 }, /* xn--o3cw4h */ - { 10128, 0, 0, 1 }, /* xn--ogbpf8fl */ - { 10141, 0, 0, 1 }, /* xn--p1acf */ - { 10151, 0, 0, 1 }, /* xn--p1ai */ - { 10160, 0, 0, 1 }, /* xn--pbt977c */ - { 10172, 0, 0, 1 }, /* xn--pgbs0dh */ - { 10184, 0, 0, 1 }, /* xn--pssy2u */ - { 10195, 0, 0, 1 }, /* xn--q9jyb4c */ - { 5297, 0, 0, 1 }, /* xn--qcka1pmc */ - { 10207, 0, 0, 1 }, /* xn--qxam */ - { 10216, 0, 0, 1 }, /* xn--rhqv96g */ - { 10228, 0, 0, 1 }, /* xn--rovu88b */ - { 10240, 0, 0, 1 }, /* xn--s9brj9c */ - { 10252, 0, 0, 1 }, /* xn--ses554g */ - { 10264, 0, 0, 1 }, /* xn--t60b56a */ - { 10276, 0, 0, 1 }, /* xn--tckwe */ - { 10286, 0, 0, 1 }, /* xn--tiq49xqyj */ - { 10300, 0, 0, 1 }, /* xn--unup4y */ - { 10311, 0, 0, 1 }, /* xn--vermgensberater-ctb */ - { 10335, 0, 0, 1 }, /* xn--vermgensberatung-pwb */ - { 10360, 0, 0, 1 }, /* xn--vhquv */ - { 10370, 0, 0, 1 }, /* xn--vuq861b */ - { 10382, 0, 0, 1 }, /* xn--w4r85el8fhu5dnra */ - { 10403, 0, 0, 1 }, /* xn--w4rs40l */ - { 10415, 0, 0, 1 }, /* xn--wgbh1c */ - { 10426, 0, 0, 1 }, /* xn--wgbl6a */ - { 10437, 0, 0, 1 }, /* xn--xhq521b */ - { 10449, 0, 0, 1 }, /* xn--xkc2al3hye2a */ - { 10466, 0, 0, 1 }, /* xn--xkc2dl3a5ee0h */ - { 10484, 0, 0, 1 }, /* xn--y9a3aq */ - { 10495, 0, 0, 1 }, /* xn--yfro4i67o */ - { 10509, 0, 0, 1 }, /* xn--ygbi2ammx */ - { 10523, 0, 0, 1 }, /* xn--zfr164b */ - { 10535, 0, 0, 1 }, /* xperia */ - { 10542, 0, 0, 1 }, /* xxx */ - { 10546, 7752, 1, 1 }, /* xyz */ - { 10550, 0, 0, 1 }, /* yachts */ - { 10557, 0, 0, 1 }, /* yahoo */ - { 10563, 0, 0, 1 }, /* yamaxun */ - { 10571, 0, 0, 1 }, /* yandex */ - { 10578, 3687, 1, 0 }, /* ye */ - { 10581, 0, 0, 1 }, /* yodobashi */ - { 10599, 0, 0, 1 }, /* yoga */ - { 10604, 0, 0, 1 }, /* yokohama */ - { 2194, 0, 0, 1 }, /* you */ - { 8055, 0, 0, 1 }, /* youtube */ - { 10613, 0, 0, 1 }, /* yt */ - { 10616, 0, 0, 1 }, /* yun */ - { 6329, 3531, 17, 0 }, /* za */ - { 10625, 0, 0, 1 }, /* zappos */ - { 10632, 0, 0, 1 }, /* zara */ - { 10637, 0, 0, 1 }, /* zero */ - { 10642, 0, 0, 1 }, /* zip */ - { 10646, 0, 0, 1 }, /* zippo */ - { 10652, 7753, 11, 1 }, /* zm */ - { 10658, 3548, 1, 1 }, /* zone */ - { 6773, 0, 0, 1 }, /* zuerich */ - { 10663, 3687, 1, 0 }, /* zw */ - { 1913, 3672, 1, 1 }, /* com.ar */ - { 2624, 0, 0, 1 }, /* edu.ar */ - { 11325, 0, 0, 1 }, /* gob.ar */ - { 3686, 0, 0, 1 }, /* gov.ar */ - { 3632, 0, 0, 1 }, /* int.ar */ - { 4195, 0, 0, 1 }, /* mil.ar */ - { 4185, 0, 0, 1 }, /* net.ar */ - { 6070, 0, 0, 1 }, /* org.ar */ - { 11335, 0, 0, 1 }, /* tur.ar */ + { 9684, 0, 0, 1 }, /* xn--mgbai9a5eva00b */ + { 9703, 0, 0, 1 }, /* xn--mgbai9azgqp6j */ + { 9721, 0, 0, 1 }, /* xn--mgbayh7gpa */ + { 9736, 0, 0, 1 }, /* xn--mgbb9fbpob */ + { 9751, 0, 0, 1 }, /* xn--mgbbh1a71e */ + { 9766, 0, 0, 1 }, /* xn--mgbc0a9azcg */ + { 9782, 0, 0, 1 }, /* xn--mgbca7dzdo */ + { 9797, 0, 0, 1 }, /* xn--mgberp4a5d4a87g */ + { 9817, 0, 0, 1 }, /* xn--mgberp4a5d4ar */ + { 9835, 0, 0, 1 }, /* xn--mgbi4ecexp */ + { 9850, 0, 0, 1 }, /* xn--mgbpl2fh */ + { 9863, 0, 0, 1 }, /* xn--mgbqly7c0a67fbc */ + { 9883, 0, 0, 1 }, /* xn--mgbqly7cvafr */ + { 9900, 0, 0, 1 }, /* xn--mgbt3dhd */ + { 9913, 0, 0, 1 }, /* xn--mgbtf8fl */ + { 9926, 0, 0, 1 }, /* xn--mgbtx2b */ + { 9938, 0, 0, 1 }, /* xn--mgbx4cd0ab */ + { 9953, 0, 0, 1 }, /* xn--mix082f */ + { 9965, 0, 0, 1 }, /* xn--mix891f */ + { 9977, 0, 0, 1 }, /* xn--mk1bu44c */ + { 9990, 0, 0, 1 }, /* xn--mxtq1m */ + { 10001, 0, 0, 1 }, /* xn--ngbc5azd */ + { 10014, 0, 0, 1 }, /* xn--ngbe9e0a */ + { 10027, 0, 0, 1 }, /* xn--ngbrx */ + { 10037, 0, 0, 1 }, /* xn--nnx388a */ + { 10049, 0, 0, 1 }, /* xn--node */ + { 10058, 0, 0, 1 }, /* xn--nqv7f */ + { 10068, 0, 0, 1 }, /* xn--nqv7fs00ema */ + { 10084, 0, 0, 1 }, /* xn--nyqy26a */ + { 10096, 7907, 6, 1 }, /* xn--o3cw4h */ + { 10107, 0, 0, 1 }, /* xn--ogbpf8fl */ + { 10120, 0, 0, 1 }, /* xn--p1acf */ + { 10130, 0, 0, 1 }, /* xn--p1ai */ + { 10139, 0, 0, 1 }, /* xn--pbt977c */ + { 10151, 0, 0, 1 }, /* xn--pgbs0dh */ + { 10163, 0, 0, 1 }, /* xn--pssy2u */ + { 10174, 0, 0, 1 }, /* xn--q9jyb4c */ + { 5276, 0, 0, 1 }, /* xn--qcka1pmc */ + { 10186, 0, 0, 1 }, /* xn--qxam */ + { 10195, 0, 0, 1 }, /* xn--rhqv96g */ + { 10207, 0, 0, 1 }, /* xn--rovu88b */ + { 10219, 0, 0, 1 }, /* xn--s9brj9c */ + { 10231, 0, 0, 1 }, /* xn--ses554g */ + { 10243, 0, 0, 1 }, /* xn--t60b56a */ + { 10255, 0, 0, 1 }, /* xn--tckwe */ + { 10265, 0, 0, 1 }, /* xn--tiq49xqyj */ + { 10279, 0, 0, 1 }, /* xn--unup4y */ + { 10290, 0, 0, 1 }, /* xn--vermgensberater-ctb */ + { 10314, 0, 0, 1 }, /* xn--vermgensberatung-pwb */ + { 10339, 0, 0, 1 }, /* xn--vhquv */ + { 10349, 0, 0, 1 }, /* xn--vuq861b */ + { 10361, 0, 0, 1 }, /* xn--w4r85el8fhu5dnra */ + { 10382, 0, 0, 1 }, /* xn--w4rs40l */ + { 10394, 0, 0, 1 }, /* xn--wgbh1c */ + { 10405, 0, 0, 1 }, /* xn--wgbl6a */ + { 10416, 0, 0, 1 }, /* xn--xhq521b */ + { 10428, 0, 0, 1 }, /* xn--xkc2al3hye2a */ + { 10445, 0, 0, 1 }, /* xn--xkc2dl3a5ee0h */ + { 10463, 0, 0, 1 }, /* xn--y9a3aq */ + { 10474, 0, 0, 1 }, /* xn--yfro4i67o */ + { 10488, 0, 0, 1 }, /* xn--ygbi2ammx */ + { 10502, 0, 0, 1 }, /* xn--zfr164b */ + { 10514, 0, 0, 1 }, /* xperia */ + { 10521, 0, 0, 1 }, /* xxx */ + { 10525, 7913, 1, 1 }, /* xyz */ + { 10529, 0, 0, 1 }, /* yachts */ + { 10536, 0, 0, 1 }, /* yahoo */ + { 10542, 0, 0, 1 }, /* yamaxun */ + { 10550, 0, 0, 1 }, /* yandex */ + { 10557, 3853, 1, 0 }, /* ye */ + { 10560, 0, 0, 1 }, /* yodobashi */ + { 10578, 0, 0, 1 }, /* yoga */ + { 10583, 0, 0, 1 }, /* yokohama */ + { 2195, 0, 0, 1 }, /* you */ + { 8053, 0, 0, 1 }, /* youtube */ + { 10592, 0, 0, 1 }, /* yt */ + { 10595, 0, 0, 1 }, /* yun */ + { 6312, 3696, 17, 0 }, /* za */ + { 10604, 0, 0, 1 }, /* zappos */ + { 10611, 0, 0, 1 }, /* zara */ + { 10616, 0, 0, 1 }, /* zero */ + { 10621, 0, 0, 1 }, /* zip */ + { 10625, 0, 0, 1 }, /* zippo */ + { 10631, 7914, 11, 1 }, /* zm */ + { 10637, 3713, 2, 1 }, /* zone */ + { 6756, 0, 0, 1 }, /* zuerich */ + { 10642, 7925, 5, 1 }, /* zw */ + { 1914, 3838, 1, 1 }, /* com.ar */ + { 2623, 0, 0, 1 }, /* edu.ar */ + { 11304, 0, 0, 1 }, /* gob.ar */ + { 3671, 0, 0, 1 }, /* gov.ar */ + { 3617, 0, 0, 1 }, /* int.ar */ + { 4170, 0, 0, 1 }, /* mil.ar */ + { 11308, 0, 0, 1 }, /* musica.ar */ + { 5737, 0, 0, 1 }, /* net.ar */ + { 6053, 0, 0, 1 }, /* org.ar */ + { 11321, 0, 0, 1 }, /* tur.ar */ + { 11361, 0, 0, 1 }, /* 12hp.at */ + { 11366, 0, 0, 1 }, /* 2ix.at */ + { 11370, 0, 0, 1 }, /* 4lima.at */ { 62, 0, 0, 1 }, /* ac.at */ - { 985, 0, 0, 1 }, /* biz.at */ - { 113, 3672, 1, 1 }, /* co.at */ - { 4056, 0, 0, 1 }, /* futurehosting.at */ - { 11375, 0, 0, 1 }, /* futuremailing.at */ - { 11318, 0, 0, 1 }, /* gv.at */ - { 3167, 0, 0, 1 }, /* info.at */ + { 986, 0, 0, 1 }, /* biz.at */ + { 113, 3838, 1, 1 }, /* co.at */ + { 11376, 0, 0, 1 }, /* futurehosting.at */ + { 11390, 0, 0, 1 }, /* futuremailing.at */ + { 11297, 0, 0, 1 }, /* gv.at */ + { 3152, 0, 0, 1 }, /* info.at */ + { 11404, 0, 0, 1 }, /* lima-city.at */ { 137, 0, 0, 1 }, /* or.at */ - { 3163, 1572, 2, 0 }, /* ortsinfo.at */ - { 11393, 0, 0, 1 }, /* priv.at */ - { 397, 3687, 1, 0 }, /* ex.ortsinfo.at */ - { 11403, 3687, 1, 0 }, /* kunden.ortsinfo.at */ - { 2003, 0, 0, 1 }, /* act.au */ - { 7340, 0, 0, 1 }, /* asn.au */ - { 1913, 3672, 1, 1 }, /* com.au */ - { 11412, 0, 0, 1 }, /* conf.au */ - { 2624, 3688, 8, 1 }, /* edu.au */ - { 3686, 3696, 5, 1 }, /* gov.au */ + { 3148, 1573, 2, 0 }, /* ortsinfo.at */ + { 11418, 0, 0, 1 }, /* priv.at */ + { 397, 3853, 1, 0 }, /* ex.ortsinfo.at */ + { 11428, 3853, 1, 0 }, /* kunden.ortsinfo.at */ + { 2004, 0, 0, 1 }, /* act.au */ + { 7329, 0, 0, 1 }, /* asn.au */ + { 1914, 3838, 1, 1 }, /* com.au */ + { 11437, 0, 0, 1 }, /* conf.au */ + { 2623, 3854, 8, 1 }, /* edu.au */ + { 3671, 3862, 5, 1 }, /* gov.au */ { 437, 0, 0, 1 }, /* id.au */ - { 3167, 0, 0, 1 }, /* info.au */ - { 4185, 0, 0, 1 }, /* net.au */ - { 11417, 0, 0, 1 }, /* nsw.au */ + { 3152, 0, 0, 1 }, /* info.au */ + { 5737, 0, 0, 1 }, /* net.au */ + { 11442, 0, 0, 1 }, /* nsw.au */ { 97, 0, 0, 1 }, /* nt.au */ - { 6070, 0, 0, 1 }, /* org.au */ - { 11427, 0, 0, 1 }, /* oz.au */ - { 11430, 0, 0, 1 }, /* qld.au */ - { 1493, 0, 0, 1 }, /* sa.au */ + { 6053, 0, 0, 1 }, /* org.au */ + { 11452, 0, 0, 1 }, /* oz.au */ + { 11455, 0, 0, 1 }, /* qld.au */ + { 1494, 0, 0, 1 }, /* sa.au */ { 536, 0, 0, 1 }, /* tas.au */ - { 11435, 0, 0, 1 }, /* vic.au */ - { 5971, 0, 0, 1 }, /* wa.au */ + { 11460, 0, 0, 1 }, /* vic.au */ + { 5943, 0, 0, 1 }, /* wa.au */ { 62, 0, 0, 1 }, /* ac.be */ - { 10666, 0, 0, 1 }, /* blogspot.be */ - { 11450, 3687, 1, 0 }, /* transurl.be */ - { 2473, 0, 0, 1 }, /* adm.br */ - { 11632, 0, 0, 1 }, /* adv.br */ - { 3696, 0, 0, 1 }, /* agr.br */ + { 10645, 0, 0, 1 }, /* blogspot.be */ + { 11475, 3853, 1, 0 }, /* transurl.be */ + { 2476, 0, 0, 1 }, /* adm.br */ + { 11665, 0, 0, 1 }, /* adv.br */ + { 3681, 0, 0, 1 }, /* agr.br */ { 358, 0, 0, 1 }, /* am.br */ - { 11636, 0, 0, 1 }, /* arq.br */ + { 11669, 0, 0, 1 }, /* arq.br */ { 527, 0, 0, 1 }, /* art.br */ - { 11641, 0, 0, 1 }, /* ato.br */ + { 11674, 0, 0, 1 }, /* ato.br */ { 18, 0, 0, 1 }, /* b.br */ - { 980, 0, 0, 1 }, /* bio.br */ - { 1034, 0, 0, 1 }, /* blog.br */ - { 5319, 0, 0, 1 }, /* bmd.br */ - { 4199, 0, 0, 1 }, /* cim.br */ - { 5832, 0, 0, 1 }, /* cng.br */ - { 11421, 0, 0, 1 }, /* cnt.br */ - { 1913, 3672, 1, 1 }, /* com.br */ - { 2047, 0, 0, 1 }, /* coop.br */ - { 1866, 0, 0, 1 }, /* ecn.br */ - { 2614, 0, 0, 1 }, /* eco.br */ - { 2624, 0, 0, 1 }, /* edu.br */ - { 5593, 0, 0, 1 }, /* emp.br */ - { 11645, 0, 0, 1 }, /* eng.br */ - { 11649, 0, 0, 1 }, /* esp.br */ - { 7728, 0, 0, 1 }, /* etc.br */ - { 11655, 0, 0, 1 }, /* eti.br */ + { 11678, 0, 0, 1 }, /* belem.br */ + { 981, 0, 0, 1 }, /* bio.br */ + { 1035, 0, 0, 1 }, /* blog.br */ + { 5298, 0, 0, 1 }, /* bmd.br */ + { 4174, 0, 0, 1 }, /* cim.br */ + { 5804, 0, 0, 1 }, /* cng.br */ + { 11446, 0, 0, 1 }, /* cnt.br */ + { 1914, 3838, 1, 1 }, /* com.br */ + { 2048, 0, 0, 1 }, /* coop.br */ + { 11684, 0, 0, 1 }, /* cri.br */ + { 11688, 0, 0, 1 }, /* def.br */ + { 1867, 0, 0, 1 }, /* ecn.br */ + { 2613, 0, 0, 1 }, /* eco.br */ + { 2623, 0, 0, 1 }, /* edu.br */ + { 5572, 0, 0, 1 }, /* emp.br */ + { 11692, 0, 0, 1 }, /* eng.br */ + { 11696, 0, 0, 1 }, /* esp.br */ + { 7726, 0, 0, 1 }, /* etc.br */ + { 11702, 0, 0, 1 }, /* eti.br */ { 493, 0, 0, 1 }, /* far.br */ - { 11659, 0, 0, 1 }, /* flog.br */ - { 3160, 0, 0, 1 }, /* fm.br */ - { 11664, 0, 0, 1 }, /* fnd.br */ - { 11668, 0, 0, 1 }, /* fot.br */ - { 7448, 0, 0, 1 }, /* fst.br */ - { 11469, 0, 0, 1 }, /* g12.br */ - { 3485, 0, 0, 1 }, /* ggf.br */ - { 3686, 0, 0, 1 }, /* gov.br */ - { 11672, 0, 0, 1 }, /* imb.br */ - { 11676, 0, 0, 1 }, /* ind.br */ - { 5824, 0, 0, 1 }, /* inf.br */ - { 11389, 0, 0, 1 }, /* jor.br */ - { 8154, 0, 0, 1 }, /* jus.br */ - { 2646, 3791, 27, 1 }, /* leg.br */ - { 11680, 0, 0, 1 }, /* lel.br */ - { 4206, 0, 0, 1 }, /* mat.br */ - { 1858, 0, 0, 1 }, /* med.br */ - { 4195, 0, 0, 1 }, /* mil.br */ - { 1374, 0, 0, 1 }, /* mp.br */ - { 11684, 0, 0, 1 }, /* mus.br */ - { 4185, 0, 0, 1 }, /* net.br */ - { 5998, 3687, 1, 0 }, /* nom.br */ - { 11688, 0, 0, 1 }, /* not.br */ - { 7978, 0, 0, 1 }, /* ntr.br */ - { 2482, 0, 0, 1 }, /* odo.br */ - { 6070, 0, 0, 1 }, /* org.br */ - { 6210, 0, 0, 1 }, /* ppg.br */ - { 6427, 0, 0, 1 }, /* pro.br */ - { 6998, 0, 0, 1 }, /* psc.br */ - { 7274, 0, 0, 1 }, /* psi.br */ - { 7326, 0, 0, 1 }, /* qsl.br */ - { 6571, 0, 0, 1 }, /* radio.br */ - { 2608, 0, 0, 1 }, /* rec.br */ - { 11692, 0, 0, 1 }, /* slg.br */ - { 11696, 0, 0, 1 }, /* srv.br */ - { 7723, 0, 0, 1 }, /* taxi.br */ - { 11700, 0, 0, 1 }, /* teo.br */ - { 11704, 0, 0, 1 }, /* tmp.br */ - { 11708, 0, 0, 1 }, /* trd.br */ - { 11335, 0, 0, 1 }, /* tur.br */ - { 2546, 0, 0, 1 }, /* tv.br */ - { 8229, 0, 0, 1 }, /* vet.br */ - { 11712, 0, 0, 1 }, /* vlog.br */ - { 8547, 0, 0, 1 }, /* wiki.br */ - { 11717, 0, 0, 1 }, /* zlg.br */ - { 1913, 3672, 1, 1 }, /* com.by */ - { 3686, 0, 0, 1 }, /* gov.by */ - { 4195, 0, 0, 1 }, /* mil.by */ - { 6450, 0, 0, 1 }, /* of.by */ - { 11844, 3687, 1, 0 }, /* magentosite.cloud */ - { 11856, 0, 0, 1 }, /* myfusion.cloud */ - { 11865, 3687, 1, 0 }, /* statics.cloud */ + { 11706, 0, 0, 1 }, /* flog.br */ + { 11711, 0, 0, 1 }, /* floripa.br */ + { 3145, 0, 0, 1 }, /* fm.br */ + { 11719, 0, 0, 1 }, /* fnd.br */ + { 11723, 0, 0, 1 }, /* fot.br */ + { 7446, 0, 0, 1 }, /* fst.br */ + { 11494, 0, 0, 1 }, /* g12.br */ + { 3470, 0, 0, 1 }, /* ggf.br */ + { 3671, 3958, 27, 1 }, /* gov.br */ + { 11727, 0, 0, 1 }, /* imb.br */ + { 11731, 0, 0, 1 }, /* ind.br */ + { 5796, 0, 0, 1 }, /* inf.br */ + { 11735, 0, 0, 1 }, /* jampa.br */ + { 11414, 0, 0, 1 }, /* jor.br */ + { 8152, 0, 0, 1 }, /* jus.br */ + { 2645, 3958, 27, 1 }, /* leg.br */ + { 11741, 0, 0, 1 }, /* lel.br */ + { 4181, 0, 0, 1 }, /* mat.br */ + { 1859, 0, 0, 1 }, /* med.br */ + { 4170, 0, 0, 1 }, /* mil.br */ + { 1375, 0, 0, 1 }, /* mp.br */ + { 11745, 0, 0, 1 }, /* mus.br */ + { 5737, 0, 0, 1 }, /* net.br */ + { 5970, 3853, 1, 0 }, /* nom.br */ + { 11749, 0, 0, 1 }, /* not.br */ + { 7976, 0, 0, 1 }, /* ntr.br */ + { 2485, 0, 0, 1 }, /* odo.br */ + { 6053, 0, 0, 1 }, /* org.br */ + { 11762, 0, 0, 1 }, /* poa.br */ + { 6193, 0, 0, 1 }, /* ppg.br */ + { 6410, 0, 0, 1 }, /* pro.br */ + { 6987, 0, 0, 1 }, /* psc.br */ + { 7263, 0, 0, 1 }, /* psi.br */ + { 7315, 0, 0, 1 }, /* qsl.br */ + { 6554, 0, 0, 1 }, /* radio.br */ + { 11769, 0, 0, 1 }, /* rec.br */ + { 11773, 0, 0, 1 }, /* recife.br */ + { 11780, 0, 0, 1 }, /* slg.br */ + { 11784, 0, 0, 1 }, /* srv.br */ + { 7721, 0, 0, 1 }, /* taxi.br */ + { 11788, 0, 0, 1 }, /* teo.br */ + { 11792, 0, 0, 1 }, /* tmp.br */ + { 11796, 0, 0, 1 }, /* trd.br */ + { 11321, 0, 0, 1 }, /* tur.br */ + { 2549, 0, 0, 1 }, /* tv.br */ + { 8222, 0, 0, 1 }, /* vet.br */ + { 11800, 0, 0, 1 }, /* vix.br */ + { 11804, 0, 0, 1 }, /* vlog.br */ + { 8540, 0, 0, 1 }, /* wiki.br */ + { 11809, 0, 0, 1 }, /* zlg.br */ + { 1914, 3838, 1, 1 }, /* com.by */ + { 3671, 0, 0, 1 }, /* gov.by */ + { 4170, 0, 0, 1 }, /* mil.by */ + { 6433, 0, 0, 1 }, /* of.by */ + { 499, 0, 0, 1 }, /* ab.ca */ + { 2381, 3853, 1, 0 }, /* awdev.ca */ + { 35, 0, 0, 1 }, /* bc.ca */ + { 10645, 0, 0, 1 }, /* blogspot.ca */ + { 113, 0, 0, 1 }, /* co.ca */ + { 11838, 0, 0, 1 }, /* gc.ca */ + { 11728, 0, 0, 1 }, /* mb.ca */ + { 11843, 0, 0, 1 }, /* nb.ca */ + { 5797, 0, 0, 1 }, /* nf.ca */ + { 1072, 0, 0, 1 }, /* nl.ca */ + { 11621, 0, 0, 1 }, /* no-ip.ca */ + { 786, 0, 0, 1 }, /* ns.ca */ + { 97, 0, 0, 1 }, /* nt.ca */ + { 5351, 0, 0, 1 }, /* nu.ca */ + { 592, 0, 0, 1 }, /* on.ca */ + { 3724, 0, 0, 1 }, /* pe.ca */ + { 11524, 0, 0, 1 }, /* qc.ca */ + { 7301, 0, 0, 1 }, /* sk.ca */ + { 11533, 0, 0, 1 }, /* yk.ca */ + { 11951, 3853, 1, 0 }, /* magentosite.cloud */ + { 11963, 0, 0, 1 }, /* myfusion.cloud */ + { 11972, 3853, 1, 0 }, /* sensiosite.cloud */ + { 11983, 3853, 1, 0 }, /* statics.cloud */ + { 11991, 0, 0, 1 }, /* trafficplex.cloud */ + { 12003, 0, 0, 1 }, /* vapor.cloud */ { 62, 0, 0, 1 }, /* ac.cn */ - { 11875, 0, 0, 1 }, /* ah.cn */ - { 989, 0, 0, 1 }, /* bj.cn */ - { 1913, 1716, 1, 1 }, /* com.cn */ - { 11509, 0, 0, 1 }, /* cq.cn */ - { 2624, 0, 0, 1 }, /* edu.cn */ - { 3112, 0, 0, 1 }, /* fj.cn */ - { 3449, 0, 0, 1 }, /* gd.cn */ - { 3686, 0, 0, 1 }, /* gov.cn */ - { 3760, 0, 0, 1 }, /* gs.cn */ - { 11515, 0, 0, 1 }, /* gx.cn */ - { 11878, 0, 0, 1 }, /* gz.cn */ - { 2515, 0, 0, 1 }, /* ha.cn */ - { 11491, 0, 0, 1 }, /* hb.cn */ - { 11572, 0, 0, 1 }, /* he.cn */ + { 12011, 0, 0, 1 }, /* ah.cn */ + { 990, 0, 0, 1 }, /* bj.cn */ + { 1914, 1747, 1, 1 }, /* com.cn */ + { 11539, 0, 0, 1 }, /* cq.cn */ + { 2623, 0, 0, 1 }, /* edu.cn */ + { 3097, 0, 0, 1 }, /* fj.cn */ + { 3434, 0, 0, 1 }, /* gd.cn */ + { 3671, 0, 0, 1 }, /* gov.cn */ + { 3745, 0, 0, 1 }, /* gs.cn */ + { 11548, 0, 0, 1 }, /* gx.cn */ + { 12014, 0, 0, 1 }, /* gz.cn */ + { 2518, 0, 0, 1 }, /* ha.cn */ + { 11515, 0, 0, 1 }, /* hb.cn */ + { 11605, 0, 0, 1 }, /* he.cn */ { 512, 0, 0, 1 }, /* hi.cn */ - { 3940, 0, 0, 1 }, /* hk.cn */ - { 2385, 0, 0, 1 }, /* hl.cn */ - { 3954, 0, 0, 1 }, /* hn.cn */ - { 11506, 0, 0, 1 }, /* jl.cn */ - { 11512, 0, 0, 1 }, /* js.cn */ - { 7891, 0, 0, 1 }, /* jx.cn */ - { 4639, 0, 0, 1 }, /* ln.cn */ - { 4195, 0, 0, 1 }, /* mil.cn */ - { 3600, 0, 0, 1 }, /* mo.cn */ - { 4185, 0, 0, 1 }, /* net.cn */ - { 11902, 0, 0, 1 }, /* nm.cn */ - { 11907, 0, 0, 1 }, /* nx.cn */ - { 6070, 0, 0, 1 }, /* org.cn */ - { 11500, 0, 0, 1 }, /* qh.cn */ - { 2158, 0, 0, 1 }, /* sc.cn */ - { 5381, 0, 0, 1 }, /* sd.cn */ - { 1510, 0, 0, 1 }, /* sh.cn */ - { 7341, 0, 0, 1 }, /* sn.cn */ - { 7625, 0, 0, 1 }, /* sx.cn */ - { 7880, 0, 0, 1 }, /* tj.cn */ - { 8083, 0, 0, 1 }, /* tw.cn */ - { 11910, 0, 0, 1 }, /* xj.cn */ - { 8851, 0, 0, 1 }, /* xn--55qx5d.cn */ - { 9459, 0, 0, 1 }, /* xn--io0a7i.cn */ - { 11913, 0, 0, 1 }, /* xn--od0alg.cn */ - { 11924, 0, 0, 1 }, /* xz.cn */ - { 11930, 0, 0, 1 }, /* yn.cn */ - { 11933, 0, 0, 1 }, /* zj.cn */ - { 652, 1717, 3, 0 }, /* amazonaws.com.cn */ - { 11936, 3878, 2, 0 }, /* cn-north-1.amazonaws.com.cn */ - { 11947, 3687, 1, 0 }, /* compute.amazonaws.com.cn */ - { 11955, 3687, 1, 0 }, /* elb.amazonaws.com.cn */ - { 6175, 0, 0, 1 }, /* arts.co */ - { 1913, 3672, 1, 1 }, /* com.co */ - { 2624, 0, 0, 1 }, /* edu.co */ - { 11959, 0, 0, 1 }, /* firm.co */ - { 3686, 0, 0, 1 }, /* gov.co */ - { 3167, 0, 0, 1 }, /* info.co */ - { 3632, 0, 0, 1 }, /* int.co */ - { 4195, 0, 0, 1 }, /* mil.co */ - { 4185, 0, 0, 1 }, /* net.co */ - { 5998, 0, 0, 1 }, /* nom.co */ - { 6070, 0, 0, 1 }, /* org.co */ - { 2608, 0, 0, 1 }, /* rec.co */ - { 11967, 0, 0, 1 }, /* web.co */ - { 5439, 3687, 1, 0 }, /* 0emm.com */ - { 11971, 0, 0, 1 }, /* 1kapp.com */ - { 11977, 0, 0, 1 }, /* 3utilities.com */ - { 11996, 0, 0, 1 }, /* 4u.com */ + { 3922, 0, 0, 1 }, /* hk.cn */ + { 2388, 0, 0, 1 }, /* hl.cn */ + { 3936, 0, 0, 1 }, /* hn.cn */ + { 11536, 0, 0, 1 }, /* jl.cn */ + { 11545, 0, 0, 1 }, /* js.cn */ + { 7889, 0, 0, 1 }, /* jx.cn */ + { 4618, 0, 0, 1 }, /* ln.cn */ + { 4170, 0, 0, 1 }, /* mil.cn */ + { 3585, 0, 0, 1 }, /* mo.cn */ + { 5737, 0, 0, 1 }, /* net.cn */ + { 12038, 0, 0, 1 }, /* nm.cn */ + { 12043, 0, 0, 1 }, /* nx.cn */ + { 6053, 0, 0, 1 }, /* org.cn */ + { 11530, 0, 0, 1 }, /* qh.cn */ + { 2159, 0, 0, 1 }, /* sc.cn */ + { 5360, 0, 0, 1 }, /* sd.cn */ + { 1511, 0, 0, 1 }, /* sh.cn */ + { 7330, 0, 0, 1 }, /* sn.cn */ + { 7623, 0, 0, 1 }, /* sx.cn */ + { 7878, 0, 0, 1 }, /* tj.cn */ + { 8081, 0, 0, 1 }, /* tw.cn */ + { 12046, 0, 0, 1 }, /* xj.cn */ + { 8830, 0, 0, 1 }, /* xn--55qx5d.cn */ + { 9438, 0, 0, 1 }, /* xn--io0a7i.cn */ + { 12049, 0, 0, 1 }, /* xn--od0alg.cn */ + { 12060, 0, 0, 1 }, /* xz.cn */ + { 12066, 0, 0, 1 }, /* yn.cn */ + { 12069, 0, 0, 1 }, /* zj.cn */ + { 652, 1748, 3, 0 }, /* amazonaws.com.cn */ + { 12072, 4033, 2, 0 }, /* cn-north-1.amazonaws.com.cn */ + { 12083, 3853, 1, 0 }, /* compute.amazonaws.com.cn */ + { 12091, 3853, 1, 0 }, /* elb.amazonaws.com.cn */ + { 6158, 0, 0, 1 }, /* arts.co */ + { 1914, 3838, 1, 1 }, /* com.co */ + { 2623, 0, 0, 1 }, /* edu.co */ + { 12095, 0, 0, 1 }, /* firm.co */ + { 3671, 0, 0, 1 }, /* gov.co */ + { 3152, 0, 0, 1 }, /* info.co */ + { 3617, 0, 0, 1 }, /* int.co */ + { 4170, 0, 0, 1 }, /* mil.co */ + { 5737, 0, 0, 1 }, /* net.co */ + { 12100, 0, 0, 1 }, /* nodum.co */ + { 5970, 0, 0, 1 }, /* nom.co */ + { 6053, 0, 0, 1 }, /* org.co */ + { 11769, 0, 0, 1 }, /* rec.co */ + { 12109, 0, 0, 1 }, /* web.co */ + { 5418, 3853, 1, 0 }, /* 0emm.com */ + { 12113, 0, 0, 1 }, /* 1kapp.com */ + { 12119, 0, 0, 1 }, /* 3utilities.com */ + { 12138, 0, 0, 1 }, /* 4u.com */ { 217, 0, 0, 1 }, /* africa.com */ - { 11999, 0, 0, 1 }, /* alpha-myqnapcloud.com */ - { 652, 2006, 38, 1 }, /* amazonaws.com */ - { 12017, 0, 0, 1 }, /* appchizi.com */ - { 12026, 0, 0, 1 }, /* applinzi.com */ - { 7415, 0, 0, 1 }, /* appspot.com */ + { 12141, 0, 0, 1 }, /* alpha-myqnapcloud.com */ + { 652, 2045, 40, 0 }, /* amazonaws.com */ + { 12159, 0, 0, 1 }, /* appchizi.com */ + { 12168, 0, 0, 1 }, /* applinzi.com */ + { 7413, 0, 0, 1 }, /* appspot.com */ { 494, 0, 0, 1 }, /* ar.com */ - { 12035, 0, 0, 1 }, /* betainabox.com */ - { 12046, 0, 0, 1 }, /* blogdns.com */ - { 10666, 0, 0, 1 }, /* blogspot.com */ - { 12054, 0, 0, 1 }, /* blogsyte.com */ - { 12063, 0, 0, 1 }, /* bloxcms.com */ - { 12071, 3881, 2, 1 }, /* bounty-full.com */ - { 1182, 0, 0, 1 }, /* br.com */ - { 12083, 0, 0, 1 }, /* cechire.com */ - { 12091, 0, 0, 1 }, /* ciscofreak.com */ - { 12102, 0, 0, 1 }, /* cloudcontrolapp.com */ - { 12118, 0, 0, 1 }, /* cloudcontrolled.com */ + { 6001, 0, 0, 1 }, /* barsyonline.com */ + { 12177, 0, 0, 1 }, /* betainabox.com */ + { 12188, 0, 0, 1 }, /* blogdns.com */ + { 10645, 0, 0, 1 }, /* blogspot.com */ + { 12196, 0, 0, 1 }, /* blogsyte.com */ + { 12205, 0, 0, 1 }, /* bloxcms.com */ + { 12213, 4036, 2, 1 }, /* bounty-full.com */ + { 12225, 0, 0, 1 }, /* bplaced.com */ + { 1183, 0, 0, 1 }, /* br.com */ + { 12233, 0, 0, 1 }, /* cechire.com */ + { 12241, 0, 0, 1 }, /* ciscofreak.com */ + { 12252, 0, 0, 1 }, /* cloudcontrolapp.com */ + { 12268, 0, 0, 1 }, /* cloudcontrolled.com */ { 842, 0, 0, 1 }, /* cn.com */ { 113, 0, 0, 1 }, /* co.com */ - { 12134, 0, 0, 1 }, /* codespot.com */ - { 12143, 0, 0, 1 }, /* damnserver.com */ - { 12154, 0, 0, 1 }, /* ddnsking.com */ - { 2276, 0, 0, 1 }, /* de.com */ - { 12163, 0, 0, 1 }, /* dev-myqnapcloud.com */ - { 6821, 0, 0, 1 }, /* ditchyourip.com */ - { 12179, 0, 0, 1 }, /* dnsalias.com */ - { 12188, 0, 0, 1 }, /* dnsdojo.com */ - { 12196, 0, 0, 1 }, /* dnsiskinky.com */ - { 12207, 0, 0, 1 }, /* doesntexist.com */ - { 12219, 0, 0, 1 }, /* dontexist.com */ - { 12229, 0, 0, 1 }, /* doomdns.com */ - { 12237, 0, 0, 1 }, /* dreamhosters.com */ - { 12250, 0, 0, 1 }, /* dsmynas.com */ - { 12258, 0, 0, 1 }, /* dyn-o-saur.com */ - { 12269, 0, 0, 1 }, /* dynalias.com */ - { 12278, 0, 0, 1 }, /* dyndns-at-home.com */ - { 12293, 0, 0, 1 }, /* dyndns-at-work.com */ - { 12308, 0, 0, 1 }, /* dyndns-blog.com */ - { 3248, 0, 0, 1 }, /* dyndns-free.com */ - { 12320, 0, 0, 1 }, /* dyndns-home.com */ - { 12332, 0, 0, 1 }, /* dyndns-ip.com */ - { 12342, 0, 0, 1 }, /* dyndns-mail.com */ - { 12354, 0, 0, 1 }, /* dyndns-office.com */ - { 12368, 0, 0, 1 }, /* dyndns-pics.com */ - { 12380, 0, 0, 1 }, /* dyndns-remote.com */ - { 12394, 0, 0, 1 }, /* dyndns-server.com */ - { 12408, 0, 0, 1 }, /* dyndns-web.com */ - { 8540, 0, 0, 1 }, /* dyndns-wiki.com */ - { 12419, 0, 0, 1 }, /* dyndns-work.com */ - { 12431, 0, 0, 1 }, /* dynns.com */ - { 7668, 3687, 1, 0 }, /* elasticbeanstalk.com */ - { 5172, 0, 0, 1 }, /* est-a-la-maison.com */ - { 12437, 0, 0, 1 }, /* est-a-la-masion.com */ - { 12453, 0, 0, 1 }, /* est-le-patron.com */ - { 12467, 0, 0, 1 }, /* est-mon-blogueur.com */ - { 2798, 0, 0, 1 }, /* eu.com */ - { 12484, 3883, 4, 1 }, /* evennode.com */ - { 12493, 0, 0, 1 }, /* familyds.com */ - { 12502, 3887, 1, 1 }, /* fbsbx.com */ - { 12508, 0, 0, 1 }, /* firebaseapp.com */ - { 12520, 0, 0, 1 }, /* firewall-gateway.com */ - { 12537, 0, 0, 1 }, /* flynnhub.com */ - { 12546, 0, 0, 1 }, /* freebox-os.com */ - { 12557, 0, 0, 1 }, /* freeboxos.com */ - { 12567, 0, 0, 1 }, /* from-ak.com */ - { 12575, 0, 0, 1 }, /* from-al.com */ - { 12583, 0, 0, 1 }, /* from-ar.com */ - { 12591, 0, 0, 1 }, /* from-ca.com */ - { 12599, 0, 0, 1 }, /* from-ct.com */ - { 12607, 0, 0, 1 }, /* from-dc.com */ - { 12615, 0, 0, 1 }, /* from-de.com */ - { 12623, 0, 0, 1 }, /* from-fl.com */ - { 12631, 0, 0, 1 }, /* from-ga.com */ - { 12639, 0, 0, 1 }, /* from-hi.com */ - { 12647, 0, 0, 1 }, /* from-ia.com */ - { 12655, 0, 0, 1 }, /* from-id.com */ - { 12663, 0, 0, 1 }, /* from-il.com */ - { 12671, 0, 0, 1 }, /* from-in.com */ - { 12679, 0, 0, 1 }, /* from-ks.com */ - { 12687, 0, 0, 1 }, /* from-ky.com */ - { 12695, 0, 0, 1 }, /* from-ma.com */ - { 12703, 0, 0, 1 }, /* from-md.com */ - { 12711, 0, 0, 1 }, /* from-mi.com */ - { 12719, 0, 0, 1 }, /* from-mn.com */ - { 12727, 0, 0, 1 }, /* from-mo.com */ - { 12735, 0, 0, 1 }, /* from-ms.com */ - { 5604, 0, 0, 1 }, /* from-mt.com */ - { 12743, 0, 0, 1 }, /* from-nc.com */ - { 12751, 0, 0, 1 }, /* from-nd.com */ - { 12759, 0, 0, 1 }, /* from-ne.com */ - { 12767, 0, 0, 1 }, /* from-nh.com */ - { 12775, 0, 0, 1 }, /* from-nj.com */ - { 11897, 0, 0, 1 }, /* from-nm.com */ - { 12783, 0, 0, 1 }, /* from-nv.com */ - { 12791, 0, 0, 1 }, /* from-oh.com */ - { 12799, 0, 0, 1 }, /* from-ok.com */ - { 12807, 0, 0, 1 }, /* from-or.com */ - { 12815, 0, 0, 1 }, /* from-pa.com */ - { 6397, 0, 0, 1 }, /* from-pr.com */ - { 12823, 0, 0, 1 }, /* from-ri.com */ - { 12831, 0, 0, 1 }, /* from-sc.com */ - { 7101, 0, 0, 1 }, /* from-sd.com */ - { 12839, 0, 0, 1 }, /* from-tn.com */ - { 12847, 0, 0, 1 }, /* from-tx.com */ - { 12855, 0, 0, 1 }, /* from-ut.com */ - { 12863, 0, 0, 1 }, /* from-va.com */ - { 12871, 0, 0, 1 }, /* from-vt.com */ - { 12879, 0, 0, 1 }, /* from-wa.com */ - { 12887, 0, 0, 1 }, /* from-wi.com */ - { 12895, 0, 0, 1 }, /* from-wv.com */ - { 12903, 0, 0, 1 }, /* from-wy.com */ - { 3441, 0, 0, 1 }, /* gb.com */ - { 12911, 0, 0, 1 }, /* geekgalaxy.com */ - { 12922, 0, 0, 1 }, /* getmyip.com */ - { 12930, 2065, 3, 1 }, /* githubcloud.com */ - { 12942, 3687, 1, 0 }, /* githubcloudusercontent.com */ - { 12965, 0, 0, 1 }, /* githubusercontent.com */ - { 12983, 0, 0, 1 }, /* googleapis.com */ - { 12994, 0, 0, 1 }, /* googlecode.com */ - { 11809, 0, 0, 1 }, /* gotdns.com */ - { 13005, 0, 0, 1 }, /* gotpantheon.com */ - { 3697, 0, 0, 1 }, /* gr.com */ - { 13017, 0, 0, 1 }, /* health-carereform.com */ - { 13035, 0, 0, 1 }, /* herokuapp.com */ - { 13045, 0, 0, 1 }, /* herokussl.com */ - { 3940, 0, 0, 1 }, /* hk.com */ - { 13055, 0, 0, 1 }, /* hobby-site.com */ - { 13066, 0, 0, 1 }, /* homelinux.com */ - { 13076, 0, 0, 1 }, /* homesecuritymac.com */ - { 13092, 0, 0, 1 }, /* homesecuritypc.com */ - { 13107, 0, 0, 1 }, /* homeunix.com */ - { 4138, 0, 0, 1 }, /* hu.com */ - { 13116, 0, 0, 1 }, /* iamallama.com */ - { 13126, 0, 0, 1 }, /* is-a-anarchist.com */ - { 13141, 0, 0, 1 }, /* is-a-blogger.com */ - { 13154, 0, 0, 1 }, /* is-a-bookkeeper.com */ - { 13170, 0, 0, 1 }, /* is-a-bulls-fan.com */ - { 13185, 0, 0, 1 }, /* is-a-caterer.com */ - { 13198, 0, 0, 1 }, /* is-a-chef.com */ - { 13208, 0, 0, 1 }, /* is-a-conservative.com */ - { 13226, 0, 0, 1 }, /* is-a-cpa.com */ - { 13235, 0, 0, 1 }, /* is-a-cubicle-slave.com */ - { 2333, 0, 0, 1 }, /* is-a-democrat.com */ - { 13254, 0, 0, 1 }, /* is-a-designer.com */ - { 2491, 0, 0, 1 }, /* is-a-doctor.com */ - { 13268, 0, 0, 1 }, /* is-a-financialadvisor.com */ - { 13290, 0, 0, 1 }, /* is-a-geek.com */ - { 3725, 0, 0, 1 }, /* is-a-green.com */ - { 3808, 0, 0, 1 }, /* is-a-guru.com */ - { 13300, 0, 0, 1 }, /* is-a-hard-worker.com */ - { 13317, 0, 0, 1 }, /* is-a-hunter.com */ - { 13329, 0, 0, 1 }, /* is-a-landscaper.com */ - { 4844, 0, 0, 1 }, /* is-a-lawyer.com */ - { 13345, 0, 0, 1 }, /* is-a-liberal.com */ - { 13358, 0, 0, 1 }, /* is-a-libertarian.com */ - { 13375, 0, 0, 1 }, /* is-a-llama.com */ - { 13386, 0, 0, 1 }, /* is-a-musician.com */ - { 13400, 0, 0, 1 }, /* is-a-nascarfan.com */ - { 13415, 0, 0, 1 }, /* is-a-nurse.com */ - { 13426, 0, 0, 1 }, /* is-a-painter.com */ - { 11280, 0, 0, 1 }, /* is-a-personaltrainer.com */ - { 13439, 0, 0, 1 }, /* is-a-photographer.com */ - { 13457, 0, 0, 1 }, /* is-a-player.com */ - { 6718, 0, 0, 1 }, /* is-a-republican.com */ - { 13469, 0, 0, 1 }, /* is-a-rockstar.com */ - { 13483, 0, 0, 1 }, /* is-a-socialist.com */ - { 11260, 0, 0, 1 }, /* is-a-student.com */ - { 13498, 0, 0, 1 }, /* is-a-teacher.com */ - { 13511, 0, 0, 1 }, /* is-a-techie.com */ - { 13523, 0, 0, 1 }, /* is-a-therapist.com */ + { 12284, 0, 0, 1 }, /* codespot.com */ + { 12293, 0, 0, 1 }, /* damnserver.com */ + { 12304, 0, 0, 1 }, /* ddnsking.com */ + { 2277, 0, 0, 1 }, /* de.com */ + { 12313, 0, 0, 1 }, /* dev-myqnapcloud.com */ + { 6804, 0, 0, 1 }, /* ditchyourip.com */ + { 12329, 0, 0, 1 }, /* dnsalias.com */ + { 12338, 0, 0, 1 }, /* dnsdojo.com */ + { 12346, 0, 0, 1 }, /* dnsiskinky.com */ + { 12357, 0, 0, 1 }, /* doesntexist.com */ + { 12369, 0, 0, 1 }, /* dontexist.com */ + { 12379, 0, 0, 1 }, /* doomdns.com */ + { 12387, 0, 0, 1 }, /* drayddns.com */ + { 12396, 0, 0, 1 }, /* dreamhosters.com */ + { 12409, 0, 0, 1 }, /* dsmynas.com */ + { 12417, 0, 0, 1 }, /* dyn-o-saur.com */ + { 12428, 0, 0, 1 }, /* dynalias.com */ + { 12437, 0, 0, 1 }, /* dyndns-at-home.com */ + { 12452, 0, 0, 1 }, /* dyndns-at-work.com */ + { 12467, 0, 0, 1 }, /* dyndns-blog.com */ + { 3233, 0, 0, 1 }, /* dyndns-free.com */ + { 12479, 0, 0, 1 }, /* dyndns-home.com */ + { 12491, 0, 0, 1 }, /* dyndns-ip.com */ + { 12501, 0, 0, 1 }, /* dyndns-mail.com */ + { 12513, 0, 0, 1 }, /* dyndns-office.com */ + { 12527, 0, 0, 1 }, /* dyndns-pics.com */ + { 12539, 0, 0, 1 }, /* dyndns-remote.com */ + { 12553, 0, 0, 1 }, /* dyndns-server.com */ + { 12567, 0, 0, 1 }, /* dyndns-web.com */ + { 8533, 0, 0, 1 }, /* dyndns-wiki.com */ + { 12578, 0, 0, 1 }, /* dyndns-work.com */ + { 12590, 0, 0, 1 }, /* dynns.com */ + { 7666, 3853, 1, 0 }, /* elasticbeanstalk.com */ + { 5151, 0, 0, 1 }, /* est-a-la-maison.com */ + { 12596, 0, 0, 1 }, /* est-a-la-masion.com */ + { 12612, 0, 0, 1 }, /* est-le-patron.com */ + { 12626, 0, 0, 1 }, /* est-mon-blogueur.com */ + { 2797, 0, 0, 1 }, /* eu.com */ + { 12643, 4038, 6, 0 }, /* evennode.com */ + { 12652, 0, 0, 1 }, /* familyds.com */ + { 12661, 4044, 1, 0 }, /* fbsbx.com */ + { 12667, 0, 0, 1 }, /* firebaseapp.com */ + { 12679, 0, 0, 1 }, /* firewall-gateway.com */ + { 12696, 0, 0, 1 }, /* flynnhub.com */ + { 12705, 0, 0, 1 }, /* freebox-os.com */ + { 12716, 0, 0, 1 }, /* freeboxos.com */ + { 12726, 0, 0, 1 }, /* from-ak.com */ + { 12734, 0, 0, 1 }, /* from-al.com */ + { 12742, 0, 0, 1 }, /* from-ar.com */ + { 12750, 0, 0, 1 }, /* from-ca.com */ + { 12758, 0, 0, 1 }, /* from-ct.com */ + { 12766, 0, 0, 1 }, /* from-dc.com */ + { 12774, 0, 0, 1 }, /* from-de.com */ + { 12782, 0, 0, 1 }, /* from-fl.com */ + { 12790, 0, 0, 1 }, /* from-ga.com */ + { 12798, 0, 0, 1 }, /* from-hi.com */ + { 12806, 0, 0, 1 }, /* from-ia.com */ + { 12814, 0, 0, 1 }, /* from-id.com */ + { 12822, 0, 0, 1 }, /* from-il.com */ + { 12830, 0, 0, 1 }, /* from-in.com */ + { 12838, 0, 0, 1 }, /* from-ks.com */ + { 12846, 0, 0, 1 }, /* from-ky.com */ + { 12854, 0, 0, 1 }, /* from-ma.com */ + { 12862, 0, 0, 1 }, /* from-md.com */ + { 12870, 0, 0, 1 }, /* from-mi.com */ + { 12878, 0, 0, 1 }, /* from-mn.com */ + { 12886, 0, 0, 1 }, /* from-mo.com */ + { 12894, 0, 0, 1 }, /* from-ms.com */ + { 5583, 0, 0, 1 }, /* from-mt.com */ + { 12902, 0, 0, 1 }, /* from-nc.com */ + { 12910, 0, 0, 1 }, /* from-nd.com */ + { 12918, 0, 0, 1 }, /* from-ne.com */ + { 12926, 0, 0, 1 }, /* from-nh.com */ + { 12934, 0, 0, 1 }, /* from-nj.com */ + { 12033, 0, 0, 1 }, /* from-nm.com */ + { 12942, 0, 0, 1 }, /* from-nv.com */ + { 12950, 0, 0, 1 }, /* from-oh.com */ + { 12958, 0, 0, 1 }, /* from-ok.com */ + { 12966, 0, 0, 1 }, /* from-or.com */ + { 12974, 0, 0, 1 }, /* from-pa.com */ + { 6380, 0, 0, 1 }, /* from-pr.com */ + { 12982, 0, 0, 1 }, /* from-ri.com */ + { 12990, 0, 0, 1 }, /* from-sc.com */ + { 7090, 0, 0, 1 }, /* from-sd.com */ + { 12998, 0, 0, 1 }, /* from-tn.com */ + { 13006, 0, 0, 1 }, /* from-tx.com */ + { 13014, 0, 0, 1 }, /* from-ut.com */ + { 13022, 0, 0, 1 }, /* from-va.com */ + { 13030, 0, 0, 1 }, /* from-vt.com */ + { 13038, 0, 0, 1 }, /* from-wa.com */ + { 13046, 0, 0, 1 }, /* from-wi.com */ + { 13054, 0, 0, 1 }, /* from-wv.com */ + { 13062, 0, 0, 1 }, /* from-wy.com */ + { 3426, 0, 0, 1 }, /* gb.com */ + { 13070, 0, 0, 1 }, /* geekgalaxy.com */ + { 13081, 0, 0, 1 }, /* getmyip.com */ + { 13089, 2109, 3, 1 }, /* githubcloud.com */ + { 13101, 3853, 1, 0 }, /* githubcloudusercontent.com */ + { 13124, 0, 0, 1 }, /* githubusercontent.com */ + { 13142, 0, 0, 1 }, /* googleapis.com */ + { 13153, 0, 0, 1 }, /* googlecode.com */ + { 11908, 0, 0, 1 }, /* gotdns.com */ + { 13164, 0, 0, 1 }, /* gotpantheon.com */ + { 3682, 0, 0, 1 }, /* gr.com */ + { 13176, 0, 0, 1 }, /* health-carereform.com */ + { 13194, 0, 0, 1 }, /* herokuapp.com */ + { 13204, 0, 0, 1 }, /* herokussl.com */ + { 3922, 0, 0, 1 }, /* hk.com */ + { 13214, 0, 0, 1 }, /* hobby-site.com */ + { 13225, 0, 0, 1 }, /* homelinux.com */ + { 13235, 0, 0, 1 }, /* homesecuritymac.com */ + { 13251, 0, 0, 1 }, /* homesecuritypc.com */ + { 13266, 0, 0, 1 }, /* homeunix.com */ + { 4119, 0, 0, 1 }, /* hu.com */ + { 13275, 0, 0, 1 }, /* iamallama.com */ + { 13285, 0, 0, 1 }, /* is-a-anarchist.com */ + { 13300, 0, 0, 1 }, /* is-a-blogger.com */ + { 13313, 0, 0, 1 }, /* is-a-bookkeeper.com */ + { 13329, 0, 0, 1 }, /* is-a-bulls-fan.com */ + { 13344, 0, 0, 1 }, /* is-a-caterer.com */ + { 13357, 0, 0, 1 }, /* is-a-chef.com */ + { 13367, 0, 0, 1 }, /* is-a-conservative.com */ + { 13385, 0, 0, 1 }, /* is-a-cpa.com */ + { 13394, 0, 0, 1 }, /* is-a-cubicle-slave.com */ + { 2334, 0, 0, 1 }, /* is-a-democrat.com */ + { 13413, 0, 0, 1 }, /* is-a-designer.com */ + { 2494, 0, 0, 1 }, /* is-a-doctor.com */ + { 13427, 0, 0, 1 }, /* is-a-financialadvisor.com */ + { 13449, 0, 0, 1 }, /* is-a-geek.com */ + { 3710, 0, 0, 1 }, /* is-a-green.com */ + { 3790, 0, 0, 1 }, /* is-a-guru.com */ + { 13459, 0, 0, 1 }, /* is-a-hard-worker.com */ + { 13476, 0, 0, 1 }, /* is-a-hunter.com */ + { 13488, 0, 0, 1 }, /* is-a-landscaper.com */ + { 4823, 0, 0, 1 }, /* is-a-lawyer.com */ + { 13504, 0, 0, 1 }, /* is-a-liberal.com */ + { 13517, 0, 0, 1 }, /* is-a-libertarian.com */ + { 13534, 0, 0, 1 }, /* is-a-llama.com */ + { 13545, 0, 0, 1 }, /* is-a-musician.com */ + { 13559, 0, 0, 1 }, /* is-a-nascarfan.com */ + { 13574, 0, 0, 1 }, /* is-a-nurse.com */ + { 13585, 0, 0, 1 }, /* is-a-painter.com */ + { 11259, 0, 0, 1 }, /* is-a-personaltrainer.com */ + { 13598, 0, 0, 1 }, /* is-a-photographer.com */ + { 13616, 0, 0, 1 }, /* is-a-player.com */ + { 6701, 0, 0, 1 }, /* is-a-republican.com */ + { 13628, 0, 0, 1 }, /* is-a-rockstar.com */ + { 13642, 0, 0, 1 }, /* is-a-socialist.com */ + { 11239, 0, 0, 1 }, /* is-a-student.com */ + { 13657, 0, 0, 1 }, /* is-a-teacher.com */ + { 13670, 0, 0, 1 }, /* is-a-techie.com */ + { 13682, 0, 0, 1 }, /* is-a-therapist.com */ { 83, 0, 0, 1 }, /* is-an-accountant.com */ { 128, 0, 0, 1 }, /* is-an-actor.com */ - { 13538, 0, 0, 1 }, /* is-an-actress.com */ - { 13552, 0, 0, 1 }, /* is-an-anarchist.com */ - { 13568, 0, 0, 1 }, /* is-an-artist.com */ - { 2670, 0, 0, 1 }, /* is-an-engineer.com */ - { 13581, 0, 0, 1 }, /* is-an-entertainer.com */ - { 13599, 0, 0, 1 }, /* is-certified.com */ - { 13612, 0, 0, 1 }, /* is-gone.com */ - { 13620, 0, 0, 1 }, /* is-into-anime.com */ - { 1470, 0, 0, 1 }, /* is-into-cars.com */ - { 13634, 0, 0, 1 }, /* is-into-cartoons.com */ - { 3397, 0, 0, 1 }, /* is-into-games.com */ - { 13651, 0, 0, 1 }, /* is-leet.com */ - { 13659, 0, 0, 1 }, /* is-not-certified.com */ - { 13676, 0, 0, 1 }, /* is-slick.com */ - { 13685, 0, 0, 1 }, /* is-uberleet.com */ - { 13697, 0, 0, 1 }, /* is-with-theband.com */ - { 13713, 0, 0, 1 }, /* isa-geek.com */ - { 13722, 0, 0, 1 }, /* isa-hockeynut.com */ - { 13736, 0, 0, 1 }, /* issmarterthanyou.com */ - { 13753, 2068, 1, 0 }, /* joyent.com */ - { 13760, 0, 0, 1 }, /* jpn.com */ - { 3123, 0, 0, 1 }, /* kr.com */ - { 13764, 0, 0, 1 }, /* likes-pie.com */ - { 13774, 0, 0, 1 }, /* likescandy.com */ - { 13785, 0, 0, 1 }, /* logoip.com */ - { 13792, 3888, 1, 1 }, /* meteorapp.com */ + { 13697, 0, 0, 1 }, /* is-an-actress.com */ + { 13711, 0, 0, 1 }, /* is-an-anarchist.com */ + { 13727, 0, 0, 1 }, /* is-an-artist.com */ + { 2669, 0, 0, 1 }, /* is-an-engineer.com */ + { 13740, 0, 0, 1 }, /* is-an-entertainer.com */ + { 13758, 0, 0, 1 }, /* is-certified.com */ + { 13771, 0, 0, 1 }, /* is-gone.com */ + { 13779, 0, 0, 1 }, /* is-into-anime.com */ + { 1471, 0, 0, 1 }, /* is-into-cars.com */ + { 13793, 0, 0, 1 }, /* is-into-cartoons.com */ + { 3382, 0, 0, 1 }, /* is-into-games.com */ + { 13810, 0, 0, 1 }, /* is-leet.com */ + { 13818, 0, 0, 1 }, /* is-not-certified.com */ + { 13835, 0, 0, 1 }, /* is-slick.com */ + { 13844, 0, 0, 1 }, /* is-uberleet.com */ + { 13856, 0, 0, 1 }, /* is-with-theband.com */ + { 13872, 0, 0, 1 }, /* isa-geek.com */ + { 13881, 0, 0, 1 }, /* isa-hockeynut.com */ + { 13895, 0, 0, 1 }, /* issmarterthanyou.com */ + { 13912, 0, 0, 1 }, /* jdevcloud.com */ + { 13922, 2112, 1, 0 }, /* joyent.com */ + { 13929, 0, 0, 1 }, /* jpn.com */ + { 3108, 0, 0, 1 }, /* kr.com */ + { 13933, 0, 0, 1 }, /* likes-pie.com */ + { 13943, 0, 0, 1 }, /* likescandy.com */ + { 13954, 0, 0, 1 }, /* logoip.com */ + { 13961, 4045, 1, 1 }, /* meteorapp.com */ { 396, 0, 0, 1 }, /* mex.com */ - { 2421, 0, 0, 1 }, /* myactivedirectory.com */ - { 13802, 0, 0, 1 }, /* myasustor.com */ - { 13812, 0, 0, 1 }, /* mydrobo.com */ - { 12005, 0, 0, 1 }, /* myqnapcloud.com */ - { 1347, 0, 0, 1 }, /* mysecuritycamera.com */ - { 13820, 0, 0, 1 }, /* myshopblocks.com */ - { 13833, 0, 0, 1 }, /* myvnc.com */ - { 13839, 0, 0, 1 }, /* neat-url.com */ - { 13848, 0, 0, 1 }, /* net-freaks.com */ - { 4048, 0, 0, 1 }, /* nfshost.com */ - { 1517, 0, 0, 1 }, /* no.com */ - { 13859, 0, 0, 1 }, /* on-aptible.com */ - { 13870, 0, 0, 1 }, /* onthewifi.com */ - { 13880, 0, 0, 1 }, /* operaunite.com */ - { 13891, 0, 0, 1 }, /* outsystemscloud.com */ - { 13907, 0, 0, 1 }, /* ownprovider.com */ - { 13919, 0, 0, 1 }, /* pagefrontapp.com */ - { 13932, 0, 0, 1 }, /* pagespeedmobilizer.com */ - { 13951, 0, 0, 1 }, /* pgfog.com */ - { 13957, 0, 0, 1 }, /* point2this.com */ - { 13968, 3889, 1, 1 }, /* prgmr.com */ - { 13974, 0, 0, 1 }, /* publishproxy.com */ - { 13987, 0, 0, 1 }, /* qa2.com */ - { 11494, 0, 0, 1 }, /* qc.com */ - { 13991, 0, 0, 1 }, /* quicksytes.com */ - { 14002, 0, 0, 1 }, /* rackmaze.com */ - { 14011, 0, 0, 1 }, /* remotewd.com */ - { 1837, 0, 0, 1 }, /* rhcloud.com */ - { 2190, 0, 0, 1 }, /* ru.com */ - { 1493, 0, 0, 1 }, /* sa.com */ - { 14020, 0, 0, 1 }, /* saves-the-whales.com */ - { 1498, 0, 0, 1 }, /* se.com */ - { 14037, 0, 0, 1 }, /* securitytactics.com */ - { 11594, 0, 0, 1 }, /* selfip.com */ - { 14053, 0, 0, 1 }, /* sells-for-less.com */ - { 14068, 0, 0, 1 }, /* sells-for-u.com */ - { 14080, 0, 0, 1 }, /* servebbs.com */ + { 2424, 0, 0, 1 }, /* myactivedirectory.com */ + { 13971, 0, 0, 1 }, /* myasustor.com */ + { 13981, 0, 0, 1 }, /* mydrobo.com */ + { 12147, 0, 0, 1 }, /* myqnapcloud.com */ + { 1348, 0, 0, 1 }, /* mysecuritycamera.com */ + { 13989, 0, 0, 1 }, /* myshopblocks.com */ + { 14002, 0, 0, 1 }, /* mytuleap.com */ + { 14011, 0, 0, 1 }, /* myvnc.com */ + { 14017, 0, 0, 1 }, /* neat-url.com */ + { 14026, 0, 0, 1 }, /* net-freaks.com */ + { 4030, 0, 0, 1 }, /* nfshost.com */ + { 1518, 0, 0, 1 }, /* no.com */ + { 14037, 0, 0, 1 }, /* on-aptible.com */ + { 14048, 0, 0, 1 }, /* onthewifi.com */ + { 14058, 0, 0, 1 }, /* operaunite.com */ + { 14069, 0, 0, 1 }, /* outsystemscloud.com */ + { 14085, 0, 0, 1 }, /* ownprovider.com */ + { 14097, 0, 0, 1 }, /* pagefrontapp.com */ + { 14110, 0, 0, 1 }, /* pagespeedmobilizer.com */ + { 14129, 0, 0, 1 }, /* pgfog.com */ + { 14135, 0, 0, 1 }, /* point2this.com */ + { 14146, 4046, 1, 0 }, /* prgmr.com */ + { 14152, 0, 0, 1 }, /* publishproxy.com */ + { 14165, 0, 0, 1 }, /* qa2.com */ + { 11524, 0, 0, 1 }, /* qc.com */ + { 14169, 0, 0, 1 }, /* quicksytes.com */ + { 14180, 3853, 1, 0 }, /* quipelements.com */ + { 14193, 0, 0, 1 }, /* rackmaze.com */ + { 14202, 0, 0, 1 }, /* remotewd.com */ + { 1838, 0, 0, 1 }, /* rhcloud.com */ + { 2191, 0, 0, 1 }, /* ru.com */ + { 1494, 0, 0, 1 }, /* sa.com */ + { 14211, 0, 0, 1 }, /* saves-the-whales.com */ + { 1499, 0, 0, 1 }, /* se.com */ + { 14228, 0, 0, 1 }, /* securitytactics.com */ + { 11627, 0, 0, 1 }, /* selfip.com */ + { 14244, 0, 0, 1 }, /* sells-for-less.com */ + { 14259, 0, 0, 1 }, /* sells-for-u.com */ + { 14271, 0, 0, 1 }, /* servebbs.com */ { 876, 0, 0, 1 }, /* servebeer.com */ - { 14089, 0, 0, 1 }, /* servecounterstrike.com */ - { 2832, 0, 0, 1 }, /* serveexchange.com */ - { 14108, 0, 0, 1 }, /* serveftp.com */ - { 14117, 0, 0, 1 }, /* servegame.com */ - { 14127, 0, 0, 1 }, /* servehalflife.com */ - { 14141, 0, 0, 1 }, /* servehttp.com */ - { 14151, 0, 0, 1 }, /* servehumour.com */ - { 14163, 0, 0, 1 }, /* serveirc.com */ - { 14172, 0, 0, 1 }, /* servemp3.com */ - { 14181, 0, 0, 1 }, /* servep2p.com */ - { 6279, 0, 0, 1 }, /* servepics.com */ - { 14190, 0, 0, 1 }, /* servequake.com */ - { 14201, 0, 0, 1 }, /* servesarcasm.com */ - { 14214, 0, 0, 1 }, /* simple-url.com */ - { 14228, 0, 0, 1 }, /* sinaapp.com */ - { 6682, 0, 0, 1 }, /* space-to-rent.com */ - { 6587, 0, 0, 1 }, /* stufftoread.com */ - { 10591, 0, 0, 1 }, /* teaches-yoga.com */ - { 14236, 0, 0, 1 }, /* townnews-staging.com */ - { 8122, 0, 0, 1 }, /* uk.com */ - { 14253, 0, 0, 1 }, /* unusualperson.com */ + { 14280, 0, 0, 1 }, /* servecounterstrike.com */ + { 2831, 0, 0, 1 }, /* serveexchange.com */ + { 14299, 0, 0, 1 }, /* serveftp.com */ + { 14308, 0, 0, 1 }, /* servegame.com */ + { 14318, 0, 0, 1 }, /* servehalflife.com */ + { 14332, 0, 0, 1 }, /* servehttp.com */ + { 14342, 0, 0, 1 }, /* servehumour.com */ + { 14354, 0, 0, 1 }, /* serveirc.com */ + { 14363, 0, 0, 1 }, /* servemp3.com */ + { 14372, 0, 0, 1 }, /* servep2p.com */ + { 6262, 0, 0, 1 }, /* servepics.com */ + { 14381, 0, 0, 1 }, /* servequake.com */ + { 14392, 0, 0, 1 }, /* servesarcasm.com */ + { 14405, 0, 0, 1 }, /* simple-url.com */ + { 14419, 0, 0, 1 }, /* sinaapp.com */ + { 6665, 0, 0, 1 }, /* space-to-rent.com */ + { 6570, 0, 0, 1 }, /* stufftoread.com */ + { 10570, 0, 0, 1 }, /* teaches-yoga.com */ + { 14427, 0, 0, 1 }, /* townnews-staging.com */ + { 8120, 0, 0, 1 }, /* uk.com */ + { 14444, 0, 0, 1 }, /* unusualperson.com */ { 264, 0, 0, 1 }, /* us.com */ { 911, 0, 0, 1 }, /* uy.com */ - { 14225, 0, 0, 1 }, /* vipsinaapp.com */ - { 3552, 0, 0, 1 }, /* withgoogle.com */ - { 8051, 0, 0, 1 }, /* withyoutube.com */ - { 14267, 0, 0, 1 }, /* workisboring.com */ - { 14280, 0, 0, 1 }, /* writesthisblog.com */ + { 14416, 0, 0, 1 }, /* vipsinaapp.com */ + { 3537, 0, 0, 1 }, /* withgoogle.com */ + { 8049, 0, 0, 1 }, /* withyoutube.com */ + { 14458, 0, 0, 1 }, /* workisboring.com */ + { 14471, 0, 0, 1 }, /* wpdevcloud.com */ + { 14482, 0, 0, 1 }, /* writesthisblog.com */ { 674, 0, 0, 1 }, /* xenapponazure.com */ - { 14295, 0, 0, 1 }, /* yolasite.com */ - { 6329, 0, 0, 1 }, /* za.com */ - { 14307, 2044, 1, 0 }, /* ap-northeast-1.amazonaws.com */ - { 14325, 2045, 3, 0 }, /* ap-northeast-2.amazonaws.com */ - { 14343, 2048, 3, 0 }, /* ap-south-1.amazonaws.com */ - { 14357, 2051, 1, 0 }, /* ap-southeast-1.amazonaws.com */ - { 14375, 2052, 1, 0 }, /* ap-southeast-2.amazonaws.com */ - { 14393, 2053, 3, 0 }, /* ca-central-1.amazonaws.com */ - { 11947, 3687, 1, 0 }, /* compute.amazonaws.com */ - { 14406, 3687, 1, 0 }, /* compute-1.amazonaws.com */ - { 11955, 3687, 1, 0 }, /* elb.amazonaws.com */ - { 14419, 2056, 3, 0 }, /* eu-central-1.amazonaws.com */ - { 14435, 2059, 1, 0 }, /* eu-west-1.amazonaws.com */ - { 11473, 3687, 1, 0 }, /* s3.amazonaws.com */ - { 14304, 0, 0, 1 }, /* s3-ap-northeast-1.amazonaws.com */ - { 14322, 0, 0, 1 }, /* s3-ap-northeast-2.amazonaws.com */ - { 14340, 0, 0, 1 }, /* s3-ap-south-1.amazonaws.com */ - { 14354, 0, 0, 1 }, /* s3-ap-southeast-1.amazonaws.com */ - { 14372, 0, 0, 1 }, /* s3-ap-southeast-2.amazonaws.com */ - { 14390, 0, 0, 1 }, /* s3-ca-central-1.amazonaws.com */ - { 14416, 0, 0, 1 }, /* s3-eu-central-1.amazonaws.com */ - { 14432, 0, 0, 1 }, /* s3-eu-west-1.amazonaws.com */ - { 14445, 0, 0, 1 }, /* s3-external-1.amazonaws.com */ - { 14459, 0, 0, 1 }, /* s3-fips-us-gov-west-1.amazonaws.com */ - { 14481, 0, 0, 1 }, /* s3-sa-east-1.amazonaws.com */ - { 14494, 0, 0, 1 }, /* s3-us-east-2.amazonaws.com */ - { 14507, 0, 0, 1 }, /* s3-us-gov-west-1.amazonaws.com */ - { 14524, 0, 0, 1 }, /* s3-us-west-1.amazonaws.com */ - { 14537, 0, 0, 1 }, /* s3-us-west-2.amazonaws.com */ - { 14550, 0, 0, 1 }, /* s3-website-ap-northeast-1.amazonaws.com */ - { 14576, 0, 0, 1 }, /* s3-website-ap-southeast-1.amazonaws.com */ - { 14602, 0, 0, 1 }, /* s3-website-ap-southeast-2.amazonaws.com */ - { 14628, 0, 0, 1 }, /* s3-website-eu-west-1.amazonaws.com */ - { 14649, 0, 0, 1 }, /* s3-website-sa-east-1.amazonaws.com */ - { 14670, 0, 0, 1 }, /* s3-website-us-east-1.amazonaws.com */ - { 14691, 0, 0, 1 }, /* s3-website-us-west-1.amazonaws.com */ - { 14712, 0, 0, 1 }, /* s3-website-us-west-2.amazonaws.com */ - { 14484, 2060, 1, 0 }, /* sa-east-1.amazonaws.com */ - { 14681, 2061, 1, 1 }, /* us-east-1.amazonaws.com */ - { 14497, 2062, 3, 0 }, /* us-east-2.amazonaws.com */ - { 14733, 3880, 1, 0 }, /* dualstack.ap-northeast-1.amazonaws.com */ - { 14733, 3880, 1, 0 }, /* dualstack.ap-northeast-2.amazonaws.com */ - { 11473, 0, 0, 1 }, /* s3.ap-northeast-2.amazonaws.com */ - { 8490, 0, 0, 1 }, /* s3-website.ap-northeast-2.amazonaws.com */ - { 14733, 3880, 1, 0 }, /* dualstack.ap-south-1.amazonaws.com */ - { 11473, 0, 0, 1 }, /* s3.ap-south-1.amazonaws.com */ - { 8490, 0, 0, 1 }, /* s3-website.ap-south-1.amazonaws.com */ - { 14733, 3880, 1, 0 }, /* dualstack.ap-southeast-1.amazonaws.com */ - { 14733, 3880, 1, 0 }, /* dualstack.ap-southeast-2.amazonaws.com */ - { 14733, 3880, 1, 0 }, /* dualstack.ca-central-1.amazonaws.com */ - { 11473, 0, 0, 1 }, /* s3.ca-central-1.amazonaws.com */ - { 8490, 0, 0, 1 }, /* s3-website.ca-central-1.amazonaws.com */ - { 14733, 3880, 1, 0 }, /* dualstack.eu-central-1.amazonaws.com */ - { 11473, 0, 0, 1 }, /* s3.eu-central-1.amazonaws.com */ - { 8490, 0, 0, 1 }, /* s3-website.eu-central-1.amazonaws.com */ - { 14733, 3880, 1, 0 }, /* dualstack.eu-west-1.amazonaws.com */ - { 14733, 3880, 1, 0 }, /* dualstack.sa-east-1.amazonaws.com */ - { 14733, 3880, 1, 1 }, /* dualstack.us-east-1.amazonaws.com */ - { 14733, 3880, 1, 0 }, /* dualstack.us-east-2.amazonaws.com */ - { 11473, 0, 0, 1 }, /* s3.us-east-2.amazonaws.com */ - { 8490, 0, 0, 1 }, /* s3-website.us-east-2.amazonaws.com */ - { 11736, 3687, 1, 0 }, /* api.githubcloud.com */ - { 5814, 3687, 1, 0 }, /* ext.githubcloud.com */ - { 4380, 0, 0, 1 }, /* gist.githubcloud.com */ - { 14774, 3687, 1, 0 }, /* cns.joyent.com */ + { 14497, 0, 0, 1 }, /* yolasite.com */ + { 6312, 0, 0, 1 }, /* za.com */ + { 14509, 2085, 1, 0 }, /* ap-northeast-1.amazonaws.com */ + { 14527, 2086, 3, 0 }, /* ap-northeast-2.amazonaws.com */ + { 14545, 2089, 3, 0 }, /* ap-south-1.amazonaws.com */ + { 14559, 2092, 1, 0 }, /* ap-southeast-1.amazonaws.com */ + { 14577, 2093, 1, 0 }, /* ap-southeast-2.amazonaws.com */ + { 14595, 2094, 3, 0 }, /* ca-central-1.amazonaws.com */ + { 12083, 3853, 1, 0 }, /* compute.amazonaws.com */ + { 14608, 3853, 1, 0 }, /* compute-1.amazonaws.com */ + { 12091, 3853, 1, 0 }, /* elb.amazonaws.com */ + { 14621, 2097, 3, 0 }, /* eu-central-1.amazonaws.com */ + { 14637, 2100, 1, 0 }, /* eu-west-1.amazonaws.com */ + { 14650, 2101, 3, 0 }, /* eu-west-2.amazonaws.com */ + { 11498, 0, 0, 1 }, /* s3.amazonaws.com */ + { 14506, 0, 0, 1 }, /* s3-ap-northeast-1.amazonaws.com */ + { 14524, 0, 0, 1 }, /* s3-ap-northeast-2.amazonaws.com */ + { 14542, 0, 0, 1 }, /* s3-ap-south-1.amazonaws.com */ + { 14556, 0, 0, 1 }, /* s3-ap-southeast-1.amazonaws.com */ + { 14574, 0, 0, 1 }, /* s3-ap-southeast-2.amazonaws.com */ + { 14592, 0, 0, 1 }, /* s3-ca-central-1.amazonaws.com */ + { 14618, 0, 0, 1 }, /* s3-eu-central-1.amazonaws.com */ + { 14634, 0, 0, 1 }, /* s3-eu-west-1.amazonaws.com */ + { 14647, 0, 0, 1 }, /* s3-eu-west-2.amazonaws.com */ + { 14660, 0, 0, 1 }, /* s3-external-1.amazonaws.com */ + { 14674, 0, 0, 1 }, /* s3-fips-us-gov-west-1.amazonaws.com */ + { 14696, 0, 0, 1 }, /* s3-sa-east-1.amazonaws.com */ + { 14709, 0, 0, 1 }, /* s3-us-east-2.amazonaws.com */ + { 14722, 0, 0, 1 }, /* s3-us-gov-west-1.amazonaws.com */ + { 14739, 0, 0, 1 }, /* s3-us-west-1.amazonaws.com */ + { 14752, 0, 0, 1 }, /* s3-us-west-2.amazonaws.com */ + { 14765, 0, 0, 1 }, /* s3-website-ap-northeast-1.amazonaws.com */ + { 14791, 0, 0, 1 }, /* s3-website-ap-southeast-1.amazonaws.com */ + { 14817, 0, 0, 1 }, /* s3-website-ap-southeast-2.amazonaws.com */ + { 14843, 0, 0, 1 }, /* s3-website-eu-west-1.amazonaws.com */ + { 14864, 0, 0, 1 }, /* s3-website-sa-east-1.amazonaws.com */ + { 14885, 0, 0, 1 }, /* s3-website-us-east-1.amazonaws.com */ + { 14906, 0, 0, 1 }, /* s3-website-us-west-1.amazonaws.com */ + { 14927, 0, 0, 1 }, /* s3-website-us-west-2.amazonaws.com */ + { 14699, 2104, 1, 0 }, /* sa-east-1.amazonaws.com */ + { 14896, 2105, 1, 1 }, /* us-east-1.amazonaws.com */ + { 14712, 2106, 3, 0 }, /* us-east-2.amazonaws.com */ + { 14948, 4035, 1, 0 }, /* dualstack.ap-northeast-1.amazonaws.com */ + { 14948, 4035, 1, 0 }, /* dualstack.ap-northeast-2.amazonaws.com */ + { 11498, 0, 0, 1 }, /* s3.ap-northeast-2.amazonaws.com */ + { 8483, 0, 0, 1 }, /* s3-website.ap-northeast-2.amazonaws.com */ + { 14948, 4035, 1, 0 }, /* dualstack.ap-south-1.amazonaws.com */ + { 11498, 0, 0, 1 }, /* s3.ap-south-1.amazonaws.com */ + { 8483, 0, 0, 1 }, /* s3-website.ap-south-1.amazonaws.com */ + { 14948, 4035, 1, 0 }, /* dualstack.ap-southeast-1.amazonaws.com */ + { 14948, 4035, 1, 0 }, /* dualstack.ap-southeast-2.amazonaws.com */ + { 14948, 4035, 1, 0 }, /* dualstack.ca-central-1.amazonaws.com */ + { 11498, 0, 0, 1 }, /* s3.ca-central-1.amazonaws.com */ + { 8483, 0, 0, 1 }, /* s3-website.ca-central-1.amazonaws.com */ + { 14948, 4035, 1, 0 }, /* dualstack.eu-central-1.amazonaws.com */ + { 11498, 0, 0, 1 }, /* s3.eu-central-1.amazonaws.com */ + { 8483, 0, 0, 1 }, /* s3-website.eu-central-1.amazonaws.com */ + { 14948, 4035, 1, 0 }, /* dualstack.eu-west-1.amazonaws.com */ + { 14948, 4035, 1, 0 }, /* dualstack.eu-west-2.amazonaws.com */ + { 11498, 0, 0, 1 }, /* s3.eu-west-2.amazonaws.com */ + { 8483, 0, 0, 1 }, /* s3-website.eu-west-2.amazonaws.com */ + { 14948, 4035, 1, 0 }, /* dualstack.sa-east-1.amazonaws.com */ + { 14948, 4035, 1, 0 }, /* dualstack.us-east-1.amazonaws.com */ + { 14948, 4035, 1, 0 }, /* dualstack.us-east-2.amazonaws.com */ + { 11498, 0, 0, 1 }, /* s3.us-east-2.amazonaws.com */ + { 8483, 0, 0, 1 }, /* s3-website.us-east-2.amazonaws.com */ + { 11828, 3853, 1, 0 }, /* api.githubcloud.com */ + { 5786, 3853, 1, 0 }, /* ext.githubcloud.com */ + { 4359, 0, 0, 1 }, /* gist.githubcloud.com */ + { 14999, 3853, 1, 0 }, /* cns.joyent.com */ { 62, 0, 0, 1 }, /* ac.cy */ - { 985, 0, 0, 1 }, /* biz.cy */ - { 1913, 3672, 1, 1 }, /* com.cy */ - { 14782, 0, 0, 1 }, /* ekloges.cy */ - { 3686, 0, 0, 1 }, /* gov.cy */ - { 5103, 0, 0, 1 }, /* ltd.cy */ - { 5725, 0, 0, 1 }, /* name.cy */ - { 4185, 0, 0, 1 }, /* net.cy */ - { 6070, 0, 0, 1 }, /* org.cy */ - { 14790, 0, 0, 1 }, /* parliament.cy */ + { 986, 0, 0, 1 }, /* biz.cy */ + { 1914, 3838, 1, 1 }, /* com.cy */ + { 15007, 0, 0, 1 }, /* ekloges.cy */ + { 3671, 0, 0, 1 }, /* gov.cy */ + { 5082, 0, 0, 1 }, /* ltd.cy */ + { 5695, 0, 0, 1 }, /* name.cy */ + { 5737, 0, 0, 1 }, /* net.cy */ + { 6053, 0, 0, 1 }, /* org.cy */ + { 15015, 0, 0, 1 }, /* parliament.cy */ { 371, 0, 0, 1 }, /* press.cy */ - { 6427, 0, 0, 1 }, /* pro.cy */ - { 7910, 0, 0, 1 }, /* tm.cy */ - { 10666, 0, 0, 1 }, /* blogspot.de */ - { 1913, 0, 0, 1 }, /* com.de */ - { 14807, 3913, 1, 1 }, /* cosidns.de */ - { 14815, 0, 0, 1 }, /* dd-dns.de */ - { 14822, 3914, 2, 1 }, /* ddnss.de */ - { 14828, 0, 0, 1 }, /* dnshome.de */ - { 14836, 0, 0, 1 }, /* dnsupdater.de */ - { 14847, 0, 0, 1 }, /* dray-dns.de */ - { 14856, 0, 0, 1 }, /* draydns.de */ - { 14864, 0, 0, 1 }, /* dyn-ip24.de */ - { 14873, 0, 0, 1 }, /* dyn-vpn.de */ - { 14881, 0, 0, 1 }, /* dynamisches-dns.de */ - { 14897, 0, 0, 1 }, /* dyndns1.de */ - { 14905, 0, 0, 1 }, /* dynvpn.de */ - { 12520, 0, 0, 1 }, /* firewall-gateway.de */ - { 14912, 0, 0, 1 }, /* fuettertdasnetz.de */ - { 13787, 0, 0, 1 }, /* goip.de */ - { 14928, 3913, 1, 1 }, /* home-webserver.de */ - { 14943, 0, 0, 1 }, /* internet-dns.de */ - { 14956, 0, 0, 1 }, /* isteingeek.de */ - { 14967, 0, 0, 1 }, /* istmein.de */ - { 14975, 0, 0, 1 }, /* keymachine.de */ - { 14986, 0, 0, 1 }, /* l-o-g-i-n.de */ - { 14996, 0, 0, 1 }, /* lebtimnetz.de */ - { 15007, 0, 0, 1 }, /* leitungsen.de */ - { 13785, 0, 0, 1 }, /* logoip.de */ - { 15018, 0, 0, 1 }, /* mein-vigor.de */ - { 15029, 0, 0, 1 }, /* my-gateway.de */ - { 15040, 0, 0, 1 }, /* my-router.de */ - { 15050, 0, 0, 1 }, /* my-vigor.de */ - { 15059, 0, 0, 1 }, /* my-wan.de */ - { 15066, 0, 0, 1 }, /* myhome-server.de */ - { 15080, 0, 0, 1 }, /* spdns.de */ - { 15086, 0, 0, 1 }, /* syno-ds.de */ - { 15094, 0, 0, 1 }, /* synology-diskstation.de */ - { 15115, 0, 0, 1 }, /* synology-ds.de */ - { 15127, 0, 0, 1 }, /* taifun-dns.de */ - { 15138, 0, 0, 1 }, /* traeumtgerade.de */ - { 15178, 0, 0, 1 }, /* aip.ee */ - { 1913, 3672, 1, 1 }, /* com.ee */ - { 2624, 0, 0, 1 }, /* edu.ee */ - { 4174, 0, 0, 1 }, /* fie.ee */ - { 3686, 0, 0, 1 }, /* gov.ee */ - { 15182, 0, 0, 1 }, /* lib.ee */ - { 1858, 0, 0, 1 }, /* med.ee */ - { 6070, 0, 0, 1 }, /* org.ee */ - { 15186, 0, 0, 1 }, /* pri.ee */ - { 15190, 0, 0, 1 }, /* riik.ee */ - { 1913, 3672, 1, 1 }, /* com.eg */ - { 2624, 0, 0, 1 }, /* edu.eg */ - { 15195, 0, 0, 1 }, /* eun.eg */ - { 3686, 0, 0, 1 }, /* gov.eg */ - { 4195, 0, 0, 1 }, /* mil.eg */ - { 5725, 0, 0, 1 }, /* name.eg */ - { 4185, 0, 0, 1 }, /* net.eg */ - { 6070, 0, 0, 1 }, /* org.eg */ - { 15209, 0, 0, 1 }, /* sci.eg */ - { 1913, 3672, 1, 1 }, /* com.es */ - { 2624, 0, 0, 1 }, /* edu.es */ - { 11325, 0, 0, 1 }, /* gob.es */ - { 5998, 0, 0, 1 }, /* nom.es */ - { 6070, 0, 0, 1 }, /* org.es */ - { 11947, 3687, 1, 0 }, /* compute.estate */ - { 11367, 0, 0, 1 }, /* cloudns.eu */ - { 15103, 0, 0, 1 }, /* diskstation.eu */ - { 15213, 0, 0, 1 }, /* mycd.eu */ - { 15080, 0, 0, 1 }, /* spdns.eu */ - { 11450, 3687, 1, 0 }, /* transurl.eu */ - { 15218, 0, 0, 1 }, /* wellbeingzone.eu */ - { 6180, 3960, 1, 1 }, /* party.eus */ + { 6410, 0, 0, 1 }, /* pro.cy */ + { 7908, 0, 0, 1 }, /* tm.cy */ + { 10645, 0, 0, 1 }, /* blogspot.cz */ + { 113, 0, 0, 1 }, /* co.cz */ + { 11501, 0, 0, 1 }, /* e4.cz */ + { 15026, 4068, 2, 0 }, /* metacentrum.cz */ + { 15038, 0, 0, 1 }, /* realm.cz */ + { 11361, 0, 0, 1 }, /* 12hp.de */ + { 11366, 0, 0, 1 }, /* 2ix.de */ + { 11370, 0, 0, 1 }, /* 4lima.de */ + { 11518, 0, 0, 1 }, /* barsy.de */ + { 10645, 0, 0, 1 }, /* blogspot.de */ + { 12225, 0, 0, 1 }, /* bplaced.de */ + { 1914, 0, 0, 1 }, /* com.de */ + { 15051, 4070, 1, 0 }, /* cosidns.de */ + { 15059, 0, 0, 1 }, /* dd-dns.de */ + { 15066, 4071, 2, 1 }, /* ddnss.de */ + { 15072, 0, 0, 1 }, /* dnshome.de */ + { 15080, 0, 0, 1 }, /* dnsupdater.de */ + { 15091, 0, 0, 1 }, /* dray-dns.de */ + { 15100, 0, 0, 1 }, /* draydns.de */ + { 15108, 0, 0, 1 }, /* dyn-ip24.de */ + { 15117, 0, 0, 1 }, /* dyn-vpn.de */ + { 15125, 0, 0, 1 }, /* dynamisches-dns.de */ + { 15141, 0, 0, 1 }, /* dyndns1.de */ + { 15149, 0, 0, 1 }, /* dynvpn.de */ + { 12679, 0, 0, 1 }, /* firewall-gateway.de */ + { 15156, 0, 0, 1 }, /* fuettertdasnetz.de */ + { 15172, 0, 0, 1 }, /* git-repos.de */ + { 13956, 0, 0, 1 }, /* goip.de */ + { 15182, 4070, 1, 1 }, /* home-webserver.de */ + { 15197, 0, 0, 1 }, /* internet-dns.de */ + { 15210, 0, 0, 1 }, /* isteingeek.de */ + { 15221, 0, 0, 1 }, /* istmein.de */ + { 15229, 0, 0, 1 }, /* keymachine.de */ + { 15240, 0, 0, 1 }, /* l-o-g-i-n.de */ + { 15250, 0, 0, 1 }, /* lcube-server.de */ + { 15263, 0, 0, 1 }, /* lebtimnetz.de */ + { 15274, 0, 0, 1 }, /* leitungsen.de */ + { 11404, 0, 0, 1 }, /* lima-city.de */ + { 13954, 0, 0, 1 }, /* logoip.de */ + { 15285, 0, 0, 1 }, /* mein-vigor.de */ + { 15296, 0, 0, 1 }, /* my-gateway.de */ + { 15307, 0, 0, 1 }, /* my-router.de */ + { 15317, 0, 0, 1 }, /* my-vigor.de */ + { 15326, 0, 0, 1 }, /* my-wan.de */ + { 15333, 0, 0, 1 }, /* myhome-server.de */ + { 15347, 0, 0, 1 }, /* spdns.de */ + { 11915, 0, 0, 1 }, /* square7.de */ + { 15353, 0, 0, 1 }, /* svn-repos.de */ + { 15363, 0, 0, 1 }, /* syno-ds.de */ + { 15371, 0, 0, 1 }, /* synology-diskstation.de */ + { 15392, 0, 0, 1 }, /* synology-ds.de */ + { 15404, 0, 0, 1 }, /* taifun-dns.de */ + { 15415, 0, 0, 1 }, /* traeumtgerade.de */ + { 15455, 0, 0, 1 }, /* aip.ee */ + { 1914, 3838, 1, 1 }, /* com.ee */ + { 2623, 0, 0, 1 }, /* edu.ee */ + { 4155, 0, 0, 1 }, /* fie.ee */ + { 3671, 0, 0, 1 }, /* gov.ee */ + { 15459, 0, 0, 1 }, /* lib.ee */ + { 1859, 0, 0, 1 }, /* med.ee */ + { 6053, 0, 0, 1 }, /* org.ee */ + { 15463, 0, 0, 1 }, /* pri.ee */ + { 15467, 0, 0, 1 }, /* riik.ee */ + { 1914, 3838, 1, 1 }, /* com.eg */ + { 2623, 0, 0, 1 }, /* edu.eg */ + { 15472, 0, 0, 1 }, /* eun.eg */ + { 3671, 0, 0, 1 }, /* gov.eg */ + { 4170, 0, 0, 1 }, /* mil.eg */ + { 5695, 0, 0, 1 }, /* name.eg */ + { 5737, 0, 0, 1 }, /* net.eg */ + { 6053, 0, 0, 1 }, /* org.eg */ + { 15486, 0, 0, 1 }, /* sci.eg */ + { 1914, 3838, 1, 1 }, /* com.es */ + { 2623, 0, 0, 1 }, /* edu.es */ + { 11304, 0, 0, 1 }, /* gob.es */ + { 5970, 0, 0, 1 }, /* nom.es */ + { 6053, 0, 0, 1 }, /* org.es */ + { 12083, 3853, 1, 0 }, /* compute.estate */ + { 11518, 0, 0, 1 }, /* barsy.eu */ + { 11353, 0, 0, 1 }, /* cloudns.eu */ + { 15380, 0, 0, 1 }, /* diskstation.eu */ + { 15490, 0, 0, 1 }, /* mycd.eu */ + { 15347, 0, 0, 1 }, /* spdns.eu */ + { 11475, 3853, 1, 0 }, /* transurl.eu */ + { 15495, 0, 0, 1 }, /* wellbeingzone.eu */ + { 6163, 4117, 1, 0 }, /* party.eus */ { 62, 0, 0, 1 }, /* ac.id */ - { 985, 0, 0, 1 }, /* biz.id */ - { 113, 3672, 1, 1 }, /* co.id */ - { 15714, 0, 0, 1 }, /* desa.id */ + { 986, 0, 0, 1 }, /* biz.id */ + { 113, 3838, 1, 1 }, /* co.id */ + { 16007, 0, 0, 1 }, /* desa.id */ { 257, 0, 0, 1 }, /* go.id */ - { 4195, 0, 0, 1 }, /* mil.id */ + { 4170, 0, 0, 1 }, /* mil.id */ { 70, 0, 0, 1 }, /* my.id */ - { 4185, 0, 0, 1 }, /* net.id */ + { 5737, 0, 0, 1 }, /* net.id */ { 137, 0, 0, 1 }, /* or.id */ - { 1145, 0, 0, 1 }, /* sch.id */ - { 11967, 0, 0, 1 }, /* web.id */ + { 1146, 0, 0, 1 }, /* sch.id */ + { 12109, 0, 0, 1 }, /* web.id */ { 62, 0, 0, 1 }, /* ac.il */ - { 113, 3672, 1, 1 }, /* co.il */ - { 3686, 0, 0, 1 }, /* gov.il */ - { 11727, 0, 0, 1 }, /* idf.il */ - { 15174, 0, 0, 1 }, /* k12.il */ - { 15719, 0, 0, 1 }, /* muni.il */ - { 4185, 0, 0, 1 }, /* net.il */ - { 6070, 0, 0, 1 }, /* org.il */ + { 113, 3838, 1, 1 }, /* co.il */ + { 3671, 0, 0, 1 }, /* gov.il */ + { 11819, 0, 0, 1 }, /* idf.il */ + { 15451, 0, 0, 1 }, /* k12.il */ + { 16012, 0, 0, 1 }, /* muni.il */ + { 5737, 0, 0, 1 }, /* net.il */ + { 6053, 0, 0, 1 }, /* org.il */ { 62, 0, 0, 1 }, /* ac.im */ - { 113, 4141, 2, 1 }, /* co.im */ - { 1913, 0, 0, 1 }, /* com.im */ - { 4185, 0, 0, 1 }, /* net.im */ - { 6070, 0, 0, 1 }, /* org.im */ + { 113, 4303, 2, 1 }, /* co.im */ + { 1914, 0, 0, 1 }, /* com.im */ + { 5737, 0, 0, 1 }, /* net.im */ + { 6053, 0, 0, 1 }, /* org.im */ { 166, 0, 0, 1 }, /* ro.im */ { 24, 0, 0, 1 }, /* tt.im */ - { 2546, 0, 0, 1 }, /* tv.im */ - { 15840, 0, 0, 1 }, /* backplaneapp.io */ - { 15853, 0, 0, 1 }, /* boxfuse.io */ - { 15861, 0, 0, 1 }, /* browsersafetymark.io */ - { 1913, 0, 0, 1 }, /* com.io */ - { 15152, 0, 0, 1 }, /* dedyn.io */ - { 15879, 0, 0, 1 }, /* drud.io */ - { 15884, 4173, 1, 1 }, /* enonic.io */ - { 15891, 0, 0, 1 }, /* github.io */ - { 15898, 0, 0, 1 }, /* gitlab.io */ - { 15905, 0, 0, 1 }, /* hasura-app.io */ - { 15916, 0, 0, 1 }, /* hzc.io */ - { 15920, 3887, 1, 1 }, /* lair.io */ - { 15925, 0, 0, 1 }, /* ngrok.io */ - { 15931, 0, 0, 1 }, /* nid.io */ - { 15935, 0, 0, 1 }, /* pantheonsite.io */ - { 15948, 0, 0, 1 }, /* protonet.io */ - { 15957, 0, 0, 1 }, /* sandcats.io */ - { 15966, 0, 0, 1 }, /* shiftedit.io */ - { 15976, 0, 0, 1 }, /* spacekit.io */ - { 15985, 3687, 1, 0 }, /* stolos.io */ + { 2549, 0, 0, 1 }, /* tv.im */ + { 16133, 0, 0, 1 }, /* backplaneapp.io */ + { 16146, 0, 0, 1 }, /* boxfuse.io */ + { 16154, 0, 0, 1 }, /* browsersafetymark.io */ + { 1914, 0, 0, 1 }, /* com.io */ + { 15429, 0, 0, 1 }, /* dedyn.io */ + { 16172, 0, 0, 1 }, /* definima.io */ + { 16181, 0, 0, 1 }, /* drud.io */ + { 16186, 4336, 1, 1 }, /* enonic.io */ + { 16193, 0, 0, 1 }, /* github.io */ + { 16200, 0, 0, 1 }, /* gitlab.io */ + { 16207, 0, 0, 1 }, /* hasura-app.io */ + { 16218, 0, 0, 1 }, /* hzc.io */ + { 16222, 4044, 1, 0 }, /* lair.io */ + { 16227, 0, 0, 1 }, /* ngrok.io */ + { 16233, 0, 0, 1 }, /* nid.io */ + { 12100, 0, 0, 1 }, /* nodum.io */ + { 16237, 0, 0, 1 }, /* pantheonsite.io */ + { 16250, 0, 0, 1 }, /* protonet.io */ + { 16259, 0, 0, 1 }, /* sandcats.io */ + { 16268, 0, 0, 1 }, /* shiftedit.io */ + { 16278, 0, 0, 1 }, /* spacekit.io */ + { 16287, 3853, 1, 0 }, /* stolos.io */ + { 16294, 2264, 4, 0 }, /* thingdust.io */ + { 16304, 0, 0, 1 }, /* vaporcloud.io */ + { 16315, 0, 0, 1 }, /* wedeploy.io */ + { 2383, 4337, 1, 0 }, /* dev.thingdust.io */ + { 11766, 4337, 1, 0 }, /* disrec.thingdust.io */ + { 6414, 4337, 1, 0 }, /* prod.thingdust.io */ + { 16333, 4337, 1, 0 }, /* testing.thingdust.io */ { 62, 0, 0, 1 }, /* ac.jp */ { 141, 0, 0, 1 }, /* ad.jp */ - { 18576, 4568, 52, 1 }, /* aichi.jp */ - { 18585, 4620, 28, 1 }, /* akita.jp */ - { 18591, 4648, 22, 1 }, /* aomori.jp */ - { 10666, 0, 0, 1 }, /* blogspot.jp */ - { 18603, 4670, 58, 1 }, /* chiba.jp */ + { 18921, 4735, 52, 1 }, /* aichi.jp */ + { 18930, 4787, 28, 1 }, /* akita.jp */ + { 18936, 4815, 22, 1 }, /* aomori.jp */ + { 10645, 0, 0, 1 }, /* blogspot.jp */ + { 18948, 4837, 58, 1 }, /* chiba.jp */ { 113, 0, 0, 1 }, /* co.jp */ - { 1859, 0, 0, 1 }, /* ed.jp */ - { 18609, 4728, 22, 1 }, /* ehime.jp */ - { 18615, 4750, 15, 1 }, /* fukui.jp */ - { 18621, 4765, 63, 1 }, /* fukuoka.jp */ - { 18633, 4828, 51, 1 }, /* fukushima.jp */ - { 18643, 4879, 38, 1 }, /* gifu.jp */ + { 1860, 0, 0, 1 }, /* ed.jp */ + { 18954, 4895, 22, 1 }, /* ehime.jp */ + { 18960, 4917, 15, 1 }, /* fukui.jp */ + { 18966, 4932, 63, 1 }, /* fukuoka.jp */ + { 18978, 4995, 51, 1 }, /* fukushima.jp */ + { 18988, 5046, 38, 1 }, /* gifu.jp */ { 257, 0, 0, 1 }, /* go.jp */ - { 3697, 0, 0, 1 }, /* gr.jp */ - { 18648, 4917, 36, 1 }, /* gunma.jp */ - { 18658, 4953, 25, 1 }, /* hiroshima.jp */ - { 18668, 4978, 142, 1 }, /* hokkaido.jp */ - { 18677, 5120, 46, 1 }, /* hyogo.jp */ - { 18683, 5166, 51, 1 }, /* ibaraki.jp */ - { 18692, 5217, 19, 1 }, /* ishikawa.jp */ - { 18701, 5236, 34, 1 }, /* iwate.jp */ - { 18709, 5270, 15, 1 }, /* kagawa.jp */ - { 18716, 5285, 20, 1 }, /* kagoshima.jp */ - { 18726, 5305, 30, 1 }, /* kanagawa.jp */ - { 18735, 5335, 2, 0 }, /* kawasaki.jp */ - { 18744, 5335, 2, 0 }, /* kitakyushu.jp */ - { 858, 5335, 2, 0 }, /* kobe.jp */ - { 18755, 5337, 31, 1 }, /* kochi.jp */ - { 5553, 5368, 23, 1 }, /* kumamoto.jp */ - { 4708, 5391, 31, 1 }, /* kyoto.jp */ - { 11693, 0, 0, 1 }, /* lg.jp */ - { 18763, 5422, 30, 1 }, /* mie.jp */ - { 18767, 5452, 32, 1 }, /* miyagi.jp */ - { 18774, 5484, 27, 1 }, /* miyazaki.jp */ - { 18790, 5511, 75, 1 }, /* nagano.jp */ - { 18797, 5586, 22, 1 }, /* nagasaki.jp */ - { 5714, 5335, 2, 0 }, /* nagoya.jp */ - { 17635, 5608, 38, 1 }, /* nara.jp */ - { 1203, 0, 0, 1 }, /* ne.jp */ - { 18806, 5646, 34, 1 }, /* niigata.jp */ - { 18815, 5680, 19, 1 }, /* oita.jp */ - { 18820, 5699, 26, 1 }, /* okayama.jp */ - { 5966, 5725, 42, 1 }, /* okinawa.jp */ + { 3682, 0, 0, 1 }, /* gr.jp */ + { 18993, 5084, 36, 1 }, /* gunma.jp */ + { 19003, 5120, 25, 1 }, /* hiroshima.jp */ + { 19013, 5145, 142, 1 }, /* hokkaido.jp */ + { 19022, 5287, 46, 1 }, /* hyogo.jp */ + { 19028, 5333, 51, 1 }, /* ibaraki.jp */ + { 19037, 5384, 19, 1 }, /* ishikawa.jp */ + { 19046, 5403, 34, 1 }, /* iwate.jp */ + { 19054, 5437, 15, 1 }, /* kagawa.jp */ + { 19061, 5452, 20, 1 }, /* kagoshima.jp */ + { 19071, 5472, 30, 1 }, /* kanagawa.jp */ + { 19080, 5502, 2, 0 }, /* kawasaki.jp */ + { 19089, 5502, 2, 0 }, /* kitakyushu.jp */ + { 858, 5502, 2, 0 }, /* kobe.jp */ + { 19100, 5504, 31, 1 }, /* kochi.jp */ + { 5532, 5535, 23, 1 }, /* kumamoto.jp */ + { 4687, 5558, 31, 1 }, /* kyoto.jp */ + { 11781, 0, 0, 1 }, /* lg.jp */ + { 19108, 5589, 30, 1 }, /* mie.jp */ + { 19112, 5619, 32, 1 }, /* miyagi.jp */ + { 19119, 5651, 27, 1 }, /* miyazaki.jp */ + { 19135, 5678, 75, 1 }, /* nagano.jp */ + { 19142, 5753, 22, 1 }, /* nagasaki.jp */ + { 5684, 5502, 2, 0 }, /* nagoya.jp */ + { 17980, 5775, 38, 1 }, /* nara.jp */ + { 1204, 0, 0, 1 }, /* ne.jp */ + { 19151, 5813, 34, 1 }, /* niigata.jp */ + { 19160, 5847, 19, 1 }, /* oita.jp */ + { 19165, 5866, 26, 1 }, /* okayama.jp */ + { 5938, 5892, 42, 1 }, /* okinawa.jp */ { 137, 0, 0, 1 }, /* or.jp */ - { 6098, 5767, 50, 1 }, /* osaka.jp */ - { 3353, 5817, 26, 1 }, /* saga.jp */ - { 18828, 5843, 69, 1 }, /* saitama.jp */ - { 18836, 5335, 2, 0 }, /* sapporo.jp */ - { 18851, 5335, 2, 0 }, /* sendai.jp */ - { 18858, 5912, 23, 1 }, /* shiga.jp */ - { 18864, 5935, 23, 1 }, /* shimane.jp */ - { 18872, 5958, 36, 1 }, /* shizuoka.jp */ - { 18881, 5994, 31, 1 }, /* tochigi.jp */ - { 18889, 6025, 17, 1 }, /* tokushima.jp */ - { 7924, 6042, 57, 1 }, /* tokyo.jp */ - { 18899, 6099, 13, 1 }, /* tottori.jp */ - { 18909, 6112, 24, 1 }, /* toyama.jp */ - { 18916, 6136, 29, 1 }, /* wakayama.jp */ - { 18925, 0, 0, 1 }, /* xn--0trq7p7nn.jp */ - { 18939, 0, 0, 1 }, /* xn--1ctwo.jp */ - { 18949, 0, 0, 1 }, /* xn--1lqs03n.jp */ - { 18961, 0, 0, 1 }, /* xn--1lqs71d.jp */ - { 18973, 0, 0, 1 }, /* xn--2m4a15e.jp */ - { 18985, 0, 0, 1 }, /* xn--32vp30h.jp */ - { 18997, 0, 0, 1 }, /* xn--4it168d.jp */ - { 19009, 0, 0, 1 }, /* xn--4it797k.jp */ - { 19021, 0, 0, 1 }, /* xn--4pvxs.jp */ - { 19031, 0, 0, 1 }, /* xn--5js045d.jp */ - { 19043, 0, 0, 1 }, /* xn--5rtp49c.jp */ - { 19055, 0, 0, 1 }, /* xn--5rtq34k.jp */ - { 19067, 0, 0, 1 }, /* xn--6btw5a.jp */ - { 19078, 0, 0, 1 }, /* xn--6orx2r.jp */ - { 19089, 0, 0, 1 }, /* xn--7t0a264c.jp */ - { 19102, 0, 0, 1 }, /* xn--8ltr62k.jp */ - { 11988, 0, 0, 1 }, /* xn--8pvr4u.jp */ - { 19114, 0, 0, 1 }, /* xn--c3s14m.jp */ - { 19125, 0, 0, 1 }, /* xn--d5qv7z876c.jp */ - { 19140, 0, 0, 1 }, /* xn--djrs72d6uy.jp */ - { 19155, 0, 0, 1 }, /* xn--djty4k.jp */ - { 19166, 0, 0, 1 }, /* xn--efvn9s.jp */ - { 19177, 0, 0, 1 }, /* xn--ehqz56n.jp */ - { 19189, 0, 0, 1 }, /* xn--elqq16h.jp */ - { 19201, 0, 0, 1 }, /* xn--f6qx53a.jp */ - { 19213, 0, 0, 1 }, /* xn--k7yn95e.jp */ - { 19225, 0, 0, 1 }, /* xn--kbrq7o.jp */ - { 19236, 0, 0, 1 }, /* xn--klt787d.jp */ - { 19248, 0, 0, 1 }, /* xn--kltp7d.jp */ - { 19259, 0, 0, 1 }, /* xn--kltx9a.jp */ - { 19270, 0, 0, 1 }, /* xn--klty5x.jp */ - { 19281, 0, 0, 1 }, /* xn--mkru45i.jp */ - { 19293, 0, 0, 1 }, /* xn--nit225k.jp */ - { 19305, 0, 0, 1 }, /* xn--ntso0iqx3a.jp */ - { 19320, 0, 0, 1 }, /* xn--ntsq17g.jp */ - { 19332, 0, 0, 1 }, /* xn--pssu33l.jp */ - { 19344, 0, 0, 1 }, /* xn--qqqt11m.jp */ - { 19356, 0, 0, 1 }, /* xn--rht27z.jp */ - { 19367, 0, 0, 1 }, /* xn--rht3d.jp */ - { 19377, 0, 0, 1 }, /* xn--rht61e.jp */ - { 19388, 0, 0, 1 }, /* xn--rny31h.jp */ - { 19399, 0, 0, 1 }, /* xn--tor131o.jp */ - { 19411, 0, 0, 1 }, /* xn--uist22h.jp */ - { 19423, 0, 0, 1 }, /* xn--uisz3g.jp */ - { 19434, 0, 0, 1 }, /* xn--uuwu58a.jp */ - { 19446, 0, 0, 1 }, /* xn--vgu402c.jp */ - { 19458, 0, 0, 1 }, /* xn--zbx025d.jp */ - { 19470, 6165, 34, 1 }, /* yamagata.jp */ - { 19479, 6199, 16, 1 }, /* yamaguchi.jp */ - { 19489, 6215, 28, 1 }, /* yamanashi.jp */ - { 10604, 5335, 2, 0 }, /* yokohama.jp */ - { 11410, 0, 0, 1 }, /* *.ke */ - { 113, 3672, 1, 1 }, /* co.ke */ - { 29741, 6319, 2, 1 }, /* static.land */ - { 1913, 3672, 1, 1 }, /* com.mt */ - { 2624, 0, 0, 1 }, /* edu.mt */ - { 4185, 0, 0, 1 }, /* net.mt */ - { 6070, 0, 0, 1 }, /* org.mt */ - { 1226, 7042, 1, 1 }, /* her.name */ - { 13964, 7042, 1, 1 }, /* his.name */ - { 2225, 3687, 1, 0 }, /* alwaysdata.net */ - { 1364, 0, 0, 1 }, /* at-band-camp.net */ - { 5453, 0, 0, 1 }, /* azure-mobile.net */ - { 29748, 0, 0, 1 }, /* azurewebsites.net */ - { 12046, 0, 0, 1 }, /* blogdns.net */ - { 33891, 0, 0, 1 }, /* bounceme.net */ - { 33900, 0, 0, 1 }, /* broke-it.net */ - { 33909, 0, 0, 1 }, /* buyshouses.net */ - { 11481, 7044, 1, 1 }, /* cdn77.net */ - { 33920, 0, 0, 1 }, /* cdn77-ssl.net */ - { 33930, 0, 0, 1 }, /* cloudapp.net */ - { 33939, 0, 0, 1 }, /* cloudfront.net */ - { 33950, 0, 0, 1 }, /* cloudfunctions.net */ - { 33965, 3687, 1, 0 }, /* cryptonomic.net */ - { 29802, 0, 0, 1 }, /* ddns.net */ - { 12179, 0, 0, 1 }, /* dnsalias.net */ - { 12188, 0, 0, 1 }, /* dnsdojo.net */ - { 33977, 0, 0, 1 }, /* does-it.net */ - { 12219, 0, 0, 1 }, /* dontexist.net */ - { 12250, 0, 0, 1 }, /* dsmynas.net */ - { 12269, 0, 0, 1 }, /* dynalias.net */ - { 33985, 0, 0, 1 }, /* dynathome.net */ - { 33995, 0, 0, 1 }, /* dynv6.net */ - { 6074, 0, 0, 1 }, /* eating-organic.net */ - { 34001, 0, 0, 1 }, /* endofinternet.net */ - { 12493, 0, 0, 1 }, /* familyds.net */ - { 34015, 2397, 2, 0 }, /* fastly.net */ - { 34022, 0, 0, 1 }, /* feste-ip.net */ - { 12520, 0, 0, 1 }, /* firewall-gateway.net */ - { 34031, 0, 0, 1 }, /* from-az.net */ - { 34039, 0, 0, 1 }, /* from-co.net */ - { 34047, 0, 0, 1 }, /* from-la.net */ - { 34055, 0, 0, 1 }, /* from-ny.net */ - { 3441, 0, 0, 1 }, /* gb.net */ - { 34063, 0, 0, 1 }, /* gets-it.net */ - { 34071, 0, 0, 1 }, /* ham-radio-op.net */ - { 34084, 0, 0, 1 }, /* homeftp.net */ - { 34092, 0, 0, 1 }, /* homeip.net */ - { 13066, 0, 0, 1 }, /* homelinux.net */ - { 13107, 0, 0, 1 }, /* homeunix.net */ - { 4138, 0, 0, 1 }, /* hu.net */ + { 6081, 5934, 50, 1 }, /* osaka.jp */ + { 3338, 5984, 26, 1 }, /* saga.jp */ + { 19173, 6010, 69, 1 }, /* saitama.jp */ + { 19181, 5502, 2, 0 }, /* sapporo.jp */ + { 19196, 5502, 2, 0 }, /* sendai.jp */ + { 19203, 6079, 23, 1 }, /* shiga.jp */ + { 19209, 6102, 23, 1 }, /* shimane.jp */ + { 19217, 6125, 36, 1 }, /* shizuoka.jp */ + { 19226, 6161, 31, 1 }, /* tochigi.jp */ + { 19234, 6192, 17, 1 }, /* tokushima.jp */ + { 7922, 6209, 57, 1 }, /* tokyo.jp */ + { 19244, 6266, 13, 1 }, /* tottori.jp */ + { 19254, 6279, 24, 1 }, /* toyama.jp */ + { 19261, 6303, 29, 1 }, /* wakayama.jp */ + { 19270, 0, 0, 1 }, /* xn--0trq7p7nn.jp */ + { 19284, 0, 0, 1 }, /* xn--1ctwo.jp */ + { 19294, 0, 0, 1 }, /* xn--1lqs03n.jp */ + { 19306, 0, 0, 1 }, /* xn--1lqs71d.jp */ + { 19318, 0, 0, 1 }, /* xn--2m4a15e.jp */ + { 19330, 0, 0, 1 }, /* xn--32vp30h.jp */ + { 19342, 0, 0, 1 }, /* xn--4it168d.jp */ + { 19354, 0, 0, 1 }, /* xn--4it797k.jp */ + { 19366, 0, 0, 1 }, /* xn--4pvxs.jp */ + { 19376, 0, 0, 1 }, /* xn--5js045d.jp */ + { 19388, 0, 0, 1 }, /* xn--5rtp49c.jp */ + { 19400, 0, 0, 1 }, /* xn--5rtq34k.jp */ + { 19412, 0, 0, 1 }, /* xn--6btw5a.jp */ + { 19423, 0, 0, 1 }, /* xn--6orx2r.jp */ + { 19434, 0, 0, 1 }, /* xn--7t0a264c.jp */ + { 19447, 0, 0, 1 }, /* xn--8ltr62k.jp */ + { 12130, 0, 0, 1 }, /* xn--8pvr4u.jp */ + { 19459, 0, 0, 1 }, /* xn--c3s14m.jp */ + { 19470, 0, 0, 1 }, /* xn--d5qv7z876c.jp */ + { 19485, 0, 0, 1 }, /* xn--djrs72d6uy.jp */ + { 19500, 0, 0, 1 }, /* xn--djty4k.jp */ + { 19511, 0, 0, 1 }, /* xn--efvn9s.jp */ + { 19522, 0, 0, 1 }, /* xn--ehqz56n.jp */ + { 19534, 0, 0, 1 }, /* xn--elqq16h.jp */ + { 19546, 0, 0, 1 }, /* xn--f6qx53a.jp */ + { 19558, 0, 0, 1 }, /* xn--k7yn95e.jp */ + { 19570, 0, 0, 1 }, /* xn--kbrq7o.jp */ + { 19581, 0, 0, 1 }, /* xn--klt787d.jp */ + { 19593, 0, 0, 1 }, /* xn--kltp7d.jp */ + { 19604, 0, 0, 1 }, /* xn--kltx9a.jp */ + { 19615, 0, 0, 1 }, /* xn--klty5x.jp */ + { 19626, 0, 0, 1 }, /* xn--mkru45i.jp */ + { 19638, 0, 0, 1 }, /* xn--nit225k.jp */ + { 19650, 0, 0, 1 }, /* xn--ntso0iqx3a.jp */ + { 19665, 0, 0, 1 }, /* xn--ntsq17g.jp */ + { 19677, 0, 0, 1 }, /* xn--pssu33l.jp */ + { 19689, 0, 0, 1 }, /* xn--qqqt11m.jp */ + { 19701, 0, 0, 1 }, /* xn--rht27z.jp */ + { 19712, 0, 0, 1 }, /* xn--rht3d.jp */ + { 19722, 0, 0, 1 }, /* xn--rht61e.jp */ + { 19733, 0, 0, 1 }, /* xn--rny31h.jp */ + { 19744, 0, 0, 1 }, /* xn--tor131o.jp */ + { 19756, 0, 0, 1 }, /* xn--uist22h.jp */ + { 19768, 0, 0, 1 }, /* xn--uisz3g.jp */ + { 19779, 0, 0, 1 }, /* xn--uuwu58a.jp */ + { 19791, 0, 0, 1 }, /* xn--vgu402c.jp */ + { 19803, 0, 0, 1 }, /* xn--zbx025d.jp */ + { 19815, 6332, 34, 1 }, /* yamagata.jp */ + { 19824, 6366, 16, 1 }, /* yamaguchi.jp */ + { 19834, 6382, 28, 1 }, /* yamanashi.jp */ + { 10583, 5502, 2, 0 }, /* yokohama.jp */ + { 11435, 0, 0, 1 }, /* *.ke */ + { 113, 3838, 1, 0 }, /* co.ke */ + { 30086, 6486, 2, 1 }, /* static.land */ + { 62, 0, 0, 1 }, /* ac.me */ + { 30131, 0, 0, 1 }, /* brasilia.me */ + { 30140, 0, 0, 1 }, /* c66.me */ + { 113, 0, 0, 1 }, /* co.me */ + { 30144, 6539, 1, 1 }, /* daplie.me */ + { 12391, 0, 0, 1 }, /* ddns.me */ + { 15380, 0, 0, 1 }, /* diskstation.me */ + { 30151, 0, 0, 1 }, /* dnsfor.me */ + { 11551, 0, 0, 1 }, /* dscloud.me */ + { 2623, 0, 0, 1 }, /* edu.me */ + { 30158, 0, 0, 1 }, /* filegear.me */ + { 3671, 0, 0, 1 }, /* gov.me */ + { 30167, 0, 0, 1 }, /* hopto.me */ + { 30173, 0, 0, 1 }, /* i234.me */ + { 18633, 0, 0, 1 }, /* its.me */ + { 30178, 0, 0, 1 }, /* loginto.me */ + { 30186, 0, 0, 1 }, /* myds.me */ + { 5737, 0, 0, 1 }, /* net.me */ + { 30191, 0, 0, 1 }, /* noip.me */ + { 6053, 0, 0, 1 }, /* org.me */ + { 11418, 0, 0, 1 }, /* priv.me */ + { 30196, 0, 0, 1 }, /* synology.me */ + { 11634, 0, 0, 1 }, /* webhop.me */ + { 16315, 0, 0, 1 }, /* wedeploy.me */ + { 30205, 0, 0, 1 }, /* yombo.me */ + { 1914, 3838, 1, 1 }, /* com.mt */ + { 2623, 0, 0, 1 }, /* edu.mt */ + { 5737, 0, 0, 1 }, /* net.mt */ + { 6053, 0, 0, 1 }, /* org.mt */ + { 1227, 7188, 1, 0 }, /* her.name */ + { 14142, 7188, 1, 0 }, /* his.name */ + { 2226, 3853, 1, 0 }, /* alwaysdata.net */ + { 1365, 0, 0, 1 }, /* at-band-camp.net */ + { 5432, 0, 0, 1 }, /* azure-mobile.net */ + { 30093, 0, 0, 1 }, /* azurewebsites.net */ + { 11518, 0, 0, 1 }, /* barsy.net */ + { 12188, 0, 0, 1 }, /* blogdns.net */ + { 34255, 0, 0, 1 }, /* bounceme.net */ + { 12225, 0, 0, 1 }, /* bplaced.net */ + { 34264, 0, 0, 1 }, /* broke-it.net */ + { 34273, 0, 0, 1 }, /* buyshouses.net */ + { 34284, 7191, 1, 0 }, /* cdn77.net */ + { 34290, 0, 0, 1 }, /* cdn77-ssl.net */ + { 15880, 0, 0, 1 }, /* cloudaccess.net */ + { 34300, 0, 0, 1 }, /* cloudapp.net */ + { 34309, 0, 0, 1 }, /* cloudfront.net */ + { 34320, 0, 0, 1 }, /* cloudfunctions.net */ + { 34335, 3853, 1, 0 }, /* cryptonomic.net */ + { 12391, 0, 0, 1 }, /* ddns.net */ + { 16172, 0, 0, 1 }, /* definima.net */ + { 12329, 0, 0, 1 }, /* dnsalias.net */ + { 12338, 0, 0, 1 }, /* dnsdojo.net */ + { 34347, 0, 0, 1 }, /* does-it.net */ + { 12369, 0, 0, 1 }, /* dontexist.net */ + { 12409, 0, 0, 1 }, /* dsmynas.net */ + { 12428, 0, 0, 1 }, /* dynalias.net */ + { 34355, 0, 0, 1 }, /* dynathome.net */ + { 34365, 0, 0, 1 }, /* dynv6.net */ + { 6057, 0, 0, 1 }, /* eating-organic.net */ + { 34371, 0, 0, 1 }, /* endofinternet.net */ + { 12652, 0, 0, 1 }, /* familyds.net */ + { 34385, 2502, 4, 0 }, /* fastly.net */ + { 34392, 7197, 1, 1 }, /* fastlylb.net */ + { 34401, 0, 0, 1 }, /* feste-ip.net */ + { 12679, 0, 0, 1 }, /* firewall-gateway.net */ + { 4038, 0, 0, 1 }, /* flynnhosting.net */ + { 34410, 0, 0, 1 }, /* from-az.net */ + { 34418, 0, 0, 1 }, /* from-co.net */ + { 34426, 0, 0, 1 }, /* from-la.net */ + { 34434, 0, 0, 1 }, /* from-ny.net */ + { 3426, 0, 0, 1 }, /* gb.net */ + { 34442, 0, 0, 1 }, /* gets-it.net */ + { 34450, 0, 0, 1 }, /* ham-radio-op.net */ + { 34463, 0, 0, 1 }, /* homeftp.net */ + { 34471, 0, 0, 1 }, /* homeip.net */ + { 13225, 0, 0, 1 }, /* homelinux.net */ + { 13266, 0, 0, 1 }, /* homeunix.net */ + { 4119, 0, 0, 1 }, /* hu.net */ { 898, 0, 0, 1 }, /* in.net */ { 718, 0, 0, 1 }, /* in-the-band.net */ - { 13198, 0, 0, 1 }, /* is-a-chef.net */ - { 13290, 0, 0, 1 }, /* is-a-geek.net */ - { 13713, 0, 0, 1 }, /* isa-geek.net */ - { 4495, 0, 0, 1 }, /* jp.net */ - { 34099, 0, 0, 1 }, /* kicks-ass.net */ - { 34109, 0, 0, 1 }, /* knx-server.net */ - { 34120, 0, 0, 1 }, /* mydissent.net */ - { 34130, 0, 0, 1 }, /* myeffect.net */ - { 8086, 0, 0, 1 }, /* myfritz.net */ - { 34139, 0, 0, 1 }, /* mymediapc.net */ - { 7622, 0, 0, 1 }, /* mypsx.net */ - { 1347, 0, 0, 1 }, /* mysecuritycamera.net */ - { 34149, 0, 0, 1 }, /* nhlfan.net */ - { 11588, 0, 0, 1 }, /* no-ip.net */ - { 34156, 0, 0, 1 }, /* office-on-the.net */ - { 34170, 0, 0, 1 }, /* pgafan.net */ - { 10655, 0, 0, 1 }, /* podzone.net */ - { 34177, 0, 0, 1 }, /* privatizehealthinsurance.net */ - { 14002, 0, 0, 1 }, /* rackmaze.net */ - { 34202, 0, 0, 1 }, /* redirectme.net */ - { 34213, 0, 0, 1 }, /* scrapper-site.net */ - { 1498, 0, 0, 1 }, /* se.net */ - { 11594, 0, 0, 1 }, /* selfip.net */ - { 34227, 0, 0, 1 }, /* sells-it.net */ - { 14080, 0, 0, 1 }, /* servebbs.net */ - { 1029, 0, 0, 1 }, /* serveblog.net */ - { 14108, 0, 0, 1 }, /* serveftp.net */ - { 34236, 0, 0, 1 }, /* serveminecraft.net */ - { 34251, 0, 0, 1 }, /* static-access.net */ - { 13996, 0, 0, 1 }, /* sytes.net */ - { 34265, 0, 0, 1 }, /* t3l3p0rt.net */ - { 3883, 0, 0, 1 }, /* thruhere.net */ - { 8122, 0, 0, 1 }, /* uk.net */ - { 11601, 0, 0, 1 }, /* webhop.net */ - { 6329, 0, 0, 1 }, /* za.net */ - { 6431, 7045, 2, 0 }, /* prod.fastly.net */ - { 13051, 7047, 3, 0 }, /* ssl.fastly.net */ - { 34274, 3687, 1, 0 }, /* alces.network */ - { 1913, 3672, 1, 1 }, /* com.ng */ - { 2624, 0, 0, 1 }, /* edu.ng */ - { 3686, 0, 0, 1 }, /* gov.ng */ + { 34478, 0, 0, 1 }, /* ipifony.net */ + { 13357, 0, 0, 1 }, /* is-a-chef.net */ + { 13449, 0, 0, 1 }, /* is-a-geek.net */ + { 13872, 0, 0, 1 }, /* isa-geek.net */ + { 4474, 0, 0, 1 }, /* jp.net */ + { 34486, 0, 0, 1 }, /* kicks-ass.net */ + { 34496, 0, 0, 1 }, /* knx-server.net */ + { 34507, 0, 0, 1 }, /* moonscale.net */ + { 34517, 0, 0, 1 }, /* mydissent.net */ + { 34527, 0, 0, 1 }, /* myeffect.net */ + { 8084, 0, 0, 1 }, /* myfritz.net */ + { 34536, 0, 0, 1 }, /* mymediapc.net */ + { 7620, 0, 0, 1 }, /* mypsx.net */ + { 1348, 0, 0, 1 }, /* mysecuritycamera.net */ + { 34546, 0, 0, 1 }, /* nhlfan.net */ + { 11621, 0, 0, 1 }, /* no-ip.net */ + { 34553, 0, 0, 1 }, /* office-on-the.net */ + { 34567, 0, 0, 1 }, /* pgafan.net */ + { 10634, 0, 0, 1 }, /* podzone.net */ + { 34574, 0, 0, 1 }, /* privatizehealthinsurance.net */ + { 14193, 0, 0, 1 }, /* rackmaze.net */ + { 34599, 0, 0, 1 }, /* redirectme.net */ + { 2191, 0, 0, 1 }, /* ru.net */ + { 34610, 0, 0, 1 }, /* scrapper-site.net */ + { 1499, 0, 0, 1 }, /* se.net */ + { 11627, 0, 0, 1 }, /* selfip.net */ + { 34624, 0, 0, 1 }, /* sells-it.net */ + { 14271, 0, 0, 1 }, /* servebbs.net */ + { 1030, 0, 0, 1 }, /* serveblog.net */ + { 14299, 0, 0, 1 }, /* serveftp.net */ + { 34633, 0, 0, 1 }, /* serveminecraft.net */ + { 11915, 0, 0, 1 }, /* square7.net */ + { 34648, 0, 0, 1 }, /* static-access.net */ + { 14174, 0, 0, 1 }, /* sytes.net */ + { 34662, 0, 0, 1 }, /* t3l3p0rt.net */ + { 3865, 0, 0, 1 }, /* thruhere.net */ + { 11901, 0, 0, 1 }, /* twmail.net */ + { 8120, 0, 0, 1 }, /* uk.net */ + { 11634, 0, 0, 1 }, /* webhop.net */ + { 6312, 0, 0, 1 }, /* za.net */ + { 34671, 0, 0, 1 }, /* freetls.fastly.net */ + { 5198, 0, 0, 1 }, /* map.fastly.net */ + { 6414, 7192, 2, 0 }, /* prod.fastly.net */ + { 13210, 7194, 3, 0 }, /* ssl.fastly.net */ + { 34679, 3853, 1, 0 }, /* alces.network */ + { 1914, 3838, 1, 1 }, /* com.ng */ + { 2623, 0, 0, 1 }, /* edu.ng */ + { 3671, 0, 0, 1 }, /* gov.ng */ { 58, 0, 0, 1 }, /* i.ng */ - { 4195, 0, 0, 1 }, /* mil.ng */ - { 5448, 0, 0, 1 }, /* mobi.ng */ - { 5725, 0, 0, 1 }, /* name.ng */ - { 4185, 0, 0, 1 }, /* net.ng */ - { 6070, 0, 0, 1 }, /* org.ng */ - { 1145, 0, 0, 1 }, /* sch.ng */ - { 10666, 0, 0, 1 }, /* blogspot.nl */ - { 1289, 0, 0, 1 }, /* bv.nl */ + { 4170, 0, 0, 1 }, /* mil.ng */ + { 5427, 0, 0, 1 }, /* mobi.ng */ + { 5695, 0, 0, 1 }, /* name.ng */ + { 5737, 0, 0, 1 }, /* net.ng */ + { 6053, 0, 0, 1 }, /* org.ng */ + { 1146, 0, 0, 1 }, /* sch.ng */ + { 10645, 0, 0, 1 }, /* blogspot.nl */ + { 1290, 0, 0, 1 }, /* bv.nl */ + { 34685, 0, 0, 1 }, /* cistron.nl */ { 113, 0, 0, 1 }, /* co.nl */ - { 11450, 3687, 1, 0 }, /* transurl.nl */ - { 34280, 0, 0, 1 }, /* virtueeldomein.nl */ - { 1, 7074, 1, 1 }, /* aa.no */ - { 34295, 0, 0, 1 }, /* aarborte.no */ - { 34304, 0, 0, 1 }, /* aejrie.no */ - { 34312, 0, 0, 1 }, /* afjord.no */ - { 34319, 0, 0, 1 }, /* agdenes.no */ - { 11875, 7074, 1, 1 }, /* ah.no */ - { 34327, 7075, 1, 1 }, /* akershus.no */ - { 34336, 0, 0, 1 }, /* aknoluokta.no */ - { 34347, 0, 0, 1 }, /* akrehamn.no */ + { 34693, 0, 0, 1 }, /* demon.nl */ + { 11475, 3853, 1, 0 }, /* transurl.nl */ + { 34699, 0, 0, 1 }, /* virtueeldomein.nl */ + { 1, 7222, 1, 1 }, /* aa.no */ + { 34714, 0, 0, 1 }, /* aarborte.no */ + { 34723, 0, 0, 1 }, /* aejrie.no */ + { 34731, 0, 0, 1 }, /* afjord.no */ + { 34738, 0, 0, 1 }, /* agdenes.no */ + { 12011, 7222, 1, 1 }, /* ah.no */ + { 34746, 7223, 1, 0 }, /* akershus.no */ + { 34755, 0, 0, 1 }, /* aknoluokta.no */ + { 34766, 0, 0, 1 }, /* akrehamn.no */ { 290, 0, 0, 1 }, /* al.no */ - { 34356, 0, 0, 1 }, /* alaheadju.no */ - { 34366, 0, 0, 1 }, /* alesund.no */ - { 34374, 0, 0, 1 }, /* algard.no */ - { 34381, 0, 0, 1 }, /* alstahaug.no */ - { 34392, 0, 0, 1 }, /* alta.no */ - { 34397, 0, 0, 1 }, /* alvdal.no */ - { 34404, 0, 0, 1 }, /* amli.no */ - { 34409, 0, 0, 1 }, /* amot.no */ - { 34414, 0, 0, 1 }, /* andasuolo.no */ - { 34424, 0, 0, 1 }, /* andebu.no */ - { 34432, 0, 0, 1 }, /* andoy.no */ - { 34439, 0, 0, 1 }, /* ardal.no */ - { 34445, 0, 0, 1 }, /* aremark.no */ - { 34453, 0, 0, 1 }, /* arendal.no */ - { 5690, 0, 0, 1 }, /* arna.no */ - { 34461, 0, 0, 1 }, /* aseral.no */ - { 34468, 0, 0, 1 }, /* asker.no */ - { 4595, 0, 0, 1 }, /* askim.no */ - { 34474, 0, 0, 1 }, /* askoy.no */ - { 34480, 0, 0, 1 }, /* askvoll.no */ - { 34488, 0, 0, 1 }, /* asnes.no */ - { 34494, 0, 0, 1 }, /* audnedaln.no */ - { 34504, 0, 0, 1 }, /* aukra.no */ - { 34510, 0, 0, 1 }, /* aure.no */ - { 34515, 0, 0, 1 }, /* aurland.no */ - { 34523, 0, 0, 1 }, /* aurskog-holand.no */ - { 34538, 0, 0, 1 }, /* austevoll.no */ - { 34548, 0, 0, 1 }, /* austrheim.no */ - { 34558, 0, 0, 1 }, /* averoy.no */ - { 34565, 0, 0, 1 }, /* badaddja.no */ - { 34574, 0, 0, 1 }, /* bahcavuotna.no */ - { 34586, 0, 0, 1 }, /* bahccavuotna.no */ - { 34599, 0, 0, 1 }, /* baidar.no */ - { 34606, 0, 0, 1 }, /* bajddar.no */ - { 4815, 0, 0, 1 }, /* balat.no */ - { 34614, 0, 0, 1 }, /* balestrand.no */ - { 34625, 0, 0, 1 }, /* ballangen.no */ - { 34635, 0, 0, 1 }, /* balsfjord.no */ - { 34645, 0, 0, 1 }, /* bamble.no */ - { 34652, 0, 0, 1 }, /* bardu.no */ - { 34658, 0, 0, 1 }, /* barum.no */ - { 34664, 0, 0, 1 }, /* batsfjord.no */ - { 34674, 0, 0, 1 }, /* bearalvahki.no */ - { 34686, 0, 0, 1 }, /* beardu.no */ - { 34693, 0, 0, 1 }, /* beiarn.no */ - { 1044, 0, 0, 1 }, /* berg.no */ - { 15724, 0, 0, 1 }, /* bergen.no */ - { 34709, 0, 0, 1 }, /* berlevag.no */ - { 34718, 0, 0, 1 }, /* bievat.no */ - { 34725, 0, 0, 1 }, /* bindal.no */ - { 34732, 0, 0, 1 }, /* birkenes.no */ - { 34741, 0, 0, 1 }, /* bjarkoy.no */ - { 34749, 0, 0, 1 }, /* bjerkreim.no */ - { 3607, 0, 0, 1 }, /* bjugn.no */ - { 10666, 0, 0, 1 }, /* blogspot.no */ - { 34759, 0, 0, 1 }, /* bodo.no */ - { 4631, 0, 0, 1 }, /* bokn.no */ - { 34764, 0, 0, 1 }, /* bomlo.no */ - { 34770, 0, 0, 1 }, /* bremanger.no */ - { 34780, 0, 0, 1 }, /* bronnoy.no */ - { 34788, 0, 0, 1 }, /* bronnoysund.no */ - { 34800, 0, 0, 1 }, /* brumunddal.no */ - { 34811, 0, 0, 1 }, /* bryne.no */ - { 19705, 7074, 1, 1 }, /* bu.no */ - { 34817, 0, 0, 1 }, /* budejju.no */ - { 34825, 7075, 1, 1 }, /* buskerud.no */ - { 34834, 0, 0, 1 }, /* bygland.no */ - { 34842, 0, 0, 1 }, /* bykle.no */ - { 34848, 0, 0, 1 }, /* cahcesuolo.no */ + { 34775, 0, 0, 1 }, /* alaheadju.no */ + { 34785, 0, 0, 1 }, /* alesund.no */ + { 34793, 0, 0, 1 }, /* algard.no */ + { 34800, 0, 0, 1 }, /* alstahaug.no */ + { 34811, 0, 0, 1 }, /* alta.no */ + { 34816, 0, 0, 1 }, /* alvdal.no */ + { 34823, 0, 0, 1 }, /* amli.no */ + { 34828, 0, 0, 1 }, /* amot.no */ + { 34833, 0, 0, 1 }, /* andasuolo.no */ + { 34843, 0, 0, 1 }, /* andebu.no */ + { 34851, 0, 0, 1 }, /* andoy.no */ + { 34858, 0, 0, 1 }, /* ardal.no */ + { 34864, 0, 0, 1 }, /* aremark.no */ + { 34872, 0, 0, 1 }, /* arendal.no */ + { 5660, 0, 0, 1 }, /* arna.no */ + { 34880, 0, 0, 1 }, /* aseral.no */ + { 34887, 0, 0, 1 }, /* asker.no */ + { 4574, 0, 0, 1 }, /* askim.no */ + { 34893, 0, 0, 1 }, /* askoy.no */ + { 34899, 0, 0, 1 }, /* askvoll.no */ + { 34907, 0, 0, 1 }, /* asnes.no */ + { 34913, 0, 0, 1 }, /* audnedaln.no */ + { 34923, 0, 0, 1 }, /* aukra.no */ + { 34929, 0, 0, 1 }, /* aure.no */ + { 34934, 0, 0, 1 }, /* aurland.no */ + { 34942, 0, 0, 1 }, /* aurskog-holand.no */ + { 34957, 0, 0, 1 }, /* austevoll.no */ + { 34967, 0, 0, 1 }, /* austrheim.no */ + { 34977, 0, 0, 1 }, /* averoy.no */ + { 34984, 0, 0, 1 }, /* badaddja.no */ + { 34993, 0, 0, 1 }, /* bahcavuotna.no */ + { 35005, 0, 0, 1 }, /* bahccavuotna.no */ + { 35018, 0, 0, 1 }, /* baidar.no */ + { 35025, 0, 0, 1 }, /* bajddar.no */ + { 4794, 0, 0, 1 }, /* balat.no */ + { 35033, 0, 0, 1 }, /* balestrand.no */ + { 35044, 0, 0, 1 }, /* ballangen.no */ + { 35054, 0, 0, 1 }, /* balsfjord.no */ + { 35064, 0, 0, 1 }, /* bamble.no */ + { 35071, 0, 0, 1 }, /* bardu.no */ + { 35077, 0, 0, 1 }, /* barum.no */ + { 35083, 0, 0, 1 }, /* batsfjord.no */ + { 35093, 0, 0, 1 }, /* bearalvahki.no */ + { 35105, 0, 0, 1 }, /* beardu.no */ + { 35112, 0, 0, 1 }, /* beiarn.no */ + { 1045, 0, 0, 1 }, /* berg.no */ + { 16017, 0, 0, 1 }, /* bergen.no */ + { 35128, 0, 0, 1 }, /* berlevag.no */ + { 35137, 0, 0, 1 }, /* bievat.no */ + { 35144, 0, 0, 1 }, /* bindal.no */ + { 35151, 0, 0, 1 }, /* birkenes.no */ + { 35160, 0, 0, 1 }, /* bjarkoy.no */ + { 35168, 0, 0, 1 }, /* bjerkreim.no */ + { 3592, 0, 0, 1 }, /* bjugn.no */ + { 10645, 0, 0, 1 }, /* blogspot.no */ + { 35178, 0, 0, 1 }, /* bodo.no */ + { 4610, 0, 0, 1 }, /* bokn.no */ + { 35183, 0, 0, 1 }, /* bomlo.no */ + { 35189, 0, 0, 1 }, /* bremanger.no */ + { 35199, 0, 0, 1 }, /* bronnoy.no */ + { 35207, 0, 0, 1 }, /* bronnoysund.no */ + { 35219, 0, 0, 1 }, /* brumunddal.no */ + { 35230, 0, 0, 1 }, /* bryne.no */ + { 20050, 7222, 1, 1 }, /* bu.no */ + { 35236, 0, 0, 1 }, /* budejju.no */ + { 35244, 7223, 1, 0 }, /* buskerud.no */ + { 35253, 0, 0, 1 }, /* bygland.no */ + { 35261, 0, 0, 1 }, /* bykle.no */ + { 35267, 0, 0, 1 }, /* cahcesuolo.no */ { 113, 0, 0, 1 }, /* co.no */ - { 34859, 0, 0, 1 }, /* davvenjarga.no */ - { 25699, 0, 0, 1 }, /* davvesiida.no */ - { 34871, 0, 0, 1 }, /* deatnu.no */ - { 34878, 0, 0, 1 }, /* dep.no */ - { 34882, 0, 0, 1 }, /* dielddanuorri.no */ - { 34896, 0, 0, 1 }, /* divtasvuodna.no */ - { 34909, 0, 0, 1 }, /* divttasvuotna.no */ - { 26989, 0, 0, 1 }, /* donna.no */ - { 34923, 0, 0, 1 }, /* dovre.no */ - { 5362, 0, 0, 1 }, /* drammen.no */ - { 34929, 0, 0, 1 }, /* drangedal.no */ - { 34939, 0, 0, 1 }, /* drobak.no */ - { 34946, 0, 0, 1 }, /* dyroy.no */ - { 34952, 0, 0, 1 }, /* egersund.no */ - { 34964, 0, 0, 1 }, /* eid.no */ - { 34968, 0, 0, 1 }, /* eidfjord.no */ - { 34700, 0, 0, 1 }, /* eidsberg.no */ - { 34977, 0, 0, 1 }, /* eidskog.no */ - { 34985, 0, 0, 1 }, /* eidsvoll.no */ - { 34994, 0, 0, 1 }, /* eigersund.no */ - { 35004, 0, 0, 1 }, /* elverum.no */ - { 35012, 0, 0, 1 }, /* enebakk.no */ - { 35020, 0, 0, 1 }, /* engerdal.no */ - { 5760, 0, 0, 1 }, /* etne.no */ - { 35029, 0, 0, 1 }, /* etnedal.no */ - { 35037, 0, 0, 1 }, /* evenassi.no */ - { 35046, 0, 0, 1 }, /* evenes.no */ - { 35053, 0, 0, 1 }, /* evje-og-hornnes.no */ - { 35069, 0, 0, 1 }, /* farsund.no */ - { 35077, 0, 0, 1 }, /* fauske.no */ - { 4422, 0, 0, 1 }, /* fedje.no */ - { 2785, 0, 0, 1 }, /* fet.no */ - { 35084, 0, 0, 1 }, /* fetsund.no */ - { 29704, 0, 0, 1 }, /* fhs.no */ - { 35092, 0, 0, 1 }, /* finnoy.no */ - { 35099, 0, 0, 1 }, /* fitjar.no */ - { 35106, 0, 0, 1 }, /* fjaler.no */ - { 35113, 0, 0, 1 }, /* fjell.no */ - { 4717, 0, 0, 1 }, /* fla.no */ - { 35119, 0, 0, 1 }, /* flakstad.no */ - { 35128, 0, 0, 1 }, /* flatanger.no */ - { 35138, 0, 0, 1 }, /* flekkefjord.no */ - { 35150, 0, 0, 1 }, /* flesberg.no */ - { 35159, 0, 0, 1 }, /* flora.no */ - { 35165, 0, 0, 1 }, /* floro.no */ - { 3160, 7074, 1, 1 }, /* fm.no */ - { 35171, 0, 0, 1 }, /* folkebibl.no */ - { 35181, 0, 0, 1 }, /* folldal.no */ - { 35189, 0, 0, 1 }, /* forde.no */ - { 35195, 0, 0, 1 }, /* forsand.no */ - { 35203, 0, 0, 1 }, /* fosnes.no */ - { 35210, 0, 0, 1 }, /* frana.no */ - { 35216, 0, 0, 1 }, /* fredrikstad.no */ - { 35228, 0, 0, 1 }, /* frei.no */ - { 35233, 0, 0, 1 }, /* frogn.no */ - { 35239, 0, 0, 1 }, /* froland.no */ - { 35247, 0, 0, 1 }, /* frosta.no */ - { 35254, 0, 0, 1 }, /* froya.no */ - { 35260, 0, 0, 1 }, /* fuoisku.no */ - { 35268, 0, 0, 1 }, /* fuossko.no */ - { 20533, 0, 0, 1 }, /* fusa.no */ - { 35276, 0, 0, 1 }, /* fylkesbibl.no */ - { 35287, 0, 0, 1 }, /* fyresdal.no */ - { 35296, 0, 0, 1 }, /* gaivuotna.no */ - { 35306, 0, 0, 1 }, /* galsa.no */ - { 35312, 0, 0, 1 }, /* gamvik.no */ - { 35319, 0, 0, 1 }, /* gangaviika.no */ - { 35330, 0, 0, 1 }, /* gaular.no */ - { 35337, 0, 0, 1 }, /* gausdal.no */ - { 35345, 0, 0, 1 }, /* giehtavuoatna.no */ - { 35359, 0, 0, 1 }, /* gildeskal.no */ - { 35369, 0, 0, 1 }, /* giske.no */ - { 35375, 0, 0, 1 }, /* gjemnes.no */ - { 35383, 0, 0, 1 }, /* gjerdrum.no */ - { 35392, 0, 0, 1 }, /* gjerstad.no */ - { 35401, 0, 0, 1 }, /* gjesdal.no */ - { 35409, 0, 0, 1 }, /* gjovik.no */ - { 35416, 0, 0, 1 }, /* gloppen.no */ - { 35424, 0, 0, 1 }, /* gol.no */ - { 35428, 0, 0, 1 }, /* gran.no */ - { 35433, 0, 0, 1 }, /* grane.no */ - { 8271, 0, 0, 1 }, /* granvin.no */ - { 35439, 0, 0, 1 }, /* gratangen.no */ - { 35449, 0, 0, 1 }, /* grimstad.no */ - { 35458, 0, 0, 1 }, /* grong.no */ - { 35464, 0, 0, 1 }, /* grue.no */ - { 35469, 0, 0, 1 }, /* gulen.no */ - { 35475, 0, 0, 1 }, /* guovdageaidnu.no */ - { 2515, 0, 0, 1 }, /* ha.no */ - { 35489, 0, 0, 1 }, /* habmer.no */ - { 35496, 0, 0, 1 }, /* hadsel.no */ - { 35503, 0, 0, 1 }, /* hagebostad.no */ - { 35514, 0, 0, 1 }, /* halden.no */ - { 35521, 0, 0, 1 }, /* halsa.no */ - { 17217, 0, 0, 1 }, /* hamar.no */ - { 35527, 0, 0, 1 }, /* hamaroy.no */ - { 35535, 0, 0, 1 }, /* hammarfeasta.no */ - { 35548, 0, 0, 1 }, /* hammerfest.no */ - { 35559, 0, 0, 1 }, /* hapmir.no */ - { 35566, 0, 0, 1 }, /* haram.no */ - { 34961, 0, 0, 1 }, /* hareid.no */ - { 35572, 0, 0, 1 }, /* harstad.no */ - { 35580, 0, 0, 1 }, /* hasvik.no */ - { 35587, 0, 0, 1 }, /* hattfjelldal.no */ - { 35600, 0, 0, 1 }, /* haugesund.no */ - { 35610, 7076, 3, 1 }, /* hedmark.no */ - { 35618, 0, 0, 1 }, /* hemne.no */ - { 35624, 0, 0, 1 }, /* hemnes.no */ - { 35631, 0, 0, 1 }, /* hemsedal.no */ - { 35643, 0, 0, 1 }, /* herad.no */ - { 29615, 0, 0, 1 }, /* hitra.no */ - { 35649, 0, 0, 1 }, /* hjartdal.no */ - { 35658, 0, 0, 1 }, /* hjelmeland.no */ - { 2385, 7074, 1, 1 }, /* hl.no */ - { 3947, 7074, 1, 1 }, /* hm.no */ - { 35669, 0, 0, 1 }, /* hobol.no */ - { 30446, 0, 0, 1 }, /* hof.no */ - { 35675, 0, 0, 1 }, /* hokksund.no */ - { 35684, 0, 0, 1 }, /* hol.no */ - { 35688, 0, 0, 1 }, /* hole.no */ - { 35693, 0, 0, 1 }, /* holmestrand.no */ - { 35705, 0, 0, 1 }, /* holtalen.no */ - { 35714, 0, 0, 1 }, /* honefoss.no */ - { 15252, 7079, 1, 1 }, /* hordaland.no */ - { 35723, 0, 0, 1 }, /* hornindal.no */ - { 35733, 0, 0, 1 }, /* horten.no */ - { 35740, 0, 0, 1 }, /* hoyanger.no */ - { 35749, 0, 0, 1 }, /* hoylandet.no */ - { 35759, 0, 0, 1 }, /* hurdal.no */ - { 35766, 0, 0, 1 }, /* hurum.no */ - { 35772, 0, 0, 1 }, /* hvaler.no */ - { 35779, 0, 0, 1 }, /* hyllestad.no */ - { 35789, 0, 0, 1 }, /* ibestad.no */ - { 35797, 0, 0, 1 }, /* idrett.no */ - { 35804, 0, 0, 1 }, /* inderoy.no */ - { 35812, 0, 0, 1 }, /* iveland.no */ - { 3766, 0, 0, 1 }, /* ivgu.no */ - { 35820, 7074, 1, 1 }, /* jan-mayen.no */ - { 35830, 0, 0, 1 }, /* jessheim.no */ - { 35839, 0, 0, 1 }, /* jevnaker.no */ - { 35848, 0, 0, 1 }, /* jolster.no */ - { 35856, 0, 0, 1 }, /* jondal.no */ - { 35863, 0, 0, 1 }, /* jorpeland.no */ - { 34311, 0, 0, 1 }, /* kafjord.no */ - { 35873, 0, 0, 1 }, /* karasjohka.no */ - { 35884, 0, 0, 1 }, /* karasjok.no */ - { 35893, 0, 0, 1 }, /* karlsoy.no */ - { 35901, 0, 0, 1 }, /* karmoy.no */ - { 35908, 0, 0, 1 }, /* kautokeino.no */ - { 35919, 0, 0, 1 }, /* kirkenes.no */ - { 35928, 0, 0, 1 }, /* klabu.no */ - { 11444, 0, 0, 1 }, /* klepp.no */ - { 35934, 0, 0, 1 }, /* kommune.no */ - { 35942, 0, 0, 1 }, /* kongsberg.no */ - { 35952, 0, 0, 1 }, /* kongsvinger.no */ - { 35964, 0, 0, 1 }, /* kopervik.no */ - { 35973, 0, 0, 1 }, /* kraanghke.no */ - { 35983, 0, 0, 1 }, /* kragero.no */ - { 35991, 0, 0, 1 }, /* kristiansand.no */ - { 36004, 0, 0, 1 }, /* kristiansund.no */ - { 36017, 0, 0, 1 }, /* krodsherad.no */ - { 36028, 0, 0, 1 }, /* krokstadelva.no */ - { 36041, 0, 0, 1 }, /* kvafjord.no */ - { 36050, 0, 0, 1 }, /* kvalsund.no */ + { 35278, 0, 0, 1 }, /* davvenjarga.no */ + { 26044, 0, 0, 1 }, /* davvesiida.no */ + { 35290, 0, 0, 1 }, /* deatnu.no */ + { 35297, 0, 0, 1 }, /* dep.no */ + { 35301, 0, 0, 1 }, /* dielddanuorri.no */ + { 35315, 0, 0, 1 }, /* divtasvuodna.no */ + { 35328, 0, 0, 1 }, /* divttasvuotna.no */ + { 27334, 0, 0, 1 }, /* donna.no */ + { 35342, 0, 0, 1 }, /* dovre.no */ + { 5341, 0, 0, 1 }, /* drammen.no */ + { 35348, 0, 0, 1 }, /* drangedal.no */ + { 35358, 0, 0, 1 }, /* drobak.no */ + { 35365, 0, 0, 1 }, /* dyroy.no */ + { 35371, 0, 0, 1 }, /* egersund.no */ + { 35383, 0, 0, 1 }, /* eid.no */ + { 35387, 0, 0, 1 }, /* eidfjord.no */ + { 35119, 0, 0, 1 }, /* eidsberg.no */ + { 35396, 0, 0, 1 }, /* eidskog.no */ + { 35404, 0, 0, 1 }, /* eidsvoll.no */ + { 35413, 0, 0, 1 }, /* eigersund.no */ + { 35423, 0, 0, 1 }, /* elverum.no */ + { 35431, 0, 0, 1 }, /* enebakk.no */ + { 35439, 0, 0, 1 }, /* engerdal.no */ + { 5730, 0, 0, 1 }, /* etne.no */ + { 35448, 0, 0, 1 }, /* etnedal.no */ + { 35456, 0, 0, 1 }, /* evenassi.no */ + { 35465, 0, 0, 1 }, /* evenes.no */ + { 35472, 0, 0, 1 }, /* evje-og-hornnes.no */ + { 35488, 0, 0, 1 }, /* farsund.no */ + { 35496, 0, 0, 1 }, /* fauske.no */ + { 4401, 0, 0, 1 }, /* fedje.no */ + { 2784, 0, 0, 1 }, /* fet.no */ + { 35503, 0, 0, 1 }, /* fetsund.no */ + { 30049, 0, 0, 1 }, /* fhs.no */ + { 35511, 0, 0, 1 }, /* finnoy.no */ + { 35518, 0, 0, 1 }, /* fitjar.no */ + { 35525, 0, 0, 1 }, /* fjaler.no */ + { 35532, 0, 0, 1 }, /* fjell.no */ + { 4696, 0, 0, 1 }, /* fla.no */ + { 35538, 0, 0, 1 }, /* flakstad.no */ + { 35547, 0, 0, 1 }, /* flatanger.no */ + { 35557, 0, 0, 1 }, /* flekkefjord.no */ + { 35569, 0, 0, 1 }, /* flesberg.no */ + { 35578, 0, 0, 1 }, /* flora.no */ + { 35584, 0, 0, 1 }, /* floro.no */ + { 3145, 7222, 1, 1 }, /* fm.no */ + { 35590, 0, 0, 1 }, /* folkebibl.no */ + { 35600, 0, 0, 1 }, /* folldal.no */ + { 35608, 0, 0, 1 }, /* forde.no */ + { 35614, 0, 0, 1 }, /* forsand.no */ + { 35622, 0, 0, 1 }, /* fosnes.no */ + { 35629, 0, 0, 1 }, /* frana.no */ + { 35635, 0, 0, 1 }, /* fredrikstad.no */ + { 35647, 0, 0, 1 }, /* frei.no */ + { 35652, 0, 0, 1 }, /* frogn.no */ + { 35658, 0, 0, 1 }, /* froland.no */ + { 35666, 0, 0, 1 }, /* frosta.no */ + { 35673, 0, 0, 1 }, /* froya.no */ + { 35679, 0, 0, 1 }, /* fuoisku.no */ + { 35687, 0, 0, 1 }, /* fuossko.no */ + { 20878, 0, 0, 1 }, /* fusa.no */ + { 35695, 0, 0, 1 }, /* fylkesbibl.no */ + { 35706, 0, 0, 1 }, /* fyresdal.no */ + { 35715, 0, 0, 1 }, /* gaivuotna.no */ + { 35725, 0, 0, 1 }, /* galsa.no */ + { 35731, 0, 0, 1 }, /* gamvik.no */ + { 35738, 0, 0, 1 }, /* gangaviika.no */ + { 35749, 0, 0, 1 }, /* gaular.no */ + { 35756, 0, 0, 1 }, /* gausdal.no */ + { 35764, 0, 0, 1 }, /* giehtavuoatna.no */ + { 35778, 0, 0, 1 }, /* gildeskal.no */ + { 35788, 0, 0, 1 }, /* giske.no */ + { 35794, 0, 0, 1 }, /* gjemnes.no */ + { 35802, 0, 0, 1 }, /* gjerdrum.no */ + { 35811, 0, 0, 1 }, /* gjerstad.no */ + { 35820, 0, 0, 1 }, /* gjesdal.no */ + { 35828, 0, 0, 1 }, /* gjovik.no */ + { 35835, 0, 0, 1 }, /* gloppen.no */ + { 35843, 0, 0, 1 }, /* gol.no */ + { 35847, 0, 0, 1 }, /* gran.no */ + { 35852, 0, 0, 1 }, /* grane.no */ + { 8264, 0, 0, 1 }, /* granvin.no */ + { 35858, 0, 0, 1 }, /* gratangen.no */ + { 35868, 0, 0, 1 }, /* grimstad.no */ + { 35877, 0, 0, 1 }, /* grong.no */ + { 35883, 0, 0, 1 }, /* grue.no */ + { 35888, 0, 0, 1 }, /* gulen.no */ + { 35894, 0, 0, 1 }, /* guovdageaidnu.no */ + { 2518, 0, 0, 1 }, /* ha.no */ + { 35908, 0, 0, 1 }, /* habmer.no */ + { 35915, 0, 0, 1 }, /* hadsel.no */ + { 35922, 0, 0, 1 }, /* hagebostad.no */ + { 35933, 0, 0, 1 }, /* halden.no */ + { 35940, 0, 0, 1 }, /* halsa.no */ + { 17562, 0, 0, 1 }, /* hamar.no */ + { 35946, 0, 0, 1 }, /* hamaroy.no */ + { 35954, 0, 0, 1 }, /* hammarfeasta.no */ + { 35967, 0, 0, 1 }, /* hammerfest.no */ + { 35978, 0, 0, 1 }, /* hapmir.no */ + { 35985, 0, 0, 1 }, /* haram.no */ + { 35380, 0, 0, 1 }, /* hareid.no */ + { 35991, 0, 0, 1 }, /* harstad.no */ + { 35999, 0, 0, 1 }, /* hasvik.no */ + { 36006, 0, 0, 1 }, /* hattfjelldal.no */ + { 36019, 0, 0, 1 }, /* haugesund.no */ + { 36029, 7224, 3, 0 }, /* hedmark.no */ + { 36037, 0, 0, 1 }, /* hemne.no */ + { 36043, 0, 0, 1 }, /* hemnes.no */ + { 36050, 0, 0, 1 }, /* hemsedal.no */ + { 36062, 0, 0, 1 }, /* herad.no */ + { 29960, 0, 0, 1 }, /* hitra.no */ + { 36068, 0, 0, 1 }, /* hjartdal.no */ + { 36077, 0, 0, 1 }, /* hjelmeland.no */ + { 2388, 7222, 1, 1 }, /* hl.no */ + { 3929, 7222, 1, 1 }, /* hm.no */ + { 36088, 0, 0, 1 }, /* hobol.no */ + { 30804, 0, 0, 1 }, /* hof.no */ + { 36094, 0, 0, 1 }, /* hokksund.no */ + { 36103, 0, 0, 1 }, /* hol.no */ + { 36107, 0, 0, 1 }, /* hole.no */ + { 36112, 0, 0, 1 }, /* holmestrand.no */ + { 36124, 0, 0, 1 }, /* holtalen.no */ + { 36133, 0, 0, 1 }, /* honefoss.no */ + { 15529, 7227, 1, 0 }, /* hordaland.no */ + { 36142, 0, 0, 1 }, /* hornindal.no */ + { 36152, 0, 0, 1 }, /* horten.no */ + { 36159, 0, 0, 1 }, /* hoyanger.no */ + { 36168, 0, 0, 1 }, /* hoylandet.no */ + { 36178, 0, 0, 1 }, /* hurdal.no */ + { 36185, 0, 0, 1 }, /* hurum.no */ + { 36191, 0, 0, 1 }, /* hvaler.no */ + { 36198, 0, 0, 1 }, /* hyllestad.no */ + { 36208, 0, 0, 1 }, /* ibestad.no */ + { 36216, 0, 0, 1 }, /* idrett.no */ + { 36223, 0, 0, 1 }, /* inderoy.no */ + { 36231, 0, 0, 1 }, /* iveland.no */ + { 3751, 0, 0, 1 }, /* ivgu.no */ + { 36239, 7222, 1, 1 }, /* jan-mayen.no */ + { 36249, 0, 0, 1 }, /* jessheim.no */ + { 36258, 0, 0, 1 }, /* jevnaker.no */ + { 36267, 0, 0, 1 }, /* jolster.no */ + { 36275, 0, 0, 1 }, /* jondal.no */ + { 36282, 0, 0, 1 }, /* jorpeland.no */ + { 34730, 0, 0, 1 }, /* kafjord.no */ + { 36292, 0, 0, 1 }, /* karasjohka.no */ + { 36303, 0, 0, 1 }, /* karasjok.no */ + { 36312, 0, 0, 1 }, /* karlsoy.no */ + { 36320, 0, 0, 1 }, /* karmoy.no */ + { 36327, 0, 0, 1 }, /* kautokeino.no */ + { 36338, 0, 0, 1 }, /* kirkenes.no */ + { 36347, 0, 0, 1 }, /* klabu.no */ + { 11469, 0, 0, 1 }, /* klepp.no */ + { 36353, 0, 0, 1 }, /* kommune.no */ + { 36361, 0, 0, 1 }, /* kongsberg.no */ + { 36371, 0, 0, 1 }, /* kongsvinger.no */ + { 36383, 0, 0, 1 }, /* kopervik.no */ + { 36392, 0, 0, 1 }, /* kraanghke.no */ + { 36402, 0, 0, 1 }, /* kragero.no */ + { 36410, 0, 0, 1 }, /* kristiansand.no */ + { 36423, 0, 0, 1 }, /* kristiansund.no */ + { 36436, 0, 0, 1 }, /* krodsherad.no */ + { 36447, 0, 0, 1 }, /* krokstadelva.no */ + { 36460, 0, 0, 1 }, /* kvafjord.no */ + { 36469, 0, 0, 1 }, /* kvalsund.no */ { 356, 0, 0, 1 }, /* kvam.no */ - { 36059, 0, 0, 1 }, /* kvanangen.no */ - { 36069, 0, 0, 1 }, /* kvinesdal.no */ - { 36079, 0, 0, 1 }, /* kvinnherad.no */ - { 36090, 0, 0, 1 }, /* kviteseid.no */ - { 36100, 0, 0, 1 }, /* kvitsoy.no */ - { 36108, 0, 0, 1 }, /* laakesvuemie.no */ - { 36121, 0, 0, 1 }, /* lahppi.no */ - { 36128, 0, 0, 1 }, /* langevag.no */ - { 34438, 0, 0, 1 }, /* lardal.no */ - { 36137, 0, 0, 1 }, /* larvik.no */ - { 36144, 0, 0, 1 }, /* lavagis.no */ - { 36152, 0, 0, 1 }, /* lavangen.no */ - { 36161, 0, 0, 1 }, /* leangaviika.no */ - { 36173, 0, 0, 1 }, /* lebesby.no */ - { 36181, 0, 0, 1 }, /* leikanger.no */ - { 36191, 0, 0, 1 }, /* leirfjord.no */ - { 36201, 0, 0, 1 }, /* leirvik.no */ - { 36214, 0, 0, 1 }, /* leka.no */ - { 36219, 0, 0, 1 }, /* leksvik.no */ - { 36227, 0, 0, 1 }, /* lenvik.no */ - { 36234, 0, 0, 1 }, /* lerdal.no */ - { 36241, 0, 0, 1 }, /* lesja.no */ - { 36247, 0, 0, 1 }, /* levanger.no */ - { 2735, 0, 0, 1 }, /* lier.no */ - { 36256, 0, 0, 1 }, /* lierne.no */ - { 36263, 0, 0, 1 }, /* lillehammer.no */ - { 36275, 0, 0, 1 }, /* lillesand.no */ - { 36285, 0, 0, 1 }, /* lindas.no */ - { 36292, 0, 0, 1 }, /* lindesnes.no */ - { 36302, 0, 0, 1 }, /* loabat.no */ - { 36309, 0, 0, 1 }, /* lodingen.no */ - { 17163, 0, 0, 1 }, /* lom.no */ - { 36318, 0, 0, 1 }, /* loppa.no */ - { 36324, 0, 0, 1 }, /* lorenskog.no */ - { 36334, 0, 0, 1 }, /* loten.no */ - { 36342, 0, 0, 1 }, /* lund.no */ - { 36347, 0, 0, 1 }, /* lunner.no */ - { 36354, 0, 0, 1 }, /* luroy.no */ - { 36360, 0, 0, 1 }, /* luster.no */ - { 36367, 0, 0, 1 }, /* lyngdal.no */ - { 36375, 0, 0, 1 }, /* lyngen.no */ - { 36382, 0, 0, 1 }, /* malatvuopmi.no */ - { 5142, 0, 0, 1 }, /* malselv.no */ - { 36394, 0, 0, 1 }, /* malvik.no */ - { 36401, 0, 0, 1 }, /* mandal.no */ - { 36408, 0, 0, 1 }, /* marker.no */ - { 36415, 0, 0, 1 }, /* marnardal.no */ - { 36425, 0, 0, 1 }, /* masfjorden.no */ - { 7401, 0, 0, 1 }, /* masoy.no */ - { 36436, 0, 0, 1 }, /* matta-varjjat.no */ - { 35662, 0, 0, 1 }, /* meland.no */ - { 36450, 0, 0, 1 }, /* meldal.no */ - { 36457, 0, 0, 1 }, /* melhus.no */ - { 36464, 0, 0, 1 }, /* meloy.no */ - { 36470, 0, 0, 1 }, /* meraker.no */ - { 36478, 0, 0, 1 }, /* midsund.no */ - { 36486, 0, 0, 1 }, /* midtre-gauldal.no */ - { 4195, 0, 0, 1 }, /* mil.no */ - { 36501, 0, 0, 1 }, /* mjondalen.no */ - { 36511, 0, 0, 1 }, /* mo-i-rana.no */ - { 36521, 0, 0, 1 }, /* moareke.no */ - { 36529, 0, 0, 1 }, /* modalen.no */ - { 36537, 0, 0, 1 }, /* modum.no */ - { 36543, 0, 0, 1 }, /* molde.no */ - { 36549, 7080, 2, 1 }, /* more-og-romsdal.no */ - { 36565, 0, 0, 1 }, /* mosjoen.no */ - { 36573, 0, 0, 1 }, /* moskenes.no */ - { 17792, 0, 0, 1 }, /* moss.no */ - { 36582, 0, 0, 1 }, /* mosvik.no */ - { 5601, 7074, 1, 1 }, /* mr.no */ - { 36589, 0, 0, 1 }, /* muosat.no */ - { 5644, 0, 0, 1 }, /* museum.no */ - { 36596, 0, 0, 1 }, /* naamesjevuemie.no */ - { 36611, 0, 0, 1 }, /* namdalseid.no */ - { 36622, 0, 0, 1 }, /* namsos.no */ - { 36629, 0, 0, 1 }, /* namsskogan.no */ - { 36640, 0, 0, 1 }, /* nannestad.no */ - { 36650, 0, 0, 1 }, /* naroy.no */ - { 36656, 0, 0, 1 }, /* narviika.no */ - { 36665, 0, 0, 1 }, /* narvik.no */ - { 36672, 0, 0, 1 }, /* naustdal.no */ - { 36681, 0, 0, 1 }, /* navuotna.no */ - { 36690, 0, 0, 1 }, /* nedre-eiker.no */ - { 36702, 0, 0, 1 }, /* nesna.no */ - { 36708, 0, 0, 1 }, /* nesodden.no */ - { 36717, 0, 0, 1 }, /* nesoddtangen.no */ - { 36730, 0, 0, 1 }, /* nesseby.no */ - { 36738, 0, 0, 1 }, /* nesset.no */ - { 36745, 0, 0, 1 }, /* nissedal.no */ - { 36754, 0, 0, 1 }, /* nittedal.no */ - { 1071, 7074, 1, 1 }, /* nl.no */ - { 36763, 0, 0, 1 }, /* nord-aurdal.no */ - { 36775, 0, 0, 1 }, /* nord-fron.no */ - { 36785, 0, 0, 1 }, /* nord-odal.no */ - { 36795, 0, 0, 1 }, /* norddal.no */ - { 36803, 0, 0, 1 }, /* nordkapp.no */ - { 36812, 7082, 4, 1 }, /* nordland.no */ - { 36821, 0, 0, 1 }, /* nordre-land.no */ - { 36833, 0, 0, 1 }, /* nordreisa.no */ - { 36843, 0, 0, 1 }, /* nore-og-uvdal.no */ - { 36857, 0, 0, 1 }, /* notodden.no */ - { 36866, 0, 0, 1 }, /* notteroy.no */ - { 97, 7074, 1, 1 }, /* nt.no */ - { 36875, 0, 0, 1 }, /* odda.no */ - { 6450, 7074, 1, 1 }, /* of.no */ - { 36880, 0, 0, 1 }, /* oksnes.no */ - { 452, 7074, 1, 1 }, /* ol.no */ - { 36887, 0, 0, 1 }, /* omasvuotna.no */ - { 36898, 0, 0, 1 }, /* oppdal.no */ - { 36905, 0, 0, 1 }, /* oppegard.no */ - { 36914, 0, 0, 1 }, /* orkanger.no */ - { 36923, 0, 0, 1 }, /* orkdal.no */ - { 4782, 0, 0, 1 }, /* orland.no */ - { 36930, 0, 0, 1 }, /* orskog.no */ - { 36937, 0, 0, 1 }, /* orsta.no */ - { 26376, 0, 0, 1 }, /* osen.no */ - { 36943, 7074, 1, 1 }, /* oslo.no */ - { 36948, 0, 0, 1 }, /* osoyro.no */ - { 36955, 0, 0, 1 }, /* osteroy.no */ - { 36963, 7086, 1, 1 }, /* ostfold.no */ - { 36971, 0, 0, 1 }, /* ostre-toten.no */ - { 36983, 0, 0, 1 }, /* overhalla.no */ - { 36993, 0, 0, 1 }, /* ovre-eiker.no */ - { 37004, 0, 0, 1 }, /* oyer.no */ - { 37009, 0, 0, 1 }, /* oygarden.no */ - { 37018, 0, 0, 1 }, /* oystre-slidre.no */ - { 37032, 0, 0, 1 }, /* porsanger.no */ - { 37042, 0, 0, 1 }, /* porsangu.no */ - { 37051, 0, 0, 1 }, /* porsgrunn.no */ - { 11393, 0, 0, 1 }, /* priv.no */ - { 7983, 0, 0, 1 }, /* rade.no */ - { 37061, 0, 0, 1 }, /* radoy.no */ - { 37067, 0, 0, 1 }, /* rahkkeravju.no */ - { 37079, 0, 0, 1 }, /* raholt.no */ - { 37086, 0, 0, 1 }, /* raisa.no */ - { 37092, 0, 0, 1 }, /* rakkestad.no */ - { 37102, 0, 0, 1 }, /* ralingen.no */ - { 35211, 0, 0, 1 }, /* rana.no */ - { 37111, 0, 0, 1 }, /* randaberg.no */ - { 37121, 0, 0, 1 }, /* rauma.no */ - { 37127, 0, 0, 1 }, /* rendalen.no */ - { 37136, 0, 0, 1 }, /* rennebu.no */ - { 37144, 0, 0, 1 }, /* rennesoy.no */ - { 37153, 0, 0, 1 }, /* rindal.no */ - { 37160, 0, 0, 1 }, /* ringebu.no */ - { 37168, 0, 0, 1 }, /* ringerike.no */ - { 37178, 0, 0, 1 }, /* ringsaker.no */ - { 37188, 0, 0, 1 }, /* risor.no */ - { 37194, 0, 0, 1 }, /* rissa.no */ - { 3271, 7074, 1, 1 }, /* rl.no */ - { 37200, 0, 0, 1 }, /* roan.no */ - { 37205, 0, 0, 1 }, /* rodoy.no */ - { 37211, 0, 0, 1 }, /* rollag.no */ - { 37219, 0, 0, 1 }, /* romsa.no */ - { 37225, 0, 0, 1 }, /* romskog.no */ - { 37233, 0, 0, 1 }, /* roros.no */ - { 37239, 0, 0, 1 }, /* rost.no */ - { 37244, 0, 0, 1 }, /* royken.no */ - { 37251, 0, 0, 1 }, /* royrvik.no */ - { 37259, 0, 0, 1 }, /* ruovat.no */ - { 37266, 0, 0, 1 }, /* rygge.no */ - { 37272, 0, 0, 1 }, /* salangen.no */ - { 2792, 0, 0, 1 }, /* salat.no */ - { 37281, 0, 0, 1 }, /* saltdal.no */ - { 37289, 0, 0, 1 }, /* samnanger.no */ - { 37299, 0, 0, 1 }, /* sandefjord.no */ - { 37310, 0, 0, 1 }, /* sandnes.no */ - { 37318, 0, 0, 1 }, /* sandnessjoen.no */ - { 34431, 0, 0, 1 }, /* sandoy.no */ - { 6064, 0, 0, 1 }, /* sarpsborg.no */ - { 37331, 0, 0, 1 }, /* sauda.no */ - { 35640, 0, 0, 1 }, /* sauherad.no */ - { 30198, 0, 0, 1 }, /* sel.no */ - { 37337, 0, 0, 1 }, /* selbu.no */ - { 37343, 0, 0, 1 }, /* selje.no */ - { 37349, 0, 0, 1 }, /* seljord.no */ - { 11497, 7074, 1, 1 }, /* sf.no */ - { 37357, 0, 0, 1 }, /* siellak.no */ - { 37365, 0, 0, 1 }, /* sigdal.no */ - { 37372, 0, 0, 1 }, /* siljan.no */ - { 37379, 0, 0, 1 }, /* sirdal.no */ - { 37386, 0, 0, 1 }, /* skanit.no */ - { 37393, 0, 0, 1 }, /* skanland.no */ - { 37402, 0, 0, 1 }, /* skaun.no */ - { 37408, 0, 0, 1 }, /* skedsmo.no */ - { 37416, 0, 0, 1 }, /* skedsmokorset.no */ - { 4585, 0, 0, 1 }, /* ski.no */ - { 37430, 0, 0, 1 }, /* skien.no */ - { 37436, 0, 0, 1 }, /* skierva.no */ - { 8224, 0, 0, 1 }, /* skiptvet.no */ - { 37444, 0, 0, 1 }, /* skjak.no */ - { 37450, 0, 0, 1 }, /* skjervoy.no */ - { 37459, 0, 0, 1 }, /* skodje.no */ - { 37466, 0, 0, 1 }, /* slattum.no */ - { 37474, 0, 0, 1 }, /* smola.no */ - { 37480, 0, 0, 1 }, /* snaase.no */ - { 37487, 0, 0, 1 }, /* snasa.no */ - { 37493, 0, 0, 1 }, /* snillfjord.no */ - { 37504, 0, 0, 1 }, /* snoasa.no */ - { 37511, 0, 0, 1 }, /* sogndal.no */ - { 37519, 0, 0, 1 }, /* sogne.no */ - { 37525, 0, 0, 1 }, /* sokndal.no */ - { 37533, 0, 0, 1 }, /* sola.no */ - { 36340, 0, 0, 1 }, /* solund.no */ - { 37538, 0, 0, 1 }, /* somna.no */ - { 37544, 0, 0, 1 }, /* sondre-land.no */ - { 37556, 0, 0, 1 }, /* songdalen.no */ - { 37566, 0, 0, 1 }, /* sor-aurdal.no */ - { 37577, 0, 0, 1 }, /* sor-fron.no */ - { 37586, 0, 0, 1 }, /* sor-odal.no */ - { 37595, 0, 0, 1 }, /* sor-varanger.no */ - { 37608, 0, 0, 1 }, /* sorfold.no */ - { 37616, 0, 0, 1 }, /* sorreisa.no */ - { 37625, 0, 0, 1 }, /* sortland.no */ - { 37634, 0, 0, 1 }, /* sorum.no */ - { 37640, 0, 0, 1 }, /* spjelkavik.no */ - { 37651, 0, 0, 1 }, /* spydeberg.no */ - { 619, 7074, 1, 1 }, /* st.no */ - { 37661, 0, 0, 1 }, /* stange.no */ - { 37668, 0, 0, 1 }, /* stat.no */ - { 37673, 0, 0, 1 }, /* stathelle.no */ - { 37683, 0, 0, 1 }, /* stavanger.no */ - { 37693, 0, 0, 1 }, /* stavern.no */ - { 37701, 0, 0, 1 }, /* steigen.no */ - { 37709, 0, 0, 1 }, /* steinkjer.no */ - { 37719, 0, 0, 1 }, /* stjordal.no */ - { 37728, 0, 0, 1 }, /* stjordalshalsen.no */ - { 37744, 0, 0, 1 }, /* stokke.no */ - { 37751, 0, 0, 1 }, /* stor-elvdal.no */ - { 37763, 0, 0, 1 }, /* stord.no */ - { 37769, 0, 0, 1 }, /* stordal.no */ - { 37777, 0, 0, 1 }, /* storfjord.no */ - { 34618, 0, 0, 1 }, /* strand.no */ - { 37787, 0, 0, 1 }, /* stranda.no */ - { 11927, 0, 0, 1 }, /* stryn.no */ - { 37795, 0, 0, 1 }, /* sula.no */ - { 37800, 0, 0, 1 }, /* suldal.no */ - { 34369, 0, 0, 1 }, /* sund.no */ - { 37807, 0, 0, 1 }, /* sunndal.no */ - { 37815, 0, 0, 1 }, /* surnadal.no */ - { 37824, 7074, 1, 1 }, /* svalbard.no */ - { 37833, 0, 0, 1 }, /* sveio.no */ - { 37839, 0, 0, 1 }, /* svelvik.no */ - { 37847, 0, 0, 1 }, /* sykkylven.no */ - { 26078, 0, 0, 1 }, /* tana.no */ - { 37857, 0, 0, 1 }, /* tananger.no */ - { 37866, 7087, 2, 1 }, /* telemark.no */ - { 7261, 0, 0, 1 }, /* time.no */ - { 37875, 0, 0, 1 }, /* tingvoll.no */ - { 37884, 0, 0, 1 }, /* tinn.no */ - { 37889, 0, 0, 1 }, /* tjeldsund.no */ - { 37899, 0, 0, 1 }, /* tjome.no */ - { 7910, 7074, 1, 1 }, /* tm.no */ - { 37745, 0, 0, 1 }, /* tokke.no */ - { 37905, 0, 0, 1 }, /* tolga.no */ - { 37911, 0, 0, 1 }, /* tonsberg.no */ - { 37920, 0, 0, 1 }, /* torsken.no */ - { 3302, 7074, 1, 1 }, /* tr.no */ - { 37928, 0, 0, 1 }, /* trana.no */ - { 37934, 0, 0, 1 }, /* tranby.no */ - { 37941, 0, 0, 1 }, /* tranoy.no */ - { 37948, 0, 0, 1 }, /* troandin.no */ - { 37957, 0, 0, 1 }, /* trogstad.no */ - { 37218, 0, 0, 1 }, /* tromsa.no */ - { 37966, 0, 0, 1 }, /* tromso.no */ - { 37973, 0, 0, 1 }, /* trondheim.no */ - { 37983, 0, 0, 1 }, /* trysil.no */ - { 37990, 0, 0, 1 }, /* tvedestrand.no */ - { 38002, 0, 0, 1 }, /* tydal.no */ - { 38008, 0, 0, 1 }, /* tynset.no */ - { 38015, 0, 0, 1 }, /* tysfjord.no */ - { 38024, 0, 0, 1 }, /* tysnes.no */ - { 38031, 0, 0, 1 }, /* tysvar.no */ - { 38038, 0, 0, 1 }, /* ullensaker.no */ - { 38049, 0, 0, 1 }, /* ullensvang.no */ - { 38060, 0, 0, 1 }, /* ulvik.no */ - { 38066, 0, 0, 1 }, /* unjarga.no */ - { 38074, 0, 0, 1 }, /* utsira.no */ - { 834, 7074, 1, 1 }, /* va.no */ - { 38081, 0, 0, 1 }, /* vaapste.no */ - { 38089, 0, 0, 1 }, /* vadso.no */ - { 38095, 0, 0, 1 }, /* vaga.no */ - { 38100, 0, 0, 1 }, /* vagan.no */ - { 38106, 0, 0, 1 }, /* vagsoy.no */ - { 38113, 0, 0, 1 }, /* vaksdal.no */ - { 38121, 0, 0, 1 }, /* valle.no */ - { 38055, 0, 0, 1 }, /* vang.no */ - { 38127, 0, 0, 1 }, /* vanylven.no */ - { 38136, 0, 0, 1 }, /* vardo.no */ - { 38142, 0, 0, 1 }, /* varggat.no */ - { 38150, 0, 0, 1 }, /* varoy.no */ - { 38156, 0, 0, 1 }, /* vefsn.no */ - { 38162, 0, 0, 1 }, /* vega.no */ - { 38167, 0, 0, 1 }, /* vegarshei.no */ - { 38177, 0, 0, 1 }, /* vennesla.no */ - { 38186, 0, 0, 1 }, /* verdal.no */ - { 38193, 0, 0, 1 }, /* verran.no */ - { 38200, 0, 0, 1 }, /* vestby.no */ - { 38207, 7089, 1, 1 }, /* vestfold.no */ - { 38216, 0, 0, 1 }, /* vestnes.no */ - { 38224, 0, 0, 1 }, /* vestre-slidre.no */ - { 38238, 0, 0, 1 }, /* vestre-toten.no */ - { 38251, 0, 0, 1 }, /* vestvagoy.no */ - { 38261, 0, 0, 1 }, /* vevelstad.no */ - { 9687, 7074, 1, 1 }, /* vf.no */ - { 3759, 0, 0, 1 }, /* vgs.no */ - { 6943, 0, 0, 1 }, /* vik.no */ - { 38271, 0, 0, 1 }, /* vikna.no */ - { 38277, 0, 0, 1 }, /* vindafjord.no */ - { 38288, 0, 0, 1 }, /* voagat.no */ - { 38295, 0, 0, 1 }, /* volda.no */ - { 38301, 0, 0, 1 }, /* voss.no */ - { 38306, 0, 0, 1 }, /* vossevangen.no */ - { 38318, 0, 0, 1 }, /* xn--andy-ira.no */ - { 38331, 0, 0, 1 }, /* xn--asky-ira.no */ - { 38344, 0, 0, 1 }, /* xn--aurskog-hland-jnb.no */ - { 38366, 0, 0, 1 }, /* xn--avery-yua.no */ - { 38380, 0, 0, 1 }, /* xn--bdddj-mrabd.no */ - { 38396, 0, 0, 1 }, /* xn--bearalvhki-y4a.no */ - { 38415, 0, 0, 1 }, /* xn--berlevg-jxa.no */ - { 38431, 0, 0, 1 }, /* xn--bhcavuotna-s4a.no */ - { 38450, 0, 0, 1 }, /* xn--bhccavuotna-k7a.no */ - { 38470, 0, 0, 1 }, /* xn--bidr-5nac.no */ - { 6528, 0, 0, 1 }, /* xn--bievt-0qa.no */ - { 38484, 0, 0, 1 }, /* xn--bjarky-fya.no */ - { 38499, 0, 0, 1 }, /* xn--bjddar-pta.no */ - { 38514, 0, 0, 1 }, /* xn--blt-elab.no */ - { 38527, 0, 0, 1 }, /* xn--bmlo-gra.no */ - { 38540, 0, 0, 1 }, /* xn--bod-2na.no */ - { 38552, 0, 0, 1 }, /* xn--brnny-wuac.no */ - { 38567, 0, 0, 1 }, /* xn--brnnysund-m8ac.no */ - { 38586, 0, 0, 1 }, /* xn--brum-voa.no */ - { 38599, 0, 0, 1 }, /* xn--btsfjord-9za.no */ - { 38616, 0, 0, 1 }, /* xn--davvenjrga-y4a.no */ - { 38635, 0, 0, 1 }, /* xn--dnna-gra.no */ - { 38648, 0, 0, 1 }, /* xn--drbak-wua.no */ - { 38662, 0, 0, 1 }, /* xn--dyry-ira.no */ - { 38675, 0, 0, 1 }, /* xn--eveni-0qa01ga.no */ - { 38693, 0, 0, 1 }, /* xn--finny-yua.no */ - { 38707, 0, 0, 1 }, /* xn--fjord-lra.no */ - { 38721, 0, 0, 1 }, /* xn--fl-zia.no */ - { 38732, 0, 0, 1 }, /* xn--flor-jra.no */ - { 38745, 0, 0, 1 }, /* xn--frde-gra.no */ - { 38758, 0, 0, 1 }, /* xn--frna-woa.no */ - { 38771, 0, 0, 1 }, /* xn--frya-hra.no */ - { 38784, 0, 0, 1 }, /* xn--ggaviika-8ya47h.no */ - { 38804, 0, 0, 1 }, /* xn--gildeskl-g0a.no */ - { 38821, 0, 0, 1 }, /* xn--givuotna-8ya.no */ - { 38838, 0, 0, 1 }, /* xn--gjvik-wua.no */ - { 38852, 0, 0, 1 }, /* xn--gls-elac.no */ - { 38865, 0, 0, 1 }, /* xn--h-2fa.no */ - { 38875, 0, 0, 1 }, /* xn--hbmer-xqa.no */ - { 38889, 0, 0, 1 }, /* xn--hcesuolo-7ya35b.no */ - { 38909, 0, 0, 1 }, /* xn--hgebostad-g3a.no */ - { 38927, 0, 0, 1 }, /* xn--hmmrfeasta-s4ac.no */ - { 38947, 0, 0, 1 }, /* xn--hnefoss-q1a.no */ - { 38963, 0, 0, 1 }, /* xn--hobl-ira.no */ - { 38976, 0, 0, 1 }, /* xn--holtlen-hxa.no */ - { 38992, 0, 0, 1 }, /* xn--hpmir-xqa.no */ - { 39006, 0, 0, 1 }, /* xn--hyanger-q1a.no */ - { 39022, 0, 0, 1 }, /* xn--hylandet-54a.no */ - { 39039, 0, 0, 1 }, /* xn--indery-fya.no */ - { 39054, 0, 0, 1 }, /* xn--jlster-bya.no */ - { 39069, 0, 0, 1 }, /* xn--jrpeland-54a.no */ - { 39086, 0, 0, 1 }, /* xn--karmy-yua.no */ - { 39100, 0, 0, 1 }, /* xn--kfjord-iua.no */ - { 39115, 0, 0, 1 }, /* xn--klbu-woa.no */ - { 39128, 0, 0, 1 }, /* xn--koluokta-7ya57h.no */ - { 39148, 0, 0, 1 }, /* xn--krager-gya.no */ - { 39163, 0, 0, 1 }, /* xn--kranghke-b0a.no */ - { 39180, 0, 0, 1 }, /* xn--krdsherad-m8a.no */ - { 39198, 0, 0, 1 }, /* xn--krehamn-dxa.no */ - { 39214, 0, 0, 1 }, /* xn--krjohka-hwab49j.no */ - { 39234, 0, 0, 1 }, /* xn--ksnes-uua.no */ - { 39248, 0, 0, 1 }, /* xn--kvfjord-nxa.no */ - { 39264, 0, 0, 1 }, /* xn--kvitsy-fya.no */ - { 39279, 0, 0, 1 }, /* xn--kvnangen-k0a.no */ - { 39296, 0, 0, 1 }, /* xn--l-1fa.no */ - { 39306, 0, 0, 1 }, /* xn--laheadju-7ya.no */ - { 39323, 0, 0, 1 }, /* xn--langevg-jxa.no */ - { 39339, 0, 0, 1 }, /* xn--ldingen-q1a.no */ - { 39355, 0, 0, 1 }, /* xn--leagaviika-52b.no */ - { 39374, 0, 0, 1 }, /* xn--lesund-hua.no */ - { 39389, 0, 0, 1 }, /* xn--lgrd-poac.no */ - { 39403, 0, 0, 1 }, /* xn--lhppi-xqa.no */ - { 39417, 0, 0, 1 }, /* xn--linds-pra.no */ - { 39431, 0, 0, 1 }, /* xn--loabt-0qa.no */ - { 39445, 0, 0, 1 }, /* xn--lrdal-sra.no */ - { 39459, 0, 0, 1 }, /* xn--lrenskog-54a.no */ - { 39476, 0, 0, 1 }, /* xn--lt-liac.no */ - { 39488, 0, 0, 1 }, /* xn--lten-gra.no */ - { 39501, 0, 0, 1 }, /* xn--lury-ira.no */ - { 39514, 0, 0, 1 }, /* xn--mely-ira.no */ - { 39527, 0, 0, 1 }, /* xn--merker-kua.no */ - { 39542, 0, 0, 1 }, /* xn--mjndalen-64a.no */ - { 39559, 0, 0, 1 }, /* xn--mlatvuopmi-s4a.no */ - { 39578, 0, 0, 1 }, /* xn--mli-tla.no */ - { 39590, 0, 0, 1 }, /* xn--mlselv-iua.no */ - { 39605, 0, 0, 1 }, /* xn--moreke-jua.no */ - { 39620, 0, 0, 1 }, /* xn--mosjen-eya.no */ - { 39635, 0, 0, 1 }, /* xn--mot-tla.no */ - { 39647, 7090, 2, 1 }, /* xn--mre-og-romsdal-qqb.no */ - { 39670, 0, 0, 1 }, /* xn--msy-ula0h.no */ - { 39684, 0, 0, 1 }, /* xn--mtta-vrjjat-k7af.no */ - { 39705, 0, 0, 1 }, /* xn--muost-0qa.no */ - { 1545, 0, 0, 1 }, /* xn--nmesjevuemie-tcba.no */ - { 39719, 0, 0, 1 }, /* xn--nry-yla5g.no */ - { 39733, 0, 0, 1 }, /* xn--nttery-byae.no */ - { 39749, 0, 0, 1 }, /* xn--nvuotna-hwa.no */ - { 39765, 0, 0, 1 }, /* xn--oppegrd-ixa.no */ - { 39781, 0, 0, 1 }, /* xn--ostery-fya.no */ - { 39796, 0, 0, 1 }, /* xn--osyro-wua.no */ - { 39810, 0, 0, 1 }, /* xn--porsgu-sta26f.no */ - { 39828, 0, 0, 1 }, /* xn--rady-ira.no */ - { 39841, 0, 0, 1 }, /* xn--rdal-poa.no */ - { 39854, 0, 0, 1 }, /* xn--rde-ula.no */ - { 5695, 0, 0, 1 }, /* xn--rdy-0nab.no */ - { 39866, 0, 0, 1 }, /* xn--rennesy-v1a.no */ + { 36478, 0, 0, 1 }, /* kvanangen.no */ + { 36488, 0, 0, 1 }, /* kvinesdal.no */ + { 36498, 0, 0, 1 }, /* kvinnherad.no */ + { 36509, 0, 0, 1 }, /* kviteseid.no */ + { 36519, 0, 0, 1 }, /* kvitsoy.no */ + { 36527, 0, 0, 1 }, /* laakesvuemie.no */ + { 36540, 0, 0, 1 }, /* lahppi.no */ + { 36547, 0, 0, 1 }, /* langevag.no */ + { 34857, 0, 0, 1 }, /* lardal.no */ + { 36556, 0, 0, 1 }, /* larvik.no */ + { 36563, 0, 0, 1 }, /* lavagis.no */ + { 36571, 0, 0, 1 }, /* lavangen.no */ + { 36580, 0, 0, 1 }, /* leangaviika.no */ + { 36592, 0, 0, 1 }, /* lebesby.no */ + { 36600, 0, 0, 1 }, /* leikanger.no */ + { 36610, 0, 0, 1 }, /* leirfjord.no */ + { 36620, 0, 0, 1 }, /* leirvik.no */ + { 36633, 0, 0, 1 }, /* leka.no */ + { 36638, 0, 0, 1 }, /* leksvik.no */ + { 36646, 0, 0, 1 }, /* lenvik.no */ + { 36653, 0, 0, 1 }, /* lerdal.no */ + { 36660, 0, 0, 1 }, /* lesja.no */ + { 36666, 0, 0, 1 }, /* levanger.no */ + { 2734, 0, 0, 1 }, /* lier.no */ + { 36675, 0, 0, 1 }, /* lierne.no */ + { 36682, 0, 0, 1 }, /* lillehammer.no */ + { 36694, 0, 0, 1 }, /* lillesand.no */ + { 36704, 0, 0, 1 }, /* lindas.no */ + { 36711, 0, 0, 1 }, /* lindesnes.no */ + { 36721, 0, 0, 1 }, /* loabat.no */ + { 36728, 0, 0, 1 }, /* lodingen.no */ + { 17508, 0, 0, 1 }, /* lom.no */ + { 36737, 0, 0, 1 }, /* loppa.no */ + { 36743, 0, 0, 1 }, /* lorenskog.no */ + { 36753, 0, 0, 1 }, /* loten.no */ + { 36761, 0, 0, 1 }, /* lund.no */ + { 36766, 0, 0, 1 }, /* lunner.no */ + { 36773, 0, 0, 1 }, /* luroy.no */ + { 36779, 0, 0, 1 }, /* luster.no */ + { 36786, 0, 0, 1 }, /* lyngdal.no */ + { 36794, 0, 0, 1 }, /* lyngen.no */ + { 36801, 0, 0, 1 }, /* malatvuopmi.no */ + { 5121, 0, 0, 1 }, /* malselv.no */ + { 36813, 0, 0, 1 }, /* malvik.no */ + { 36820, 0, 0, 1 }, /* mandal.no */ + { 36827, 0, 0, 1 }, /* marker.no */ + { 36834, 0, 0, 1 }, /* marnardal.no */ + { 36844, 0, 0, 1 }, /* masfjorden.no */ + { 7390, 0, 0, 1 }, /* masoy.no */ + { 36855, 0, 0, 1 }, /* matta-varjjat.no */ + { 36081, 0, 0, 1 }, /* meland.no */ + { 36869, 0, 0, 1 }, /* meldal.no */ + { 36876, 0, 0, 1 }, /* melhus.no */ + { 36883, 0, 0, 1 }, /* meloy.no */ + { 36889, 0, 0, 1 }, /* meraker.no */ + { 36897, 0, 0, 1 }, /* midsund.no */ + { 36905, 0, 0, 1 }, /* midtre-gauldal.no */ + { 4170, 0, 0, 1 }, /* mil.no */ + { 36920, 0, 0, 1 }, /* mjondalen.no */ + { 36930, 0, 0, 1 }, /* mo-i-rana.no */ + { 36940, 0, 0, 1 }, /* moareke.no */ + { 36948, 0, 0, 1 }, /* modalen.no */ + { 36956, 0, 0, 1 }, /* modum.no */ + { 36962, 0, 0, 1 }, /* molde.no */ + { 36968, 7228, 2, 0 }, /* more-og-romsdal.no */ + { 36984, 0, 0, 1 }, /* mosjoen.no */ + { 36992, 0, 0, 1 }, /* moskenes.no */ + { 18137, 0, 0, 1 }, /* moss.no */ + { 37001, 0, 0, 1 }, /* mosvik.no */ + { 5580, 7222, 1, 1 }, /* mr.no */ + { 37008, 0, 0, 1 }, /* muosat.no */ + { 5623, 0, 0, 1 }, /* museum.no */ + { 37015, 0, 0, 1 }, /* naamesjevuemie.no */ + { 37030, 0, 0, 1 }, /* namdalseid.no */ + { 37041, 0, 0, 1 }, /* namsos.no */ + { 37048, 0, 0, 1 }, /* namsskogan.no */ + { 37059, 0, 0, 1 }, /* nannestad.no */ + { 37069, 0, 0, 1 }, /* naroy.no */ + { 37075, 0, 0, 1 }, /* narviika.no */ + { 37084, 0, 0, 1 }, /* narvik.no */ + { 37091, 0, 0, 1 }, /* naustdal.no */ + { 37100, 0, 0, 1 }, /* navuotna.no */ + { 37109, 0, 0, 1 }, /* nedre-eiker.no */ + { 37121, 0, 0, 1 }, /* nesna.no */ + { 37127, 0, 0, 1 }, /* nesodden.no */ + { 37136, 0, 0, 1 }, /* nesoddtangen.no */ + { 37149, 0, 0, 1 }, /* nesseby.no */ + { 37157, 0, 0, 1 }, /* nesset.no */ + { 37164, 0, 0, 1 }, /* nissedal.no */ + { 37173, 0, 0, 1 }, /* nittedal.no */ + { 1072, 7222, 1, 1 }, /* nl.no */ + { 37182, 0, 0, 1 }, /* nord-aurdal.no */ + { 37194, 0, 0, 1 }, /* nord-fron.no */ + { 37204, 0, 0, 1 }, /* nord-odal.no */ + { 37214, 0, 0, 1 }, /* norddal.no */ + { 37222, 0, 0, 1 }, /* nordkapp.no */ + { 37231, 7230, 4, 0 }, /* nordland.no */ + { 37240, 0, 0, 1 }, /* nordre-land.no */ + { 37252, 0, 0, 1 }, /* nordreisa.no */ + { 37262, 0, 0, 1 }, /* nore-og-uvdal.no */ + { 37276, 0, 0, 1 }, /* notodden.no */ + { 37285, 0, 0, 1 }, /* notteroy.no */ + { 97, 7222, 1, 1 }, /* nt.no */ + { 37294, 0, 0, 1 }, /* odda.no */ + { 6433, 7222, 1, 1 }, /* of.no */ + { 37299, 0, 0, 1 }, /* oksnes.no */ + { 452, 7222, 1, 1 }, /* ol.no */ + { 37306, 0, 0, 1 }, /* omasvuotna.no */ + { 37317, 0, 0, 1 }, /* oppdal.no */ + { 37324, 0, 0, 1 }, /* oppegard.no */ + { 37333, 0, 0, 1 }, /* orkanger.no */ + { 37342, 0, 0, 1 }, /* orkdal.no */ + { 4761, 0, 0, 1 }, /* orland.no */ + { 37349, 0, 0, 1 }, /* orskog.no */ + { 37356, 0, 0, 1 }, /* orsta.no */ + { 26721, 0, 0, 1 }, /* osen.no */ + { 37362, 7222, 1, 1 }, /* oslo.no */ + { 37367, 0, 0, 1 }, /* osoyro.no */ + { 37374, 0, 0, 1 }, /* osteroy.no */ + { 37382, 7234, 1, 0 }, /* ostfold.no */ + { 37390, 0, 0, 1 }, /* ostre-toten.no */ + { 37402, 0, 0, 1 }, /* overhalla.no */ + { 37412, 0, 0, 1 }, /* ovre-eiker.no */ + { 37423, 0, 0, 1 }, /* oyer.no */ + { 37428, 0, 0, 1 }, /* oygarden.no */ + { 37437, 0, 0, 1 }, /* oystre-slidre.no */ + { 37451, 0, 0, 1 }, /* porsanger.no */ + { 37461, 0, 0, 1 }, /* porsangu.no */ + { 37470, 0, 0, 1 }, /* porsgrunn.no */ + { 11418, 0, 0, 1 }, /* priv.no */ + { 7981, 0, 0, 1 }, /* rade.no */ + { 37480, 0, 0, 1 }, /* radoy.no */ + { 37486, 0, 0, 1 }, /* rahkkeravju.no */ + { 37498, 0, 0, 1 }, /* raholt.no */ + { 37505, 0, 0, 1 }, /* raisa.no */ + { 37511, 0, 0, 1 }, /* rakkestad.no */ + { 37521, 0, 0, 1 }, /* ralingen.no */ + { 35630, 0, 0, 1 }, /* rana.no */ + { 37530, 0, 0, 1 }, /* randaberg.no */ + { 37540, 0, 0, 1 }, /* rauma.no */ + { 37546, 0, 0, 1 }, /* rendalen.no */ + { 37555, 0, 0, 1 }, /* rennebu.no */ + { 37563, 0, 0, 1 }, /* rennesoy.no */ + { 37572, 0, 0, 1 }, /* rindal.no */ + { 37579, 0, 0, 1 }, /* ringebu.no */ + { 37587, 0, 0, 1 }, /* ringerike.no */ + { 37597, 0, 0, 1 }, /* ringsaker.no */ + { 37607, 0, 0, 1 }, /* risor.no */ + { 37613, 0, 0, 1 }, /* rissa.no */ + { 3256, 7222, 1, 1 }, /* rl.no */ + { 37619, 0, 0, 1 }, /* roan.no */ + { 37624, 0, 0, 1 }, /* rodoy.no */ + { 37630, 0, 0, 1 }, /* rollag.no */ + { 37638, 0, 0, 1 }, /* romsa.no */ + { 37644, 0, 0, 1 }, /* romskog.no */ + { 37652, 0, 0, 1 }, /* roros.no */ + { 37658, 0, 0, 1 }, /* rost.no */ + { 37663, 0, 0, 1 }, /* royken.no */ + { 37670, 0, 0, 1 }, /* royrvik.no */ + { 37678, 0, 0, 1 }, /* ruovat.no */ + { 37685, 0, 0, 1 }, /* rygge.no */ + { 37691, 0, 0, 1 }, /* salangen.no */ + { 2791, 0, 0, 1 }, /* salat.no */ + { 37700, 0, 0, 1 }, /* saltdal.no */ + { 37708, 0, 0, 1 }, /* samnanger.no */ + { 37718, 0, 0, 1 }, /* sandefjord.no */ + { 37729, 0, 0, 1 }, /* sandnes.no */ + { 37737, 0, 0, 1 }, /* sandnessjoen.no */ + { 34850, 0, 0, 1 }, /* sandoy.no */ + { 6047, 0, 0, 1 }, /* sarpsborg.no */ + { 37750, 0, 0, 1 }, /* sauda.no */ + { 36059, 0, 0, 1 }, /* sauherad.no */ + { 30556, 0, 0, 1 }, /* sel.no */ + { 37756, 0, 0, 1 }, /* selbu.no */ + { 37762, 0, 0, 1 }, /* selje.no */ + { 37768, 0, 0, 1 }, /* seljord.no */ + { 11527, 7222, 1, 1 }, /* sf.no */ + { 37776, 0, 0, 1 }, /* siellak.no */ + { 37784, 0, 0, 1 }, /* sigdal.no */ + { 37791, 0, 0, 1 }, /* siljan.no */ + { 37798, 0, 0, 1 }, /* sirdal.no */ + { 37805, 0, 0, 1 }, /* skanit.no */ + { 37812, 0, 0, 1 }, /* skanland.no */ + { 37821, 0, 0, 1 }, /* skaun.no */ + { 37827, 0, 0, 1 }, /* skedsmo.no */ + { 37835, 0, 0, 1 }, /* skedsmokorset.no */ + { 4564, 0, 0, 1 }, /* ski.no */ + { 37849, 0, 0, 1 }, /* skien.no */ + { 37855, 0, 0, 1 }, /* skierva.no */ + { 8217, 0, 0, 1 }, /* skiptvet.no */ + { 37863, 0, 0, 1 }, /* skjak.no */ + { 37869, 0, 0, 1 }, /* skjervoy.no */ + { 37878, 0, 0, 1 }, /* skodje.no */ + { 37885, 0, 0, 1 }, /* slattum.no */ + { 37893, 0, 0, 1 }, /* smola.no */ + { 37899, 0, 0, 1 }, /* snaase.no */ + { 37906, 0, 0, 1 }, /* snasa.no */ + { 37912, 0, 0, 1 }, /* snillfjord.no */ + { 37923, 0, 0, 1 }, /* snoasa.no */ + { 37930, 0, 0, 1 }, /* sogndal.no */ + { 37938, 0, 0, 1 }, /* sogne.no */ + { 37944, 0, 0, 1 }, /* sokndal.no */ + { 37952, 0, 0, 1 }, /* sola.no */ + { 36759, 0, 0, 1 }, /* solund.no */ + { 37957, 0, 0, 1 }, /* somna.no */ + { 37963, 0, 0, 1 }, /* sondre-land.no */ + { 37975, 0, 0, 1 }, /* songdalen.no */ + { 37985, 0, 0, 1 }, /* sor-aurdal.no */ + { 37996, 0, 0, 1 }, /* sor-fron.no */ + { 38005, 0, 0, 1 }, /* sor-odal.no */ + { 38014, 0, 0, 1 }, /* sor-varanger.no */ + { 38027, 0, 0, 1 }, /* sorfold.no */ + { 38035, 0, 0, 1 }, /* sorreisa.no */ + { 38044, 0, 0, 1 }, /* sortland.no */ + { 38053, 0, 0, 1 }, /* sorum.no */ + { 38059, 0, 0, 1 }, /* spjelkavik.no */ + { 38070, 0, 0, 1 }, /* spydeberg.no */ + { 619, 7222, 1, 1 }, /* st.no */ + { 38080, 0, 0, 1 }, /* stange.no */ + { 38087, 0, 0, 1 }, /* stat.no */ + { 38092, 0, 0, 1 }, /* stathelle.no */ + { 38102, 0, 0, 1 }, /* stavanger.no */ + { 38112, 0, 0, 1 }, /* stavern.no */ + { 38120, 0, 0, 1 }, /* steigen.no */ + { 38128, 0, 0, 1 }, /* steinkjer.no */ + { 38138, 0, 0, 1 }, /* stjordal.no */ + { 38147, 0, 0, 1 }, /* stjordalshalsen.no */ + { 38163, 0, 0, 1 }, /* stokke.no */ + { 38170, 0, 0, 1 }, /* stor-elvdal.no */ + { 38182, 0, 0, 1 }, /* stord.no */ + { 38188, 0, 0, 1 }, /* stordal.no */ + { 38196, 0, 0, 1 }, /* storfjord.no */ + { 35037, 0, 0, 1 }, /* strand.no */ + { 38206, 0, 0, 1 }, /* stranda.no */ + { 12063, 0, 0, 1 }, /* stryn.no */ + { 38214, 0, 0, 1 }, /* sula.no */ + { 38219, 0, 0, 1 }, /* suldal.no */ + { 34788, 0, 0, 1 }, /* sund.no */ + { 38226, 0, 0, 1 }, /* sunndal.no */ + { 38234, 0, 0, 1 }, /* surnadal.no */ + { 38243, 7222, 1, 1 }, /* svalbard.no */ + { 38252, 0, 0, 1 }, /* sveio.no */ + { 38258, 0, 0, 1 }, /* svelvik.no */ + { 38266, 0, 0, 1 }, /* sykkylven.no */ + { 26423, 0, 0, 1 }, /* tana.no */ + { 38276, 0, 0, 1 }, /* tananger.no */ + { 38285, 7235, 2, 0 }, /* telemark.no */ + { 7250, 0, 0, 1 }, /* time.no */ + { 38294, 0, 0, 1 }, /* tingvoll.no */ + { 38303, 0, 0, 1 }, /* tinn.no */ + { 38308, 0, 0, 1 }, /* tjeldsund.no */ + { 38318, 0, 0, 1 }, /* tjome.no */ + { 7908, 7222, 1, 1 }, /* tm.no */ + { 38164, 0, 0, 1 }, /* tokke.no */ + { 38324, 0, 0, 1 }, /* tolga.no */ + { 38330, 0, 0, 1 }, /* tonsberg.no */ + { 38339, 0, 0, 1 }, /* torsken.no */ + { 3287, 7222, 1, 1 }, /* tr.no */ + { 38347, 0, 0, 1 }, /* trana.no */ + { 38353, 0, 0, 1 }, /* tranby.no */ + { 38360, 0, 0, 1 }, /* tranoy.no */ + { 38367, 0, 0, 1 }, /* troandin.no */ + { 38376, 0, 0, 1 }, /* trogstad.no */ + { 37637, 0, 0, 1 }, /* tromsa.no */ + { 38385, 0, 0, 1 }, /* tromso.no */ + { 38392, 0, 0, 1 }, /* trondheim.no */ + { 38402, 0, 0, 1 }, /* trysil.no */ + { 38409, 0, 0, 1 }, /* tvedestrand.no */ + { 38421, 0, 0, 1 }, /* tydal.no */ + { 38427, 0, 0, 1 }, /* tynset.no */ + { 38434, 0, 0, 1 }, /* tysfjord.no */ + { 38443, 0, 0, 1 }, /* tysnes.no */ + { 38450, 0, 0, 1 }, /* tysvar.no */ + { 38457, 0, 0, 1 }, /* ullensaker.no */ + { 38468, 0, 0, 1 }, /* ullensvang.no */ + { 38479, 0, 0, 1 }, /* ulvik.no */ + { 38485, 0, 0, 1 }, /* unjarga.no */ + { 38493, 0, 0, 1 }, /* utsira.no */ + { 834, 7222, 1, 1 }, /* va.no */ + { 38500, 0, 0, 1 }, /* vaapste.no */ + { 38508, 0, 0, 1 }, /* vadso.no */ + { 38514, 0, 0, 1 }, /* vaga.no */ + { 38519, 0, 0, 1 }, /* vagan.no */ + { 38525, 0, 0, 1 }, /* vagsoy.no */ + { 38532, 0, 0, 1 }, /* vaksdal.no */ + { 38540, 0, 0, 1 }, /* valle.no */ + { 38474, 0, 0, 1 }, /* vang.no */ + { 38546, 0, 0, 1 }, /* vanylven.no */ + { 38555, 0, 0, 1 }, /* vardo.no */ + { 38561, 0, 0, 1 }, /* varggat.no */ + { 38569, 0, 0, 1 }, /* varoy.no */ + { 38575, 0, 0, 1 }, /* vefsn.no */ + { 38581, 0, 0, 1 }, /* vega.no */ + { 38586, 0, 0, 1 }, /* vegarshei.no */ + { 38596, 0, 0, 1 }, /* vennesla.no */ + { 38605, 0, 0, 1 }, /* verdal.no */ + { 38612, 0, 0, 1 }, /* verran.no */ + { 38619, 0, 0, 1 }, /* vestby.no */ + { 38626, 7237, 1, 0 }, /* vestfold.no */ + { 38635, 0, 0, 1 }, /* vestnes.no */ + { 38643, 0, 0, 1 }, /* vestre-slidre.no */ + { 38657, 0, 0, 1 }, /* vestre-toten.no */ + { 38670, 0, 0, 1 }, /* vestvagoy.no */ + { 38680, 0, 0, 1 }, /* vevelstad.no */ + { 9666, 7222, 1, 1 }, /* vf.no */ + { 3744, 0, 0, 1 }, /* vgs.no */ + { 6932, 0, 0, 1 }, /* vik.no */ + { 38690, 0, 0, 1 }, /* vikna.no */ + { 38696, 0, 0, 1 }, /* vindafjord.no */ + { 38707, 0, 0, 1 }, /* voagat.no */ + { 38714, 0, 0, 1 }, /* volda.no */ + { 38720, 0, 0, 1 }, /* voss.no */ + { 38725, 0, 0, 1 }, /* vossevangen.no */ + { 38737, 0, 0, 1 }, /* xn--andy-ira.no */ + { 38750, 0, 0, 1 }, /* xn--asky-ira.no */ + { 38763, 0, 0, 1 }, /* xn--aurskog-hland-jnb.no */ + { 38785, 0, 0, 1 }, /* xn--avery-yua.no */ + { 38799, 0, 0, 1 }, /* xn--bdddj-mrabd.no */ + { 38815, 0, 0, 1 }, /* xn--bearalvhki-y4a.no */ + { 38834, 0, 0, 1 }, /* xn--berlevg-jxa.no */ + { 38850, 0, 0, 1 }, /* xn--bhcavuotna-s4a.no */ + { 38869, 0, 0, 1 }, /* xn--bhccavuotna-k7a.no */ + { 38889, 0, 0, 1 }, /* xn--bidr-5nac.no */ + { 6511, 0, 0, 1 }, /* xn--bievt-0qa.no */ + { 38903, 0, 0, 1 }, /* xn--bjarky-fya.no */ + { 38918, 0, 0, 1 }, /* xn--bjddar-pta.no */ + { 38933, 0, 0, 1 }, /* xn--blt-elab.no */ + { 38946, 0, 0, 1 }, /* xn--bmlo-gra.no */ + { 38959, 0, 0, 1 }, /* xn--bod-2na.no */ + { 38971, 0, 0, 1 }, /* xn--brnny-wuac.no */ + { 38986, 0, 0, 1 }, /* xn--brnnysund-m8ac.no */ + { 39005, 0, 0, 1 }, /* xn--brum-voa.no */ + { 39018, 0, 0, 1 }, /* xn--btsfjord-9za.no */ + { 39035, 0, 0, 1 }, /* xn--davvenjrga-y4a.no */ + { 39054, 0, 0, 1 }, /* xn--dnna-gra.no */ + { 39067, 0, 0, 1 }, /* xn--drbak-wua.no */ + { 39081, 0, 0, 1 }, /* xn--dyry-ira.no */ + { 39094, 0, 0, 1 }, /* xn--eveni-0qa01ga.no */ + { 39112, 0, 0, 1 }, /* xn--finny-yua.no */ + { 39126, 0, 0, 1 }, /* xn--fjord-lra.no */ + { 39140, 0, 0, 1 }, /* xn--fl-zia.no */ + { 39151, 0, 0, 1 }, /* xn--flor-jra.no */ + { 39164, 0, 0, 1 }, /* xn--frde-gra.no */ + { 39177, 0, 0, 1 }, /* xn--frna-woa.no */ + { 39190, 0, 0, 1 }, /* xn--frya-hra.no */ + { 39203, 0, 0, 1 }, /* xn--ggaviika-8ya47h.no */ + { 39223, 0, 0, 1 }, /* xn--gildeskl-g0a.no */ + { 39240, 0, 0, 1 }, /* xn--givuotna-8ya.no */ + { 39257, 0, 0, 1 }, /* xn--gjvik-wua.no */ + { 39271, 0, 0, 1 }, /* xn--gls-elac.no */ + { 39284, 0, 0, 1 }, /* xn--h-2fa.no */ + { 39294, 0, 0, 1 }, /* xn--hbmer-xqa.no */ + { 39308, 0, 0, 1 }, /* xn--hcesuolo-7ya35b.no */ + { 39328, 0, 0, 1 }, /* xn--hgebostad-g3a.no */ + { 39346, 0, 0, 1 }, /* xn--hmmrfeasta-s4ac.no */ + { 39366, 0, 0, 1 }, /* xn--hnefoss-q1a.no */ + { 39382, 0, 0, 1 }, /* xn--hobl-ira.no */ + { 39395, 0, 0, 1 }, /* xn--holtlen-hxa.no */ + { 39411, 0, 0, 1 }, /* xn--hpmir-xqa.no */ + { 39425, 0, 0, 1 }, /* xn--hyanger-q1a.no */ + { 39441, 0, 0, 1 }, /* xn--hylandet-54a.no */ + { 39458, 0, 0, 1 }, /* xn--indery-fya.no */ + { 39473, 0, 0, 1 }, /* xn--jlster-bya.no */ + { 39488, 0, 0, 1 }, /* xn--jrpeland-54a.no */ + { 39505, 0, 0, 1 }, /* xn--karmy-yua.no */ + { 39519, 0, 0, 1 }, /* xn--kfjord-iua.no */ + { 39534, 0, 0, 1 }, /* xn--klbu-woa.no */ + { 39547, 0, 0, 1 }, /* xn--koluokta-7ya57h.no */ + { 39567, 0, 0, 1 }, /* xn--krager-gya.no */ + { 39582, 0, 0, 1 }, /* xn--kranghke-b0a.no */ + { 39599, 0, 0, 1 }, /* xn--krdsherad-m8a.no */ + { 39617, 0, 0, 1 }, /* xn--krehamn-dxa.no */ + { 39633, 0, 0, 1 }, /* xn--krjohka-hwab49j.no */ + { 39653, 0, 0, 1 }, /* xn--ksnes-uua.no */ + { 39667, 0, 0, 1 }, /* xn--kvfjord-nxa.no */ + { 39683, 0, 0, 1 }, /* xn--kvitsy-fya.no */ + { 39698, 0, 0, 1 }, /* xn--kvnangen-k0a.no */ + { 39715, 0, 0, 1 }, /* xn--l-1fa.no */ + { 39725, 0, 0, 1 }, /* xn--laheadju-7ya.no */ + { 39742, 0, 0, 1 }, /* xn--langevg-jxa.no */ + { 39758, 0, 0, 1 }, /* xn--ldingen-q1a.no */ + { 39774, 0, 0, 1 }, /* xn--leagaviika-52b.no */ + { 39793, 0, 0, 1 }, /* xn--lesund-hua.no */ + { 39808, 0, 0, 1 }, /* xn--lgrd-poac.no */ + { 39822, 0, 0, 1 }, /* xn--lhppi-xqa.no */ + { 39836, 0, 0, 1 }, /* xn--linds-pra.no */ + { 39850, 0, 0, 1 }, /* xn--loabt-0qa.no */ + { 39864, 0, 0, 1 }, /* xn--lrdal-sra.no */ + { 39878, 0, 0, 1 }, /* xn--lrenskog-54a.no */ + { 39895, 0, 0, 1 }, /* xn--lt-liac.no */ + { 39907, 0, 0, 1 }, /* xn--lten-gra.no */ + { 39920, 0, 0, 1 }, /* xn--lury-ira.no */ + { 39933, 0, 0, 1 }, /* xn--mely-ira.no */ + { 39946, 0, 0, 1 }, /* xn--merker-kua.no */ + { 39961, 0, 0, 1 }, /* xn--mjndalen-64a.no */ + { 39978, 0, 0, 1 }, /* xn--mlatvuopmi-s4a.no */ + { 39997, 0, 0, 1 }, /* xn--mli-tla.no */ + { 40009, 0, 0, 1 }, /* xn--mlselv-iua.no */ + { 40024, 0, 0, 1 }, /* xn--moreke-jua.no */ + { 40039, 0, 0, 1 }, /* xn--mosjen-eya.no */ + { 40054, 0, 0, 1 }, /* xn--mot-tla.no */ + { 40066, 7238, 2, 0 }, /* xn--mre-og-romsdal-qqb.no */ + { 40089, 0, 0, 1 }, /* xn--msy-ula0h.no */ + { 40103, 0, 0, 1 }, /* xn--mtta-vrjjat-k7af.no */ + { 40124, 0, 0, 1 }, /* xn--muost-0qa.no */ + { 1546, 0, 0, 1 }, /* xn--nmesjevuemie-tcba.no */ + { 40138, 0, 0, 1 }, /* xn--nry-yla5g.no */ + { 40152, 0, 0, 1 }, /* xn--nttery-byae.no */ + { 40168, 0, 0, 1 }, /* xn--nvuotna-hwa.no */ + { 40184, 0, 0, 1 }, /* xn--oppegrd-ixa.no */ + { 40200, 0, 0, 1 }, /* xn--ostery-fya.no */ + { 40215, 0, 0, 1 }, /* xn--osyro-wua.no */ + { 40229, 0, 0, 1 }, /* xn--porsgu-sta26f.no */ + { 40247, 0, 0, 1 }, /* xn--rady-ira.no */ + { 11753, 0, 0, 1 }, /* xn--rdal-poa.no */ + { 40260, 0, 0, 1 }, /* xn--rde-ula.no */ + { 5665, 0, 0, 1 }, /* xn--rdy-0nab.no */ + { 40272, 0, 0, 1 }, /* xn--rennesy-v1a.no */ { 175, 0, 0, 1 }, /* xn--rhkkervju-01af.no */ - { 39882, 0, 0, 1 }, /* xn--rholt-mra.no */ - { 39896, 0, 0, 1 }, /* xn--risa-5na.no */ - { 39909, 0, 0, 1 }, /* xn--risr-ira.no */ - { 39922, 0, 0, 1 }, /* xn--rland-uua.no */ - { 39936, 0, 0, 1 }, /* xn--rlingen-mxa.no */ - { 39952, 0, 0, 1 }, /* xn--rmskog-bya.no */ - { 39967, 0, 0, 1 }, /* xn--rros-gra.no */ - { 39980, 0, 0, 1 }, /* xn--rskog-uua.no */ - { 39994, 0, 0, 1 }, /* xn--rst-0na.no */ - { 40006, 0, 0, 1 }, /* xn--rsta-fra.no */ - { 40019, 0, 0, 1 }, /* xn--ryken-vua.no */ - { 40033, 0, 0, 1 }, /* xn--ryrvik-bya.no */ - { 40048, 0, 0, 1 }, /* xn--s-1fa.no */ - { 3424, 0, 0, 1 }, /* xn--sandnessjen-ogb.no */ - { 40058, 0, 0, 1 }, /* xn--sandy-yua.no */ - { 40072, 0, 0, 1 }, /* xn--seral-lra.no */ - { 40086, 0, 0, 1 }, /* xn--sgne-gra.no */ - { 40099, 0, 0, 1 }, /* xn--skierv-uta.no */ - { 40114, 0, 0, 1 }, /* xn--skjervy-v1a.no */ - { 40130, 0, 0, 1 }, /* xn--skjk-soa.no */ - { 40143, 0, 0, 1 }, /* xn--sknit-yqa.no */ - { 40157, 0, 0, 1 }, /* xn--sknland-fxa.no */ - { 40173, 0, 0, 1 }, /* xn--slat-5na.no */ - { 40186, 0, 0, 1 }, /* xn--slt-elab.no */ - { 40199, 0, 0, 1 }, /* xn--smla-hra.no */ - { 40212, 0, 0, 1 }, /* xn--smna-gra.no */ - { 40225, 0, 0, 1 }, /* xn--snase-nra.no */ - { 40239, 0, 0, 1 }, /* xn--sndre-land-0cb.no */ - { 40258, 0, 0, 1 }, /* xn--snes-poa.no */ - { 40271, 0, 0, 1 }, /* xn--snsa-roa.no */ - { 40284, 0, 0, 1 }, /* xn--sr-aurdal-l8a.no */ - { 40302, 0, 0, 1 }, /* xn--sr-fron-q1a.no */ - { 40318, 0, 0, 1 }, /* xn--sr-odal-q1a.no */ - { 40334, 0, 0, 1 }, /* xn--sr-varanger-ggb.no */ - { 40354, 0, 0, 1 }, /* xn--srfold-bya.no */ - { 40369, 0, 0, 1 }, /* xn--srreisa-q1a.no */ - { 40385, 0, 0, 1 }, /* xn--srum-gra.no */ - { 40398, 7092, 1, 1 }, /* xn--stfold-9xa.no */ - { 40413, 0, 0, 1 }, /* xn--stjrdal-s1a.no */ - { 40429, 0, 0, 1 }, /* xn--stjrdalshalsen-sqb.no */ - { 40452, 0, 0, 1 }, /* xn--stre-toten-zcb.no */ - { 40471, 0, 0, 1 }, /* xn--tjme-hra.no */ - { 40484, 0, 0, 1 }, /* xn--tnsberg-q1a.no */ - { 40500, 0, 0, 1 }, /* xn--trany-yua.no */ - { 40514, 0, 0, 1 }, /* xn--trgstad-r1a.no */ - { 40530, 0, 0, 1 }, /* xn--trna-woa.no */ - { 40543, 0, 0, 1 }, /* xn--troms-zua.no */ - { 40557, 0, 0, 1 }, /* xn--tysvr-vra.no */ - { 40571, 0, 0, 1 }, /* xn--unjrga-rta.no */ - { 40586, 0, 0, 1 }, /* xn--vads-jra.no */ - { 40599, 0, 0, 1 }, /* xn--vard-jra.no */ - { 40612, 0, 0, 1 }, /* xn--vegrshei-c0a.no */ - { 40629, 0, 0, 1 }, /* xn--vestvgy-ixa6o.no */ - { 40647, 0, 0, 1 }, /* xn--vg-yiab.no */ - { 40659, 0, 0, 1 }, /* xn--vgan-qoa.no */ - { 40672, 0, 0, 1 }, /* xn--vgsy-qoa0j.no */ - { 40687, 0, 0, 1 }, /* xn--vre-eiker-k8a.no */ - { 40705, 0, 0, 1 }, /* xn--vrggt-xqad.no */ - { 40720, 0, 0, 1 }, /* xn--vry-yla5g.no */ - { 40734, 0, 0, 1 }, /* xn--yer-zna.no */ - { 40746, 0, 0, 1 }, /* xn--ygarden-p1a.no */ - { 40762, 0, 0, 1 }, /* xn--ystre-slidre-ujb.no */ + { 40288, 0, 0, 1 }, /* xn--rholt-mra.no */ + { 40302, 0, 0, 1 }, /* xn--risa-5na.no */ + { 40315, 0, 0, 1 }, /* xn--risr-ira.no */ + { 40328, 0, 0, 1 }, /* xn--rland-uua.no */ + { 40342, 0, 0, 1 }, /* xn--rlingen-mxa.no */ + { 40358, 0, 0, 1 }, /* xn--rmskog-bya.no */ + { 40373, 0, 0, 1 }, /* xn--rros-gra.no */ + { 40386, 0, 0, 1 }, /* xn--rskog-uua.no */ + { 40400, 0, 0, 1 }, /* xn--rst-0na.no */ + { 40412, 0, 0, 1 }, /* xn--rsta-fra.no */ + { 40425, 0, 0, 1 }, /* xn--ryken-vua.no */ + { 40439, 0, 0, 1 }, /* xn--ryrvik-bya.no */ + { 40454, 0, 0, 1 }, /* xn--s-1fa.no */ + { 3409, 0, 0, 1 }, /* xn--sandnessjen-ogb.no */ + { 40464, 0, 0, 1 }, /* xn--sandy-yua.no */ + { 40478, 0, 0, 1 }, /* xn--seral-lra.no */ + { 40492, 0, 0, 1 }, /* xn--sgne-gra.no */ + { 40505, 0, 0, 1 }, /* xn--skierv-uta.no */ + { 40520, 0, 0, 1 }, /* xn--skjervy-v1a.no */ + { 40536, 0, 0, 1 }, /* xn--skjk-soa.no */ + { 40549, 0, 0, 1 }, /* xn--sknit-yqa.no */ + { 40563, 0, 0, 1 }, /* xn--sknland-fxa.no */ + { 40579, 0, 0, 1 }, /* xn--slat-5na.no */ + { 40592, 0, 0, 1 }, /* xn--slt-elab.no */ + { 40605, 0, 0, 1 }, /* xn--smla-hra.no */ + { 40618, 0, 0, 1 }, /* xn--smna-gra.no */ + { 40631, 0, 0, 1 }, /* xn--snase-nra.no */ + { 40645, 0, 0, 1 }, /* xn--sndre-land-0cb.no */ + { 40664, 0, 0, 1 }, /* xn--snes-poa.no */ + { 40677, 0, 0, 1 }, /* xn--snsa-roa.no */ + { 40690, 0, 0, 1 }, /* xn--sr-aurdal-l8a.no */ + { 40708, 0, 0, 1 }, /* xn--sr-fron-q1a.no */ + { 40724, 0, 0, 1 }, /* xn--sr-odal-q1a.no */ + { 40740, 0, 0, 1 }, /* xn--sr-varanger-ggb.no */ + { 40760, 0, 0, 1 }, /* xn--srfold-bya.no */ + { 40775, 0, 0, 1 }, /* xn--srreisa-q1a.no */ + { 40791, 0, 0, 1 }, /* xn--srum-gra.no */ + { 40804, 7240, 1, 0 }, /* xn--stfold-9xa.no */ + { 40819, 0, 0, 1 }, /* xn--stjrdal-s1a.no */ + { 40835, 0, 0, 1 }, /* xn--stjrdalshalsen-sqb.no */ + { 40858, 0, 0, 1 }, /* xn--stre-toten-zcb.no */ + { 40877, 0, 0, 1 }, /* xn--tjme-hra.no */ + { 40890, 0, 0, 1 }, /* xn--tnsberg-q1a.no */ + { 40906, 0, 0, 1 }, /* xn--trany-yua.no */ + { 40920, 0, 0, 1 }, /* xn--trgstad-r1a.no */ + { 40936, 0, 0, 1 }, /* xn--trna-woa.no */ + { 40949, 0, 0, 1 }, /* xn--troms-zua.no */ + { 40963, 0, 0, 1 }, /* xn--tysvr-vra.no */ + { 40977, 0, 0, 1 }, /* xn--unjrga-rta.no */ + { 40992, 0, 0, 1 }, /* xn--vads-jra.no */ + { 41005, 0, 0, 1 }, /* xn--vard-jra.no */ + { 41018, 0, 0, 1 }, /* xn--vegrshei-c0a.no */ + { 41035, 0, 0, 1 }, /* xn--vestvgy-ixa6o.no */ + { 41053, 0, 0, 1 }, /* xn--vg-yiab.no */ + { 41065, 0, 0, 1 }, /* xn--vgan-qoa.no */ + { 41078, 0, 0, 1 }, /* xn--vgsy-qoa0j.no */ + { 41093, 0, 0, 1 }, /* xn--vre-eiker-k8a.no */ + { 41111, 0, 0, 1 }, /* xn--vrggt-xqad.no */ + { 41126, 0, 0, 1 }, /* xn--vry-yla5g.no */ + { 41140, 0, 0, 1 }, /* xn--yer-zna.no */ + { 41152, 0, 0, 1 }, /* xn--ygarden-p1a.no */ + { 41168, 0, 0, 1 }, /* xn--ystre-slidre-ujb.no */ { 62, 0, 0, 1 }, /* ac.nz */ - { 113, 3672, 1, 1 }, /* co.nz */ - { 40854, 0, 0, 1 }, /* cri.nz */ - { 13295, 0, 0, 1 }, /* geek.nz */ - { 8368, 0, 0, 1 }, /* gen.nz */ - { 40858, 0, 0, 1 }, /* govt.nz */ - { 3862, 0, 0, 1 }, /* health.nz */ - { 4624, 0, 0, 1 }, /* iwi.nz */ - { 4623, 0, 0, 1 }, /* kiwi.nz */ - { 40863, 0, 0, 1 }, /* maori.nz */ - { 4195, 0, 0, 1 }, /* mil.nz */ - { 4185, 0, 0, 1 }, /* net.nz */ - { 6070, 0, 0, 1 }, /* org.nz */ - { 14790, 0, 0, 1 }, /* parliament.nz */ - { 7042, 0, 0, 1 }, /* school.nz */ - { 40869, 0, 0, 1 }, /* xn--mori-qsa.nz */ + { 113, 3838, 1, 1 }, /* co.nz */ + { 11684, 0, 0, 1 }, /* cri.nz */ + { 13454, 0, 0, 1 }, /* geek.nz */ + { 8361, 0, 0, 1 }, /* gen.nz */ + { 41260, 0, 0, 1 }, /* govt.nz */ + { 3844, 0, 0, 1 }, /* health.nz */ + { 4603, 0, 0, 1 }, /* iwi.nz */ + { 4602, 0, 0, 1 }, /* kiwi.nz */ + { 41265, 0, 0, 1 }, /* maori.nz */ + { 4170, 0, 0, 1 }, /* mil.nz */ + { 5737, 0, 0, 1 }, /* net.nz */ + { 6053, 0, 0, 1 }, /* org.nz */ + { 15015, 0, 0, 1 }, /* parliament.nz */ + { 7031, 0, 0, 1 }, /* school.nz */ + { 41271, 0, 0, 1 }, /* xn--mori-qsa.nz */ { 157, 0, 0, 1 }, /* ae.org */ - { 40882, 7106, 1, 1 }, /* amune.org */ - { 12046, 0, 0, 1 }, /* blogdns.org */ - { 7299, 0, 0, 1 }, /* blogsite.org */ - { 40888, 0, 0, 1 }, /* bmoattachments.org */ - { 40903, 0, 0, 1 }, /* boldlygoingnowhere.org */ - { 40922, 0, 0, 1 }, /* cable-modem.org */ - { 11481, 7107, 2, 1 }, /* cdn77.org */ - { 7114, 3247, 1, 0 }, /* cdn77-secure.org */ - { 40934, 0, 0, 1 }, /* certmgr.org */ - { 11367, 0, 0, 1 }, /* cloudns.org */ - { 40942, 0, 0, 1 }, /* collegefan.org */ - { 40953, 0, 0, 1 }, /* couchpotatofries.org */ - { 14822, 0, 0, 1 }, /* ddnss.org */ - { 15103, 0, 0, 1 }, /* diskstation.org */ - { 12179, 0, 0, 1 }, /* dnsalias.org */ - { 12188, 0, 0, 1 }, /* dnsdojo.org */ - { 12207, 0, 0, 1 }, /* doesntexist.org */ - { 12219, 0, 0, 1 }, /* dontexist.org */ - { 12229, 0, 0, 1 }, /* doomdns.org */ - { 12250, 0, 0, 1 }, /* dsmynas.org */ - { 40970, 0, 0, 1 }, /* duckdns.org */ - { 40978, 0, 0, 1 }, /* dvrdns.org */ - { 12269, 0, 0, 1 }, /* dynalias.org */ - { 11526, 7110, 2, 1 }, /* dyndns.org */ - { 34001, 0, 0, 1 }, /* endofinternet.org */ - { 40985, 0, 0, 1 }, /* endoftheinternet.org */ - { 2798, 7112, 55, 1 }, /* eu.org */ - { 12493, 0, 0, 1 }, /* familyds.org */ - { 41002, 0, 0, 1 }, /* from-me.org */ - { 41010, 0, 0, 1 }, /* game-host.org */ - { 11809, 0, 0, 1 }, /* gotdns.org */ - { 41020, 0, 0, 1 }, /* hepforge.org */ - { 3940, 0, 0, 1 }, /* hk.org */ - { 13055, 0, 0, 1 }, /* hobby-site.org */ - { 41029, 0, 0, 1 }, /* homedns.org */ - { 34084, 0, 0, 1 }, /* homeftp.org */ - { 13066, 0, 0, 1 }, /* homelinux.org */ - { 13107, 0, 0, 1 }, /* homeunix.org */ - { 29814, 0, 0, 1 }, /* hopto.org */ - { 41037, 0, 0, 1 }, /* is-a-bruinsfan.org */ - { 41052, 0, 0, 1 }, /* is-a-candidate.org */ - { 41067, 0, 0, 1 }, /* is-a-celticsfan.org */ - { 13198, 0, 0, 1 }, /* is-a-chef.org */ - { 13290, 0, 0, 1 }, /* is-a-geek.org */ - { 41083, 0, 0, 1 }, /* is-a-knight.org */ - { 15232, 0, 0, 1 }, /* is-a-linux-user.org */ - { 41095, 0, 0, 1 }, /* is-a-patsfan.org */ - { 41108, 0, 0, 1 }, /* is-a-soxfan.org */ - { 41120, 0, 0, 1 }, /* is-found.org */ - { 41129, 0, 0, 1 }, /* is-lost.org */ - { 41137, 0, 0, 1 }, /* is-saved.org */ - { 41146, 0, 0, 1 }, /* is-very-bad.org */ - { 41158, 0, 0, 1 }, /* is-very-evil.org */ - { 41171, 0, 0, 1 }, /* is-very-good.org */ - { 41184, 0, 0, 1 }, /* is-very-nice.org */ - { 41197, 0, 0, 1 }, /* is-very-sweet.org */ - { 13713, 0, 0, 1 }, /* isa-geek.org */ - { 11512, 0, 0, 1 }, /* js.org */ - { 34099, 0, 0, 1 }, /* kicks-ass.org */ - { 41211, 0, 0, 1 }, /* misconfused.org */ - { 2931, 0, 0, 1 }, /* mlbfan.org */ - { 41223, 0, 0, 1 }, /* my-firewall.org */ - { 41235, 0, 0, 1 }, /* myfirewall.org */ - { 11582, 0, 0, 1 }, /* myftp.org */ - { 1347, 0, 0, 1 }, /* mysecuritycamera.org */ - { 41246, 0, 0, 1 }, /* nflfan.org */ - { 11588, 0, 0, 1 }, /* no-ip.org */ - { 41253, 0, 0, 1 }, /* pimienta.org */ - { 10655, 0, 0, 1 }, /* podzone.org */ - { 41262, 0, 0, 1 }, /* poivron.org */ - { 41270, 0, 0, 1 }, /* potager.org */ - { 41278, 0, 0, 1 }, /* read-books.org */ - { 41289, 0, 0, 1 }, /* readmyblog.org */ - { 11594, 0, 0, 1 }, /* selfip.org */ - { 41300, 0, 0, 1 }, /* sellsyourhome.org */ - { 14080, 0, 0, 1 }, /* servebbs.org */ - { 14108, 0, 0, 1 }, /* serveftp.org */ - { 14117, 0, 0, 1 }, /* servegame.org */ - { 15080, 0, 0, 1 }, /* spdns.org */ - { 41314, 0, 0, 1 }, /* stuff-4-sale.org */ - { 41327, 0, 0, 1 }, /* sweetpepper.org */ - { 41339, 0, 0, 1 }, /* tunk.org */ - { 2921, 0, 0, 1 }, /* tuxfamily.org */ - { 41344, 0, 0, 1 }, /* ufcfan.org */ + { 41284, 7255, 1, 0 }, /* amune.org */ + { 12188, 0, 0, 1 }, /* blogdns.org */ + { 41290, 0, 0, 1 }, /* blogsite.org */ + { 41299, 0, 0, 1 }, /* bmoattachments.org */ + { 41314, 0, 0, 1 }, /* boldlygoingnowhere.org */ + { 41333, 0, 0, 1 }, /* cable-modem.org */ + { 34284, 7256, 2, 0 }, /* cdn77.org */ + { 7103, 3360, 1, 0 }, /* cdn77-secure.org */ + { 41345, 0, 0, 1 }, /* certmgr.org */ + { 11353, 0, 0, 1 }, /* cloudns.org */ + { 41353, 0, 0, 1 }, /* collegefan.org */ + { 41364, 0, 0, 1 }, /* couchpotatofries.org */ + { 15066, 0, 0, 1 }, /* ddnss.org */ + { 15380, 0, 0, 1 }, /* diskstation.org */ + { 12329, 0, 0, 1 }, /* dnsalias.org */ + { 12338, 0, 0, 1 }, /* dnsdojo.org */ + { 12357, 0, 0, 1 }, /* doesntexist.org */ + { 12369, 0, 0, 1 }, /* dontexist.org */ + { 12379, 0, 0, 1 }, /* doomdns.org */ + { 12409, 0, 0, 1 }, /* dsmynas.org */ + { 41381, 0, 0, 1 }, /* duckdns.org */ + { 41389, 0, 0, 1 }, /* dvrdns.org */ + { 12428, 0, 0, 1 }, /* dynalias.org */ + { 11559, 7259, 2, 1 }, /* dyndns.org */ + { 34371, 0, 0, 1 }, /* endofinternet.org */ + { 41396, 0, 0, 1 }, /* endoftheinternet.org */ + { 2797, 7261, 55, 1 }, /* eu.org */ + { 12652, 0, 0, 1 }, /* familyds.org */ + { 41413, 0, 0, 1 }, /* fedorainfracloud.org */ + { 41430, 0, 0, 1 }, /* fedorapeople.org */ + { 33324, 4188, 1, 0 }, /* fedoraproject.org */ + { 41443, 0, 0, 1 }, /* from-me.org */ + { 41451, 0, 0, 1 }, /* game-host.org */ + { 11908, 0, 0, 1 }, /* gotdns.org */ + { 41461, 0, 0, 1 }, /* hepforge.org */ + { 3922, 0, 0, 1 }, /* hk.org */ + { 13214, 0, 0, 1 }, /* hobby-site.org */ + { 41470, 0, 0, 1 }, /* homedns.org */ + { 34463, 0, 0, 1 }, /* homeftp.org */ + { 13225, 0, 0, 1 }, /* homelinux.org */ + { 13266, 0, 0, 1 }, /* homeunix.org */ + { 30167, 0, 0, 1 }, /* hopto.org */ + { 41478, 0, 0, 1 }, /* is-a-bruinsfan.org */ + { 41493, 0, 0, 1 }, /* is-a-candidate.org */ + { 41508, 0, 0, 1 }, /* is-a-celticsfan.org */ + { 13357, 0, 0, 1 }, /* is-a-chef.org */ + { 13449, 0, 0, 1 }, /* is-a-geek.org */ + { 41524, 0, 0, 1 }, /* is-a-knight.org */ + { 15509, 0, 0, 1 }, /* is-a-linux-user.org */ + { 41536, 0, 0, 1 }, /* is-a-patsfan.org */ + { 41549, 0, 0, 1 }, /* is-a-soxfan.org */ + { 41561, 0, 0, 1 }, /* is-found.org */ + { 41570, 0, 0, 1 }, /* is-lost.org */ + { 41578, 0, 0, 1 }, /* is-saved.org */ + { 41587, 0, 0, 1 }, /* is-very-bad.org */ + { 41599, 0, 0, 1 }, /* is-very-evil.org */ + { 41612, 0, 0, 1 }, /* is-very-good.org */ + { 41625, 0, 0, 1 }, /* is-very-nice.org */ + { 41638, 0, 0, 1 }, /* is-very-sweet.org */ + { 13872, 0, 0, 1 }, /* isa-geek.org */ + { 11545, 0, 0, 1 }, /* js.org */ + { 34486, 0, 0, 1 }, /* kicks-ass.org */ + { 41652, 0, 0, 1 }, /* misconfused.org */ + { 2916, 0, 0, 1 }, /* mlbfan.org */ + { 41664, 0, 0, 1 }, /* my-firewall.org */ + { 41676, 0, 0, 1 }, /* myfirewall.org */ + { 11615, 0, 0, 1 }, /* myftp.org */ + { 1348, 0, 0, 1 }, /* mysecuritycamera.org */ + { 41687, 0, 0, 1 }, /* nflfan.org */ + { 11621, 0, 0, 1 }, /* no-ip.org */ + { 41694, 0, 0, 1 }, /* pimienta.org */ + { 10634, 0, 0, 1 }, /* podzone.org */ + { 41703, 0, 0, 1 }, /* poivron.org */ + { 41711, 0, 0, 1 }, /* potager.org */ + { 41719, 0, 0, 1 }, /* read-books.org */ + { 41730, 0, 0, 1 }, /* readmyblog.org */ + { 11627, 0, 0, 1 }, /* selfip.org */ + { 41741, 0, 0, 1 }, /* sellsyourhome.org */ + { 14271, 0, 0, 1 }, /* servebbs.org */ + { 14299, 0, 0, 1 }, /* serveftp.org */ + { 14308, 0, 0, 1 }, /* servegame.org */ + { 15347, 0, 0, 1 }, /* spdns.org */ + { 41755, 0, 0, 1 }, /* stuff-4-sale.org */ + { 41768, 0, 0, 1 }, /* sweetpepper.org */ + { 41780, 0, 0, 1 }, /* tunk.org */ + { 2906, 0, 0, 1 }, /* tuxfamily.org */ + { 11901, 0, 0, 1 }, /* twmail.org */ + { 41785, 0, 0, 1 }, /* ufcfan.org */ { 264, 0, 0, 1 }, /* us.org */ - { 11601, 0, 0, 1 }, /* webhop.org */ - { 41351, 0, 0, 1 }, /* wmflabs.org */ - { 6329, 0, 0, 1 }, /* za.org */ - { 41359, 0, 0, 1 }, /* zapto.org */ - { 41369, 7109, 1, 0 }, /* origin.cdn77-secure.org */ - { 41384, 0, 0, 1 }, /* agro.pl */ - { 6578, 0, 0, 1 }, /* aid.pl */ + { 11634, 0, 0, 1 }, /* webhop.org */ + { 41792, 0, 0, 1 }, /* wmflabs.org */ + { 6312, 0, 0, 1 }, /* za.org */ + { 41800, 0, 0, 1 }, /* zapto.org */ + { 41815, 7258, 1, 0 }, /* origin.cdn77-secure.org */ + { 41830, 0, 0, 1 }, /* agro.pl */ + { 6561, 0, 0, 1 }, /* aid.pl */ { 527, 0, 0, 1 }, /* art.pl */ - { 7909, 0, 0, 1 }, /* atm.pl */ - { 41389, 0, 0, 1 }, /* augustow.pl */ + { 7907, 0, 0, 1 }, /* atm.pl */ + { 41835, 0, 0, 1 }, /* augustow.pl */ { 629, 0, 0, 1 }, /* auto.pl */ - { 41398, 0, 0, 1 }, /* babia-gora.pl */ - { 41409, 0, 0, 1 }, /* bedzin.pl */ - { 41416, 0, 0, 1 }, /* beep.pl */ - { 41421, 0, 0, 1 }, /* beskidy.pl */ - { 41429, 0, 0, 1 }, /* bialowieza.pl */ - { 41440, 0, 0, 1 }, /* bialystok.pl */ - { 41450, 0, 0, 1 }, /* bielawa.pl */ - { 41458, 0, 0, 1 }, /* bieszczady.pl */ - { 985, 0, 0, 1 }, /* biz.pl */ - { 41469, 0, 0, 1 }, /* boleslawiec.pl */ - { 41481, 0, 0, 1 }, /* bydgoszcz.pl */ - { 41491, 0, 0, 1 }, /* bytom.pl */ - { 41497, 0, 0, 1 }, /* cieszyn.pl */ + { 41844, 0, 0, 1 }, /* babia-gora.pl */ + { 41855, 0, 0, 1 }, /* bedzin.pl */ + { 41862, 0, 0, 1 }, /* beep.pl */ + { 41867, 0, 0, 1 }, /* beskidy.pl */ + { 41875, 0, 0, 1 }, /* bialowieza.pl */ + { 41886, 0, 0, 1 }, /* bialystok.pl */ + { 41896, 0, 0, 1 }, /* bielawa.pl */ + { 41904, 0, 0, 1 }, /* bieszczady.pl */ + { 986, 0, 0, 1 }, /* biz.pl */ + { 41915, 0, 0, 1 }, /* boleslawiec.pl */ + { 41927, 0, 0, 1 }, /* bydgoszcz.pl */ + { 41937, 0, 0, 1 }, /* bytom.pl */ + { 41943, 0, 0, 1 }, /* cieszyn.pl */ { 113, 0, 0, 1 }, /* co.pl */ - { 1913, 0, 0, 1 }, /* com.pl */ - { 2589, 0, 0, 1 }, /* czeladz.pl */ - { 41505, 0, 0, 1 }, /* czest.pl */ - { 36209, 0, 0, 1 }, /* dlugoleka.pl */ - { 2624, 0, 0, 1 }, /* edu.pl */ - { 41511, 0, 0, 1 }, /* elblag.pl */ - { 5027, 0, 0, 1 }, /* elk.pl */ - { 41522, 0, 0, 1 }, /* gda.pl */ - { 41526, 0, 0, 1 }, /* gdansk.pl */ - { 41533, 0, 0, 1 }, /* gdynia.pl */ - { 41540, 0, 0, 1 }, /* gliwice.pl */ - { 41548, 0, 0, 1 }, /* glogow.pl */ - { 41555, 0, 0, 1 }, /* gmina.pl */ - { 41561, 0, 0, 1 }, /* gniezno.pl */ - { 41569, 0, 0, 1 }, /* gorlice.pl */ - { 3686, 7212, 47, 1 }, /* gov.pl */ - { 41577, 0, 0, 1 }, /* grajewo.pl */ - { 7330, 0, 0, 1 }, /* gsm.pl */ - { 41585, 0, 0, 1 }, /* ilawa.pl */ - { 3167, 0, 0, 1 }, /* info.pl */ - { 41591, 0, 0, 1 }, /* jaworzno.pl */ - { 41600, 0, 0, 1 }, /* jelenia-gora.pl */ - { 41613, 0, 0, 1 }, /* jgora.pl */ - { 41619, 0, 0, 1 }, /* kalisz.pl */ - { 41626, 0, 0, 1 }, /* karpacz.pl */ - { 41634, 0, 0, 1 }, /* kartuzy.pl */ - { 41642, 0, 0, 1 }, /* kaszuby.pl */ - { 41650, 0, 0, 1 }, /* katowice.pl */ - { 41659, 0, 0, 1 }, /* kazimierz-dolny.pl */ - { 41675, 0, 0, 1 }, /* kepno.pl */ - { 41681, 0, 0, 1 }, /* ketrzyn.pl */ - { 41689, 0, 0, 1 }, /* klodzko.pl */ - { 41697, 0, 0, 1 }, /* kobierzyce.pl */ - { 41708, 0, 0, 1 }, /* kolobrzeg.pl */ - { 41718, 0, 0, 1 }, /* konin.pl */ - { 41724, 0, 0, 1 }, /* konskowola.pl */ - { 41735, 0, 0, 1 }, /* krakow.pl */ - { 41742, 0, 0, 1 }, /* kutno.pl */ - { 41748, 0, 0, 1 }, /* lapy.pl */ - { 41753, 0, 0, 1 }, /* lebork.pl */ - { 41760, 0, 0, 1 }, /* legnica.pl */ - { 41768, 0, 0, 1 }, /* lezajsk.pl */ - { 41776, 0, 0, 1 }, /* limanowa.pl */ - { 41785, 0, 0, 1 }, /* lomza.pl */ - { 2198, 0, 0, 1 }, /* lowicz.pl */ - { 41791, 0, 0, 1 }, /* lubin.pl */ - { 41797, 0, 0, 1 }, /* lukow.pl */ - { 2651, 0, 0, 1 }, /* mail.pl */ - { 41803, 0, 0, 1 }, /* malbork.pl */ - { 41811, 0, 0, 1 }, /* malopolska.pl */ - { 41822, 0, 0, 1 }, /* mazowsze.pl */ - { 41831, 0, 0, 1 }, /* mazury.pl */ - { 1858, 0, 0, 1 }, /* med.pl */ - { 5327, 0, 0, 1 }, /* media.pl */ - { 41838, 0, 0, 1 }, /* miasta.pl */ - { 41845, 0, 0, 1 }, /* mielec.pl */ - { 41852, 0, 0, 1 }, /* mielno.pl */ - { 4195, 0, 0, 1 }, /* mil.pl */ - { 41859, 0, 0, 1 }, /* mragowo.pl */ - { 41867, 0, 0, 1 }, /* naklo.pl */ - { 4185, 0, 0, 1 }, /* net.pl */ - { 15199, 0, 0, 1 }, /* nieruchomosci.pl */ - { 5998, 0, 0, 1 }, /* nom.pl */ - { 41873, 0, 0, 1 }, /* nowaruda.pl */ - { 41882, 0, 0, 1 }, /* nysa.pl */ - { 41887, 0, 0, 1 }, /* olawa.pl */ - { 41893, 0, 0, 1 }, /* olecko.pl */ - { 41900, 0, 0, 1 }, /* olkusz.pl */ - { 41907, 0, 0, 1 }, /* olsztyn.pl */ - { 41915, 0, 0, 1 }, /* opoczno.pl */ - { 41923, 0, 0, 1 }, /* opole.pl */ - { 6070, 0, 0, 1 }, /* org.pl */ - { 41929, 0, 0, 1 }, /* ostroda.pl */ - { 41937, 0, 0, 1 }, /* ostroleka.pl */ - { 41947, 0, 0, 1 }, /* ostrowiec.pl */ - { 4657, 0, 0, 1 }, /* ostrowwlkp.pl */ - { 5618, 0, 0, 1 }, /* pc.pl */ - { 41957, 0, 0, 1 }, /* pila.pl */ - { 7652, 0, 0, 1 }, /* pisz.pl */ - { 41962, 0, 0, 1 }, /* podhale.pl */ - { 41970, 0, 0, 1 }, /* podlasie.pl */ - { 41979, 0, 0, 1 }, /* polkowice.pl */ - { 41989, 0, 0, 1 }, /* pomorskie.pl */ - { 41999, 0, 0, 1 }, /* pomorze.pl */ - { 42007, 0, 0, 1 }, /* powiat.pl */ - { 42014, 0, 0, 1 }, /* poznan.pl */ - { 11393, 0, 0, 1 }, /* priv.pl */ - { 42021, 0, 0, 1 }, /* prochowice.pl */ - { 42032, 0, 0, 1 }, /* pruszkow.pl */ - { 42041, 0, 0, 1 }, /* przeworsk.pl */ - { 42051, 0, 0, 1 }, /* pulawy.pl */ - { 42058, 0, 0, 1 }, /* radom.pl */ - { 42064, 0, 0, 1 }, /* rawa-maz.pl */ - { 2765, 0, 0, 1 }, /* realestate.pl */ - { 15620, 0, 0, 1 }, /* rel.pl */ - { 42073, 0, 0, 1 }, /* rybnik.pl */ - { 42080, 0, 0, 1 }, /* rzeszow.pl */ - { 42088, 0, 0, 1 }, /* sanok.pl */ - { 42094, 0, 0, 1 }, /* sejny.pl */ - { 7168, 0, 0, 1 }, /* sex.pl */ - { 7245, 0, 0, 1 }, /* shop.pl */ - { 42100, 0, 0, 1 }, /* sklep.pl */ - { 42106, 0, 0, 1 }, /* skoczow.pl */ - { 42114, 0, 0, 1 }, /* slask.pl */ - { 42120, 0, 0, 1 }, /* slupsk.pl */ - { 42127, 0, 0, 1 }, /* sopot.pl */ - { 36625, 0, 0, 1 }, /* sos.pl */ - { 42133, 0, 0, 1 }, /* sosnowiec.pl */ - { 42143, 0, 0, 1 }, /* stalowa-wola.pl */ - { 42156, 0, 0, 1 }, /* starachowice.pl */ - { 42169, 0, 0, 1 }, /* stargard.pl */ - { 42178, 0, 0, 1 }, /* suwalki.pl */ - { 42186, 0, 0, 1 }, /* swidnica.pl */ - { 42195, 0, 0, 1 }, /* swiebodzin.pl */ - { 42206, 0, 0, 1 }, /* swinoujscie.pl */ - { 42218, 0, 0, 1 }, /* szczecin.pl */ - { 42227, 0, 0, 1 }, /* szczytno.pl */ - { 42236, 0, 0, 1 }, /* szkola.pl */ - { 42243, 0, 0, 1 }, /* targi.pl */ - { 42249, 0, 0, 1 }, /* tarnobrzeg.pl */ - { 42260, 0, 0, 1 }, /* tgory.pl */ - { 7910, 0, 0, 1 }, /* tm.pl */ - { 42266, 0, 0, 1 }, /* tourism.pl */ - { 8005, 0, 0, 1 }, /* travel.pl */ - { 42274, 0, 0, 1 }, /* turek.pl */ - { 42280, 0, 0, 1 }, /* turystyka.pl */ - { 42290, 0, 0, 1 }, /* tychy.pl */ - { 42296, 0, 0, 1 }, /* ustka.pl */ - { 42302, 0, 0, 1 }, /* walbrzych.pl */ - { 42312, 0, 0, 1 }, /* warmia.pl */ - { 42319, 0, 0, 1 }, /* warszawa.pl */ + { 1914, 0, 0, 1 }, /* com.pl */ + { 2588, 0, 0, 1 }, /* czeladz.pl */ + { 41951, 0, 0, 1 }, /* czest.pl */ + { 36628, 0, 0, 1 }, /* dlugoleka.pl */ + { 2623, 0, 0, 1 }, /* edu.pl */ + { 41957, 0, 0, 1 }, /* elblag.pl */ + { 5006, 0, 0, 1 }, /* elk.pl */ + { 41968, 0, 0, 1 }, /* gda.pl */ + { 41972, 0, 0, 1 }, /* gdansk.pl */ + { 41979, 0, 0, 1 }, /* gdynia.pl */ + { 41986, 0, 0, 1 }, /* gliwice.pl */ + { 41994, 0, 0, 1 }, /* glogow.pl */ + { 42001, 0, 0, 1 }, /* gmina.pl */ + { 42007, 0, 0, 1 }, /* gniezno.pl */ + { 42015, 0, 0, 1 }, /* gorlice.pl */ + { 3671, 7362, 47, 1 }, /* gov.pl */ + { 42023, 0, 0, 1 }, /* grajewo.pl */ + { 7319, 0, 0, 1 }, /* gsm.pl */ + { 42031, 0, 0, 1 }, /* ilawa.pl */ + { 3152, 0, 0, 1 }, /* info.pl */ + { 42037, 0, 0, 1 }, /* jaworzno.pl */ + { 42046, 0, 0, 1 }, /* jelenia-gora.pl */ + { 42059, 0, 0, 1 }, /* jgora.pl */ + { 42065, 0, 0, 1 }, /* kalisz.pl */ + { 42072, 0, 0, 1 }, /* karpacz.pl */ + { 42080, 0, 0, 1 }, /* kartuzy.pl */ + { 42088, 0, 0, 1 }, /* kaszuby.pl */ + { 42096, 0, 0, 1 }, /* katowice.pl */ + { 42105, 0, 0, 1 }, /* kazimierz-dolny.pl */ + { 42121, 0, 0, 1 }, /* kepno.pl */ + { 42127, 0, 0, 1 }, /* ketrzyn.pl */ + { 42135, 0, 0, 1 }, /* klodzko.pl */ + { 42143, 0, 0, 1 }, /* kobierzyce.pl */ + { 42154, 0, 0, 1 }, /* kolobrzeg.pl */ + { 42164, 0, 0, 1 }, /* konin.pl */ + { 42170, 0, 0, 1 }, /* konskowola.pl */ + { 42181, 0, 0, 1 }, /* krakow.pl */ + { 42188, 0, 0, 1 }, /* kutno.pl */ + { 42194, 0, 0, 1 }, /* lapy.pl */ + { 42199, 0, 0, 1 }, /* lebork.pl */ + { 42206, 0, 0, 1 }, /* legnica.pl */ + { 42214, 0, 0, 1 }, /* lezajsk.pl */ + { 42222, 0, 0, 1 }, /* limanowa.pl */ + { 42231, 0, 0, 1 }, /* lomza.pl */ + { 2199, 0, 0, 1 }, /* lowicz.pl */ + { 42237, 0, 0, 1 }, /* lubin.pl */ + { 42243, 0, 0, 1 }, /* lukow.pl */ + { 2650, 0, 0, 1 }, /* mail.pl */ + { 42249, 0, 0, 1 }, /* malbork.pl */ + { 42257, 0, 0, 1 }, /* malopolska.pl */ + { 42268, 0, 0, 1 }, /* mazowsze.pl */ + { 42277, 0, 0, 1 }, /* mazury.pl */ + { 1859, 0, 0, 1 }, /* med.pl */ + { 5306, 0, 0, 1 }, /* media.pl */ + { 42284, 0, 0, 1 }, /* miasta.pl */ + { 42291, 0, 0, 1 }, /* mielec.pl */ + { 42298, 0, 0, 1 }, /* mielno.pl */ + { 4170, 0, 0, 1 }, /* mil.pl */ + { 42305, 0, 0, 1 }, /* mragowo.pl */ + { 42313, 0, 0, 1 }, /* naklo.pl */ + { 5737, 0, 0, 1 }, /* net.pl */ + { 15476, 0, 0, 1 }, /* nieruchomosci.pl */ + { 5970, 0, 0, 1 }, /* nom.pl */ + { 42319, 0, 0, 1 }, /* nowaruda.pl */ + { 42328, 0, 0, 1 }, /* nysa.pl */ + { 42333, 0, 0, 1 }, /* olawa.pl */ + { 42339, 0, 0, 1 }, /* olecko.pl */ + { 42346, 0, 0, 1 }, /* olkusz.pl */ + { 42353, 0, 0, 1 }, /* olsztyn.pl */ + { 42361, 0, 0, 1 }, /* opoczno.pl */ + { 42369, 0, 0, 1 }, /* opole.pl */ + { 6053, 0, 0, 1 }, /* org.pl */ + { 42375, 0, 0, 1 }, /* ostroda.pl */ + { 42383, 0, 0, 1 }, /* ostroleka.pl */ + { 42393, 0, 0, 1 }, /* ostrowiec.pl */ + { 4636, 0, 0, 1 }, /* ostrowwlkp.pl */ + { 5597, 0, 0, 1 }, /* pc.pl */ + { 42403, 0, 0, 1 }, /* pila.pl */ + { 7650, 0, 0, 1 }, /* pisz.pl */ + { 42408, 0, 0, 1 }, /* podhale.pl */ + { 42416, 0, 0, 1 }, /* podlasie.pl */ + { 42425, 0, 0, 1 }, /* polkowice.pl */ + { 42435, 0, 0, 1 }, /* pomorskie.pl */ + { 42445, 0, 0, 1 }, /* pomorze.pl */ + { 42453, 0, 0, 1 }, /* powiat.pl */ + { 42460, 0, 0, 1 }, /* poznan.pl */ + { 11418, 0, 0, 1 }, /* priv.pl */ + { 42467, 0, 0, 1 }, /* prochowice.pl */ + { 42478, 0, 0, 1 }, /* pruszkow.pl */ + { 42487, 0, 0, 1 }, /* przeworsk.pl */ + { 42497, 0, 0, 1 }, /* pulawy.pl */ + { 42504, 0, 0, 1 }, /* radom.pl */ + { 42510, 0, 0, 1 }, /* rawa-maz.pl */ + { 2764, 0, 0, 1 }, /* realestate.pl */ + { 15913, 0, 0, 1 }, /* rel.pl */ + { 42519, 0, 0, 1 }, /* rybnik.pl */ + { 42526, 0, 0, 1 }, /* rzeszow.pl */ + { 42534, 0, 0, 1 }, /* sanok.pl */ + { 42540, 0, 0, 1 }, /* sejny.pl */ + { 7157, 0, 0, 1 }, /* sex.pl */ + { 7234, 0, 0, 1 }, /* shop.pl */ + { 42546, 0, 0, 1 }, /* sklep.pl */ + { 42552, 0, 0, 1 }, /* skoczow.pl */ + { 42560, 0, 0, 1 }, /* slask.pl */ + { 42566, 0, 0, 1 }, /* slupsk.pl */ + { 42573, 0, 0, 1 }, /* sopot.pl */ + { 37044, 0, 0, 1 }, /* sos.pl */ + { 42579, 0, 0, 1 }, /* sosnowiec.pl */ + { 42589, 0, 0, 1 }, /* stalowa-wola.pl */ + { 42602, 0, 0, 1 }, /* starachowice.pl */ + { 42615, 0, 0, 1 }, /* stargard.pl */ + { 42624, 0, 0, 1 }, /* suwalki.pl */ + { 42632, 0, 0, 1 }, /* swidnica.pl */ + { 42641, 0, 0, 1 }, /* swiebodzin.pl */ + { 42652, 0, 0, 1 }, /* swinoujscie.pl */ + { 42664, 0, 0, 1 }, /* szczecin.pl */ + { 42673, 0, 0, 1 }, /* szczytno.pl */ + { 42682, 0, 0, 1 }, /* szkola.pl */ + { 42689, 0, 0, 1 }, /* targi.pl */ + { 42695, 0, 0, 1 }, /* tarnobrzeg.pl */ + { 42706, 0, 0, 1 }, /* tgory.pl */ + { 7908, 0, 0, 1 }, /* tm.pl */ + { 42712, 0, 0, 1 }, /* tourism.pl */ + { 8003, 0, 0, 1 }, /* travel.pl */ + { 42720, 0, 0, 1 }, /* turek.pl */ + { 42726, 0, 0, 1 }, /* turystyka.pl */ + { 42736, 0, 0, 1 }, /* tychy.pl */ + { 42742, 0, 0, 1 }, /* ustka.pl */ + { 42748, 0, 0, 1 }, /* walbrzych.pl */ + { 42758, 0, 0, 1 }, /* warmia.pl */ + { 42765, 0, 0, 1 }, /* warszawa.pl */ { 648, 0, 0, 1 }, /* waw.pl */ - { 42328, 0, 0, 1 }, /* wegrow.pl */ - { 42335, 0, 0, 1 }, /* wielun.pl */ - { 1784, 0, 0, 1 }, /* wlocl.pl */ - { 42342, 0, 0, 1 }, /* wloclawek.pl */ - { 42352, 0, 0, 1 }, /* wodzislaw.pl */ - { 42362, 0, 0, 1 }, /* wolomin.pl */ - { 42370, 0, 0, 1 }, /* wroc.pl */ - { 4836, 0, 0, 1 }, /* wroclaw.pl */ - { 42375, 0, 0, 1 }, /* zachpomor.pl */ - { 42385, 0, 0, 1 }, /* zagan.pl */ - { 42391, 0, 0, 1 }, /* zakopane.pl */ - { 42400, 0, 0, 1 }, /* zarow.pl */ - { 42406, 0, 0, 1 }, /* zgora.pl */ - { 42412, 0, 0, 1 }, /* zgorzelec.pl */ - { 1913, 0, 0, 1 }, /* com.sh */ - { 3686, 0, 0, 1 }, /* gov.sh */ - { 42638, 0, 0, 1 }, /* hashbang.sh */ - { 4195, 0, 0, 1 }, /* mil.sh */ - { 4185, 0, 0, 1 }, /* net.sh */ - { 5894, 0, 0, 1 }, /* now.sh */ - { 6070, 0, 0, 1 }, /* org.sh */ - { 42647, 3687, 1, 0 }, /* platform.sh */ - { 16266, 0, 0, 1 }, /* av.tr */ - { 14085, 0, 0, 1 }, /* bbs.tr */ - { 42972, 0, 0, 1 }, /* bel.tr */ - { 985, 0, 0, 1 }, /* biz.tr */ - { 1913, 3672, 1, 1 }, /* com.tr */ - { 11349, 0, 0, 1 }, /* dr.tr */ - { 2624, 0, 0, 1 }, /* edu.tr */ - { 8368, 0, 0, 1 }, /* gen.tr */ - { 3686, 0, 0, 1 }, /* gov.tr */ - { 3167, 0, 0, 1 }, /* info.tr */ - { 15174, 0, 0, 1 }, /* k12.tr */ - { 42976, 0, 0, 1 }, /* kep.tr */ - { 4195, 0, 0, 1 }, /* mil.tr */ - { 5725, 0, 0, 1 }, /* name.tr */ - { 5521, 3685, 1, 1 }, /* nc.tr */ - { 4185, 0, 0, 1 }, /* net.tr */ - { 6070, 0, 0, 1 }, /* org.tr */ - { 15170, 0, 0, 1 }, /* pol.tr */ + { 42774, 0, 0, 1 }, /* wegrow.pl */ + { 42781, 0, 0, 1 }, /* wielun.pl */ + { 1785, 0, 0, 1 }, /* wlocl.pl */ + { 42788, 0, 0, 1 }, /* wloclawek.pl */ + { 42798, 0, 0, 1 }, /* wodzislaw.pl */ + { 42808, 0, 0, 1 }, /* wolomin.pl */ + { 42816, 0, 0, 1 }, /* wroc.pl */ + { 4815, 0, 0, 1 }, /* wroclaw.pl */ + { 42821, 0, 0, 1 }, /* zachpomor.pl */ + { 42831, 0, 0, 1 }, /* zagan.pl */ + { 42837, 0, 0, 1 }, /* zakopane.pl */ + { 42846, 0, 0, 1 }, /* zarow.pl */ + { 42852, 0, 0, 1 }, /* zgora.pl */ + { 42858, 0, 0, 1 }, /* zgorzelec.pl */ + { 62, 0, 0, 1 }, /* ac.ru */ + { 43021, 0, 0, 1 }, /* adygeya.ru */ + { 43029, 0, 0, 1 }, /* bashkiria.ru */ + { 4328, 0, 0, 1 }, /* bir.ru */ + { 10645, 0, 0, 1 }, /* blogspot.ru */ + { 931, 0, 0, 1 }, /* cbg.ru */ + { 43039, 7505, 1, 0 }, /* cldmail.ru */ + { 1914, 0, 0, 1 }, /* com.ru */ + { 43047, 0, 0, 1 }, /* dagestan.ru */ + { 2623, 0, 0, 1 }, /* edu.ru */ + { 3671, 0, 0, 1 }, /* gov.ru */ + { 43056, 0, 0, 1 }, /* grozny.ru */ + { 3617, 0, 0, 1 }, /* int.ru */ + { 43063, 0, 0, 1 }, /* kalmykia.ru */ + { 43072, 0, 0, 1 }, /* kustanai.ru */ + { 43081, 0, 0, 1 }, /* marine.ru */ + { 4170, 0, 0, 1 }, /* mil.ru */ + { 43088, 0, 0, 1 }, /* mordovia.ru */ + { 7300, 0, 0, 1 }, /* msk.ru */ + { 43097, 0, 0, 1 }, /* mytis.ru */ + { 43103, 0, 0, 1 }, /* nalchik.ru */ + { 43111, 0, 0, 1 }, /* nov.ru */ + { 43115, 0, 0, 1 }, /* pyatigorsk.ru */ + { 11300, 0, 0, 1 }, /* spb.ru */ + { 42996, 0, 0, 1 }, /* test.ru */ + { 43126, 0, 0, 1 }, /* vladikavkaz.ru */ + { 43138, 0, 0, 1 }, /* vladimir.ru */ + { 1914, 0, 0, 1 }, /* com.sh */ + { 3671, 0, 0, 1 }, /* gov.sh */ + { 43215, 0, 0, 1 }, /* hashbang.sh */ + { 4170, 0, 0, 1 }, /* mil.sh */ + { 5737, 0, 0, 1 }, /* net.sh */ + { 5866, 0, 0, 1 }, /* now.sh */ + { 6053, 0, 0, 1 }, /* org.sh */ + { 43224, 3853, 1, 0 }, /* platform.sh */ + { 30107, 0, 0, 1 }, /* cyon.site */ + { 43233, 3853, 1, 0 }, /* platformsh.site */ + { 16611, 0, 0, 1 }, /* av.tr */ + { 14276, 0, 0, 1 }, /* bbs.tr */ + { 43668, 0, 0, 1 }, /* bel.tr */ + { 986, 0, 0, 1 }, /* biz.tr */ + { 1914, 3838, 1, 1 }, /* com.tr */ + { 11335, 0, 0, 1 }, /* dr.tr */ + { 2623, 0, 0, 1 }, /* edu.tr */ + { 8361, 0, 0, 1 }, /* gen.tr */ + { 3671, 0, 0, 1 }, /* gov.tr */ + { 3152, 0, 0, 1 }, /* info.tr */ + { 15451, 0, 0, 1 }, /* k12.tr */ + { 43672, 0, 0, 1 }, /* kep.tr */ + { 4170, 0, 0, 1 }, /* mil.tr */ + { 5695, 0, 0, 1 }, /* name.tr */ + { 5500, 3851, 1, 1 }, /* nc.tr */ + { 5737, 0, 0, 1 }, /* net.tr */ + { 6053, 0, 0, 1 }, /* org.tr */ + { 15447, 0, 0, 1 }, /* pol.tr */ { 279, 0, 0, 1 }, /* tel.tr */ - { 2546, 0, 0, 1 }, /* tv.tr */ - { 11967, 0, 0, 1 }, /* web.tr */ + { 2549, 0, 0, 1 }, /* tv.tr */ + { 12109, 0, 0, 1 }, /* web.tr */ + { 10645, 0, 0, 1 }, /* blogspot.tw */ + { 1850, 0, 0, 1 }, /* club.tw */ + { 1914, 7744, 1, 1 }, /* com.tw */ + { 985, 0, 0, 1 }, /* ebiz.tw */ + { 2623, 0, 0, 1 }, /* edu.tw */ + { 3377, 0, 0, 1 }, /* game.tw */ + { 3671, 0, 0, 1 }, /* gov.tw */ + { 15748, 0, 0, 1 }, /* idv.tw */ + { 4170, 0, 0, 1 }, /* mil.tw */ + { 5737, 0, 0, 1 }, /* net.tw */ + { 6053, 0, 0, 1 }, /* org.tw */ + { 11480, 0, 0, 1 }, /* url.tw */ + { 43710, 0, 0, 1 }, /* xn--czrw28b.tw */ + { 15834, 0, 0, 1 }, /* xn--uc0atv.tw */ + { 43722, 0, 0, 1 }, /* xn--zf0ao64a.tw */ { 62, 0, 0, 1 }, /* ac.uk */ - { 113, 7686, 3, 1 }, /* co.uk */ - { 3686, 7689, 2, 1 }, /* gov.uk */ - { 5103, 0, 0, 1 }, /* ltd.uk */ - { 1693, 0, 0, 1 }, /* me.uk */ - { 4185, 0, 0, 1 }, /* net.uk */ - { 43402, 0, 0, 1 }, /* nhs.uk */ - { 6070, 0, 0, 1 }, /* org.uk */ - { 4860, 0, 0, 1 }, /* plc.uk */ - { 43406, 0, 0, 1 }, /* police.uk */ - { 1145, 3687, 1, 0 }, /* sch.uk */ - { 4892, 7691, 3, 1 }, /* ak.us */ - { 290, 7691, 3, 1 }, /* al.us */ - { 494, 7691, 3, 1 }, /* ar.us */ - { 537, 7691, 3, 1 }, /* as.us */ - { 671, 7691, 3, 1 }, /* az.us */ - { 221, 7691, 3, 1 }, /* ca.us */ - { 11367, 0, 0, 1 }, /* cloudns.us */ - { 113, 7691, 3, 1 }, /* co.us */ - { 2004, 7691, 3, 1 }, /* ct.us */ - { 12612, 7691, 3, 1 }, /* dc.us */ - { 2276, 7691, 3, 1 }, /* de.us */ - { 5842, 0, 0, 1 }, /* dni.us */ - { 15879, 0, 0, 1 }, /* drud.us */ - { 11314, 0, 0, 1 }, /* fed.us */ - { 210, 7691, 3, 1 }, /* fl.us */ - { 3355, 7691, 3, 1 }, /* ga.us */ - { 43421, 0, 0, 1 }, /* golffan.us */ - { 3768, 7691, 3, 1 }, /* gu.us */ - { 512, 7694, 2, 1 }, /* hi.us */ - { 547, 7691, 3, 1 }, /* ia.us */ - { 437, 7691, 3, 1 }, /* id.us */ - { 2653, 7691, 3, 1 }, /* il.us */ - { 898, 7691, 3, 1 }, /* in.us */ - { 43429, 0, 0, 1 }, /* is-by.us */ - { 8291, 0, 0, 1 }, /* isa.us */ - { 31995, 0, 0, 1 }, /* kids.us */ - { 6843, 7691, 3, 1 }, /* ks.us */ - { 4705, 7691, 3, 1 }, /* ky.us */ - { 2173, 7691, 3, 1 }, /* la.us */ - { 43435, 0, 0, 1 }, /* land-4-sale.us */ - { 5151, 3522, 3, 1 }, /* ma.us */ - { 5320, 7691, 3, 1 }, /* md.us */ - { 1693, 7691, 3, 1 }, /* me.us */ - { 5397, 7691, 3, 1 }, /* mi.us */ - { 5445, 7691, 3, 1 }, /* mn.us */ - { 3600, 7691, 3, 1 }, /* mo.us */ - { 1059, 7691, 3, 1 }, /* ms.us */ - { 5609, 7691, 3, 1 }, /* mt.us */ - { 5521, 7691, 3, 1 }, /* nc.us */ - { 727, 7694, 2, 1 }, /* nd.us */ - { 1203, 7691, 3, 1 }, /* ne.us */ - { 12772, 7691, 3, 1 }, /* nh.us */ - { 4467, 7691, 3, 1 }, /* nj.us */ - { 11902, 7691, 3, 1 }, /* nm.us */ - { 29838, 0, 0, 1 }, /* noip.us */ - { 43447, 0, 0, 1 }, /* nsn.us */ - { 12788, 7691, 3, 1 }, /* nv.us */ - { 206, 7691, 3, 1 }, /* ny.us */ - { 6794, 7691, 3, 1 }, /* oh.us */ - { 1126, 7691, 3, 1 }, /* ok.us */ - { 137, 7691, 3, 1 }, /* or.us */ - { 522, 7691, 3, 1 }, /* pa.us */ - { 43451, 0, 0, 1 }, /* pointto.us */ - { 6402, 7691, 3, 1 }, /* pr.us */ - { 2994, 7691, 3, 1 }, /* ri.us */ - { 2158, 7691, 3, 1 }, /* sc.us */ - { 5381, 7694, 2, 1 }, /* sd.us */ - { 41314, 0, 0, 1 }, /* stuff-4-sale.us */ - { 5613, 7691, 3, 1 }, /* tn.us */ - { 12852, 7691, 3, 1 }, /* tx.us */ - { 3841, 7691, 3, 1 }, /* ut.us */ - { 834, 7691, 3, 1 }, /* va.us */ - { 8237, 7691, 3, 1 }, /* vi.us */ - { 12876, 7691, 3, 1 }, /* vt.us */ - { 5971, 7691, 3, 1 }, /* wa.us */ - { 4625, 7691, 3, 1 }, /* wi.us */ - { 12900, 7699, 1, 1 }, /* wv.us */ - { 12908, 7691, 3, 1 }, /* wy.us */ - { 1579, 0, 0, 1 }, /* cc.ma.us */ - { 15174, 7696, 3, 1 }, /* k12.ma.us */ - { 15182, 0, 0, 1 }, /* lib.ma.us */ - { 1913, 3672, 1, 1 }, /* com.uy */ - { 2624, 0, 0, 1 }, /* edu.uy */ - { 43471, 0, 0, 1 }, /* gub.uy */ - { 4195, 0, 0, 1 }, /* mil.uy */ - { 4185, 0, 0, 1 }, /* net.uy */ - { 6070, 0, 0, 1 }, /* org.uy */ + { 113, 7848, 3, 1 }, /* co.uk */ + { 3671, 7851, 2, 1 }, /* gov.uk */ + { 5082, 0, 0, 1 }, /* ltd.uk */ + { 1694, 0, 0, 1 }, /* me.uk */ + { 5737, 0, 0, 1 }, /* net.uk */ + { 44107, 0, 0, 1 }, /* nhs.uk */ + { 6053, 0, 0, 1 }, /* org.uk */ + { 4839, 0, 0, 1 }, /* plc.uk */ + { 44111, 0, 0, 1 }, /* police.uk */ + { 1146, 3853, 1, 0 }, /* sch.uk */ + { 4871, 7853, 3, 1 }, /* ak.us */ + { 290, 7853, 3, 1 }, /* al.us */ + { 494, 7853, 3, 1 }, /* ar.us */ + { 537, 7853, 3, 1 }, /* as.us */ + { 671, 7853, 3, 1 }, /* az.us */ + { 221, 7853, 3, 1 }, /* ca.us */ + { 11353, 0, 0, 1 }, /* cloudns.us */ + { 113, 7853, 3, 1 }, /* co.us */ + { 2005, 7853, 3, 1 }, /* ct.us */ + { 12771, 7853, 3, 1 }, /* dc.us */ + { 2277, 7853, 3, 1 }, /* de.us */ + { 5814, 0, 0, 1 }, /* dni.us */ + { 16181, 0, 0, 1 }, /* drud.us */ + { 11293, 0, 0, 1 }, /* fed.us */ + { 210, 7853, 3, 1 }, /* fl.us */ + { 3340, 7853, 3, 1 }, /* ga.us */ + { 44126, 0, 0, 1 }, /* golffan.us */ + { 3753, 7853, 3, 1 }, /* gu.us */ + { 512, 7856, 2, 1 }, /* hi.us */ + { 547, 7853, 3, 1 }, /* ia.us */ + { 437, 7853, 3, 1 }, /* id.us */ + { 2652, 7853, 3, 1 }, /* il.us */ + { 898, 7853, 3, 1 }, /* in.us */ + { 44134, 0, 0, 1 }, /* is-by.us */ + { 8284, 0, 0, 1 }, /* isa.us */ + { 32353, 0, 0, 1 }, /* kids.us */ + { 6826, 7853, 3, 1 }, /* ks.us */ + { 4684, 7853, 3, 1 }, /* ky.us */ + { 2174, 7853, 3, 1 }, /* la.us */ + { 44140, 0, 0, 1 }, /* land-4-sale.us */ + { 5130, 3679, 3, 1 }, /* ma.us */ + { 5299, 7853, 3, 1 }, /* md.us */ + { 1694, 7853, 3, 1 }, /* me.us */ + { 5376, 7853, 3, 1 }, /* mi.us */ + { 5424, 7853, 3, 1 }, /* mn.us */ + { 3585, 7853, 3, 1 }, /* mo.us */ + { 1060, 7853, 3, 1 }, /* ms.us */ + { 5588, 7853, 3, 1 }, /* mt.us */ + { 5500, 7853, 3, 1 }, /* nc.us */ + { 727, 7856, 2, 1 }, /* nd.us */ + { 1204, 7853, 3, 1 }, /* ne.us */ + { 12931, 7853, 3, 1 }, /* nh.us */ + { 4446, 7853, 3, 1 }, /* nj.us */ + { 12038, 7853, 3, 1 }, /* nm.us */ + { 30191, 0, 0, 1 }, /* noip.us */ + { 44152, 0, 0, 1 }, /* nsn.us */ + { 12947, 7853, 3, 1 }, /* nv.us */ + { 206, 7853, 3, 1 }, /* ny.us */ + { 6777, 7853, 3, 1 }, /* oh.us */ + { 1127, 7853, 3, 1 }, /* ok.us */ + { 137, 7853, 3, 1 }, /* or.us */ + { 522, 7853, 3, 1 }, /* pa.us */ + { 44156, 0, 0, 1 }, /* pointto.us */ + { 6385, 7853, 3, 1 }, /* pr.us */ + { 2979, 7853, 3, 1 }, /* ri.us */ + { 2159, 7853, 3, 1 }, /* sc.us */ + { 5360, 7856, 2, 1 }, /* sd.us */ + { 41755, 0, 0, 1 }, /* stuff-4-sale.us */ + { 5592, 7853, 3, 1 }, /* tn.us */ + { 13011, 7853, 3, 1 }, /* tx.us */ + { 3823, 7853, 3, 1 }, /* ut.us */ + { 834, 7853, 3, 1 }, /* va.us */ + { 8230, 7853, 3, 1 }, /* vi.us */ + { 13035, 7853, 3, 1 }, /* vt.us */ + { 5943, 7853, 3, 1 }, /* wa.us */ + { 4604, 7853, 3, 1 }, /* wi.us */ + { 13059, 7861, 1, 1 }, /* wv.us */ + { 13067, 7853, 3, 1 }, /* wy.us */ + { 1580, 0, 0, 1 }, /* cc.ma.us */ + { 15451, 7858, 3, 1 }, /* k12.ma.us */ + { 15459, 0, 0, 1 }, /* lib.ma.us */ + { 1914, 3838, 1, 1 }, /* com.uy */ + { 2623, 0, 0, 1 }, /* edu.uy */ + { 44176, 0, 0, 1 }, /* gub.uy */ + { 4170, 0, 0, 1 }, /* mil.uy */ + { 5737, 0, 0, 1 }, /* net.uy */ + { 6053, 0, 0, 1 }, /* org.uy */ + { 13441, 3853, 1, 0 }, /* advisor.ws */ + { 1914, 0, 0, 1 }, /* com.ws */ + { 11559, 0, 0, 1 }, /* dyndns.ws */ + { 2623, 0, 0, 1 }, /* edu.ws */ + { 3671, 0, 0, 1 }, /* gov.ws */ + { 44184, 0, 0, 1 }, /* mypets.ws */ + { 5737, 0, 0, 1 }, /* net.ws */ + { 6053, 0, 0, 1 }, /* org.ws */ { 62, 0, 0, 1 }, /* ac.za */ - { 43533, 0, 0, 1 }, /* agric.za */ - { 5099, 0, 0, 1 }, /* alt.za */ - { 113, 3672, 1, 1 }, /* co.za */ - { 2624, 0, 0, 1 }, /* edu.za */ - { 3686, 0, 0, 1 }, /* gov.za */ - { 43539, 0, 0, 1 }, /* grondar.za */ - { 4840, 0, 0, 1 }, /* law.za */ - { 4195, 0, 0, 1 }, /* mil.za */ - { 4185, 0, 0, 1 }, /* net.za */ - { 976, 0, 0, 1 }, /* ngo.za */ - { 7786, 0, 0, 1 }, /* nis.za */ - { 5998, 0, 0, 1 }, /* nom.za */ - { 6070, 0, 0, 1 }, /* org.za */ - { 7042, 0, 0, 1 }, /* school.za */ - { 7910, 0, 0, 1 }, /* tm.za */ - { 11967, 0, 0, 1 }, /* web.za */ - { 43547, 3687, 1, 0 }, /* triton.zone */ + { 44324, 0, 0, 1 }, /* agric.za */ + { 5078, 0, 0, 1 }, /* alt.za */ + { 113, 3838, 1, 1 }, /* co.za */ + { 2623, 0, 0, 1 }, /* edu.za */ + { 3671, 0, 0, 1 }, /* gov.za */ + { 44330, 0, 0, 1 }, /* grondar.za */ + { 4819, 0, 0, 1 }, /* law.za */ + { 4170, 0, 0, 1 }, /* mil.za */ + { 5737, 0, 0, 1 }, /* net.za */ + { 977, 0, 0, 1 }, /* ngo.za */ + { 7784, 0, 0, 1 }, /* nis.za */ + { 5970, 0, 0, 1 }, /* nom.za */ + { 6053, 0, 0, 1 }, /* org.za */ + { 7031, 0, 0, 1 }, /* school.za */ + { 7908, 0, 0, 1 }, /* tm.za */ + { 12109, 0, 0, 1 }, /* web.za */ + { 11371, 0, 0, 1 }, /* lima.zone */ + { 44338, 3853, 1, 0 }, /* triton.zone */ }; static const REGISTRY_U16 kLeafNodeTable[] = { - 1913, /* com.ac */ - 2624, /* edu.ac */ - 3686, /* gov.ac */ - 4195, /* mil.ac */ - 4185, /* net.ac */ - 6070, /* org.ac */ - 5998, /* nom.ad */ + 1914, /* com.ac */ + 2623, /* edu.ac */ + 3671, /* gov.ac */ + 4170, /* mil.ac */ + 5737, /* net.ac */ + 6053, /* org.ac */ + 5970, /* nom.ad */ 62, /* ac.ae */ -10666, /* blogspot.ae */ +10645, /* blogspot.ae */ 113, /* co.ae */ - 3686, /* gov.ae */ - 4195, /* mil.ae */ - 4185, /* net.ae */ - 6070, /* org.ae */ - 1145, /* sch.ae */ -10675, /* accident-investigation.aero */ -10698, /* accident-prevention.aero */ -10718, /* aerobatic.aero */ - 1845, /* aeroclub.aero */ -10728, /* aerodrome.aero */ -10738, /* agents.aero */ -10745, /* air-surveillance.aero */ -10762, /* air-traffic-control.aero */ -10782, /* aircraft.aero */ -10791, /* airline.aero */ -10799, /* airport.aero */ -10807, /* airtraffic.aero */ -10818, /* ambulance.aero */ -10828, /* amusement.aero */ -10848, /* association.aero */ + 3671, /* gov.ae */ + 4170, /* mil.ae */ + 5737, /* net.ae */ + 6053, /* org.ae */ + 1146, /* sch.ae */ +10654, /* accident-investigation.aero */ +10677, /* accident-prevention.aero */ +10697, /* aerobatic.aero */ + 1846, /* aeroclub.aero */ +10707, /* aerodrome.aero */ +10717, /* agents.aero */ +10724, /* air-surveillance.aero */ +10741, /* air-traffic-control.aero */ +10761, /* aircraft.aero */ +10770, /* airline.aero */ +10778, /* airport.aero */ +10786, /* airtraffic.aero */ +10797, /* ambulance.aero */ +10807, /* amusement.aero */ +10827, /* association.aero */ 622, /* author.aero */ -10860, /* ballooning.aero */ - 1215, /* broker.aero */ -10871, /* caa.aero */ -10875, /* cargo.aero */ - 1527, /* catering.aero */ -10881, /* certification.aero */ -10895, /* championship.aero */ -10908, /* charter.aero */ -10916, /* civilaviation.aero */ - 1849, /* club.aero */ -10930, /* conference.aero */ -10941, /* consultant.aero */ - 1988, /* consulting.aero */ -10774, /* control.aero */ -10952, /* council.aero */ -10960, /* crew.aero */ - 2373, /* design.aero */ -10965, /* dgca.aero */ -10970, /* educator.aero */ -10979, /* emergency.aero */ -10989, /* engine.aero */ - 2676, /* engineer.aero */ -10996, /* entertainment.aero */ - 2725, /* equipment.aero */ - 2837, /* exchange.aero */ +10839, /* ballooning.aero */ + 1216, /* broker.aero */ +10850, /* caa.aero */ +10854, /* cargo.aero */ + 1528, /* catering.aero */ +10860, /* certification.aero */ +10874, /* championship.aero */ +10887, /* charter.aero */ +10895, /* civilaviation.aero */ + 1850, /* club.aero */ +10909, /* conference.aero */ +10920, /* consultant.aero */ + 1989, /* consulting.aero */ +10753, /* control.aero */ +10931, /* council.aero */ +10939, /* crew.aero */ + 2374, /* design.aero */ +10944, /* dgca.aero */ +10949, /* educator.aero */ +10958, /* emergency.aero */ +10968, /* engine.aero */ + 2675, /* engineer.aero */ +10975, /* entertainment.aero */ + 2724, /* equipment.aero */ + 2836, /* exchange.aero */ 369, /* express.aero */ -11010, /* federation.aero */ -11021, /* flight.aero */ -11028, /* freight.aero */ -11036, /* fuel.aero */ -11045, /* gliding.aero */ -11053, /* government.aero */ -11064, /* groundhandling.aero */ - 3753, /* group.aero */ -11041, /* hanggliding.aero */ -11079, /* homebuilt.aero */ - 4280, /* insurance.aero */ -11089, /* journal.aero */ -11097, /* journalist.aero */ -11108, /* leasing.aero */ - 4549, /* logistics.aero */ -11116, /* magazine.aero */ -11125, /* maintenance.aero */ - 5327, /* media.aero */ -11137, /* microlight.aero */ -11148, /* modelling.aero */ -11158, /* navigation.aero */ -11169, /* parachuting.aero */ -11181, /* paragliding.aero */ -10838, /* passenger-association.aero */ -11193, /* pilot.aero */ +10989, /* federation.aero */ +11000, /* flight.aero */ +11007, /* freight.aero */ +11015, /* fuel.aero */ +11024, /* gliding.aero */ +11032, /* government.aero */ +11043, /* groundhandling.aero */ + 3738, /* group.aero */ +11020, /* hanggliding.aero */ +11058, /* homebuilt.aero */ + 4255, /* insurance.aero */ +11068, /* journal.aero */ +11076, /* journalist.aero */ +11087, /* leasing.aero */ + 4528, /* logistics.aero */ +11095, /* magazine.aero */ +11104, /* maintenance.aero */ + 5306, /* media.aero */ +11116, /* microlight.aero */ +11127, /* modelling.aero */ +11137, /* navigation.aero */ +11148, /* parachuting.aero */ +11160, /* paragliding.aero */ +10817, /* passenger-association.aero */ +11172, /* pilot.aero */ 371, /* press.aero */ -11199, /* production.aero */ -11210, /* recreation.aero */ -11221, /* repbody.aero */ - 6301, /* res.aero */ - 1383, /* research.aero */ -11229, /* rotorcraft.aero */ - 6902, /* safety.aero */ -11240, /* scientist.aero */ - 7147, /* services.aero */ - 4111, /* show.aero */ -11250, /* skydiving.aero */ - 7371, /* software.aero */ -11265, /* student.aero */ -11273, /* trader.aero */ - 7988, /* trading.aero */ -11293, /* trainer.aero */ - 2118, /* union.aero */ -11301, /* workinggroup.aero */ - 8612, /* works.aero */ - 1913, /* com.af */ - 2624, /* edu.af */ - 3686, /* gov.af */ - 4185, /* net.af */ - 6070, /* org.af */ +11178, /* production.aero */ +11189, /* recreation.aero */ +11200, /* repbody.aero */ + 6284, /* res.aero */ + 1384, /* research.aero */ +11208, /* rotorcraft.aero */ + 6891, /* safety.aero */ +11219, /* scientist.aero */ + 7136, /* services.aero */ + 4092, /* show.aero */ +11229, /* skydiving.aero */ + 7360, /* software.aero */ +11244, /* student.aero */ +11252, /* trader.aero */ + 7986, /* trading.aero */ +11272, /* trainer.aero */ + 2119, /* union.aero */ +11280, /* workinggroup.aero */ + 8605, /* works.aero */ + 1914, /* com.af */ + 2623, /* edu.af */ + 3671, /* gov.af */ + 5737, /* net.af */ + 6053, /* org.af */ 113, /* co.ag */ - 1913, /* com.ag */ - 4185, /* net.ag */ - 5998, /* nom.ag */ - 6070, /* org.ag */ - 1913, /* com.ai */ - 4185, /* net.ai */ - 5951, /* off.ai */ - 6070, /* org.ai */ -10666, /* blogspot.al */ - 1913, /* com.al */ - 2624, /* edu.al */ - 3686, /* gov.al */ - 4195, /* mil.al */ - 4185, /* net.al */ - 6070, /* org.al */ -10666, /* blogspot.am */ + 1914, /* com.ag */ + 5737, /* net.ag */ + 5970, /* nom.ag */ + 6053, /* org.ag */ + 1914, /* com.ai */ + 5737, /* net.ai */ + 5923, /* off.ai */ + 6053, /* org.ai */ +10645, /* blogspot.al */ + 1914, /* com.al */ + 2623, /* edu.al */ + 3671, /* gov.al */ + 4170, /* mil.al */ + 5737, /* net.al */ + 6053, /* org.al */ +10645, /* blogspot.am */ 113, /* co.ao */ - 1859, /* ed.ao */ -11318, /* gv.ao */ - 2098, /* it.ao */ - 1036, /* og.ao */ -11322, /* pb.ao */ -11339, /* e164.arpa */ -11344, /* in-addr.arpa */ -11352, /* ip6.arpa */ - 4359, /* iris.arpa */ -11359, /* uri.arpa */ -11363, /* urn.arpa */ - 3686, /* gov.as */ -11367, /* cloudns.asia */ -11410, /* *.ex.ortsinfo.at */ - 2003, /* act.edu.au */ -11417, /* nsw.edu.au */ + 1860, /* ed.ao */ +11297, /* gv.ao */ + 2099, /* it.ao */ + 1037, /* og.ao */ +11301, /* pb.ao */ +11325, /* e164.arpa */ +11330, /* in-addr.arpa */ +11338, /* ip6.arpa */ + 4338, /* iris.arpa */ +11345, /* uri.arpa */ +11349, /* urn.arpa */ + 3671, /* gov.as */ +11353, /* cloudns.asia */ +11435, /* *.ex.ortsinfo.at */ + 2004, /* act.edu.au */ +11442, /* nsw.edu.au */ 97, /* nt.edu.au */ -11430, /* qld.edu.au */ - 1493, /* sa.edu.au */ +11455, /* qld.edu.au */ + 1494, /* sa.edu.au */ 536, /* tas.edu.au */ -11435, /* vic.edu.au */ - 5971, /* wa.edu.au */ -11430, /* qld.gov.au */ - 1493, /* sa.gov.au */ +11460, /* vic.edu.au */ + 5943, /* wa.edu.au */ +11455, /* qld.gov.au */ + 1494, /* sa.gov.au */ 536, /* tas.gov.au */ -11435, /* vic.gov.au */ - 5971, /* wa.gov.au */ - 1913, /* com.aw */ - 985, /* biz.az */ - 1913, /* com.az */ - 2624, /* edu.az */ - 3686, /* gov.az */ - 3167, /* info.az */ - 3632, /* int.az */ - 4195, /* mil.az */ - 5725, /* name.az */ - 4185, /* net.az */ - 6070, /* org.az */ +11460, /* vic.gov.au */ + 5943, /* wa.gov.au */ + 1914, /* com.aw */ + 986, /* biz.az */ + 1914, /* com.az */ + 2623, /* edu.az */ + 3671, /* gov.az */ + 3152, /* info.az */ + 3617, /* int.az */ + 4170, /* mil.az */ + 5695, /* name.az */ + 5737, /* net.az */ + 6053, /* org.az */ 469, /* pp.az */ - 6427, /* pro.az */ - 985, /* biz.bb */ + 6410, /* pro.az */ + 986, /* biz.bb */ 113, /* co.bb */ - 1913, /* com.bb */ - 2624, /* edu.bb */ - 3686, /* gov.bb */ - 3167, /* info.bb */ - 4185, /* net.bb */ - 6070, /* org.bb */ - 7514, /* store.bb */ - 2546, /* tv.bb */ -11462, /* 0.bg */ -11467, /* 1.bg */ -11471, /* 2.bg */ -11474, /* 3.bg */ -11342, /* 4.bg */ -11479, /* 5.bg */ -11354, /* 6.bg */ -11485, /* 7.bg */ -11487, /* 8.bg */ -11489, /* 9.bg */ + 1914, /* com.bb */ + 2623, /* edu.bb */ + 3671, /* gov.bb */ + 3152, /* info.bb */ + 5737, /* net.bb */ + 6053, /* org.bb */ + 7512, /* store.bb */ + 2549, /* tv.bb */ +11487, /* 0.bg */ +11492, /* 1.bg */ +11496, /* 2.bg */ +11499, /* 3.bg */ +11328, /* 4.bg */ +11504, /* 5.bg */ +11340, /* 6.bg */ +11509, /* 7.bg */ +11511, /* 8.bg */ +11513, /* 9.bg */ 2, /* a.bg */ 18, /* b.bg */ -10666, /* blogspot.bg */ +11518, /* barsy.bg */ +10645, /* blogspot.bg */ 36, /* c.bg */ 142, /* d.bg */ 32, /* e.bg */ @@ -4653,7 +4838,7 @@ static const REGISTRY_U16 kLeafNodeTable[] = { 162, /* g.bg */ 14, /* h.bg */ 58, /* i.bg */ - 990, /* j.bg */ + 991, /* j.bg */ 734, /* k.bg */ 211, /* l.bg */ 354, /* m.bg */ @@ -4665,4015 +4850,4014 @@ static const REGISTRY_U16 kLeafNodeTable[] = { 110, /* s.bg */ 25, /* t.bg */ 585, /* u.bg */ - 1290, /* v.bg */ + 1291, /* v.bg */ 650, /* w.bg */ 398, /* x.bg */ 71, /* y.bg */ 326, /* z.bg */ 113, /* co.bi */ - 1913, /* com.bi */ - 2624, /* edu.bi */ + 1914, /* com.bi */ + 2623, /* edu.bi */ 137, /* or.bi */ - 6070, /* org.bi */ -11367, /* cloudns.biz */ -11518, /* dscloud.biz */ -11526, /* dyndns.biz */ -11533, /* for-better.biz */ -11549, /* for-more.biz */ -11558, /* for-some.biz */ -11567, /* for-the.biz */ -11575, /* mmafan.biz */ -11582, /* myftp.biz */ -11588, /* no-ip.biz */ -11594, /* selfip.biz */ -11601, /* webhop.biz */ -11614, /* asso.bj */ -11619, /* barreau.bj */ -10666, /* blogspot.bj */ -11627, /* gouv.bj */ - 1913, /* com.bo */ - 2624, /* edu.bo */ -11325, /* gob.bo */ - 3686, /* gov.bo */ - 3632, /* int.bo */ - 4195, /* mil.bo */ - 4185, /* net.bo */ - 6070, /* org.bo */ - 2546, /* tv.bo */ - 62, /* ac.leg.br */ - 290, /* al.leg.br */ - 358, /* am.leg.br */ - 1662, /* ap.leg.br */ - 308, /* ba.leg.br */ - 273, /* ce.leg.br */ -11728, /* df.leg.br */ - 558, /* es.leg.br */ - 257, /* go.leg.br */ - 5151, /* ma.leg.br */ - 4670, /* mg.leg.br */ - 1059, /* ms.leg.br */ - 5609, /* mt.leg.br */ - 522, /* pa.leg.br */ -11322, /* pb.leg.br */ - 3739, /* pe.leg.br */ -11737, /* pi.leg.br */ - 6402, /* pr.leg.br */ -11740, /* rj.leg.br */ - 821, /* rn.leg.br */ - 166, /* ro.leg.br */ -11743, /* rr.leg.br */ - 1272, /* rs.leg.br */ - 2158, /* sc.leg.br */ - 1498, /* se.leg.br */ -11650, /* sp.leg.br */ - 631, /* to.leg.br */ + 6053, /* org.bi */ +11353, /* cloudns.biz */ +11551, /* dscloud.biz */ +11559, /* dyndns.biz */ +11566, /* for-better.biz */ +11582, /* for-more.biz */ +11591, /* for-some.biz */ +11600, /* for-the.biz */ +11608, /* mmafan.biz */ +11615, /* myftp.biz */ +11621, /* no-ip.biz */ +11627, /* selfip.biz */ +11634, /* webhop.biz */ +11647, /* asso.bj */ +11652, /* barreau.bj */ +10645, /* blogspot.bj */ +11660, /* gouv.bj */ + 1914, /* com.bo */ + 2623, /* edu.bo */ +11304, /* gob.bo */ + 3671, /* gov.bo */ + 3617, /* int.bo */ + 4170, /* mil.bo */ + 5737, /* net.bo */ + 6053, /* org.bo */ + 2549, /* tv.bo */ + 62, /* ac.gov.br */ + 290, /* al.gov.br */ + 358, /* am.gov.br */ + 1663, /* ap.gov.br */ + 308, /* ba.gov.br */ + 273, /* ce.gov.br */ +11820, /* df.gov.br */ + 558, /* es.gov.br */ + 257, /* go.gov.br */ + 5130, /* ma.gov.br */ + 4649, /* mg.gov.br */ + 1060, /* ms.gov.br */ + 5588, /* mt.gov.br */ + 522, /* pa.gov.br */ +11301, /* pb.gov.br */ + 3724, /* pe.gov.br */ +11829, /* pi.gov.br */ + 6385, /* pr.gov.br */ +11835, /* rj.gov.br */ + 821, /* rn.gov.br */ + 166, /* ro.gov.br */ +11542, /* rr.gov.br */ + 1273, /* rs.gov.br */ + 2159, /* sc.gov.br */ + 1499, /* se.gov.br */ +11697, /* sp.gov.br */ + 631, /* to.gov.br */ 113, /* co.bw */ - 6070, /* org.bw */ - 1913, /* com.bz */ - 2624, /* edu.bz */ - 3686, /* gov.bz */ - 4185, /* net.bz */ - 6070, /* org.bz */ - 6329, /* za.bz */ - 499, /* ab.ca */ - 35, /* bc.ca */ -10666, /* blogspot.ca */ - 113, /* co.ca */ -11746, /* gc.ca */ -11673, /* mb.ca */ -11751, /* nb.ca */ - 5825, /* nf.ca */ - 1071, /* nl.ca */ -11588, /* no-ip.ca */ - 786, /* ns.ca */ - 97, /* nt.ca */ - 5372, /* nu.ca */ - 592, /* on.ca */ - 3739, /* pe.ca */ -11494, /* qc.ca */ - 7312, /* sk.ca */ -11503, /* yk.ca */ -11367, /* cloudns.cc */ -11763, /* fantasyleague.cc */ -11777, /* ftpaccess.cc */ -11787, /* game-server.cc */ - 6256, /* myphotos.cc */ -11799, /* scrapping.cc */ -10666, /* blogspot.ch */ -11809, /* gotdns.ch */ + 6053, /* org.bw */ + 1914, /* com.bz */ + 2623, /* edu.bz */ + 3671, /* gov.bz */ + 5737, /* net.bz */ + 6053, /* org.bz */ + 6312, /* za.bz */ +11353, /* cloudns.cc */ +11855, /* fantasyleague.cc */ +11869, /* ftpaccess.cc */ +11879, /* game-server.cc */ + 6239, /* myphotos.cc */ +11891, /* scrapping.cc */ +11901, /* twmail.cc */ +11361, /* 12hp.ch */ +11366, /* 2ix.ch */ +11370, /* 4lima.ch */ +10645, /* blogspot.ch */ +11908, /* gotdns.ch */ +11404, /* lima-city.ch */ +11915, /* square7.ch */ 62, /* ac.ci */ -11614, /* asso.ci */ +11647, /* asso.ci */ 113, /* co.ci */ - 1913, /* com.ci */ - 1859, /* ed.ci */ - 2624, /* edu.ci */ + 1914, /* com.ci */ + 1860, /* ed.ci */ + 2623, /* edu.ci */ 257, /* go.ci */ -11627, /* gouv.ci */ - 3632, /* int.ci */ - 5320, /* md.ci */ - 4185, /* net.ci */ +11660, /* gouv.ci */ + 3617, /* int.ci */ + 5299, /* md.ci */ + 5737, /* net.ci */ 137, /* or.ci */ - 6070, /* org.ci */ -11816, /* presse.ci */ -11823, /* xn--aroport-bya.ci */ -11839, /* !www.ck */ -11410, /* *.ck */ -10666, /* blogspot.cl */ + 6053, /* org.ci */ +11923, /* presse.ci */ +11930, /* xn--aroport-bya.ci */ +11946, /* !www.ck */ +11435, /* *.ck */ +10645, /* blogspot.cl */ 113, /* co.cl */ -11325, /* gob.cl */ - 3686, /* gov.cl */ - 4195, /* mil.cl */ +11304, /* gob.cl */ + 3671, /* gov.cl */ + 4170, /* mil.cl */ 113, /* co.cm */ - 1913, /* com.cm */ - 3686, /* gov.cm */ - 4185, /* net.cm */ - 7668, /* elasticbeanstalk.cn-north-1.amazonaws.com.cn */ -11473, /* s3.cn-north-1.amazonaws.com.cn */ -11473, /* s3.dualstack.ap-northeast-1.amazonaws.com */ -14743, /* alpha.bounty-full.com */ -14749, /* beta.bounty-full.com */ -11464, /* eu-1.evennode.com */ -14754, /* eu-2.evennode.com */ -14759, /* us-1.evennode.com */ -14764, /* us-2.evennode.com */ -14769, /* apps.fbsbx.com */ - 2798, /* eu.meteorapp.com */ -14778, /* xen.prgmr.com */ + 1914, /* com.cm */ + 3671, /* gov.cm */ + 5737, /* net.cm */ + 7666, /* elasticbeanstalk.cn-north-1.amazonaws.com.cn */ +11498, /* s3.cn-north-1.amazonaws.com.cn */ +11498, /* s3.dualstack.ap-northeast-1.amazonaws.com */ +14958, /* alpha.bounty-full.com */ +14964, /* beta.bounty-full.com */ +11489, /* eu-1.evennode.com */ +14969, /* eu-2.evennode.com */ +14974, /* eu-3.evennode.com */ +14979, /* us-1.evennode.com */ +14984, /* us-2.evennode.com */ +14989, /* us-3.evennode.com */ +14994, /* apps.fbsbx.com */ + 2797, /* eu.meteorapp.com */ +15003, /* xen.prgmr.com */ + 2277, /* de.cool */ 62, /* ac.cr */ 113, /* co.cr */ - 1859, /* ed.cr */ - 3009, /* fi.cr */ + 1860, /* ed.cr */ + 2994, /* fi.cr */ 257, /* go.cr */ 137, /* or.cr */ - 1493, /* sa.cr */ - 1913, /* com.cu */ - 2624, /* edu.cu */ - 3686, /* gov.cu */ - 5824, /* inf.cu */ - 4185, /* net.cu */ - 6070, /* org.cu */ - 1913, /* com.cw */ - 2624, /* edu.cw */ - 4185, /* net.cw */ - 6070, /* org.cw */ - 7802, /* ath.cx */ - 3686, /* gov.cx */ -10666, /* blogspot.cz */ - 113, /* co.cz */ -11476, /* e4.cz */ -14801, /* realm.cz */ -15154, /* dyn.cosidns.de */ -15154, /* dyn.ddnss.de */ -11526, /* dyndns.ddnss.de */ - 985, /* biz.dk */ -10666, /* blogspot.dk */ + 1494, /* sa.cr */ + 1914, /* com.cu */ + 2623, /* edu.cu */ + 3671, /* gov.cu */ + 5796, /* inf.cu */ + 5737, /* net.cu */ + 6053, /* org.cu */ + 1914, /* com.cw */ + 2623, /* edu.cw */ + 5737, /* net.cw */ + 6053, /* org.cw */ + 7800, /* ath.cx */ + 3671, /* gov.cx */ + 3152, /* info.cx */ + 1840, /* cloud.metacentrum.cz */ +15044, /* custom.metacentrum.cz */ +15431, /* dyn.cosidns.de */ +15431, /* dyn.ddnss.de */ +11559, /* dyndns.ddnss.de */ + 986, /* biz.dk */ +10645, /* blogspot.dk */ 113, /* co.dk */ -11959, /* firm.dk */ -15158, /* reg.dk */ - 7514, /* store.dk */ +12095, /* firm.dk */ +15435, /* reg.dk */ + 7512, /* store.dk */ 527, /* art.do */ - 1913, /* com.do */ - 2624, /* edu.do */ -11325, /* gob.do */ - 3686, /* gov.do */ - 4195, /* mil.do */ - 4185, /* net.do */ - 6070, /* org.do */ -15162, /* sld.do */ -11967, /* web.do */ + 1914, /* com.do */ + 2623, /* edu.do */ +11304, /* gob.do */ + 3671, /* gov.do */ + 4170, /* mil.do */ + 5737, /* net.do */ + 6053, /* org.do */ +15439, /* sld.do */ +12109, /* web.do */ 527, /* art.dz */ -11614, /* asso.dz */ - 1913, /* com.dz */ - 2624, /* edu.dz */ - 3686, /* gov.dz */ - 4185, /* net.dz */ - 6070, /* org.dz */ -15170, /* pol.dz */ - 1913, /* com.ec */ - 2624, /* edu.ec */ - 4231, /* fin.ec */ -11325, /* gob.ec */ - 3686, /* gov.ec */ - 3167, /* info.ec */ -15174, /* k12.ec */ - 1858, /* med.ec */ - 4195, /* mil.ec */ - 4185, /* net.ec */ - 6070, /* org.ec */ - 6427, /* pro.ec */ - 985, /* biz.et */ - 1913, /* com.et */ - 2624, /* edu.et */ - 3686, /* gov.et */ - 3167, /* info.et */ - 5725, /* name.et */ - 4185, /* net.et */ - 6070, /* org.et */ -15243, /* user.party.eus */ -15248, /* ybo.faith */ -15256, /* aland.fi */ -10666, /* blogspot.fi */ - 3618, /* dy.fi */ - 8548, /* iki.fi */ - 6363, /* ptplus.fit */ -15272, /* aeroport.fr */ -15281, /* assedic.fr */ -11614, /* asso.fr */ - 1520, /* avocat.fr */ -15289, /* avoues.fr */ -10666, /* blogspot.fr */ - 3785, /* cci.fr */ -15296, /* chambagri.fr */ -15306, /* chirurgiens-dentistes.fr */ -15328, /* chirurgiens-dentistes-en-france.fr */ - 1913, /* com.fr */ -15360, /* experts-comptables.fr */ -15379, /* fbx-os.fr */ -15386, /* fbxos.fr */ -12546, /* freebox-os.fr */ -12557, /* freeboxos.fr */ - 2846, /* geometre-expert.fr */ -11627, /* gouv.fr */ -15392, /* greta.fr */ -15398, /* huissier-justice.fr */ -15415, /* medecin.fr */ - 5998, /* nom.fr */ -15423, /* notaires.fr */ -11964, /* on-web.fr */ -15432, /* pharmacien.fr */ - 6713, /* port.fr */ -15443, /* prd.fr */ -11816, /* presse.fr */ - 7910, /* tm.fr */ -15447, /* veterinaire.fr */ - 1913, /* com.ge */ - 2624, /* edu.ge */ - 3686, /* gov.ge */ - 4195, /* mil.ge */ - 4185, /* net.ge */ - 6070, /* org.ge */ -15459, /* pvt.ge */ +11647, /* asso.dz */ + 1914, /* com.dz */ + 2623, /* edu.dz */ + 3671, /* gov.dz */ + 5737, /* net.dz */ + 6053, /* org.dz */ +15447, /* pol.dz */ + 1914, /* com.ec */ + 2623, /* edu.ec */ + 4206, /* fin.ec */ +11304, /* gob.ec */ + 3671, /* gov.ec */ + 3152, /* info.ec */ +15451, /* k12.ec */ + 1859, /* med.ec */ + 4170, /* mil.ec */ + 5737, /* net.ec */ + 6053, /* org.ec */ + 6410, /* pro.ec */ + 986, /* biz.et */ + 1914, /* com.et */ + 2623, /* edu.et */ + 3671, /* gov.et */ + 3152, /* info.et */ + 5695, /* name.et */ + 5737, /* net.et */ + 6053, /* org.et */ +15520, /* user.party.eus */ +15525, /* ybo.faith */ +11832, /* storj.farm */ +15533, /* aland.fi */ +10645, /* blogspot.fi */ + 3603, /* dy.fi */ + 8541, /* iki.fi */ + 6346, /* ptplus.fit */ +15549, /* aeroport.fr */ +15558, /* assedic.fr */ +11647, /* asso.fr */ + 1521, /* avocat.fr */ +15566, /* avoues.fr */ +10645, /* blogspot.fr */ + 3767, /* cci.fr */ +15573, /* chambagri.fr */ +15583, /* chirurgiens-dentistes.fr */ +15605, /* chirurgiens-dentistes-en-france.fr */ + 1914, /* com.fr */ +15637, /* experts-comptables.fr */ +15656, /* fbx-os.fr */ +15663, /* fbxos.fr */ +12705, /* freebox-os.fr */ +12716, /* freeboxos.fr */ + 2845, /* geometre-expert.fr */ +11660, /* gouv.fr */ +15669, /* greta.fr */ +15675, /* huissier-justice.fr */ +15692, /* medecin.fr */ + 5970, /* nom.fr */ +15700, /* notaires.fr */ +12106, /* on-web.fr */ +15709, /* pharmacien.fr */ + 6696, /* port.fr */ +15720, /* prd.fr */ +11923, /* presse.fr */ + 7908, /* tm.fr */ +15724, /* veterinaire.fr */ + 1914, /* com.ge */ + 2623, /* edu.ge */ + 3671, /* gov.ge */ + 4170, /* mil.ge */ + 5737, /* net.ge */ + 6053, /* org.ge */ +15736, /* pvt.ge */ 113, /* co.gg */ - 4185, /* net.gg */ - 6070, /* org.gg */ - 1913, /* com.gh */ - 2624, /* edu.gh */ - 3686, /* gov.gh */ - 4195, /* mil.gh */ - 6070, /* org.gh */ - 1913, /* com.gi */ - 2624, /* edu.gi */ - 3686, /* gov.gi */ - 5103, /* ltd.gi */ -15463, /* mod.gi */ - 6070, /* org.gi */ +15740, /* cya.gg */ + 5737, /* net.gg */ + 6053, /* org.gg */ + 1914, /* com.gh */ + 2623, /* edu.gh */ + 3671, /* gov.gh */ + 4170, /* mil.gh */ + 6053, /* org.gh */ + 1914, /* com.gi */ + 2623, /* edu.gi */ + 3671, /* gov.gi */ + 5082, /* ltd.gi */ +15744, /* mod.gi */ + 6053, /* org.gi */ 113, /* co.gl */ - 1913, /* com.gl */ - 2624, /* edu.gl */ - 4185, /* net.gl */ - 6070, /* org.gl */ + 1914, /* com.gl */ + 2623, /* edu.gl */ + 5737, /* net.gl */ + 6053, /* org.gl */ 62, /* ac.gn */ - 1913, /* com.gn */ - 2624, /* edu.gn */ - 3686, /* gov.gn */ - 4185, /* net.gn */ - 6070, /* org.gn */ -11614, /* asso.gp */ - 1913, /* com.gp */ - 2624, /* edu.gp */ - 5448, /* mobi.gp */ - 4185, /* net.gp */ - 6070, /* org.gp */ -10666, /* blogspot.gr */ - 1913, /* com.gr */ - 2624, /* edu.gr */ - 3686, /* gov.gr */ - 4185, /* net.gr */ - 6070, /* org.gr */ - 1913, /* com.gt */ - 2624, /* edu.gt */ -11325, /* gob.gt */ -11676, /* ind.gt */ - 4195, /* mil.gt */ - 4185, /* net.gt */ - 6070, /* org.gt */ + 1914, /* com.gn */ + 2623, /* edu.gn */ + 3671, /* gov.gn */ + 5737, /* net.gn */ + 6053, /* org.gn */ + 1840, /* cloud.goog */ +11647, /* asso.gp */ + 1914, /* com.gp */ + 2623, /* edu.gp */ + 5427, /* mobi.gp */ + 5737, /* net.gp */ + 6053, /* org.gp */ +10645, /* blogspot.gr */ + 1914, /* com.gr */ + 2623, /* edu.gr */ + 3671, /* gov.gr */ + 5737, /* net.gr */ + 6053, /* org.gr */ + 1914, /* com.gt */ + 2623, /* edu.gt */ +11304, /* gob.gt */ +11731, /* ind.gt */ + 4170, /* mil.gt */ + 5737, /* net.gt */ + 6053, /* org.gt */ 113, /* co.gy */ - 1913, /* com.gy */ - 2624, /* edu.gy */ - 3686, /* gov.gy */ - 4185, /* net.gy */ - 6070, /* org.gy */ -10666, /* blogspot.hk */ - 1913, /* com.hk */ - 2624, /* edu.hk */ - 3686, /* gov.hk */ -15467, /* idv.hk */ -15471, /* inc.hk */ - 5103, /* ltd.hk */ - 4185, /* net.hk */ - 6070, /* org.hk */ - 8851, /* xn--55qx5d.hk */ -15475, /* xn--ciqpn.hk */ -15485, /* xn--gmq050i.hk */ -15497, /* xn--gmqw5a.hk */ - 9459, /* xn--io0a7i.hk */ -15508, /* xn--lcvr32d.hk */ -15520, /* xn--mk0axi.hk */ -10011, /* xn--mxtq1m.hk */ -11913, /* xn--od0alg.hk */ -15531, /* xn--od0aq3b.hk */ -15543, /* xn--tn0ag.hk */ -15553, /* xn--uc0atv.hk */ -15564, /* xn--uc0ay4a.hk */ -15576, /* xn--wcvs22d.hk */ -15588, /* xn--zf0avx.hk */ - 1913, /* com.hn */ - 2624, /* edu.hn */ -11325, /* gob.hn */ - 4195, /* mil.hn */ - 4185, /* net.hn */ - 6070, /* org.hn */ -15599, /* opencraft.hosting */ -10666, /* blogspot.hr */ - 1913, /* com.hr */ -15609, /* from.hr */ - 986, /* iz.hr */ - 5725, /* name.hr */ + 1914, /* com.gy */ + 2623, /* edu.gy */ + 3671, /* gov.gy */ + 5737, /* net.gy */ + 6053, /* org.gy */ +10645, /* blogspot.hk */ + 1914, /* com.hk */ + 2623, /* edu.hk */ + 3671, /* gov.hk */ +15748, /* idv.hk */ +15752, /* inc.hk */ + 5082, /* ltd.hk */ + 5737, /* net.hk */ + 6053, /* org.hk */ + 8830, /* xn--55qx5d.hk */ +15756, /* xn--ciqpn.hk */ +15766, /* xn--gmq050i.hk */ +15778, /* xn--gmqw5a.hk */ + 9438, /* xn--io0a7i.hk */ +15789, /* xn--lcvr32d.hk */ +15801, /* xn--mk0axi.hk */ + 9990, /* xn--mxtq1m.hk */ +12049, /* xn--od0alg.hk */ +15812, /* xn--od0aq3b.hk */ +15824, /* xn--tn0ag.hk */ +15834, /* xn--uc0atv.hk */ +15845, /* xn--uc0ay4a.hk */ +15857, /* xn--wcvs22d.hk */ +15869, /* xn--zf0avx.hk */ + 1914, /* com.hn */ + 2623, /* edu.hn */ +11304, /* gob.hn */ + 4170, /* mil.hn */ + 5737, /* net.hn */ + 6053, /* org.hn */ +15880, /* cloudaccess.host */ + 7288, /* freesite.host */ +15892, /* opencraft.hosting */ +10645, /* blogspot.hr */ + 1914, /* com.hr */ +15902, /* from.hr */ + 987, /* iz.hr */ + 5695, /* name.hr */ 148, /* adult.ht */ 527, /* art.ht */ -11614, /* asso.ht */ - 1913, /* com.ht */ - 2047, /* coop.ht */ - 2624, /* edu.ht */ -11959, /* firm.ht */ -11627, /* gouv.ht */ - 3167, /* info.ht */ - 1858, /* med.ht */ - 4185, /* net.ht */ - 6070, /* org.ht */ -15614, /* perso.ht */ -15170, /* pol.ht */ - 6427, /* pro.ht */ -15620, /* rel.ht */ - 7245, /* shop.ht */ -11459, /* 2000.hu */ -15624, /* agrar.hu */ -10666, /* blogspot.hu */ -15630, /* bolt.hu */ - 1513, /* casino.hu */ - 1765, /* city.hu */ +11647, /* asso.ht */ + 1914, /* com.ht */ + 2048, /* coop.ht */ + 2623, /* edu.ht */ +12095, /* firm.ht */ +11660, /* gouv.ht */ + 3152, /* info.ht */ + 1859, /* med.ht */ + 5737, /* net.ht */ + 6053, /* org.ht */ +15907, /* perso.ht */ +15447, /* pol.ht */ + 6410, /* pro.ht */ +15913, /* rel.ht */ + 7234, /* shop.ht */ +11484, /* 2000.hu */ +15917, /* agrar.hu */ +10645, /* blogspot.hu */ +15923, /* bolt.hu */ + 1514, /* casino.hu */ + 1766, /* city.hu */ 113, /* co.hu */ -15635, /* erotica.hu */ -15643, /* erotika.hu */ - 3031, /* film.hu */ - 3223, /* forum.hu */ - 3405, /* games.hu */ - 7749, /* hotel.hu */ - 3167, /* info.hu */ -15651, /* ingatlan.hu */ -15660, /* jogasz.hu */ -15667, /* konyvelo.hu */ -15676, /* lakas.hu */ - 5327, /* media.hu */ - 5808, /* news.hu */ - 6070, /* org.hu */ -11393, /* priv.hu */ -15682, /* reklam.hu */ - 7168, /* sex.hu */ - 7245, /* shop.hu */ -15693, /* sport.hu */ - 4911, /* suli.hu */ -11398, /* szex.hu */ - 7910, /* tm.hu */ -15699, /* tozsde.hu */ -15706, /* utazas.hu */ - 8247, /* video.hu */ -10666, /* blogspot.ie */ - 3686, /* gov.ie */ - 5103, /* ltd.co.im */ - 4860, /* plc.co.im */ +15928, /* erotica.hu */ +15936, /* erotika.hu */ + 3016, /* film.hu */ + 3208, /* forum.hu */ + 3390, /* games.hu */ + 7747, /* hotel.hu */ + 3152, /* info.hu */ +15944, /* ingatlan.hu */ +15953, /* jogasz.hu */ +15960, /* konyvelo.hu */ +15969, /* lakas.hu */ + 5306, /* media.hu */ + 5780, /* news.hu */ + 6053, /* org.hu */ +11418, /* priv.hu */ +15975, /* reklam.hu */ + 7157, /* sex.hu */ + 7234, /* shop.hu */ +15986, /* sport.hu */ + 4890, /* suli.hu */ +11423, /* szex.hu */ + 7908, /* tm.hu */ +15992, /* tozsde.hu */ +15999, /* utazas.hu */ + 8240, /* video.hu */ +10645, /* blogspot.ie */ + 3671, /* gov.ie */ + 5082, /* ltd.co.im */ + 4839, /* plc.co.im */ 62, /* ac.in */ -10666, /* blogspot.in */ -11367, /* cloudns.in */ +11518, /* barsy.in */ +10645, /* blogspot.in */ +11353, /* cloudns.in */ 113, /* co.in */ - 2624, /* edu.in */ -11959, /* firm.in */ - 8368, /* gen.in */ - 3686, /* gov.in */ -11676, /* ind.in */ - 4195, /* mil.in */ - 4185, /* net.in */ - 1815, /* nic.in */ - 6070, /* org.in */ - 6301, /* res.in */ -15731, /* barrel-of-knowledge.info */ -15751, /* barrell-of-knowledge.info */ -11367, /* cloudns.info */ -15772, /* dvrcam.info */ -15779, /* dynamic-dns.info */ -11526, /* dyndns.info */ -15791, /* for-our.info */ -15799, /* groks-the.info */ -15809, /* groks-this.info */ -11544, /* here-for-more.info */ - 1889, /* ilovecollege.info */ -15820, /* knowsitall.info */ -11588, /* no-ip.info */ -15831, /* nsupdate.info */ -11594, /* selfip.info */ -11601, /* webhop.info */ -15992, /* customer.enonic.io */ + 2623, /* edu.in */ +12095, /* firm.in */ + 8361, /* gen.in */ + 3671, /* gov.in */ +11731, /* ind.in */ + 4170, /* mil.in */ + 5737, /* net.in */ + 1816, /* nic.in */ + 6053, /* org.in */ + 6284, /* res.in */ +16024, /* barrel-of-knowledge.info */ +16044, /* barrell-of-knowledge.info */ +11353, /* cloudns.info */ +16065, /* dvrcam.info */ +16072, /* dynamic-dns.info */ +11559, /* dyndns.info */ +16084, /* for-our.info */ +16092, /* groks-the.info */ +16102, /* groks-this.info */ +11577, /* here-for-more.info */ + 1890, /* ilovecollege.info */ +16113, /* knowsitall.info */ +11621, /* no-ip.info */ +16124, /* nsupdate.info */ +11627, /* selfip.info */ +11634, /* webhop.info */ +16324, /* customer.enonic.io */ +16341, /* cust.dev.thingdust.io */ 62, /* ac.ir */ 113, /* co.ir */ - 3686, /* gov.ir */ + 3671, /* gov.ir */ 437, /* id.ir */ - 4185, /* net.ir */ - 6070, /* org.ir */ - 1145, /* sch.ir */ - 9626, /* xn--mgba3a4f16a.ir */ - 9642, /* xn--mgba3a4fra.ir */ -10666, /* blogspot.is */ - 1913, /* com.is */ -16001, /* cupcake.is */ - 2624, /* edu.is */ - 3686, /* gov.is */ - 3632, /* int.is */ - 4185, /* net.is */ - 6070, /* org.is */ - 1181, /* abr.it */ -16009, /* abruzzo.it */ + 5737, /* net.ir */ + 6053, /* org.ir */ + 1146, /* sch.ir */ + 9605, /* xn--mgba3a4f16a.ir */ + 9621, /* xn--mgba3a4fra.ir */ +10645, /* blogspot.is */ + 1914, /* com.is */ +16346, /* cupcake.is */ + 2623, /* edu.is */ + 3671, /* gov.is */ + 3617, /* int.is */ + 5737, /* net.is */ + 6053, /* org.is */ + 1182, /* abr.it */ +16354, /* abruzzo.it */ 226, /* ag.it */ -16017, /* agrigento.it */ +16362, /* agrigento.it */ 290, /* al.it */ -16027, /* alessandria.it */ -16047, /* alto-adige.it */ -16066, /* altoadige.it */ +16372, /* alessandria.it */ +16392, /* alto-adige.it */ +16411, /* altoadige.it */ 234, /* an.it */ -16081, /* ancona.it */ -16088, /* andria-barletta-trani.it */ -16110, /* andria-trani-barletta.it */ -16132, /* andriabarlettatrani.it */ -16152, /* andriatranibarletta.it */ +16426, /* ancona.it */ +16433, /* andria-barletta-trani.it */ +16455, /* andria-trani-barletta.it */ +16477, /* andriabarlettatrani.it */ +16497, /* andriatranibarletta.it */ 448, /* ao.it */ -16176, /* aosta.it */ -16182, /* aosta-valley.it */ -16195, /* aostavalley.it */ -16213, /* aoste.it */ - 1662, /* ap.it */ +16521, /* aosta.it */ +16527, /* aosta-valley.it */ +16540, /* aostavalley.it */ +16558, /* aoste.it */ + 1663, /* ap.it */ 480, /* aq.it */ -16220, /* aquila.it */ +16565, /* aquila.it */ 494, /* ar.it */ -16227, /* arezzo.it */ -16234, /* ascoli-piceno.it */ -16248, /* ascolipiceno.it */ -16261, /* asti.it */ +16572, /* arezzo.it */ +16579, /* ascoli-piceno.it */ +16593, /* ascolipiceno.it */ +16606, /* asti.it */ 562, /* at.it */ -16266, /* av.it */ -16269, /* avellino.it */ +16611, /* av.it */ +16614, /* avellino.it */ 308, /* ba.it */ -16278, /* balsan.it */ -16287, /* bari.it */ -16292, /* barletta-trani-andria.it */ -16314, /* barlettatraniandria.it */ - 1081, /* bas.it */ -16334, /* basilicata.it */ -16345, /* belluno.it */ -16353, /* benevento.it */ -16363, /* bergamo.it */ - 931, /* bg.it */ +16623, /* balsan.it */ +16632, /* bari.it */ +16637, /* barletta-trani-andria.it */ +16659, /* barlettatraniandria.it */ + 1082, /* bas.it */ +16679, /* basilicata.it */ +16690, /* belluno.it */ +16698, /* benevento.it */ +16708, /* bergamo.it */ + 932, /* bg.it */ 57, /* bi.it */ -16371, /* biella.it */ -16380, /* bl.it */ -10666, /* blogspot.it */ - 1067, /* bn.it */ - 1086, /* bo.it */ -16383, /* bologna.it */ -16391, /* bolzano.it */ -16399, /* bozen.it */ - 1182, /* br.it */ -16405, /* brescia.it */ -16413, /* brindisi.it */ - 1240, /* bs.it */ +16716, /* biella.it */ +16725, /* bl.it */ +10645, /* blogspot.it */ + 1068, /* bn.it */ + 1087, /* bo.it */ +16728, /* bologna.it */ +16736, /* bolzano.it */ +16744, /* bozen.it */ + 1183, /* br.it */ +16750, /* brescia.it */ +16758, /* brindisi.it */ + 1241, /* bs.it */ 829, /* bt.it */ - 1295, /* bz.it */ + 1296, /* bz.it */ 221, /* ca.it */ -16422, /* cagliari.it */ - 1319, /* cal.it */ -16437, /* calabria.it */ -16446, /* caltanissetta.it */ - 1343, /* cam.it */ -16460, /* campania.it */ -16469, /* campidano-medio.it */ -16485, /* campidanomedio.it */ -11608, /* campobasso.it */ -16500, /* carbonia-iglesias.it */ -16518, /* carboniaiglesias.it */ -16535, /* carrara-massa.it */ -16549, /* carraramassa.it */ -16562, /* caserta.it */ -16570, /* catania.it */ -16578, /* catanzaro.it */ - 4415, /* cb.it */ +16767, /* cagliari.it */ + 1320, /* cal.it */ +16782, /* calabria.it */ +16791, /* caltanissetta.it */ + 1344, /* cam.it */ +16805, /* campania.it */ +16814, /* campidano-medio.it */ +16830, /* campidanomedio.it */ +11641, /* campobasso.it */ +16845, /* carbonia-iglesias.it */ +16863, /* carboniaiglesias.it */ +16880, /* carrara-massa.it */ +16894, /* carraramassa.it */ +16907, /* caserta.it */ +16915, /* catania.it */ +16923, /* catanzaro.it */ + 4394, /* cb.it */ 273, /* ce.it */ -16588, /* cesena-forli.it */ -16601, /* cesenaforli.it */ - 1146, /* ch.it */ -16613, /* chieti.it */ - 1713, /* ci.it */ - 1787, /* cl.it */ +16933, /* cesena-forli.it */ +16946, /* cesenaforli.it */ + 1147, /* ch.it */ +16958, /* chieti.it */ + 1714, /* ci.it */ + 1788, /* cl.it */ 842, /* cn.it */ 113, /* co.it */ -16620, /* como.it */ -16625, /* cosenza.it */ - 2091, /* cr.it */ -16633, /* cremona.it */ -16641, /* crotone.it */ +16965, /* como.it */ +16970, /* cosenza.it */ + 2092, /* cr.it */ +16978, /* cremona.it */ +16986, /* crotone.it */ 429, /* cs.it */ - 2004, /* ct.it */ -16654, /* cuneo.it */ - 2202, /* cz.it */ -16660, /* dell-ogliastra.it */ -16675, /* dellogliastra.it */ - 2624, /* edu.it */ -16689, /* emilia-romagna.it */ -16704, /* emiliaromagna.it */ - 5600, /* emr.it */ - 3421, /* en.it */ -16721, /* enna.it */ - 3850, /* fc.it */ - 1312, /* fe.it */ -16726, /* fermo.it */ -16732, /* ferrara.it */ -16740, /* fg.it */ - 3009, /* fi.it */ -16743, /* firenze.it */ -16751, /* florence.it */ - 3160, /* fm.it */ -16760, /* foggia.it */ -16767, /* forli-cesena.it */ -16780, /* forlicesena.it */ - 3245, /* fr.it */ -16792, /* friuli-v-giulia.it */ -16808, /* friuli-ve-giulia.it */ -16825, /* friuli-vegiulia.it */ -16841, /* friuli-venezia-giulia.it */ -16863, /* friuli-veneziagiulia.it */ -16884, /* friuli-vgiulia.it */ -16899, /* friuliv-giulia.it */ -16914, /* friulive-giulia.it */ -16930, /* friulivegiulia.it */ -16945, /* friulivenezia-giulia.it */ -16966, /* friuliveneziagiulia.it */ -16986, /* friulivgiulia.it */ -17000, /* frosinone.it */ - 8233, /* fvg.it */ - 1899, /* ge.it */ -17010, /* genoa.it */ -17016, /* genova.it */ + 2005, /* ct.it */ +16999, /* cuneo.it */ + 2203, /* cz.it */ +17005, /* dell-ogliastra.it */ +17020, /* dellogliastra.it */ + 2623, /* edu.it */ +17034, /* emilia-romagna.it */ +17049, /* emiliaromagna.it */ + 5579, /* emr.it */ + 3406, /* en.it */ +17066, /* enna.it */ + 3832, /* fc.it */ + 1313, /* fe.it */ +17071, /* fermo.it */ +17077, /* ferrara.it */ +17085, /* fg.it */ + 2994, /* fi.it */ +17088, /* firenze.it */ +17096, /* florence.it */ + 3145, /* fm.it */ +17105, /* foggia.it */ +17112, /* forli-cesena.it */ +17125, /* forlicesena.it */ + 3230, /* fr.it */ +17137, /* friuli-v-giulia.it */ +17153, /* friuli-ve-giulia.it */ +17170, /* friuli-vegiulia.it */ +17186, /* friuli-venezia-giulia.it */ +17208, /* friuli-veneziagiulia.it */ +17229, /* friuli-vgiulia.it */ +17244, /* friuliv-giulia.it */ +17259, /* friulive-giulia.it */ +17275, /* friulivegiulia.it */ +17290, /* friulivenezia-giulia.it */ +17311, /* friuliveneziagiulia.it */ +17331, /* friulivgiulia.it */ +17345, /* frosinone.it */ + 8226, /* fvg.it */ + 1900, /* ge.it */ +17355, /* genoa.it */ +17361, /* genova.it */ 257, /* go.it */ -17023, /* gorizia.it */ - 3686, /* gov.it */ - 3697, /* gr.it */ -17031, /* grosseto.it */ -17040, /* iglesias-carbonia.it */ -17058, /* iglesiascarbonia.it */ - 4200, /* im.it */ -17075, /* imperia.it */ - 3722, /* is.it */ -17083, /* isernia.it */ - 3123, /* kr.it */ -17091, /* la-spezia.it */ -16219, /* laquila.it */ -17101, /* laspezia.it */ -17110, /* latina.it */ +17368, /* gorizia.it */ + 3671, /* gov.it */ + 3682, /* gr.it */ +17376, /* grosseto.it */ +17385, /* iglesias-carbonia.it */ +17403, /* iglesiascarbonia.it */ + 4175, /* im.it */ +17420, /* imperia.it */ + 3707, /* is.it */ +17428, /* isernia.it */ + 3108, /* kr.it */ +17436, /* la-spezia.it */ +16564, /* laquila.it */ +17446, /* laspezia.it */ +17455, /* latina.it */ 670, /* laz.it */ -17117, /* lazio.it */ - 4452, /* lc.it */ +17462, /* lazio.it */ + 4431, /* lc.it */ 40, /* le.it */ -11721, /* lecce.it */ -17128, /* lecco.it */ - 4377, /* li.it */ -17134, /* lig.it */ -17138, /* liguria.it */ -17146, /* livorno.it */ - 3378, /* lo.it */ -17158, /* lodi.it */ -17163, /* lom.it */ -17167, /* lombardia.it */ -17177, /* lombardy.it */ +11813, /* lecce.it */ +17473, /* lecco.it */ + 4356, /* li.it */ +17479, /* lig.it */ +17483, /* liguria.it */ +17491, /* livorno.it */ + 3363, /* lo.it */ +17503, /* lodi.it */ +17508, /* lom.it */ +17512, /* lombardia.it */ +17522, /* lombardy.it */ 151, /* lt.it */ - 5112, /* lu.it */ -17186, /* lucania.it */ -17194, /* lucca.it */ -17200, /* macerata.it */ -17209, /* mantova.it */ -17219, /* mar.it */ -11886, /* marche.it */ -17223, /* massa-carrara.it */ -17237, /* massacarrara.it */ -17250, /* matera.it */ -11673, /* mb.it */ - 5307, /* mc.it */ - 1693, /* me.it */ -17257, /* medio-campidano.it */ -17273, /* mediocampidano.it */ - 7283, /* messina.it */ - 5397, /* mi.it */ -17294, /* milan.it */ -17300, /* milano.it */ - 5445, /* mn.it */ - 3600, /* mo.it */ -17307, /* modena.it */ -17314, /* mol.it */ -17318, /* molise.it */ -17325, /* monza.it */ -17331, /* monza-brianza.it */ -17345, /* monza-e-della-brianza.it */ -17367, /* monzabrianza.it */ -17380, /* monzaebrianza.it */ -17394, /* monzaedellabrianza.it */ - 1059, /* ms.it */ - 5609, /* mt.it */ + 5091, /* lu.it */ +17531, /* lucania.it */ +17539, /* lucca.it */ +17545, /* macerata.it */ +17554, /* mantova.it */ +17564, /* mar.it */ +12022, /* marche.it */ +17568, /* massa-carrara.it */ +17582, /* massacarrara.it */ +17595, /* matera.it */ +11728, /* mb.it */ + 5286, /* mc.it */ + 1694, /* me.it */ +17602, /* medio-campidano.it */ +17618, /* mediocampidano.it */ + 7272, /* messina.it */ + 5376, /* mi.it */ +17639, /* milan.it */ +17645, /* milano.it */ + 5424, /* mn.it */ + 3585, /* mo.it */ +17652, /* modena.it */ +17659, /* mol.it */ +17663, /* molise.it */ +17670, /* monza.it */ +17676, /* monza-brianza.it */ +17690, /* monza-e-della-brianza.it */ +17712, /* monzabrianza.it */ +17725, /* monzaebrianza.it */ +17739, /* monzaedellabrianza.it */ + 1060, /* ms.it */ + 5588, /* mt.it */ 172, /* na.it */ -17413, /* naples.it */ -17420, /* napoli.it */ - 1517, /* no.it */ -17427, /* novara.it */ - 5372, /* nu.it */ -17434, /* nuoro.it */ - 1036, /* og.it */ -16665, /* ogliastra.it */ -17440, /* olbia-tempio.it */ -17453, /* olbiatempio.it */ +17758, /* naples.it */ +17765, /* napoli.it */ + 1518, /* no.it */ +17772, /* novara.it */ + 5351, /* nu.it */ +17779, /* nuoro.it */ + 1037, /* og.it */ +17010, /* ogliastra.it */ +17785, /* olbia-tempio.it */ +17798, /* olbiatempio.it */ 137, /* or.it */ -17465, /* oristano.it */ +17810, /* oristano.it */ 777, /* ot.it */ 522, /* pa.it */ -17474, /* padova.it */ - 8094, /* padua.it */ -17481, /* palermo.it */ -17489, /* parma.it */ -17495, /* pavia.it */ - 5618, /* pc.it */ -17501, /* pd.it */ - 3739, /* pe.it */ -17504, /* perugia.it */ -17512, /* pesaro-urbino.it */ -17526, /* pesarourbino.it */ -17539, /* pescara.it */ - 6211, /* pg.it */ -11737, /* pi.it */ -17547, /* piacenza.it */ -17556, /* piedmont.it */ -17565, /* piemonte.it */ -17574, /* pisa.it */ -17579, /* pistoia.it */ - 5444, /* pmn.it */ - 4674, /* pn.it */ - 6969, /* po.it */ -17592, /* pordenone.it */ -17602, /* potenza.it */ - 6402, /* pr.it */ -17610, /* prato.it */ - 6510, /* pt.it */ -17619, /* pu.it */ - 8113, /* pug.it */ -17622, /* puglia.it */ -17629, /* pv.it */ -17632, /* pz.it */ - 1361, /* ra.it */ -17640, /* ragusa.it */ -16718, /* ravenna.it */ - 4885, /* rc.it */ +17819, /* padova.it */ + 8092, /* padua.it */ +17826, /* palermo.it */ +17834, /* parma.it */ +17840, /* pavia.it */ + 5597, /* pc.it */ +17846, /* pd.it */ + 3724, /* pe.it */ +17849, /* perugia.it */ +17857, /* pesaro-urbino.it */ +17871, /* pesarourbino.it */ +17884, /* pescara.it */ + 6194, /* pg.it */ +11829, /* pi.it */ +17892, /* piacenza.it */ +17901, /* piedmont.it */ +17910, /* piemonte.it */ +17919, /* pisa.it */ +17924, /* pistoia.it */ + 5423, /* pmn.it */ + 4653, /* pn.it */ + 6958, /* po.it */ +17937, /* pordenone.it */ +17947, /* potenza.it */ + 6385, /* pr.it */ +17955, /* prato.it */ + 6493, /* pt.it */ +17964, /* pu.it */ + 8111, /* pug.it */ +17967, /* puglia.it */ +17974, /* pv.it */ +17977, /* pz.it */ + 1362, /* ra.it */ +17985, /* ragusa.it */ +17063, /* ravenna.it */ + 4864, /* rc.it */ 80, /* re.it */ -17647, /* reggio-calabria.it */ -17663, /* reggio-emilia.it */ -16431, /* reggiocalabria.it */ -17677, /* reggioemilia.it */ - 1046, /* rg.it */ - 2994, /* ri.it */ -11653, /* rieti.it */ - 5410, /* rimini.it */ - 2950, /* rm.it */ +17992, /* reggio-calabria.it */ +18008, /* reggio-emilia.it */ +16776, /* reggiocalabria.it */ +18022, /* reggioemilia.it */ + 1047, /* rg.it */ + 2979, /* ri.it */ +11700, /* rieti.it */ + 5389, /* rimini.it */ + 2935, /* rm.it */ 821, /* rn.it */ 166, /* ro.it */ -17699, /* roma.it */ - 1691, /* rome.it */ -17704, /* rovigo.it */ - 1493, /* sa.it */ -17711, /* salerno.it */ -17719, /* sar.it */ -17723, /* sardegna.it */ -17732, /* sardinia.it */ -17741, /* sassari.it */ -17749, /* savona.it */ - 2364, /* si.it */ -17758, /* sic.it */ -17762, /* sicilia.it */ -17770, /* sicily.it */ -17777, /* siena.it */ -17783, /* siracusa.it */ - 7345, /* so.it */ - 6813, /* sondrio.it */ -11650, /* sp.it */ - 7437, /* sr.it */ +18044, /* roma.it */ + 1692, /* rome.it */ +18049, /* rovigo.it */ + 1494, /* sa.it */ +18056, /* salerno.it */ +18064, /* sar.it */ +18068, /* sardegna.it */ +18077, /* sardinia.it */ +18086, /* sassari.it */ +18094, /* savona.it */ + 2365, /* si.it */ +18103, /* sic.it */ +18107, /* sicilia.it */ +18115, /* sicily.it */ +18122, /* siena.it */ +18128, /* siracusa.it */ + 7334, /* so.it */ + 6796, /* sondrio.it */ +11697, /* sp.it */ + 7435, /* sr.it */ 374, /* ss.it */ -17805, /* suedtirol.it */ - 7595, /* sv.it */ +18150, /* suedtirol.it */ + 7593, /* sv.it */ 570, /* ta.it */ -17823, /* taa.it */ -17827, /* taranto.it */ +18168, /* taa.it */ +18172, /* taranto.it */ 334, /* te.it */ -17835, /* tempio-olbia.it */ -17848, /* tempioolbia.it */ -17860, /* teramo.it */ - 2749, /* terni.it */ - 5613, /* tn.it */ +18180, /* tempio-olbia.it */ +18193, /* tempioolbia.it */ +18205, /* teramo.it */ + 2748, /* terni.it */ + 5592, /* tn.it */ 631, /* to.it */ -17867, /* torino.it */ +18212, /* torino.it */ 636, /* tos.it */ -17874, /* toscana.it */ -11585, /* tp.it */ - 3302, /* tr.it */ -17882, /* trani-andria-barletta.it */ -17904, /* trani-barletta-andria.it */ -17926, /* traniandriabarletta.it */ -17946, /* tranibarlettaandria.it */ -17966, /* trapani.it */ -17974, /* trentino.it */ -17983, /* trentino-a-adige.it */ -18000, /* trentino-aadige.it */ -18016, /* trentino-alto-adige.it */ -18036, /* trentino-altoadige.it */ -18055, /* trentino-s-tirol.it */ -18072, /* trentino-stirol.it */ -18088, /* trentino-sud-tirol.it */ -18107, /* trentino-sudtirol.it */ -18125, /* trentino-sued-tirol.it */ -18145, /* trentino-suedtirol.it */ -18164, /* trentinoa-adige.it */ -18180, /* trentinoaadige.it */ -16039, /* trentinoalto-adige.it */ -16058, /* trentinoaltoadige.it */ -18195, /* trentinos-tirol.it */ - 7865, /* trentinostirol.it */ -18211, /* trentinosud-tirol.it */ -18229, /* trentinosudtirol.it */ -18246, /* trentinosued-tirol.it */ -17797, /* trentinosuedtirol.it */ -18265, /* trento.it */ -18272, /* treviso.it */ -18280, /* trieste.it */ +18219, /* toscana.it */ +11618, /* tp.it */ + 3287, /* tr.it */ +18227, /* trani-andria-barletta.it */ +18249, /* trani-barletta-andria.it */ +18271, /* traniandriabarletta.it */ +18291, /* tranibarlettaandria.it */ +18311, /* trapani.it */ +18319, /* trentino.it */ +18328, /* trentino-a-adige.it */ +18345, /* trentino-aadige.it */ +18361, /* trentino-alto-adige.it */ +18381, /* trentino-altoadige.it */ +18400, /* trentino-s-tirol.it */ +18417, /* trentino-stirol.it */ +18433, /* trentino-sud-tirol.it */ +18452, /* trentino-sudtirol.it */ +18470, /* trentino-sued-tirol.it */ +18490, /* trentino-suedtirol.it */ +18509, /* trentinoa-adige.it */ +18525, /* trentinoaadige.it */ +16384, /* trentinoalto-adige.it */ +16403, /* trentinoaltoadige.it */ +18540, /* trentinos-tirol.it */ + 7863, /* trentinostirol.it */ +18556, /* trentinosud-tirol.it */ +18574, /* trentinosudtirol.it */ +18591, /* trentinosued-tirol.it */ +18142, /* trentinosuedtirol.it */ +18610, /* trento.it */ +18617, /* treviso.it */ +18625, /* trieste.it */ 109, /* ts.it */ -18292, /* turin.it */ -18298, /* tuscany.it */ - 2546, /* tv.it */ - 1842, /* ud.it */ -18306, /* udine.it */ -18312, /* umb.it */ -18316, /* umbria.it */ -18323, /* urbino-pesaro.it */ -18337, /* urbinopesaro.it */ +18637, /* turin.it */ +18643, /* tuscany.it */ + 2549, /* tv.it */ + 1843, /* ud.it */ +18651, /* udine.it */ +18657, /* umb.it */ +18661, /* umbria.it */ +18668, /* urbino-pesaro.it */ +18682, /* urbinopesaro.it */ 834, /* va.it */ -18350, /* val-d-aosta.it */ -18362, /* val-daosta.it */ -18373, /* vald-aosta.it */ -16172, /* valdaosta.it */ -18384, /* valle-aosta.it */ -18396, /* valle-d-aosta.it */ -18410, /* valle-daosta.it */ -18423, /* valleaosta.it */ -18434, /* valled-aosta.it */ -18447, /* valledaosta.it */ -18459, /* vallee-aoste.it */ -16207, /* valleeaoste.it */ +18695, /* val-d-aosta.it */ +18707, /* val-daosta.it */ +18718, /* vald-aosta.it */ +16517, /* valdaosta.it */ +18729, /* valle-aosta.it */ +18741, /* valle-d-aosta.it */ +18755, /* valle-daosta.it */ +18768, /* valleaosta.it */ +18779, /* valled-aosta.it */ +18792, /* valledaosta.it */ +18804, /* vallee-aoste.it */ +16552, /* valleeaoste.it */ 447, /* vao.it */ -18472, /* varese.it */ -18479, /* vb.it */ - 6561, /* vc.it */ -18482, /* vda.it */ +18817, /* varese.it */ +18824, /* vb.it */ + 6544, /* vc.it */ +18827, /* vda.it */ 125, /* ve.it */ - 7158, /* ven.it */ -18486, /* veneto.it */ -18493, /* venezia.it */ - 4167, /* venice.it */ -18501, /* verbania.it */ -18510, /* vercelli.it */ -18519, /* verona.it */ - 8237, /* vi.it */ -18526, /* vibo-valentia.it */ -18540, /* vibovalentia.it */ -18553, /* vicenza.it */ -18561, /* viterbo.it */ - 2582, /* vr.it */ - 8080, /* vs.it */ -12876, /* vt.it */ -18569, /* vv.it */ - 1913, /* com.jo */ - 2624, /* edu.jo */ - 3686, /* gov.jo */ - 4195, /* mil.jo */ - 5725, /* name.jo */ - 4185, /* net.jo */ - 6070, /* org.jo */ - 1145, /* sch.jo */ + 7147, /* ven.it */ +18831, /* veneto.it */ +18838, /* venezia.it */ + 4148, /* venice.it */ +18846, /* verbania.it */ +18855, /* vercelli.it */ +18864, /* verona.it */ + 8230, /* vi.it */ +18871, /* vibo-valentia.it */ +18885, /* vibovalentia.it */ +18898, /* vicenza.it */ +18906, /* viterbo.it */ + 2585, /* vr.it */ + 8078, /* vs.it */ +13035, /* vt.it */ +18914, /* vv.it */ + 113, /* co.je */ + 5737, /* net.je */ + 6053, /* org.je */ + 1914, /* com.jo */ + 2623, /* edu.jo */ + 3671, /* gov.jo */ + 4170, /* mil.jo */ + 5695, /* name.jo */ + 5737, /* net.jo */ + 6053, /* org.jo */ + 1146, /* sch.jo */ 244, /* aisai.aichi.jp */ -10609, /* ama.aichi.jp */ -19505, /* anjo.aichi.jp */ -19510, /* asuke.aichi.jp */ -19516, /* chiryu.aichi.jp */ -19523, /* chita.aichi.jp */ -19529, /* fuso.aichi.jp */ -19534, /* gamagori.aichi.jp */ -19543, /* handa.aichi.jp */ -19549, /* hazu.aichi.jp */ -19554, /* hekinan.aichi.jp */ -19562, /* higashiura.aichi.jp */ -19573, /* ichinomiya.aichi.jp */ -19584, /* inazawa.aichi.jp */ -19592, /* inuyama.aichi.jp */ -19600, /* isshiki.aichi.jp */ -19608, /* iwakura.aichi.jp */ -19616, /* kanie.aichi.jp */ -19622, /* kariya.aichi.jp */ -19629, /* kasugai.aichi.jp */ -19637, /* kira.aichi.jp */ -19642, /* kiyosu.aichi.jp */ -19649, /* komaki.aichi.jp */ -19656, /* konan.aichi.jp */ -17815, /* kota.aichi.jp */ -19662, /* mihama.aichi.jp */ -19678, /* miyoshi.aichi.jp */ -19686, /* nishio.aichi.jp */ -19693, /* nisshin.aichi.jp */ -19704, /* obu.aichi.jp */ -19708, /* oguchi.aichi.jp */ -19715, /* oharu.aichi.jp */ -19721, /* okazaki.aichi.jp */ -19729, /* owariasahi.aichi.jp */ -17035, /* seto.aichi.jp */ -19746, /* shikatsu.aichi.jp */ -19755, /* shinshiro.aichi.jp */ -19765, /* shitara.aichi.jp */ -19773, /* tahara.aichi.jp */ -19780, /* takahama.aichi.jp */ -19789, /* tobishima.aichi.jp */ -19799, /* toei.aichi.jp */ -11731, /* togo.aichi.jp */ -19804, /* tokai.aichi.jp */ - 5721, /* tokoname.aichi.jp */ -19810, /* toyoake.aichi.jp */ -19818, /* toyohashi.aichi.jp */ -19828, /* toyokawa.aichi.jp */ -19837, /* toyone.aichi.jp */ - 7966, /* toyota.aichi.jp */ -19848, /* tsushima.aichi.jp */ -19857, /* yatomi.aichi.jp */ -18585, /* akita.akita.jp */ -19864, /* daisen.akita.jp */ -19871, /* fujisato.akita.jp */ -19880, /* gojome.akita.jp */ -19887, /* hachirogata.akita.jp */ -19899, /* happou.akita.jp */ -19906, /* higashinaruse.akita.jp */ -19924, /* honjo.akita.jp */ -19930, /* honjyo.akita.jp */ -18695, /* ikawa.akita.jp */ -19944, /* kamikoani.akita.jp */ -19954, /* kamioka.akita.jp */ -19962, /* katagami.akita.jp */ - 8143, /* kazuno.akita.jp */ -19971, /* kitaakita.akita.jp */ - 6097, /* kosaka.akita.jp */ -19981, /* kyowa.akita.jp */ -19989, /* misato.akita.jp */ -20000, /* mitane.akita.jp */ -20007, /* moriyoshi.akita.jp */ -20017, /* nikaho.akita.jp */ -20024, /* noshiro.akita.jp */ - 2239, /* odate.akita.jp */ -10600, /* oga.akita.jp */ -19893, /* ogata.akita.jp */ -20044, /* semboku.akita.jp */ -20052, /* yokote.akita.jp */ -19920, /* yurihonjo.akita.jp */ -18591, /* aomori.aomori.jp */ -20059, /* gonohe.aomori.jp */ -20066, /* hachinohe.aomori.jp */ -20076, /* hashikami.aomori.jp */ -20086, /* hiranai.aomori.jp */ -20094, /* hirosaki.aomori.jp */ -20103, /* itayanagi.aomori.jp */ -20113, /* kuroishi.aomori.jp */ -20122, /* misawa.aomori.jp */ -20129, /* mutsu.aomori.jp */ -20135, /* nakadomari.aomori.jp */ -20146, /* noheji.aomori.jp */ -20153, /* oirase.aomori.jp */ -20160, /* owani.aomori.jp */ -20166, /* rokunohe.aomori.jp */ -20175, /* sannohe.aomori.jp */ -20183, /* shichinohe.aomori.jp */ -20194, /* shingo.aomori.jp */ -20201, /* takko.aomori.jp */ -20207, /* towada.aomori.jp */ -20214, /* tsugaru.aomori.jp */ -20222, /* tsuruta.aomori.jp */ -20230, /* abiko.chiba.jp */ -19734, /* asahi.chiba.jp */ -20236, /* chonan.chiba.jp */ -20243, /* chosei.chiba.jp */ -20250, /* choshi.chiba.jp */ -20261, /* chuo.chiba.jp */ -20266, /* funabashi.chiba.jp */ -20276, /* futtsu.chiba.jp */ -20283, /* hanamigawa.chiba.jp */ -20294, /* ichihara.chiba.jp */ -20303, /* ichikawa.chiba.jp */ -19573, /* ichinomiya.chiba.jp */ -20312, /* inzai.chiba.jp */ -17288, /* isumi.chiba.jp */ -20318, /* kamagaya.chiba.jp */ -20327, /* kamogawa.chiba.jp */ -20336, /* kashiwa.chiba.jp */ -20346, /* katori.chiba.jp */ -20358, /* katsuura.chiba.jp */ -20367, /* kimitsu.chiba.jp */ -20375, /* kisarazu.chiba.jp */ -20384, /* kozaki.chiba.jp */ -20391, /* kujukuri.chiba.jp */ -20400, /* kyonan.chiba.jp */ -20407, /* matsudo.chiba.jp */ -20415, /* midori.chiba.jp */ -19662, /* mihama.chiba.jp */ -20422, /* minamiboso.chiba.jp */ -20433, /* mobara.chiba.jp */ -20440, /* mutsuzawa.chiba.jp */ -20450, /* nagara.chiba.jp */ -20457, /* nagareyama.chiba.jp */ -20468, /* narashino.chiba.jp */ -20478, /* narita.chiba.jp */ -20485, /* noda.chiba.jp */ -20490, /* oamishirasato.chiba.jp */ -20504, /* omigawa.chiba.jp */ -20512, /* onjuku.chiba.jp */ -20522, /* otaki.chiba.jp */ +10588, /* ama.aichi.jp */ +19850, /* anjo.aichi.jp */ +19855, /* asuke.aichi.jp */ +19861, /* chiryu.aichi.jp */ +19868, /* chita.aichi.jp */ +19874, /* fuso.aichi.jp */ +19879, /* gamagori.aichi.jp */ +19888, /* handa.aichi.jp */ +19894, /* hazu.aichi.jp */ +19899, /* hekinan.aichi.jp */ +19907, /* higashiura.aichi.jp */ +19918, /* ichinomiya.aichi.jp */ +19929, /* inazawa.aichi.jp */ +19937, /* inuyama.aichi.jp */ +19945, /* isshiki.aichi.jp */ +19953, /* iwakura.aichi.jp */ +19961, /* kanie.aichi.jp */ +19967, /* kariya.aichi.jp */ +19974, /* kasugai.aichi.jp */ +19982, /* kira.aichi.jp */ +19987, /* kiyosu.aichi.jp */ +19994, /* komaki.aichi.jp */ +20001, /* konan.aichi.jp */ +18160, /* kota.aichi.jp */ +20007, /* mihama.aichi.jp */ +20023, /* miyoshi.aichi.jp */ +20031, /* nishio.aichi.jp */ +20038, /* nisshin.aichi.jp */ +20049, /* obu.aichi.jp */ +20053, /* oguchi.aichi.jp */ +20060, /* oharu.aichi.jp */ +20066, /* okazaki.aichi.jp */ +20074, /* owariasahi.aichi.jp */ +17380, /* seto.aichi.jp */ +20091, /* shikatsu.aichi.jp */ +20100, /* shinshiro.aichi.jp */ +20110, /* shitara.aichi.jp */ +20118, /* tahara.aichi.jp */ +20125, /* takahama.aichi.jp */ +20134, /* tobishima.aichi.jp */ +20144, /* toei.aichi.jp */ +11823, /* togo.aichi.jp */ +20149, /* tokai.aichi.jp */ + 5691, /* tokoname.aichi.jp */ +20155, /* toyoake.aichi.jp */ +20163, /* toyohashi.aichi.jp */ +20173, /* toyokawa.aichi.jp */ +20182, /* toyone.aichi.jp */ + 7964, /* toyota.aichi.jp */ +20193, /* tsushima.aichi.jp */ +20202, /* yatomi.aichi.jp */ +18930, /* akita.akita.jp */ +20209, /* daisen.akita.jp */ +20216, /* fujisato.akita.jp */ +20225, /* gojome.akita.jp */ +20232, /* hachirogata.akita.jp */ +20244, /* happou.akita.jp */ +20251, /* higashinaruse.akita.jp */ +20269, /* honjo.akita.jp */ +20275, /* honjyo.akita.jp */ +19040, /* ikawa.akita.jp */ +20289, /* kamikoani.akita.jp */ +20299, /* kamioka.akita.jp */ +20307, /* katagami.akita.jp */ + 8141, /* kazuno.akita.jp */ +20316, /* kitaakita.akita.jp */ + 6080, /* kosaka.akita.jp */ +20326, /* kyowa.akita.jp */ +20334, /* misato.akita.jp */ +20345, /* mitane.akita.jp */ +20352, /* moriyoshi.akita.jp */ +20362, /* nikaho.akita.jp */ +20369, /* noshiro.akita.jp */ + 2240, /* odate.akita.jp */ +10579, /* oga.akita.jp */ +20238, /* ogata.akita.jp */ +20389, /* semboku.akita.jp */ +20397, /* yokote.akita.jp */ +20265, /* yurihonjo.akita.jp */ +18936, /* aomori.aomori.jp */ +20404, /* gonohe.aomori.jp */ +20411, /* hachinohe.aomori.jp */ +20421, /* hashikami.aomori.jp */ +20431, /* hiranai.aomori.jp */ +20439, /* hirosaki.aomori.jp */ +20448, /* itayanagi.aomori.jp */ +20458, /* kuroishi.aomori.jp */ +20467, /* misawa.aomori.jp */ +20474, /* mutsu.aomori.jp */ +20480, /* nakadomari.aomori.jp */ +20491, /* noheji.aomori.jp */ +20498, /* oirase.aomori.jp */ +20505, /* owani.aomori.jp */ +20511, /* rokunohe.aomori.jp */ +20520, /* sannohe.aomori.jp */ +20528, /* shichinohe.aomori.jp */ +20539, /* shingo.aomori.jp */ +20546, /* takko.aomori.jp */ +20552, /* towada.aomori.jp */ +20559, /* tsugaru.aomori.jp */ +20567, /* tsuruta.aomori.jp */ +20575, /* abiko.chiba.jp */ +20079, /* asahi.chiba.jp */ +20581, /* chonan.chiba.jp */ +20588, /* chosei.chiba.jp */ +20595, /* choshi.chiba.jp */ +20606, /* chuo.chiba.jp */ +20611, /* funabashi.chiba.jp */ +20621, /* futtsu.chiba.jp */ +20628, /* hanamigawa.chiba.jp */ +20639, /* ichihara.chiba.jp */ +20648, /* ichikawa.chiba.jp */ +19918, /* ichinomiya.chiba.jp */ +20657, /* inzai.chiba.jp */ +17633, /* isumi.chiba.jp */ +20663, /* kamagaya.chiba.jp */ +20672, /* kamogawa.chiba.jp */ +20681, /* kashiwa.chiba.jp */ +20691, /* katori.chiba.jp */ +20703, /* katsuura.chiba.jp */ +20712, /* kimitsu.chiba.jp */ +20720, /* kisarazu.chiba.jp */ +20729, /* kozaki.chiba.jp */ +20736, /* kujukuri.chiba.jp */ +20745, /* kyonan.chiba.jp */ +20752, /* matsudo.chiba.jp */ +20760, /* midori.chiba.jp */ +20007, /* mihama.chiba.jp */ +20767, /* minamiboso.chiba.jp */ +20778, /* mobara.chiba.jp */ +20785, /* mutsuzawa.chiba.jp */ +20795, /* nagara.chiba.jp */ +20802, /* nagareyama.chiba.jp */ +20813, /* narashino.chiba.jp */ +20823, /* narita.chiba.jp */ +20830, /* noda.chiba.jp */ +20835, /* oamishirasato.chiba.jp */ +20849, /* omigawa.chiba.jp */ +20857, /* onjuku.chiba.jp */ +20867, /* otaki.chiba.jp */ 154, /* sakae.chiba.jp */ - 6909, /* sakura.chiba.jp */ -20528, /* shimofusa.chiba.jp */ -20538, /* shirako.chiba.jp */ -20546, /* shiroi.chiba.jp */ -20553, /* shisui.chiba.jp */ -20560, /* sodegaura.chiba.jp */ -20570, /* sosa.chiba.jp */ -20576, /* tako.chiba.jp */ -20581, /* tateyama.chiba.jp */ -20590, /* togane.chiba.jp */ -20597, /* tohnosho.chiba.jp */ -19987, /* tomisato.chiba.jp */ -20606, /* urayasu.chiba.jp */ -20614, /* yachimata.chiba.jp */ -20624, /* yachiyo.chiba.jp */ -18598, /* yokaichiba.chiba.jp */ -20632, /* yokoshibahikari.chiba.jp */ -20648, /* yotsukaido.chiba.jp */ -20660, /* ainan.ehime.jp */ -20667, /* honai.ehime.jp */ -20676, /* ikata.ehime.jp */ -20682, /* imabari.ehime.jp */ -20628, /* iyo.ehime.jp */ -20701, /* kamijima.ehime.jp */ -20710, /* kihoku.ehime.jp */ -20717, /* kumakogen.ehime.jp */ -20727, /* masaki.ehime.jp */ -20734, /* matsuno.ehime.jp */ -20749, /* matsuyama.ehime.jp */ -20673, /* namikata.ehime.jp */ -20759, /* niihama.ehime.jp */ -20768, /* ozu.ehime.jp */ -20772, /* saijo.ehime.jp */ -20690, /* seiyo.ehime.jp */ -20778, /* shikokuchuo.ehime.jp */ -20791, /* tobe.ehime.jp */ -11758, /* toon.ehime.jp */ -20805, /* uchiko.ehime.jp */ -20812, /* uwajima.ehime.jp */ -20820, /* yawatahama.ehime.jp */ -20837, /* echizen.fukui.jp */ -20845, /* eiheiji.fukui.jp */ -18615, /* fukui.fukui.jp */ -20853, /* ikeda.fukui.jp */ -20859, /* katsuyama.fukui.jp */ -19662, /* mihama.fukui.jp */ -20831, /* minamiechizen.fukui.jp */ -20869, /* obama.fukui.jp */ -11893, /* ohi.fukui.jp */ -20876, /* ono.fukui.jp */ -20880, /* sabae.fukui.jp */ -20886, /* sakai.fukui.jp */ -19780, /* takahama.fukui.jp */ -20892, /* tsuruga.fukui.jp */ -20900, /* wakasa.fukui.jp */ -20907, /* ashiya.fukuoka.jp */ -20914, /* buzen.fukuoka.jp */ -20920, /* chikugo.fukuoka.jp */ -20928, /* chikuho.fukuoka.jp */ -20936, /* chikujo.fukuoka.jp */ -20944, /* chikushino.fukuoka.jp */ -20955, /* chikuzen.fukuoka.jp */ -20261, /* chuo.fukuoka.jp */ -20964, /* dazaifu.fukuoka.jp */ -20972, /* fukuchi.fukuoka.jp */ -20980, /* hakata.fukuoka.jp */ -20987, /* higashi.fukuoka.jp */ -20995, /* hirokawa.fukuoka.jp */ -21004, /* hisayama.fukuoka.jp */ -21013, /* iizuka.fukuoka.jp */ -21020, /* inatsuki.fukuoka.jp */ -20019, /* kaho.fukuoka.jp */ -21029, /* kasuga.fukuoka.jp */ -21036, /* kasuya.fukuoka.jp */ -21043, /* kawara.fukuoka.jp */ -21050, /* keisen.fukuoka.jp */ -20032, /* koga.fukuoka.jp */ -21057, /* kurate.fukuoka.jp */ -21064, /* kurogi.fukuoka.jp */ -21078, /* kurume.fukuoka.jp */ -21088, /* minami.fukuoka.jp */ -21095, /* miyako.fukuoka.jp */ -21104, /* miyama.fukuoka.jp */ -21111, /* miyawaka.fukuoka.jp */ -21120, /* mizumaki.fukuoka.jp */ -21129, /* munakata.fukuoka.jp */ -18707, /* nakagawa.fukuoka.jp */ -21138, /* nakama.fukuoka.jp */ -21149, /* nishi.fukuoka.jp */ -20037, /* nogata.fukuoka.jp */ -21155, /* ogori.fukuoka.jp */ -21161, /* okagaki.fukuoka.jp */ -19831, /* okawa.fukuoka.jp */ -21170, /* oki.fukuoka.jp */ -21174, /* omuta.fukuoka.jp */ -21180, /* onga.fukuoka.jp */ -21190, /* onojo.fukuoka.jp */ - 4710, /* oto.fukuoka.jp */ -21201, /* saigawa.fukuoka.jp */ -21209, /* sasaguri.fukuoka.jp */ -21218, /* shingu.fukuoka.jp */ -21225, /* shinyoshitomi.fukuoka.jp */ -20666, /* shonai.fukuoka.jp */ -21239, /* soeda.fukuoka.jp */ -21248, /* sue.fukuoka.jp */ -21252, /* tachiarai.fukuoka.jp */ -21264, /* tagawa.fukuoka.jp */ -21273, /* takata.fukuoka.jp */ -21280, /* toho.fukuoka.jp */ -21285, /* toyotsu.fukuoka.jp */ -21293, /* tsuiki.fukuoka.jp */ -21300, /* ukiha.fukuoka.jp */ -17290, /* umi.fukuoka.jp */ -21307, /* usui.fukuoka.jp */ -21312, /* yamada.fukuoka.jp */ -21319, /* yame.fukuoka.jp */ -21324, /* yanagawa.fukuoka.jp */ -21333, /* yukuhashi.fukuoka.jp */ -21343, /* aizubange.fukushima.jp */ -21353, /* aizumisato.fukushima.jp */ -21364, /* aizuwakamatsu.fukushima.jp */ -21378, /* asakawa.fukushima.jp */ -21386, /* bandai.fukushima.jp */ - 2240, /* date.fukushima.jp */ -18633, /* fukushima.fukushima.jp */ -21393, /* furudono.fukushima.jp */ -21402, /* futaba.fukushima.jp */ -21409, /* hanawa.fukushima.jp */ -20987, /* higashi.fukushima.jp */ -21416, /* hirata.fukushima.jp */ -21423, /* hirono.fukushima.jp */ -21430, /* iitate.fukushima.jp */ -21437, /* inawashiro.fukushima.jp */ -18692, /* ishikawa.fukushima.jp */ -21452, /* iwaki.fukushima.jp */ -21458, /* izumizaki.fukushima.jp */ -21468, /* kagamiishi.fukushima.jp */ -21479, /* kaneyama.fukushima.jp */ -21488, /* kawamata.fukushima.jp */ -21271, /* kitakata.fukushima.jp */ -21497, /* kitashiobara.fukushima.jp */ -21510, /* koori.fukushima.jp */ -21522, /* koriyama.fukushima.jp */ -21531, /* kunimi.fukushima.jp */ -21538, /* miharu.fukushima.jp */ -21545, /* mishima.fukushima.jp */ -18761, /* namie.fukushima.jp */ - 5836, /* nango.fukushima.jp */ -21553, /* nishiaizu.fukushima.jp */ -21563, /* nishigo.fukushima.jp */ -21571, /* okuma.fukushima.jp */ -21577, /* omotego.fukushima.jp */ -20876, /* ono.fukushima.jp */ -21585, /* otama.fukushima.jp */ -21591, /* samegawa.fukushima.jp */ -21600, /* shimogo.fukushima.jp */ -21615, /* shirakawa.fukushima.jp */ -21625, /* showa.fukushima.jp */ -21631, /* soma.fukushima.jp */ -21636, /* sukagawa.fukushima.jp */ -21645, /* taishin.fukushima.jp */ -21653, /* tamakawa.fukushima.jp */ -21662, /* tanagura.fukushima.jp */ -21671, /* tenei.fukushima.jp */ -21677, /* yabuki.fukushima.jp */ -21691, /* yamato.fukushima.jp */ -21698, /* yamatsuri.fukushima.jp */ -21708, /* yanaizu.fukushima.jp */ -21716, /* yugawa.fukushima.jp */ -21723, /* anpachi.gifu.jp */ -16776, /* ena.gifu.jp */ -18643, /* gifu.gifu.jp */ -21731, /* ginan.gifu.jp */ - 2481, /* godo.gifu.jp */ - 4470, /* gujo.gifu.jp */ -21737, /* hashima.gifu.jp */ -21745, /* hichiso.gifu.jp */ -21756, /* hida.gifu.jp */ -21608, /* higashishirakawa.gifu.jp */ -21761, /* ibigawa.gifu.jp */ -20853, /* ikeda.gifu.jp */ -21769, /* kakamigahara.gifu.jp */ -21782, /* kani.gifu.jp */ -21787, /* kasahara.gifu.jp */ -21796, /* kasamatsu.gifu.jp */ -21806, /* kawaue.gifu.jp */ -21813, /* kitagata.gifu.jp */ -21824, /* mino.gifu.jp */ -21829, /* minokamo.gifu.jp */ -21838, /* mitake.gifu.jp */ -21845, /* mizunami.gifu.jp */ -21854, /* motosu.gifu.jp */ -21861, /* nakatsugawa.gifu.jp */ -21874, /* ogaki.gifu.jp */ -21880, /* sakahogi.gifu.jp */ -21895, /* seki.gifu.jp */ -21900, /* sekigahara.gifu.jp */ -21615, /* shirakawa.gifu.jp */ -21911, /* tajimi.gifu.jp */ -21918, /* takayama.gifu.jp */ -21927, /* tarui.gifu.jp */ -21169, /* toki.gifu.jp */ -21933, /* tomika.gifu.jp */ -21940, /* wanouchi.gifu.jp */ -19470, /* yamagata.gifu.jp */ -21949, /* yaotsu.gifu.jp */ -21958, /* yoro.gifu.jp */ -21963, /* annaka.gunma.jp */ -21970, /* chiyoda.gunma.jp */ -21978, /* fujioka.gunma.jp */ -21986, /* higashiagatsuma.gunma.jp */ -22002, /* isesaki.gunma.jp */ -22010, /* itakura.gunma.jp */ -22018, /* kanna.gunma.jp */ - 5915, /* kanra.gunma.jp */ -22024, /* katashina.gunma.jp */ -22034, /* kawaba.gunma.jp */ -22041, /* kiryu.gunma.jp */ -22047, /* kusatsu.gunma.jp */ -22055, /* maebashi.gunma.jp */ -22064, /* meiwa.gunma.jp */ -20415, /* midori.gunma.jp */ -22070, /* minakami.gunma.jp */ -22079, /* naganohara.gunma.jp */ -22090, /* nakanojo.gunma.jp */ -22099, /* nanmoku.gunma.jp */ -22107, /* numata.gunma.jp */ -22114, /* oizumi.gunma.jp */ -22123, /* ora.gunma.jp */ - 7969, /* ota.gunma.jp */ -22127, /* shibukawa.gunma.jp */ -22137, /* shimonita.gunma.jp */ -22147, /* shinto.gunma.jp */ -21625, /* showa.gunma.jp */ -22154, /* takasaki.gunma.jp */ -21918, /* takayama.gunma.jp */ -22163, /* tamamura.gunma.jp */ -22172, /* tatebayashi.gunma.jp */ -22184, /* tomioka.gunma.jp */ -22192, /* tsukiyono.gunma.jp */ -22202, /* tsumagoi.gunma.jp */ - 5882, /* ueno.gunma.jp */ -22211, /* yoshioka.gunma.jp */ -21085, /* asaminami.hiroshima.jp */ -22220, /* daiwa.hiroshima.jp */ -22226, /* etajima.hiroshima.jp */ -22234, /* fuchu.hiroshima.jp */ -22240, /* fukuyama.hiroshima.jp */ -22249, /* hatsukaichi.hiroshima.jp */ -22261, /* higashihiroshima.hiroshima.jp */ -22278, /* hongo.hiroshima.jp */ -22284, /* jinsekikogen.hiroshima.jp */ -22297, /* kaita.hiroshima.jp */ -18617, /* kui.hiroshima.jp */ -22303, /* kumano.hiroshima.jp */ - 6582, /* kure.hiroshima.jp */ -22314, /* mihara.hiroshima.jp */ -19678, /* miyoshi.hiroshima.jp */ -21965, /* naka.hiroshima.jp */ -22321, /* onomichi.hiroshima.jp */ -20696, /* osakikamijima.hiroshima.jp */ -22330, /* otake.hiroshima.jp */ - 6099, /* saka.hiroshima.jp */ -22336, /* sera.hiroshima.jp */ -21145, /* seranishi.hiroshima.jp */ -22341, /* shinichi.hiroshima.jp */ -22350, /* shobara.hiroshima.jp */ -22358, /* takehara.hiroshima.jp */ -22367, /* abashiri.hokkaido.jp */ -22378, /* abira.hokkaido.jp */ -22384, /* aibetsu.hokkaido.jp */ -22376, /* akabira.hokkaido.jp */ -22392, /* akkeshi.hokkaido.jp */ -22400, /* asahikawa.hokkaido.jp */ -22410, /* ashibetsu.hokkaido.jp */ -22420, /* ashoro.hokkaido.jp */ -22427, /* assabu.hokkaido.jp */ -21995, /* atsuma.hokkaido.jp */ -22434, /* bibai.hokkaido.jp */ -22440, /* biei.hokkaido.jp */ -22445, /* bifuka.hokkaido.jp */ -22452, /* bihoro.hokkaido.jp */ -22459, /* biratori.hokkaido.jp */ -22468, /* chippubetsu.hokkaido.jp */ -22480, /* chitose.hokkaido.jp */ - 2240, /* date.hokkaido.jp */ -22488, /* ebetsu.hokkaido.jp */ -22495, /* embetsu.hokkaido.jp */ -22503, /* eniwa.hokkaido.jp */ -22509, /* erimo.hokkaido.jp */ -16076, /* esan.hokkaido.jp */ -22515, /* esashi.hokkaido.jp */ -22522, /* fukagawa.hokkaido.jp */ -18633, /* fukushima.hokkaido.jp */ -22535, /* furano.hokkaido.jp */ -22542, /* furubira.hokkaido.jp */ -22551, /* haboro.hokkaido.jp */ - 2236, /* hakodate.hokkaido.jp */ -22558, /* hamatonbetsu.hokkaido.jp */ -22571, /* hidaka.hokkaido.jp */ -22578, /* higashikagura.hokkaido.jp */ -22592, /* higashikawa.hokkaido.jp */ -22604, /* hiroo.hokkaido.jp */ -22610, /* hokuryu.hokkaido.jp */ -22618, /* hokuto.hokkaido.jp */ -22625, /* honbetsu.hokkaido.jp */ -22634, /* horokanai.hokkaido.jp */ -22644, /* horonobe.hokkaido.jp */ -20853, /* ikeda.hokkaido.jp */ -22653, /* imakane.hokkaido.jp */ -22661, /* ishikari.hokkaido.jp */ -22670, /* iwamizawa.hokkaido.jp */ -22680, /* iwanai.hokkaido.jp */ -22531, /* kamifurano.hokkaido.jp */ -22687, /* kamikawa.hokkaido.jp */ -22696, /* kamishihoro.hokkaido.jp */ -22708, /* kamisunagawa.hokkaido.jp */ -22721, /* kamoenai.hokkaido.jp */ -22730, /* kayabe.hokkaido.jp */ -22737, /* kembuchi.hokkaido.jp */ -22746, /* kikonai.hokkaido.jp */ -22754, /* kimobetsu.hokkaido.jp */ -18654, /* kitahiroshima.hokkaido.jp */ -22764, /* kitami.hokkaido.jp */ -22771, /* kiyosato.hokkaido.jp */ -22780, /* koshimizu.hokkaido.jp */ -22790, /* kunneppu.hokkaido.jp */ -22799, /* kuriyama.hokkaido.jp */ -22808, /* kuromatsunai.hokkaido.jp */ -22821, /* kushiro.hokkaido.jp */ -22829, /* kutchan.hokkaido.jp */ -19981, /* kyowa.hokkaido.jp */ -22837, /* mashike.hokkaido.jp */ -22845, /* matsumae.hokkaido.jp */ -22854, /* mikasa.hokkaido.jp */ -22861, /* minamifurano.hokkaido.jp */ -22874, /* mombetsu.hokkaido.jp */ -22883, /* moseushi.hokkaido.jp */ -22894, /* mukawa.hokkaido.jp */ -22901, /* muroran.hokkaido.jp */ -22909, /* naie.hokkaido.jp */ -18707, /* nakagawa.hokkaido.jp */ -22914, /* nakasatsunai.hokkaido.jp */ -22927, /* nakatombetsu.hokkaido.jp */ -22940, /* nanae.hokkaido.jp */ -22946, /* nanporo.hokkaido.jp */ -21956, /* nayoro.hokkaido.jp */ -22954, /* nemuro.hokkaido.jp */ -22961, /* niikappu.hokkaido.jp */ -15267, /* niki.hokkaido.jp */ -22970, /* nishiokoppe.hokkaido.jp */ -22982, /* noboribetsu.hokkaido.jp */ -22107, /* numata.hokkaido.jp */ -22994, /* obihiro.hokkaido.jp */ -23002, /* obira.hokkaido.jp */ -23008, /* oketo.hokkaido.jp */ -22975, /* okoppe.hokkaido.jp */ -23014, /* otaru.hokkaido.jp */ -20790, /* otobe.hokkaido.jp */ -23020, /* otofuke.hokkaido.jp */ -23028, /* otoineppu.hokkaido.jp */ - 5625, /* oumu.hokkaido.jp */ -22121, /* ozora.hokkaido.jp */ -17616, /* pippu.hokkaido.jp */ -23038, /* rankoshi.hokkaido.jp */ -23047, /* rebun.hokkaido.jp */ -23053, /* rikubetsu.hokkaido.jp */ -23063, /* rishiri.hokkaido.jp */ -23071, /* rishirifuji.hokkaido.jp */ -17697, /* saroma.hokkaido.jp */ -23083, /* sarufutsu.hokkaido.jp */ -23093, /* shakotan.hokkaido.jp */ -23102, /* shari.hokkaido.jp */ -23108, /* shibecha.hokkaido.jp */ -22411, /* shibetsu.hokkaido.jp */ -23117, /* shikabe.hokkaido.jp */ -23125, /* shikaoi.hokkaido.jp */ -23133, /* shimamaki.hokkaido.jp */ -22782, /* shimizu.hokkaido.jp */ -23143, /* shimokawa.hokkaido.jp */ -23153, /* shinshinotsu.hokkaido.jp */ -23166, /* shintoku.hokkaido.jp */ -23175, /* shiranuka.hokkaido.jp */ -23185, /* shiraoi.hokkaido.jp */ -23193, /* shiriuchi.hokkaido.jp */ -23203, /* sobetsu.hokkaido.jp */ -22712, /* sunagawa.hokkaido.jp */ -23211, /* taiki.hokkaido.jp */ -23217, /* takasu.hokkaido.jp */ -23224, /* takikawa.hokkaido.jp */ -23233, /* takinoue.hokkaido.jp */ -23242, /* teshikaga.hokkaido.jp */ -23252, /* tobetsu.hokkaido.jp */ -23260, /* tohma.hokkaido.jp */ -23266, /* tomakomai.hokkaido.jp */ -23276, /* tomari.hokkaido.jp */ -23283, /* toya.hokkaido.jp */ -23288, /* toyako.hokkaido.jp */ -23295, /* toyotomi.hokkaido.jp */ -23304, /* toyoura.hokkaido.jp */ -23312, /* tsubetsu.hokkaido.jp */ -23321, /* tsukigata.hokkaido.jp */ -23331, /* urakawa.hokkaido.jp */ -23339, /* urausu.hokkaido.jp */ -22613, /* uryu.hokkaido.jp */ -23346, /* utashinai.hokkaido.jp */ -23356, /* wakkanai.hokkaido.jp */ -23365, /* wassamu.hokkaido.jp */ -23373, /* yakumo.hokkaido.jp */ -23380, /* yoichi.hokkaido.jp */ -23387, /* aioi.hyogo.jp */ -23392, /* akashi.hyogo.jp */ -20542, /* ako.hyogo.jp */ -23399, /* amagasaki.hyogo.jp */ -21873, /* aogaki.hyogo.jp */ -23412, /* asago.hyogo.jp */ -20907, /* ashiya.hyogo.jp */ -23424, /* awaji.hyogo.jp */ -23430, /* fukusaki.hyogo.jp */ -23439, /* goshiki.hyogo.jp */ -23447, /* harima.hyogo.jp */ -23454, /* himeji.hyogo.jp */ -20303, /* ichikawa.hyogo.jp */ -23463, /* inagawa.hyogo.jp */ -22765, /* itami.hyogo.jp */ -23471, /* kakogawa.hyogo.jp */ -23480, /* kamigori.hyogo.jp */ -22687, /* kamikawa.hyogo.jp */ -23489, /* kasai.hyogo.jp */ -21029, /* kasuga.hyogo.jp */ -23495, /* kawanishi.hyogo.jp */ -23505, /* miki.hyogo.jp */ -23418, /* minamiawaji.hyogo.jp */ -23510, /* nishinomiya.hyogo.jp */ -21448, /* nishiwaki.hyogo.jp */ -20876, /* ono.hyogo.jp */ -23522, /* sanda.hyogo.jp */ -23528, /* sannan.hyogo.jp */ -23535, /* sasayama.hyogo.jp */ -23544, /* sayo.hyogo.jp */ -21218, /* shingu.hyogo.jp */ -23549, /* shinonsen.hyogo.jp */ -23559, /* shiso.hyogo.jp */ -23568, /* sumoto.hyogo.jp */ -23575, /* taishi.hyogo.jp */ -23584, /* taka.hyogo.jp */ -23589, /* takarazuka.hyogo.jp */ -23409, /* takasago.hyogo.jp */ -23600, /* takino.hyogo.jp */ -23610, /* tamba.hyogo.jp */ -23616, /* tatsuno.hyogo.jp */ -23624, /* toyooka.hyogo.jp */ -23632, /* yabu.hyogo.jp */ -23639, /* yashiro.hyogo.jp */ -23647, /* yoka.hyogo.jp */ -19830, /* yokawa.hyogo.jp */ - 5396, /* ami.ibaraki.jp */ -19734, /* asahi.ibaraki.jp */ -23658, /* bando.ibaraki.jp */ -23664, /* chikusei.ibaraki.jp */ + 6898, /* sakura.chiba.jp */ +20873, /* shimofusa.chiba.jp */ +20883, /* shirako.chiba.jp */ +20891, /* shiroi.chiba.jp */ +20898, /* shisui.chiba.jp */ +20905, /* sodegaura.chiba.jp */ +20915, /* sosa.chiba.jp */ +20921, /* tako.chiba.jp */ +20926, /* tateyama.chiba.jp */ +20935, /* togane.chiba.jp */ +20942, /* tohnosho.chiba.jp */ +20332, /* tomisato.chiba.jp */ +20951, /* urayasu.chiba.jp */ +20959, /* yachimata.chiba.jp */ +20969, /* yachiyo.chiba.jp */ +18943, /* yokaichiba.chiba.jp */ +20977, /* yokoshibahikari.chiba.jp */ +20993, /* yotsukaido.chiba.jp */ +21005, /* ainan.ehime.jp */ +21012, /* honai.ehime.jp */ +21021, /* ikata.ehime.jp */ +21027, /* imabari.ehime.jp */ +20973, /* iyo.ehime.jp */ +21046, /* kamijima.ehime.jp */ +21055, /* kihoku.ehime.jp */ +21062, /* kumakogen.ehime.jp */ +21072, /* masaki.ehime.jp */ +21079, /* matsuno.ehime.jp */ +21094, /* matsuyama.ehime.jp */ +21018, /* namikata.ehime.jp */ +21104, /* niihama.ehime.jp */ +21113, /* ozu.ehime.jp */ +21117, /* saijo.ehime.jp */ +21035, /* seiyo.ehime.jp */ +21123, /* shikokuchuo.ehime.jp */ +21136, /* tobe.ehime.jp */ +11850, /* toon.ehime.jp */ +21150, /* uchiko.ehime.jp */ +21157, /* uwajima.ehime.jp */ +21165, /* yawatahama.ehime.jp */ +21182, /* echizen.fukui.jp */ +21190, /* eiheiji.fukui.jp */ +18960, /* fukui.fukui.jp */ +21198, /* ikeda.fukui.jp */ +21204, /* katsuyama.fukui.jp */ +20007, /* mihama.fukui.jp */ +21176, /* minamiechizen.fukui.jp */ +21214, /* obama.fukui.jp */ +12029, /* ohi.fukui.jp */ +21221, /* ono.fukui.jp */ +21225, /* sabae.fukui.jp */ +21231, /* sakai.fukui.jp */ +20125, /* takahama.fukui.jp */ +21237, /* tsuruga.fukui.jp */ +21245, /* wakasa.fukui.jp */ +21252, /* ashiya.fukuoka.jp */ +21259, /* buzen.fukuoka.jp */ +21265, /* chikugo.fukuoka.jp */ +21273, /* chikuho.fukuoka.jp */ +21281, /* chikujo.fukuoka.jp */ +21289, /* chikushino.fukuoka.jp */ +21300, /* chikuzen.fukuoka.jp */ +20606, /* chuo.fukuoka.jp */ +21309, /* dazaifu.fukuoka.jp */ +21317, /* fukuchi.fukuoka.jp */ +21325, /* hakata.fukuoka.jp */ +21332, /* higashi.fukuoka.jp */ +21340, /* hirokawa.fukuoka.jp */ +21349, /* hisayama.fukuoka.jp */ +21358, /* iizuka.fukuoka.jp */ +21365, /* inatsuki.fukuoka.jp */ +20364, /* kaho.fukuoka.jp */ +21374, /* kasuga.fukuoka.jp */ +21381, /* kasuya.fukuoka.jp */ +21388, /* kawara.fukuoka.jp */ +21395, /* keisen.fukuoka.jp */ +20377, /* koga.fukuoka.jp */ +21402, /* kurate.fukuoka.jp */ +21409, /* kurogi.fukuoka.jp */ +21423, /* kurume.fukuoka.jp */ +21433, /* minami.fukuoka.jp */ +21440, /* miyako.fukuoka.jp */ +21449, /* miyama.fukuoka.jp */ +21456, /* miyawaka.fukuoka.jp */ +21465, /* mizumaki.fukuoka.jp */ +21474, /* munakata.fukuoka.jp */ +19052, /* nakagawa.fukuoka.jp */ +21483, /* nakama.fukuoka.jp */ +21494, /* nishi.fukuoka.jp */ +20382, /* nogata.fukuoka.jp */ +21500, /* ogori.fukuoka.jp */ +21506, /* okagaki.fukuoka.jp */ +20176, /* okawa.fukuoka.jp */ +21515, /* oki.fukuoka.jp */ +21519, /* omuta.fukuoka.jp */ +21525, /* onga.fukuoka.jp */ +21535, /* onojo.fukuoka.jp */ + 4689, /* oto.fukuoka.jp */ +21546, /* saigawa.fukuoka.jp */ +21554, /* sasaguri.fukuoka.jp */ +21563, /* shingu.fukuoka.jp */ +21570, /* shinyoshitomi.fukuoka.jp */ +21011, /* shonai.fukuoka.jp */ +21584, /* soeda.fukuoka.jp */ +21593, /* sue.fukuoka.jp */ +21597, /* tachiarai.fukuoka.jp */ +21609, /* tagawa.fukuoka.jp */ +21618, /* takata.fukuoka.jp */ +21625, /* toho.fukuoka.jp */ +21630, /* toyotsu.fukuoka.jp */ +21638, /* tsuiki.fukuoka.jp */ +21645, /* ukiha.fukuoka.jp */ +17635, /* umi.fukuoka.jp */ +21652, /* usui.fukuoka.jp */ +21657, /* yamada.fukuoka.jp */ +21664, /* yame.fukuoka.jp */ +21669, /* yanagawa.fukuoka.jp */ +21678, /* yukuhashi.fukuoka.jp */ +21688, /* aizubange.fukushima.jp */ +21698, /* aizumisato.fukushima.jp */ +21709, /* aizuwakamatsu.fukushima.jp */ +21723, /* asakawa.fukushima.jp */ +21731, /* bandai.fukushima.jp */ + 2241, /* date.fukushima.jp */ +18978, /* fukushima.fukushima.jp */ +21738, /* furudono.fukushima.jp */ +21747, /* futaba.fukushima.jp */ +21754, /* hanawa.fukushima.jp */ +21332, /* higashi.fukushima.jp */ +21761, /* hirata.fukushima.jp */ +21768, /* hirono.fukushima.jp */ +21775, /* iitate.fukushima.jp */ +21782, /* inawashiro.fukushima.jp */ +19037, /* ishikawa.fukushima.jp */ +21797, /* iwaki.fukushima.jp */ +21803, /* izumizaki.fukushima.jp */ +21813, /* kagamiishi.fukushima.jp */ +21824, /* kaneyama.fukushima.jp */ +21833, /* kawamata.fukushima.jp */ +21616, /* kitakata.fukushima.jp */ +21842, /* kitashiobara.fukushima.jp */ +21855, /* koori.fukushima.jp */ +21867, /* koriyama.fukushima.jp */ +21876, /* kunimi.fukushima.jp */ +21883, /* miharu.fukushima.jp */ +21890, /* mishima.fukushima.jp */ +19106, /* namie.fukushima.jp */ + 5808, /* nango.fukushima.jp */ +21898, /* nishiaizu.fukushima.jp */ +21908, /* nishigo.fukushima.jp */ +21916, /* okuma.fukushima.jp */ +21922, /* omotego.fukushima.jp */ +21221, /* ono.fukushima.jp */ +21930, /* otama.fukushima.jp */ +21936, /* samegawa.fukushima.jp */ +21945, /* shimogo.fukushima.jp */ +21960, /* shirakawa.fukushima.jp */ +21970, /* showa.fukushima.jp */ +21976, /* soma.fukushima.jp */ +21981, /* sukagawa.fukushima.jp */ +21990, /* taishin.fukushima.jp */ +21998, /* tamakawa.fukushima.jp */ +22007, /* tanagura.fukushima.jp */ +22016, /* tenei.fukushima.jp */ +22022, /* yabuki.fukushima.jp */ +22036, /* yamato.fukushima.jp */ +22043, /* yamatsuri.fukushima.jp */ +22053, /* yanaizu.fukushima.jp */ +22061, /* yugawa.fukushima.jp */ +22068, /* anpachi.gifu.jp */ +17121, /* ena.gifu.jp */ +18988, /* gifu.gifu.jp */ +22076, /* ginan.gifu.jp */ + 2484, /* godo.gifu.jp */ + 4449, /* gujo.gifu.jp */ +22082, /* hashima.gifu.jp */ +22090, /* hichiso.gifu.jp */ +22101, /* hida.gifu.jp */ +21953, /* higashishirakawa.gifu.jp */ +22106, /* ibigawa.gifu.jp */ +21198, /* ikeda.gifu.jp */ +22114, /* kakamigahara.gifu.jp */ +22127, /* kani.gifu.jp */ +22132, /* kasahara.gifu.jp */ +22141, /* kasamatsu.gifu.jp */ +22151, /* kawaue.gifu.jp */ +22158, /* kitagata.gifu.jp */ +22169, /* mino.gifu.jp */ +22174, /* minokamo.gifu.jp */ +22183, /* mitake.gifu.jp */ +22190, /* mizunami.gifu.jp */ +22199, /* motosu.gifu.jp */ +22206, /* nakatsugawa.gifu.jp */ +22219, /* ogaki.gifu.jp */ +22225, /* sakahogi.gifu.jp */ +22240, /* seki.gifu.jp */ +22245, /* sekigahara.gifu.jp */ +21960, /* shirakawa.gifu.jp */ +22256, /* tajimi.gifu.jp */ +22263, /* takayama.gifu.jp */ +22272, /* tarui.gifu.jp */ +21514, /* toki.gifu.jp */ +22278, /* tomika.gifu.jp */ +22285, /* wanouchi.gifu.jp */ +19815, /* yamagata.gifu.jp */ +22294, /* yaotsu.gifu.jp */ +22303, /* yoro.gifu.jp */ +22308, /* annaka.gunma.jp */ +22315, /* chiyoda.gunma.jp */ +22323, /* fujioka.gunma.jp */ +22331, /* higashiagatsuma.gunma.jp */ +22347, /* isesaki.gunma.jp */ +22355, /* itakura.gunma.jp */ +22363, /* kanna.gunma.jp */ + 5887, /* kanra.gunma.jp */ +22369, /* katashina.gunma.jp */ +22379, /* kawaba.gunma.jp */ +22386, /* kiryu.gunma.jp */ +22392, /* kusatsu.gunma.jp */ +22400, /* maebashi.gunma.jp */ +22409, /* meiwa.gunma.jp */ +20760, /* midori.gunma.jp */ +22415, /* minakami.gunma.jp */ +22424, /* naganohara.gunma.jp */ +22435, /* nakanojo.gunma.jp */ +22444, /* nanmoku.gunma.jp */ +22452, /* numata.gunma.jp */ +22459, /* oizumi.gunma.jp */ +22468, /* ora.gunma.jp */ + 7967, /* ota.gunma.jp */ +22472, /* shibukawa.gunma.jp */ +22482, /* shimonita.gunma.jp */ +22492, /* shinto.gunma.jp */ +21970, /* showa.gunma.jp */ +22499, /* takasaki.gunma.jp */ +22263, /* takayama.gunma.jp */ +22508, /* tamamura.gunma.jp */ +22517, /* tatebayashi.gunma.jp */ +22529, /* tomioka.gunma.jp */ +22537, /* tsukiyono.gunma.jp */ +22547, /* tsumagoi.gunma.jp */ + 5854, /* ueno.gunma.jp */ +22556, /* yoshioka.gunma.jp */ +21430, /* asaminami.hiroshima.jp */ +22565, /* daiwa.hiroshima.jp */ +22571, /* etajima.hiroshima.jp */ +22579, /* fuchu.hiroshima.jp */ +22585, /* fukuyama.hiroshima.jp */ +22594, /* hatsukaichi.hiroshima.jp */ +22606, /* higashihiroshima.hiroshima.jp */ +22623, /* hongo.hiroshima.jp */ +22629, /* jinsekikogen.hiroshima.jp */ +22642, /* kaita.hiroshima.jp */ +18962, /* kui.hiroshima.jp */ +22648, /* kumano.hiroshima.jp */ + 6565, /* kure.hiroshima.jp */ +22659, /* mihara.hiroshima.jp */ +20023, /* miyoshi.hiroshima.jp */ +22310, /* naka.hiroshima.jp */ +22666, /* onomichi.hiroshima.jp */ +21041, /* osakikamijima.hiroshima.jp */ +22675, /* otake.hiroshima.jp */ + 6082, /* saka.hiroshima.jp */ +22681, /* sera.hiroshima.jp */ +21490, /* seranishi.hiroshima.jp */ +22686, /* shinichi.hiroshima.jp */ +22695, /* shobara.hiroshima.jp */ +22703, /* takehara.hiroshima.jp */ +22712, /* abashiri.hokkaido.jp */ +22723, /* abira.hokkaido.jp */ +22729, /* aibetsu.hokkaido.jp */ +22721, /* akabira.hokkaido.jp */ +22737, /* akkeshi.hokkaido.jp */ +22745, /* asahikawa.hokkaido.jp */ +22755, /* ashibetsu.hokkaido.jp */ +22765, /* ashoro.hokkaido.jp */ +22772, /* assabu.hokkaido.jp */ +22340, /* atsuma.hokkaido.jp */ +22779, /* bibai.hokkaido.jp */ +22785, /* biei.hokkaido.jp */ +22790, /* bifuka.hokkaido.jp */ +22797, /* bihoro.hokkaido.jp */ +22804, /* biratori.hokkaido.jp */ +22813, /* chippubetsu.hokkaido.jp */ +22825, /* chitose.hokkaido.jp */ + 2241, /* date.hokkaido.jp */ +22833, /* ebetsu.hokkaido.jp */ +22840, /* embetsu.hokkaido.jp */ +22848, /* eniwa.hokkaido.jp */ +22854, /* erimo.hokkaido.jp */ +16421, /* esan.hokkaido.jp */ +22860, /* esashi.hokkaido.jp */ +22867, /* fukagawa.hokkaido.jp */ +18978, /* fukushima.hokkaido.jp */ +22880, /* furano.hokkaido.jp */ +22887, /* furubira.hokkaido.jp */ +22896, /* haboro.hokkaido.jp */ + 2237, /* hakodate.hokkaido.jp */ +22903, /* hamatonbetsu.hokkaido.jp */ +22916, /* hidaka.hokkaido.jp */ +22923, /* higashikagura.hokkaido.jp */ +22937, /* higashikawa.hokkaido.jp */ +22949, /* hiroo.hokkaido.jp */ +22955, /* hokuryu.hokkaido.jp */ +22963, /* hokuto.hokkaido.jp */ +22970, /* honbetsu.hokkaido.jp */ +22979, /* horokanai.hokkaido.jp */ +22989, /* horonobe.hokkaido.jp */ +21198, /* ikeda.hokkaido.jp */ +22998, /* imakane.hokkaido.jp */ +23006, /* ishikari.hokkaido.jp */ +23015, /* iwamizawa.hokkaido.jp */ +23025, /* iwanai.hokkaido.jp */ +22876, /* kamifurano.hokkaido.jp */ +23032, /* kamikawa.hokkaido.jp */ +23041, /* kamishihoro.hokkaido.jp */ +23053, /* kamisunagawa.hokkaido.jp */ +23066, /* kamoenai.hokkaido.jp */ +23075, /* kayabe.hokkaido.jp */ +23082, /* kembuchi.hokkaido.jp */ +23091, /* kikonai.hokkaido.jp */ +23099, /* kimobetsu.hokkaido.jp */ +18999, /* kitahiroshima.hokkaido.jp */ +23109, /* kitami.hokkaido.jp */ +23116, /* kiyosato.hokkaido.jp */ +23125, /* koshimizu.hokkaido.jp */ +23135, /* kunneppu.hokkaido.jp */ +23144, /* kuriyama.hokkaido.jp */ +23153, /* kuromatsunai.hokkaido.jp */ +23166, /* kushiro.hokkaido.jp */ +23174, /* kutchan.hokkaido.jp */ +20326, /* kyowa.hokkaido.jp */ +23182, /* mashike.hokkaido.jp */ +23190, /* matsumae.hokkaido.jp */ +23199, /* mikasa.hokkaido.jp */ +23206, /* minamifurano.hokkaido.jp */ +23219, /* mombetsu.hokkaido.jp */ +23228, /* moseushi.hokkaido.jp */ +23239, /* mukawa.hokkaido.jp */ +23246, /* muroran.hokkaido.jp */ +23254, /* naie.hokkaido.jp */ +19052, /* nakagawa.hokkaido.jp */ +23259, /* nakasatsunai.hokkaido.jp */ +23272, /* nakatombetsu.hokkaido.jp */ +23285, /* nanae.hokkaido.jp */ +23291, /* nanporo.hokkaido.jp */ +22301, /* nayoro.hokkaido.jp */ +23299, /* nemuro.hokkaido.jp */ +23306, /* niikappu.hokkaido.jp */ +15544, /* niki.hokkaido.jp */ +23315, /* nishiokoppe.hokkaido.jp */ +23327, /* noboribetsu.hokkaido.jp */ +22452, /* numata.hokkaido.jp */ +23339, /* obihiro.hokkaido.jp */ +23347, /* obira.hokkaido.jp */ +23353, /* oketo.hokkaido.jp */ +23320, /* okoppe.hokkaido.jp */ +23359, /* otaru.hokkaido.jp */ +21135, /* otobe.hokkaido.jp */ +23365, /* otofuke.hokkaido.jp */ +23373, /* otoineppu.hokkaido.jp */ + 5604, /* oumu.hokkaido.jp */ +22466, /* ozora.hokkaido.jp */ +17961, /* pippu.hokkaido.jp */ +23383, /* rankoshi.hokkaido.jp */ +23392, /* rebun.hokkaido.jp */ +23398, /* rikubetsu.hokkaido.jp */ +23408, /* rishiri.hokkaido.jp */ +23416, /* rishirifuji.hokkaido.jp */ +18042, /* saroma.hokkaido.jp */ +23428, /* sarufutsu.hokkaido.jp */ +23438, /* shakotan.hokkaido.jp */ +23447, /* shari.hokkaido.jp */ +23453, /* shibecha.hokkaido.jp */ +22756, /* shibetsu.hokkaido.jp */ +23462, /* shikabe.hokkaido.jp */ +23470, /* shikaoi.hokkaido.jp */ +23478, /* shimamaki.hokkaido.jp */ +23127, /* shimizu.hokkaido.jp */ +23488, /* shimokawa.hokkaido.jp */ +23498, /* shinshinotsu.hokkaido.jp */ +23511, /* shintoku.hokkaido.jp */ +23520, /* shiranuka.hokkaido.jp */ +23530, /* shiraoi.hokkaido.jp */ +23538, /* shiriuchi.hokkaido.jp */ +23548, /* sobetsu.hokkaido.jp */ +23057, /* sunagawa.hokkaido.jp */ +23556, /* taiki.hokkaido.jp */ +23562, /* takasu.hokkaido.jp */ +23569, /* takikawa.hokkaido.jp */ +23578, /* takinoue.hokkaido.jp */ +23587, /* teshikaga.hokkaido.jp */ +23597, /* tobetsu.hokkaido.jp */ +23605, /* tohma.hokkaido.jp */ +23611, /* tomakomai.hokkaido.jp */ +23621, /* tomari.hokkaido.jp */ +23628, /* toya.hokkaido.jp */ +23633, /* toyako.hokkaido.jp */ +23640, /* toyotomi.hokkaido.jp */ +23649, /* toyoura.hokkaido.jp */ +23657, /* tsubetsu.hokkaido.jp */ +23666, /* tsukigata.hokkaido.jp */ +23676, /* urakawa.hokkaido.jp */ +23684, /* urausu.hokkaido.jp */ +22958, /* uryu.hokkaido.jp */ +23691, /* utashinai.hokkaido.jp */ +23701, /* wakkanai.hokkaido.jp */ +23710, /* wassamu.hokkaido.jp */ +23718, /* yakumo.hokkaido.jp */ +23725, /* yoichi.hokkaido.jp */ +23732, /* aioi.hyogo.jp */ +23737, /* akashi.hyogo.jp */ +20887, /* ako.hyogo.jp */ +23744, /* amagasaki.hyogo.jp */ +22218, /* aogaki.hyogo.jp */ +23757, /* asago.hyogo.jp */ +21252, /* ashiya.hyogo.jp */ +23769, /* awaji.hyogo.jp */ +23775, /* fukusaki.hyogo.jp */ +23784, /* goshiki.hyogo.jp */ +23792, /* harima.hyogo.jp */ +23799, /* himeji.hyogo.jp */ +20648, /* ichikawa.hyogo.jp */ +23808, /* inagawa.hyogo.jp */ +23110, /* itami.hyogo.jp */ +23816, /* kakogawa.hyogo.jp */ +23825, /* kamigori.hyogo.jp */ +23032, /* kamikawa.hyogo.jp */ +23834, /* kasai.hyogo.jp */ +21374, /* kasuga.hyogo.jp */ +23840, /* kawanishi.hyogo.jp */ +23850, /* miki.hyogo.jp */ +23763, /* minamiawaji.hyogo.jp */ +23855, /* nishinomiya.hyogo.jp */ +21793, /* nishiwaki.hyogo.jp */ +21221, /* ono.hyogo.jp */ +23867, /* sanda.hyogo.jp */ +23873, /* sannan.hyogo.jp */ +23880, /* sasayama.hyogo.jp */ +23889, /* sayo.hyogo.jp */ +21563, /* shingu.hyogo.jp */ +23894, /* shinonsen.hyogo.jp */ +23904, /* shiso.hyogo.jp */ +23913, /* sumoto.hyogo.jp */ +23920, /* taishi.hyogo.jp */ +23929, /* taka.hyogo.jp */ +23934, /* takarazuka.hyogo.jp */ +23754, /* takasago.hyogo.jp */ +23945, /* takino.hyogo.jp */ +23955, /* tamba.hyogo.jp */ +23961, /* tatsuno.hyogo.jp */ +23969, /* toyooka.hyogo.jp */ +23977, /* yabu.hyogo.jp */ +23984, /* yashiro.hyogo.jp */ +23992, /* yoka.hyogo.jp */ +20175, /* yokawa.hyogo.jp */ + 5375, /* ami.ibaraki.jp */ +20079, /* asahi.ibaraki.jp */ +24003, /* bando.ibaraki.jp */ +24009, /* chikusei.ibaraki.jp */ 254, /* daigo.ibaraki.jp */ -23673, /* fujishiro.ibaraki.jp */ - 3921, /* hitachi.ibaraki.jp */ -23683, /* hitachinaka.ibaraki.jp */ -23695, /* hitachiomiya.ibaraki.jp */ -23708, /* hitachiota.ibaraki.jp */ -18683, /* ibaraki.ibaraki.jp */ - 7287, /* ina.ibaraki.jp */ -23725, /* inashiki.ibaraki.jp */ -20575, /* itako.ibaraki.jp */ -23734, /* iwama.ibaraki.jp */ -23740, /* joso.ibaraki.jp */ -23745, /* kamisu.ibaraki.jp */ -23752, /* kasama.ibaraki.jp */ -23761, /* kashima.ibaraki.jp */ -23769, /* kasumigaura.ibaraki.jp */ -20032, /* koga.ibaraki.jp */ -23781, /* miho.ibaraki.jp */ - 7919, /* mito.ibaraki.jp */ -23786, /* moriya.ibaraki.jp */ -21965, /* naka.ibaraki.jp */ -23793, /* namegata.ibaraki.jp */ -23802, /* oarai.ibaraki.jp */ -20330, /* ogawa.ibaraki.jp */ -23816, /* omitama.ibaraki.jp */ -23824, /* ryugasaki.ibaraki.jp */ -20886, /* sakai.ibaraki.jp */ -23834, /* sakuragawa.ibaraki.jp */ -23845, /* shimodate.ibaraki.jp */ -23855, /* shimotsuma.ibaraki.jp */ -23866, /* shirosato.ibaraki.jp */ -11439, /* sowa.ibaraki.jp */ -23876, /* suifu.ibaraki.jp */ -23882, /* takahagi.ibaraki.jp */ -23891, /* tamatsukuri.ibaraki.jp */ -19804, /* tokai.ibaraki.jp */ -23903, /* tomobe.ibaraki.jp */ - 1201, /* tone.ibaraki.jp */ -23910, /* toride.ibaraki.jp */ -23917, /* tsuchiura.ibaraki.jp */ -23927, /* tsukuba.ibaraki.jp */ -23935, /* uchihara.ibaraki.jp */ -23944, /* ushiku.ibaraki.jp */ -20624, /* yachiyo.ibaraki.jp */ -19470, /* yamagata.ibaraki.jp */ -23951, /* yawara.ibaraki.jp */ -23958, /* yuki.ibaraki.jp */ -23963, /* anamizu.ishikawa.jp */ -23971, /* hakui.ishikawa.jp */ -23977, /* hakusan.ishikawa.jp */ -23247, /* kaga.ishikawa.jp */ -23994, /* kahoku.ishikawa.jp */ -24001, /* kanazawa.ishikawa.jp */ -18582, /* kawakita.ishikawa.jp */ - 4642, /* komatsu.ishikawa.jp */ -24010, /* nakanoto.ishikawa.jp */ -24019, /* nanao.ishikawa.jp */ -24029, /* nomi.ishikawa.jp */ -24034, /* nonoichi.ishikawa.jp */ -24014, /* noto.ishikawa.jp */ -24045, /* shika.ishikawa.jp */ -24051, /* suzu.ishikawa.jp */ -24056, /* tsubata.ishikawa.jp */ -24064, /* tsurugi.ishikawa.jp */ -24072, /* uchinada.ishikawa.jp */ -20813, /* wajima.ishikawa.jp */ -24081, /* fudai.iwate.jp */ -24087, /* fujisawa.iwate.jp */ -24096, /* hanamaki.iwate.jp */ -24105, /* hiraizumi.iwate.jp */ -21423, /* hirono.iwate.jp */ -20185, /* ichinohe.iwate.jp */ -21889, /* ichinoseki.iwate.jp */ -24115, /* iwaizumi.iwate.jp */ -18701, /* iwate.iwate.jp */ -24124, /* joboji.iwate.jp */ -24131, /* kamaishi.iwate.jp */ -24140, /* kanegasaki.iwate.jp */ -24151, /* karumai.iwate.jp */ -24159, /* kawai.iwate.jp */ -24165, /* kitakami.iwate.jp */ -24174, /* kuji.iwate.jp */ -20168, /* kunohe.iwate.jp */ -24179, /* kuzumaki.iwate.jp */ -21095, /* miyako.iwate.jp */ -24188, /* mizusawa.iwate.jp */ -24197, /* morioka.iwate.jp */ -24205, /* ninohe.iwate.jp */ -20485, /* noda.iwate.jp */ -24212, /* ofunato.iwate.jp */ -24221, /* oshu.iwate.jp */ -24226, /* otsuchi.iwate.jp */ -24234, /* rikuzentakata.iwate.jp */ -20338, /* shiwa.iwate.jp */ -24248, /* shizukuishi.iwate.jp */ -24260, /* sumita.iwate.jp */ -24267, /* tanohata.iwate.jp */ -20875, /* tono.iwate.jp */ -24276, /* yahaba.iwate.jp */ -21312, /* yamada.iwate.jp */ -24283, /* ayagawa.kagawa.jp */ -24291, /* higashikagawa.kagawa.jp */ -24305, /* kanonji.kagawa.jp */ -24313, /* kotohira.kagawa.jp */ -24322, /* manno.kagawa.jp */ - 3388, /* marugame.kagawa.jp */ -24328, /* mitoyo.kagawa.jp */ -24335, /* naoshima.kagawa.jp */ -24344, /* sanuki.kagawa.jp */ -24351, /* tadotsu.kagawa.jp */ -24359, /* takamatsu.kagawa.jp */ -24369, /* tonosho.kagawa.jp */ -24025, /* uchinomi.kagawa.jp */ -24377, /* utazu.kagawa.jp */ -24383, /* zentsuji.kagawa.jp */ -24392, /* akune.kagoshima.jp */ -24399, /* amami.kagoshima.jp */ -24405, /* hioki.kagoshima.jp */ - 8291, /* isa.kagoshima.jp */ - 6657, /* isen.kagoshima.jp */ -22115, /* izumi.kagoshima.jp */ -18716, /* kagoshima.kagoshima.jp */ -24411, /* kanoya.kagoshima.jp */ -24418, /* kawanabe.kagoshima.jp */ -24427, /* kinko.kagoshima.jp */ -24433, /* kouyama.kagoshima.jp */ -24441, /* makurazaki.kagoshima.jp */ -23565, /* matsumoto.kagoshima.jp */ -19996, /* minamitane.kagoshima.jp */ -24452, /* nakatane.kagoshima.jp */ -24461, /* nishinoomote.kagoshima.jp */ -18844, /* satsumasendai.kagoshima.jp */ -24474, /* soo.kagoshima.jp */ -24478, /* tarumizu.kagoshima.jp */ -21306, /* yusui.kagoshima.jp */ -19937, /* aikawa.kanagawa.jp */ -24487, /* atsugi.kanagawa.jp */ -24494, /* ayase.kanagawa.jp */ -24500, /* chigasaki.kanagawa.jp */ -23719, /* ebina.kanagawa.jp */ -24087, /* fujisawa.kanagawa.jp */ -24510, /* hadano.kanagawa.jp */ -24517, /* hakone.kanagawa.jp */ -24524, /* hiratsuka.kanagawa.jp */ -24534, /* isehara.kanagawa.jp */ -24542, /* kaisei.kanagawa.jp */ -24549, /* kamakura.kanagawa.jp */ -24558, /* kiyokawa.kanagawa.jp */ -24567, /* matsuda.kanagawa.jp */ -24575, /* minamiashigara.kanagawa.jp */ -24590, /* miura.kanagawa.jp */ -24596, /* nakai.kanagawa.jp */ -24602, /* ninomiya.kanagawa.jp */ -24611, /* odawara.kanagawa.jp */ - 5486, /* oi.kanagawa.jp */ -24622, /* oiso.kanagawa.jp */ -22310, /* sagamihara.kanagawa.jp */ -22892, /* samukawa.kanagawa.jp */ -24627, /* tsukui.kanagawa.jp */ -24634, /* yamakita.kanagawa.jp */ -21691, /* yamato.kanagawa.jp */ -24643, /* yokosuka.kanagawa.jp */ -24652, /* yugawara.kanagawa.jp */ -19499, /* zama.kanagawa.jp */ -24661, /* zushi.kanagawa.jp */ - 1764, /* !city.kawasaki.jp */ -11410, /* *.kawasaki.jp */ -18687, /* aki.kochi.jp */ -24667, /* geisei.kochi.jp */ -22571, /* hidaka.kochi.jp */ -24674, /* higashitsuno.kochi.jp */ - 1516, /* ino.kochi.jp */ -24693, /* kagami.kochi.jp */ -20081, /* kami.kochi.jp */ -21262, /* kitagawa.kochi.jp */ -18755, /* kochi.kochi.jp */ -22314, /* mihara.kochi.jp */ -18907, /* motoyama.kochi.jp */ -24708, /* muroto.kochi.jp */ -24715, /* nahari.kochi.jp */ -24722, /* nakamura.kochi.jp */ -24731, /* nankoku.kochi.jp */ -24739, /* nishitosa.kochi.jp */ -24749, /* niyodogawa.kochi.jp */ -18756, /* ochi.kochi.jp */ -19831, /* okawa.kochi.jp */ -24760, /* otoyo.kochi.jp */ -24766, /* otsuki.kochi.jp */ -21379, /* sakawa.kochi.jp */ -24773, /* sukumo.kochi.jp */ -24780, /* susaki.kochi.jp */ -24744, /* tosa.kochi.jp */ -24787, /* tosashimizu.kochi.jp */ -24330, /* toyo.kochi.jp */ -20736, /* tsuno.kochi.jp */ -24799, /* umaji.kochi.jp */ -24805, /* yasuda.kochi.jp */ -24812, /* yusuhara.kochi.jp */ -24825, /* amakusa.kumamoto.jp */ -24833, /* arao.kumamoto.jp */ - 7344, /* aso.kumamoto.jp */ -24838, /* choyo.kumamoto.jp */ -24844, /* gyokuto.kumamoto.jp */ -24821, /* kamiamakusa.kumamoto.jp */ -24852, /* kikuchi.kumamoto.jp */ - 5553, /* kumamoto.kumamoto.jp */ -24860, /* mashiki.kumamoto.jp */ -24868, /* mifune.kumamoto.jp */ -24875, /* minamata.kumamoto.jp */ -24884, /* minamioguni.kumamoto.jp */ -24896, /* nagasu.kumamoto.jp */ -24903, /* nishihara.kumamoto.jp */ -24890, /* oguni.kumamoto.jp */ -20768, /* ozu.kumamoto.jp */ -23568, /* sumoto.kumamoto.jp */ -24913, /* takamori.kumamoto.jp */ - 7591, /* uki.kumamoto.jp */ +24018, /* fujishiro.ibaraki.jp */ + 3903, /* hitachi.ibaraki.jp */ +24028, /* hitachinaka.ibaraki.jp */ +24040, /* hitachiomiya.ibaraki.jp */ +24053, /* hitachiota.ibaraki.jp */ +19028, /* ibaraki.ibaraki.jp */ + 7276, /* ina.ibaraki.jp */ +24070, /* inashiki.ibaraki.jp */ +20920, /* itako.ibaraki.jp */ +24079, /* iwama.ibaraki.jp */ +24085, /* joso.ibaraki.jp */ +24090, /* kamisu.ibaraki.jp */ +24097, /* kasama.ibaraki.jp */ +24106, /* kashima.ibaraki.jp */ +24114, /* kasumigaura.ibaraki.jp */ +20377, /* koga.ibaraki.jp */ +24126, /* miho.ibaraki.jp */ + 7917, /* mito.ibaraki.jp */ +24131, /* moriya.ibaraki.jp */ +22310, /* naka.ibaraki.jp */ +24138, /* namegata.ibaraki.jp */ +24147, /* oarai.ibaraki.jp */ +20675, /* ogawa.ibaraki.jp */ +24161, /* omitama.ibaraki.jp */ +24169, /* ryugasaki.ibaraki.jp */ +21231, /* sakai.ibaraki.jp */ +24179, /* sakuragawa.ibaraki.jp */ +24190, /* shimodate.ibaraki.jp */ +24200, /* shimotsuma.ibaraki.jp */ +24211, /* shirosato.ibaraki.jp */ +11464, /* sowa.ibaraki.jp */ +24221, /* suifu.ibaraki.jp */ +24227, /* takahagi.ibaraki.jp */ +24236, /* tamatsukuri.ibaraki.jp */ +20149, /* tokai.ibaraki.jp */ +24248, /* tomobe.ibaraki.jp */ + 1202, /* tone.ibaraki.jp */ +24255, /* toride.ibaraki.jp */ +24262, /* tsuchiura.ibaraki.jp */ +24272, /* tsukuba.ibaraki.jp */ +24280, /* uchihara.ibaraki.jp */ +24289, /* ushiku.ibaraki.jp */ +20969, /* yachiyo.ibaraki.jp */ +19815, /* yamagata.ibaraki.jp */ +24296, /* yawara.ibaraki.jp */ +24303, /* yuki.ibaraki.jp */ +24308, /* anamizu.ishikawa.jp */ +24316, /* hakui.ishikawa.jp */ +24322, /* hakusan.ishikawa.jp */ +23592, /* kaga.ishikawa.jp */ +24339, /* kahoku.ishikawa.jp */ +24346, /* kanazawa.ishikawa.jp */ +18927, /* kawakita.ishikawa.jp */ + 4621, /* komatsu.ishikawa.jp */ +24355, /* nakanoto.ishikawa.jp */ +24364, /* nanao.ishikawa.jp */ +24374, /* nomi.ishikawa.jp */ +24379, /* nonoichi.ishikawa.jp */ +24359, /* noto.ishikawa.jp */ +24390, /* shika.ishikawa.jp */ +24396, /* suzu.ishikawa.jp */ +24401, /* tsubata.ishikawa.jp */ +24409, /* tsurugi.ishikawa.jp */ +24417, /* uchinada.ishikawa.jp */ +21158, /* wajima.ishikawa.jp */ +24426, /* fudai.iwate.jp */ +24432, /* fujisawa.iwate.jp */ +24441, /* hanamaki.iwate.jp */ +24450, /* hiraizumi.iwate.jp */ +21768, /* hirono.iwate.jp */ +20530, /* ichinohe.iwate.jp */ +22234, /* ichinoseki.iwate.jp */ +24460, /* iwaizumi.iwate.jp */ +19046, /* iwate.iwate.jp */ +24469, /* joboji.iwate.jp */ +24476, /* kamaishi.iwate.jp */ +24485, /* kanegasaki.iwate.jp */ +24496, /* karumai.iwate.jp */ +24504, /* kawai.iwate.jp */ +24510, /* kitakami.iwate.jp */ +24519, /* kuji.iwate.jp */ +20513, /* kunohe.iwate.jp */ +24524, /* kuzumaki.iwate.jp */ +21440, /* miyako.iwate.jp */ +24533, /* mizusawa.iwate.jp */ +24542, /* morioka.iwate.jp */ +24550, /* ninohe.iwate.jp */ +20830, /* noda.iwate.jp */ +24557, /* ofunato.iwate.jp */ +24566, /* oshu.iwate.jp */ +24571, /* otsuchi.iwate.jp */ +24579, /* rikuzentakata.iwate.jp */ +20683, /* shiwa.iwate.jp */ +24593, /* shizukuishi.iwate.jp */ +24605, /* sumita.iwate.jp */ +24612, /* tanohata.iwate.jp */ +21220, /* tono.iwate.jp */ +24621, /* yahaba.iwate.jp */ +21657, /* yamada.iwate.jp */ +24628, /* ayagawa.kagawa.jp */ +24636, /* higashikagawa.kagawa.jp */ +24650, /* kanonji.kagawa.jp */ +24658, /* kotohira.kagawa.jp */ +24667, /* manno.kagawa.jp */ + 3373, /* marugame.kagawa.jp */ +24673, /* mitoyo.kagawa.jp */ +24680, /* naoshima.kagawa.jp */ +24689, /* sanuki.kagawa.jp */ +24696, /* tadotsu.kagawa.jp */ +24704, /* takamatsu.kagawa.jp */ +24714, /* tonosho.kagawa.jp */ +24370, /* uchinomi.kagawa.jp */ +24722, /* utazu.kagawa.jp */ +24728, /* zentsuji.kagawa.jp */ +24737, /* akune.kagoshima.jp */ +24744, /* amami.kagoshima.jp */ +24750, /* hioki.kagoshima.jp */ + 8284, /* isa.kagoshima.jp */ + 6640, /* isen.kagoshima.jp */ +22460, /* izumi.kagoshima.jp */ +19061, /* kagoshima.kagoshima.jp */ +24756, /* kanoya.kagoshima.jp */ +24763, /* kawanabe.kagoshima.jp */ +24772, /* kinko.kagoshima.jp */ +24778, /* kouyama.kagoshima.jp */ +24786, /* makurazaki.kagoshima.jp */ +23910, /* matsumoto.kagoshima.jp */ +20341, /* minamitane.kagoshima.jp */ +24797, /* nakatane.kagoshima.jp */ +24806, /* nishinoomote.kagoshima.jp */ +19189, /* satsumasendai.kagoshima.jp */ +24819, /* soo.kagoshima.jp */ +24823, /* tarumizu.kagoshima.jp */ +21651, /* yusui.kagoshima.jp */ +20282, /* aikawa.kanagawa.jp */ +24832, /* atsugi.kanagawa.jp */ +24839, /* ayase.kanagawa.jp */ +24845, /* chigasaki.kanagawa.jp */ +24064, /* ebina.kanagawa.jp */ +24432, /* fujisawa.kanagawa.jp */ +24855, /* hadano.kanagawa.jp */ +24862, /* hakone.kanagawa.jp */ +24869, /* hiratsuka.kanagawa.jp */ +24879, /* isehara.kanagawa.jp */ +24887, /* kaisei.kanagawa.jp */ +24894, /* kamakura.kanagawa.jp */ +24903, /* kiyokawa.kanagawa.jp */ +24912, /* matsuda.kanagawa.jp */ +24920, /* minamiashigara.kanagawa.jp */ +24935, /* miura.kanagawa.jp */ +24941, /* nakai.kanagawa.jp */ +24947, /* ninomiya.kanagawa.jp */ +24956, /* odawara.kanagawa.jp */ + 5465, /* oi.kanagawa.jp */ +24967, /* oiso.kanagawa.jp */ +22655, /* sagamihara.kanagawa.jp */ +23237, /* samukawa.kanagawa.jp */ +24972, /* tsukui.kanagawa.jp */ +24979, /* yamakita.kanagawa.jp */ +22036, /* yamato.kanagawa.jp */ +24988, /* yokosuka.kanagawa.jp */ +24997, /* yugawara.kanagawa.jp */ +19844, /* zama.kanagawa.jp */ +25006, /* zushi.kanagawa.jp */ + 1765, /* !city.kawasaki.jp */ +11435, /* *.kawasaki.jp */ +19032, /* aki.kochi.jp */ +25012, /* geisei.kochi.jp */ +22916, /* hidaka.kochi.jp */ +25019, /* higashitsuno.kochi.jp */ + 1517, /* ino.kochi.jp */ +25038, /* kagami.kochi.jp */ +20426, /* kami.kochi.jp */ +21607, /* kitagawa.kochi.jp */ +19100, /* kochi.kochi.jp */ +22659, /* mihara.kochi.jp */ +19252, /* motoyama.kochi.jp */ +25053, /* muroto.kochi.jp */ +25060, /* nahari.kochi.jp */ +25067, /* nakamura.kochi.jp */ +25076, /* nankoku.kochi.jp */ +25084, /* nishitosa.kochi.jp */ +25094, /* niyodogawa.kochi.jp */ +19101, /* ochi.kochi.jp */ +20176, /* okawa.kochi.jp */ +25105, /* otoyo.kochi.jp */ +25111, /* otsuki.kochi.jp */ +21724, /* sakawa.kochi.jp */ +25118, /* sukumo.kochi.jp */ +25125, /* susaki.kochi.jp */ +25089, /* tosa.kochi.jp */ +25132, /* tosashimizu.kochi.jp */ +24675, /* toyo.kochi.jp */ +21081, /* tsuno.kochi.jp */ +25144, /* umaji.kochi.jp */ +25150, /* yasuda.kochi.jp */ +25157, /* yusuhara.kochi.jp */ +25170, /* amakusa.kumamoto.jp */ +25178, /* arao.kumamoto.jp */ + 7333, /* aso.kumamoto.jp */ +25183, /* choyo.kumamoto.jp */ +25189, /* gyokuto.kumamoto.jp */ +25166, /* kamiamakusa.kumamoto.jp */ +25197, /* kikuchi.kumamoto.jp */ + 5532, /* kumamoto.kumamoto.jp */ +25205, /* mashiki.kumamoto.jp */ +25213, /* mifune.kumamoto.jp */ +25220, /* minamata.kumamoto.jp */ +25229, /* minamioguni.kumamoto.jp */ +25241, /* nagasu.kumamoto.jp */ +25248, /* nishihara.kumamoto.jp */ +25235, /* oguni.kumamoto.jp */ +21113, /* ozu.kumamoto.jp */ +23913, /* sumoto.kumamoto.jp */ +25258, /* takamori.kumamoto.jp */ + 7589, /* uki.kumamoto.jp */ 630, /* uto.kumamoto.jp */ -24922, /* yamaga.kumamoto.jp */ -21691, /* yamato.kumamoto.jp */ -24929, /* yatsushiro.kumamoto.jp */ -22731, /* ayabe.kyoto.jp */ -24940, /* fukuchiyama.kyoto.jp */ -24952, /* higashiyama.kyoto.jp */ - 2275, /* ide.kyoto.jp */ - 6026, /* ine.kyoto.jp */ -24964, /* joyo.kyoto.jp */ -24969, /* kameoka.kyoto.jp */ -21833, /* kamo.kyoto.jp */ -18586, /* kita.kyoto.jp */ -24977, /* kizu.kyoto.jp */ -21102, /* kumiyama.kyoto.jp */ -23607, /* kyotamba.kyoto.jp */ -24982, /* kyotanabe.kyoto.jp */ -24992, /* kyotango.kyoto.jp */ -25001, /* maizuru.kyoto.jp */ -21088, /* minami.kyoto.jp */ -25009, /* minamiyamashiro.kyoto.jp */ -25025, /* miyazu.kyoto.jp */ -25032, /* muko.kyoto.jp */ -25037, /* nagaokakyo.kyoto.jp */ -25048, /* nakagyo.kyoto.jp */ -25056, /* nantan.kyoto.jp */ -25063, /* oyamazaki.kyoto.jp */ -25073, /* sakyo.kyoto.jp */ -25079, /* seika.kyoto.jp */ -24985, /* tanabe.kyoto.jp */ - 7253, /* uji.kyoto.jp */ -25085, /* ujitawara.kyoto.jp */ -25095, /* wazuka.kyoto.jp */ -25102, /* yamashina.kyoto.jp */ -25112, /* yawata.kyoto.jp */ -19734, /* asahi.mie.jp */ -25119, /* inabe.mie.jp */ - 2145, /* ise.mie.jp */ -25125, /* kameyama.mie.jp */ -25134, /* kawagoe.mie.jp */ -25142, /* kiho.mie.jp */ -25147, /* kisosaki.mie.jp */ -25156, /* kiwa.mie.jp */ -25161, /* komono.mie.jp */ -22303, /* kumano.mie.jp */ -25168, /* kuwana.mie.jp */ -25175, /* matsusaka.mie.jp */ -22064, /* meiwa.mie.jp */ -19662, /* mihama.mie.jp */ -25185, /* minamiise.mie.jp */ -25195, /* misugi.mie.jp */ -21104, /* miyama.mie.jp */ -16285, /* nabari.mie.jp */ -18637, /* shima.mie.jp */ -25202, /* suzuka.mie.jp */ -25209, /* tado.mie.jp */ -23211, /* taiki.mie.jp */ -20523, /* taki.mie.jp */ -25214, /* tamaki.mie.jp */ -25221, /* toba.mie.jp */ - 3309, /* tsu.mie.jp */ -21396, /* udono.mie.jp */ -25226, /* ureshino.mie.jp */ -25235, /* watarai.mie.jp */ -18572, /* yokkaichi.mie.jp */ -25243, /* furukawa.miyagi.jp */ -25252, /* higashimatsushima.miyagi.jp */ -25270, /* ishinomaki.miyagi.jp */ -25281, /* iwanuma.miyagi.jp */ -25289, /* kakuda.miyagi.jp */ -20081, /* kami.miyagi.jp */ -18735, /* kawasaki.miyagi.jp */ -25296, /* marumori.miyagi.jp */ -19846, /* matsushima.miyagi.jp */ -25305, /* minamisanriku.miyagi.jp */ -19989, /* misato.miyagi.jp */ -25319, /* murata.miyagi.jp */ -25326, /* natori.miyagi.jp */ -25333, /* ogawara.miyagi.jp */ -24316, /* ohira.miyagi.jp */ -25341, /* onagawa.miyagi.jp */ -20097, /* osaki.miyagi.jp */ -25349, /* rifu.miyagi.jp */ -25354, /* semine.miyagi.jp */ -25361, /* shibata.miyagi.jp */ -25369, /* shichikashuku.miyagi.jp */ -25383, /* shikama.miyagi.jp */ -25391, /* shiogama.miyagi.jp */ -25400, /* shiroishi.miyagi.jp */ -25410, /* tagajo.miyagi.jp */ -25417, /* taiwa.miyagi.jp */ -25426, /* tome.miyagi.jp */ -25431, /* tomiya.miyagi.jp */ -25438, /* wakuya.miyagi.jp */ -25445, /* watari.miyagi.jp */ -25452, /* yamamoto.miyagi.jp */ -25461, /* zao.miyagi.jp */ -20323, /* aya.miyazaki.jp */ -24687, /* ebino.miyazaki.jp */ -25471, /* gokase.miyazaki.jp */ -25478, /* hyuga.miyazaki.jp */ -25484, /* kadogawa.miyazaki.jp */ -25493, /* kawaminami.miyazaki.jp */ -25504, /* kijo.miyazaki.jp */ -21262, /* kitagawa.miyazaki.jp */ -21271, /* kitakata.miyazaki.jp */ -25509, /* kitaura.miyazaki.jp */ -25517, /* kobayashi.miyazaki.jp */ -25527, /* kunitomi.miyazaki.jp */ -18635, /* kushima.miyazaki.jp */ -25536, /* mimata.miyazaki.jp */ -21185, /* miyakonojo.miyazaki.jp */ -18774, /* miyazaki.miyazaki.jp */ - 6104, /* morotsuka.miyazaki.jp */ -25543, /* nichinan.miyazaki.jp */ -25552, /* nishimera.miyazaki.jp */ -25562, /* nobeoka.miyazaki.jp */ -25570, /* saito.miyazaki.jp */ -25576, /* shiiba.miyazaki.jp */ -25583, /* shintomi.miyazaki.jp */ -25592, /* takaharu.miyazaki.jp */ -25601, /* takanabe.miyazaki.jp */ -25610, /* takazaki.miyazaki.jp */ -20736, /* tsuno.miyazaki.jp */ - 3924, /* achi.nagano.jp */ -25626, /* agematsu.nagano.jp */ -25636, /* anan.nagano.jp */ -25641, /* aoki.nagano.jp */ -19734, /* asahi.nagano.jp */ -25646, /* azumino.nagano.jp */ -25654, /* chikuhoku.nagano.jp */ -25664, /* chikuma.nagano.jp */ -25672, /* chino.nagano.jp */ -25678, /* fujimi.nagano.jp */ -25685, /* hakuba.nagano.jp */ -19775, /* hara.nagano.jp */ -25692, /* hiraya.nagano.jp */ -25705, /* iida.nagano.jp */ -25710, /* iijima.nagano.jp */ -25717, /* iiyama.nagano.jp */ -25724, /* iizuna.nagano.jp */ -20853, /* ikeda.nagano.jp */ -25731, /* ikusaka.nagano.jp */ - 7287, /* ina.nagano.jp */ -25739, /* karuizawa.nagano.jp */ -25749, /* kawakami.nagano.jp */ -25758, /* kiso.nagano.jp */ -18629, /* kisofukushima.nagano.jp */ -25763, /* kitaaiki.nagano.jp */ -25772, /* komagane.nagano.jp */ -25781, /* komoro.nagano.jp */ -25788, /* matsukawa.nagano.jp */ -23565, /* matsumoto.nagano.jp */ -25798, /* miasa.nagano.jp */ -25804, /* minamiaiki.nagano.jp */ -25815, /* minamimaki.nagano.jp */ -25826, /* minamiminowa.nagano.jp */ -25832, /* minowa.nagano.jp */ -25839, /* miyada.nagano.jp */ -25846, /* miyota.nagano.jp */ -25853, /* mochizuki.nagano.jp */ -18790, /* nagano.nagano.jp */ -18728, /* nagawa.nagano.jp */ -25863, /* nagiso.nagano.jp */ -18707, /* nakagawa.nagano.jp */ -25870, /* nakano.nagano.jp */ -25877, /* nozawaonsen.nagano.jp */ -25889, /* obuse.nagano.jp */ -20330, /* ogawa.nagano.jp */ -25465, /* okaya.nagano.jp */ -25901, /* omachi.nagano.jp */ -19860, /* omi.nagano.jp */ -25908, /* ookuwa.nagano.jp */ -24043, /* ooshika.nagano.jp */ -20522, /* otaki.nagano.jp */ -25915, /* otari.nagano.jp */ +25267, /* yamaga.kumamoto.jp */ +22036, /* yamato.kumamoto.jp */ +25274, /* yatsushiro.kumamoto.jp */ +23076, /* ayabe.kyoto.jp */ +25285, /* fukuchiyama.kyoto.jp */ +25297, /* higashiyama.kyoto.jp */ + 2276, /* ide.kyoto.jp */ + 6009, /* ine.kyoto.jp */ +25309, /* joyo.kyoto.jp */ +25314, /* kameoka.kyoto.jp */ +22178, /* kamo.kyoto.jp */ +18931, /* kita.kyoto.jp */ +25322, /* kizu.kyoto.jp */ +21447, /* kumiyama.kyoto.jp */ +23952, /* kyotamba.kyoto.jp */ +25327, /* kyotanabe.kyoto.jp */ +25337, /* kyotango.kyoto.jp */ +25346, /* maizuru.kyoto.jp */ +21433, /* minami.kyoto.jp */ +25354, /* minamiyamashiro.kyoto.jp */ +25370, /* miyazu.kyoto.jp */ +25377, /* muko.kyoto.jp */ +25382, /* nagaokakyo.kyoto.jp */ +25393, /* nakagyo.kyoto.jp */ +25401, /* nantan.kyoto.jp */ +25408, /* oyamazaki.kyoto.jp */ +25418, /* sakyo.kyoto.jp */ +25424, /* seika.kyoto.jp */ +25330, /* tanabe.kyoto.jp */ + 7242, /* uji.kyoto.jp */ +25430, /* ujitawara.kyoto.jp */ +25440, /* wazuka.kyoto.jp */ +25447, /* yamashina.kyoto.jp */ +25457, /* yawata.kyoto.jp */ +20079, /* asahi.mie.jp */ +25464, /* inabe.mie.jp */ + 2146, /* ise.mie.jp */ +25470, /* kameyama.mie.jp */ +25479, /* kawagoe.mie.jp */ +25487, /* kiho.mie.jp */ +25492, /* kisosaki.mie.jp */ +25501, /* kiwa.mie.jp */ +25506, /* komono.mie.jp */ +22648, /* kumano.mie.jp */ +25513, /* kuwana.mie.jp */ +25520, /* matsusaka.mie.jp */ +22409, /* meiwa.mie.jp */ +20007, /* mihama.mie.jp */ +25530, /* minamiise.mie.jp */ +25540, /* misugi.mie.jp */ +21449, /* miyama.mie.jp */ +16630, /* nabari.mie.jp */ +18982, /* shima.mie.jp */ +25547, /* suzuka.mie.jp */ +25554, /* tado.mie.jp */ +23556, /* taiki.mie.jp */ +20868, /* taki.mie.jp */ +25559, /* tamaki.mie.jp */ +25566, /* toba.mie.jp */ + 3294, /* tsu.mie.jp */ +21741, /* udono.mie.jp */ +25571, /* ureshino.mie.jp */ +25580, /* watarai.mie.jp */ +18917, /* yokkaichi.mie.jp */ +25588, /* furukawa.miyagi.jp */ +25597, /* higashimatsushima.miyagi.jp */ +25615, /* ishinomaki.miyagi.jp */ +25626, /* iwanuma.miyagi.jp */ +25634, /* kakuda.miyagi.jp */ +20426, /* kami.miyagi.jp */ +19080, /* kawasaki.miyagi.jp */ +25641, /* marumori.miyagi.jp */ +20191, /* matsushima.miyagi.jp */ +25650, /* minamisanriku.miyagi.jp */ +20334, /* misato.miyagi.jp */ +25664, /* murata.miyagi.jp */ +25671, /* natori.miyagi.jp */ +25678, /* ogawara.miyagi.jp */ +24661, /* ohira.miyagi.jp */ +25686, /* onagawa.miyagi.jp */ +20442, /* osaki.miyagi.jp */ +25694, /* rifu.miyagi.jp */ +25699, /* semine.miyagi.jp */ +25706, /* shibata.miyagi.jp */ +25714, /* shichikashuku.miyagi.jp */ +25728, /* shikama.miyagi.jp */ +25736, /* shiogama.miyagi.jp */ +25745, /* shiroishi.miyagi.jp */ +25755, /* tagajo.miyagi.jp */ +25762, /* taiwa.miyagi.jp */ +25771, /* tome.miyagi.jp */ +25776, /* tomiya.miyagi.jp */ +25783, /* wakuya.miyagi.jp */ +25790, /* watari.miyagi.jp */ +25797, /* yamamoto.miyagi.jp */ +25806, /* zao.miyagi.jp */ +20668, /* aya.miyazaki.jp */ +25032, /* ebino.miyazaki.jp */ +25816, /* gokase.miyazaki.jp */ +25823, /* hyuga.miyazaki.jp */ +25829, /* kadogawa.miyazaki.jp */ +25838, /* kawaminami.miyazaki.jp */ +25849, /* kijo.miyazaki.jp */ +21607, /* kitagawa.miyazaki.jp */ +21616, /* kitakata.miyazaki.jp */ +25854, /* kitaura.miyazaki.jp */ +25862, /* kobayashi.miyazaki.jp */ +25872, /* kunitomi.miyazaki.jp */ +18980, /* kushima.miyazaki.jp */ +25881, /* mimata.miyazaki.jp */ +21530, /* miyakonojo.miyazaki.jp */ +19119, /* miyazaki.miyazaki.jp */ + 6087, /* morotsuka.miyazaki.jp */ +25888, /* nichinan.miyazaki.jp */ +25897, /* nishimera.miyazaki.jp */ +25907, /* nobeoka.miyazaki.jp */ +25915, /* saito.miyazaki.jp */ +25921, /* shiiba.miyazaki.jp */ +25928, /* shintomi.miyazaki.jp */ +25937, /* takaharu.miyazaki.jp */ +25946, /* takanabe.miyazaki.jp */ +25955, /* takazaki.miyazaki.jp */ +21081, /* tsuno.miyazaki.jp */ + 3906, /* achi.nagano.jp */ +25971, /* agematsu.nagano.jp */ +25981, /* anan.nagano.jp */ +25986, /* aoki.nagano.jp */ +20079, /* asahi.nagano.jp */ +25991, /* azumino.nagano.jp */ +25999, /* chikuhoku.nagano.jp */ +26009, /* chikuma.nagano.jp */ +26017, /* chino.nagano.jp */ +26023, /* fujimi.nagano.jp */ +26030, /* hakuba.nagano.jp */ +20120, /* hara.nagano.jp */ +26037, /* hiraya.nagano.jp */ +26050, /* iida.nagano.jp */ +26055, /* iijima.nagano.jp */ +26062, /* iiyama.nagano.jp */ +26069, /* iizuna.nagano.jp */ +21198, /* ikeda.nagano.jp */ +26076, /* ikusaka.nagano.jp */ + 7276, /* ina.nagano.jp */ +26084, /* karuizawa.nagano.jp */ +26094, /* kawakami.nagano.jp */ +26103, /* kiso.nagano.jp */ +18974, /* kisofukushima.nagano.jp */ +26108, /* kitaaiki.nagano.jp */ +26117, /* komagane.nagano.jp */ +26126, /* komoro.nagano.jp */ +26133, /* matsukawa.nagano.jp */ +23910, /* matsumoto.nagano.jp */ +26143, /* miasa.nagano.jp */ +26149, /* minamiaiki.nagano.jp */ +26160, /* minamimaki.nagano.jp */ +26171, /* minamiminowa.nagano.jp */ +26177, /* minowa.nagano.jp */ +26184, /* miyada.nagano.jp */ +26191, /* miyota.nagano.jp */ +26198, /* mochizuki.nagano.jp */ +19135, /* nagano.nagano.jp */ +19073, /* nagawa.nagano.jp */ +26208, /* nagiso.nagano.jp */ +19052, /* nakagawa.nagano.jp */ +26215, /* nakano.nagano.jp */ +26222, /* nozawaonsen.nagano.jp */ +26234, /* obuse.nagano.jp */ +20675, /* ogawa.nagano.jp */ +25810, /* okaya.nagano.jp */ +26246, /* omachi.nagano.jp */ +20205, /* omi.nagano.jp */ +26253, /* ookuwa.nagano.jp */ +24388, /* ooshika.nagano.jp */ +20867, /* otaki.nagano.jp */ +26260, /* otari.nagano.jp */ 154, /* sakae.nagano.jp */ -25921, /* sakaki.nagano.jp */ -25928, /* saku.nagano.jp */ -25933, /* sakuho.nagano.jp */ -25940, /* shimosuwa.nagano.jp */ -25895, /* shinanomachi.nagano.jp */ -25950, /* shiojiri.nagano.jp */ -25945, /* suwa.nagano.jp */ -25959, /* suzaka.nagano.jp */ -25966, /* takagi.nagano.jp */ -24913, /* takamori.nagano.jp */ -21918, /* takayama.nagano.jp */ -25973, /* tateshina.nagano.jp */ -23616, /* tatsuno.nagano.jp */ -25983, /* togakushi.nagano.jp */ -25993, /* togura.nagano.jp */ -19859, /* tomi.nagano.jp */ -26000, /* ueda.nagano.jp */ -20209, /* wada.nagano.jp */ -19470, /* yamagata.nagano.jp */ -26005, /* yamanouchi.nagano.jp */ -26016, /* yasaka.nagano.jp */ -26023, /* yasuoka.nagano.jp */ -26031, /* chijiwa.nagasaki.jp */ -23087, /* futsu.nagasaki.jp */ -26047, /* goto.nagasaki.jp */ -26052, /* hasami.nagasaki.jp */ -26059, /* hirado.nagasaki.jp */ - 8548, /* iki.nagasaki.jp */ -26066, /* isahaya.nagasaki.jp */ -26074, /* kawatana.nagasaki.jp */ -26083, /* kuchinotsu.nagasaki.jp */ -26094, /* matsuura.nagasaki.jp */ -18797, /* nagasaki.nagasaki.jp */ -20869, /* obama.nagasaki.jp */ -26103, /* omura.nagasaki.jp */ -19740, /* oseto.nagasaki.jp */ -26109, /* saikai.nagasaki.jp */ -26116, /* sasebo.nagasaki.jp */ -26123, /* seihi.nagasaki.jp */ -26129, /* shimabara.nagasaki.jp */ -26039, /* shinkamigoto.nagasaki.jp */ -26139, /* togitsu.nagasaki.jp */ -19848, /* tsushima.nagasaki.jp */ -26147, /* unzen.nagasaki.jp */ -23659, /* ando.nara.jp */ -26154, /* gose.nara.jp */ -11356, /* heguri.nara.jp */ -26159, /* higashiyoshino.nara.jp */ -26174, /* ikaruga.nara.jp */ -26182, /* ikoma.nara.jp */ -26188, /* kamikitayama.nara.jp */ -26201, /* kanmaki.nara.jp */ -26209, /* kashiba.nara.jp */ -26217, /* kashihara.nara.jp */ -26227, /* katsuragi.nara.jp */ -24159, /* kawai.nara.jp */ -25749, /* kawakami.nara.jp */ -23495, /* kawanishi.nara.jp */ -26237, /* koryo.nara.jp */ -20519, /* kurotaki.nara.jp */ -26245, /* mitsue.nara.jp */ -26252, /* miyake.nara.jp */ -17635, /* nara.nara.jp */ -26259, /* nosegawa.nara.jp */ -24127, /* oji.nara.jp */ -26268, /* ouda.nara.jp */ -26273, /* oyodo.nara.jp */ -26279, /* sakurai.nara.jp */ -26287, /* sango.nara.jp */ -26293, /* shimoichi.nara.jp */ -26303, /* shimokitayama.nara.jp */ -26317, /* shinjo.nara.jp */ -26324, /* soni.nara.jp */ -20344, /* takatori.nara.jp */ -26329, /* tawaramoto.nara.jp */ -26340, /* tenkawa.nara.jp */ -26348, /* tenri.nara.jp */ -24571, /* uda.nara.jp */ -21516, /* yamatokoriyama.nara.jp */ -26354, /* yamatotakada.nara.jp */ -26367, /* yamazoe.nara.jp */ -26166, /* yoshino.nara.jp */ - 3354, /* aga.niigata.jp */ -18791, /* agano.niigata.jp */ -26375, /* gosen.niigata.jp */ -26381, /* itoigawa.niigata.jp */ -26390, /* izumozaki.niigata.jp */ -26400, /* joetsu.niigata.jp */ -21833, /* kamo.niigata.jp */ -26407, /* kariwa.niigata.jp */ -26414, /* kashiwazaki.niigata.jp */ -26426, /* minamiuonuma.niigata.jp */ -26439, /* mitsuke.niigata.jp */ -26447, /* muika.niigata.jp */ -26453, /* murakami.niigata.jp */ -26462, /* myoko.niigata.jp */ -26468, /* nagaoka.niigata.jp */ -18806, /* niigata.niigata.jp */ -26476, /* ojiya.niigata.jp */ -19860, /* omi.niigata.jp */ -26482, /* sado.niigata.jp */ -19504, /* sanjo.niigata.jp */ -26487, /* seiro.niigata.jp */ -26493, /* seirou.niigata.jp */ -26500, /* sekikawa.niigata.jp */ -25361, /* shibata.niigata.jp */ -19964, /* tagami.niigata.jp */ -26509, /* tainai.niigata.jp */ -26516, /* tochio.niigata.jp */ -26523, /* tokamachi.niigata.jp */ -26533, /* tsubame.niigata.jp */ -26541, /* tsunan.niigata.jp */ -26432, /* uonuma.niigata.jp */ -26548, /* yahiko.niigata.jp */ -18814, /* yoita.niigata.jp */ -26555, /* yuzawa.niigata.jp */ -26562, /* beppu.oita.jp */ -26568, /* bungoono.oita.jp */ -26577, /* bungotakada.oita.jp */ -26589, /* hasama.oita.jp */ -26596, /* hiji.oita.jp */ -26601, /* himeshima.oita.jp */ -19524, /* hita.oita.jp */ -26243, /* kamitsue.oita.jp */ -26611, /* kokonoe.oita.jp */ -26619, /* kuju.oita.jp */ -26624, /* kunisaki.oita.jp */ - 7540, /* kusu.oita.jp */ -18815, /* oita.oita.jp */ -26633, /* saiki.oita.jp */ -26639, /* taketa.oita.jp */ -26646, /* tsukumi.oita.jp */ -17643, /* usa.oita.jp */ -26654, /* usuki.oita.jp */ -26660, /* yufu.oita.jp */ -26665, /* akaiwa.okayama.jp */ -26672, /* asakuchi.okayama.jp */ -26681, /* bizen.okayama.jp */ -26687, /* hayashima.okayama.jp */ -26699, /* ibara.okayama.jp */ -26705, /* kagamino.okayama.jp */ -26714, /* kasaoka.okayama.jp */ -20257, /* kibichuo.okayama.jp */ -26722, /* kumenan.okayama.jp */ -26730, /* kurashiki.okayama.jp */ -26740, /* maniwa.okayama.jp */ -26747, /* misaki.okayama.jp */ -20108, /* nagi.okayama.jp */ -26760, /* niimi.okayama.jp */ -26766, /* nishiawakura.okayama.jp */ -18820, /* okayama.okayama.jp */ -26779, /* satosho.okayama.jp */ -26787, /* setouchi.okayama.jp */ -26317, /* shinjo.okayama.jp */ -26796, /* shoo.okayama.jp */ -26801, /* soja.okayama.jp */ -26806, /* takahashi.okayama.jp */ -26816, /* tamano.okayama.jp */ -20751, /* tsuyama.okayama.jp */ - 4539, /* wake.okayama.jp */ -26823, /* yakage.okayama.jp */ -26833, /* aguni.okinawa.jp */ -26839, /* ginowan.okinawa.jp */ -26847, /* ginoza.okinawa.jp */ -26854, /* gushikami.okinawa.jp */ -26864, /* haebaru.okinawa.jp */ -20987, /* higashi.okinawa.jp */ -26872, /* hirara.okinawa.jp */ -26879, /* iheya.okinawa.jp */ -26885, /* ishigaki.okinawa.jp */ -18692, /* ishikawa.okinawa.jp */ - 5195, /* itoman.okinawa.jp */ -26894, /* izena.okinawa.jp */ -26900, /* kadena.okinawa.jp */ - 7316, /* kin.okinawa.jp */ -26907, /* kitadaito.okinawa.jp */ -26917, /* kitanakagusuku.okinawa.jp */ -26932, /* kumejima.okinawa.jp */ -26941, /* kunigami.okinawa.jp */ -26950, /* minamidaito.okinawa.jp */ -19701, /* motobu.okinawa.jp */ -26964, /* nago.okinawa.jp */ -11881, /* naha.okinawa.jp */ -26921, /* nakagusuku.okinawa.jp */ -26969, /* nakijin.okinawa.jp */ -26977, /* nanjo.okinawa.jp */ -24903, /* nishihara.okinawa.jp */ -26983, /* ogimi.okinawa.jp */ - 5966, /* okinawa.okinawa.jp */ -26990, /* onna.okinawa.jp */ -26995, /* shimoji.okinawa.jp */ -27003, /* taketomi.okinawa.jp */ -27012, /* tarama.okinawa.jp */ -27019, /* tokashiki.okinawa.jp */ -27029, /* tomigusuku.okinawa.jp */ -27040, /* tonaki.okinawa.jp */ -27047, /* urasoe.okinawa.jp */ -27054, /* uruma.okinawa.jp */ -27060, /* yaese.okinawa.jp */ -27066, /* yomitan.okinawa.jp */ -27074, /* yonabaru.okinawa.jp */ -26830, /* yonaguni.okinawa.jp */ -24398, /* zamami.okinawa.jp */ -27083, /* abeno.osaka.jp */ -27089, /* chihayaakasaka.osaka.jp */ -20261, /* chuo.osaka.jp */ -26911, /* daito.osaka.jp */ -27104, /* fujiidera.osaka.jp */ -27114, /* habikino.osaka.jp */ -27123, /* hannan.osaka.jp */ -27130, /* higashiosaka.osaka.jp */ -19669, /* higashisumiyoshi.osaka.jp */ -27143, /* higashiyodogawa.osaka.jp */ -27159, /* hirakata.osaka.jp */ -18683, /* ibaraki.osaka.jp */ -20853, /* ikeda.osaka.jp */ -22115, /* izumi.osaka.jp */ -27168, /* izumiotsu.osaka.jp */ -27178, /* izumisano.osaka.jp */ -27188, /* kadoma.osaka.jp */ -27195, /* kaizuka.osaka.jp */ -25635, /* kanan.osaka.jp */ -27203, /* kashiwara.osaka.jp */ -27213, /* katano.osaka.jp */ -18783, /* kawachinagano.osaka.jp */ -27220, /* kishiwada.osaka.jp */ -18586, /* kita.osaka.jp */ -27230, /* kumatori.osaka.jp */ -27239, /* matsubara.osaka.jp */ -27254, /* minato.osaka.jp */ -27261, /* minoh.osaka.jp */ -26747, /* misaki.osaka.jp */ -27267, /* moriguchi.osaka.jp */ -27277, /* neyagawa.osaka.jp */ -21149, /* nishi.osaka.jp */ - 7109, /* nose.osaka.jp */ -27286, /* osakasayama.osaka.jp */ -20886, /* sakai.osaka.jp */ -21006, /* sayama.osaka.jp */ -27298, /* sennan.osaka.jp */ -27305, /* settsu.osaka.jp */ -27312, /* shijonawate.osaka.jp */ -27324, /* shimamoto.osaka.jp */ -27334, /* suita.osaka.jp */ -27340, /* tadaoka.osaka.jp */ -23575, /* taishi.osaka.jp */ -27348, /* tajiri.osaka.jp */ -27355, /* takaishi.osaka.jp */ -27364, /* takatsuki.osaka.jp */ -27374, /* tondabayashi.osaka.jp */ -27387, /* toyonaka.osaka.jp */ -27396, /* toyono.osaka.jp */ -27403, /* yao.osaka.jp */ -27407, /* ariake.saga.jp */ -20479, /* arita.saga.jp */ -27414, /* fukudomi.saga.jp */ -27423, /* genkai.saga.jp */ -27430, /* hamatama.saga.jp */ -20839, /* hizen.saga.jp */ -27439, /* imari.saga.jp */ -27445, /* kamimine.saga.jp */ -27454, /* kanzaki.saga.jp */ -27462, /* karatsu.saga.jp */ -23761, /* kashima.saga.jp */ -21813, /* kitagata.saga.jp */ -27470, /* kitahata.saga.jp */ -27479, /* kiyama.saga.jp */ -27486, /* kouhoku.saga.jp */ -27494, /* kyuragi.saga.jp */ -27502, /* nishiarita.saga.jp */ - 3509, /* ogi.saga.jp */ -25901, /* omachi.saga.jp */ -21943, /* ouchi.saga.jp */ - 3353, /* saga.saga.jp */ -25400, /* shiroishi.saga.jp */ -27513, /* taku.saga.jp */ -19768, /* tara.saga.jp */ -21856, /* tosu.saga.jp */ -27518, /* yoshinogari.saga.jp */ -27530, /* arakawa.saitama.jp */ -26017, /* asaka.saitama.jp */ -27545, /* chichibu.saitama.jp */ -25678, /* fujimi.saitama.jp */ -27554, /* fujimino.saitama.jp */ -27563, /* fukaya.saitama.jp */ -27570, /* hanno.saitama.jp */ -27576, /* hanyu.saitama.jp */ -27582, /* hasuda.saitama.jp */ -27589, /* hatogaya.saitama.jp */ -27598, /* hatoyama.saitama.jp */ -22571, /* hidaka.saitama.jp */ -27538, /* higashichichibu.saitama.jp */ -20742, /* higashimatsuyama.saitama.jp */ -19924, /* honjo.saitama.jp */ - 7287, /* ina.saitama.jp */ -27607, /* iruma.saitama.jp */ -27613, /* iwatsuki.saitama.jp */ -27622, /* kamiizumi.saitama.jp */ -22687, /* kamikawa.saitama.jp */ -27632, /* kamisato.saitama.jp */ -27641, /* kasukabe.saitama.jp */ -25134, /* kawagoe.saitama.jp */ -27650, /* kawaguchi.saitama.jp */ -27660, /* kawajima.saitama.jp */ -27669, /* kazo.saitama.jp */ -27674, /* kitamoto.saitama.jp */ -27683, /* koshigaya.saitama.jp */ -27693, /* kounosu.saitama.jp */ -27701, /* kuki.saitama.jp */ -27706, /* kumagaya.saitama.jp */ -27715, /* matsubushi.saitama.jp */ -27726, /* minano.saitama.jp */ -19989, /* misato.saitama.jp */ -23637, /* miyashiro.saitama.jp */ -19678, /* miyoshi.saitama.jp */ -27733, /* moroyama.saitama.jp */ -27742, /* nagatoro.saitama.jp */ -27751, /* namegawa.saitama.jp */ -27760, /* niiza.saitama.jp */ -27766, /* ogano.saitama.jp */ -20330, /* ogawa.saitama.jp */ -26153, /* ogose.saitama.jp */ -27772, /* okegawa.saitama.jp */ -19578, /* omiya.saitama.jp */ -20522, /* otaki.saitama.jp */ -27780, /* ranzan.saitama.jp */ -24700, /* ryokami.saitama.jp */ -18828, /* saitama.saitama.jp */ -27787, /* sakado.saitama.jp */ -27794, /* satte.saitama.jp */ -21006, /* sayama.saitama.jp */ -19602, /* shiki.saitama.jp */ -27800, /* shiraoka.saitama.jp */ -27809, /* soka.saitama.jp */ -27814, /* sugito.saitama.jp */ -27821, /* toda.saitama.jp */ -27826, /* tokigawa.saitama.jp */ -27835, /* tokorozawa.saitama.jp */ -27846, /* tsurugashima.saitama.jp */ -27859, /* urawa.saitama.jp */ -27865, /* warabi.saitama.jp */ -27872, /* yashio.saitama.jp */ -27879, /* yokoze.saitama.jp */ -22197, /* yono.saitama.jp */ -27886, /* yorii.saitama.jp */ -27896, /* yoshida.saitama.jp */ -27904, /* yoshikawa.saitama.jp */ -27914, /* yoshimi.saitama.jp */ -27922, /* aisho.shiga.jp */ -16366, /* gamo.shiga.jp */ -27928, /* higashiomi.shiga.jp */ -27939, /* hikone.shiga.jp */ -27946, /* koka.shiga.jp */ -19656, /* konan.shiga.jp */ -27951, /* kosei.shiga.jp */ -21196, /* koto.shiga.jp */ -22047, /* kusatsu.shiga.jp */ -26697, /* maibara.shiga.jp */ -27957, /* moriyama.shiga.jp */ -27966, /* nagahama.shiga.jp */ -27975, /* nishiazai.shiga.jp */ -27985, /* notogawa.shiga.jp */ -27994, /* omihachiman.shiga.jp */ -21288, /* otsu.shiga.jp */ -28012, /* ritto.shiga.jp */ -28018, /* ryuoh.shiga.jp */ -23759, /* takashima.shiga.jp */ -27364, /* takatsuki.shiga.jp */ -28024, /* torahime.shiga.jp */ -28033, /* toyosato.shiga.jp */ -20609, /* yasu.shiga.jp */ -25967, /* akagi.shimane.jp */ -10609, /* ama.shimane.jp */ -28006, /* gotsu.shimane.jp */ -28042, /* hamada.shimane.jp */ -28049, /* higashiizumo.shimane.jp */ -18694, /* hikawa.shimane.jp */ -28062, /* hikimi.shimane.jp */ -28056, /* izumo.shimane.jp */ -28078, /* kakinoki.shimane.jp */ -28087, /* masuda.shimane.jp */ -21245, /* matsue.shimane.jp */ -19989, /* misato.shimane.jp */ -28094, /* nishinoshima.shimane.jp */ -28107, /* ohda.shimane.jp */ -28112, /* okinoshima.shimane.jp */ -28069, /* okuizumo.shimane.jp */ -18864, /* shimane.shimane.jp */ -28123, /* tamayu.shimane.jp */ -28130, /* tsuwano.shimane.jp */ -28138, /* unnan.shimane.jp */ -23373, /* yakumo.shimane.jp */ -28144, /* yasugi.shimane.jp */ -28151, /* yatsuka.shimane.jp */ -21257, /* arai.shizuoka.jp */ -23652, /* atami.shizuoka.jp */ -23078, /* fuji.shizuoka.jp */ -28159, /* fujieda.shizuoka.jp */ -28167, /* fujikawa.shizuoka.jp */ -28176, /* fujinomiya.shizuoka.jp */ -28187, /* fukuroi.shizuoka.jp */ - 5289, /* gotemba.shizuoka.jp */ -28195, /* haibara.shizuoka.jp */ -28203, /* hamamatsu.shizuoka.jp */ -28213, /* higashiizu.shizuoka.jp */ - 7920, /* ito.shizuoka.jp */ -28224, /* iwata.shizuoka.jp */ -21559, /* izu.shizuoka.jp */ -28230, /* izunokuni.shizuoka.jp */ -28240, /* kakegawa.shizuoka.jp */ -28249, /* kannami.shizuoka.jp */ -28257, /* kawanehon.shizuoka.jp */ -28267, /* kawazu.shizuoka.jp */ -28274, /* kikugawa.shizuoka.jp */ -28283, /* kosai.shizuoka.jp */ -28289, /* makinohara.shizuoka.jp */ -28300, /* matsuzaki.shizuoka.jp */ -28310, /* minamiizu.shizuoka.jp */ -21545, /* mishima.shizuoka.jp */ -28320, /* morimachi.shizuoka.jp */ -28330, /* nishiizu.shizuoka.jp */ -28339, /* numazu.shizuoka.jp */ -28346, /* omaezaki.shizuoka.jp */ -28355, /* shimada.shizuoka.jp */ -22782, /* shimizu.shizuoka.jp */ - 5473, /* shimoda.shizuoka.jp */ -18872, /* shizuoka.shizuoka.jp */ -28363, /* susono.shizuoka.jp */ -28370, /* yaizu.shizuoka.jp */ -27896, /* yoshida.shizuoka.jp */ -23985, /* ashikaga.tochigi.jp */ -11640, /* bato.tochigi.jp */ -28376, /* haga.tochigi.jp */ -28381, /* ichikai.tochigi.jp */ -28389, /* iwafune.tochigi.jp */ -28397, /* kaminokawa.tochigi.jp */ -28408, /* kanuma.tochigi.jp */ -28415, /* karasuyama.tochigi.jp */ -24619, /* kuroiso.tochigi.jp */ -28426, /* mashiko.tochigi.jp */ -28434, /* mibu.tochigi.jp */ -28439, /* moka.tochigi.jp */ -28444, /* motegi.tochigi.jp */ -28451, /* nasu.tochigi.jp */ -28456, /* nasushiobara.tochigi.jp */ -28469, /* nikko.tochigi.jp */ -28475, /* nishikata.tochigi.jp */ - 3508, /* nogi.tochigi.jp */ -24316, /* ohira.tochigi.jp */ -28485, /* ohtawara.tochigi.jp */ -18910, /* oyama.tochigi.jp */ - 6909, /* sakura.tochigi.jp */ -27183, /* sano.tochigi.jp */ -28494, /* shimotsuke.tochigi.jp */ -28505, /* shioya.tochigi.jp */ -28512, /* takanezawa.tochigi.jp */ -18881, /* tochigi.tochigi.jp */ -28523, /* tsuga.tochigi.jp */ -28529, /* ujiie.tochigi.jp */ -28535, /* utsunomiya.tochigi.jp */ -28546, /* yaita.tochigi.jp */ -24108, /* aizumi.tokushima.jp */ -25636, /* anan.tokushima.jp */ -18602, /* ichiba.tokushima.jp */ -28552, /* itano.tokushima.jp */ -20659, /* kainan.tokushima.jp */ -19844, /* komatsushima.tokushima.jp */ -28558, /* matsushige.tokushima.jp */ -28569, /* mima.tokushima.jp */ -21088, /* minami.tokushima.jp */ -19678, /* miyoshi.tokushima.jp */ -28574, /* mugi.tokushima.jp */ -18707, /* nakagawa.tokushima.jp */ -28579, /* naruto.tokushima.jp */ -28586, /* sanagochi.tokushima.jp */ -28596, /* shishikui.tokushima.jp */ -18889, /* tokushima.tokushima.jp */ -28606, /* wajiki.tokushima.jp */ -25619, /* adachi.tokyo.jp */ -28613, /* akiruno.tokyo.jp */ -28621, /* akishima.tokyo.jp */ -28630, /* aogashima.tokyo.jp */ -27530, /* arakawa.tokyo.jp */ -28640, /* bunkyo.tokyo.jp */ -21970, /* chiyoda.tokyo.jp */ -28647, /* chofu.tokyo.jp */ -20261, /* chuo.tokyo.jp */ -23808, /* edogawa.tokyo.jp */ -22234, /* fuchu.tokyo.jp */ -28653, /* fussa.tokyo.jp */ -28659, /* hachijo.tokyo.jp */ -28667, /* hachioji.tokyo.jp */ -28676, /* hamura.tokyo.jp */ -21071, /* higashikurume.tokyo.jp */ -28683, /* higashimurayama.tokyo.jp */ -21684, /* higashiyamato.tokyo.jp */ -20473, /* hino.tokyo.jp */ -28699, /* hinode.tokyo.jp */ -28706, /* hinohara.tokyo.jp */ -26754, /* inagi.tokyo.jp */ -28715, /* itabashi.tokyo.jp */ -28724, /* katsushika.tokyo.jp */ -18586, /* kita.tokyo.jp */ -28735, /* kiyose.tokyo.jp */ -28742, /* kodaira.tokyo.jp */ -28750, /* koganei.tokyo.jp */ -28758, /* kokubunji.tokyo.jp */ -28768, /* komae.tokyo.jp */ -21196, /* koto.tokyo.jp */ -28774, /* kouzushima.tokyo.jp */ -28785, /* kunitachi.tokyo.jp */ -21753, /* machida.tokyo.jp */ -28795, /* meguro.tokyo.jp */ -27254, /* minato.tokyo.jp */ -23582, /* mitaka.tokyo.jp */ -28802, /* mizuho.tokyo.jp */ -28809, /* musashimurayama.tokyo.jp */ -28825, /* musashino.tokyo.jp */ -25870, /* nakano.tokyo.jp */ -28835, /* nerima.tokyo.jp */ -28842, /* ogasawara.tokyo.jp */ -28852, /* okutama.tokyo.jp */ - 1692, /* ome.tokyo.jp */ -18661, /* oshima.tokyo.jp */ - 7969, /* ota.tokyo.jp */ -28873, /* setagaya.tokyo.jp */ -28882, /* shibuya.tokyo.jp */ -23461, /* shinagawa.tokyo.jp */ -28890, /* shinjuku.tokyo.jp */ -28899, /* suginami.tokyo.jp */ -28908, /* sumida.tokyo.jp */ -28915, /* tachikawa.tokyo.jp */ -28925, /* taito.tokyo.jp */ -18831, /* tama.tokyo.jp */ -28865, /* toshima.tokyo.jp */ -28931, /* chizu.tottori.jp */ -20473, /* hino.tottori.jp */ -28937, /* kawahara.tottori.jp */ - 3456, /* koge.tottori.jp */ -28946, /* kotoura.tottori.jp */ -28954, /* misasa.tottori.jp */ -28961, /* nanbu.tottori.jp */ -25543, /* nichinan.tottori.jp */ -27249, /* sakaiminato.tottori.jp */ -18899, /* tottori.tottori.jp */ -20900, /* wakasa.tottori.jp */ -25027, /* yazu.tottori.jp */ -26962, /* yonago.tottori.jp */ -19734, /* asahi.toyama.jp */ -22234, /* fuchu.toyama.jp */ -28967, /* fukumitsu.toyama.jp */ -28977, /* funahashi.toyama.jp */ -27917, /* himi.toyama.jp */ -22784, /* imizu.toyama.jp */ -21089, /* inami.toyama.jp */ -28987, /* johana.toyama.jp */ -28994, /* kamiichi.toyama.jp */ -29003, /* kurobe.toyama.jp */ -29010, /* nakaniikawa.toyama.jp */ -29022, /* namerikawa.toyama.jp */ -29033, /* nanto.toyama.jp */ -29039, /* nyuzen.toyama.jp */ -29046, /* oyabe.toyama.jp */ -29052, /* taira.toyama.jp */ -29058, /* takaoka.toyama.jp */ -20581, /* tateyama.toyama.jp */ -29066, /* toga.toyama.jp */ -29071, /* tonami.toyama.jp */ -18909, /* toyama.toyama.jp */ -29078, /* unazuki.toyama.jp */ -20767, /* uozu.toyama.jp */ -21312, /* yamada.toyama.jp */ -29086, /* arida.wakayama.jp */ -29092, /* aridagawa.wakayama.jp */ -29102, /* gobo.wakayama.jp */ -29107, /* hashimoto.wakayama.jp */ -22571, /* hidaka.wakayama.jp */ -29117, /* hirogawa.wakayama.jp */ -21089, /* inami.wakayama.jp */ -29126, /* iwade.wakayama.jp */ -20659, /* kainan.wakayama.jp */ -29132, /* kamitonda.wakayama.jp */ -26227, /* katsuragi.wakayama.jp */ -21822, /* kimino.wakayama.jp */ -29142, /* kinokawa.wakayama.jp */ -26192, /* kitayama.wakayama.jp */ -29151, /* koya.wakayama.jp */ -10620, /* koza.wakayama.jp */ -29156, /* kozagawa.wakayama.jp */ -29165, /* kudoyama.wakayama.jp */ -29174, /* kushimoto.wakayama.jp */ -19662, /* mihama.wakayama.jp */ -19989, /* misato.wakayama.jp */ -20353, /* nachikatsuura.wakayama.jp */ -21218, /* shingu.wakayama.jp */ -29184, /* shirahama.wakayama.jp */ -29194, /* taiji.wakayama.jp */ -24985, /* tanabe.wakayama.jp */ -18916, /* wakayama.wakayama.jp */ -29200, /* yuasa.wakayama.jp */ -29206, /* yura.wakayama.jp */ -19734, /* asahi.yamagata.jp */ -29211, /* funagata.yamagata.jp */ -29220, /* higashine.yamagata.jp */ - 2274, /* iide.yamagata.jp */ -23994, /* kahoku.yamagata.jp */ -29230, /* kaminoyama.yamagata.jp */ -21479, /* kaneyama.yamagata.jp */ -23495, /* kawanishi.yamagata.jp */ -29241, /* mamurogawa.yamagata.jp */ -22689, /* mikawa.yamagata.jp */ -28690, /* murayama.yamagata.jp */ -29252, /* nagai.yamagata.jp */ -29258, /* nakayama.yamagata.jp */ -29267, /* nanyo.yamagata.jp */ -18691, /* nishikawa.yamagata.jp */ -29273, /* obanazawa.yamagata.jp */ - 1676, /* oe.yamagata.jp */ -24890, /* oguni.yamagata.jp */ -29283, /* ohkura.yamagata.jp */ -29290, /* oishida.yamagata.jp */ -29298, /* sagae.yamagata.jp */ -29304, /* sakata.yamagata.jp */ -29311, /* sakegawa.yamagata.jp */ -26317, /* shinjo.yamagata.jp */ -29320, /* shirataka.yamagata.jp */ -20666, /* shonai.yamagata.jp */ -29330, /* takahata.yamagata.jp */ -29339, /* tendo.yamagata.jp */ -29345, /* tozawa.yamagata.jp */ -29352, /* tsuruoka.yamagata.jp */ -19470, /* yamagata.yamagata.jp */ -29361, /* yamanobe.yamagata.jp */ -29370, /* yonezawa.yamagata.jp */ -29379, /* yuza.yamagata.jp */ -22430, /* abu.yamaguchi.jp */ -23886, /* hagi.yamaguchi.jp */ -20641, /* hikari.yamaguchi.jp */ -28648, /* hofu.yamaguchi.jp */ -29384, /* iwakuni.yamaguchi.jp */ -29392, /* kudamatsu.yamaguchi.jp */ -29402, /* mitou.yamaguchi.jp */ -29408, /* nagato.yamaguchi.jp */ -18661, /* oshima.yamaguchi.jp */ -29415, /* shimonoseki.yamaguchi.jp */ -29427, /* shunan.yamaguchi.jp */ -29434, /* tabuse.yamaguchi.jp */ -29441, /* tokuyama.yamaguchi.jp */ - 7966, /* toyota.yamaguchi.jp */ - 8059, /* ube.yamaguchi.jp */ -29450, /* yuu.yamaguchi.jp */ -20261, /* chuo.yamanashi.jp */ -29454, /* doshi.yamanashi.jp */ -29460, /* fuefuki.yamanashi.jp */ -28167, /* fujikawa.yamanashi.jp */ -20796, /* fujikawaguchiko.yamanashi.jp */ -27892, /* fujiyoshida.yamanashi.jp */ -29468, /* hayakawa.yamanashi.jp */ -22618, /* hokuto.yamanashi.jp */ -29477, /* ichikawamisato.yamanashi.jp */ -19806, /* kai.yamanashi.jp */ -29492, /* kofu.yamanashi.jp */ -24220, /* koshu.yamanashi.jp */ -29497, /* kosuge.yamanashi.jp */ -29504, /* minami-alps.yamanashi.jp */ -29516, /* minobu.yamanashi.jp */ -29523, /* nakamichi.yamanashi.jp */ -28961, /* nanbu.yamanashi.jp */ -29533, /* narusawa.yamanashi.jp */ -29542, /* nirasaki.yamanashi.jp */ -29551, /* nishikatsura.yamanashi.jp */ -26167, /* oshino.yamanashi.jp */ -24766, /* otsuki.yamanashi.jp */ -21625, /* showa.yamanashi.jp */ -29564, /* tabayama.yamanashi.jp */ -29573, /* tsuru.yamanashi.jp */ -29579, /* uenohara.yamanashi.jp */ -29588, /* yamanakako.yamanashi.jp */ -19489, /* yamanashi.yamanashi.jp */ - 985, /* biz.ki */ - 1913, /* com.ki */ - 2624, /* edu.ki */ - 3686, /* gov.ki */ - 3167, /* info.ki */ - 4185, /* net.ki */ - 6070, /* org.ki */ - 3548, /* ass.km */ -11614, /* asso.km */ - 1913, /* com.km */ - 2047, /* coop.km */ - 2624, /* edu.km */ -11627, /* gouv.km */ - 3686, /* gov.km */ -15415, /* medecin.km */ - 4195, /* mil.km */ - 5998, /* nom.km */ -15423, /* notaires.km */ - 6070, /* org.km */ -29599, /* pharmaciens.km */ -15443, /* prd.km */ -11816, /* presse.km */ - 7910, /* tm.km */ -15447, /* veterinaire.km */ - 2624, /* edu.kn */ - 3686, /* gov.kn */ - 4185, /* net.kn */ - 6070, /* org.kn */ - 1913, /* com.kp */ - 2624, /* edu.kp */ - 3686, /* gov.kp */ - 6070, /* org.kp */ -29611, /* rep.kp */ -16671, /* tra.kp */ +26266, /* sakaki.nagano.jp */ +26273, /* saku.nagano.jp */ +26278, /* sakuho.nagano.jp */ +26285, /* shimosuwa.nagano.jp */ +26240, /* shinanomachi.nagano.jp */ +26295, /* shiojiri.nagano.jp */ +26290, /* suwa.nagano.jp */ +26304, /* suzaka.nagano.jp */ +26311, /* takagi.nagano.jp */ +25258, /* takamori.nagano.jp */ +22263, /* takayama.nagano.jp */ +26318, /* tateshina.nagano.jp */ +23961, /* tatsuno.nagano.jp */ +26328, /* togakushi.nagano.jp */ +26338, /* togura.nagano.jp */ +20204, /* tomi.nagano.jp */ +26345, /* ueda.nagano.jp */ +20554, /* wada.nagano.jp */ +19815, /* yamagata.nagano.jp */ +26350, /* yamanouchi.nagano.jp */ +26361, /* yasaka.nagano.jp */ +26368, /* yasuoka.nagano.jp */ +26376, /* chijiwa.nagasaki.jp */ +23432, /* futsu.nagasaki.jp */ +26392, /* goto.nagasaki.jp */ +26397, /* hasami.nagasaki.jp */ +26404, /* hirado.nagasaki.jp */ + 8541, /* iki.nagasaki.jp */ +26411, /* isahaya.nagasaki.jp */ +26419, /* kawatana.nagasaki.jp */ +26428, /* kuchinotsu.nagasaki.jp */ +26439, /* matsuura.nagasaki.jp */ +19142, /* nagasaki.nagasaki.jp */ +21214, /* obama.nagasaki.jp */ +26448, /* omura.nagasaki.jp */ +20085, /* oseto.nagasaki.jp */ +26454, /* saikai.nagasaki.jp */ +26461, /* sasebo.nagasaki.jp */ +26468, /* seihi.nagasaki.jp */ +26474, /* shimabara.nagasaki.jp */ +26384, /* shinkamigoto.nagasaki.jp */ +26484, /* togitsu.nagasaki.jp */ +20193, /* tsushima.nagasaki.jp */ +26492, /* unzen.nagasaki.jp */ +24004, /* ando.nara.jp */ +26499, /* gose.nara.jp */ +11342, /* heguri.nara.jp */ +26504, /* higashiyoshino.nara.jp */ +26519, /* ikaruga.nara.jp */ +26527, /* ikoma.nara.jp */ +26533, /* kamikitayama.nara.jp */ +26546, /* kanmaki.nara.jp */ +26554, /* kashiba.nara.jp */ +26562, /* kashihara.nara.jp */ +26572, /* katsuragi.nara.jp */ +24504, /* kawai.nara.jp */ +26094, /* kawakami.nara.jp */ +23840, /* kawanishi.nara.jp */ +26582, /* koryo.nara.jp */ +20864, /* kurotaki.nara.jp */ +26590, /* mitsue.nara.jp */ +26597, /* miyake.nara.jp */ +17980, /* nara.nara.jp */ +26604, /* nosegawa.nara.jp */ +24472, /* oji.nara.jp */ +26613, /* ouda.nara.jp */ +26618, /* oyodo.nara.jp */ +26624, /* sakurai.nara.jp */ +26632, /* sango.nara.jp */ +26638, /* shimoichi.nara.jp */ +26648, /* shimokitayama.nara.jp */ +26662, /* shinjo.nara.jp */ +26669, /* soni.nara.jp */ +20689, /* takatori.nara.jp */ +26674, /* tawaramoto.nara.jp */ +26685, /* tenkawa.nara.jp */ +26693, /* tenri.nara.jp */ +24916, /* uda.nara.jp */ +21861, /* yamatokoriyama.nara.jp */ +26699, /* yamatotakada.nara.jp */ +26712, /* yamazoe.nara.jp */ +26511, /* yoshino.nara.jp */ + 3339, /* aga.niigata.jp */ +19136, /* agano.niigata.jp */ +26720, /* gosen.niigata.jp */ +26726, /* itoigawa.niigata.jp */ +26735, /* izumozaki.niigata.jp */ +26745, /* joetsu.niigata.jp */ +22178, /* kamo.niigata.jp */ +26752, /* kariwa.niigata.jp */ +26759, /* kashiwazaki.niigata.jp */ +26771, /* minamiuonuma.niigata.jp */ +26784, /* mitsuke.niigata.jp */ +26792, /* muika.niigata.jp */ +26798, /* murakami.niigata.jp */ +26807, /* myoko.niigata.jp */ +26813, /* nagaoka.niigata.jp */ +19151, /* niigata.niigata.jp */ +26821, /* ojiya.niigata.jp */ +20205, /* omi.niigata.jp */ +26827, /* sado.niigata.jp */ +19849, /* sanjo.niigata.jp */ +26832, /* seiro.niigata.jp */ +26838, /* seirou.niigata.jp */ +26845, /* sekikawa.niigata.jp */ +25706, /* shibata.niigata.jp */ +20309, /* tagami.niigata.jp */ +26854, /* tainai.niigata.jp */ +26861, /* tochio.niigata.jp */ +26868, /* tokamachi.niigata.jp */ +26878, /* tsubame.niigata.jp */ +26886, /* tsunan.niigata.jp */ +26777, /* uonuma.niigata.jp */ +26893, /* yahiko.niigata.jp */ +19159, /* yoita.niigata.jp */ +26900, /* yuzawa.niigata.jp */ +26907, /* beppu.oita.jp */ +26913, /* bungoono.oita.jp */ +26922, /* bungotakada.oita.jp */ +26934, /* hasama.oita.jp */ +26941, /* hiji.oita.jp */ +26946, /* himeshima.oita.jp */ +19869, /* hita.oita.jp */ +26588, /* kamitsue.oita.jp */ +26956, /* kokonoe.oita.jp */ +26964, /* kuju.oita.jp */ +26969, /* kunisaki.oita.jp */ + 7538, /* kusu.oita.jp */ +19160, /* oita.oita.jp */ +26978, /* saiki.oita.jp */ +26984, /* taketa.oita.jp */ +26991, /* tsukumi.oita.jp */ +17988, /* usa.oita.jp */ +26999, /* usuki.oita.jp */ +27005, /* yufu.oita.jp */ +27010, /* akaiwa.okayama.jp */ +27017, /* asakuchi.okayama.jp */ +27026, /* bizen.okayama.jp */ +27032, /* hayashima.okayama.jp */ +27044, /* ibara.okayama.jp */ +27050, /* kagamino.okayama.jp */ +27059, /* kasaoka.okayama.jp */ +20602, /* kibichuo.okayama.jp */ +27067, /* kumenan.okayama.jp */ +27075, /* kurashiki.okayama.jp */ +27085, /* maniwa.okayama.jp */ +27092, /* misaki.okayama.jp */ +20453, /* nagi.okayama.jp */ +27105, /* niimi.okayama.jp */ +27111, /* nishiawakura.okayama.jp */ +19165, /* okayama.okayama.jp */ +27124, /* satosho.okayama.jp */ +27132, /* setouchi.okayama.jp */ +26662, /* shinjo.okayama.jp */ +27141, /* shoo.okayama.jp */ +27146, /* soja.okayama.jp */ +27151, /* takahashi.okayama.jp */ +27161, /* tamano.okayama.jp */ +21096, /* tsuyama.okayama.jp */ + 4518, /* wake.okayama.jp */ +27168, /* yakage.okayama.jp */ +27178, /* aguni.okinawa.jp */ +27184, /* ginowan.okinawa.jp */ +27192, /* ginoza.okinawa.jp */ +27199, /* gushikami.okinawa.jp */ +27209, /* haebaru.okinawa.jp */ +21332, /* higashi.okinawa.jp */ +27217, /* hirara.okinawa.jp */ +27224, /* iheya.okinawa.jp */ +27230, /* ishigaki.okinawa.jp */ +19037, /* ishikawa.okinawa.jp */ + 5174, /* itoman.okinawa.jp */ +27239, /* izena.okinawa.jp */ +27245, /* kadena.okinawa.jp */ + 7305, /* kin.okinawa.jp */ +27252, /* kitadaito.okinawa.jp */ +27262, /* kitanakagusuku.okinawa.jp */ +27277, /* kumejima.okinawa.jp */ +27286, /* kunigami.okinawa.jp */ +27295, /* minamidaito.okinawa.jp */ +20046, /* motobu.okinawa.jp */ +27309, /* nago.okinawa.jp */ +12017, /* naha.okinawa.jp */ +27266, /* nakagusuku.okinawa.jp */ +27314, /* nakijin.okinawa.jp */ +27322, /* nanjo.okinawa.jp */ +25248, /* nishihara.okinawa.jp */ +27328, /* ogimi.okinawa.jp */ + 5938, /* okinawa.okinawa.jp */ +27335, /* onna.okinawa.jp */ +27340, /* shimoji.okinawa.jp */ +27348, /* taketomi.okinawa.jp */ +27357, /* tarama.okinawa.jp */ +27364, /* tokashiki.okinawa.jp */ +27374, /* tomigusuku.okinawa.jp */ +27385, /* tonaki.okinawa.jp */ +27392, /* urasoe.okinawa.jp */ +27399, /* uruma.okinawa.jp */ +27405, /* yaese.okinawa.jp */ +27411, /* yomitan.okinawa.jp */ +27419, /* yonabaru.okinawa.jp */ +27175, /* yonaguni.okinawa.jp */ +24743, /* zamami.okinawa.jp */ +27428, /* abeno.osaka.jp */ +27434, /* chihayaakasaka.osaka.jp */ +20606, /* chuo.osaka.jp */ +27256, /* daito.osaka.jp */ +27449, /* fujiidera.osaka.jp */ +27459, /* habikino.osaka.jp */ +27468, /* hannan.osaka.jp */ +27475, /* higashiosaka.osaka.jp */ +20014, /* higashisumiyoshi.osaka.jp */ +27488, /* higashiyodogawa.osaka.jp */ +27504, /* hirakata.osaka.jp */ +19028, /* ibaraki.osaka.jp */ +21198, /* ikeda.osaka.jp */ +22460, /* izumi.osaka.jp */ +27513, /* izumiotsu.osaka.jp */ +27523, /* izumisano.osaka.jp */ +27533, /* kadoma.osaka.jp */ +27540, /* kaizuka.osaka.jp */ +25980, /* kanan.osaka.jp */ +27548, /* kashiwara.osaka.jp */ +27558, /* katano.osaka.jp */ +19128, /* kawachinagano.osaka.jp */ +27565, /* kishiwada.osaka.jp */ +18931, /* kita.osaka.jp */ +27575, /* kumatori.osaka.jp */ +27584, /* matsubara.osaka.jp */ +27599, /* minato.osaka.jp */ +27606, /* minoh.osaka.jp */ +27092, /* misaki.osaka.jp */ +27612, /* moriguchi.osaka.jp */ +27622, /* neyagawa.osaka.jp */ +21494, /* nishi.osaka.jp */ + 7098, /* nose.osaka.jp */ +27631, /* osakasayama.osaka.jp */ +21231, /* sakai.osaka.jp */ +21351, /* sayama.osaka.jp */ +27643, /* sennan.osaka.jp */ +27650, /* settsu.osaka.jp */ +27657, /* shijonawate.osaka.jp */ +27669, /* shimamoto.osaka.jp */ +27679, /* suita.osaka.jp */ +27685, /* tadaoka.osaka.jp */ +23920, /* taishi.osaka.jp */ +27693, /* tajiri.osaka.jp */ +27700, /* takaishi.osaka.jp */ +27709, /* takatsuki.osaka.jp */ +27719, /* tondabayashi.osaka.jp */ +27732, /* toyonaka.osaka.jp */ +27741, /* toyono.osaka.jp */ +27748, /* yao.osaka.jp */ +27752, /* ariake.saga.jp */ +20824, /* arita.saga.jp */ +27759, /* fukudomi.saga.jp */ +27768, /* genkai.saga.jp */ +27775, /* hamatama.saga.jp */ +21184, /* hizen.saga.jp */ +27784, /* imari.saga.jp */ +27790, /* kamimine.saga.jp */ +27799, /* kanzaki.saga.jp */ +27807, /* karatsu.saga.jp */ +24106, /* kashima.saga.jp */ +22158, /* kitagata.saga.jp */ +27815, /* kitahata.saga.jp */ +27824, /* kiyama.saga.jp */ +27831, /* kouhoku.saga.jp */ +27839, /* kyuragi.saga.jp */ +27847, /* nishiarita.saga.jp */ + 3494, /* ogi.saga.jp */ +26246, /* omachi.saga.jp */ +22288, /* ouchi.saga.jp */ + 3338, /* saga.saga.jp */ +25745, /* shiroishi.saga.jp */ +27858, /* taku.saga.jp */ +20113, /* tara.saga.jp */ +22201, /* tosu.saga.jp */ +27863, /* yoshinogari.saga.jp */ +27875, /* arakawa.saitama.jp */ +26362, /* asaka.saitama.jp */ +27890, /* chichibu.saitama.jp */ +26023, /* fujimi.saitama.jp */ +27899, /* fujimino.saitama.jp */ +27908, /* fukaya.saitama.jp */ +27915, /* hanno.saitama.jp */ +27921, /* hanyu.saitama.jp */ +27927, /* hasuda.saitama.jp */ +27934, /* hatogaya.saitama.jp */ +27943, /* hatoyama.saitama.jp */ +22916, /* hidaka.saitama.jp */ +27883, /* higashichichibu.saitama.jp */ +21087, /* higashimatsuyama.saitama.jp */ +20269, /* honjo.saitama.jp */ + 7276, /* ina.saitama.jp */ +27952, /* iruma.saitama.jp */ +27958, /* iwatsuki.saitama.jp */ +27967, /* kamiizumi.saitama.jp */ +23032, /* kamikawa.saitama.jp */ +27977, /* kamisato.saitama.jp */ +27986, /* kasukabe.saitama.jp */ +25479, /* kawagoe.saitama.jp */ +27995, /* kawaguchi.saitama.jp */ +28005, /* kawajima.saitama.jp */ +28014, /* kazo.saitama.jp */ +28019, /* kitamoto.saitama.jp */ +28028, /* koshigaya.saitama.jp */ +28038, /* kounosu.saitama.jp */ +28046, /* kuki.saitama.jp */ +28051, /* kumagaya.saitama.jp */ +28060, /* matsubushi.saitama.jp */ +28071, /* minano.saitama.jp */ +20334, /* misato.saitama.jp */ +23982, /* miyashiro.saitama.jp */ +20023, /* miyoshi.saitama.jp */ +28078, /* moroyama.saitama.jp */ +28087, /* nagatoro.saitama.jp */ +28096, /* namegawa.saitama.jp */ +28105, /* niiza.saitama.jp */ +28111, /* ogano.saitama.jp */ +20675, /* ogawa.saitama.jp */ +26498, /* ogose.saitama.jp */ +28117, /* okegawa.saitama.jp */ +19923, /* omiya.saitama.jp */ +20867, /* otaki.saitama.jp */ +28125, /* ranzan.saitama.jp */ +25045, /* ryokami.saitama.jp */ +19173, /* saitama.saitama.jp */ +28132, /* sakado.saitama.jp */ +28139, /* satte.saitama.jp */ +21351, /* sayama.saitama.jp */ +19947, /* shiki.saitama.jp */ +28145, /* shiraoka.saitama.jp */ +28154, /* soka.saitama.jp */ +28159, /* sugito.saitama.jp */ +28166, /* toda.saitama.jp */ +28171, /* tokigawa.saitama.jp */ +28180, /* tokorozawa.saitama.jp */ +28191, /* tsurugashima.saitama.jp */ +28204, /* urawa.saitama.jp */ +28210, /* warabi.saitama.jp */ +28217, /* yashio.saitama.jp */ +28224, /* yokoze.saitama.jp */ +22542, /* yono.saitama.jp */ +28231, /* yorii.saitama.jp */ +28241, /* yoshida.saitama.jp */ +28249, /* yoshikawa.saitama.jp */ +28259, /* yoshimi.saitama.jp */ +28267, /* aisho.shiga.jp */ +16711, /* gamo.shiga.jp */ +28273, /* higashiomi.shiga.jp */ +28284, /* hikone.shiga.jp */ +28291, /* koka.shiga.jp */ +20001, /* konan.shiga.jp */ +28296, /* kosei.shiga.jp */ +21541, /* koto.shiga.jp */ +22392, /* kusatsu.shiga.jp */ +27042, /* maibara.shiga.jp */ +28302, /* moriyama.shiga.jp */ +28311, /* nagahama.shiga.jp */ +28320, /* nishiazai.shiga.jp */ +28330, /* notogawa.shiga.jp */ +28339, /* omihachiman.shiga.jp */ +21633, /* otsu.shiga.jp */ +28357, /* ritto.shiga.jp */ +28363, /* ryuoh.shiga.jp */ +24104, /* takashima.shiga.jp */ +27709, /* takatsuki.shiga.jp */ +28369, /* torahime.shiga.jp */ +28378, /* toyosato.shiga.jp */ +20954, /* yasu.shiga.jp */ +26312, /* akagi.shimane.jp */ +10588, /* ama.shimane.jp */ +28351, /* gotsu.shimane.jp */ +28387, /* hamada.shimane.jp */ +28394, /* higashiizumo.shimane.jp */ +19039, /* hikawa.shimane.jp */ +28407, /* hikimi.shimane.jp */ +28401, /* izumo.shimane.jp */ +28423, /* kakinoki.shimane.jp */ +28432, /* masuda.shimane.jp */ +21590, /* matsue.shimane.jp */ +20334, /* misato.shimane.jp */ +28439, /* nishinoshima.shimane.jp */ +28452, /* ohda.shimane.jp */ +28457, /* okinoshima.shimane.jp */ +28414, /* okuizumo.shimane.jp */ +19209, /* shimane.shimane.jp */ +28468, /* tamayu.shimane.jp */ +28475, /* tsuwano.shimane.jp */ +28483, /* unnan.shimane.jp */ +23718, /* yakumo.shimane.jp */ +28489, /* yasugi.shimane.jp */ +28496, /* yatsuka.shimane.jp */ +21602, /* arai.shizuoka.jp */ +23997, /* atami.shizuoka.jp */ +23423, /* fuji.shizuoka.jp */ +28504, /* fujieda.shizuoka.jp */ +28512, /* fujikawa.shizuoka.jp */ +28521, /* fujinomiya.shizuoka.jp */ +28532, /* fukuroi.shizuoka.jp */ + 5268, /* gotemba.shizuoka.jp */ +28540, /* haibara.shizuoka.jp */ +28548, /* hamamatsu.shizuoka.jp */ +28558, /* higashiizu.shizuoka.jp */ + 7918, /* ito.shizuoka.jp */ +28569, /* iwata.shizuoka.jp */ +21904, /* izu.shizuoka.jp */ +28575, /* izunokuni.shizuoka.jp */ +28585, /* kakegawa.shizuoka.jp */ +28594, /* kannami.shizuoka.jp */ +28602, /* kawanehon.shizuoka.jp */ +28612, /* kawazu.shizuoka.jp */ +28619, /* kikugawa.shizuoka.jp */ +28628, /* kosai.shizuoka.jp */ +28634, /* makinohara.shizuoka.jp */ +28645, /* matsuzaki.shizuoka.jp */ +28655, /* minamiizu.shizuoka.jp */ +21890, /* mishima.shizuoka.jp */ +28665, /* morimachi.shizuoka.jp */ +28675, /* nishiizu.shizuoka.jp */ +28684, /* numazu.shizuoka.jp */ +28691, /* omaezaki.shizuoka.jp */ +28700, /* shimada.shizuoka.jp */ +23127, /* shimizu.shizuoka.jp */ + 5452, /* shimoda.shizuoka.jp */ +19217, /* shizuoka.shizuoka.jp */ +28708, /* susono.shizuoka.jp */ +28715, /* yaizu.shizuoka.jp */ +28241, /* yoshida.shizuoka.jp */ +24330, /* ashikaga.tochigi.jp */ +11673, /* bato.tochigi.jp */ +28721, /* haga.tochigi.jp */ +28726, /* ichikai.tochigi.jp */ +28734, /* iwafune.tochigi.jp */ +28742, /* kaminokawa.tochigi.jp */ +28753, /* kanuma.tochigi.jp */ +28760, /* karasuyama.tochigi.jp */ +24964, /* kuroiso.tochigi.jp */ +28771, /* mashiko.tochigi.jp */ +28779, /* mibu.tochigi.jp */ +28784, /* moka.tochigi.jp */ +28789, /* motegi.tochigi.jp */ +28796, /* nasu.tochigi.jp */ +28801, /* nasushiobara.tochigi.jp */ +28814, /* nikko.tochigi.jp */ +28820, /* nishikata.tochigi.jp */ + 3493, /* nogi.tochigi.jp */ +24661, /* ohira.tochigi.jp */ +28830, /* ohtawara.tochigi.jp */ +19255, /* oyama.tochigi.jp */ + 6898, /* sakura.tochigi.jp */ +27528, /* sano.tochigi.jp */ +28839, /* shimotsuke.tochigi.jp */ +28850, /* shioya.tochigi.jp */ +28857, /* takanezawa.tochigi.jp */ +19226, /* tochigi.tochigi.jp */ +28868, /* tsuga.tochigi.jp */ +28874, /* ujiie.tochigi.jp */ +28880, /* utsunomiya.tochigi.jp */ +28891, /* yaita.tochigi.jp */ +24453, /* aizumi.tokushima.jp */ +25981, /* anan.tokushima.jp */ +18947, /* ichiba.tokushima.jp */ +28897, /* itano.tokushima.jp */ +21004, /* kainan.tokushima.jp */ +20189, /* komatsushima.tokushima.jp */ +28903, /* matsushige.tokushima.jp */ +28914, /* mima.tokushima.jp */ +21433, /* minami.tokushima.jp */ +20023, /* miyoshi.tokushima.jp */ +28919, /* mugi.tokushima.jp */ +19052, /* nakagawa.tokushima.jp */ +28924, /* naruto.tokushima.jp */ +28931, /* sanagochi.tokushima.jp */ +28941, /* shishikui.tokushima.jp */ +19234, /* tokushima.tokushima.jp */ +28951, /* wajiki.tokushima.jp */ +25964, /* adachi.tokyo.jp */ +28958, /* akiruno.tokyo.jp */ +28966, /* akishima.tokyo.jp */ +28975, /* aogashima.tokyo.jp */ +27875, /* arakawa.tokyo.jp */ +28985, /* bunkyo.tokyo.jp */ +22315, /* chiyoda.tokyo.jp */ +28992, /* chofu.tokyo.jp */ +20606, /* chuo.tokyo.jp */ +24153, /* edogawa.tokyo.jp */ +22579, /* fuchu.tokyo.jp */ +28998, /* fussa.tokyo.jp */ +29004, /* hachijo.tokyo.jp */ +29012, /* hachioji.tokyo.jp */ +29021, /* hamura.tokyo.jp */ +21416, /* higashikurume.tokyo.jp */ +29028, /* higashimurayama.tokyo.jp */ +22029, /* higashiyamato.tokyo.jp */ +20818, /* hino.tokyo.jp */ +29044, /* hinode.tokyo.jp */ +29051, /* hinohara.tokyo.jp */ +27099, /* inagi.tokyo.jp */ +29060, /* itabashi.tokyo.jp */ +29069, /* katsushika.tokyo.jp */ +18931, /* kita.tokyo.jp */ +29080, /* kiyose.tokyo.jp */ +29087, /* kodaira.tokyo.jp */ +29095, /* koganei.tokyo.jp */ +29103, /* kokubunji.tokyo.jp */ +29113, /* komae.tokyo.jp */ +21541, /* koto.tokyo.jp */ +29119, /* kouzushima.tokyo.jp */ +29130, /* kunitachi.tokyo.jp */ +22098, /* machida.tokyo.jp */ +29140, /* meguro.tokyo.jp */ +27599, /* minato.tokyo.jp */ +23927, /* mitaka.tokyo.jp */ +29147, /* mizuho.tokyo.jp */ +29154, /* musashimurayama.tokyo.jp */ +29170, /* musashino.tokyo.jp */ +26215, /* nakano.tokyo.jp */ +29180, /* nerima.tokyo.jp */ +29187, /* ogasawara.tokyo.jp */ +29197, /* okutama.tokyo.jp */ + 1693, /* ome.tokyo.jp */ +19006, /* oshima.tokyo.jp */ + 7967, /* ota.tokyo.jp */ +29218, /* setagaya.tokyo.jp */ +29227, /* shibuya.tokyo.jp */ +23806, /* shinagawa.tokyo.jp */ +29235, /* shinjuku.tokyo.jp */ +29244, /* suginami.tokyo.jp */ +29253, /* sumida.tokyo.jp */ +29260, /* tachikawa.tokyo.jp */ +29270, /* taito.tokyo.jp */ +19176, /* tama.tokyo.jp */ +29210, /* toshima.tokyo.jp */ +29276, /* chizu.tottori.jp */ +20818, /* hino.tottori.jp */ +29282, /* kawahara.tottori.jp */ + 3441, /* koge.tottori.jp */ +29291, /* kotoura.tottori.jp */ +29299, /* misasa.tottori.jp */ +29306, /* nanbu.tottori.jp */ +25888, /* nichinan.tottori.jp */ +27594, /* sakaiminato.tottori.jp */ +19244, /* tottori.tottori.jp */ +21245, /* wakasa.tottori.jp */ +25372, /* yazu.tottori.jp */ +27307, /* yonago.tottori.jp */ +20079, /* asahi.toyama.jp */ +22579, /* fuchu.toyama.jp */ +29312, /* fukumitsu.toyama.jp */ +29322, /* funahashi.toyama.jp */ +28262, /* himi.toyama.jp */ +23129, /* imizu.toyama.jp */ +21434, /* inami.toyama.jp */ +29332, /* johana.toyama.jp */ +29339, /* kamiichi.toyama.jp */ +29348, /* kurobe.toyama.jp */ +29355, /* nakaniikawa.toyama.jp */ +29367, /* namerikawa.toyama.jp */ +29378, /* nanto.toyama.jp */ +29384, /* nyuzen.toyama.jp */ +29391, /* oyabe.toyama.jp */ +29397, /* taira.toyama.jp */ +29403, /* takaoka.toyama.jp */ +20926, /* tateyama.toyama.jp */ +29411, /* toga.toyama.jp */ +29416, /* tonami.toyama.jp */ +19254, /* toyama.toyama.jp */ +29423, /* unazuki.toyama.jp */ +21112, /* uozu.toyama.jp */ +21657, /* yamada.toyama.jp */ +29431, /* arida.wakayama.jp */ +29437, /* aridagawa.wakayama.jp */ +29447, /* gobo.wakayama.jp */ +29452, /* hashimoto.wakayama.jp */ +22916, /* hidaka.wakayama.jp */ +29462, /* hirogawa.wakayama.jp */ +21434, /* inami.wakayama.jp */ +29471, /* iwade.wakayama.jp */ +21004, /* kainan.wakayama.jp */ +29477, /* kamitonda.wakayama.jp */ +26572, /* katsuragi.wakayama.jp */ +22167, /* kimino.wakayama.jp */ +29487, /* kinokawa.wakayama.jp */ +26537, /* kitayama.wakayama.jp */ +29496, /* koya.wakayama.jp */ +10599, /* koza.wakayama.jp */ +29501, /* kozagawa.wakayama.jp */ +29510, /* kudoyama.wakayama.jp */ +29519, /* kushimoto.wakayama.jp */ +20007, /* mihama.wakayama.jp */ +20334, /* misato.wakayama.jp */ +20698, /* nachikatsuura.wakayama.jp */ +21563, /* shingu.wakayama.jp */ +29529, /* shirahama.wakayama.jp */ +29539, /* taiji.wakayama.jp */ +25330, /* tanabe.wakayama.jp */ +19261, /* wakayama.wakayama.jp */ +29545, /* yuasa.wakayama.jp */ +29551, /* yura.wakayama.jp */ +20079, /* asahi.yamagata.jp */ +29556, /* funagata.yamagata.jp */ +29565, /* higashine.yamagata.jp */ + 2275, /* iide.yamagata.jp */ +24339, /* kahoku.yamagata.jp */ +29575, /* kaminoyama.yamagata.jp */ +21824, /* kaneyama.yamagata.jp */ +23840, /* kawanishi.yamagata.jp */ +29586, /* mamurogawa.yamagata.jp */ +23034, /* mikawa.yamagata.jp */ +29035, /* murayama.yamagata.jp */ +29597, /* nagai.yamagata.jp */ +29603, /* nakayama.yamagata.jp */ +29612, /* nanyo.yamagata.jp */ +19036, /* nishikawa.yamagata.jp */ +29618, /* obanazawa.yamagata.jp */ + 1677, /* oe.yamagata.jp */ +25235, /* oguni.yamagata.jp */ +29628, /* ohkura.yamagata.jp */ +29635, /* oishida.yamagata.jp */ +29643, /* sagae.yamagata.jp */ +29649, /* sakata.yamagata.jp */ +29656, /* sakegawa.yamagata.jp */ +26662, /* shinjo.yamagata.jp */ +29665, /* shirataka.yamagata.jp */ +21011, /* shonai.yamagata.jp */ +29675, /* takahata.yamagata.jp */ +29684, /* tendo.yamagata.jp */ +29690, /* tozawa.yamagata.jp */ +29697, /* tsuruoka.yamagata.jp */ +19815, /* yamagata.yamagata.jp */ +29706, /* yamanobe.yamagata.jp */ +29715, /* yonezawa.yamagata.jp */ +29724, /* yuza.yamagata.jp */ +22775, /* abu.yamaguchi.jp */ +24231, /* hagi.yamaguchi.jp */ +20986, /* hikari.yamaguchi.jp */ +28993, /* hofu.yamaguchi.jp */ +29729, /* iwakuni.yamaguchi.jp */ +29737, /* kudamatsu.yamaguchi.jp */ +29747, /* mitou.yamaguchi.jp */ +29753, /* nagato.yamaguchi.jp */ +19006, /* oshima.yamaguchi.jp */ +29760, /* shimonoseki.yamaguchi.jp */ +29772, /* shunan.yamaguchi.jp */ +29779, /* tabuse.yamaguchi.jp */ +29786, /* tokuyama.yamaguchi.jp */ + 7964, /* toyota.yamaguchi.jp */ + 8057, /* ube.yamaguchi.jp */ +29795, /* yuu.yamaguchi.jp */ +20606, /* chuo.yamanashi.jp */ +29799, /* doshi.yamanashi.jp */ +29805, /* fuefuki.yamanashi.jp */ +28512, /* fujikawa.yamanashi.jp */ +21141, /* fujikawaguchiko.yamanashi.jp */ +28237, /* fujiyoshida.yamanashi.jp */ +29813, /* hayakawa.yamanashi.jp */ +22963, /* hokuto.yamanashi.jp */ +29822, /* ichikawamisato.yamanashi.jp */ +20151, /* kai.yamanashi.jp */ +29837, /* kofu.yamanashi.jp */ +24565, /* koshu.yamanashi.jp */ +29842, /* kosuge.yamanashi.jp */ +29849, /* minami-alps.yamanashi.jp */ +29861, /* minobu.yamanashi.jp */ +29868, /* nakamichi.yamanashi.jp */ +29306, /* nanbu.yamanashi.jp */ +29878, /* narusawa.yamanashi.jp */ +29887, /* nirasaki.yamanashi.jp */ +29896, /* nishikatsura.yamanashi.jp */ +26512, /* oshino.yamanashi.jp */ +25111, /* otsuki.yamanashi.jp */ +21970, /* showa.yamanashi.jp */ +29909, /* tabayama.yamanashi.jp */ +29918, /* tsuru.yamanashi.jp */ +29924, /* uenohara.yamanashi.jp */ +29933, /* yamanakako.yamanashi.jp */ +19834, /* yamanashi.yamanashi.jp */ + 986, /* biz.ki */ + 1914, /* com.ki */ + 2623, /* edu.ki */ + 3671, /* gov.ki */ + 3152, /* info.ki */ + 5737, /* net.ki */ + 6053, /* org.ki */ + 3533, /* ass.km */ +11647, /* asso.km */ + 1914, /* com.km */ + 2048, /* coop.km */ + 2623, /* edu.km */ +11660, /* gouv.km */ + 3671, /* gov.km */ +15692, /* medecin.km */ + 4170, /* mil.km */ + 5970, /* nom.km */ +15700, /* notaires.km */ + 6053, /* org.km */ +29944, /* pharmaciens.km */ +15720, /* prd.km */ +11923, /* presse.km */ + 7908, /* tm.km */ +15724, /* veterinaire.km */ + 2623, /* edu.kn */ + 3671, /* gov.kn */ + 5737, /* net.kn */ + 6053, /* org.kn */ + 1914, /* com.kp */ + 2623, /* edu.kp */ + 3671, /* gov.kp */ + 6053, /* org.kp */ +29956, /* rep.kp */ +17016, /* tra.kp */ 62, /* ac.kr */ -10666, /* blogspot.kr */ -29621, /* busan.kr */ -29627, /* chungbuk.kr */ -29636, /* chungnam.kr */ +10645, /* blogspot.kr */ +29966, /* busan.kr */ +29972, /* chungbuk.kr */ +29981, /* chungnam.kr */ 113, /* co.kr */ -29645, /* daegu.kr */ -29651, /* daejeon.kr */ +29990, /* daegu.kr */ +29996, /* daejeon.kr */ 558, /* es.kr */ -29659, /* gangwon.kr */ +30004, /* gangwon.kr */ 257, /* go.kr */ -29667, /* gwangju.kr */ -29675, /* gyeongbuk.kr */ -29685, /* gyeonggi.kr */ -29694, /* gyeongnam.kr */ - 9270, /* hs.kr */ -29708, /* incheon.kr */ -29716, /* jeju.kr */ - 8117, /* jeonbuk.kr */ -29721, /* jeonnam.kr */ - 4579, /* kg.kr */ - 4195, /* mil.kr */ - 1059, /* ms.kr */ - 1203, /* ne.kr */ +30012, /* gwangju.kr */ +30020, /* gyeongbuk.kr */ +30030, /* gyeonggi.kr */ +30039, /* gyeongnam.kr */ + 9249, /* hs.kr */ +30053, /* incheon.kr */ +30061, /* jeju.kr */ + 8115, /* jeonbuk.kr */ +30066, /* jeonnam.kr */ + 4558, /* kg.kr */ + 4170, /* mil.kr */ + 1060, /* ms.kr */ + 1204, /* ne.kr */ 137, /* or.kr */ - 3739, /* pe.kr */ + 3724, /* pe.kr */ 80, /* re.kr */ - 2158, /* sc.kr */ -29729, /* seoul.kr */ -29735, /* ulsan.kr */ + 2159, /* sc.kr */ +30074, /* seoul.kr */ +30080, /* ulsan.kr */ 113, /* co.krd */ - 2624, /* edu.krd */ - 5911, /* bnr.la */ + 2623, /* edu.krd */ + 5883, /* bnr.la */ 36, /* c.la */ - 1913, /* com.la */ - 2624, /* edu.la */ - 3686, /* gov.la */ - 3167, /* info.la */ - 3632, /* int.la */ - 4185, /* net.la */ - 6070, /* org.la */ - 4523, /* per.la */ - 2380, /* dev.static.land */ -29756, /* sites.static.land */ + 1914, /* com.la */ + 2623, /* edu.la */ + 3671, /* gov.la */ + 3152, /* info.la */ + 3617, /* int.la */ + 5737, /* net.la */ + 6053, /* org.la */ + 4502, /* per.la */ + 2383, /* dev.static.land */ +30101, /* sites.static.land */ 113, /* co.lc */ - 1913, /* com.lc */ - 2624, /* edu.lc */ - 3686, /* gov.lc */ - 4185, /* net.lc */ - 6070, /* org.lc */ - 4492, /* oy.lc */ -29762, /* cyon.link */ -29767, /* mypep.link */ + 1914, /* com.lc */ + 2623, /* edu.lc */ + 3671, /* gov.lc */ + 5737, /* net.lc */ + 6053, /* org.lc */ + 4471, /* oy.lc */ +30107, /* cyon.link */ +30112, /* mypep.link */ 62, /* ac.lk */ -29773, /* assn.lk */ - 1913, /* com.lk */ - 2624, /* edu.lk */ - 3686, /* gov.lk */ -29778, /* grp.lk */ - 7749, /* hotel.lk */ - 3632, /* int.lk */ - 5103, /* ltd.lk */ - 4185, /* net.lk */ - 976, /* ngo.lk */ - 6070, /* org.lk */ - 1145, /* sch.lk */ -29782, /* soc.lk */ -11967, /* web.lk */ - 7340, /* asn.lv */ - 1913, /* com.lv */ -11412, /* conf.lv */ - 2624, /* edu.lv */ - 3686, /* gov.lv */ +30118, /* assn.lk */ + 1914, /* com.lk */ + 2623, /* edu.lk */ + 3671, /* gov.lk */ +30123, /* grp.lk */ + 7747, /* hotel.lk */ + 3617, /* int.lk */ + 5082, /* ltd.lk */ + 5737, /* net.lk */ + 977, /* ngo.lk */ + 6053, /* org.lk */ + 1146, /* sch.lk */ +30127, /* soc.lk */ +12109, /* web.lk */ + 7329, /* asn.lv */ + 1914, /* com.lv */ +11437, /* conf.lv */ + 2623, /* edu.lv */ + 3671, /* gov.lv */ 437, /* id.lv */ - 4195, /* mil.lv */ - 4185, /* net.lv */ - 6070, /* org.lv */ - 1913, /* com.ly */ - 2624, /* edu.ly */ - 3686, /* gov.ly */ + 4170, /* mil.lv */ + 5737, /* net.lv */ + 6053, /* org.lv */ + 1914, /* com.ly */ + 2623, /* edu.ly */ + 3671, /* gov.ly */ 437, /* id.ly */ - 1858, /* med.ly */ - 4185, /* net.ly */ - 6070, /* org.ly */ - 4860, /* plc.ly */ - 1145, /* sch.ly */ + 1859, /* med.ly */ + 5737, /* net.ly */ + 6053, /* org.ly */ + 4839, /* plc.ly */ + 1146, /* sch.ly */ 62, /* ac.ma */ 113, /* co.ma */ - 3686, /* gov.ma */ - 4185, /* net.ma */ - 6070, /* org.ma */ + 3671, /* gov.ma */ + 5737, /* net.ma */ + 6053, /* org.ma */ 371, /* press.ma */ -15043, /* router.management */ -11614, /* asso.mc */ - 7910, /* tm.mc */ - 62, /* ac.me */ -29786, /* brasilia.me */ - 113, /* co.me */ -29795, /* daplie.me */ -29802, /* ddns.me */ -15103, /* diskstation.me */ -29807, /* dnsfor.me */ -11518, /* dscloud.me */ - 2624, /* edu.me */ - 3686, /* gov.me */ -29814, /* hopto.me */ -29820, /* i234.me */ -18288, /* its.me */ -29825, /* loginto.me */ -29833, /* myds.me */ - 4185, /* net.me */ -29838, /* noip.me */ - 6070, /* org.me */ -11393, /* priv.me */ -29843, /* synology.me */ -11601, /* webhop.me */ -29852, /* yombo.me */ +15310, /* router.management */ +11647, /* asso.mc */ + 7908, /* tm.mc */ +30211, /* localhost.daplie.me */ 113, /* co.mg */ - 1913, /* com.mg */ - 2624, /* edu.mg */ - 3686, /* gov.mg */ - 4195, /* mil.mg */ - 5998, /* nom.mg */ - 6070, /* org.mg */ -15443, /* prd.mg */ - 7910, /* tm.mg */ -10666, /* blogspot.mk */ - 1913, /* com.mk */ - 2624, /* edu.mk */ - 3686, /* gov.mk */ - 5824, /* inf.mk */ - 5725, /* name.mk */ - 4185, /* net.mk */ - 6070, /* org.mk */ - 1913, /* com.ml */ - 2624, /* edu.ml */ -11627, /* gouv.ml */ - 3686, /* gov.ml */ - 4185, /* net.ml */ - 6070, /* org.ml */ -11816, /* presse.ml */ - 2624, /* edu.mn */ - 3686, /* gov.mn */ - 5933, /* nyc.mn */ - 6070, /* org.mn */ -11518, /* dscloud.mobi */ + 1914, /* com.mg */ + 2623, /* edu.mg */ + 3671, /* gov.mg */ + 4170, /* mil.mg */ + 5970, /* nom.mg */ + 6053, /* org.mg */ +15720, /* prd.mg */ + 7908, /* tm.mg */ +10645, /* blogspot.mk */ + 1914, /* com.mk */ + 2623, /* edu.mk */ + 3671, /* gov.mk */ + 5796, /* inf.mk */ + 5695, /* name.mk */ + 5737, /* net.mk */ + 6053, /* org.mk */ + 1914, /* com.ml */ + 2623, /* edu.ml */ +11660, /* gouv.ml */ + 3671, /* gov.ml */ + 5737, /* net.ml */ + 6053, /* org.ml */ +11923, /* presse.ml */ + 2623, /* edu.mn */ + 3671, /* gov.mn */ + 5905, /* nyc.mn */ + 6053, /* org.mn */ +11551, /* dscloud.mobi */ 62, /* ac.mu */ 113, /* co.mu */ - 1913, /* com.mu */ - 3686, /* gov.mu */ - 4185, /* net.mu */ + 1914, /* com.mu */ + 3671, /* gov.mu */ + 5737, /* net.mu */ 137, /* or.mu */ - 6070, /* org.mu */ + 6053, /* org.mu */ 65, /* academy.museum */ -29858, /* agriculture.museum */ - 3824, /* air.museum */ -29870, /* airguard.museum */ -29879, /* alabama.museum */ -29887, /* alaska.museum */ -29894, /* amber.museum */ -10818, /* ambulance.museum */ -29906, /* american.museum */ -29915, /* americana.museum */ -29925, /* americanantiques.museum */ -29942, /* americanart.museum */ +30221, /* agriculture.museum */ + 3806, /* air.museum */ +30233, /* airguard.museum */ +30242, /* alabama.museum */ +30250, /* alaska.museum */ +30257, /* amber.museum */ +10797, /* ambulance.museum */ +30269, /* american.museum */ +30278, /* americana.museum */ +30288, /* americanantiques.museum */ +30305, /* americanart.museum */ 412, /* amsterdam.museum */ 726, /* and.museum */ -29960, /* annefrank.museum */ -29970, /* anthro.museum */ -29977, /* anthropology.museum */ -29933, /* antiques.museum */ -30001, /* aquarium.museum */ -30010, /* arboretum.museum */ -30020, /* archaeological.museum */ -30035, /* archaeology.museum */ -30047, /* architecture.museum */ +30323, /* annefrank.museum */ +30333, /* anthro.museum */ +30340, /* anthropology.museum */ +30296, /* antiques.museum */ +30364, /* aquarium.museum */ +30373, /* arboretum.museum */ +30383, /* archaeological.museum */ +30398, /* archaeology.museum */ +30410, /* architecture.museum */ 527, /* art.museum */ - 2367, /* artanddesign.museum */ - 1590, /* artcenter.museum */ -30060, /* artdeco.museum */ - 2628, /* arteducation.museum */ - 3364, /* artgallery.museum */ - 6175, /* arts.museum */ -30068, /* artsandcrafts.museum */ -30082, /* asmatart.museum */ -30091, /* assassination.museum */ -30105, /* assisi.museum */ -10848, /* association.museum */ -30112, /* astronomy.museum */ -30122, /* atlanta.museum */ -30130, /* austin.museum */ -30137, /* australia.museum */ -30147, /* automotive.museum */ -10921, /* aviation.museum */ -30158, /* axis.museum */ -30163, /* badajoz.museum */ - 2211, /* baghdad.museum */ -30176, /* bahn.museum */ -30181, /* bale.museum */ -30186, /* baltimore.museum */ + 2368, /* artanddesign.museum */ + 1591, /* artcenter.museum */ +30423, /* artdeco.museum */ + 2627, /* arteducation.museum */ + 3349, /* artgallery.museum */ + 6158, /* arts.museum */ +30431, /* artsandcrafts.museum */ +30445, /* asmatart.museum */ +30454, /* assassination.museum */ +30468, /* assisi.museum */ +10827, /* association.museum */ +30475, /* astronomy.museum */ +30485, /* atlanta.museum */ +30493, /* austin.museum */ +30500, /* australia.museum */ +30510, /* automotive.museum */ +10900, /* aviation.museum */ +30521, /* axis.museum */ +30526, /* badajoz.museum */ + 2212, /* baghdad.museum */ +30539, /* bahn.museum */ +17468, /* bale.museum */ +30544, /* baltimore.museum */ 740, /* barcelona.museum */ 789, /* baseball.museum */ -30196, /* basel.museum */ -30202, /* baths.museum */ -30208, /* bauern.museum */ -30215, /* beauxarts.museum */ -30225, /* beeldengeluid.museum */ -30239, /* bellevue.museum */ -30248, /* bergbau.museum */ -30256, /* berkeley.museum */ +30554, /* basel.museum */ +30560, /* baths.museum */ +30566, /* bauern.museum */ +30573, /* beauxarts.museum */ +30583, /* beeldengeluid.museum */ +30597, /* bellevue.museum */ +30606, /* bergbau.museum */ +30614, /* berkeley.museum */ 894, /* berlin.museum */ -30265, /* bern.museum */ - 950, /* bible.museum */ -30270, /* bilbao.museum */ -30277, /* bill.museum */ -30282, /* birdart.museum */ - 6335, /* birthplace.museum */ -30290, /* bonn.museum */ - 1156, /* boston.museum */ -30295, /* botanical.museum */ -30305, /* botanicalgarden.museum */ -30321, /* botanicgarden.museum */ -30335, /* botany.museum */ -30342, /* brandywinevalley.museum */ -30359, /* brasil.museum */ -30366, /* bristol.museum */ -30374, /* british.museum */ -30382, /* britishcolumbia.museum */ -30398, /* broadcast.museum */ -30408, /* brunel.museum */ -30415, /* brussel.museum */ - 1230, /* brussels.museum */ -30423, /* bruxelles.museum */ -30433, /* building.museum */ -30442, /* burghof.museum */ +30623, /* bern.museum */ + 951, /* bible.museum */ +30628, /* bilbao.museum */ +30635, /* bill.museum */ +30640, /* birdart.museum */ + 6318, /* birthplace.museum */ +30648, /* bonn.museum */ + 1157, /* boston.museum */ +30653, /* botanical.museum */ +30663, /* botanicalgarden.museum */ +30679, /* botanicgarden.museum */ +30693, /* botany.museum */ +30700, /* brandywinevalley.museum */ +30717, /* brasil.museum */ +30724, /* bristol.museum */ +30732, /* british.museum */ +30740, /* britishcolumbia.museum */ +30756, /* broadcast.museum */ +30766, /* brunel.museum */ +30773, /* brussel.museum */ + 1231, /* brussels.museum */ +30781, /* bruxelles.museum */ +30791, /* building.museum */ +30800, /* burghof.museum */ 263, /* bus.museum */ -30450, /* bushey.museum */ -30457, /* cadaques.museum */ -30466, /* california.museum */ -30477, /* cambridge.museum */ - 6730, /* can.museum */ -30487, /* canada.museum */ -30494, /* capebreton.museum */ -30505, /* carrier.museum */ -30513, /* cartoonart.museum */ -30524, /* casadelamoneda.museum */ -30539, /* castle.museum */ -30546, /* castres.museum */ -30554, /* celtic.museum */ - 1593, /* center.museum */ -30561, /* chattanooga.museum */ -30573, /* cheltenham.museum */ -30584, /* chesapeakebay.museum */ -30598, /* chicago.museum */ -30606, /* children.museum */ -30615, /* childrens.museum */ -30625, /* childrensgarden.museum */ -30641, /* chiropractic.museum */ -30654, /* chocolate.museum */ -30664, /* christiansburg.museum */ -30679, /* cincinnati.museum */ -30690, /* cinema.museum */ -30697, /* circus.museum */ -30704, /* civilisation.museum */ -30717, /* civilization.museum */ -30730, /* civilwar.museum */ -30739, /* clinton.museum */ -30755, /* clock.museum */ +30808, /* bushey.museum */ +30815, /* cadaques.museum */ +30824, /* california.museum */ +30835, /* cambridge.museum */ + 6713, /* can.museum */ +30845, /* canada.museum */ +30852, /* capebreton.museum */ +30863, /* carrier.museum */ +30871, /* cartoonart.museum */ +30882, /* casadelamoneda.museum */ +30897, /* castle.museum */ +30904, /* castres.museum */ +30912, /* celtic.museum */ + 1594, /* center.museum */ +30919, /* chattanooga.museum */ +30931, /* cheltenham.museum */ +30942, /* chesapeakebay.museum */ +30956, /* chicago.museum */ +30964, /* children.museum */ +30973, /* childrens.museum */ +30983, /* childrensgarden.museum */ +30999, /* chiropractic.museum */ +31012, /* chocolate.museum */ +31022, /* christiansburg.museum */ +31037, /* cincinnati.museum */ +31048, /* cinema.museum */ +31055, /* circus.museum */ +31062, /* civilisation.museum */ +31075, /* civilization.museum */ +31088, /* civilwar.museum */ +31097, /* clinton.museum */ +31113, /* clock.museum */ 288, /* coal.museum */ -30761, /* coastaldefence.museum */ -15262, /* cody.museum */ -30776, /* coldwar.museum */ -30784, /* collection.museum */ -30795, /* colonialwilliamsburg.museum */ -30816, /* coloradoplateau.museum */ -30389, /* columbia.museum */ -30832, /* columbus.museum */ -30841, /* communication.museum */ -30869, /* communications.museum */ - 1934, /* community.museum */ - 1952, /* computer.museum */ -30884, /* computerhistory.museum */ -30900, /* contemporary.museum */ -30913, /* contemporaryart.museum */ -30929, /* convent.museum */ -30937, /* copenhagen.museum */ -30948, /* corporation.museum */ -30960, /* corvette.museum */ -30969, /* costume.museum */ -30979, /* countryestate.museum */ -30993, /* county.museum */ -30075, /* crafts.museum */ -31000, /* cranbrook.museum */ -11212, /* creation.museum */ -31010, /* cultural.museum */ -31019, /* culturalcenter.museum */ -29862, /* culture.museum */ -31044, /* cyber.museum */ - 2187, /* cymru.museum */ -31058, /* dali.museum */ -31063, /* dallas.museum */ -31070, /* database.museum */ -11348, /* ddr.museum */ -31081, /* decorativearts.museum */ -31103, /* delaware.museum */ -31112, /* delmenhorst.museum */ -31124, /* denmark.museum */ - 3985, /* depot.museum */ - 2373, /* design.museum */ -31132, /* detroit.museum */ -31140, /* dinosaur.museum */ -31149, /* discovery.museum */ -31159, /* dolls.museum */ -31165, /* donostia.museum */ -31174, /* durham.museum */ +31119, /* coastaldefence.museum */ +15539, /* cody.museum */ +31134, /* coldwar.museum */ +31142, /* collection.museum */ +31153, /* colonialwilliamsburg.museum */ +31174, /* coloradoplateau.museum */ +30747, /* columbia.museum */ +31190, /* columbus.museum */ +31199, /* communication.museum */ +31227, /* communications.museum */ + 1935, /* community.museum */ + 1953, /* computer.museum */ +31242, /* computerhistory.museum */ +31258, /* contemporary.museum */ +31271, /* contemporaryart.museum */ +31287, /* convent.museum */ +31295, /* copenhagen.museum */ +31306, /* corporation.museum */ +31318, /* corvette.museum */ +31327, /* costume.museum */ +31337, /* countryestate.museum */ +31351, /* county.museum */ +30438, /* crafts.museum */ +31358, /* cranbrook.museum */ +11191, /* creation.museum */ +31368, /* cultural.museum */ +31377, /* culturalcenter.museum */ +30225, /* culture.museum */ +31402, /* cyber.museum */ + 2188, /* cymru.museum */ +31416, /* dali.museum */ +31421, /* dallas.museum */ +31428, /* database.museum */ +11334, /* ddr.museum */ +31439, /* decorativearts.museum */ +31461, /* delaware.museum */ +31470, /* delmenhorst.museum */ +31482, /* denmark.museum */ + 3967, /* depot.museum */ + 2374, /* design.museum */ +31490, /* detroit.museum */ +31498, /* dinosaur.museum */ +31507, /* discovery.museum */ +31517, /* dolls.museum */ +31523, /* donostia.museum */ +31532, /* durham.museum */ 213, /* eastafrica.museum */ -31181, /* eastcoast.museum */ - 2631, /* education.museum */ -31191, /* educational.museum */ -31203, /* egyptian.museum */ -30171, /* eisenbahn.museum */ -17690, /* elburg.museum */ -31212, /* elvendrell.museum */ -31223, /* embroidery.museum */ -31234, /* encyclopedic.museum */ -31247, /* england.museum */ -31255, /* entomology.museum */ -31266, /* environment.museum */ -31278, /* environmentalconservation.museum */ -31304, /* epilepsy.museum */ - 7166, /* essex.museum */ - 2769, /* estate.museum */ -31313, /* ethnology.museum */ -31323, /* exeter.museum */ -31330, /* exhibition.museum */ +31539, /* eastcoast.museum */ + 2630, /* education.museum */ +31549, /* educational.museum */ +31561, /* egyptian.museum */ +30534, /* eisenbahn.museum */ +18035, /* elburg.museum */ +31570, /* elvendrell.museum */ +31581, /* embroidery.museum */ +31592, /* encyclopedic.museum */ +31605, /* england.museum */ +31613, /* entomology.museum */ +31624, /* environment.museum */ +31636, /* environmentalconservation.museum */ +31662, /* epilepsy.museum */ + 7155, /* essex.museum */ + 2768, /* estate.museum */ +31671, /* ethnology.museum */ +31681, /* exeter.museum */ +31688, /* exhibition.museum */ 385, /* family.museum */ - 2948, /* farm.museum */ - 2721, /* farmequipment.museum */ - 2953, /* farmers.museum */ -31341, /* farmstead.museum */ -31351, /* field.museum */ -31357, /* figueres.museum */ -31366, /* filatelia.museum */ - 3031, /* film.museum */ -31376, /* fineart.museum */ -31384, /* finearts.museum */ -31393, /* finland.museum */ -31401, /* flanders.museum */ -31410, /* florida.museum */ + 2933, /* farm.museum */ + 2720, /* farmequipment.museum */ + 2938, /* farmers.museum */ +31699, /* farmstead.museum */ +31709, /* field.museum */ +31715, /* figueres.museum */ +31724, /* filatelia.museum */ + 3016, /* film.museum */ +31734, /* fineart.museum */ +31742, /* finearts.museum */ +31751, /* finland.museum */ +31759, /* flanders.museum */ +31768, /* florida.museum */ 270, /* force.museum */ -31418, /* fortmissoula.museum */ -31431, /* fortworth.museum */ - 3229, /* foundation.museum */ -31441, /* francaise.museum */ -31451, /* frankfurt.museum */ -31461, /* franziskaner.museum */ -31474, /* freemasonry.museum */ -31486, /* freiburg.museum */ -31495, /* fribourg.museum */ -31504, /* frog.museum */ -31509, /* fundacio.museum */ - 3332, /* furniture.museum */ - 3367, /* gallery.museum */ - 3417, /* garden.museum */ -12529, /* gateway.museum */ -31518, /* geelvinck.museum */ -31528, /* gemological.museum */ -31540, /* geology.museum */ -31548, /* georgia.museum */ -31556, /* giessen.museum */ -31564, /* glas.museum */ - 3546, /* glass.museum */ -31569, /* gorge.museum */ -31575, /* grandrapids.museum */ -31587, /* graz.museum */ -31592, /* guernsey.museum */ -31601, /* halloffame.museum */ - 3828, /* hamburg.museum */ -31612, /* handson.museum */ -31620, /* harvestcelebration.museum */ -31639, /* hawaii.museum */ - 3862, /* health.museum */ -31646, /* heimatunduhren.museum */ -31661, /* hellas.museum */ - 3874, /* helsinki.museum */ -31668, /* hembygdsforbund.museum */ -31692, /* heritage.museum */ -31701, /* histoire.museum */ -31710, /* historical.museum */ -31721, /* historicalsociety.museum */ -31739, /* historichouses.museum */ -31754, /* historisch.museum */ -31770, /* historisches.museum */ -30892, /* history.museum */ - 7064, /* historyofscience.museum */ -31793, /* horology.museum */ - 4105, /* house.museum */ -31802, /* humanities.museum */ -31813, /* illustration.museum */ -31826, /* imageandsound.museum */ -31840, /* indian.museum */ -31847, /* indiana.museum */ -31855, /* indianapolis.museum */ - 5223, /* indianmarket.museum */ -31868, /* intelligence.museum */ +31776, /* fortmissoula.museum */ +31789, /* fortworth.museum */ + 3214, /* foundation.museum */ +31799, /* francaise.museum */ +31809, /* frankfurt.museum */ +31819, /* franziskaner.museum */ +31832, /* freemasonry.museum */ +31844, /* freiburg.museum */ +31853, /* fribourg.museum */ +31862, /* frog.museum */ +31867, /* fundacio.museum */ + 3317, /* furniture.museum */ + 3352, /* gallery.museum */ + 3402, /* garden.museum */ +12688, /* gateway.museum */ +31876, /* geelvinck.museum */ +31886, /* gemological.museum */ +31898, /* geology.museum */ +31906, /* georgia.museum */ +31914, /* giessen.museum */ +31922, /* glas.museum */ + 3531, /* glass.museum */ +31927, /* gorge.museum */ +31933, /* grandrapids.museum */ +31945, /* graz.museum */ +31950, /* guernsey.museum */ +31959, /* halloffame.museum */ + 3810, /* hamburg.museum */ +31970, /* handson.museum */ +31978, /* harvestcelebration.museum */ +31997, /* hawaii.museum */ + 3844, /* health.museum */ +32004, /* heimatunduhren.museum */ +32019, /* hellas.museum */ + 3856, /* helsinki.museum */ +32026, /* hembygdsforbund.museum */ +32050, /* heritage.museum */ +32059, /* histoire.museum */ +32068, /* historical.museum */ +32079, /* historicalsociety.museum */ +32097, /* historichouses.museum */ +32112, /* historisch.museum */ +32128, /* historisches.museum */ +31250, /* history.museum */ + 7053, /* historyofscience.museum */ +32151, /* horology.museum */ + 4086, /* house.museum */ +32160, /* humanities.museum */ +32171, /* illustration.museum */ +32184, /* imageandsound.museum */ +32198, /* indian.museum */ +32205, /* indiana.museum */ +32213, /* indianapolis.museum */ + 5202, /* indianmarket.museum */ +32226, /* intelligence.museum */ 116, /* interactive.museum */ 478, /* iraq.museum */ -31881, /* iron.museum */ -31886, /* isleofman.museum */ -31896, /* jamison.museum */ -31904, /* jefferson.museum */ -31914, /* jerusalem.museum */ - 4439, /* jewelry.museum */ -31924, /* jewish.museum */ -31931, /* jewishart.museum */ - 3115, /* jfk.museum */ -31941, /* journalism.museum */ -31952, /* judaica.museum */ -31960, /* judygarland.museum */ -31972, /* juedisches.museum */ -31983, /* juif.museum */ -31988, /* karate.museum */ -11329, /* karikatur.museum */ -31995, /* kids.museum */ - 8344, /* koebenhavn.museum */ - 4636, /* koeln.museum */ -32000, /* kunst.museum */ -32006, /* kunstsammlung.museum */ -32020, /* kunstunddesign.museum */ -32035, /* labor.museum */ -32041, /* labour.museum */ -32048, /* lajolla.museum */ -32056, /* lancashire.museum */ -32067, /* landes.museum */ -32074, /* lans.museum */ -32079, /* larsson.museum */ -32087, /* lewismiller.museum */ - 4980, /* lincoln.museum */ - 5937, /* linz.museum */ - 5014, /* living.museum */ -32101, /* livinghistory.museum */ -32115, /* localhistory.museum */ - 5064, /* london.museum */ -32128, /* losangeles.museum */ -32139, /* louvre.museum */ -32146, /* loyalist.museum */ -32155, /* lucerne.museum */ -32163, /* luxembourg.museum */ -32174, /* luzern.museum */ +32239, /* iron.museum */ +32244, /* isleofman.museum */ +32254, /* jamison.museum */ +32262, /* jefferson.museum */ +32272, /* jerusalem.museum */ + 4418, /* jewelry.museum */ +32282, /* jewish.museum */ +32289, /* jewishart.museum */ + 3100, /* jfk.museum */ +32299, /* journalism.museum */ +32310, /* judaica.museum */ +32318, /* judygarland.museum */ +32330, /* juedisches.museum */ +32341, /* juif.museum */ +32346, /* karate.museum */ +11315, /* karikatur.museum */ +32353, /* kids.museum */ + 8337, /* koebenhavn.museum */ + 4615, /* koeln.museum */ +32358, /* kunst.museum */ +32364, /* kunstsammlung.museum */ +32378, /* kunstunddesign.museum */ +32393, /* labor.museum */ +32399, /* labour.museum */ +32406, /* lajolla.museum */ +32414, /* lancashire.museum */ +32425, /* landes.museum */ +32432, /* lans.museum */ +32437, /* larsson.museum */ +32445, /* lewismiller.museum */ + 4959, /* lincoln.museum */ + 5909, /* linz.museum */ + 4993, /* living.museum */ +32459, /* livinghistory.museum */ +32473, /* localhistory.museum */ + 5043, /* london.museum */ +32486, /* losangeles.museum */ +32497, /* louvre.museum */ +32504, /* loyalist.museum */ +32513, /* lucerne.museum */ +32521, /* luxembourg.museum */ +32532, /* luzern.museum */ 140, /* mad.museum */ - 5160, /* madrid.museum */ -32181, /* mallorca.museum */ -32190, /* manchester.museum */ -32201, /* mansion.museum */ -32209, /* mansions.museum */ -11905, /* manx.museum */ -32218, /* marburg.museum */ -32226, /* maritime.museum */ -32235, /* maritimo.museum */ -32244, /* maryland.museum */ -32253, /* marylhurst.museum */ - 5327, /* media.museum */ - 1315, /* medical.museum */ -32264, /* medizinhistorisches.museum */ -32284, /* meeres.museum */ - 5353, /* memorial.museum */ -32291, /* mesaverde.museum */ -32301, /* michigan.museum */ -32310, /* midatlantic.museum */ -32322, /* military.museum */ -32335, /* mill.museum */ -32340, /* miners.museum */ -32347, /* mining.museum */ -32354, /* minnesota.museum */ -32364, /* missile.museum */ -31422, /* missoula.museum */ -32372, /* modern.museum */ -32379, /* moma.museum */ - 5500, /* money.museum */ -32384, /* monmouth.museum */ -32393, /* monticello.museum */ -32404, /* montreal.museum */ - 5546, /* moscow.museum */ -32413, /* motorcycle.museum */ -32424, /* muenchen.museum */ -32433, /* muenster.museum */ - 4102, /* mulhouse.museum */ -32442, /* muncie.museum */ -32449, /* museet.museum */ -32456, /* museumcenter.museum */ -32469, /* museumvereniging.museum */ -17756, /* music.museum */ - 4313, /* national.museum */ -32486, /* nationalfirearms.museum */ -31684, /* nationalheritage.museum */ -29900, /* nativeamerican.museum */ -32503, /* naturalhistory.museum */ - 5630, /* naturalhistorymuseum.museum */ -32518, /* naturalsciences.museum */ -32534, /* nature.museum */ -31765, /* naturhistorisches.museum */ -32541, /* natuurwetenschappen.museum */ -32561, /* naumburg.museum */ -32570, /* naval.museum */ -32576, /* nebraska.museum */ - 2755, /* neues.museum */ -32585, /* newhampshire.museum */ -32598, /* newjersey.museum */ -32608, /* newmexico.museum */ -32618, /* newport.museum */ -32626, /* newspaper.museum */ -32636, /* newyork.museum */ -32644, /* niepce.museum */ -32651, /* norfolk.museum */ -32659, /* north.museum */ - 5921, /* nrw.museum */ -32665, /* nuernberg.museum */ -32675, /* nuremberg.museum */ - 5933, /* nyc.museum */ -32685, /* nyny.museum */ -32690, /* oceanographic.museum */ -32704, /* oceanographique.museum */ -32720, /* omaha.museum */ - 6023, /* online.museum */ -32726, /* ontario.museum */ -32734, /* openair.museum */ -32742, /* oregon.museum */ -32749, /* oregontrail.museum */ -32761, /* otago.museum */ - 3202, /* oxford.museum */ -32767, /* pacific.museum */ -32775, /* paderborn.museum */ -32785, /* palace.museum */ -32792, /* paleo.museum */ -32798, /* palmsprings.museum */ -32810, /* panama.museum */ - 6154, /* paris.museum */ -32817, /* pasadena.museum */ - 6217, /* pharmacy.museum */ -32826, /* philadelphia.museum */ -32839, /* philadelphiaarea.museum */ -32856, /* philately.museum */ -32866, /* phoenix.museum */ - 6244, /* photography.museum */ -32874, /* pilots.museum */ - 3497, /* pittsburgh.museum */ -32881, /* planetarium.museum */ -32893, /* plantation.museum */ -32904, /* plants.museum */ -32911, /* plaza.museum */ -32917, /* portal.museum */ -32924, /* portland.museum */ -32933, /* portlligat.museum */ -30855, /* posts-and-telecommunications.museum */ -32944, /* preservation.museum */ -32957, /* presidio.museum */ + 5139, /* madrid.museum */ +32539, /* mallorca.museum */ +32548, /* manchester.museum */ +32559, /* mansion.museum */ +32567, /* mansions.museum */ +12041, /* manx.museum */ +32576, /* marburg.museum */ +32584, /* maritime.museum */ +32593, /* maritimo.museum */ +32602, /* maryland.museum */ +32611, /* marylhurst.museum */ + 5306, /* media.museum */ + 1316, /* medical.museum */ +32622, /* medizinhistorisches.museum */ +32642, /* meeres.museum */ + 5332, /* memorial.museum */ +32649, /* mesaverde.museum */ +32659, /* michigan.museum */ +32668, /* midatlantic.museum */ +32680, /* military.museum */ +32693, /* mill.museum */ +32698, /* miners.museum */ +32705, /* mining.museum */ +32712, /* minnesota.museum */ +32722, /* missile.museum */ +31780, /* missoula.museum */ +32730, /* modern.museum */ +32737, /* moma.museum */ + 5479, /* money.museum */ +32742, /* monmouth.museum */ +32751, /* monticello.museum */ +32762, /* montreal.museum */ + 5525, /* moscow.museum */ +32771, /* motorcycle.museum */ +32782, /* muenchen.museum */ +32791, /* muenster.museum */ + 4083, /* mulhouse.museum */ +32800, /* muncie.museum */ +32807, /* museet.museum */ +32814, /* museumcenter.museum */ +32827, /* museumvereniging.museum */ +18101, /* music.museum */ + 4288, /* national.museum */ +32844, /* nationalfirearms.museum */ +32042, /* nationalheritage.museum */ +30263, /* nativeamerican.museum */ +32861, /* naturalhistory.museum */ + 5609, /* naturalhistorymuseum.museum */ +32876, /* naturalsciences.museum */ +32892, /* nature.museum */ +32123, /* naturhistorisches.museum */ +32899, /* natuurwetenschappen.museum */ +32919, /* naumburg.museum */ +32928, /* naval.museum */ +32934, /* nebraska.museum */ + 2754, /* neues.museum */ +32943, /* newhampshire.museum */ +32956, /* newjersey.museum */ +32966, /* newmexico.museum */ +32976, /* newport.museum */ +32984, /* newspaper.museum */ +32994, /* newyork.museum */ +33002, /* niepce.museum */ +33009, /* norfolk.museum */ +33017, /* north.museum */ + 5893, /* nrw.museum */ +33023, /* nuernberg.museum */ +33033, /* nuremberg.museum */ + 5905, /* nyc.museum */ +33043, /* nyny.museum */ +33048, /* oceanographic.museum */ +33062, /* oceanographique.museum */ +33078, /* omaha.museum */ + 6006, /* online.museum */ +33084, /* ontario.museum */ +33092, /* openair.museum */ +33100, /* oregon.museum */ +33107, /* oregontrail.museum */ +33119, /* otago.museum */ + 3187, /* oxford.museum */ +33125, /* pacific.museum */ +33133, /* paderborn.museum */ +33143, /* palace.museum */ +33150, /* paleo.museum */ +33156, /* palmsprings.museum */ +33168, /* panama.museum */ + 6137, /* paris.museum */ +33175, /* pasadena.museum */ + 6200, /* pharmacy.museum */ +33184, /* philadelphia.museum */ +33197, /* philadelphiaarea.museum */ +33214, /* philately.museum */ +33224, /* phoenix.museum */ + 6227, /* photography.museum */ +33232, /* pilots.museum */ + 3482, /* pittsburgh.museum */ +33239, /* planetarium.museum */ +33251, /* plantation.museum */ +33262, /* plants.museum */ +33269, /* plaza.museum */ +33275, /* portal.museum */ +33282, /* portland.museum */ +33291, /* portlligat.museum */ +31213, /* posts-and-telecommunications.museum */ +33302, /* preservation.museum */ +33315, /* presidio.museum */ 371, /* press.museum */ -32966, /* project.museum */ +33330, /* project.museum */ 711, /* public.museum */ -32974, /* pubol.museum */ - 6547, /* quebec.museum */ -32980, /* railroad.museum */ -32989, /* railway.museum */ - 1383, /* research.museum */ -32997, /* resistance.museum */ -33008, /* riodejaneiro.museum */ -33021, /* rochester.museum */ -33031, /* rockart.museum */ -17699, /* roma.museum */ -33039, /* russia.museum */ -33046, /* saintlouis.museum */ -31918, /* salem.museum */ -31050, /* salvadordali.museum */ -33057, /* salzburg.museum */ -33066, /* sandiego.museum */ - 1732, /* sanfrancisco.museum */ -33075, /* santabarbara.museum */ -33088, /* santacruz.museum */ -33098, /* santafe.museum */ -33106, /* saskatchewan.museum */ -33119, /* satx.museum */ -33124, /* savannahga.museum */ -33135, /* schlesisches.museum */ -33148, /* schoenbrunn.museum */ -33160, /* schokoladen.museum */ - 7042, /* school.museum */ -33172, /* schweiz.museum */ - 7073, /* science.museum */ -33180, /* science-fiction.museum */ -33196, /* scienceandhistory.museum */ -33214, /* scienceandindustry.museum */ -33233, /* sciencecenter.museum */ -33247, /* sciencecenters.museum */ -33262, /* sciencehistory.museum */ -32525, /* sciences.museum */ -33277, /* sciencesnaturelles.museum */ -33296, /* scotland.museum */ -33305, /* seaport.museum */ -33313, /* settlement.museum */ -33324, /* settlers.museum */ - 7216, /* shell.museum */ -33333, /* sherbrooke.museum */ -33344, /* sibenik.museum */ - 7278, /* silk.museum */ - 4585, /* ski.museum */ -33352, /* skole.museum */ -31731, /* society.museum */ -33358, /* sologne.museum */ -33366, /* soundandvision.museum */ -33381, /* southcarolina.museum */ -33395, /* southwest.museum */ - 2889, /* space.museum */ - 6524, /* spy.museum */ -33405, /* square.museum */ -33412, /* stadt.museum */ -33418, /* stalbans.museum */ -33427, /* starnberg.museum */ +33338, /* pubol.museum */ + 6530, /* quebec.museum */ +33344, /* railroad.museum */ +33353, /* railway.museum */ + 1384, /* research.museum */ +33361, /* resistance.museum */ +33372, /* riodejaneiro.museum */ +33385, /* rochester.museum */ +33395, /* rockart.museum */ +18044, /* roma.museum */ +33403, /* russia.museum */ +33410, /* saintlouis.museum */ +32276, /* salem.museum */ +31408, /* salvadordali.museum */ +33421, /* salzburg.museum */ +33430, /* sandiego.museum */ + 1733, /* sanfrancisco.museum */ +33439, /* santabarbara.museum */ +33452, /* santacruz.museum */ +33462, /* santafe.museum */ +33470, /* saskatchewan.museum */ +33483, /* satx.museum */ +33488, /* savannahga.museum */ +33499, /* schlesisches.museum */ +33512, /* schoenbrunn.museum */ +33524, /* schokoladen.museum */ + 7031, /* school.museum */ +33536, /* schweiz.museum */ + 7062, /* science.museum */ +33544, /* science-fiction.museum */ +33560, /* scienceandhistory.museum */ +33578, /* scienceandindustry.museum */ +33597, /* sciencecenter.museum */ +33611, /* sciencecenters.museum */ +33626, /* sciencehistory.museum */ +32883, /* sciences.museum */ +33641, /* sciencesnaturelles.museum */ +33660, /* scotland.museum */ +33669, /* seaport.museum */ +33677, /* settlement.museum */ +33688, /* settlers.museum */ + 7205, /* shell.museum */ +33697, /* sherbrooke.museum */ +33708, /* sibenik.museum */ + 7267, /* silk.museum */ + 4564, /* ski.museum */ +33716, /* skole.museum */ +32089, /* society.museum */ +33722, /* sologne.museum */ +33730, /* soundandvision.museum */ +33745, /* southcarolina.museum */ +33759, /* southwest.museum */ + 2874, /* space.museum */ + 6507, /* spy.museum */ +33769, /* square.museum */ +33776, /* stadt.museum */ +33782, /* stalbans.museum */ +33791, /* starnberg.museum */ 331, /* state.museum */ -31096, /* stateofdelaware.museum */ - 6355, /* station.museum */ - 7732, /* steam.museum */ -33437, /* steiermark.museum */ - 3950, /* stjohn.museum */ - 7496, /* stockholm.museum */ -33448, /* stpetersburg.museum */ -33461, /* stuttgart.museum */ -33471, /* suisse.museum */ -33478, /* surgeonshall.museum */ -33491, /* surrey.museum */ -33498, /* svizzera.museum */ -33507, /* sweden.museum */ - 7628, /* sydney.museum */ -33514, /* tank.museum */ - 1862, /* tcm.museum */ - 7738, /* technology.museum */ -33519, /* telekommunikation.museum */ - 8295, /* television.museum */ -33537, /* texas.museum */ -33543, /* textile.museum */ - 7810, /* theater.museum */ - 7261, /* time.museum */ -33551, /* timekeeping.museum */ -33563, /* topology.museum */ -17867, /* torino.museum */ -33572, /* touch.museum */ - 1402, /* town.museum */ -15689, /* transport.museum */ - 2641, /* tree.museum */ -33578, /* trolley.museum */ - 8041, /* trust.museum */ -33586, /* trustee.museum */ -31655, /* uhren.museum */ -33594, /* ulm.museum */ -33598, /* undersea.museum */ - 8132, /* university.museum */ -17643, /* usa.museum */ -29990, /* usantiques.museum */ -33607, /* usarts.museum */ -30977, /* uscountryestate.museum */ -31034, /* usculture.museum */ -31079, /* usdecorativearts.museum */ - 3415, /* usgarden.museum */ -31783, /* ushistory.museum */ -33614, /* ushuaia.museum */ -32099, /* uslivinghistory.museum */ -11873, /* utah.museum */ -11434, /* uvic.museum */ -16188, /* valley.museum */ -17820, /* vantaa.museum */ -33622, /* versailles.museum */ - 8257, /* viking.museum */ -33633, /* village.museum */ -33641, /* virginia.museum */ -33650, /* virtual.museum */ -33658, /* virtuel.museum */ - 8333, /* vlaanderen.museum */ -33666, /* volkenkunde.museum */ - 8412, /* wales.museum */ -33678, /* wallonie.museum */ -30735, /* war.museum */ -33687, /* washingtondc.museum */ -33700, /* watch-and-clock.museum */ -30747, /* watchandclock.museum */ -33716, /* western.museum */ -33724, /* westfalen.museum */ -33734, /* whaling.museum */ -33742, /* wildlife.museum */ -30803, /* williamsburg.museum */ -32331, /* windmill.museum */ - 7241, /* workshop.museum */ -33751, /* xn--9dbhblg6di.museum */ -33766, /* xn--comunicaes-v6a2o.museum */ -33787, /* xn--correios-e-telecomunicaes-ghc29a.museum */ -33824, /* xn--h1aegh.museum */ -33835, /* xn--lns-qla.museum */ -32639, /* york.museum */ -33847, /* yorkshire.museum */ -33857, /* yosemite.museum */ -33866, /* youth.museum */ -33872, /* zoological.museum */ -33883, /* zoology.museum */ +31454, /* stateofdelaware.museum */ + 6338, /* station.museum */ + 7730, /* steam.museum */ +33801, /* steiermark.museum */ + 3932, /* stjohn.museum */ + 7494, /* stockholm.museum */ +33812, /* stpetersburg.museum */ +33825, /* stuttgart.museum */ +33835, /* suisse.museum */ +33842, /* surgeonshall.museum */ +33855, /* surrey.museum */ +33862, /* svizzera.museum */ +33871, /* sweden.museum */ + 7626, /* sydney.museum */ +33878, /* tank.museum */ + 1863, /* tcm.museum */ + 7736, /* technology.museum */ +33883, /* telekommunikation.museum */ + 8288, /* television.museum */ +33901, /* texas.museum */ +33907, /* textile.museum */ + 7808, /* theater.museum */ + 7250, /* time.museum */ +33915, /* timekeeping.museum */ +33927, /* topology.museum */ +18212, /* torino.museum */ +33936, /* touch.museum */ + 1403, /* town.museum */ +15982, /* transport.museum */ + 2640, /* tree.museum */ +33942, /* trolley.museum */ + 8039, /* trust.museum */ +33950, /* trustee.museum */ +32013, /* uhren.museum */ +33958, /* ulm.museum */ +33962, /* undersea.museum */ + 8130, /* university.museum */ +17988, /* usa.museum */ +30353, /* usantiques.museum */ +33971, /* usarts.museum */ +31335, /* uscountryestate.museum */ +31392, /* usculture.museum */ +31437, /* usdecorativearts.museum */ + 3400, /* usgarden.museum */ +32141, /* ushistory.museum */ +33978, /* ushuaia.museum */ +32457, /* uslivinghistory.museum */ +12009, /* utah.museum */ +11459, /* uvic.museum */ +16533, /* valley.museum */ +18165, /* vantaa.museum */ +33986, /* versailles.museum */ + 8250, /* viking.museum */ +33997, /* village.museum */ +34005, /* virginia.museum */ +34014, /* virtual.museum */ +34022, /* virtuel.museum */ + 8326, /* vlaanderen.museum */ +34030, /* volkenkunde.museum */ + 8405, /* wales.museum */ +34042, /* wallonie.museum */ +31093, /* war.museum */ +34051, /* washingtondc.museum */ +34064, /* watch-and-clock.museum */ +31105, /* watchandclock.museum */ +34080, /* western.museum */ +34088, /* westfalen.museum */ +34098, /* whaling.museum */ +34106, /* wildlife.museum */ +31161, /* williamsburg.museum */ +32689, /* windmill.museum */ + 7230, /* workshop.museum */ +34115, /* xn--9dbhblg6di.museum */ +34130, /* xn--comunicaes-v6a2o.museum */ +34151, /* xn--correios-e-telecomunicaes-ghc29a.museum */ +34188, /* xn--h1aegh.museum */ +34199, /* xn--lns-qla.museum */ +32997, /* york.museum */ +34211, /* yorkshire.museum */ +34221, /* yosemite.museum */ +34230, /* youth.museum */ +34236, /* zoological.museum */ +34247, /* zoology.museum */ 164, /* aero.mv */ - 985, /* biz.mv */ - 1913, /* com.mv */ - 2047, /* coop.mv */ - 2624, /* edu.mv */ - 3686, /* gov.mv */ - 3167, /* info.mv */ - 3632, /* int.mv */ - 4195, /* mil.mv */ - 5644, /* museum.mv */ - 5725, /* name.mv */ - 4185, /* net.mv */ - 6070, /* org.mv */ - 6427, /* pro.mv */ + 986, /* biz.mv */ + 1914, /* com.mv */ + 2048, /* coop.mv */ + 2623, /* edu.mv */ + 3671, /* gov.mv */ + 3152, /* info.mv */ + 3617, /* int.mv */ + 4170, /* mil.mv */ + 5623, /* museum.mv */ + 5695, /* name.mv */ + 5737, /* net.mv */ + 6053, /* org.mv */ + 6410, /* pro.mv */ 62, /* ac.mw */ - 985, /* biz.mw */ + 986, /* biz.mw */ 113, /* co.mw */ - 1913, /* com.mw */ - 2047, /* coop.mw */ - 2624, /* edu.mw */ - 3686, /* gov.mw */ - 3632, /* int.mw */ - 5644, /* museum.mw */ - 4185, /* net.mw */ - 6070, /* org.mw */ -10666, /* blogspot.mx */ - 1913, /* com.mx */ - 2624, /* edu.mx */ -11325, /* gob.mx */ - 4185, /* net.mx */ - 6070, /* org.mx */ -10666, /* blogspot.my */ - 1913, /* com.my */ - 2624, /* edu.my */ - 3686, /* gov.my */ - 4195, /* mil.my */ - 5725, /* name.my */ - 4185, /* net.my */ - 6070, /* org.my */ + 1914, /* com.mw */ + 2048, /* coop.mw */ + 2623, /* edu.mw */ + 3671, /* gov.mw */ + 3617, /* int.mw */ + 5623, /* museum.mw */ + 5737, /* net.mw */ + 6053, /* org.mw */ +10645, /* blogspot.mx */ + 1914, /* com.mx */ + 2623, /* edu.mx */ +11304, /* gob.mx */ + 5737, /* net.mx */ + 6053, /* org.mx */ +10645, /* blogspot.my */ + 1914, /* com.my */ + 2623, /* edu.my */ + 3671, /* gov.my */ + 4170, /* mil.my */ + 5695, /* name.my */ + 5737, /* net.my */ + 6053, /* org.my */ 62, /* ac.mz */ -11632, /* adv.mz */ +11665, /* adv.mz */ 113, /* co.mz */ - 2624, /* edu.mz */ - 3686, /* gov.mz */ - 4195, /* mil.mz */ - 4185, /* net.mz */ - 6070, /* org.mz */ + 2623, /* edu.mz */ + 3671, /* gov.mz */ + 4170, /* mil.mz */ + 5737, /* net.mz */ + 6053, /* org.mz */ 221, /* ca.na */ - 1579, /* cc.na */ + 1580, /* cc.na */ 113, /* co.na */ - 1913, /* com.na */ -11349, /* dr.na */ + 1914, /* com.na */ +11335, /* dr.na */ 898, /* in.na */ - 3167, /* info.na */ - 5448, /* mobi.na */ - 3604, /* mx.na */ - 5725, /* name.na */ + 3152, /* info.na */ + 5427, /* mobi.na */ + 3589, /* mx.na */ + 5695, /* name.na */ 137, /* or.na */ - 6070, /* org.na */ - 6427, /* pro.na */ - 7042, /* school.na */ - 2546, /* tv.na */ + 6053, /* org.na */ + 6410, /* pro.na */ + 7031, /* school.na */ + 2549, /* tv.na */ 264, /* us.na */ 659, /* ws.na */ - 3673, /* forgot.her.name */ -11614, /* asso.nc */ + 3658, /* forgot.her.name */ +11647, /* asso.nc */ + 5970, /* nom.nc */ 138, /* r.cdn77.net */ 2, /* a.prod.fastly.net */ - 3563, /* global.prod.fastly.net */ + 3548, /* global.prod.fastly.net */ 2, /* a.ssl.fastly.net */ 18, /* b.ssl.fastly.net */ - 3563, /* global.ssl.fastly.net */ - 6175, /* arts.nf */ - 1913, /* com.nf */ -11959, /* firm.nf */ - 3167, /* info.nf */ - 4185, /* net.nf */ - 1224, /* other.nf */ - 4523, /* per.nf */ - 2608, /* rec.nf */ - 7514, /* store.nf */ -11967, /* web.nf */ + 3548, /* global.ssl.fastly.net */ + 5198, /* map.fastlylb.net */ + 6158, /* arts.nf */ + 1914, /* com.nf */ +12095, /* firm.nf */ + 3152, /* info.nf */ + 5737, /* net.nf */ + 1225, /* other.nf */ + 4502, /* per.nf */ +11769, /* rec.nf */ + 7512, /* store.nf */ +12109, /* web.nf */ 62, /* ac.ni */ - 985, /* biz.ni */ + 986, /* biz.ni */ 113, /* co.ni */ - 1913, /* com.ni */ - 2624, /* edu.ni */ -11325, /* gob.ni */ + 1914, /* com.ni */ + 2623, /* edu.ni */ +11304, /* gob.ni */ 898, /* in.ni */ - 3167, /* info.ni */ - 3632, /* int.ni */ - 4195, /* mil.ni */ - 4185, /* net.ni */ - 5998, /* nom.ni */ - 6070, /* org.ni */ -11967, /* web.ni */ - 3760, /* gs.aa.no */ - 8069, /* nes.akershus.no */ + 3152, /* info.ni */ + 3617, /* int.ni */ + 4170, /* mil.ni */ + 5737, /* net.ni */ + 5970, /* nom.ni */ + 6053, /* org.ni */ +12109, /* web.ni */ + 3745, /* gs.aa.no */ + 8067, /* nes.akershus.no */ 637, /* os.hedmark.no */ -35773, /* valer.hedmark.no */ -40788, /* xn--vler-qoa.hedmark.no */ +36192, /* valer.hedmark.no */ +41194, /* xn--vler-qoa.hedmark.no */ 637, /* os.hordaland.no */ -40801, /* heroy.more-og-romsdal.no */ -40807, /* sande.more-og-romsdal.no */ - 1086, /* bo.nordland.no */ -40801, /* heroy.nordland.no */ -40813, /* xn--b-5ga.nordland.no */ -40823, /* xn--hery-ira.nordland.no */ -35773, /* valer.ostfold.no */ - 1086, /* bo.telemark.no */ -40813, /* xn--b-5ga.telemark.no */ -40807, /* sande.vestfold.no */ -40807, /* sande.xn--mre-og-romsdal-qqb.no */ -40823, /* xn--hery-ira.xn--mre-og-romsdal-qqb.no */ -40788, /* xn--vler-qoa.xn--stfold-9xa.no */ -40836, /* merseine.nu */ -25356, /* mine.nu */ -40845, /* shacknet.nu */ +41207, /* heroy.more-og-romsdal.no */ +41213, /* sande.more-og-romsdal.no */ + 1087, /* bo.nordland.no */ +41207, /* heroy.nordland.no */ +41219, /* xn--b-5ga.nordland.no */ +41229, /* xn--hery-ira.nordland.no */ +36192, /* valer.ostfold.no */ + 1087, /* bo.telemark.no */ +41219, /* xn--b-5ga.telemark.no */ +41213, /* sande.vestfold.no */ +41213, /* sande.xn--mre-og-romsdal-qqb.no */ +41229, /* xn--hery-ira.xn--mre-og-romsdal-qqb.no */ +41194, /* xn--vler-qoa.xn--stfold-9xa.no */ +41242, /* merseine.nu */ +25701, /* mine.nu */ +41251, /* shacknet.nu */ 113, /* co.om */ - 1913, /* com.om */ - 2624, /* edu.om */ - 3686, /* gov.om */ - 1858, /* med.om */ - 5644, /* museum.om */ - 4185, /* net.om */ - 6070, /* org.om */ - 6427, /* pro.om */ - 4994, /* homelink.one */ -17123, /* tele.amune.org */ + 1914, /* com.om */ + 2623, /* edu.om */ + 3671, /* gov.om */ + 1859, /* med.om */ + 5623, /* museum.om */ + 5737, /* net.om */ + 6053, /* org.om */ + 6410, /* pro.om */ + 4973, /* homelink.one */ +11518, /* barsy.online */ +41806, /* tele.amune.org */ 36, /* c.cdn77.org */ -41365, /* rsc.cdn77.org */ -13051, /* ssl.origin.cdn77-secure.org */ +41811, /* rsc.cdn77.org */ +13210, /* ssl.origin.cdn77-secure.org */ 257, /* go.dyndns.org */ - 6804, /* home.dyndns.org */ + 6787, /* home.dyndns.org */ 290, /* al.eu.org */ -11614, /* asso.eu.org */ +11647, /* asso.eu.org */ 562, /* at.eu.org */ 584, /* au.eu.org */ 860, /* be.eu.org */ - 931, /* bg.eu.org */ + 932, /* bg.eu.org */ 221, /* ca.eu.org */ - 1583, /* cd.eu.org */ - 1146, /* ch.eu.org */ + 1584, /* cd.eu.org */ + 1147, /* ch.eu.org */ 842, /* cn.eu.org */ 241, /* cy.eu.org */ - 2202, /* cz.eu.org */ - 2276, /* de.eu.org */ - 2470, /* dk.eu.org */ - 2624, /* edu.eu.org */ - 1886, /* ee.eu.org */ + 2203, /* cz.eu.org */ + 2277, /* de.eu.org */ + 2473, /* dk.eu.org */ + 2623, /* edu.eu.org */ + 1887, /* ee.eu.org */ 558, /* es.eu.org */ - 3009, /* fi.eu.org */ - 3245, /* fr.eu.org */ - 3697, /* gr.eu.org */ - 4118, /* hr.eu.org */ - 4138, /* hu.eu.org */ + 2994, /* fi.eu.org */ + 3230, /* fr.eu.org */ + 3682, /* gr.eu.org */ + 4099, /* hr.eu.org */ + 4119, /* hu.eu.org */ 31, /* ie.eu.org */ - 2653, /* il.eu.org */ + 2652, /* il.eu.org */ 898, /* in.eu.org */ - 3632, /* int.eu.org */ - 3722, /* is.eu.org */ - 2098, /* it.eu.org */ - 4495, /* jp.eu.org */ - 3123, /* kr.eu.org */ + 3617, /* int.eu.org */ + 3707, /* is.eu.org */ + 2099, /* it.eu.org */ + 4474, /* jp.eu.org */ + 3108, /* kr.eu.org */ 151, /* lt.eu.org */ - 5112, /* lu.eu.org */ - 5147, /* lv.eu.org */ - 5307, /* mc.eu.org */ - 1693, /* me.eu.org */ - 5433, /* mk.eu.org */ - 5609, /* mt.eu.org */ + 5091, /* lu.eu.org */ + 5126, /* lv.eu.org */ + 5286, /* mc.eu.org */ + 1694, /* me.eu.org */ + 5412, /* mk.eu.org */ + 5588, /* mt.eu.org */ 70, /* my.eu.org */ - 4185, /* net.eu.org */ - 971, /* ng.eu.org */ - 1071, /* nl.eu.org */ - 1517, /* no.eu.org */ + 5737, /* net.eu.org */ + 972, /* ng.eu.org */ + 1072, /* nl.eu.org */ + 1518, /* no.eu.org */ 325, /* nz.eu.org */ - 6154, /* paris.eu.org */ - 5089, /* pl.eu.org */ - 6510, /* pt.eu.org */ -41376, /* q-a.eu.org */ + 6137, /* paris.eu.org */ + 5068, /* pl.eu.org */ + 6493, /* pt.eu.org */ +41822, /* q-a.eu.org */ 166, /* ro.eu.org */ - 2190, /* ru.eu.org */ - 1498, /* se.eu.org */ - 2364, /* si.eu.org */ - 7312, /* sk.eu.org */ - 3302, /* tr.eu.org */ - 8122, /* uk.eu.org */ + 2191, /* ru.eu.org */ + 1499, /* se.eu.org */ + 2365, /* si.eu.org */ + 7301, /* sk.eu.org */ + 3287, /* tr.eu.org */ + 8120, /* uk.eu.org */ 264, /* us.eu.org */ -15166, /* nerdpol.ovh */ - 1085, /* abo.pa */ +15443, /* nerdpol.ovh */ + 1086, /* abo.pa */ 62, /* ac.pa */ - 1913, /* com.pa */ - 2624, /* edu.pa */ -11325, /* gob.pa */ - 970, /* ing.pa */ - 1858, /* med.pa */ - 4185, /* net.pa */ - 5998, /* nom.pa */ - 6070, /* org.pa */ -15162, /* sld.pa */ -10666, /* blogspot.pe */ - 1913, /* com.pe */ - 2624, /* edu.pe */ -11325, /* gob.pe */ - 4195, /* mil.pe */ - 4185, /* net.pe */ - 5998, /* nom.pe */ - 6070, /* org.pe */ - 1913, /* com.pf */ - 2624, /* edu.pf */ - 6070, /* org.pf */ - 1913, /* com.ph */ - 2624, /* edu.ph */ - 3686, /* gov.ph */ + 1914, /* com.pa */ + 2623, /* edu.pa */ +11304, /* gob.pa */ + 971, /* ing.pa */ + 1859, /* med.pa */ + 5737, /* net.pa */ + 5970, /* nom.pa */ + 6053, /* org.pa */ +15439, /* sld.pa */ +10645, /* blogspot.pe */ + 1914, /* com.pe */ + 2623, /* edu.pe */ +11304, /* gob.pe */ + 4170, /* mil.pe */ + 5737, /* net.pe */ + 5970, /* nom.pe */ + 6053, /* org.pe */ + 1914, /* com.pf */ + 2623, /* edu.pf */ + 6053, /* org.pf */ + 1914, /* com.ph */ + 2623, /* edu.ph */ + 3671, /* gov.ph */ 58, /* i.ph */ - 4195, /* mil.ph */ - 4185, /* net.ph */ - 976, /* ngo.ph */ - 6070, /* org.ph */ - 985, /* biz.pk */ - 1913, /* com.pk */ - 2624, /* edu.pk */ + 4170, /* mil.ph */ + 5737, /* net.ph */ + 977, /* ngo.ph */ + 6053, /* org.ph */ +11506, /* 1337.pictures */ + 986, /* biz.pk */ + 1914, /* com.pk */ + 2623, /* edu.pk */ 402, /* fam.pk */ -11325, /* gob.pk */ -41380, /* gok.pk */ -32745, /* gon.pk */ - 3669, /* gop.pk */ - 4515, /* gos.pk */ - 3686, /* gov.pk */ - 3167, /* info.pk */ - 4185, /* net.pk */ - 6070, /* org.pk */ -11967, /* web.pk */ - 1662, /* ap.gov.pl */ -42422, /* griw.gov.pl */ +11304, /* gob.pk */ +41826, /* gok.pk */ +33103, /* gon.pk */ + 3654, /* gop.pk */ + 4494, /* gos.pk */ + 3671, /* gov.pk */ + 3152, /* info.pk */ + 5737, /* net.pk */ + 6053, /* org.pk */ +12109, /* web.pk */ + 1663, /* ap.gov.pl */ +42868, /* griw.gov.pl */ 715, /* ic.gov.pl */ - 3722, /* is.gov.pl */ -42427, /* kmpsp.gov.pl */ -42433, /* konsulat.gov.pl */ -42442, /* kppsp.gov.pl */ -42448, /* kwp.gov.pl */ -42452, /* kwpsp.gov.pl */ -42458, /* mup.gov.pl */ - 1063, /* mw.gov.pl */ -42462, /* oirm.gov.pl */ -42467, /* oum.gov.pl */ + 3707, /* is.gov.pl */ +42873, /* kmpsp.gov.pl */ +42879, /* konsulat.gov.pl */ +42888, /* kppsp.gov.pl */ +42894, /* kwp.gov.pl */ +42898, /* kwpsp.gov.pl */ +42904, /* mup.gov.pl */ + 1064, /* mw.gov.pl */ +42908, /* oirm.gov.pl */ +42913, /* oum.gov.pl */ 522, /* pa.gov.pl */ -42471, /* pinb.gov.pl */ -42476, /* piw.gov.pl */ - 6969, /* po.gov.pl */ -42429, /* psp.gov.pl */ -42480, /* psse.gov.pl */ -42485, /* pup.gov.pl */ - 3818, /* rzgw.gov.pl */ - 1493, /* sa.gov.pl */ -42489, /* sdn.gov.pl */ -35272, /* sko.gov.pl */ - 7345, /* so.gov.pl */ - 7437, /* sr.gov.pl */ -42493, /* starostwo.gov.pl */ - 8114, /* ug.gov.pl */ -42503, /* ugim.gov.pl */ - 3226, /* um.gov.pl */ -42508, /* umig.gov.pl */ -42513, /* upow.gov.pl */ -17587, /* uppo.gov.pl */ +42917, /* pinb.gov.pl */ +42922, /* piw.gov.pl */ + 6958, /* po.gov.pl */ +42875, /* psp.gov.pl */ +42926, /* psse.gov.pl */ +42931, /* pup.gov.pl */ + 3800, /* rzgw.gov.pl */ + 1494, /* sa.gov.pl */ +42935, /* sdn.gov.pl */ +35691, /* sko.gov.pl */ + 7334, /* so.gov.pl */ + 7435, /* sr.gov.pl */ +42939, /* starostwo.gov.pl */ + 8112, /* ug.gov.pl */ +42949, /* ugim.gov.pl */ + 3211, /* um.gov.pl */ +42954, /* umig.gov.pl */ +42959, /* upow.gov.pl */ +17932, /* uppo.gov.pl */ 264, /* us.gov.pl */ -42522, /* uw.gov.pl */ -42525, /* uzs.gov.pl */ -42529, /* wif.gov.pl */ -42533, /* wiih.gov.pl */ -11749, /* winb.gov.pl */ -40783, /* wios.gov.pl */ -42538, /* witd.gov.pl */ -42543, /* wiw.gov.pl */ - 6884, /* wsa.gov.pl */ - 4677, /* wskr.gov.pl */ -11425, /* wuoz.gov.pl */ -42518, /* wzmiuw.gov.pl */ -42547, /* zp.gov.pl */ +42968, /* uw.gov.pl */ +42971, /* uzs.gov.pl */ +42975, /* wif.gov.pl */ +42979, /* wiih.gov.pl */ +11841, /* winb.gov.pl */ +41189, /* wios.gov.pl */ +42984, /* witd.gov.pl */ +42989, /* wiw.gov.pl */ + 6873, /* wsa.gov.pl */ + 4656, /* wskr.gov.pl */ +11450, /* wuoz.gov.pl */ +42964, /* wzmiuw.gov.pl */ +42993, /* zp.gov.pl */ 113, /* co.pn */ - 2624, /* edu.pn */ - 3686, /* gov.pn */ - 4185, /* net.pn */ - 6070, /* org.pn */ + 2623, /* edu.pn */ + 3671, /* gov.pn */ + 5737, /* net.pn */ + 6053, /* org.pn */ 62, /* ac.pr */ - 985, /* biz.pr */ - 1913, /* com.pr */ - 2624, /* edu.pr */ + 986, /* biz.pr */ + 1914, /* com.pr */ + 2623, /* edu.pr */ 902, /* est.pr */ - 3686, /* gov.pr */ - 3167, /* info.pr */ -42555, /* isla.pr */ - 5725, /* name.pr */ - 4185, /* net.pr */ - 6070, /* org.pr */ - 6427, /* pro.pr */ - 6448, /* prof.pr */ + 3671, /* gov.pr */ + 3152, /* info.pr */ +43001, /* isla.pr */ + 5695, /* name.pr */ + 5737, /* net.pr */ + 6053, /* org.pr */ + 6410, /* pro.pr */ + 6431, /* prof.pr */ 0, /* aaa.pro */ - 1302, /* aca.pro */ -16649, /* acct.pro */ - 1520, /* avocat.pro */ + 1303, /* aca.pro */ +16994, /* acct.pro */ + 1521, /* avocat.pro */ 736, /* bar.pro */ -11367, /* cloudns.pro */ -13231, /* cpa.pro */ -11645, /* eng.pro */ -42560, /* jur.pro */ - 4840, /* law.pro */ - 1858, /* med.pro */ - 4126, /* recht.pro */ - 1913, /* com.ps */ - 2624, /* edu.ps */ - 3686, /* gov.ps */ - 4185, /* net.ps */ - 6070, /* org.ps */ -17154, /* plo.ps */ - 1964, /* sec.ps */ -10666, /* blogspot.pt */ - 1913, /* com.pt */ - 2624, /* edu.pt */ - 3686, /* gov.pt */ - 3632, /* int.pt */ - 4185, /* net.pt */ -28860, /* nome.pt */ - 6070, /* org.pt */ -16378, /* publ.pt */ -42564, /* belau.pw */ -11367, /* cloudns.pw */ +11353, /* cloudns.pro */ +13390, /* cpa.pro */ +11692, /* eng.pro */ +43006, /* jur.pro */ + 4819, /* law.pro */ + 1859, /* med.pro */ + 4107, /* recht.pro */ + 1914, /* com.ps */ + 2623, /* edu.ps */ + 3671, /* gov.ps */ + 5737, /* net.ps */ + 6053, /* org.ps */ +17499, /* plo.ps */ + 1965, /* sec.ps */ +10645, /* blogspot.pt */ + 1914, /* com.pt */ + 2623, /* edu.pt */ + 3671, /* gov.pt */ + 3617, /* int.pt */ + 5737, /* net.pt */ +29205, /* nome.pt */ + 6053, /* org.pt */ +16723, /* publ.pt */ +43010, /* belau.pw */ +11353, /* cloudns.pw */ 113, /* co.pw */ - 1859, /* ed.pw */ + 1860, /* ed.pw */ 257, /* go.pw */ - 1203, /* ne.pw */ + 1204, /* ne.pw */ 137, /* or.pw */ - 1913, /* com.py */ - 2047, /* coop.py */ - 2624, /* edu.py */ - 3686, /* gov.py */ - 4195, /* mil.py */ - 4185, /* net.py */ - 6070, /* org.py */ -10666, /* blogspot.qa */ - 1913, /* com.qa */ - 2624, /* edu.qa */ - 3686, /* gov.qa */ - 4195, /* mil.qa */ - 5725, /* name.qa */ - 4185, /* net.qa */ - 6070, /* org.qa */ - 1145, /* sch.qa */ -11614, /* asso.re */ -10666, /* blogspot.re */ - 1913, /* com.re */ - 5998, /* nom.re */ - 6175, /* arts.ro */ -10666, /* blogspot.ro */ - 1913, /* com.ro */ -11959, /* firm.ro */ - 3167, /* info.ro */ - 5998, /* nom.ro */ + 1914, /* com.py */ + 2048, /* coop.py */ + 2623, /* edu.py */ + 3671, /* gov.py */ + 4170, /* mil.py */ + 5737, /* net.py */ + 6053, /* org.py */ +10645, /* blogspot.qa */ + 1914, /* com.qa */ + 2623, /* edu.qa */ + 3671, /* gov.qa */ + 4170, /* mil.qa */ + 5695, /* name.qa */ + 5737, /* net.qa */ + 6053, /* org.qa */ + 1146, /* sch.qa */ +11647, /* asso.re */ +10645, /* blogspot.re */ + 1914, /* com.re */ + 5970, /* nom.re */ +43016, /* clan.rip */ + 6158, /* arts.ro */ +10645, /* blogspot.ro */ + 1914, /* com.ro */ +12095, /* firm.ro */ + 3152, /* info.ro */ + 5970, /* nom.ro */ 97, /* nt.ro */ - 6070, /* org.ro */ - 2608, /* rec.ro */ - 7245, /* shop.ro */ - 7514, /* store.ro */ - 7910, /* tm.ro */ -11840, /* www.ro */ + 6053, /* org.ro */ +11769, /* rec.ro */ + 7234, /* shop.ro */ + 7512, /* store.ro */ + 7908, /* tm.ro */ +11947, /* www.ro */ +11404, /* lima-city.rocks */ + 7396, /* webspace.rocks */ 62, /* ac.rs */ -10666, /* blogspot.rs */ +10645, /* blogspot.rs */ 113, /* co.rs */ - 2624, /* edu.rs */ - 3686, /* gov.rs */ + 2623, /* edu.rs */ + 3671, /* gov.rs */ 898, /* in.rs */ - 6070, /* org.rs */ - 62, /* ac.ru */ -10666, /* blogspot.ru */ - 2624, /* edu.ru */ - 3686, /* gov.ru */ - 3632, /* int.ru */ - 4195, /* mil.ru */ -42550, /* test.ru */ + 6053, /* org.rs */ +11515, /* hb.cldmail.ru */ 62, /* ac.rw */ 113, /* co.rw */ - 1913, /* com.rw */ - 2624, /* edu.rw */ -11627, /* gouv.rw */ - 3686, /* gov.rw */ - 3632, /* int.rw */ - 4195, /* mil.rw */ - 4185, /* net.rw */ - 1913, /* com.sa */ - 2624, /* edu.sa */ - 3686, /* gov.sa */ - 1858, /* med.sa */ - 4185, /* net.sa */ - 6070, /* org.sa */ - 6513, /* pub.sa */ - 1145, /* sch.sa */ - 1913, /* com.sd */ - 2624, /* edu.sd */ - 3686, /* gov.sd */ - 3167, /* info.sd */ - 1858, /* med.sd */ - 4185, /* net.sd */ - 6070, /* org.sd */ - 2546, /* tv.sd */ + 1914, /* com.rw */ + 2623, /* edu.rw */ +11660, /* gouv.rw */ + 3671, /* gov.rw */ + 3617, /* int.rw */ + 4170, /* mil.rw */ + 5737, /* net.rw */ + 1914, /* com.sa */ + 2623, /* edu.sa */ + 3671, /* gov.sa */ + 1859, /* med.sa */ + 5737, /* net.sa */ + 6053, /* org.sa */ + 6496, /* pub.sa */ + 1146, /* sch.sa */ + 1914, /* com.sd */ + 2623, /* edu.sd */ + 3671, /* gov.sd */ + 3152, /* info.sd */ + 1859, /* med.sd */ + 5737, /* net.sd */ + 6053, /* org.sd */ + 2549, /* tv.sd */ 2, /* a.se */ 62, /* ac.se */ 18, /* b.se */ 855, /* bd.se */ -10666, /* blogspot.se */ -29954, /* brand.se */ +10645, /* blogspot.se */ +30317, /* brand.se */ 36, /* c.se */ - 1913, /* com.se */ + 1914, /* com.se */ 142, /* d.se */ 32, /* e.se */ 192, /* f.se */ - 4576, /* fh.se */ -42570, /* fhsk.se */ -42575, /* fhv.se */ + 4555, /* fh.se */ +43147, /* fhsk.se */ +43152, /* fhv.se */ 162, /* g.se */ 14, /* h.se */ 58, /* i.se */ 734, /* k.se */ -42579, /* komforb.se */ -42587, /* kommunalforbund.se */ -42603, /* komvux.se */ +43156, /* komforb.se */ +43164, /* kommunalforbund.se */ +43180, /* komvux.se */ 211, /* l.se */ -42610, /* lanbib.se */ +43187, /* lanbib.se */ 354, /* m.se */ 235, /* n.se */ -42617, /* naturbruksgymn.se */ +43194, /* naturbruksgymn.se */ 49, /* o.se */ - 6070, /* org.se */ + 6053, /* org.se */ 7, /* p.se */ -42632, /* parti.se */ +43209, /* parti.se */ 469, /* pp.se */ 371, /* press.se */ 138, /* r.se */ 110, /* s.se */ 25, /* t.se */ - 7910, /* tm.se */ + 7908, /* tm.se */ 585, /* u.se */ 650, /* w.se */ 398, /* x.se */ 71, /* y.se */ 326, /* z.se */ -10666, /* blogspot.sg */ - 1913, /* com.sg */ - 2624, /* edu.sg */ - 3686, /* gov.sg */ - 4185, /* net.sg */ - 6070, /* org.sg */ - 4523, /* per.sg */ -29762, /* cyon.site */ +10645, /* blogspot.sg */ + 1914, /* com.sg */ + 2623, /* edu.sg */ + 3671, /* gov.sg */ + 5737, /* net.sg */ + 6053, /* org.sg */ + 4502, /* per.sg */ 527, /* art.sn */ -10666, /* blogspot.sn */ - 1913, /* com.sn */ - 2624, /* edu.sn */ -11627, /* gouv.sn */ - 6070, /* org.sn */ -15614, /* perso.sn */ -42656, /* univ.sn */ - 1913, /* com.so */ - 4185, /* net.so */ - 6070, /* org.so */ -42661, /* stackspace.space */ +10645, /* blogspot.sn */ + 1914, /* com.sn */ + 2623, /* edu.sn */ +11660, /* gouv.sn */ + 6053, /* org.sn */ +15907, /* perso.sn */ +43244, /* univ.sn */ + 1914, /* com.so */ + 5737, /* net.so */ + 6053, /* org.so */ +43249, /* stackspace.space */ +43260, /* uber.space */ +43265, /* xs4all.space */ 113, /* co.st */ - 1913, /* com.st */ -42672, /* consulado.st */ - 2624, /* edu.st */ -42682, /* embaixada.st */ - 3686, /* gov.st */ - 4195, /* mil.st */ - 4185, /* net.st */ - 6070, /* org.st */ -42692, /* principe.st */ -25423, /* saotome.st */ - 7514, /* store.st */ -42701, /* adygeya.su */ -42709, /* arkhangelsk.su */ -42721, /* balashov.su */ -42730, /* bashkiria.su */ -42740, /* bryansk.su */ -42748, /* dagestan.su */ -42757, /* grozny.su */ -42764, /* ivanovo.su */ -42772, /* kalmykia.su */ -42781, /* kaluga.su */ -42788, /* karelia.su */ -42796, /* khakassia.su */ -42806, /* krasnodar.su */ -42816, /* kurgan.su */ -42823, /* lenug.su */ -42829, /* mordovia.su */ - 7311, /* msk.su */ -42838, /* murmansk.su */ -42847, /* nalchik.su */ -42855, /* nov.su */ -42859, /* obninsk.su */ -42867, /* penza.su */ -42873, /* pokrovsk.su */ -42882, /* sochi.su */ -11321, /* spb.su */ -42888, /* togliatti.su */ -42898, /* troitsk.su */ -42906, /* tula.su */ - 8158, /* tuva.su */ -42911, /* vladikavkaz.su */ -42923, /* vladimir.su */ -41518, /* vologda.su */ - 1913, /* com.sv */ - 2624, /* edu.sv */ -11325, /* gob.sv */ - 6070, /* org.sv */ - 4687, /* red.sv */ -42932, /* knightpoint.systems */ + 1914, /* com.st */ +43272, /* consulado.st */ + 2623, /* edu.st */ +43282, /* embaixada.st */ + 3671, /* gov.st */ + 4170, /* mil.st */ + 5737, /* net.st */ + 6053, /* org.st */ +43292, /* principe.st */ +25768, /* saotome.st */ + 7512, /* store.st */ +43301, /* abkhazia.su */ +43021, /* adygeya.su */ +43310, /* aktyubinsk.su */ +43321, /* arkhangelsk.su */ +43333, /* armenia.su */ +43341, /* ashgabad.su */ +43350, /* azerbaijan.su */ +43361, /* balashov.su */ +43029, /* bashkiria.su */ +43370, /* bryansk.su */ +43378, /* bukhara.su */ +43386, /* chimkent.su */ +43047, /* dagestan.su */ +43395, /* east-kazakhstan.su */ + 5735, /* exnet.su */ +31906, /* georgia.su */ +43056, /* grozny.su */ +43411, /* ivanovo.su */ +43419, /* jambyl.su */ +43063, /* kalmykia.su */ +43426, /* kaluga.su */ +43433, /* karacol.su */ +43441, /* karaganda.su */ +43451, /* karelia.su */ +43459, /* khakassia.su */ +43469, /* krasnodar.su */ +43479, /* kurgan.su */ +43072, /* kustanai.su */ +43486, /* lenug.su */ +43492, /* mangyshlak.su */ +43088, /* mordovia.su */ + 7300, /* msk.su */ +43503, /* murmansk.su */ +43103, /* nalchik.su */ +43512, /* navoi.su */ +43518, /* north-kazakhstan.su */ +43111, /* nov.su */ +43535, /* obninsk.su */ +43543, /* penza.su */ +43549, /* pokrovsk.su */ +43558, /* sochi.su */ +11300, /* spb.su */ +43564, /* tashkent.su */ +43573, /* termez.su */ +43580, /* togliatti.su */ +43590, /* troitsk.su */ +43598, /* tselinograd.su */ +43610, /* tula.su */ +43615, /* tuva.su */ +43126, /* vladikavkaz.su */ +43138, /* vladimir.su */ +41964, /* vologda.su */ + 1914, /* com.sv */ + 2623, /* edu.sv */ +11304, /* gob.sv */ + 6053, /* org.sv */ + 4666, /* red.sv */ +43620, /* knightpoint.systems */ 62, /* ac.sz */ 113, /* co.sz */ - 6070, /* org.sz */ + 6053, /* org.sz */ 62, /* ac.th */ 113, /* co.th */ 257, /* go.th */ 898, /* in.th */ - 5397, /* mi.th */ - 4185, /* net.th */ + 5376, /* mi.th */ + 5737, /* net.th */ 137, /* or.th */ 62, /* ac.tj */ - 985, /* biz.tj */ + 986, /* biz.tj */ 113, /* co.tj */ - 1913, /* com.tj */ - 2624, /* edu.tj */ + 1914, /* com.tj */ + 2623, /* edu.tj */ 257, /* go.tj */ - 3686, /* gov.tj */ - 3632, /* int.tj */ - 4195, /* mil.tj */ - 5725, /* name.tj */ - 4185, /* net.tj */ - 1815, /* nic.tj */ - 6070, /* org.tj */ -42550, /* test.tj */ -11967, /* web.tj */ + 3671, /* gov.tj */ + 3617, /* int.tj */ + 4170, /* mil.tj */ + 5695, /* name.tj */ + 5737, /* net.tj */ + 1816, /* nic.tj */ + 6053, /* org.tj */ +42996, /* test.tj */ +12109, /* web.tj */ 113, /* co.tm */ - 1913, /* com.tm */ - 2624, /* edu.tm */ - 3686, /* gov.tm */ - 4195, /* mil.tm */ - 4185, /* net.tm */ - 5998, /* nom.tm */ - 6070, /* org.tm */ -42944, /* agrinet.tn */ - 1913, /* com.tn */ -42952, /* defense.tn */ -42960, /* edunet.tn */ - 6192, /* ens.tn */ - 4231, /* fin.tn */ - 3686, /* gov.tn */ -11676, /* ind.tn */ - 3167, /* info.tn */ - 7904, /* intl.tn */ - 1910, /* mincom.tn */ + 1914, /* com.tm */ + 2623, /* edu.tm */ + 3671, /* gov.tm */ + 4170, /* mil.tm */ + 5737, /* net.tm */ + 5970, /* nom.tm */ + 6053, /* org.tm */ +43632, /* agrinet.tn */ + 1914, /* com.tn */ +43640, /* defense.tn */ +43648, /* edunet.tn */ + 6175, /* ens.tn */ + 4206, /* fin.tn */ + 3671, /* gov.tn */ +11731, /* ind.tn */ + 3152, /* info.tn */ + 7902, /* intl.tn */ + 1911, /* mincom.tn */ 561, /* nat.tn */ - 4185, /* net.tn */ - 6070, /* org.tn */ -15614, /* perso.tn */ -42967, /* rnrt.tn */ -11754, /* rns.tn */ - 5929, /* rnu.tn */ -42266, /* tourism.tn */ - 6676, /* turen.tn */ + 5737, /* net.tn */ + 6053, /* org.tn */ +15907, /* perso.tn */ +43655, /* rnrt.tn */ +11846, /* rns.tn */ + 5901, /* rnu.tn */ +42712, /* tourism.tn */ + 6659, /* turen.tn */ + 1914, /* com.to */ + 2623, /* edu.to */ + 3671, /* gov.to */ + 4170, /* mil.to */ + 5737, /* net.to */ + 6053, /* org.to */ +43660, /* vpnplus.to */ 164, /* aero.tt */ - 985, /* biz.tt */ + 986, /* biz.tt */ 113, /* co.tt */ - 1913, /* com.tt */ - 2047, /* coop.tt */ - 2624, /* edu.tt */ - 3686, /* gov.tt */ - 3167, /* info.tt */ - 3632, /* int.tt */ - 4475, /* jobs.tt */ - 5448, /* mobi.tt */ - 5644, /* museum.tt */ - 5725, /* name.tt */ - 4185, /* net.tt */ - 6070, /* org.tt */ - 6427, /* pro.tt */ - 8005, /* travel.tt */ -42980, /* better-than.tv */ -11526, /* dyndns.tv */ -42992, /* on-the-web.tv */ -43003, /* worse-than.tv */ -10666, /* blogspot.tw */ - 1849, /* club.tw */ - 1913, /* com.tw */ - 984, /* ebiz.tw */ - 2624, /* edu.tw */ - 3392, /* game.tw */ - 3686, /* gov.tw */ -15467, /* idv.tw */ - 4195, /* mil.tw */ - 4185, /* net.tw */ - 6070, /* org.tw */ -43014, /* xn--czrw28b.tw */ -15553, /* xn--uc0atv.tw */ -43026, /* xn--zf0ao64a.tw */ + 1914, /* com.tt */ + 2048, /* coop.tt */ + 2623, /* edu.tt */ + 3671, /* gov.tt */ + 3152, /* info.tt */ + 3617, /* int.tt */ + 4454, /* jobs.tt */ + 5427, /* mobi.tt */ + 5623, /* museum.tt */ + 5695, /* name.tt */ + 5737, /* net.tt */ + 6053, /* org.tt */ + 6410, /* pro.tt */ + 8003, /* travel.tt */ +43676, /* better-than.tv */ +11559, /* dyndns.tv */ +43688, /* on-the-web.tv */ +43699, /* worse-than.tv */ +43735, /* mymailer.com.tw */ 62, /* ac.tz */ 113, /* co.tz */ 257, /* go.tz */ - 7749, /* hotel.tz */ - 3167, /* info.tz */ - 1693, /* me.tz */ - 4195, /* mil.tz */ - 5448, /* mobi.tz */ - 1203, /* ne.tz */ + 7747, /* hotel.tz */ + 3152, /* info.tz */ + 1694, /* me.tz */ + 4170, /* mil.tz */ + 5427, /* mobi.tz */ + 1204, /* ne.tz */ 137, /* or.tz */ - 2158, /* sc.tz */ - 2546, /* tv.tz */ - 985, /* biz.ua */ - 1579, /* cc.ua */ -43039, /* cherkassy.ua */ -43049, /* cherkasy.ua */ - 3680, /* chernigov.ua */ - 3929, /* chernihiv.ua */ -43058, /* chernivtsi.ua */ -43069, /* chernovtsy.ua */ - 995, /* ck.ua */ + 2159, /* sc.tz */ + 2549, /* tv.tz */ + 986, /* biz.ua */ + 1580, /* cc.ua */ +43744, /* cherkassy.ua */ +43754, /* cherkasy.ua */ + 3665, /* chernigov.ua */ + 3911, /* chernihiv.ua */ +43763, /* chernivtsi.ua */ +43774, /* chernovtsy.ua */ + 996, /* ck.ua */ 842, /* cn.ua */ 113, /* co.ua */ - 1913, /* com.ua */ - 2091, /* cr.ua */ -43080, /* crimea.ua */ - 2176, /* cv.ua */ + 1914, /* com.ua */ + 2092, /* cr.ua */ +43785, /* crimea.ua */ + 2177, /* cv.ua */ 285, /* dn.ua */ -43087, /* dnepropetrovsk.ua */ -43102, /* dnipropetrovsk.ua */ -43117, /* dominic.ua */ -43125, /* donetsk.ua */ -43133, /* dp.ua */ - 2624, /* edu.ua */ - 3686, /* gov.ua */ - 5169, /* if.ua */ +43792, /* dnepropetrovsk.ua */ +43807, /* dnipropetrovsk.ua */ +43822, /* dominic.ua */ +43830, /* donetsk.ua */ +43838, /* dp.ua */ + 2623, /* edu.ua */ + 3671, /* gov.ua */ + 5148, /* if.ua */ 898, /* in.ua */ - 5824, /* inf.ua */ -43136, /* ivano-frankivsk.ua */ - 4582, /* kh.ua */ -43152, /* kharkiv.ua */ -43160, /* kharkov.ua */ -43168, /* kherson.ua */ -43176, /* khmelnitskiy.ua */ -43189, /* khmelnytskyi.ua */ -43202, /* kiev.ua */ -43207, /* kirovograd.ua */ - 4628, /* km.ua */ - 3123, /* kr.ua */ -43218, /* krym.ua */ - 6843, /* ks.ua */ -43223, /* kv.ua */ -43226, /* kyiv.ua */ -11693, /* lg.ua */ + 5796, /* inf.ua */ +43841, /* ivano-frankivsk.ua */ + 4561, /* kh.ua */ +43857, /* kharkiv.ua */ +43865, /* kharkov.ua */ +43873, /* kherson.ua */ +43881, /* khmelnitskiy.ua */ +43894, /* khmelnytskyi.ua */ +43907, /* kiev.ua */ +43912, /* kirovograd.ua */ + 4607, /* km.ua */ + 3108, /* kr.ua */ +43923, /* krym.ua */ + 6826, /* ks.ua */ +43928, /* kv.ua */ +43931, /* kyiv.ua */ +11781, /* lg.ua */ 151, /* lt.ua */ - 5103, /* ltd.ua */ -43231, /* lugansk.ua */ -43239, /* lutsk.ua */ - 5147, /* lv.ua */ -43245, /* lviv.ua */ - 5433, /* mk.ua */ -43250, /* mykolaiv.ua */ - 4185, /* net.ua */ -43259, /* nikolaev.ua */ - 3178, /* od.ua */ -15713, /* odesa.ua */ -43268, /* odessa.ua */ - 6070, /* org.ua */ - 5089, /* pl.ua */ -43275, /* poltava.ua */ + 5082, /* ltd.ua */ +43936, /* lugansk.ua */ +43944, /* lutsk.ua */ + 5126, /* lv.ua */ +43950, /* lviv.ua */ + 5412, /* mk.ua */ +43955, /* mykolaiv.ua */ + 5737, /* net.ua */ +43964, /* nikolaev.ua */ + 3163, /* od.ua */ +16006, /* odesa.ua */ +43973, /* odessa.ua */ + 6053, /* org.ua */ + 5068, /* pl.ua */ +43980, /* poltava.ua */ 469, /* pp.ua */ -43283, /* rivne.ua */ -43289, /* rovno.ua */ - 8048, /* rv.ua */ - 6991, /* sb.ua */ -43295, /* sebastopol.ua */ -43306, /* sevastopol.ua */ - 7331, /* sm.ua */ - 5682, /* sumy.ua */ +43988, /* rivne.ua */ +43994, /* rovno.ua */ + 8046, /* rv.ua */ + 6980, /* sb.ua */ +44000, /* sebastopol.ua */ +44011, /* sevastopol.ua */ + 7320, /* sm.ua */ + 5652, /* sumy.ua */ 334, /* te.ua */ -43317, /* ternopil.ua */ - 5902, /* uz.ua */ -43326, /* uzhgorod.ua */ -43335, /* vinnica.ua */ -43343, /* vinnytsia.ua */ - 8352, /* vn.ua */ -43353, /* volyn.ua */ -34391, /* yalta.ua */ -43359, /* zaporizhzhe.ua */ -43371, /* zaporizhzhia.ua */ -43384, /* zhitomir.ua */ -43393, /* zhytomyr.ua */ -42547, /* zp.ua */ - 4436, /* zt.ua */ +44022, /* ternopil.ua */ + 5874, /* uz.ua */ +44031, /* uzhgorod.ua */ +44040, /* vinnica.ua */ +44048, /* vinnytsia.ua */ + 8345, /* vn.ua */ +44058, /* volyn.ua */ +34810, /* yalta.ua */ +44064, /* zaporizhzhe.ua */ +44076, /* zaporizhzhia.ua */ +44089, /* zhitomir.ua */ +44098, /* zhytomyr.ua */ +42993, /* zp.ua */ + 4415, /* zt.ua */ 62, /* ac.ug */ -10666, /* blogspot.ug */ +10645, /* blogspot.ug */ 113, /* co.ug */ - 1913, /* com.ug */ + 1914, /* com.ug */ 257, /* go.ug */ - 1203, /* ne.ug */ + 1204, /* ne.ug */ 137, /* or.ug */ - 6070, /* org.ug */ - 2158, /* sc.ug */ -10666, /* blogspot.co.uk */ -11588, /* no-ip.co.uk */ -15218, /* wellbeingzone.co.uk */ - 5955, /* homeoffice.gov.uk */ -43413, /* service.gov.uk */ - 1579, /* cc.ak.us */ -15174, /* k12.ak.us */ -15182, /* lib.ak.us */ - 1579, /* cc.hi.us */ -15182, /* lib.hi.us */ -43459, /* chtr.k12.ma.us */ -43464, /* paroch.k12.ma.us */ -15459, /* pvt.k12.ma.us */ - 1579, /* cc.wv.us */ + 6053, /* org.ug */ + 2159, /* sc.ug */ +10645, /* blogspot.co.uk */ +11621, /* no-ip.co.uk */ +15495, /* wellbeingzone.co.uk */ + 5927, /* homeoffice.gov.uk */ +44118, /* service.gov.uk */ + 1580, /* cc.ak.us */ +15451, /* k12.ak.us */ +15459, /* lib.ak.us */ + 1580, /* cc.hi.us */ +15459, /* lib.hi.us */ +44164, /* chtr.k12.ma.us */ +44169, /* paroch.k12.ma.us */ +15736, /* pvt.k12.ma.us */ + 1580, /* cc.wv.us */ 113, /* co.uz */ - 1913, /* com.uz */ - 4185, /* net.uz */ - 6070, /* org.uz */ - 6175, /* arts.ve */ + 1914, /* com.uz */ + 5737, /* net.uz */ + 6053, /* org.uz */ + 6158, /* arts.ve */ 113, /* co.ve */ - 1913, /* com.ve */ -43475, /* e12.ve */ - 2624, /* edu.ve */ -11959, /* firm.ve */ -11325, /* gob.ve */ - 3686, /* gov.ve */ - 3167, /* info.ve */ - 3632, /* int.ve */ - 4195, /* mil.ve */ - 4185, /* net.ve */ - 6070, /* org.ve */ - 2608, /* rec.ve */ - 7514, /* store.ve */ - 7640, /* tec.ve */ -11967, /* web.ve */ + 1914, /* com.ve */ +44180, /* e12.ve */ + 2623, /* edu.ve */ +12095, /* firm.ve */ +11304, /* gob.ve */ + 3671, /* gov.ve */ + 3152, /* info.ve */ + 3617, /* int.ve */ + 4170, /* mil.ve */ + 5737, /* net.ve */ + 6053, /* org.ve */ +11769, /* rec.ve */ + 7512, /* store.ve */ + 7638, /* tec.ve */ +12109, /* web.ve */ 113, /* co.vi */ - 1913, /* com.vi */ -15174, /* k12.vi */ - 4185, /* net.vi */ - 6070, /* org.vi */ + 1914, /* com.vi */ +15451, /* k12.vi */ + 5737, /* net.vi */ + 6053, /* org.vi */ 62, /* ac.vn */ - 985, /* biz.vn */ -10666, /* blogspot.vn */ - 1913, /* com.vn */ - 2624, /* edu.vn */ - 3686, /* gov.vn */ - 3862, /* health.vn */ - 3167, /* info.vn */ - 3632, /* int.vn */ - 5725, /* name.vn */ - 4185, /* net.vn */ - 6070, /* org.vn */ - 6427, /* pro.vn */ - 1913, /* com.ws */ -11526, /* dyndns.ws */ - 2624, /* edu.ws */ - 3686, /* gov.ws */ -43479, /* mypets.ws */ - 4185, /* net.ws */ - 6070, /* org.ws */ -43486, /* xn--80au.xn--90a3ac */ -43495, /* xn--90azh.xn--90a3ac */ - 9065, /* xn--c1avg.xn--90a3ac */ -43505, /* xn--d1at.xn--90a3ac */ -43514, /* xn--o1ac.xn--90a3ac */ -43523, /* xn--o1ach.xn--90a3ac */ + 986, /* biz.vn */ +10645, /* blogspot.vn */ + 1914, /* com.vn */ + 2623, /* edu.vn */ + 3671, /* gov.vn */ + 3844, /* health.vn */ + 3152, /* info.vn */ + 3617, /* int.vn */ + 5695, /* name.vn */ + 5737, /* net.vn */ + 6053, /* org.vn */ + 6410, /* pro.vn */ +44191, /* xn--80au.xn--90a3ac */ +44200, /* xn--90azh.xn--90a3ac */ + 9044, /* xn--c1avg.xn--90a3ac */ +44210, /* xn--d1at.xn--90a3ac */ +44219, /* xn--o1ac.xn--90a3ac */ +44228, /* xn--o1ach.xn--90a3ac */ +44238, /* xn--12c1fe0br.xn--o3cw4h */ +44252, /* xn--12cfi8ixb8l.xn--o3cw4h */ +44268, /* xn--12co0c3b4eva.xn--o3cw4h */ +44285, /* xn--h3cuzk1di.xn--o3cw4h */ +44299, /* xn--m3ch0j3a.xn--o3cw4h */ +44312, /* xn--o3cyx2a.xn--o3cw4h */ 466, /* fhapp.xyz */ 62, /* ac.zm */ - 985, /* biz.zm */ + 986, /* biz.zm */ 113, /* co.zm */ - 1913, /* com.zm */ - 2624, /* edu.zm */ - 3686, /* gov.zm */ - 3167, /* info.zm */ - 4195, /* mil.zm */ - 4185, /* net.zm */ - 6070, /* org.zm */ - 1145, /* sch.zm */ + 1914, /* com.zm */ + 2623, /* edu.zm */ + 3671, /* gov.zm */ + 3152, /* info.zm */ + 4170, /* mil.zm */ + 5737, /* net.zm */ + 6053, /* org.zm */ + 1146, /* sch.zm */ + 62, /* ac.zw */ + 113, /* co.zw */ + 3671, /* gov.zw */ + 4170, /* mil.zw */ + 6053, /* org.zw */ }; -static const size_t kLeafChildOffset = 3549; -static const size_t kNumRootChildren = 1553; +static const size_t kLeafChildOffset = 3715; +static const size_t kNumRootChildren = 1549; From 3b03b617a0c1d5edd50be5ad23897c4066682b73 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 11:59:28 -0700 Subject: [PATCH 038/126] Fix missing import --- TrustKitTests/TSKPinConfigurationTests.m | 1 + 1 file changed, 1 insertion(+) diff --git a/TrustKitTests/TSKPinConfigurationTests.m b/TrustKitTests/TSKPinConfigurationTests.m index b926b333..612d6da5 100644 --- a/TrustKitTests/TSKPinConfigurationTests.m +++ b/TrustKitTests/TSKPinConfigurationTests.m @@ -16,6 +16,7 @@ #import "../TrustKit/Pinning/ssl_pin_verifier.h" #import "../TrustKit/Pinning/TSKPublicKeyAlgorithm.h" #import "../TrustKit/parse_configuration.h" +#import "../TrustKit/configuration_utils.h" #import "TSKCertificateUtils.h" @interface TSKPinConfigurationTests : XCTestCase From 52c28bdcc70ff2f47f0d28c58c48462a2ddd32ca Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 12:01:26 -0700 Subject: [PATCH 039/126] Make internal or test methods private --- TrustKit/Pinning/TSKSPKIHashCache.h | 10 --------- TrustKit/Pinning/TSKSPKIHashCache.m | 25 ++++++++++++++++------ TrustKitTests/TSKPinningValidatorTests.m | 10 +++++---- TrustKitTests/TSKPublicKeyAlgorithmTests.m | 9 ++++---- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/TrustKit/Pinning/TSKSPKIHashCache.h b/TrustKit/Pinning/TSKSPKIHashCache.h index aae7852e..b5dc5b36 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.h +++ b/TrustKit/Pinning/TSKSPKIHashCache.h @@ -48,16 +48,6 @@ typedef NSMutableDictionary SPKICacheDictionnary; */ - (NSData *)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certificate publicKeyAlgorithm:(TSKPublicKeyAlgorithm)publicKeyAlgorithm; -/** - Obtain the current cache used by this instance. - */ -@property (nonatomic, nullable, readonly) NSMutableDictionary *SPKICache; - -/** - Load the SPKI cache from the filesystem. This triggers blocking file I/O. - */ -@property (nonatomic, nullable, readonly) NSMutableDictionary *SPKICacheFromFileSystem; - @end NS_ASSUME_NONNULL_END diff --git a/TrustKit/Pinning/TSKSPKIHashCache.m b/TrustKit/Pinning/TSKSPKIHashCache.m index d38dee11..57f128fd 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -69,11 +69,19 @@ @interface TSKSPKIHashCache () + // Dictionnary to cache SPKI hashes instead of having to compute them on every connection // We store one cache dictionnary per TSKPublicKeyAlgorithm we support @property (nonatomic) NSMutableDictionary *subjectPublicKeyInfoHashesCache; @property (nonatomic) dispatch_queue_t lockQueue; @property (nonatomic) NSString *spkiCacheFilename; + + +/** + Load the SPKI cache from the filesystem. This triggers blocking file I/O. + */ +- (NSMutableDictionary *)loadSPKICacheFromFileSystem; + @end #if LEGACY_IOS_KEY_EXTRACTION @@ -110,7 +118,7 @@ - (instancetype)initWithIdentifier:(NSString *)uniqueIdentifier _spkiCacheFilename = uniqueIdentifier; // if this value is nil, persistence will always fail. // First try to load a cached version from the filesystem - _subjectPublicKeyInfoHashesCache = self.SPKICacheFromFileSystem; + _subjectPublicKeyInfoHashesCache = [self loadSPKICacheFromFileSystem]; TSKLog(@"Loaded %lu SPKI cache entries from the filesystem", (unsigned long)_subjectPublicKeyInfoHashesCache.count); if (_subjectPublicKeyInfoHashesCache == nil) { @@ -204,7 +212,7 @@ - (NSData *)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certifica return subjectPublicKeyInfoHash; } -- (NSMutableDictionary *)SPKICacheFromFileSystem +- (NSMutableDictionary *)loadSPKICacheFromFileSystem { NSMutableDictionary *spkiCache; NSData *serializedSpkiCache = [NSData dataWithContentsOfURL:[self SPKICachePath]]; @@ -214,10 +222,6 @@ - (NSData *)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certifica return spkiCache; } -- (NSMutableDictionary *)SPKICache -{ - return _subjectPublicKeyInfoHashesCache; -} #pragma mark Private @@ -410,9 +414,16 @@ - (NSData *)getPublicKeyDataFromCertificate_legacy_macos:(SecCertificateRef)cert @implementation TSKSPKIHashCache (TestSupport) -- (void)resetSubjectPublicKeyInfoDiskCache { +- (void)resetSubjectPublicKeyInfoDiskCache +{ // Discard SPKI cache [NSFileManager.defaultManager removeItemAtURL:[self SPKICachePath] error:nil]; } + +- (NSMutableDictionary *)getSubjectPublicKeyInfoHashesCache +{ + return _subjectPublicKeyInfoHashesCache; +} + @end diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 8546b70a..7bb297fc 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -35,6 +35,8 @@ - (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge @interface TSKSPKIHashCache (TestSupport) - (void)resetSubjectPublicKeyInfoDiskCache; +- (NSMutableDictionary *)getSubjectPublicKeyInfoHashesCache; +- (NSMutableDictionary *)loadSPKICacheFromFileSystem; @end static BOOL AllowsAdditionalTrustAnchors = YES; // toggle in tests if needed @@ -123,7 +125,7 @@ - (void)testVerifyAgainstAnyPublicKey ]}}}; // Ensure the SPKI cache was on the filesystem is empty - NSDictionary *fsCache = spkiCache.SPKICacheFromFileSystem; + NSDictionary *fsCache = [spkiCache loadSPKICacheFromFileSystem]; XCTAssert([fsCache[@1] count] == 0, @"SPKI cache for RSA 4096 must be empty before the test"); // First test the verifyPublicKeyPin() function @@ -165,7 +167,7 @@ - (void)testVerifyAgainstAnyPublicKey [self waitForExpectationsWithTimeout:2.0 handler:nil]; // Ensure the SPKI cache was persisted to the filesystem - fsCache = spkiCache.SPKICacheFromFileSystem; + fsCache = [spkiCache loadSPKICacheFromFileSystem]; XCTAssertEqual([fsCache[@1] count], 1UL, @"SPKI cache for RSA 4096 must be persisted to the file system"); CFRelease(trust); @@ -193,7 +195,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey ]}}}; // Ensure the SPKI cache was on the filesystem is empty - NSDictionary *fsCache = spkiCache.SPKICacheFromFileSystem; + NSDictionary *fsCache = [spkiCache loadSPKICacheFromFileSystem]; XCTAssertEqual([fsCache[@1] count], 0UL, @"SPKI cache for RSA 4096 must be empty before the test"); // First test the verifyPublicKeyPin() function @@ -236,7 +238,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey [self waitForExpectationsWithTimeout:2.0 handler:nil]; // Ensure the SPKI cache was persisted to the filesystem - fsCache = spkiCache.SPKICacheFromFileSystem; + fsCache = [spkiCache loadSPKICacheFromFileSystem]; XCTAssertEqual([fsCache[@1] count], 2UL, @"SPKI cache for RSA 4096 must be persisted to the file system"); CFRelease(trust); diff --git a/TrustKitTests/TSKPublicKeyAlgorithmTests.m b/TrustKitTests/TSKPublicKeyAlgorithmTests.m index 798648d7..73a92fc1 100644 --- a/TrustKitTests/TSKPublicKeyAlgorithmTests.m +++ b/TrustKitTests/TSKPublicKeyAlgorithmTests.m @@ -22,6 +22,7 @@ @interface TSKSPKIHashCache (TestSupport) - (void)resetSubjectPublicKeyInfoDiskCache; +- (NSMutableDictionary *)getSubjectPublicKeyInfoHashesCache; @end @@ -126,8 +127,8 @@ - (void)testVerifyMultipleAlgorithms kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", // Server Key @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}); - XCTAssertEqual([spkiCache.SPKICache[@0] count], 0UL, @"SPKI cache must be empty"); - XCTAssertEqual([spkiCache.SPKICache[@1] count], 0UL, @"SPKI cache must be empty"); + XCTAssertEqual([[spkiCache getSubjectPublicKeyInfoHashesCache][@0] count], 0UL, @"SPKI cache must be empty"); + XCTAssertEqual([[spkiCache getSubjectPublicKeyInfoHashesCache][@1] count], 0UL, @"SPKI cache must be empty"); TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; verificationResult = verifyPublicKeyPin(trust, @@ -137,8 +138,8 @@ - (void)testVerifyMultipleAlgorithms spkiCache); // Ensure the SPKI cache was used; the full certificate chain is three certs and we have to go through all of them to get to the pinned leaf - XCTAssertEqual([spkiCache.SPKICache[@0] count], 3UL, @"SPKI cache must have been used"); - XCTAssertEqual([spkiCache.SPKICache[@1] count], 3UL, @"SPKI cache must have been used"); + XCTAssertEqual([[spkiCache getSubjectPublicKeyInfoHashesCache][@0] count], 3UL, @"SPKI cache must have been used"); + XCTAssertEqual([[spkiCache getSubjectPublicKeyInfoHashesCache][@1] count], 3UL, @"SPKI cache must have been used"); CFRelease(trust); CFRelease(leafCertificate); From df10cc01ab27bd7bea657e2618400aa74ffc7df0 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 12:20:44 -0700 Subject: [PATCH 040/126] Update xcode version in CI --- circle.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/circle.yml b/circle.yml index 7e3c069d..b49ef767 100644 --- a/circle.yml +++ b/circle.yml @@ -1,10 +1,10 @@ machine: xcode: - version: 8.0 + version: 8.3 test: override: - # First run Xcode 7 tests - - sudo xcode-select --switch /Applications/Xcode-7.3.app + # First run Xcode 8.0 tests + - sudo xcode-select --switch /Applications/Xcode-8.0.app # iOS 8 platform with iOS 9 SDK - xcodebuild clean test CODE_SIGNING_REQUIRED=NO @@ -19,8 +19,9 @@ test: -destination 'platform=OS X' -sdk macosx10.11 -scheme "TrustKit OS X" - # Switch to Xcode 8 - - sudo xcode-select --switch /Applications/Xcode-8.0.app + + # Switch to Xcode 8.3 + - sudo xcode-select --switch /Applications/Xcode-8.3.app # iOS 10 platform with iOS 10 SDK - xcodebuild clean test CODE_SIGNING_REQUIRED=NO From 79455247eac9224de306f161cf2ba28e6576af0e Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 12:59:56 -0700 Subject: [PATCH 041/126] Tweak documentation --- TrustKit/TSKPinValidatorResult.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TrustKit/TSKPinValidatorResult.h b/TrustKit/TSKPinValidatorResult.h index bd37de39..083c9dc6 100644 --- a/TrustKit/TSKPinValidatorResult.h +++ b/TrustKit/TSKPinValidatorResult.h @@ -36,12 +36,12 @@ typedef NS_ENUM(NSInteger, TSKPinValidationResult) TSKPinValidationResultErrorInvalidParameters, /** - The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to OS X's trust store). Only available on OS X. + The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to the macOS trust store). Only available on macOS. */ TSKPinValidationResultFailedUserDefinedTrustAnchor NS_AVAILABLE_MAC(10_9), /** - The server trust could not be evaluated due to an error when trying to generate the certificate's subject public key info hash. On iOS, this could be caused by a Keychain failure when trying to extract the certificate's public key bytes. + The server trust could not be evaluated due to an error when trying to generate the certificate's subject public key info hash. On iOS 9 or below, this could be caused by a Keychain failure when trying to extract the certificate's public key bytes. */ TSKPinValidationResultErrorCouldNotGenerateSpkiHash, }; @@ -53,13 +53,13 @@ typedef NS_ENUM(NSInteger, TSKPinValidationResult) typedef NS_ENUM(NSInteger, TSKTrustDecision) { /** - Based on the server's certificate chain and the global pinning policy for this domain, the SSL connection should be allowed. + Based on the server's certificate chain and the configured pinning policy for this domain, the SSL connection should be allowed. This return value does not necessarily mean that the pinning validation succeded (for example if `kTSKEnforcePinning` was set to `NO` for this domain). If a pinning validation failure occured and if a report URI was configured, a pin failure report was sent. */ TSKTrustDecisionShouldAllowConnection, /** - Based on the server's certificate chain and the global pinning policy for this domain, the SSL connection should be blocked. + Based on the server's certificate chain and the configured pinning policy for this domain, the SSL connection should be blocked. A pinning validation failure occured and if a report URI was configured, a pin failure report was sent. */ TSKTrustDecisionShouldBlockConnection, From af67e513ba92abf44023c55f2cc241c5ab8fd70c Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 16:23:32 -0700 Subject: [PATCH 042/126] Make internal function static --- TrustKit/parse_configuration.m | 61 +++++++++++++++++----------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/TrustKit/parse_configuration.m b/TrustKit/parse_configuration.m index 5bfb891a..b81b7703 100644 --- a/TrustKit/parse_configuration.m +++ b/TrustKit/parse_configuration.m @@ -13,7 +13,36 @@ #import #import "configuration_utils.h" -SecCertificateRef certificateFromPEM(NSString *pem); + +static SecCertificateRef certificateFromPEM(NSString *pem) +{ + // NOTE: multi-certificate PEM is not supported since this is for individual + // trust anchor certificates. + + // Strip PEM header and footers. We don't support multi-certificate PEM. + NSMutableString *pemMutable = [pem stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet].mutableCopy; + + // Strip PEM header and footer + [pemMutable replaceOccurrencesOfString:@"-----BEGIN CERTIFICATE-----" + withString:@"" + options:(NSStringCompareOptions)(NSAnchoredSearch | NSLiteralSearch) + range:NSMakeRange(0, pemMutable.length)]; + + [pemMutable replaceOccurrencesOfString:@"-----END CERTIFICATE-----" + withString:@"" + options:(NSStringCompareOptions)(NSAnchoredSearch | NSBackwardsSearch | NSLiteralSearch) + range:NSMakeRange(0, pemMutable.length)]; + + NSData *pemData = [[NSData alloc] initWithBase64EncodedString:pemMutable + options:NSDataBase64DecodingIgnoreUnknownCharacters]; + SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef)pemData); + if (!cert) + { + [NSException raise:@"TrustKit configuration invalid" format:@"Failed to parse PEM certificate"]; + } + return cert; +} + NSDictionary *parseTrustKitConfiguration(NSDictionary *TrustKitArguments) { @@ -288,33 +317,3 @@ return [finalConfiguration copy]; } - -SecCertificateRef certificateFromPEM(NSString *pem) -{ - // NOTE: multi-certificate PEM is not supported since this is for individual - // trust anchor certificates. - - // Strip PEM header and footers. We don't support multi-certificate PEM. - NSMutableString *pemMutable = [pem stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet].mutableCopy; - - // Strip PEM header and footer - [pemMutable replaceOccurrencesOfString:@"-----BEGIN CERTIFICATE-----" - withString:@"" - options:(NSStringCompareOptions)(NSAnchoredSearch | NSLiteralSearch) - range:NSMakeRange(0, pemMutable.length)]; - - [pemMutable replaceOccurrencesOfString:@"-----END CERTIFICATE-----" - withString:@"" - options:(NSStringCompareOptions)(NSAnchoredSearch | NSBackwardsSearch | NSLiteralSearch) - range:NSMakeRange(0, pemMutable.length)]; - - NSData *pemData = [[NSData alloc] initWithBase64EncodedString:pemMutable - options:NSDataBase64DecodingIgnoreUnknownCharacters]; - SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef)pemData); - if (!cert) - { - [NSException raise:@"TrustKit configuration invalid" format:@"Failed to parse PEM certificate"]; - } - return cert; -} - From d2cbf68d13e9662511b853bbe1532a3bdebb15cb Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 16:23:55 -0700 Subject: [PATCH 043/126] Minor tweaks --- TrustKit/TSKPinningValidator.h | 2 +- TrustKit/TSKPinningValidatorResult.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 358260b7..d06417da 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -89,7 +89,7 @@ typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef cert validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler; /** - Evaluate the supplied server trust against the global SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent. + Evaluate the supplied server trust against the SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent. When using the `NSURLSession` or `WKWebView` network APIs, the `handleChallenge:completionHandler:` method should be called instead, as it is simpler to use. diff --git a/TrustKit/TSKPinningValidatorResult.m b/TrustKit/TSKPinningValidatorResult.m index 65a799a0..2df6d7ce 100644 --- a/TrustKit/TSKPinningValidatorResult.m +++ b/TrustKit/TSKPinningValidatorResult.m @@ -14,7 +14,7 @@ @implementation TSKPinningValidatorResult -@synthesize certificateChain=_certificateChain; +@synthesize certificateChain = _certificateChain; - (NSArray * _Nullable)certificateChain { From 0c440e767c4ca12107a185372e3061e1e77a1e1c Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 16:33:39 -0700 Subject: [PATCH 044/126] Remove unused type --- TrustKit/TSKPinningValidator.h | 1 - 1 file changed, 1 deletion(-) diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index d06417da..bef9e7fa 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -16,7 +16,6 @@ @class TSKPinningValidatorResult; @class TSKSPKIHashCache; -typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef certificate, TSKPublicKeyAlgorithm algorithm); /** `TSKPinningValidator` is a class for manually verifying a server's identity against the global SSL pinning policy. From 16b361261698b57bcdd3b74dbee2bbeac60a268d Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 16:37:17 -0700 Subject: [PATCH 045/126] Fix analyzer error with nil return value --- TrustKit/Pinning/TSKSPKIHashCache.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TrustKit/Pinning/TSKSPKIHashCache.h b/TrustKit/Pinning/TSKSPKIHashCache.h index b5dc5b36..5ed19da2 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.h +++ b/TrustKit/Pinning/TSKSPKIHashCache.h @@ -44,9 +44,9 @@ typedef NSMutableDictionary SPKICacheDictionnary; @param certificate The certificate containing the public key that will be hashed @param publicKeyAlgorithm The public algorithm to expect was used in this certificate - @return The hash of the public key assuming it used the provided algorithm + @return The hash of the public key assuming it used the provided algorithm or nil if the hash could not be generated */ -- (NSData *)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certificate publicKeyAlgorithm:(TSKPublicKeyAlgorithm)publicKeyAlgorithm; +- (NSData * _Nullable)hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certificate publicKeyAlgorithm:(TSKPublicKeyAlgorithm)publicKeyAlgorithm; @end From 39adc9362cdedf628e4f634c2f29cdea35fcd0a6 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 16:47:23 -0700 Subject: [PATCH 046/126] Tweak comment --- TrustKit/TrustKit.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 4072e20c..700ebc05 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -145,7 +145,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo #pragma mark Notification Handlers -// The block which receives pin validation notification and turns them into pin validation reports +// The block which receives pin validation results and turns them into pin validation reports - (void)sendValidationReport:(TSKPinningValidatorResult *)result { TSKPinValidationResult validationResult = result.validationResult; From a2aaa24e485cc5b2b37b84d08766cc426daf76bb Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 16:47:30 -0700 Subject: [PATCH 047/126] Add TODO --- TrustKit/TrustKit.h | 1 + 1 file changed, 1 insertion(+) diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 3a3d5369..64efaa4e 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -198,6 +198,7 @@ FOUNDATION_EXPORT NSString * const kTSKDefaultReportUri; validation. Keep in mind that, if set, the callback may be invoked very frequently and is not a suitable place for expensive tasks. */ +// TODO(AD): Mention the fact that this will override the validation mechanism or address this @property (nonatomic, nullable) void(^validationDelegateCallback)(TSKPinningValidatorResult * _Nonnull result); /** From 86dfb91022b9833ebf928492ecf472e3f6ffe643 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 16:49:17 -0700 Subject: [PATCH 048/126] Try Xcode 9 for CI --- circle.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/circle.yml b/circle.yml index b49ef767..59397b56 100644 --- a/circle.yml +++ b/circle.yml @@ -1,10 +1,10 @@ machine: xcode: - version: 8.3 + version: 9.0 test: override: - # First run Xcode 8.0 tests - - sudo xcode-select --switch /Applications/Xcode-8.0.app + # First run Xcode 8.3 tests + - sudo xcode-select --switch /Applications/Xcode-8.3.app # iOS 8 platform with iOS 9 SDK - xcodebuild clean test CODE_SIGNING_REQUIRED=NO @@ -20,8 +20,8 @@ test: -sdk macosx10.11 -scheme "TrustKit OS X" - # Switch to Xcode 8.3 - - sudo xcode-select --switch /Applications/Xcode-8.3.app + # Switch to Xcode 9.0 + - sudo xcode-select --switch /Applications/Xcode-9.0.app # iOS 10 platform with iOS 10 SDK - xcodebuild clean test CODE_SIGNING_REQUIRED=NO From 2b07f6e4b5785ed3835a34ebcfcdaac29b223cd1 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 17:24:36 -0700 Subject: [PATCH 049/126] Remove obsolete comment --- TrustKit/TrustKit.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 64efaa4e..3780d828 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -128,8 +128,7 @@ FOUNDATION_EXPORT NSString * const kTSKDefaultReportUri; Initialize the global SSL pinning policy with the supplied configuration. This method should be called as early as possible in the App's lifecycle to ensure that - the App's very first SSL connections are validated by TrustKit. Once TrustKit has been - initialized, notifications will be posted for any SSL pinning validation performed. + the App's very first SSL connections are validated by TrustKit. @param trustKitConfig A dictionary containing various keys for configuring the global SSL pinning policy. From e04480d05febb868872f2b45e24c0b6ac6fa6719 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 17:33:41 -0700 Subject: [PATCH 050/126] Remove argument that's always nil --- TrustKit/TSKPinningValidator.m | 3 +-- TrustKit/TSKPinningValidatorResult.h | 3 +-- TrustKit/TSKPinningValidatorResult.m | 3 +-- TrustKitTests/TSKReporterTests.m | 9 +++------ 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index d9bf1b99..07245da6 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -165,8 +165,7 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: notedHostname:domainConfigKey validationResult:validationResult finalTrustDecision:finalTrustDecision - validationDuration:validationDuration - certificateChain:nil]; + validationDuration:validationDuration]; dispatch_async(self.validationResultQueue, ^{ self.validationResultHandler(result); }); diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h index 88c416fb..a3d8479e 100644 --- a/TrustKit/TSKPinningValidatorResult.h +++ b/TrustKit/TSKPinningValidatorResult.h @@ -70,7 +70,6 @@ notedHostname:(NSString * _Nonnull)notedHostname validationResult:(TSKPinValidationResult)validationResult finalTrustDecision:(TSKTrustDecision)finalTrustDecision - validationDuration:(NSTimeInterval)validationDuration - certificateChain:(NSArray * _Nullable)certificateChain; + validationDuration:(NSTimeInterval)validationDuration; @end diff --git a/TrustKit/TSKPinningValidatorResult.m b/TrustKit/TSKPinningValidatorResult.m index 2df6d7ce..ddc4621f 100644 --- a/TrustKit/TSKPinningValidatorResult.m +++ b/TrustKit/TSKPinningValidatorResult.m @@ -32,7 +32,6 @@ - (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHost validationResult:(TSKPinValidationResult)validationResult finalTrustDecision:(TSKTrustDecision)finalTrustDecision validationDuration:(NSTimeInterval)validationDuration - certificateChain:(NSArray * _Nullable)certificateChain { NSParameterAssert(serverHostname); NSParameterAssert(serverTrust); @@ -46,7 +45,7 @@ - (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHost _validationResult = validationResult; _finalTrustDecision = finalTrustDecision; _validationDuration = validationDuration; - _certificateChain = certificateChain; + _certificateChain = nil; } return self; } diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index 5e3bc81d..5ef23b9c 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -129,8 +129,7 @@ - (void)testSendReportFromValidationReport notedHostname:@"www.test.com" validationResult:TSKPinValidationResultErrorCouldNotGenerateSpkiHash finalTrustDecision:TSKTrustDecisionShouldBlockConnection - validationDuration:1.0 - certificateChain:_testCertificateChain]; + validationDuration:1.0]; [_trustKit sendValidationReport:res]; // Ensure that the reporter was called @@ -149,8 +148,7 @@ - (void)testSendReportFromValidationReport notedHostname:@"www.test.com" validationResult:TSKPinValidationResultSuccess finalTrustDecision:TSKTrustDecisionShouldAllowConnection - validationDuration:1.0 - certificateChain:_testCertificateChain]; + validationDuration:1.0]; // Ensure that the reporter was NOT called [_trustKit sendValidationReport:res]; @@ -170,8 +168,7 @@ - (void)testSendReportFromValidationReport notedHostname:@"www.test.com" validationResult:TSKPinValidationResultFailedUserDefinedTrustAnchor finalTrustDecision:TSKTrustDecisionShouldAllowConnection - validationDuration:1.0 - certificateChain:_testCertificateChain]; + validationDuration:1.0]; // Ensure that the reporter was NOT called [_trustKit sendValidationReport:res]; From 59c062b9982a9d3a39730231252e8e113f410548 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 17:40:15 -0700 Subject: [PATCH 051/126] Rename file --- TrustKit.xcodeproj/project.pbxproj | 4 ++-- TrustKit/Pinning/ssl_pin_verifier.h | 2 +- TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m | 2 +- TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m | 2 +- .../{TSKPinValidatorResult.h => TSKPinValidationResult.h} | 2 +- TrustKit/TSKPinningValidator.h | 2 +- TrustKit/TSKPinningValidator.m | 2 +- TrustKit/TSKPinningValidatorResult.h | 2 +- TrustKit/TrustKit.h | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) rename TrustKit/{TSKPinValidatorResult.h => TSKPinValidationResult.h} (99%) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 93b39464..69294c7f 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -393,7 +393,7 @@ 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ssl_pin_verifier.m; path = Pinning/ssl_pin_verifier.m; sourceTree = ""; }; 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ssl_pin_verifier.h; path = Pinning/ssl_pin_verifier.h; sourceTree = ""; }; 8CE919291AEA0F7E002B29AE /* domain_registry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = domain_registry.h; path = Dependencies/domain_registry/domain_registry.h; sourceTree = ""; }; - FC1A08FD1E579F630055B12C /* TSKPinValidatorResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinValidatorResult.h; sourceTree = ""; }; + FC1A08FD1E579F630055B12C /* TSKPinValidationResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinValidationResult.h; sourceTree = ""; }; FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; FC1A09081E57AC450055B12C /* TSKSPKIHashCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKSPKIHashCache.h; path = Pinning/TSKSPKIHashCache.h; sourceTree = ""; }; @@ -573,7 +573,7 @@ FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */, 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */, 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */, - FC1A08FD1E579F630055B12C /* TSKPinValidatorResult.h */, + FC1A08FD1E579F630055B12C /* TSKPinValidationResult.h */, FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */, FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */, FCE7D6371EEA04180081EEEF /* Framework */, diff --git a/TrustKit/Pinning/ssl_pin_verifier.h b/TrustKit/Pinning/ssl_pin_verifier.h index e632d6a5..a2773e47 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.h +++ b/TrustKit/Pinning/ssl_pin_verifier.h @@ -9,7 +9,7 @@ */ -#import "../TSKPinValidatorResult.h" +#import "../TSKPinValidationResult.h" @import Foundation; @class TSKSPKIHashCache; diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m index 1228d5a7..625c4600 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m @@ -9,7 +9,7 @@ #import "TSKNSURLConnectionDelegateProxy.h" #import "../TrustKit.h" #import "../TSKLog.h" -#import "../TSKPinValidatorResult.h" +#import "../TSKPinValidationResult.h" #import "../TSKPinningValidator.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m index c447c19c..064db3b8 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m @@ -9,7 +9,7 @@ #import "TSKNSURLSessionDelegateProxy.h" #import "../TrustKit.h" #import "../TSKLog.h" -#import "../TSKPinValidatorResult.h" +#import "../TSKPinValidationResult.h" #import "../TSKPinningValidator.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" diff --git a/TrustKit/TSKPinValidatorResult.h b/TrustKit/TSKPinValidationResult.h similarity index 99% rename from TrustKit/TSKPinValidatorResult.h rename to TrustKit/TSKPinValidationResult.h index 083c9dc6..c9023bfa 100644 --- a/TrustKit/TSKPinValidatorResult.h +++ b/TrustKit/TSKPinValidationResult.h @@ -1,6 +1,6 @@ /* - TSKPinningValidator.h + TSKPinningValidation.h TrustKit Copyright 2015 The TrustKit Project Authors diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index bef9e7fa..5e82f3e4 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -9,7 +9,7 @@ */ -#import "TSKPinValidatorResult.h" +#import "TSKPinValidationResult.h" #import "TSKPublicKeyAlgorithm.h" @import Foundation; diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index 07245da6..9468ff17 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -11,7 +11,7 @@ #import "TSKPinningValidator.h" #import "TSKTrustKitConfig.h" -#import "TSKPinValidatorResult.h" +#import "TSKPinValidationResult.h" #import "TSKPinningValidatorResult.h" #import "Pinning/TSKSPKIHashCache.h" #import "Pinning/ssl_pin_verifier.h" diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h index a3d8479e..a2f98855 100644 --- a/TrustKit/TSKPinningValidatorResult.h +++ b/TrustKit/TSKPinningValidatorResult.h @@ -9,7 +9,7 @@ */ -#import "TSKPinValidatorResult.h" +#import "TSKPinValidationResult.h" @import Foundation; @interface TSKPinningValidatorResult : NSObject diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 3780d828..e92f7628 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -16,7 +16,7 @@ #import "TSKTrustKitConfig.h" #import "TSKPublicKeyAlgorithm.h" #import "TSKPinningValidator.h" - #import "TSKPinValidatorResult.h" + #import "TSKPinValidationResult.h" #endif /* _TRUSTKIT_ */ NS_ASSUME_NONNULL_BEGIN From 5442d9483cd46bda40f9037d26933c31d44b3026 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 21 Jun 2017 19:09:33 -0700 Subject: [PATCH 052/126] Rename TSKPinValidationResult to TSKTrustEvaluationResult to clarify purpose --- TrustKit.xcodeproj/project.pbxproj | 4 +- TrustKit/Pinning/ssl_pin_verifier.h | 12 ++-- TrustKit/Pinning/ssl_pin_verifier.m | 14 ++-- TrustKit/Reporting/TSKBackgroundReporter.h | 2 +- TrustKit/Reporting/TSKBackgroundReporter.m | 2 +- TrustKit/Reporting/TSKPinFailureReport.h | 4 +- TrustKit/Reporting/TSKPinFailureReport.m | 2 +- .../TSKNSURLConnectionDelegateProxy.m | 2 +- .../Swizzling/TSKNSURLSessionDelegateProxy.m | 2 +- TrustKit/TSKPinningValidator.h | 2 +- TrustKit/TSKPinningValidator.m | 16 ++--- TrustKit/TSKPinningValidatorResult.h | 6 +- TrustKit/TSKPinningValidatorResult.m | 2 +- ...nValidationResult.h => TSKTrustDecision.h} | 20 +++--- TrustKit/TrustKit.h | 2 +- TrustKit/TrustKit.m | 4 +- TrustKitTests/TSKNSURLConnectionTests.m | 2 +- TrustKitTests/TSKNSURLSessionTests.m | 2 +- TrustKitTests/TSKPinningValidatorTests.m | 68 +++++++++---------- TrustKitTests/TSKPublicKeyAlgorithmTests.m | 4 +- TrustKitTests/TSKReporterTests.m | 12 ++-- TrustKitTests/TSKReportsRateLimiterTests.m | 4 +- 22 files changed, 94 insertions(+), 94 deletions(-) rename TrustKit/{TSKPinValidationResult.h => TSKTrustDecision.h} (83%) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 69294c7f..2e470716 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -393,7 +393,7 @@ 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ssl_pin_verifier.m; path = Pinning/ssl_pin_verifier.m; sourceTree = ""; }; 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ssl_pin_verifier.h; path = Pinning/ssl_pin_verifier.h; sourceTree = ""; }; 8CE919291AEA0F7E002B29AE /* domain_registry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = domain_registry.h; path = Dependencies/domain_registry/domain_registry.h; sourceTree = ""; }; - FC1A08FD1E579F630055B12C /* TSKPinValidationResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinValidationResult.h; sourceTree = ""; }; + FC1A08FD1E579F630055B12C /* TSKTrustDecision.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustDecision.h; sourceTree = ""; }; FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; FC1A09081E57AC450055B12C /* TSKSPKIHashCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKSPKIHashCache.h; path = Pinning/TSKSPKIHashCache.h; sourceTree = ""; }; @@ -573,7 +573,7 @@ FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */, 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */, 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */, - FC1A08FD1E579F630055B12C /* TSKPinValidationResult.h */, + FC1A08FD1E579F630055B12C /* TSKTrustDecision.h */, FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */, FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */, FCE7D6371EEA04180081EEEF /* Framework */, diff --git a/TrustKit/Pinning/ssl_pin_verifier.h b/TrustKit/Pinning/ssl_pin_verifier.h index a2773e47..96926b5b 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.h +++ b/TrustKit/Pinning/ssl_pin_verifier.h @@ -9,14 +9,14 @@ */ -#import "../TSKPinValidationResult.h" +#import "../TSKTrustDecision.h" @import Foundation; @class TSKSPKIHashCache; // Validate that the server trust contains at least one of the know/expected pins -TSKPinValidationResult verifyPublicKeyPin(SecTrustRef _Nonnull serverTrust, - NSString * _Nonnull serverHostname, - NSArray * _Nonnull supportedAlgorithms, - NSSet * _Nonnull knownPins, - TSKSPKIHashCache * _Nullable hashCache); +TSKTrustEvaluationResult verifyPublicKeyPin(SecTrustRef _Nonnull serverTrust, + NSString * _Nonnull serverHostname, + NSArray * _Nonnull supportedAlgorithms, + NSSet * _Nonnull knownPins, + TSKSPKIHashCache * _Nullable hashCache); diff --git a/TrustKit/Pinning/ssl_pin_verifier.m b/TrustKit/Pinning/ssl_pin_verifier.m index 4b488784..3179f72e 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.m +++ b/TrustKit/Pinning/ssl_pin_verifier.m @@ -18,7 +18,7 @@ #pragma mark SSL Pin Verifier -TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *serverHostname, NSArray *supportedAlgorithms, NSSet *knownPins, TSKSPKIHashCache *hashCache) +TSKTrustEvaluationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *serverHostname, NSArray *supportedAlgorithms, NSSet *knownPins, TSKSPKIHashCache *hashCache) { NSCParameterAssert(serverTrust); NSCParameterAssert(supportedAlgorithms); @@ -26,7 +26,7 @@ TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *ser if ((serverTrust == NULL) || (supportedAlgorithms == nil) || (knownPins == nil)) { TSKLog(@"Invalid pinning parameters for %@", serverHostname); - return TSKPinValidationResultErrorInvalidParameters; + return TSKTrustEvaluationErrorInvalidParameters; } // First re-check the certificate chain using the default SSL validation in case it was disabled @@ -45,7 +45,7 @@ TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *ser { TSKLog(@"SecTrustEvaluate error for %@", serverHostname); CFRelease(serverTrust); - return TSKPinValidationResultErrorInvalidParameters; + return TSKTrustEvaluationErrorInvalidParameters; } if ((trustResult != kSecTrustResultUnspecified) && (trustResult != kSecTrustResultProceed)) @@ -55,7 +55,7 @@ TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *ser TSKLog(@"Error: default SSL validation failed for %@: %@", serverHostname, evaluationDetails); CFRelease(evaluationDetails); CFRelease(serverTrust); - return TSKPinValidationResultFailedCertificateChainNotTrusted; + return TSKTrustEvaluationFailedInvalidCertificateChain; } // Check each certificate in the server's certificate chain (the trust object); start with the CA all the way down to the leaf @@ -78,7 +78,7 @@ TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *ser { TSKLog(@"Error - could not generate the SPKI hash for %@", serverHostname); CFRelease(serverTrust); - return TSKPinValidationResultErrorCouldNotGenerateSpkiHash; + return TSKTrustEvaluationErrorCouldNotGenerateSpkiHash; } // Is the generated hash in our set of pinned hashes ? @@ -87,7 +87,7 @@ TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *ser { TSKLog(@"SSL Pin found for %@", serverHostname); CFRelease(serverTrust); - return TSKPinValidationResultSuccess; + return TSKTrustEvaluationSuccess; } } } @@ -135,5 +135,5 @@ TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *ser // If we get here, we didn't find any matching SPKI hash in the chain TSKLog(@"Error: SSL Pin not found for %@", serverHostname); CFRelease(serverTrust); - return TSKPinValidationResultFailed; + return TSKTrustEvaluationFailedNoMatchingPin; } diff --git a/TrustKit/Reporting/TSKBackgroundReporter.h b/TrustKit/Reporting/TSKBackgroundReporter.h index b1ee974c..4eca7fae 100644 --- a/TrustKit/Reporting/TSKBackgroundReporter.h +++ b/TrustKit/Reporting/TSKBackgroundReporter.h @@ -46,7 +46,7 @@ includeSubdomains:(BOOL) includeSubdomains enforcePinning:(BOOL) enforcePinning knownPins:(nonnull NSSet *) knownPins - validationResult:(TSKPinValidationResult) validationResult + validationResult:(TSKTrustEvaluationResult) validationResult expirationDate:(nullable NSDate *)knownPinsExpirationDate; - (void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error; diff --git a/TrustKit/Reporting/TSKBackgroundReporter.m b/TrustKit/Reporting/TSKBackgroundReporter.m index bd3ea3a0..dfa6f7af 100644 --- a/TrustKit/Reporting/TSKBackgroundReporter.m +++ b/TrustKit/Reporting/TSKBackgroundReporter.m @@ -130,7 +130,7 @@ - (void) pinValidationFailedForHostname:(nonnull NSString *)serverHostname includeSubdomains:(BOOL)includeSubdomains enforcePinning:(BOOL)enforcePinning knownPins:(nonnull NSSet *)knownPins - validationResult:(TSKPinValidationResult)validationResult + validationResult:(TSKTrustEvaluationResult)validationResult expirationDate:(nullable NSDate *)knownPinsExpirationDate { // Default port to 0 if not specified diff --git a/TrustKit/Reporting/TSKPinFailureReport.h b/TrustKit/Reporting/TSKPinFailureReport.h index 867f3c74..c8fd9b55 100644 --- a/TrustKit/Reporting/TSKPinFailureReport.h +++ b/TrustKit/Reporting/TSKPinFailureReport.h @@ -27,7 +27,7 @@ @property (readonly, nonatomic) BOOL includeSubdomains; @property (readonly, nonatomic, nonnull) NSArray *validatedCertificateChain; @property (readonly, nonatomic, nonnull) NSArray *knownPins; -@property (readonly, nonatomic) TSKPinValidationResult validationResult; // Not part of the HPKP spec +@property (readonly, nonatomic) TSKTrustEvaluationResult validationResult; // Not part of the HPKP spec @property (readonly, nonatomic) BOOL enforcePinning; // Not part of the HPKP spec @property (readonly, nonatomic, nullable) NSDate *knownPinsExpirationDate; // Not part of the HPKP spec @@ -47,7 +47,7 @@ enforcePinning:(BOOL)enforcePinning validatedCertificateChain:(nonnull NSArray *)validatedCertificateChain knownPins:(nonnull NSArray *)knownPins - validationResult:(TSKPinValidationResult)validationResult + validationResult:(TSKTrustEvaluationResult)validationResult expirationDate:(nullable NSDate *)knownPinsExpirationDate; // Return the report in JSON format for POSTing it diff --git a/TrustKit/Reporting/TSKPinFailureReport.m b/TrustKit/Reporting/TSKPinFailureReport.m index 945ed254..fa21ac65 100644 --- a/TrustKit/Reporting/TSKPinFailureReport.m +++ b/TrustKit/Reporting/TSKPinFailureReport.m @@ -27,7 +27,7 @@ - (nonnull instancetype) initWithAppBundleId:(nonnull NSString *)appBundleId enforcePinning:(BOOL)enforcePinning validatedCertificateChain:(nonnull NSArray *)validatedCertificateChain knownPins:(nonnull NSArray *)knownPins - validationResult:(TSKPinValidationResult)validationResult + validationResult:(TSKTrustEvaluationResult)validationResult expirationDate:(nullable NSDate *)knownPinsExpirationDate { self = [super init]; diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m index 625c4600..ac26a84f 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m @@ -9,7 +9,7 @@ #import "TSKNSURLConnectionDelegateProxy.h" #import "../TrustKit.h" #import "../TSKLog.h" -#import "../TSKPinValidationResult.h" +#import "../TSKTrustDecision.h" #import "../TSKPinningValidator.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m index 064db3b8..54c7e992 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m @@ -9,7 +9,7 @@ #import "TSKNSURLSessionDelegateProxy.h" #import "../TrustKit.h" #import "../TSKLog.h" -#import "../TSKPinValidationResult.h" +#import "../TSKTrustDecision.h" #import "../TSKPinningValidator.h" #import "../Dependencies/RSSwizzle/RSSwizzle.h" diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 5e82f3e4..6e1a46b1 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -9,7 +9,7 @@ */ -#import "TSKPinValidationResult.h" +#import "TSKTrustDecision.h" #import "TSKPublicKeyAlgorithm.h" @import Foundation; diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index 9468ff17..8b1abe39 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -11,7 +11,7 @@ #import "TSKPinningValidator.h" #import "TSKTrustKitConfig.h" -#import "TSKPinValidationResult.h" +#import "TSKTrustDecision.h" #import "TSKPinningValidatorResult.h" #import "Pinning/TSKSPKIHashCache.h" #import "Pinning/ssl_pin_verifier.h" @@ -109,13 +109,13 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: // The domain has a pinning policy that has not expired // Look for one the configured public key pins in the server's evaluated certificate chain - TSKPinValidationResult validationResult = verifyPublicKeyPin(serverTrust, - serverHostname, - domainConfig[kTSKPublicKeyAlgorithms], - domainConfig[kTSKPublicKeyHashes], - self.spkiHashCache); + TSKTrustEvaluationResult validationResult = verifyPublicKeyPin(serverTrust, + serverHostname, + domainConfig[kTSKPublicKeyAlgorithms], + domainConfig[kTSKPublicKeyHashes], + self.spkiHashCache); - if (validationResult == TSKPinValidationResultSuccess) + if (validationResult == TSKTrustEvaluationSuccess) { // Pin validation was successful TSKLog(@"Pin validation succeeded for %@", serverHostname); @@ -136,7 +136,7 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: else #endif { - if (validationResult == TSKPinValidationResultFailed) + if (validationResult == TSKTrustEvaluationFailedNoMatchingPin) { // Is pinning enforced? if ([domainConfig[kTSKEnforcePinning] boolValue] == YES) diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h index a2f98855..b229ad53 100644 --- a/TrustKit/TSKPinningValidatorResult.h +++ b/TrustKit/TSKPinningValidatorResult.h @@ -9,7 +9,7 @@ */ -#import "TSKPinValidationResult.h" +#import "TSKTrustDecision.h" @import Foundation; @interface TSKPinningValidatorResult : NSObject @@ -41,7 +41,7 @@ represents the result of evaluating the certificate chain against the configured SSL pins for this server. */ -@property (nonatomic, readonly) TSKPinValidationResult validationResult; +@property (nonatomic, readonly) TSKTrustEvaluationResult validationResult; /** The `TSKTrustDecision` returned when validating the certificate's chain, which describes @@ -68,7 +68,7 @@ - (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHostname serverTrust:(SecTrustRef _Nonnull)serverTrust notedHostname:(NSString * _Nonnull)notedHostname - validationResult:(TSKPinValidationResult)validationResult + validationResult:(TSKTrustEvaluationResult)validationResult finalTrustDecision:(TSKTrustDecision)finalTrustDecision validationDuration:(NSTimeInterval)validationDuration; diff --git a/TrustKit/TSKPinningValidatorResult.m b/TrustKit/TSKPinningValidatorResult.m index ddc4621f..b8454621 100644 --- a/TrustKit/TSKPinningValidatorResult.m +++ b/TrustKit/TSKPinningValidatorResult.m @@ -29,7 +29,7 @@ - (NSArray * _Nullable)certificateChain - (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHostname serverTrust:(SecTrustRef _Nonnull)serverTrust notedHostname:(NSString * _Nonnull)notedHostname - validationResult:(TSKPinValidationResult)validationResult + validationResult:(TSKTrustEvaluationResult)validationResult finalTrustDecision:(TSKTrustDecision)finalTrustDecision validationDuration:(NSTimeInterval)validationDuration { diff --git a/TrustKit/TSKPinValidationResult.h b/TrustKit/TSKTrustDecision.h similarity index 83% rename from TrustKit/TSKPinValidationResult.h rename to TrustKit/TSKTrustDecision.h index c9023bfa..e59e95aa 100644 --- a/TrustKit/TSKPinValidationResult.h +++ b/TrustKit/TSKTrustDecision.h @@ -1,6 +1,6 @@ /* - TSKPinningValidation.h + TSKTrustDecision.h TrustKit Copyright 2015 The TrustKit Project Authors @@ -11,43 +11,43 @@ @import Foundation; /** - Possible return values when verifying a server's identity. + Possible return values when verifying a server's identity against a set of pins. */ -typedef NS_ENUM(NSInteger, TSKPinValidationResult) +typedef NS_ENUM(NSInteger, TSKTrustEvaluationResult) { /** The server trust was succesfully evaluated and contained at least one of the configured pins. */ - TSKPinValidationResultSuccess, + TSKTrustEvaluationSuccess, /** The server trust was succesfully evaluated but did not contain any of the configured pins. */ - TSKPinValidationResultFailed, + TSKTrustEvaluationFailedNoMatchingPin, /** The server trust's evaluation failed: the server's certificate chain is not trusted. */ - TSKPinValidationResultFailedCertificateChainNotTrusted, + TSKTrustEvaluationFailedInvalidCertificateChain, /** The server trust could not be evaluated due to invalid parameters. */ - TSKPinValidationResultErrorInvalidParameters, + TSKTrustEvaluationErrorInvalidParameters, /** The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to the macOS trust store). Only available on macOS. */ - TSKPinValidationResultFailedUserDefinedTrustAnchor NS_AVAILABLE_MAC(10_9), + TSKTrustEvaluationFailedUserDefinedTrustAnchor NS_AVAILABLE_MAC(10_9), /** The server trust could not be evaluated due to an error when trying to generate the certificate's subject public key info hash. On iOS 9 or below, this could be caused by a Keychain failure when trying to extract the certificate's public key bytes. */ - TSKPinValidationResultErrorCouldNotGenerateSpkiHash, + TSKTrustEvaluationErrorCouldNotGenerateSpkiHash, }; /** - Possible return values when verifying a server's identity against the global SSL pinning policy using `TSKPinningValidator`. + Possible return values when verifying a server's identity against an SSL pinning policy. */ typedef NS_ENUM(NSInteger, TSKTrustDecision) diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index e92f7628..a276220b 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -16,7 +16,7 @@ #import "TSKTrustKitConfig.h" #import "TSKPublicKeyAlgorithm.h" #import "TSKPinningValidator.h" - #import "TSKPinValidationResult.h" + #import "TSKTrustDecision.h" #endif /* _TRUSTKIT_ */ NS_ASSUME_NONNULL_BEGIN diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 700ebc05..82a2b9aa 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -148,10 +148,10 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo // The block which receives pin validation results and turns them into pin validation reports - (void)sendValidationReport:(TSKPinningValidatorResult *)result { - TSKPinValidationResult validationResult = result.validationResult; + TSKTrustEvaluationResult validationResult = result.validationResult; // Send a report only if the there was a pinning failure - if (validationResult != TSKPinValidationResultSuccess) + if (validationResult != TSKTrustEvaluationSuccess) { #if !TARGET_OS_IPHONE if (validationResult != TSKPinValidationResultFailedUserDefinedTrustAnchor) diff --git a/TrustKitTests/TSKNSURLConnectionTests.m b/TrustKitTests/TSKNSURLConnectionTests.m index c6e52966..5a14177b 100644 --- a/TrustKitTests/TSKNSURLConnectionTests.m +++ b/TrustKitTests/TSKNSURLConnectionTests.m @@ -14,7 +14,7 @@ #import @interface TSKNSURLConnectionDelegateProxy (TestSupport) -@property (nonatomic) TSKPinValidationResult lastTrustDecision; +@property (nonatomic) TSKTrustEvaluationResult lastTrustDecision; -(BOOL)forwardToOriginalDelegateAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge forConnection:(NSURLConnection *)connection; @end diff --git a/TrustKitTests/TSKNSURLSessionTests.m b/TrustKitTests/TSKNSURLSessionTests.m index d388fbd4..ca65bf8d 100644 --- a/TrustKitTests/TSKNSURLSessionTests.m +++ b/TrustKitTests/TSKNSURLSessionTests.m @@ -20,7 +20,7 @@ + (void)resetConfiguration; @interface TSKNSURLSessionDelegateProxy (TestSupport) @property (nonatomic) id originalDelegate; -@property (nonatomic) TSKPinValidationResult lastTrustDecision; +@property (nonatomic) TSKTrustEvaluationResult lastTrustDecision; - (BOOL)forwardToOriginalDelegateAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(TSKURLSessionAuthChallengeCallback)completionHandler diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 7bb297fc..0887abbc 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -132,13 +132,13 @@ - (void)testVerifyAgainstAnyPublicKey NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); NSDictionary *domainConfig = parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"]; - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, - @"www.good.com", - domainConfig[kTSKPublicKeyAlgorithms], - domainConfig[kTSKPublicKeyHashes], - spkiCache); + TSKTrustEvaluationResult verificationResult = verifyPublicKeyPin(trust, + @"www.good.com", + domainConfig[kTSKPublicKeyAlgorithms], + domainConfig[kTSKPublicKeyHashes], + spkiCache); - XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, + XCTAssertEqual(verificationResult, TSKTrustEvaluationSuccess, @"Validation must pass against valid public key pins"); XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; @@ -150,7 +150,7 @@ - (void)testVerifyAgainstAnyPublicKey validationResultHandler:^(TSKPinningValidatorResult *result) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -201,7 +201,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); - TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + TSKTrustEvaluationResult verificationResult = TSKTrustEvaluationFailedNoMatchingPin; verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], @@ -209,7 +209,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey spkiCache); - XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, + XCTAssertEqual(verificationResult, TSKTrustEvaluationSuccess, @"Validation must pass against valid public key pins"); @@ -223,7 +223,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey validationResultHandler:^(TSKPinningValidatorResult *result) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -268,7 +268,7 @@ - (void)testVerifyAgainstCAPublicKey // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); - TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + TSKTrustEvaluationResult verificationResult = TSKTrustEvaluationFailedNoMatchingPin; verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], @@ -276,7 +276,7 @@ - (void)testVerifyAgainstCAPublicKey spkiCache); - XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, + XCTAssertEqual(verificationResult, TSKTrustEvaluationSuccess, @"Validation must pass against valid public key pins"); @@ -290,7 +290,7 @@ - (void)testVerifyAgainstCAPublicKey validationResultHandler:^(TSKPinningValidatorResult *result) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -331,7 +331,7 @@ - (void)testVerifyAgainstLeafPublicKey // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); - TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + TSKTrustEvaluationResult verificationResult = TSKTrustEvaluationFailedNoMatchingPin; verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], @@ -339,7 +339,7 @@ - (void)testVerifyAgainstLeafPublicKey spkiCache); - XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, + XCTAssertEqual(verificationResult, TSKTrustEvaluationSuccess, @"Validation must pass against valid public key pins"); @@ -353,7 +353,7 @@ - (void)testVerifyAgainstLeafPublicKey validationResultHandler:^(TSKPinningValidatorResult *result) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -394,7 +394,7 @@ - (void)testVerifyAgainstBadPublicKey // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); - TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + TSKTrustEvaluationResult verificationResult = TSKTrustEvaluationErrorInvalidParameters; verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], @@ -402,7 +402,7 @@ - (void)testVerifyAgainstBadPublicKey spkiCache); - XCTAssertEqual(verificationResult, TSKPinValidationResultFailed, + XCTAssertEqual(verificationResult, TSKTrustEvaluationFailedNoMatchingPin, @"Validation must fail against bad public key pins"); @@ -416,7 +416,7 @@ - (void)testVerifyAgainstBadPublicKey validationResultHandler:^(TSKPinningValidatorResult *result) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultFailed); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -500,14 +500,14 @@ - (void)testVerifyAgainstBadPublicKeyPinningNotEnforced // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); - TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + TSKTrustEvaluationResult verificationResult = TSKTrustEvaluationErrorInvalidParameters; verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyHashes], spkiCache); - XCTAssertEqual(verificationResult, TSKPinValidationResultFailed, @"Validation must fail against bad public key pins"); + XCTAssertEqual(verificationResult, TSKTrustEvaluationFailedNoMatchingPin, @"Validation must fail against bad public key pins"); // Then test TSKPinningValidator XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; @@ -519,7 +519,7 @@ - (void)testVerifyAgainstBadPublicKeyPinningNotEnforced validationResultHandler:^(TSKPinningValidatorResult *result) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultFailed); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -562,7 +562,7 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); - TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + TSKTrustEvaluationResult verificationResult = TSKTrustEvaluationErrorInvalidParameters; verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], @@ -570,7 +570,7 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey spkiCache); - XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, + XCTAssertEqual(verificationResult, TSKTrustEvaluationSuccess, @"Validation must pass against valid public key pins"); @@ -584,7 +584,7 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey validationResultHandler:^(TSKPinningValidatorResult *result) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultSuccess); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -627,7 +627,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); - TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + TSKTrustEvaluationResult verificationResult = TSKTrustEvaluationErrorInvalidParameters; verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], @@ -635,7 +635,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain spkiCache); - XCTAssertEqual(verificationResult, TSKPinValidationResultFailedCertificateChainNotTrusted, + XCTAssertEqual(verificationResult, TSKTrustEvaluationFailedInvalidCertificateChain, @"Validation must fail against bad certificate chain"); @@ -649,7 +649,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain validationResultHandler:^(TSKPinningValidatorResult *result) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultFailedCertificateChainNotTrusted); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedInvalidCertificateChain); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -693,7 +693,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); - TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + TSKTrustEvaluationResult verificationResult = TSKTrustEvaluationErrorInvalidParameters; verificationResult = verifyPublicKeyPin(trust, @"www.bad.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.bad.com"][kTSKPublicKeyAlgorithms], @@ -701,7 +701,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname spkiCache); - XCTAssertEqual(verificationResult, TSKPinValidationResultFailedCertificateChainNotTrusted, + XCTAssertEqual(verificationResult, TSKTrustEvaluationFailedInvalidCertificateChain, @"Validation must fail against bad hostname"); @@ -715,7 +715,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname validationResultHandler:^(TSKPinningValidatorResult *result) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultFailedCertificateChainNotTrusted); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedInvalidCertificateChain); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -758,7 +758,7 @@ - (void)testVerifyAgainstInjectedCaPublicKey // First test the verifyPublicKeyPin() function NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); - TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + TSKTrustEvaluationResult verificationResult = TSKTrustEvaluationErrorInvalidParameters; verificationResult = verifyPublicKeyPin(trust, @"www.good.com", parsedTrustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], @@ -766,7 +766,7 @@ - (void)testVerifyAgainstInjectedCaPublicKey spkiCache); - XCTAssertEqual(verificationResult, TSKTrustDecisionShouldBlockConnection, + XCTAssertEqual(verificationResult, TSKTrustEvaluationFailedNoMatchingPin, @"Validation must fail against injected pinned CA"); @@ -780,7 +780,7 @@ - (void)testVerifyAgainstInjectedCaPublicKey validationResultHandler:^(TSKPinningValidatorResult *result) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); - XCTAssertEqual(result.validationResult, TSKPinValidationResultFailed); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); diff --git a/TrustKitTests/TSKPublicKeyAlgorithmTests.m b/TrustKitTests/TSKPublicKeyAlgorithmTests.m index 73a92fc1..00891275 100644 --- a/TrustKitTests/TSKPublicKeyAlgorithmTests.m +++ b/TrustKitTests/TSKPublicKeyAlgorithmTests.m @@ -130,7 +130,7 @@ - (void)testVerifyMultipleAlgorithms XCTAssertEqual([[spkiCache getSubjectPublicKeyInfoHashesCache][@0] count], 0UL, @"SPKI cache must be empty"); XCTAssertEqual([[spkiCache getSubjectPublicKeyInfoHashesCache][@1] count], 0UL, @"SPKI cache must be empty"); - TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + TSKTrustEvaluationResult verificationResult = TSKTrustEvaluationFailedNoMatchingPin; verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig[kTSKPinnedDomains][@"www.good.com"][kTSKPublicKeyAlgorithms], @@ -146,7 +146,7 @@ - (void)testVerifyMultipleAlgorithms CFRelease(intermediateCertificate); CFRelease(rootCertificate); - XCTAssertEqual(verificationResult, TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins with multiple algorithms"); + XCTAssertEqual(verificationResult, TSKTrustEvaluationSuccess, @"Validation must pass against valid public key pins with multiple algorithms"); } diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index 5ef23b9c..158d7d58 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -121,13 +121,13 @@ - (void)testSendReportFromValidationReport includeSubdomains:NO enforcePinning:YES knownPins:knownPins - validationResult:TSKPinValidationResultErrorCouldNotGenerateSpkiHash + validationResult:TSKTrustEvaluationErrorCouldNotGenerateSpkiHash expirationDate:expirationDate]); res = [[TSKPinningValidatorResult alloc] initWithServerHostname:@"www.test.com" serverTrust:_testTrust notedHostname:@"www.test.com" - validationResult:TSKPinValidationResultErrorCouldNotGenerateSpkiHash + validationResult:TSKTrustEvaluationErrorCouldNotGenerateSpkiHash finalTrustDecision:TSKTrustDecisionShouldBlockConnection validationDuration:1.0]; [_trustKit sendValidationReport:res]; @@ -146,7 +146,7 @@ - (void)testSendReportFromValidationReport res = [[TSKPinningValidatorResult alloc] initWithServerHostname:@"www.test.com" serverTrust:_testTrust notedHostname:@"www.test.com" - validationResult:TSKPinValidationResultSuccess + validationResult:TSKTrustEvaluationSuccess finalTrustDecision:TSKTrustDecisionShouldAllowConnection validationDuration:1.0]; @@ -166,7 +166,7 @@ - (void)testSendReportFromValidationReport res = [[TSKPinningValidatorResult alloc] initWithServerHostname:@"www.test.com" serverTrust:_testTrust notedHostname:@"www.test.com" - validationResult:TSKPinValidationResultFailedUserDefinedTrustAnchor + validationResult:TSKTrustEvaluationFailedUserDefinedTrustAnchor finalTrustDecision:TSKTrustDecisionShouldAllowConnection validationDuration:1.0]; @@ -195,7 +195,7 @@ - (void)testReporter options:(NSDataBase64DecodingOptions)0], [[NSData alloc]initWithBase64EncodedString:@"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" options:(NSDataBase64DecodingOptions)0]]] - validationResult:TSKPinValidationResultFailed + validationResult:TSKTrustEvaluationFailedNoMatchingPin expirationDate:[NSDate date]]; [NSThread sleepForTimeInterval:0.1]; @@ -217,7 +217,7 @@ - (void)testReporterNilExpirationDate options:(NSDataBase64DecodingOptions)0], [[NSData alloc]initWithBase64EncodedString:@"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" options:(NSDataBase64DecodingOptions)0]]] - validationResult:TSKPinValidationResultFailed + validationResult:TSKTrustEvaluationFailedNoMatchingPin expirationDate:nil]; [NSThread sleepForTimeInterval:0.1]; diff --git a/TrustKitTests/TSKReportsRateLimiterTests.m b/TrustKitTests/TSKReportsRateLimiterTests.m index a06b1a39..818bbcc7 100644 --- a/TrustKitTests/TSKReportsRateLimiterTests.m +++ b/TrustKitTests/TSKReportsRateLimiterTests.m @@ -67,7 +67,7 @@ - (void)setUp { enforcePinning:NO validatedCertificateChain:certificateChain knownPins:formattedPins - validationResult:TSKPinValidationResultFailedCertificateChainNotTrusted + validationResult:TSKTrustEvaluationFailedInvalidCertificateChain expirationDate:[NSDate dateWithTimeIntervalSinceReferenceDate:0]]; } @@ -176,7 +176,7 @@ - (void)test_rateLimitHashing_validationResult XCTAssertFalse([self.rateLimiter shouldRateLimitReport:self.testReport], @"Wrongly rate-limited a new report"); TSKPinFailureReport *mockReport = OCMPartialMock(self.testReport); - OCMStub(mockReport.validationResult).andReturn(TSKPinValidationResultErrorCouldNotGenerateSpkiHash); + OCMStub(mockReport.validationResult).andReturn(TSKTrustEvaluationErrorCouldNotGenerateSpkiHash); XCTAssertFalse([self.rateLimiter shouldRateLimitReport:mockReport], @"Wrongly rate-limited a new report"); From a61d1c492eabe052d4b4e0e214036e916592b2aa Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Thu, 22 Jun 2017 14:01:46 -0700 Subject: [PATCH 053/126] Restore previous sample code --- TrustKit/TSKPinningValidator.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 6e1a46b1..2e7545fa 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -112,16 +112,18 @@ This method will evaluate the server trust within the authentication challenge against the global SSL pinning policy previously configured, and then call the `completionHandler` with the corresponding `disposition` and `credential`. For example, this method can be leveraged in a `WKNavigationDelegate` challenge handler method: - - (void)webView:(WKWebView *)webView - didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge - completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential *credential))completionHandler - { - if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) - { - [TSKPinningValidator handleChallenge:challenge completionHandler:completionHandler]; - } - } + - (void)webView:(WKWebView *)webView + didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential *credential))completionHandler + { + if (![TSKPinningValidator handleChallenge:challenge completionHandler:completionHandler]) + { + // TrustKit did not handle this challenge: perhaps it was not for server trust + // or the domain was not pinned. Fall back to the default behavior + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } + } @param challenge The authentication challenge, supplied by the URL loading system to the delegate's challenge handler method. From cacbff71ea2d2f4bedb121f8ffa765e395212e34 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Thu, 22 Jun 2017 14:05:32 -0700 Subject: [PATCH 054/126] Remove un-needed hidden argument --- TrustKit/TrustKit.m | 6 ------ 1 file changed, 6 deletions(-) diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 82a2b9aa..592fbba1 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -197,10 +197,6 @@ - (void)setValidationDelegateQueue:(dispatch_queue_t)validationDelegateQueue #pragma mark TrustKit Implicit Initialization via Library Constructor -// TRUSTKIT_SKIP_LIB_INITIALIZATION define allows consumers to opt out of the dylib constructor. -// This might be useful to mitigate integration risks, if the consumer doens't wish to use -// plist file, and wants to initialize lib manually later on. -#ifndef TRUSTKIT_SKIP_LIB_INITIALIZATION __attribute__((constructor)) static void initializeWithInfoPlist(int argc, const char **argv) { @@ -215,5 +211,3 @@ - (void)setValidationDelegateQueue:(dispatch_queue_t)validationDelegateQueue [TrustKit initializeWithConfiguration:trustKitConfigFromInfoPlist]; } } - -#endif From a0e6fc6dc403aee1c66efb32fe6ff363d7d3ab45 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Thu, 22 Jun 2017 14:19:17 -0700 Subject: [PATCH 055/126] Clarify and shorten documentation --- TrustKit/TrustKit.h | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index a276220b..445a1db5 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -184,24 +184,23 @@ FOUNDATION_EXPORT NSString * const kTSKDefaultReportUri; Register a block to be invoked for every request that is going through TrustKit's pinning validation mechanism. - Once TrustKit has been initialized, the callback will be invoked every time TrustKit validates - the certificate chain for a server configured in the SSL pinning policy; if the server's - hostname does not have an entry in the pinning policy, no invocations will result as no - pinning validation was performed. - - This callback can be used for performance measurement or to act upon any pinning validation - performed by TrustKit (for example to customize the reporting mechanism). The callback - provide the `TSKPinningValidatorResult` resulting from the validation. That instance provides - access to TrustKit's inner-workings which most Apps should not need. Hence, this callback - should not be set unless the App requires some advanced customization in regards to pinning - validation. Keep in mind that, if set, the callback may be invoked very frequently and is - not a suitable place for expensive tasks. + The callback will be invoked every time TrustKit validates the certificate chain for a + server configured in the SSL pinning policy; if the server's hostname does not have an + entry in the pinning policy, no invocations will result as no pinning validation was + performed. + + The callback provides the `TSKPinningValidatorResult` resulting from the validation, and can be + used for advanced features such as performance measurement or customizing the reporting mechanism. + Hence, most Apps should not have to use this callback. If set, the callback may be invoked very + frequently and is not a suitable place for expensive tasks. + + Lastly, the callback is always invoked after the validation has been completed, and therefore + cannot be used to modify the result of the validation (for example to accept invalid certificates). */ -// TODO(AD): Mention the fact that this will override the validation mechanism or address this @property (nonatomic, nullable) void(^validationDelegateCallback)(TSKPinningValidatorResult * _Nonnull result); /** - Queue on which to invoke `validationDelegateCallback` (if set). Default value is main queue. + Queue on which to invoke `validationDelegateCallback` (if set). Default value is the main queue. */ @property (nonatomic, null_resettable) dispatch_queue_t validationDelegateQueue; From fc27ad15a8d56a3780361527d5d855f48f885200 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Thu, 22 Jun 2017 14:57:56 -0700 Subject: [PATCH 056/126] Add safety checks for when using the shared instance and local instances --- TrustKit/TrustKit.m | 36 ++++++++++++++++++++---- TrustKitTests/TSKPinConfigurationTests.m | 18 ++++++++++++ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 592fbba1..f9b3e125 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -20,12 +20,16 @@ #import "TSKLog.h" // Info.plist key we read the public key hashes from -static const NSString *kTSKConfiguration = @"TSKConfiguration"; +static NSString * const kTSKConfiguration = @"TSKConfiguration"; + #pragma mark TrustKit Global State // Shared TrustKit singleton instance static TrustKit *sharedTrustKit = nil; +// The identifier we use for the singleton instance of TrustKit +static NSString * const kTSKSharedInstanceIdentifier = @"TSKSharedInstanceIdentifier"; + // A shared hash cache for use by all TrustKit instances static TSKSPKIHashCache *sharedHashCache; @@ -49,7 +53,7 @@ @implementation TrustKit + (instancetype)sharedInstance { if (!sharedTrustKit) { - // TrustKit should only be initialized once so we don't double interpose SecureTransport or get into anything unexpected + // TrustKit should only be initialized once so we don't double swizzle or get into anything unexpected [NSException raise:@"TrustKit was not initialized" format:@"TrustKit must be initialized using +initializeWithConfiguration: prior to accessing sharedInstance"]; } @@ -63,7 +67,7 @@ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedTrustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig - identifier:@"TrustKitShared"]; + identifier:kTSKSharedInstanceIdentifier]; // Hook network APIs if needed if ([sharedTrustKit.configuration[kTSKSwizzleNetworkDelegates] boolValue]) { @@ -106,14 +110,34 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo // Create our reporter for sending pin validation failures; do this before hooking NSURLSession so we don't hook ourselves _pinFailureReporter = [[TSKBackgroundReporter alloc] initAndRateLimitReports:YES]; - // Configure the pinning validator and register for pinning callbacks in order to - // trigger reports on the pinning failure reporter background queue. + // Handle global configuration flags here + // TSKIgnorePinningForUserDefinedTrustAnchors #if TARGET_OS_IPHONE BOOL userTrustAnchorBypass = NO; #else BOOL userTrustAnchorBypass = [_configuration[kTSKIgnorePinningForUserDefinedTrustAnchors] boolValue]; #endif - + + // TSKSwizzleNetworkDelegates - check if we are initializing the shared instance + if (![uniqueIdentifier isEqualToString:kTSKSharedInstanceIdentifier]) + { + if ([_configuration[kTSKSwizzleNetworkDelegates] boolValue] == YES) + { + // TSKSwizzleNetworkDelegates can only be enabled when using the shared instance, to avoid double swizzling + [NSException raise:@"TrustKit configuration invalid" + format:@"Cannot use TSKSwizzleNetworkDelegates outside the TrustKit sharedInstance"]; + } + + if ((sharedTrustKit) && ([[sharedTrustKit configuration][kTSKSwizzleNetworkDelegates] boolValue] == YES)) + { + // Local instances cannot be used if a shared instance with swizzling enabled is used, to avoid double pinning validation + [NSException raise:@"TrustKit configuration invalid" + format:@"Cannot use local TrustKit instances when the TrustKit sharedInstance has been initialized with kTSKSwizzleNetworkDelegates enabled"]; + } + } + + // Configure the pinning validator and register for pinning callbacks in order to + // trigger reports on the pinning failure reporter background queue. static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedHashCache = [[TSKSPKIHashCache alloc] initWithIdentifier:@"trustkit"]; diff --git a/TrustKitTests/TSKPinConfigurationTests.m b/TrustKitTests/TSKPinConfigurationTests.m index 612d6da5..279698e5 100644 --- a/TrustKitTests/TSKPinConfigurationTests.m +++ b/TrustKitTests/TSKPinConfigurationTests.m @@ -385,4 +385,22 @@ - (void)testAdditionalTrustAnchors XCTAssertEqualObjects(expectPem, gotPem); } +- (void)testSwizzleNetworkDelegatesInLocalInstance +{ + NSDictionary *trustKitConfig; + // Swizzling can only be enabled in the shared instance + trustKitConfig = @{kTSKSwizzleNetworkDelegates: @YES, + kTSKPinnedDomains : + @{@"good.com" : @{ + kTSKIncludeSubdomains : @YES, + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", + @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key + ]}}}; + + + XCTAssertThrows([[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:@"local"]); +} + + @end From fc1ead5888da940f611ae63df23c71515e448f6f Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Thu, 22 Jun 2017 15:02:35 -0700 Subject: [PATCH 057/126] Remove obsolete method --- TrustKit/TrustKit.h | 12 ------------ TrustKit/TrustKit.m | 5 ----- 2 files changed, 17 deletions(-) diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 445a1db5..eed11eb6 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -139,18 +139,6 @@ FOUNDATION_EXPORT NSString * const kTSKDefaultReportUri; + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig; -///---------------------------- -/// @name Current Configuration -///---------------------------- - -/** - Retrieve a copy of the global SSL pinning policy. - - @return A dictionary with a copy of the current TrustKit configuration, or `nil` if - TrustKit has not been initialized. - */ -+ (NSDictionary * _Nullable) configuration; - #pragma mark Instance Methods /** diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index f9b3e125..1db152eb 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -80,11 +80,6 @@ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig }); } -+ (NSDictionary * _Nullable)configuration -{ - TrustKit *singleton = [self sharedInstance]; - return singleton.configuration; -} #pragma mark Instance From 35542426c52a914b61e3e451025165449f30268d Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Thu, 22 Jun 2017 16:30:17 -0700 Subject: [PATCH 058/126] Make internal constant private --- TrustKit/TrustKit.h | 4 ---- TrustKit/TrustKit.m | 2 +- TrustKitTests/TSKReporterTests.m | 4 ++++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index eed11eb6..a87d1d25 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -21,10 +21,6 @@ NS_ASSUME_NONNULL_BEGIN -/** The default URI – maintained by DataTheorem – used for pinning failure reports - if none is specified in the configuration. - */ -FOUNDATION_EXPORT NSString * const kTSKDefaultReportUri; /** `TrustKit` is a class for programmatically configuring the global SSL pinning policy diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 1db152eb..a7a799ff 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -37,7 +37,7 @@ // Default report URI - can be disabled with TSKDisableDefaultReportUri // Email info@datatheorem.com if you need a free dashboard to see your App's reports -NSString * const kTSKDefaultReportUri = @"https://overmind.datatheorem.com/trustkit/report"; +static NSString * const kTSKDefaultReportUri = @"https://overmind.datatheorem.com/trustkit/report"; #pragma mark TrustKit Initialization Helper Functions diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index 158d7d58..736c9d44 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -30,6 +30,10 @@ @interface TrustKit (TestSupport) - (void)sendValidationReport:(TSKPinningValidatorResult *)result; @end + +static NSString * const kTSKDefaultReportUri = @"https://overmind.datatheorem.com/trustkit/report"; + + @interface TSKReporterTests : XCTestCase @end From 702c681e5ad9ab69be80393a950367eff186008f Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Thu, 22 Jun 2017 17:41:44 -0700 Subject: [PATCH 059/126] Bump version number in the documentation --- generate_documentation.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generate_documentation.sh b/generate_documentation.sh index 4ce83c50..43217c4f 100755 --- a/generate_documentation.sh +++ b/generate_documentation.sh @@ -6,8 +6,8 @@ jazzy \ --author "Data Theorem" \ --author_url https://datatheorem.github.io \ --github_url https://github.com/datatheorem/TrustKit \ - --github-file-prefix https://github.com/datatheorem/TrustKit/tree/1.4.2 \ - --module-version 1.4.2 \ + --github-file-prefix https://github.com/datatheorem/TrustKit/tree/1.5.0 \ + --module-version 1.5.0 \ --umbrella-header TrustKit/TrustKit.h \ --framework-root . \ --module TrustKit \ From 85f8b7b73447942fa2371121957c28c49a6c6e84 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Thu, 22 Jun 2017 17:42:56 -0700 Subject: [PATCH 060/126] Update documentation to mention singleton VS multi-instance mode --- TrustKit/TSKTrustKitConfig.h | 5 +- TrustKit/TrustKit.h | 104 +++++++++++++----------- TrustKitTests/TSKNSURLConnectionTests.m | 2 +- TrustKitTests/TSKNSURLSessionTests.m | 2 +- 4 files changed, 61 insertions(+), 52 deletions(-) diff --git a/TrustKit/TSKTrustKitConfig.h b/TrustKit/TSKTrustKitConfig.h index 48b47720..ba400193 100644 --- a/TrustKit/TSKTrustKitConfig.h +++ b/TrustKit/TSKTrustKitConfig.h @@ -6,8 +6,6 @@ // Copyright © 2017 TrustKit. All rights reserved. // -// These externs are currently fulfilled in TrustKit.m. They are here to prevent reverse -// includes since both TrustKit instances and low level internal C API need the definitions @import Foundation; @@ -40,7 +38,8 @@ typedef NSString *TSKDomainConfigurationKey; /** A boolean. If set to `YES`, TrustKit will perform method swizzling on the App's `NSURLConnection` and `NSURLSession` delegates in order to automatically add SSL pinning - validation to the App's connections. + validation to the App's connections. This option can only be used if TrustKit is + initialized in singleton mode. Swizzling allows enabling pinning within an App without having to find and modify each and every instance of `NSURLConnection` or `NSURLSession` delegates. diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index a87d1d25..25bc64bc 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -23,15 +23,23 @@ NS_ASSUME_NONNULL_BEGIN /** - `TrustKit` is a class for programmatically configuring the global SSL pinning policy - within an App. + `TrustKit` is a class for programmatically configuring an SSL pinning policy within an App. - The policy can be set either by adding it to the App's _Info.plist_ under the - `TSKConfiguration` key, or by programmatically supplying it using the `TrustKit` class - described here. Throughout the App's lifecycle, TrustKit can only be initialized once so - only one of the two techniques should be used. + For most Apps, TrustKit should be used as a singleton, where a global SSL pinning policy is + configured for the App. In singleton mode, the policy can be set either: - A TrustKit pinning policy is a dictionary which contains some global, App-wide settings + * By adding it to the App's _Info.plist_ under the `TSKConfiguration` key, or + * By programmatically supplying it using the `initializeWithConfiguration:` method. + + In singleton mode, TrustKit can only be initialized once so only one of the two techniques + should be used. + + For more complex Apps where multiple SSL pinning policies need to be used independently + (for example within different frameworks), TrustKit can be used in "multi-instance" mode + by leveraging the `initWithConfiguration:identifier:` method described at the end of this + page. + + A TrustKit pinning policy is a dictionary which contains some global, App-wide settings (of type `TSKGlobalConfigurationKey`) as well as domain-specific configuration keys (of type `TSKDomainConfigurationKey`) to be defined under the `kTSKPinnedDomains` entry. The following table shows the keys and the types of the corresponding values, and uses @@ -51,10 +59,12 @@ NS_ASSUME_NONNULL_BEGIN | ____ TSKEnforcePinning | Boolean | | ____ TSKReportUris | Array | | ____ TSKDisableDefaultReportUri | Boolean | + | ____ TSKAdditionalTrustAnchors | Array | ``` When setting the pinning policy programmatically, it has to be supplied to the - `initializeWithConfiguration:` method as a dictionary. For example: + `initializeWithConfiguration:` method as a dictionary in order to initialize the TrustKit + singleton. For example: ``` NSDictionary *trustKitConfig = @@ -82,9 +92,10 @@ NS_ASSUME_NONNULL_BEGIN }}; [TrustKit initializeWithConfiguration:trustKitConfig]; + trustKit = [TrustKit sharedInstance]; ``` - Similarly, TrustKit can be initialized in Swift: + Similarly, the TrustKit singleton can be initialized in Swift: ``` let trustKitConfig = [ @@ -106,28 +117,13 @@ NS_ASSUME_NONNULL_BEGIN */ @interface TrustKit : NSObject -///--------------------- -/// @name Initialization -///--------------------- -#pragma mark Class Methods +#pragma mark Singleton Mode /** - Access the shared TrustKit singleton instance. Raises an exception if +initializeWithConfiguration: - has not yet been invoked. - - @return the shared TrustKit singleton - */ -+ (instancetype)sharedInstance; - -/** - Initialize the global SSL pinning policy with the supplied configuration. + Initialize the global TrustKit singleton with the supplied pinning policy. - This method should be called as early as possible in the App's lifecycle to ensure that - the App's very first SSL connections are validated by TrustKit. - - @param trustKitConfig A dictionary containing various keys for configuring the global SSL - pinning policy. + @param trustKitConfig A dictionary containing various keys for configuring the SSL pinning policy. @exception NSException Thrown when the supplied configuration is invalid or TrustKit has already been initialized. @@ -135,47 +131,43 @@ NS_ASSUME_NONNULL_BEGIN + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig; -#pragma mark Instance Methods - /** - Initialize a TrustKit instance with the supplied SSL pinning policy configuration. - - This method should be called as early as possible in the App's lifecycle to ensure that - the App's very first SSL connections are validated by TrustKit. Once TrustKit has been - initialized, notifications will be posted for any SSL pinning validation performed. + Retrieve the global TrustKit singleton instance. Raises an exception if +initializeWithConfiguration: + has not yet been invoked. - @param trustKitConfig A dictionary containing various keys for configuring the global - SSL pinning policy. - @param uniqueIdentifier An identifier for this instance. It is required if you want the - pin to be persisted to disk. + @return the shared TrustKit singleton */ -- (instancetype)initWithConfiguration:(NSDictionary * _Nullable)trustKitConfig - identifier:(NSString * _Nullable)uniqueIdentifier; ++ (instancetype)sharedInstance; + /** - Retrieve the SSL pinning policy for this instance. + Retrieve the SSL pinning policy configured for this TrustKit instance. @return A dictionary with the current TrustKit configuration */ @property (nonatomic, readonly, nullable) NSDictionary *configuration; + /** - A pinning validator instance conforming to the configuration of this TrustKit instance. + Retrieve the validator instance conforming to the pinning policy of this TrustKit instance. + + The validator should be used to implement pinning validation within the App's network + authentication handlers. */ @property (nonatomic, nonnull) TSKPinningValidator *pinningValidator; + /** Register a block to be invoked for every request that is going through TrustKit's pinning validation mechanism. - The callback will be invoked every time TrustKit validates the certificate chain for a - server configured in the SSL pinning policy; if the server's hostname does not have an - entry in the pinning policy, no invocations will result as no pinning validation was - performed. + The callback will be invoked every time the validator performs pinning validation against a server's + certificate chain; if the server's hostname is not defined in the pinning policy, no invocations will + result as no pinning validation was performed. The callback provides the `TSKPinningValidatorResult` resulting from the validation, and can be used for advanced features such as performance measurement or customizing the reporting mechanism. - Hence, most Apps should not have to use this callback. If set, the callback may be invoked very + Hence, most Apps should not have to use this callback. If set, the callback may be invoked very frequently and is not a suitable place for expensive tasks. Lastly, the callback is always invoked after the validation has been completed, and therefore @@ -188,5 +180,23 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, null_resettable) dispatch_queue_t validationDelegateQueue; + +#pragma mark Multi-Instance Mode + +/** + Initialize a local TrustKit instance with the supplied SSL pinning policy configuration. + + This method is useful in scenarios where the TrustKit singleton cannot be used, for example within + larger Apps that have split some of their functionality into multiple framework/SDK. Each + framework can initialize its own instance of TrustKit and use it for pinning validation independently + of the App's other components. + + @param trustKitConfig A dictionary containing various keys for configuring the SSL pinning policy. + @param uniqueIdentifier An identifier for this instance. + */ +- (instancetype)initWithConfiguration:(NSDictionary * _Nullable)trustKitConfig + identifier:(NSString * _Nonnull)uniqueIdentifier; + + @end NS_ASSUME_NONNULL_END diff --git a/TrustKitTests/TSKNSURLConnectionTests.m b/TrustKitTests/TSKNSURLConnectionTests.m index 5a14177b..71dee01c 100644 --- a/TrustKitTests/TSKNSURLConnectionTests.m +++ b/TrustKitTests/TSKNSURLConnectionTests.m @@ -78,7 +78,7 @@ @implementation TSKNSURLConnectionTests - (void)setUp { [super setUp]; - _trustKit = [[TrustKit alloc] initWithConfiguration:@{ } identifier:nil]; + _trustKit = [[TrustKit alloc] initWithConfiguration:@{ } identifier:@"test"]; } - (void)tearDown { diff --git a/TrustKitTests/TSKNSURLSessionTests.m b/TrustKitTests/TSKNSURLSessionTests.m index ca65bf8d..32d0dc15 100644 --- a/TrustKitTests/TSKNSURLSessionTests.m +++ b/TrustKitTests/TSKNSURLSessionTests.m @@ -93,7 +93,7 @@ @implementation TSKNSURLSessionTests - (void)setUp { [super setUp]; - _trustKit = [[TrustKit alloc] initWithConfiguration:@{ } identifier:nil]; + _trustKit = [[TrustKit alloc] initWithConfiguration:@{ } identifier:@"test"]; } - (void)tearDown { From 29983dcd23ec5ed75c7d09ca4285c7a963b5325d Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 11:31:25 -0700 Subject: [PATCH 061/126] Make TSKPublicKeyAlgorithm private so it is not displayed in the documentation --- TrustKit/Pinning/TSKPublicKeyAlgorithm.h | 1 + TrustKit/TSKPinningValidator.h | 1 - TrustKit/TrustKit.h | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/TrustKit/Pinning/TSKPublicKeyAlgorithm.h b/TrustKit/Pinning/TSKPublicKeyAlgorithm.h index d806a57d..f6c1d1d3 100644 --- a/TrustKit/Pinning/TSKPublicKeyAlgorithm.h +++ b/TrustKit/Pinning/TSKPublicKeyAlgorithm.h @@ -14,6 +14,7 @@ @import Foundation; +// The internal enum we use for public key algorithms; not to be confused with the exported TSKSupportedAlgorithm typedef NS_ENUM(NSInteger, TSKPublicKeyAlgorithm) { // Some assumptions are made about this specific ordering in public_key_utils.m diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 2e7545fa..cbf69bcf 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -10,7 +10,6 @@ */ #import "TSKTrustDecision.h" -#import "TSKPublicKeyAlgorithm.h" @import Foundation; @class TSKPinningValidatorResult; diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 25bc64bc..bb280bd9 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -14,7 +14,6 @@ #ifndef _TRUSTKIT_ #define _TRUSTKIT_ #import "TSKTrustKitConfig.h" - #import "TSKPublicKeyAlgorithm.h" #import "TSKPinningValidator.h" #import "TSKTrustDecision.h" #endif /* _TRUSTKIT_ */ From 55fc551e65c66a0eb53fa237003f16cd6c2a1b94 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 11:36:52 -0700 Subject: [PATCH 062/126] Make TSKPinningValidatorResult public so it shows up in the documentation --- TrustKit/TrustKit.h | 1 + 1 file changed, 1 insertion(+) diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index bb280bd9..3d9bd5d6 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -14,6 +14,7 @@ #ifndef _TRUSTKIT_ #define _TRUSTKIT_ #import "TSKTrustKitConfig.h" + #import "TSKPinningValidatorResult.h" #import "TSKPinningValidator.h" #import "TSKTrustDecision.h" #endif /* _TRUSTKIT_ */ From 487fbe833a397b656b98eac0f6caac90e242edf1 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 11:37:13 -0700 Subject: [PATCH 063/126] Tweak documentation --- TrustKit/TSKTrustDecision.h | 1 - TrustKit/TSKTrustKitConfig.h | 10 ++-------- TrustKit/TrustKit.h | 4 ++-- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/TrustKit/TSKTrustDecision.h b/TrustKit/TSKTrustDecision.h index e59e95aa..8fd0e883 100644 --- a/TrustKit/TSKTrustDecision.h +++ b/TrustKit/TSKTrustDecision.h @@ -48,7 +48,6 @@ typedef NS_ENUM(NSInteger, TSKTrustEvaluationResult) /** Possible return values when verifying a server's identity against an SSL pinning policy. - */ typedef NS_ENUM(NSInteger, TSKTrustDecision) { diff --git a/TrustKit/TSKTrustKitConfig.h b/TrustKit/TSKTrustKitConfig.h index ba400193..2cbe2a08 100644 --- a/TrustKit/TSKTrustKitConfig.h +++ b/TrustKit/TSKTrustKitConfig.h @@ -212,7 +212,7 @@ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKExpirationDate; Standard RFC-7468 PEM format is supported (see https://tools.ietf.org/html/rfc7468#section-2 ). Note that the header, footer and any newlines are optional, but aid in readability. - ~~ SECURITY WARNING ~~ + __SECURITY WARNING:__ Misuse of this configuration option could potentially render your application vulnerable to exploits since it bypasses the normal operating system trust store. It is intended for enterprise scenarios where a company might be running their @@ -225,13 +225,7 @@ FOUNDATION_EXPORT const TSKDomainConfigurationKey kTSKAdditionalTrustAnchors; /** - A public key algorithm supported by TrustKit for computing SSL pins: - - * `kTSKAlgorithmRsa2048` - * `kTSKAlgorithmRsa4096` - * `kTSKAlgorithmEcDsaSecp256r1` - * `kTSKAlgorithmEcDsaSecp384r1` - + A public key algorithm supported by TrustKit for generating the SSL pin for a certificate. */ typedef NSString *TSKSupportedAlgorithm; diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 3d9bd5d6..569d3e78 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -63,8 +63,8 @@ NS_ASSUME_NONNULL_BEGIN ``` When setting the pinning policy programmatically, it has to be supplied to the - `initializeWithConfiguration:` method as a dictionary in order to initialize the TrustKit - singleton. For example: + `initializeWithConfiguration:` method as a dictionary in order to initialize TrustKit. + For example: ``` NSDictionary *trustKitConfig = From e28566c16dbbbae57dc40d1e19253383bbc2f23c Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 11:40:41 -0700 Subject: [PATCH 064/126] Switch to .jazzy.yaml file and use custom categories for documentation --- .jazzy.yaml | 59 +++++++++++++++++++++++++++++++++++++++ generate_documentation.sh | 14 ---------- 2 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 .jazzy.yaml delete mode 100755 generate_documentation.sh diff --git a/.jazzy.yaml b/.jazzy.yaml new file mode 100644 index 00000000..65bb975f --- /dev/null +++ b/.jazzy.yaml @@ -0,0 +1,59 @@ +module: TrustKit +author: Data Theorem +author_url: https://datatheorem.github.io +github_url: https://github.com/datatheorem/TrustKit + +clean: true +hide_documentation_coverage: true +theme: fullwidth + +objc: true +umbrella_header: "TrustKit/TrustKit.h" +framework_root: "./TrustKit" + +output: "docs/documentation" + +custom_categories: + - name: Initalizing TrustKit + children: + - TrustKit + + - name: Implementing Pinning Validation + children: + - TSKPinningValidator + - TSKTrustDecision + + - name: Receiving Validation Notifications + children: + - TSKPinningValidatorResult + - TSKTrustEvaluationResult + + - name: Global Configuration Keys + children: + - TSKGlobalConfigurationKey + - kTSKSwizzleNetworkDelegates + - kTSKPinnedDomains + - kTSKIgnorePinningForUserDefinedTrustAnchors + + - name: Domain Configuration Keys + children: + - TSKDomainConfigurationKey + - kTSKPublicKeyHashes + - kTSKPublicKeyAlgorithms + - kTSKEnforcePinning + - kTSKIncludeSubdomains + - kTSKExcludeSubdomainFromParentPolicy + - kTSKReportUris + - kTSKDisableDefaultReportUri + - kTSKExpirationDate + - kTSKAdditionalTrustAnchors + + - name: Public Key Algorithm Keys + children: + - TSKSupportedAlgorithm + - kTSKAlgorithmRsa2048 + - kTSKAlgorithmRsa4096 + - kTSKAlgorithmEcDsaSecp256r1 + - kTSKAlgorithmEcDsaSecp384r1 + + diff --git a/generate_documentation.sh b/generate_documentation.sh deleted file mode 100755 index 43217c4f..00000000 --- a/generate_documentation.sh +++ /dev/null @@ -1,14 +0,0 @@ -jazzy \ - --readme ./docs/documentation-readme.md \ - --hide-documentation-coverage \ - --objc \ - --clean \ - --author "Data Theorem" \ - --author_url https://datatheorem.github.io \ - --github_url https://github.com/datatheorem/TrustKit \ - --github-file-prefix https://github.com/datatheorem/TrustKit/tree/1.5.0 \ - --module-version 1.5.0 \ - --umbrella-header TrustKit/TrustKit.h \ - --framework-root . \ - --module TrustKit \ - --output docs/documentation \ No newline at end of file From 54b0e6d7ba55a74d9a01ff4cc7ebf86d75648f65 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 12:38:43 -0700 Subject: [PATCH 065/126] Clarify documentation and hide internal methods --- TrustKit/TSKPinningValidator.h | 125 +++++++++++++++++---------------- TrustKit/TrustKit.h | 22 +++--- 2 files changed, 77 insertions(+), 70 deletions(-) diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index cbf69bcf..be172f67 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -17,7 +17,7 @@ /** - `TSKPinningValidator` is a class for manually verifying a server's identity against the global SSL pinning policy. + `TSKPinningValidator` is a class for manually verifying a server's identity against an SSL pinning policy. In specific scenarios, TrustKit cannot intercept outgoing SSL connections and automatically validate the server's identity against the pinning policy: @@ -27,22 +27,72 @@ * Connections leveraging low-level network APIs (such as `NSStream`). * Connections initiated using a third-party SSL library such as OpenSSL. - For these connections, pin validation must be manually triggered using one of the two available methods: + For these connections, pin validation must be manually triggered using one of the two available methods within `TSKPinningValidator`. + */ +@interface TSKPinningValidator : NSObject + +#pragma mark High Level Validation Method + +/** + Helper method for handling authentication challenges received within a `NSURLSessionDelegate`, `NSURLSessionTaskDelegate` or `WKNavigationDelegate`. - * `evaluateTrust:forHostname:` which evaluates the server's certificate chain against the global SSL pinning policy. - * `handleChallenge:completionHandler:` a helper method to be used for implementing pinning validation in challenge handler methods within `NSURLSession` and `WKWebView` delegates. + This method will evaluate the server trust within the authentication challenge against the global SSL pinning policy previously configured, and then call the `completionHandler` with the corresponding `disposition` and `credential`. For example, this method can be leveraged in a `WKNavigationDelegate` challenge handler method: + + - (void)webView:(WKWebView *)webView + didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential *credential))completionHandler + { + if (![TSKPinningValidator handleChallenge:challenge completionHandler:completionHandler]) + { + // TrustKit did not handle this challenge: perhaps it was not for server trust + // or the domain was not pinned. Fall back to the default behavior + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } + } + @param challenge The authentication challenge, supplied by the URL loading system to the delegate's challenge handler method. + + @param completionHandler A block to invoke to respond to the challenge, supplied by the URL loading system to the delegate's challenge handler method. + + @return `YES` if the challenge was handled and the `completionHandler` was successfuly invoked. `NO` if the challenge could not be handled because it was not for server certificate validation (ie. the challenge's `authenticationMethod` was not `NSURLAuthenticationMethodServerTrust`). + + @exception NSException Thrown when TrustKit has not been initialized with a pinning policy. */ -@interface TSKPinningValidator : NSObject +- (BOOL)handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge + completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential * _Nullable credential))completionHandler; + + +#pragma mark Low Level Validation Method + +/** + Evaluate the supplied server trust against the SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent. + + When using the `NSURLSession` or `WKWebView` network APIs, the `handleChallenge:completionHandler:` method should be called instead, as it is simpler to use. + + When using low-level network APIs (such as `NSStream`), instructions on how to retrieve the connection's `serverTrust` are available at https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html . + + @param serverTrust The trust object representing the server's certificate chain. The trust's evaluation policy is always overridden using `SecTrustSetPolicies()` to ensure all the proper SSL checks (expiration, hostname validation, etc.) are enabled. + + @param serverHostname The hostname of the server whose identity is being validated. + + @return A `TSKTrustDecision` which describes whether the SSL connection should be allowed or blocked, based on the global pinning policy. + + @warning If no SSL pinning policy was configured for the supplied _serverHostname_, this method has no effect and will return `TSKTrustDecisionDomainNotPinned` without validating the supplied _serverTrust_ at all. This means that the server's _serverTrust_ object __must__ be verified against the device's trust store using `SecTrustEvaluate()`. Failing to do so will __disable SSL certificate validation__. + + @exception NSException Thrown when TrustKit has not been initialized with a pinning policy. + */ +- (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname:(NSString * _Nonnull)serverHostname; + -///------------------------------------ -/// @name Manual SSL Pinning Validation -///------------------------------------ +#pragma mark Internal Methods /** + /// :nodoc: If this property returns YES, pinning may include any additional trust anchors provided in a domain configuration under the kTSKAdditionalTrustAnchors key. - + This property is YES only when the preprocessor flag DEBUG is set to 1 (the default behavior for the "Debug" configuration of an Xcode project). Subclasses may override this method – with extreme caution – to alter this behavior. @@ -50,29 +100,34 @@ @property (nonatomic, class, readonly) BOOL allowsAdditionalTrustAnchors; /** + /// :nodoc: Domain pinning configuration, typically obtained by parseTrustKitConfiguration() */ @property (nonatomic, readonly, nullable) NSDictionary *pinnedDomains; /** + /// :nodoc: Set to true to ignore the trust anchors in the user trust store. Only applicable to platforms that support a user trust store (Mac OS). */ @property (nonatomic, readonly) BOOL ignorePinsForUserTrustAnchors; /** + /// :nodoc: The queue use when invoking the validationResultHandler */ @property (nonatomic, readonly, nonnull) dispatch_queue_t validationResultQueue; /** + /// :nodoc: The callback invoked with validation results */ @property (nonatomic, readonly, nonnull) void(^validationResultHandler)(TSKPinningValidatorResult * _Nonnull result); /** - Initialize an instance of TSKPinningValidatorResult - + /// :nodoc: + Initialize an instance of TSKPinningValidatorResult - should only be used within TrustKit. + @param pinnedDomains Domain pinning configuration, typically obtained by parseTrustKitConfiguration() @param hashCache The hash cache to use. If nil, no caching is performed, performance may suffer. @param ignorePinsForUserTrustAnchors Set to true to ignore the trust anchors in the user trust store @@ -86,54 +141,6 @@ validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler; -/** - Evaluate the supplied server trust against the SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent. - - When using the `NSURLSession` or `WKWebView` network APIs, the `handleChallenge:completionHandler:` method should be called instead, as it is simpler to use. - - When using low-level network APIs (such as `NSStream`), instructions on how to retrieve the connection's `serverTrust` are available at https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html . - - @param serverTrust The trust object representing the server's certificate chain. The trust's evaluation policy is always overridden using `SecTrustSetPolicies()` to ensure all the proper SSL checks (expiration, hostname validation, etc.) are enabled. - - @param serverHostname The hostname of the server whose identity is being validated. - - @return A `TSKTrustDecision` which describes whether the SSL connection should be allowed or blocked, based on the global pinning policy. - - @warning If no SSL pinning policy was configured for the supplied _serverHostname_, this method has no effect and will return `TSKTrustDecisionDomainNotPinned` without validating the supplied _serverTrust_ at all. This means that the server's _serverTrust_ object __must__ be verified against the device's trust store using `SecTrustEvaluate()`. Failing to do so will __disable SSL certificate validation__. - - @exception NSException Thrown when TrustKit has not been initialized with a pinning policy. - */ -- (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname:(NSString * _Nonnull)serverHostname; - - -/** - Helper method for handling authentication challenges received within a `NSURLSessionDelegate`, `NSURLSessionTaskDelegate` or `WKNavigationDelegate`. - - This method will evaluate the server trust within the authentication challenge against the global SSL pinning policy previously configured, and then call the `completionHandler` with the corresponding `disposition` and `credential`. For example, this method can be leveraged in a `WKNavigationDelegate` challenge handler method: - - (void)webView:(WKWebView *)webView - didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge - completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential *credential))completionHandler - { - if (![TSKPinningValidator handleChallenge:challenge completionHandler:completionHandler]) - { - // TrustKit did not handle this challenge: perhaps it was not for server trust - // or the domain was not pinned. Fall back to the default behavior - completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); - } - } - - @param challenge The authentication challenge, supplied by the URL loading system to the delegate's challenge handler method. - - @param completionHandler A block to invoke to respond to the challenge, supplied by the URL loading system to the delegate's challenge handler method. - - @return `YES` if the challenge was handled and the `completionHandler` was successfuly invoked. `NO` if the challenge could not be handled because it was not for server certificate validation (ie. the challenge's `authenticationMethod` was not `NSURLAuthenticationMethodServerTrust`). - - @exception NSException Thrown when TrustKit has not been initialized with a pinning policy. - */ -- (BOOL)handleChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge - completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential * _Nullable credential))completionHandler; @end diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 569d3e78..7d7e3257 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -29,14 +29,14 @@ NS_ASSUME_NONNULL_BEGIN configured for the App. In singleton mode, the policy can be set either: * By adding it to the App's _Info.plist_ under the `TSKConfiguration` key, or - * By programmatically supplying it using the `initializeWithConfiguration:` method. + * By programmatically supplying it using the `+initializeWithConfiguration:` method. In singleton mode, TrustKit can only be initialized once so only one of the two techniques should be used. For more complex Apps where multiple SSL pinning policies need to be used independently (for example within different frameworks), TrustKit can be used in "multi-instance" mode - by leveraging the `initWithConfiguration:identifier:` method described at the end of this + by leveraging the `-initWithConfiguration:identifier:` method described at the end of this page. A TrustKit pinning policy is a dictionary which contains some global, App-wide settings @@ -132,7 +132,7 @@ NS_ASSUME_NONNULL_BEGIN /** - Retrieve the global TrustKit singleton instance. Raises an exception if +initializeWithConfiguration: + Retrieve the global TrustKit singleton instance. Raises an exception if `+initializeWithConfiguration:` has not yet been invoked. @return the shared TrustKit singleton @@ -140,14 +140,6 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)sharedInstance; -/** - Retrieve the SSL pinning policy configured for this TrustKit instance. - - @return A dictionary with the current TrustKit configuration - */ -@property (nonatomic, readonly, nullable) NSDictionary *configuration; - - /** Retrieve the validator instance conforming to the pinning policy of this TrustKit instance. @@ -181,6 +173,14 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, null_resettable) dispatch_queue_t validationDelegateQueue; +/** + Retrieve the SSL pinning policy configured for this TrustKit instance. + + @return A dictionary with the current TrustKit configuration + */ +@property (nonatomic, readonly, nullable) NSDictionary *configuration; + + #pragma mark Multi-Instance Mode /** From 710cebab9e85651fb47c31f39339f57346ae54c3 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 12:54:13 -0700 Subject: [PATCH 066/126] Clarify documentation --- TrustKit/TSKPinningValidatorResult.h | 36 +++++++++++++--------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h index b229ad53..b60821f0 100644 --- a/TrustKit/TSKPinningValidatorResult.h +++ b/TrustKit/TSKPinningValidatorResult.h @@ -20,51 +20,49 @@ @property (nonatomic, readonly, nonnull) NSString *serverHostname; /** - The original SecTrustRef that validation was performed against. + The original `SecTrustRef` that validation was performed against. */ @property (nonatomic, readonly, nonnull) SecTrustRef serverTrust; /** The entry within the SSL pinning configuration that was used as the pinning policy for the - server being validated. It will be the same as the `kTSKValidationServerHostnameNotificationKey` - entry unless the server is a subdomain of a domain configured in the pinning policy with - `kTSKIncludeSubdomains` enabled. The corresponding pinning configuration that was used - for validation can be retrieved using: + server being validated. It will be the same as the `serverHostname` unless the server is a + subdomain of the domain configured in the pinning policy with `kTSKIncludeSubdomains` enabled. + The corresponding pinning configuration that was used for validation can be retrieved using: - NSString *notedHostname = userInfo[kTSKValidationNotedHostnameNotificationKey]; - NSDictionary *hostnameConfiguration = [TrustKit configuration][kTSKPinnedDomains][notedHostname]; + NSDictionary *hostnameConfiguration = [trustKit configuration][kTSKPinnedDomains][notedHostname]; */ @property (nonatomic, readonly, nonnull) NSString *notedHostname; /** - The `TSKPinValidationResult` returned when validating the server's certificate chain, which - represents the result of evaluating the certificate chain against the configured SSL pins - for this server. + The result of validating the server's certificate chain against the set of SSL pins configured for + the `notedHostname`. */ @property (nonatomic, readonly) TSKTrustEvaluationResult validationResult; /** - The `TSKTrustDecision` returned when validating the certificate's chain, which describes - whether the connection should be blocked or allowed, based on the `TSKPinningValidationResult` - returned when evaluating the server's certificate chain and the SSL pining policy configured - for this server. + The trust decision returned for this connection, which describes whether the connection should be blocked + or allowed, based on the `validationResult` returned when evaluating the `serverTrust` and the SSL pining + policy configured for this server. - For example, the pinning validation could have failed (returning `TSKPinningValidationFailed`) - but the policy might be set to ignore pinning validation failures for this server, thereby - returning `TSKTrustDecisionShouldAllowConnection`. + For example, the pinning validation could have failed (ie. validationResult being + `TSKTrustEvaluationFailedNoMatchingPin`) but the policy might be set to ignore pinning validation failures + for this server, thereby returning `TSKTrustDecisionShouldAllowConnection`. */ @property (nonatomic, readonly) TSKTrustDecision finalTrustDecision; /** - The time in seconds it took for the SSL pinning validation to be performed. + The time it took for the SSL pinning validation to be performed. */ @property (nonatomic, readonly) NSTimeInterval validationDuration; /** - The certificate chain returned by the server as an array of PEM-formatted certificates. + The certificate chain extracted from the `serverTrust` as PEM-formatted certificates. This is the + certificate chain sent by the server when establishing the connection. */ @property (nonatomic, readonly, nullable) NSArray *certificateChain; +/// :nodoc: - (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHostname serverTrust:(SecTrustRef _Nonnull)serverTrust notedHostname:(NSString * _Nonnull)notedHostname From cd06719d63c1f7d741493c0799644cb6782e85ec Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 12:54:24 -0700 Subject: [PATCH 067/126] Fix documentation --- TrustKit/TSKPinningValidator.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index be172f67..71da6a1e 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -43,7 +43,8 @@ completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { - if (![TSKPinningValidator handleChallenge:challenge completionHandler:completionHandler]) + TSKPinningValidator *pinningValidator = [[TrustKit sharedInstance] pinningValidator]; + if (![pinningValidator handleChallenge:challenge completionHandler:completionHandler]) { // TrustKit did not handle this challenge: perhaps it was not for server trust // or the domain was not pinned. Fall back to the default behavior From 155945b3bdce3a13057c8cb8f0eaca9fda8af9f7 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 13:00:10 -0700 Subject: [PATCH 068/126] Fix to ensure the shared cache is always used --- TrustKit/TrustKit.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index a7a799ff..f8b7c176 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -135,12 +135,12 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo // trigger reports on the pinning failure reporter background queue. static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - sharedHashCache = [[TSKSPKIHashCache alloc] initWithIdentifier:@"trustkit"]; + sharedHashCache = [[TSKSPKIHashCache alloc] initWithIdentifier:kTSKSPKISharedHashCacheIdentifier]; }); __weak typeof(self) weakSelf = self; _pinningValidator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:_configuration - hashCache:[[TSKSPKIHashCache alloc] initWithIdentifier:kTSKSPKISharedHashCacheIdentifier] + hashCache:sharedHashCache ignorePinsForUserTrustAnchors:userTrustAnchorBypass validationResultQueue:_pinFailureReporterQueue validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { From 37eb3be6050d5de8c736d6f6efc8215aa6319286 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 13:06:00 -0700 Subject: [PATCH 069/126] Switch kTSKSwizzleNetworkDelegates to OFF by default --- TrustKit/TSKTrustKitConfig.h | 2 +- TrustKit/parse_configuration.m | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/TrustKit/TSKTrustKitConfig.h b/TrustKit/TSKTrustKitConfig.h index 2cbe2a08..87dd6533 100644 --- a/TrustKit/TSKTrustKitConfig.h +++ b/TrustKit/TSKTrustKitConfig.h @@ -39,7 +39,7 @@ typedef NSString *TSKDomainConfigurationKey; A boolean. If set to `YES`, TrustKit will perform method swizzling on the App's `NSURLConnection` and `NSURLSession` delegates in order to automatically add SSL pinning validation to the App's connections. This option can only be used if TrustKit is - initialized in singleton mode. + initialized in singleton mode; default value is `NO`. Swizzling allows enabling pinning within an App without having to find and modify each and every instance of `NSURLConnection` or `NSURLSession` delegates. diff --git a/TrustKit/parse_configuration.m b/TrustKit/parse_configuration.m index b81b7703..18417dc6 100644 --- a/TrustKit/parse_configuration.m +++ b/TrustKit/parse_configuration.m @@ -63,11 +63,8 @@ static SecCertificateRef certificateFromPEM(NSString *pem) NSNumber *shouldSwizzleNetworkDelegates = TrustKitArguments[kTSKSwizzleNetworkDelegates]; if (shouldSwizzleNetworkDelegates == nil) { - // This is a required argument - [NSException raise:@"TrustKit configuration invalid" - format:@"TrustKit was initialized without specifying the kTSKSwizzleNetworkDelegates setting. Please add this boolean entry to the root of your TrustKit configuration in order to specify if auto-swizzling of the App's connection delegates should be enabled or not; see the documentation for more information."]; - // Default setting is YES - finalConfiguration[kTSKSwizzleNetworkDelegates] = @(YES); + // Default setting is NO + finalConfiguration[kTSKSwizzleNetworkDelegates] = @(NO); } else { From 8de6dd77f2da0965be71602d5bb9dacc01a6952f Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Fri, 23 Jun 2017 16:43:05 -0400 Subject: [PATCH 070/126] Various small fixes from previous PRs --- TrustKit/TSKPinningValidator.h | 6 ++++-- TrustKit/TrustKit.h | 5 +---- TrustKit/TrustKit.m | 15 +++++++++------ TrustKitTests/TSKNSURLConnectionTests.m | 2 +- TrustKitTests/TSKNSURLSessionTests.m | 2 +- TrustKitTests/TSKPinConfigurationTests.m | 1 + TrustKitTests/TSKPinningValidatorTests.m | 6 +++--- TrustKitTests/TSKReporterTests.m | 2 +- 8 files changed, 21 insertions(+), 18 deletions(-) diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 358260b7..3f67395c 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -118,9 +118,11 @@ typedef NSData* _Nullable(^HashCertificateBlock)(_Nonnull SecCertificateRef cert completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { - if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) + if (![TSKPinningValidator handleChallenge:challenge completionHandler:completionHandler]) { - [TSKPinningValidator handleChallenge:challenge completionHandler:completionHandler]; + // TrustKit did not handle this challenge: perhaps it was not for server trust + // or the domain was not pinned. Fall back to the default behavior + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); } } diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 3a3d5369..9e3ef446 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -163,11 +163,8 @@ FOUNDATION_EXPORT NSString * const kTSKDefaultReportUri; @param trustKitConfig A dictionary containing various keys for configuring the global SSL pinning policy. - @param uniqueIdentifier An identifier for this instance. It is required if you want the - pin to be persisted to disk. */ -- (instancetype)initWithConfiguration:(NSDictionary * _Nullable)trustKitConfig - identifier:(NSString * _Nullable)uniqueIdentifier; +- (instancetype)initWithConfiguration:(NSDictionary * _Nullable)trustKitConfig; /** Retrieve the SSL pinning policy for this instance. diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 4072e20c..daab801f 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -62,8 +62,7 @@ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - sharedTrustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig - identifier:@"TrustKitShared"]; + sharedTrustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; // Hook network APIs if needed if ([sharedTrustKit.configuration[kTSKSwizzleNetworkDelegates] boolValue]) { @@ -84,7 +83,7 @@ + (NSDictionary * _Nullable)configuration #pragma mark Instance -- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig identifier:(NSString *)uniqueIdentifier +- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig { NSParameterAssert(trustKitConfig); if (!trustKitConfig) { @@ -125,9 +124,13 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo ignorePinsForUserTrustAnchors:userTrustAnchorBypass validationResultQueue:_pinFailureReporterQueue validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { - + typeof(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + // Invoke client handler if set - void(^callback)(TSKPinningValidatorResult *) = self.validationDelegateCallback; + void(^callback)(TSKPinningValidatorResult *) = strongSelf.validationDelegateCallback; if (callback) { dispatch_async(self.validationDelegateQueue, ^{ callback(result); @@ -135,7 +138,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo } // Send analytics report - [weakSelf sendValidationReport:result]; + [strongSelf sendValidationReport:result]; }]; TSKLog(@"Successfully initialized with configuration %@", _configuration); diff --git a/TrustKitTests/TSKNSURLConnectionTests.m b/TrustKitTests/TSKNSURLConnectionTests.m index c6e52966..330658c1 100644 --- a/TrustKitTests/TSKNSURLConnectionTests.m +++ b/TrustKitTests/TSKNSURLConnectionTests.m @@ -78,7 +78,7 @@ @implementation TSKNSURLConnectionTests - (void)setUp { [super setUp]; - _trustKit = [[TrustKit alloc] initWithConfiguration:@{ } identifier:nil]; + _trustKit = [[TrustKit alloc] initWithConfiguration:@{ }]; } - (void)tearDown { diff --git a/TrustKitTests/TSKNSURLSessionTests.m b/TrustKitTests/TSKNSURLSessionTests.m index d388fbd4..3fec623c 100644 --- a/TrustKitTests/TSKNSURLSessionTests.m +++ b/TrustKitTests/TSKNSURLSessionTests.m @@ -93,7 +93,7 @@ @implementation TSKNSURLSessionTests - (void)setUp { [super setUp]; - _trustKit = [[TrustKit alloc] initWithConfiguration:@{ } identifier:nil]; + _trustKit = [[TrustKit alloc] initWithConfiguration:@{ }]; } - (void)tearDown { diff --git a/TrustKitTests/TSKPinConfigurationTests.m b/TrustKitTests/TSKPinConfigurationTests.m index b926b333..612d6da5 100644 --- a/TrustKitTests/TSKPinConfigurationTests.m +++ b/TrustKitTests/TSKPinConfigurationTests.m @@ -16,6 +16,7 @@ #import "../TrustKit/Pinning/ssl_pin_verifier.h" #import "../TrustKit/Pinning/TSKPublicKeyAlgorithm.h" #import "../TrustKit/parse_configuration.h" +#import "../TrustKit/configuration_utils.h" #import "TSKCertificateUtils.h" @interface TSKPinConfigurationTests : XCTestCase diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 8546b70a..0d64d8c1 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -858,7 +858,7 @@ -(void) testHandleChallengeCompletionHandlerDomainNotPinned kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=", // CA Key @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}; - TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:@"test"]; + TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; __block BOOL wasHandlerCalled = NO; void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) @@ -1029,7 +1029,7 @@ -(void) testHandleChallengeCompletionHandlerNotServerTrustAuthenticationMethod kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=", // CA Key @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Fake key ]}}}; - TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:@"test"]; + TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; __block BOOL wasHandlerCalled = NO; void (^completionHandler)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable) = ^void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential) @@ -1083,7 +1083,7 @@ - (void)testExcludedSubdomain }}; // Then test TSKPinningValidator - TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:@"test"]; + TrustKit *tk = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; XCTAssertEqual([tk.pinningValidator evaluateTrust:trust forHostname:@"unsecured.good.com"], TSKTrustDecisionDomainNotPinned); diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index 5e3bc81d..05d563a5 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -93,7 +93,7 @@ - (void)testSendReportFromValidationReport @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Fake key 2 ]}}}; - _trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:@"test"]; + _trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; // Expect a report to be sent out when a notification is posted NSSet *knownPins = [NSSet setWithArray:@[[[NSData alloc]initWithBase64EncodedString:@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" From e61d7d053fa4af82e5789be2a7b13d31fc9f6821 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 14:01:53 -0700 Subject: [PATCH 071/126] Update the Getting Started guide --- docs/getting-started.md | 258 ++++++++++++++----------------- docs/images/linking1.png | Bin 54470 -> 0 bytes docs/images/linking2_dynamic.png | Bin 25033 -> 0 bytes docs/images/linking2_static.png | Bin 30632 -> 0 bytes docs/images/linking3_static.png | Bin 71914 -> 0 bytes 5 files changed, 115 insertions(+), 143 deletions(-) delete mode 100755 docs/images/linking1.png delete mode 100755 docs/images/linking2_dynamic.png delete mode 100755 docs/images/linking2_static.png delete mode 100755 docs/images/linking3_static.png diff --git a/docs/getting-started.md b/docs/getting-started.md index c5ad6b2b..25a101a6 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -48,10 +48,11 @@ certificate: ## Deploying TrustKit -### Adding TrustKit as a Dependency - CocoaPods +### Adding TrustKit as a Dependency -The easiest way to deploy TrustKit in an App is via CocoaPods. To do so, add the -following line to your Podfile: +#### CocoaPods + +Add the following line to your Podfile: pod 'TrustKit' @@ -59,9 +60,19 @@ Then run: $ pod install -If CocoaPods cannot be used, TrustKit can be added to an Xcode project manually; -instructions on how to do so are available at the end of this guide. +#### Carthage + +Add the following line to your Cartfile: + + github "datatheorem/TrustKit" == 1.5.0 + +Then run: + + $ carthage build --platform iOS +Lastly, on your application targets’ “General” settings tab, in the “Embedded Binaries” +section, drag and drop the TrustKit framework you want to use from the Carthage/Build +folder on disk. ### Configuring a Pinning Policy @@ -81,51 +92,76 @@ A pinning policy is a dictionary of domain names and pinning configuration keys. At a minimum, the configuration should specify a list of SSL pins and the corresponding certificates' public key algorithms. For example: - #import +``` +#import + +NSDictionary *trustKitConfig = +@{ + // The list of domains we want to pin and their configuration + kTSKPinnedDomains: @{ + + @"yahoo.com" : @{ + // Pin all subdomains of yahoo.com + kTSKIncludeSubdomains:@YES, + + // Do not block connections if pinning validation failed so the App doesn't break + kTSKEnforcePinning:@NO, + + // Send reports for pin validation failures so we can track them + kTSKReportUris: @[@"https://some-reporting-server.com/log_report"], + + // The pinned public keys' algorithms + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + + // The pinned public keys' Subject Public Key Info hashes + kTSKPublicKeyHashes : @[ + @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", + @"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=", + ], + }, + + @"www.datatheorem.com" : @{ + // Block connections if pinning validation failed + kTSKEnforcePinning:@YES, + + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + + kTSKPublicKeyHashes : @[ + @"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=", + @"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8=" + ] +}}}; +``` - NSDictionary *trustKitConfig = - @{ - // Auto-swizzle NSURLSession and NSURLConnection delegates to add pinning validation - kTSKSwizzleNetworkDelegates: @YES, - - // The list of domains we want to pin and their configuration - kTSKPinnedDomains: @{ - - @"yahoo.com" : @{ - // Pin all subdomains of yahoo.com - kTSKIncludeSubdomains:@YES, - - // Do not block connections if pinning validation failed so the App doesn't break - kTSKEnforcePinning:@NO, - - // Send reports for pin validation failures so we can track them - kTSKReportUris: @[@"https://some-reporting-server.com/log_report"], - - // The pinned public keys' algorithms - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], - - // The pinned public keys' Subject Public Key Info hashes - kTSKPublicKeyHashes : @[ - @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", - @"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=", - ], - }, - - @"www.datatheorem.com" : @{ - // Block connections if pinning validation failed - kTSKEnforcePinning:@YES, - - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - - kTSKPublicKeyHashes : @[ - @"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=", - @"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8=" - ] - }}}; +The list of all the configuration keys is available in the +[documentation](https://datatheorem.github.io/TrustKit/documentation/Classes/TrustKit.html). + +### Implementing Pinning Validation +After TrustKit has been initialized, a +[`TSKPinningValidator` instance](https://datatheorem.github.io/TrustKit/documentation/Classes/TSKPinningValidator.html) +can be retrieved from the TrustKit singleton, and can be used to perform SSL pinning validation +in the App's network delegates. For example in an NSURLSessionDelegate: + +``` +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { +{ + TSKPinningValidator *pinningValidator = [[TrustKit sharedInstance] pinningValidator]; + if (![pinningValidator handleChallenge:challenge completionHandler:completionHandler]) + { + // TrustKit did not handle this challenge: perhaps it was not for server trust + // or the domain was not pinned. Fall back to the default behavior + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } +} +``` Some additional consideration in regards to the right pinning policy to deploy follow. +### Additional Notes #### Always start with pinning enforcement disabled @@ -155,6 +191,24 @@ intermediate CA in order to allow their users to use the app, or face weeks of the app being unusable. +#### Properly implement pinning in WebViews + +Adding SSL pinning to connections initiated within a `UIWebView` is difficult as the +class does not provide direct APIs to handle authentication challenges. As +mentionned in [Apple's technical note about HTTPS trust evaluation](https://developer.apple.com/library/ios/technotes/tn2232/_index.html), +customizing certificate validation in a `UIWebView` can still be achieved using +`NSURLProtocol` to intercept all outgoing connections. However, implemeting this +technique is a complex and significant engineering effort. + +Overall, the best approach to implementing SSL pinning in webviews is by +migrating to the `WKWebView` class introduced in iOS 8, which provides +[delegate methods](https://developer.apple.com/library/ios/documentation/WebKit/Reference/WKNavigationDelegate_Ref/) +to handle authentication challenges (such as server SSL certificate validation). +However, this approach still requires some testing as it seems like the +`webView:didReceiveAuthenticationChallenge:completionHandler:` delegate method +[only works reliably on iOS 9](https://bugs.webkit.org/show_bug.cgi?id=135327). + + #### Consider leveraging auto-swizzling for simple Apps By setting `kTSKSwizzleNetworkDelegates` to `YES`, TrustKit will perform method @@ -173,11 +227,12 @@ by another module or library (such as Analytics SDKs) * Apps that do no use `NSURLSession` or `NSURLConnection` for their connections. -Auto-swizzling can be disabled by setting `kTSKSwizzleNetworkDelegates` to -`NO`. Manual pinning validation can then be easily implemented in the App's -authentication handlers'; see the "Manual Pin Validation" section in this guide for -instructions. - +Even when auto-swizzling is enabled with `kTSKSwizzleNetworkDelegates`, there +are specific scenarios where TrustKit cannot intercept outgoing SSL connections +and automatically validate the server's identity against the pinning policy. For these +connections, the pin validation must be manually implemented using the +[`TSKPinningValidator` class](https://datatheorem.github.io/TrustKit/documentation/Classes/TSKPinningValidator.html) +class. #### Deploy a reporting server or use Data Theorem's free server @@ -190,7 +245,9 @@ access). This will give you an idea of how many users would be blocked, if pin validation was to be enforced. -### Debugging with TrustKit +## Appendices + +### Debugging network connections with TrustKit SSL pinning can make it difficult for developers to develop network clients or troubleshoot network requests. Common tools like Charles Proxy use self-signed @@ -201,7 +258,7 @@ man-in-the-middle attack, TrustKit will reject such proxied SSL connections. There are several options available to mitigate this issue. -#### For Small Development Teams +#### For small development teams Create a self-signed SSL certificate authority for Charles to use instead of it's default randomly generated CA. This team CA cert & private key can be shared among @@ -213,7 +270,7 @@ For debugging in production, users will need to trust the self-signed root CA which is potentially dangerous and requires the owner of the device to enter their passcode. -#### For Large or Enterprise Development Teams +#### For large or enterprise development teams If you're working in an enterprise environment where sharing a root CA certificate and private key is impractical, issuing intermediate CA certs and keys might work @@ -239,7 +296,7 @@ that you can pin your untrusted root debugging certificate from Charles, but not specifically add it to the OS trust store on your simulators/devices/computers. To use custom trust anchors, add the certificate strings to a list under the -kTSKAdditionalTrustAnchors configuration key. Each entry should include ony one +`kTSKAdditionalTrustAnchors` configuration key. Each entry should include ony one certificate in PEM format, with no password (it's a public key, right?). Exmaple: ``` @@ -298,9 +355,9 @@ certificate in PEM format, with no password (it's a public key, right?). Exmaple By default TrustKit is hard-coded to ignore the custom trust anchors created using the above processes in a production build. TrustKit does this by checking a define -in TSKPinningValidator: custom anchors are ignored unless #if DEBUG == 1. +in `TSKPinningValidator`: custom anchors are ignored unless `#if DEBUG == 1`. -The idea is that kTSKAdditionalTrustAnchors configuration is primarily intended for +The idea is that `kTSKAdditionalTrustAnchors` configuration is primarily intended for use during development. It simplifies your workflow by not requiring the iOS Simulator or iOS device to manually add a custom OS trust anchor (or added to Keychain for macOS). @@ -315,91 +372,6 @@ debugging. - Add DEBUG=1 to "Preprocessor Macros" in the Xcode target build settings for your distribution configuration (named "Release" by default). This will probably cause issues in your app. -- Subclass TSKPinningValidator and override +allowsAdditionalTrustAnchors to return -true. - -#### Other configuration settings - -The list of all the configuration keys is available in the -[documentation](https://datatheorem.github.io/TrustKit/documentation/Classes/TrustKit.html). - - -## Manual Pin Validation - -Even when auto-swizzling is enabled with `kTSKSwizzleNetworkDelegates`, there -are specific scenarios where TrustKit cannot intercept outgoing SSL connections -and automatically validate the server's identity against the pinning policy. For these -connections, the pin validation must be manually triggered: the server's trust object, -which contains its certificate chain, needs to be retrieved or built before being -passed to the -[`TSKPinningValidator` class](https://datatheorem.github.io/TrustKit/documentation/Classes/TSKPinningValidator.html) -for validation. - - -## Pinning in WebViews - -Adding SSL pinning to connections initiated within a `UIWebView` is difficult as the -class does not provide direct APIs to handle authentication challenges. As -mentionned in [Apple's technical note about HTTPS trust evaluation](https://developer.apple.com/library/ios/technotes/tn2232/_index.html), -customizing certificate validation in a `UIWebView` can still be achieved using -`NSURLProtocol` to intercept all outgoing connections. However, implemeting this -technique is a complex and significant engineering effort. - -Overall, the best approach to implementing SSL pinning in webviews is by -migrating to the `WKWebView` class introduced in iOS 8, which provides -[delegate methods](https://developer.apple.com/library/ios/documentation/WebKit/Reference/WKNavigationDelegate_Ref/) to handle authentication challenges (such as server SSL certificate -validation). However, this approach still requires some testing as it seems like the -`webView:didReceiveAuthenticationChallenge:completionHandler:` delegate method [only works reliably on iOS 9](https://bugs.webkit.org/show_bug.cgi?id=135327). - - -## Pin Validation Notifications - -Whenever TrustKit performs a pinning validation, an `NSNotification` is sent with -information about the server and certificate that were validated. These notifications -can be used for performance measurement or to act upon any pinning validation -performed by TrustKit (for example to customize the reporting mechanism). See -the TrustKit documentation for more information. - - -## Embedding TrustKit Without CocoaPods - -### Adding TrustKit as a Dependency - Static Linking - -If CocoaPods can't be used and for Apps targeting iOS 7, TrustKit can be statically -linked. - -1. Drag and drop the TrustKit Xcode project file in your project: - - ![](https://datatheorem.github.io/TrustKit/images/linking1.png) - -2. Within the "General" tab for your App's target, add _libTrustKit_Static.a_ to -the "Linked Framework and Binaries" section: - - ![](https://datatheorem.github.io/TrustKit/images/linking2_static.png) - -3. Within the "Build Settings", add TrustKit's folder to the "User Header Search -Paths" setting and set "Always Search Header Paths" to "Yes": - - ![](https://datatheorem.github.io/TrustKit/images/linking3_static.png) - -4. Add `-ObjC` to the to the "Other Linker Flags" parameter within the App's Build -Settings. - -5. Lastly, initialize TrustKit with your pinning policy. - - -### Adding TrustKit as a Dependency - Dynamic Linking - -If CocoaPods can't be used and for Apps targeting iOS 8+, macOS, tvOS or -watchOS, TrustKit can be dynamically linked. - -1. Drag and drop the TrustKit Xcode project file in your project: - - ![](https://datatheorem.github.io/TrustKit/images/linking1.png) - -2. Within the "General" tab for your App's target, add TrustKit to the -"Embedded Binaries" section: - - ![](https://datatheorem.github.io/TrustKit/images/linking2_dynamic.png) +- Subclass `TSKPinningValidator` and override `+allowsAdditionalTrustAnchors` to return +`true`. -3. Lastly, initialize TrustKit with your pinning policy. diff --git a/docs/images/linking1.png b/docs/images/linking1.png deleted file mode 100755 index 992b8b25bbb8167331889cf5931ee1a2146634c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54470 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 #8? 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 diff --git a/docs/images/linking3_static.png b/docs/images/linking3_static.png deleted file mode 100755 index 97638a5368265f02923e9bbf20987d49854b688b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71914 zcmc$^V{oR;w=W#qwr$<9ZJQI@wllHKiESqn+qP|IVw`#Yn^o^V`+R(_s;jE8ehbaj z-77*-UIHEl2L=cT2wqB3R2c{e+ye**SQZN6Z;4~=Vjd6>9EPQch@zB;2$73H+3;19Q zhTw8!7hO=7J(`l17ACBwqGuAwn~#0ZbJuHk`*RxWN%nhQJ1Yk(kVQwMXaR^SY=E#~ zJts9wNJc`&5ZNbiI1o7558y{ln+PgSW@d~Jj5K2VmJY4+B@pes3B!R)D_MmW!QHPTJpVF_?XVN*R87M;9ye9CYSBW2o zSSjCy%6=9f@o}1&WGgn%uX;6{BvNO=XPDAqZ=qcES}^c$@xhU=)Jo%;biuS^;rR2z zdtsrpfFF1@Olrm0!Hu+JQs_DqG?J>X#!H()qhv>%#_#0hV5HtbjlK{<~`1E@O&aTra-i1JSOl%U?Cj^6~vEXn+PB}A_(4I@K~A&MbVA|F{s+H zUuYlT5wK(fj>M7b3{-(nj05UNOUQ>b+A?U!ue;cX!kb8ly`Q(59`Hd+=+DExVf*Qs zA`0YTr~~0jgHQ!qbqMULdO(#}P+tpA^oh68vqB!iZBp^b*FjjTWlceoBi?*L}?z))F>`_;HBidBnZWq3T5A4^A7G)}A}lGNXp&rWVbnB0fNc`DEi}cjgFuk1S}>fEx|HLkw+N4&8$z zbBnR*(e2(vW%S)fO`3|suE`gs*Gw}I0FCzCjL(edgzb06FfsTxqUV71mg3=f?%2@5 z0>$!z25f&%ncYD!)S?67S)0oYkxSz;^dJF&TlA+G0?F?7{G`83qP+$4BZ3MEt|tO6 z3Xo-jj^!pq6JAp3lVw6o4uq)#sRGCC!>Gew4CLDdvH+3jqjo^hg}B^$Mk^q z5)!}?L<$L+MdCIJNGDMb2i=eKAO)7>M2R9J&X8p97YPYR%ZQ*8q={D>6*s|Ej{Atj z6Ot#!Oh7nfxW_sZ-zLgagexxSC}XyYi52gf`@sewCq!Am+XO`~hFLH%`^N&l1J_nm zAwO*{s|?OJ;>i$5Wdti7`#9XwIA0SaHT1~H$c9}#R?P^os$T`(eQ}WvchSecFPa_8 z>L|e}P7cxr}N*5{} zWDe8H0pw_>eoRSa7H6hpLIP^7o-E2OwX8#}>YL20)hzMnI;xjS%=4$+au&FAT%zrS5Q`hnU{85Y&Cm2YMD=jDEVg*JV7B-=Y&*KV zxV^5uEWH*UD;{Id#;~vkGLfgKe$biY{n#|}!7=0|@c?rlOhopU(8aye_b>8`=hx`b z_0Ri~`&59C4R#3j2~L5KgFu9L6!9K#3{!!xgrC8AieAJ&<4ELd@Hq@jc#c*i^)4hU z#1`L(slob#orNWV6BW&YmB)U~Y?uy~ahOrX%w;!kE6oAVgq~i*%xCyUsS<}hN%y11 zJlF6k6FWmJLu#reqd5aUeIvazbB1Hb{A)JIXwNpmx_-D|sbFP(k!9Mc#5DMbj}|~{ zPuso3UHPDXQ*B$dP>rd@q1972*Ba46-E3Ls`9r1=zKOn}y1vU$qUoV!qIJV&#V&5$ zd-dU}^0Jou7eX!q$j>PR{6eTgA8}}jrRa_;Nmn>GI!7A^pUXjqNY~7p#luIdrlZrU zIa-N4!q26(f%aa{pAWv6_-XYF4|>|V+imNe4SJQj_HA>UB%8-=fX*&AcZZV>jn19B zU*3bBboUm2UiQz9sGl}3)~-y?Qm&fEE?Qb^+zXzSZ(#Ys@{sbl_$7S#-@M*1-U)6$ zH)iG-ckI)4qL|r@+l<|!&mK9R&0p;w(!iNO8$eqiMnR@Pv;qNv3PE&1oI*UoxsV&+ zC*U^-e(3vnBnVu1_xy_-F6yUaOmuWCRlHU7x|F;CALExos0S#0Q4vx5B8kK3(Ilmi zvh=bPrPjij!nL_Sb6v(=!_zq46k#|?c#X`*QdPcHoGs)CF}S#`+}A?C)qXQ|Qv;KU zQdY@(q+29uCckWO?zas(svYE#x06lDa_7k^WUdZU8Mc&$n$t*Knub5FKjJqAy8x?o zz5Dv0{?PFn_#y2VN2FS(bj!_1V=uG){t@!A{?Qlo7~~sz*niniDvWQ~eLIPkM7>br z5{@ToN}`ouKet|>?-=b-3YH+7%!YZhUBV~Fc{T3x zekM~Qz!*dIX$rv~%vb1IvNq|-N;m_P+ic6u!ej1sffvOEd*U(`ldZ!M$HM5pee;lW zQk(g0C-fGS8afPn!f^RPeP0^J62s!Sb|vN))j~In35(H6@2JyYIJV*>|5^-l5;-0@ zBeN#M_<4E%@RkRecEn(JKkoH2|vX(|~sh!)-^#JEM^B!=sEU)if zKi}mv!9C7>#=Z8o`ezE4j-zVz)|1r#V5%a%!Z$0Loqp}nkJbF4`TW;;mriu6s?BSY zTHT-NmQmXnz%n3ZBVt3QMPMVL<=}&HyJzu+ZBwiML8iJ+T|G_Jy=1$x+b?ad<_O** zxGCTJIb`d~yZ?#p#&xsPmeAN>$gj?z<9o)S(Z9=;=veSV=qA`3Q44?FCEc&>H?n2+ z_Ugu3MUK|J3YtV+4h>TV8r~r!emI0~07SqdNH6By?r!gKUmqjSuhmYVY&zxK_rp-A z=NFx;BNx}`KHXc9SIl5{Yf2A1!n3~z1!xCJEoUGg4Dx?|U@2v?+rMXsTuW6=7fm@? zZex2}2165jBU1(sTZg~YKtQ}6+<%j{rY?p=9=0}i&fFe+B>$q|{+s@%n306&UnDNp zd?cE3ibNvzPNqZv1~vvJ5`GvWA|hTV6EkjQQSpC=|IP7{Sh%=2a5FNxySp>EvohE_ znKLqTad9y+u`sf*(Ep{NclNY%G4!Cfb0+;aCjY@l)YRG7$B+?FTnq&>c2$(H>lQsL0MT@{yXx&)%-h>m+>D9{+mVr)?EK8{o7vrFuaWa z+4lS}Q@g>dKtO^(Qldht9>AA5(7I}3?<4-Bu>nCrk&&?JZHAE%DeF<%mc_}nrZgF5 z<+Uk_nP0YdS4-nfE4t(Q%|k3{Y!B!K8{0c<*0d5c4<)CZx|KLaK(GLIrQjH2&{g!wq3{^Ecle zq5R(=fp0{+s!6iLafAj*ChVnaO_oVhmghWuuPV?MH620I?wing81&~n-qUgaL*M_W zq^*G~xc@y@B%E`?;pLsJj^m5_M+y4_U|5O`p!)_CEi}-ojF+JM%MkvJ4eMDq!QUa zlGUB=WO(iHdjBO?sqWB7VgSl4PN`9=FD5G+ec0*q>cnQhy-6i*EI=>5|7CxB^z=>q z^+K%MF8rt14{N=cam;OL(3o>~uig__-Ybm3|T4{|X>Zre@05>#EsMVW)g$qHwV z%vr(X*<>r#cfOtXgRv2>#;lm*A zDdRH8{x3N5yW!pg=wD_N#m}lI7`v58LsX6=p6o@SGzmWJi1m~(w-=^>`EeRxbI1^n z&iz~@`{>_W6BT!b-DIwgCV^^tCPOUtP=V2fJI3xt47!uY*B}ZC3NKCf61%orbA2NO z?h>#wmagpkEq<^#O6zRmb+7HerhA`t{6%Zl-mLg6vr(Kzh#s$gJo2k&2jy%WDDBR} zla7RNlrd|nEgdnvqwe~d=9b&P>qB)GVt!mY(w6V>4fu?$wsM2~UA`)!Oz;?H>Qc;a z$4{Ji*9`bu|7fIY=y0_hTg!YTCZb$M#SY`YdU9ecq6=K^1UqhJ5x0&git_SoU??q( z(9iVseSadL4_&U>QweKxYvc{*GFOZVFy3k4bqmHB)W%|YxkH_|yDY6VRor7z?!}ZI zAE0#@_fgi8tVU+7zTWKg(q2#xwSjxMn$$6TpL6OeyrWwmGmPAHpxp}*4NrMtkPfU* z5U_tnw!G{|G43yxt0;HaZFLx~HJCcM0={9)l5Qc8+BwM)=J0Hlv+Pg}=ITc%5sNbtsg<-l238c( z?%`v|frt0ze{hEAROYDT4kggfEn!j@;@+jJx9hSx-q*RWbDGtG(Anyu8gz z7fz?V*N4b(K$rPrAbR@rrY6RGY$ph7t#enG;_a(7KtASh40-Z16=T0Tj`h|uzg=)` z2Kq+cBJQX51dhep&hXB?k#MmOa!Cz; z2w=gAYq{|;yXDY8uFuAgp)7Vjog{h~ZK-p%{G!fziOglzFTRDV_PlWyD<&D}xw3-6 zhD!Z%JvL(?xFS;3R}=mQdkOX!>ljLtk8wnIUuUVdpa5j&YCA@qT#LYJ1r{vOmXHk;e&~dS|Bftzh_jE^^ za3qp+4g-@xw@l?alB`rAZ#W1Xmh78CE?OUrDZ;&bwlnMT3l&7%(aZW9(p?; z^)WOyJrZ3(AUyYqPbuC9#eg9ytmM^J(4-Y?P@gkM>3-uU|I(EEA)P&neRnCosQQRVMDNgoi(y z2?jk>TL85bRpNqUZrkBxvsR^e&CNsl=BD6s=PGLH$Ncr~4g8b_9bxsmjTv9h2xM+) zxv!{2bY7tx=fX1eR*O4>U~^uzPe-qfY%n}UnV*ou0_46?Rh5K<1;!SAM?sYaeR^bA zm~|g~S)LFmjvcekMir80aqToh=2MWTpsD(M?M?UBboJQJ3M8DsGR3V7RZ6vfh(Rmb z>^e6V?s;6jwM?b>WZafxQ%GsL0z;I)3(#uqpqgxHH#*~=YB13_s4#b7eu_WJn+`Tz zi-0ck)l9^Zw7VL2um5sqqwA$J<>YsZ^)q z_Zfk2IAn?}TP7-tLN2L3l29iOt3UicoO10Z?oi?}d109Pt09Jls6|3I_Y!%1r<Y$Mb#Na|j99oIr*&ip-13j`o`g zf0Qh#jN@{p;cD|y{${QReM{G(&GorQ5&!tbEYf^n0*nzK)H+O~kBRu3rv$L2f;T6Z z+ocothe3X3$l8btn5)N)|HaS-mgThA9dS!7aEnLfZc_{3nY#ZuAm(vXqm7n4T!}LT z7=;U~ie5gU)CZ;u+xej$seEskgBm{A)ANGoJLOV!*hUUTz^y^kch87ugiP%*CE8dM zlA61)T-;(^HHuAp``V;0&LSYeUR~gopsr+#L~Ly^kn7JMu(!OC!jd>r+R6@mt?_oz zgYZ+p7Xy^SdU-_ti;O^NqKQ!9G2!Kug1i?-AD2-UdCl+rGcEWOc^*@&Lis~cF?wSp zTFAnJN?zat>+9tlw5j3+ID&6r)_RtYU790SKX%_JlUhl%F{D{&<-Rt*Dq3E$+^PVw zs91+9jd9P)kW0)VL?epbLM;@&Cy{JcAXUlNSV;=moE#^#3$9)eCc^1>@9WFCTzXG-JQGNOex zD|F?=X_AM`#lG1FUraFY^f_gv-@FZJ``1U{938=C*__dXm&$Cm=OA&ZS99c4zsCET zXn4TQEi){zo2^fd>F0`dkbXzs44xa6sfq<8@%;WRnnx!ty-!^UKc2Q z(7LPn#0GcA8f|$6pew=VHf2+RyXIHU8k3P4e3kX0i06zmtDa*n5?NZ&Yq~>wgUV2m zq|T;}fy}754hM>YX!*TM=Yl?D0{U?2sf!vDtErv&~hmsF67i7hRO?NRw*)!EtWA-lYH4cX};zmyMPqNWHS|S0$H}mJl>y(xkIE zwN{9&-DGFoZ8y&Q(sm4H^V=VzGEzz0R`^8PxtLuEqJtw6w{Bi)C3pQ9FxOpPtnDbE zPH|$q0hJt)UxJOf*odyr=Yn9bTPTPVH=iWC;=d$zr2PHl-NX|F6OGQ*1@R$+~b*P=38;hyzf!53nK)w~hiqOK|fakB2oHjq9rnE%zu ziPCf4FO^8Hp6pJb2lm(beDZEfE)XuLNW3KH=^dB=@2l2xU6C*#_*8OpBW6I6IJ>?% zii}@om~W<~`qiPl#;X^NHpe!>31~&rP)!aqljV4D5Z3V;_MoY(#GH`;kP&skjw6bP zM*kvat!Hpzpom%tc1bb(E@Td2dOi_5&?xMre2p}(gGw36ak041rVyyan>tjG>gU4A z3$GV%Tz;Hh=|jBk&}vqPW{y@R(f(Eb0E->!{>;!DmTwfD7tq6^RWUAPW-^Y=se60f)ZqZ8{}XTTQtTiejAW8d(-`XH#h9+F`bV@*}WEokl#AcsnwX7 zM9m-PFY{#hhr|f8hiJ{~QX;Y+^JmKnxC!1X4sDtp1$uryEFLE^BQGmnHQM^_{Rf$i z4i{m4q9@S7%K_6vc>+|ti?rH_K>h1Xdb?^gQ4@nJuD*vf70vtJ)Qr9;<%!Yd`?JbR z`h7-&2l@I}J38z#VLS{))SOUICM1c<^;W1jY-k6D_Jsqn79L!Mv9=4+Kb8t9sH0B}%efj#yscNasb}(a0 zLAq*jB}teV?Z7L-Y0-E)B)w?4q%LyPtw>*Ji*`O3)KuJJn3%qNz8fOl@=$aZHOe{@ zakOJAIq}m*=4SgMY0b*|K%91jGuj}g)`K5YmtfVlDpIYf68C1ANt9#~KaBe|uy$np z&@P-0NWGCpds9OT0MMAhE0h^3*H*%zR>p_4_30IFVCFL?3o`s&f@9-KSvC1Ed17A1 zUc6&5+5w)cC9DD@CuBA6=Uo>}wvv!tX-}tvE#%=|i%l_6yW#1KcS{Y-W@Z?r#v`4K zq*m7?N!`fIu7Kt0bO=z?_8|%8VZP{Gr7(66IT%vj=!1PI67u)1x78>Mt@QMFymoG2 zo+egzc^^SS)k5l@LlUE@E(X3P+zs7?3zarchLeOmKv7@{q?kC(SUUXa!APv!HiczA zR;a)_H+hllwNvKTyRqpqBf~4-gDs(p-6vU1Dpq`&t@hv)@GMuT?C3e98-l--3Dd$j z?x7@de8IO?PL$F;-&@W7k5W9t<$;v_*1`Rab!>>&d*Vj<%i?AGEw1`J+1BCuh3d%c>ovRW zOv@!p@CH4fHs0H(q~sf0ptRb90)x^&?sSf{ZrQ>jI^0u}V_A2j4m2Q$R1GF#rw(i` z>EwK>-O^Y=&mF3gA{3XV(-tQUTF44-OO&>mB782g9(HJdl|G%7W{{~j>^Tr;jAJrp z=2CbU<5Gjov?xb$O47GoRH9z|8AZ>E6>^hsQm{NH%{H&WI!z;3K~Fb6E_z49P&Apt zhmV8k=2V%N{|))?<%({Fj-msT@QA|j+#((r8Gip9zJV00fVHlpIipK`d|REOfDF-9 zY1}_P=+$;D4@mQwGGf&TY^^UmbJ21eUV~|=>NP}xw>ua66fs{NcB8jc5fPTC-APGET`lA5@IZ54 zH926FBIR(!WFHWgXx+zBHAO=hFvq&*`Z;|W_CsZy>XYWwOE;H1EWb*B?Cqf4Q$0(H zwGYW2LFcnN3iGtEiqimN zw&}Gh*J0wK!Yj$A9SVf&$Pl&>C?)w~Gbf}SKM`wv($`ys+i``SHC~^j^KdDWlwXc=sE>>4 z3}T>XFBS6B*$xF7H zzRxdt;kL?DuepEOvtCuo8zOIWmiS98mHvJDCR@W>4)K2z!8sm0BH_}YpQ2hWbrQ_G zUDjmk;KSHl*%WN|b~2z5r%CSAE2FyyiM`AQiPrRmP75KzqzpL&$H6NHOFQpxrk2%G z6P}k*d5OGXIQvVR$#pgkjAJd~n*n~*jdE_SargX3`iQ}?=z@ex8<-@yv!B|WvV?$H z_4>_IQO{LmTe|9t8T3M>i4juS>Py2JME}3E{kWv?Xpr*$s!rUF7UlB+9Fq0oa+eb8i7PlGnzEuuiKx8}Pjx z0Q5`?YT-FjFXOjD;Gk1|Lk`h!gnEt00^Rx55VQi3eKyh~;=IGAt-gh$kyuk;e9IIP z_GeBCDYBV|&<9DvnPxUR6~W9-FF1QrR0#z>tfHMe^s4Q(^7hp{@5hrv zwQ?%=&L+SpmX<5Cy7Mw==I%VIcK>QdppRB1%<4mh!}vic=DzIXo@>kQ2DFr}*F0-` zxU#l8MlP?qzbRik1M=yhKoL$Y$)9M= z|KOk&+m=7Mey4^H%DlhUatO|6>5=ZR(WRDO;bv3m(955Pl{!_PE-nXAIQ|S~v=3^`^cJ`yROxU^Wr2 z7`Q3_J<5L3>>qEpv*}yu2^|T^S4)#G1o_(U!oSu!q+U(KH;{iGn7*!ddR2()_v=n> z6>0cUpWKAZ(+$HaXX10}XGE=nCuFkKcRHwZ|<`;O0z=l)1SljCs|am~iAEWWNms6QO#+*Cc*Du}X#*aAfD%(wVN8 zt4~(ECUjLx<(pUYSzKz_(~?m>wdndDUu$ceB8FMVAii-@X8&QW(vX1$xOIZ**J)yB z5b#Z_A&7b|F07&=tY@t}J79k@cD=WspZuXTPPJ~S`$#xPNEh}HEpL`>lwJPI0%_hG>(X=8pNro-R+5upI>6AYyV2Qo1756n#GGT+;K4_+X zDJ=mTXL8qp*xt$rY;E$txt1MC{w6!fy=lTP&7k~x;P7;#JTl(871zA3$gk|x*wLGt zO7`{nVNEs((~qg(K5W3czPGfj4PFdCzqQ`d`=0Z7=7u*h6z+;S>ubqUVnZByv{%V# zTyVQ&7N7NVu4gXVWY41Ix&~Y5C3W11sRVU-bU13F=3psL>aV9CfH?@ZcY27yJ#_I1 zxqh(tkW*)h6?0bsyK-ps)T4}ev$78BUF764*r$bmXNP%p7+@{NS(&A)6K)_p+$XZNgoIw z?q^AyI!iHORA_qFYO*)sVI*nagwLx-O6CO4H}iH5ex7{g#n>qEYUW#gOEKOcC(=43$M`=-#>o>Ggq9;TG4G164d%6@s`u(4MVZ)Xxs-H~)S&4w{ z(UeDWp6^q0Dw59DSQC-bh)PocCe9y!oTO#_&uzdB3rFrUBwE6%^ru|xcxpoh*NLSi^gUL+ca)J zMhU#c6Kl@|=Hv&xqnhzdpiMm^G`}xF#=t^vaha{bw6$JA9>3miCXDF5-d5R$nCyojFZ*P%j z^lR^RCJExWxDQ9*ld5!i&oDSafTCIMMRmBy6{8b-dm@u~-W~~^^l)^^xU2}EcALj6 zCvkPo8Ij4C?np#pJ1Bn}I+Bvw;`Z!2&Wu!vC;b#(dEyX6Ygs|puk@Nvt&SZvO5QH@ z6(+R$vzs6BMx+S{dr70qE}6^AkJ?o`4=md_+kxuO#RqK`iHFDZe$ab~pZHVlI=w^< zPe_5y59Q5+gd*G4!LpNVA=4Y7fFuY@l<;pmd?w2^hN7Y76n>T}z2DG`asKehDp&Zv zWPk4eq@n#T7-<8L^8}jsuQdI{H8ZsqcyfGB(Z2@6Kx9Pp{dR=yEoxoae>oCc4DNE^ zit28YWUqsNIAHZgs=NL=6D6GJ^2xezQ|7QkeP8zfMLU@$b-~0d=`Ip>it7Dj+UI|( z8;KfaYCyjKAVu8q;-km|V$O(ho*ZCC-p&Y7=h1 zybg;%UEkXuXY;XIm|XyVj-epG_&dY@^ty`q($?plGk!S)uoOG)RzANO7Z_2t1^;Za zJ{bi%t)U!43)W@C(U5?H9(>oB*=v75U-joDTyyvAjk{d(^5Pb0QoL)QT3UjZ?;oya z&;#r_@kaI3yd2)CLoLRz<}iK8Gye8aPj19J15Y_UkRQJ+xL8drN1kuhq5X|lk|(-* zd=X=cIzH=L*kWh)fYR7Ggqtwje767d`5n#JQJX8XVyuSAvvVx)L&q58ZsKHtsupIY zf}Hmpxry7D1H*ncS>iJ4ZW$}oX)S6P-r8c*L6X(3tY8r%*r;$|)>{Hho61NxB5s8` znf%7nnQ7GmivFY2ZZp;rhLw+>7;%?cnR6r&KjPt}u?YKhe@v;<5&J4o=zVIGAs|=a zLY;6V2$U_8s$KFbcleyW2a#&zMt$^eT>B0VYTMGA+Q(-8ExJe%&F1e_GTDntnH+rZrbidZ(me1V^fF4^3_as6>GA}E@WHOKA8 zULH)y-vErl!*d5^zm`-|5K=7m1@F$=OP~OkuG=vJp+!4&(d=8_0 zH;<{1Y3m(o#_W0%Ae7br$3g#D$A_q{+Y{yP=t zcy|C)sFTJt^^X^ihAEk2+|h$s0}Tr=M@TQEkX`&t>`qzhVKF&_^F}8|Qug8^d8{!ws@z=qHL5{!F2R%#T*Rvg!MO(Ew)3N#MSM%fv!y9 zZuqL4zYZ*+nBm>3#dhQ-1wrwjTbUk_DJ3$6Sh*w+y=W z+T=o=muiMhCM|=8v7M#UoPqKTTnMG6&&T^j;jDrPd|g-u^u#ISJv^qCTK#rf&pCn8 ztVu775!dsBc))2Ghp;$(m%lL9c+_ouBpm;-EH&WM_fA#>0oRR;&&#p{=mScoMS_P_(e*{Se%a`B- z1qgVa!2JcwjQ$gT>meOUigRPYErt-cd&-ZqoCPGLA;M zvK3&z<|s3JYMynkm<$J%gW@#QHUaX&p@u8AI4WLkpqDegX|4m(nJK@sIq~w?noSwP z?((th55-e&RGm z@Iv}-(_QO+Ozz2AT&n6oGos}Bm{Wb$N?^h2@l^+?fKhJ{CUcf<^DAnlSYS#El~)$# zIPLnO9MiOrVrKY50Bwfe1HNn0wNmZIXhCLx&0BO(!uVm1lBtowYzO;bsri{Iq@~!; zY`7U7SF-!fygGvlqf5B7rnjLim#Ry@B8D^-GW?+A6vd4v=y1wjpk`Kp+2SfGJftaIJ0Tzfx1Fh{9G{eZ^T)7_55?44Us zE<&ftBJ4Q#`c$Be1Hpc3$d;deF5g<=Qs5r51L7_JR~Z(+Np3!M^@Z*Juht7SYrCy? zLyIgkjG^S@mjDVl$r{tRJrn0=2C^LPzy-F8=a>2}&SEvY+&4aqef#k&LL&>x1;%F# z)-s0PW_+gU@MfaB`hLtS5DUYhlhZ zI^j@=ZU?I$j|g>m`22_sXuKx(9f-}VOHAFu;dTEFGRwi+OSHFCL2)!!qN38-#1dZ1 zOHF5E?nf$UN=6$?T3Aw$a$Oj$6x1)=1Y3t@fyMEDEB%5vOH(R}lpXw3%+$D#+X^Gmt~z^yOBPZ zX=Q3WHSm=#FKw_UCLbfDsR-q@5SE?R0tcb>Sa(Xd)1gOS(ryUM{&;{OVwO0y<;eMQ z@O`_`op<}$YO*X^`5qgm2X@!`CTX`tC(~&dfMYo9B$2HDIJFd)+?#*MSW~?sw`w+p z-Y)c_R-kyQ4rhjB1uI`NG8}t9;xEufHwCtprz#7hzx60T(ab7v*ofCtfs~WjP$9V# zsy3tBGL?+{QZty+3Gngd2=TRdyPmfj>4UTQT68#Mb1@^~`&(=;U2OQcbJ3W^6(~*d z@1G9x?v-6adp>{#*o-ii<3E*DG-Sh0)Eo+a<|$O8-yx^BJHdygd-7wPn2Lxodh)BU zO{f{Z^J=xrWWkVElXWJ+UQ@T48@M2L` z6;2vG^ru6}8yE3tEAX{ED55;QnNCgVr2x4Y99Z_Fd)qKw$D8X+5$GSQ3Vq3XSjH)9 zNJ8%MgeEBj;M^c~^GQ9UJ3ohVmqWldX9VDouZQLS=nG@RMho5KC%LI(d)PXT<^o18 zQ~oJ(U@i!Ix-s9h2J_kK;AiW5w@u}I&;?^+K)PW2h_Gj@o=_oWaP#eoN9&Nu zxxKX(ZtMx|*J-9pQz2uAja}kU^yXz)^@1zxXfcy;=)6+rqrl*YgrQthemepXP!7N? zJ#p57R$cqHSjsdtx2vieel-}>sMF?L9}qtn6&+kdoNZ0B-f6NhqF9I4aJccsnkw|;cNl5C^Z&^DAU-yL zbZx&E`U6&R&egx%VQRj<9a^57)oq|vg9LINTl738HjmqH-=ha6s}8bSIlW0((FO;A zY50(87vIP6BgwD(q-9Nm0C>J!J#wu>6QN$cz4hle;7RmMiME?Vq9xuxABIkFI_{xN z`AiiZ$h;&?GLn^;5bxo=&Nw>k~$Nsj~qN1Y`9NB=}t297yr1*r1&70{ROLkF4 zm@sImJ~_TD!UONvW4COF&4}H4!bhpi8t3}{G?LBZ$DiSCmuq-V@jNA+%zmwMsnHW7 zCwF?V3$5ErVyey)7pGZLunGBb3HQJOQ>+CeJ|I{KHC$RGx{^6K@i%0rp67kS&X{pg z?-jDZ8FC8fx9MsjH^OQ&+79`61xnI>`ZLpBe6YR#t0pRk1K9S*;gB}9d+4vjLlfN& z7wGS2|L>m{FrD>IfgyQNsE&z=i6%GVj~5|*3mE6;tb8^QPv)9lTt(Q?tdLptG8dtp z1hI)GWRnJwpPt-~9l?dPgaLmeY<#v(7Vh6Nx&Hc(QWLL3n6n)GoAxwCFH{7-Vv0XL zeedRQS*U6^Ha8>Yt^C2U_#4un++qoVX$DB* zQ@Ww9!Y6VJdy)+q9oGFX-&2CUkgWn$U7CxOEd+q85Ne*C1a?rg;w!XXZSr?L0OV{c z8phVxj&rk{Zs6Bd^MQKKzG94vG2cbA^@~DD)vso%mT@43?~Hu-o`YX6+suIeWM^$86>CpB$dvLoBbSw;hQ8oNYN@oxqFR;6Ra^&qZi-XinHIEbedw}X*1#B&eWr|Sl7r|*kOnnH1`0x#gIl{>*g~}yifBcCm?S_wzpDRZz1B!+U20P^Q~z`a)tT_ zs>9rix>OcEJ*9V3v!i}afAJW&@bDqIK^8yHQiO*=dvY0j#*&hD)`;m)>8Y`}PE&Cl zC&YpK-=Lw;xc*GUWlI^HxW*=}z^NNh_VHR$y4HBjYYhm@jY>erPb(kQaYQ<|j=$R* z(=XeVqzVZ{x2Xz4DC_;pu$lzMf{`8bLRU#DFL=w?5!a=GEAiV z+o;p0Te>sG5I+$T^Zd#e-w?kuPLD&_myN-+yV28;gj6Slz-kMrdE4Mo0UY9F{+E2L z^}4BfT_93*HjG3_P4h3_(CloyL%o&B46B_J0x;=0|B)bb{^7eO-dR6{$GP8FmLokQ znSV-)W_y|Wir4i0nnH9AjOyPW^5PTRbM{jUe^+7s-TuP$daQ*z-4PdORLe;G!->4GcrmBv__182r3^it z!}D`R-y7bF1(Srwk=OsrC!A^Tu+7QC_|^5C2}VNQ+TdEM8qj8om1@HiZIg1$T^4l) z`?i;pzMPd>YVE2t?0WNO*Tf=MuO91?2Yx4SG=NE@sHFCWvfChHtf@)5Z5Dh$mYth5 z)%0$&8k2%?_7FwDovEbyg8*9@dx_2X7EE1SVVNe6l!=9|w*bpGz;9Xm^3n8#pG%?R zYpB8oZ@USpFYY|rKmVme4PJvWGp>qacgdZX-KrZ=5vRU=c;JQY8tlFdID(%_FV0qG zcgr?tTKqn;dB)-jarp~J9wBD1^gXHg3C7m@ii({tNf&Myak+(jOV9>1_3xYC5pBu+ zTvhzxfF>tJx^#`}>x!(wsOW6i{~_$1!z1mM_2G#on%JJ1Xkyz=CY;!|Z6_0BV%zMP z9ox2TPpq%!eb3ovpY!eC-hcIVbwAhBt7;WjRo!bS#B)o5cGfrxCXv-1txG`% zPv@rn3nVs|yX)XZCiJ$CFsMGjQ&eg_&~UdC} zs77=-qXWh6R{3GT(P!a^oruV)!*{A<5Kh))6)XS8rF(=S!_A=iYp7(v;3c@!6{1#o~nD z274XInd7)$T9`%fn(@(TC*~gZMp^QWv36n`5ar{KyNx*NA8&v6TS5(Vpo#rB@b^dU zy^thVK)LI&6}f%>Gcw%nq_F8d3+VTmhQB8kIgzV$A$0R`l%;0 zCgopD>BFSQyOk_Kbwbjp2mV!OJrLb(^_M+5{Kbj!4k5bnb+G~fH)3S9MYsGTWny$l zF2iRYpE?76CFig6)EWR9I}+;)@jO_K4QR^*ogz;1K!ay4)>o*d!O^KTvE@`dn zF`k?sJoC!*)+*8AR1MU3%5;>;%8I@RV~9bRdhTBCDsKkF$(ygXtKdK$#$@N|Bfk^| zVtS8e)jL7Fim^2dQDHuAFCV0#jfz)-p=O=l1*3+Co%Il(D zT5J2G-4}@J&A3JssV9rJEoSR^uI;Dp zTA{0M%C4Ia+K-I*SurI+rI;qF+ncU3fZ}zaD=X`X0|V!@0}~uQ2LWgTDg|+{f4Sbm z?sSoXS{sN>*YQ$=E{h)ymSsD^ee+3pwg31@W+ljrG>$=adF?Zp0}+95GbSZfPzKoz z+Dnk`ND{*#8HI>hUcH=~TsgX+WO*iD?(eEc*Fi}QWTuX^d?^h)X}k)Nw6GA(YM6N% zz_q~r7ISU{*02esLsc_loO~)Jw@Ve@D(W@E%&0ua3~E)h6(o?}dkt#0*wg_*;tO{8 zkWhW90F!@VC6|8+rvfxfb5OtF4uhL5JsFpF-bQ`$#l=>%2sG~GYB?09h5x;El-$% zgFvb{Y{Her=r;Gr{;-fE=kNMW`FR#Q&4c)QWyA6#<|3b4-*xQHbD0+a+7_V_y+%#d(TZUDg=K& zl$5JaaSsl7CG_z%#rSYx94}{{dX!#zm{&NvT-b*^f)H1kCJrNb9h4u@#t^l=i%c^` z2T{OU9Hq$kG_XrreLvze(d zbTpIwgc~h^m;f8RVhShps4Ahh-q2d*e1wSndq#J9vD4}xR`W@rs&O!4%-{5|ppzb#3&e4=^ zKjlb6q2yCDSK(1Siu;%Cki=#jcX5;bmCPTSMaVA>8|K1?PtY_KHpY}=s)<&wi%3i^ zMFu9-@kV&Uw9}-gLcVZzU;z%yG_kpxQUN!liK#Uh zS{DJ57B9?U$L2t+j0UObjjd>+{-+}|AS=Mg5)Ip;aXb=J`(lgu7XJTA&j{stZ~{{^TIk!}SQpat-UXwt3OR!-cIc7tAU`V zEi@8!BzH^^Q#|Kx&)yTMo~6IF_eK4Y>ldTKJIDI>A2rjapq>GFxJOMH+UJpjhPUBs zbE<)P&%Fl`5s?Sy=K<55SeSdVI&S7#p0Am%c+zbcvS1uF7#)J^3u$~p!gyGk1xrXW zTHR9=`9&BMR?+fOX~hZMP*Hm#-(0SO8q>CPXZZEqaF%Rn_{}IN(zP@z-m5da} zK38YR7E`B=ZQ}pXK8CTep^DFCQ+Pa!=ux-BFdR@zHeh$X%od1kJ#T@B$D)nrrN@S3 zzDBEdlYiQiLdTRB;o+WaQPOx=s^Urwo5q@<9}P_emaV;P9Vm_A{g8HzJw2r!{%Kfd zYVPmj8{IYD!Wx=Sy5}ZRCjmc}Kv(G3&|X;w$+yQioZeuP ziYiZ{YTR_HotX1M>Be5~mPK)ECb*3u#dt%wQoJ)NrOh-HQtJTtwTUD23>vLR8taw4C z^}tLm!@|zB0vT2ug{0~08*r9-DCpsCoTsHi7L&Zf1FQae`Q?`A_%_l#+gu(jbry%8 zMe@;B0-j)VDGl5~3LGh}*$j}B>s;dFm zT;1VPFzQ#sMlv!=-rZ6%%CwQerqsJz=VjPg$_dA0u(j@*#;WW3N`&@GBJ!>Gw)g%> zkwc}H_pI$mi_{xcuAa&Rkk)HNgPpiTeFbI zD+~NqQ8*xt*OuQd#V}$?pex85!+HU)Yo2ArW5eY)e-6+2CV%Jj3v+t5v^aec6Z|(S z-lX4~rG${3g(E_J8LX+3Z&p?z7`C?0jnU=x)fA*FPCw>rVF2r(=XXl7f&_a;DRYV^ zgxf}tR0t7ud>9gaMWA6evBm<=rI}%4a%STIW19|9=b9}uQ%V=FUhH3J2XM%o%fRG! zq_WGF){0`N_w|OS!Zf(x4ltHK3L5N#pLzbGZ-81|y*1)(xTwir#aKc-F<)Qz$73*8 z5R$p8L>yrv{d^whwEn*8i99K1)6asN6uN~!m-1Hb{P#8-``Ze8AU9z=Cup6FzIG9g z$;JMt{So%#j?KKV;s?X2b15yAu;`P>!*9d^g$e1vG>tQ&0D;Dzo5HQM51?5k#<@w{ zBA>PBOAn%4Ejf&ns#9eMOh+mdTPLT!G@JI&MofYWMvgXa}8&m}d_L8MveUi{S6MRXSxhAU)cbAZW4Ri~d zo|b2ys#!EuQI+`d(z!8kyt}|6mN@Y@PzOSWFD`{x^ygbnt0xB!Y#NB&i&})&F#E@p zKPVS$On#C_l=#i6{-YK~gOr6hZ8A}ZSlf|bGST?UztK%2G7R^4;9NG`>Lb4NlE6wF?r<FoVaC7s`2T+Rmfsfxc>5ux_FpdRf6!DwgIpj&pp+7a{tdti2xOxe z8oYe70(vkV>OT-d`-c$P;u@*{twjIxkAH6!KMyhpWrc@-pZXsN9{(C&!$|-{h?78q z(SN|-IQIKM<$40fXwvl`2!a1Y2z)7Z+<(|PB*^qAd=7b}CENe7Cm<9N8*uJ`g>_*y z!vDaWOc-{>*s>xZJKn$N&IhzAVzA6E3$bMCu>Y{ktUm&lRmVa1@89T4@CTSBie3%< zKP-_K^jssanS=KKTeklCg$sm|10c|=`S$Psm*49TnvU1>KJY*6vop!2p_tmgAXk_F zGZhv5($Z3s_bcajNy!}LGKB8Oz<**(BO@3&IeAl4Q|HVK8jVInU`I#C7&|`Y&{BFB z+W+4&lzy?jJ5@tRNAFx%z^bgQM5d!3A0MB~-~MO(pjA%D;o;$Yq|Ki{e-2JeB%!x; zb#<|VfoPU)VrTrnJe)^H9$wqt)~~bKAUr=mkJVr3>H^!2Civ&)e1m+GJ*eJ#Damto zr6nZ;($er+9FO!_zBY7Ouuw(~{vWaRWq}M-`^ey+^3A0U2+Ut_;Tf=8vLpWE zt+tXV$V44|>=Mal$U9^V-tr}igF{2rsSs4I6{3A8|JOuR1^K#uxw^WFF+S(y48-{RY_^WWL=eytryK@Xr*2 zoGODiDj9C2X0yK2S3*0r$kHv5QF*29zD4;L`<&b@jCH}#pZ-s-qNJ}0TWJb=u58m# zZ`y201daWJYu$Ug7r3aVFlS9cN;`)CEvY2n1qB7uj6&6`zy3GpZy^JRK7u^%4uu6l zbW=I$T<3NuPJX0xvxO&Y;I{hgkJi?w|xVT_$GxcF`Cn~$O{*5_WMpPqI zmgnn~=fCtUv8(*))eD%voc&FwNZ>Q;t;F zzv4;qG@mgiQVrey3zy*6|2-QB#FvnenAR~iCWC{6%P)0zch7Y?nTJ6{Onk#9AUMGQ z0~uHhmscY)R=XCZ)iK3|v=ZvZ;aeN(+uep4)k>0ufWMXUR6099ACg=)GyYEUkJ18x zz{yH2F2{>?t(qH)ix<^@Hj@#sAv{UFt|YXyh;D9f{avv->gu@O&u4584-dM7zrGwU z)ta=OiOKoEX**%w`MgtArhJ}sW#{tf%u8YOe1$+><@ivExjk3lBGGU6av{ga?f(%p zXz<=L8fL`ghl|owWQ7L#%qjO?ENpB|idd+!Z68Tu_mxaHlYc(z*>3ZNrHQ?wS(E>d9Q%@j>x+*1yg%!luC*?l(oPc+5@IvxLlU|l zeral14{3y!B(-^XE9vkKAVPyj{@n;|=aQ`gQ7VTL<%JfG232r*#{CKD7Ri*~2sA13 zm!In#`<`7_hrZHiBc`vPt0aOBw+U!}g7bNMBo~b)M8LwrDx01olYgafm8`kiDpzB- zR5BaTKcR!4Tv;d`bPkWNGodhUbHm=EvoeALK$S8P+Vzzab~`$dD$`K)w=U2K2STt8 zH05*{aP-sC^j)-=CcZeKJah*7LcDQI$74)m&-BZ=>N&C>xr9W%oYoln3cc+YlgK76f5)5$yWw z_;r-`xFC*M`7aVW$+yN&q8gOP(~uiH7h#p>Uw@Fs(#7vMijoHYOd1D^I$WuSZMh>e zGHtXU)zJ=%ENFlM1UD_8Tv%EtFLT9-y{_e0Qt3&b7-`V5ku=nXd)nM}kA-dE(%bU@ zHF51}%1bB)^!^~kG4q0B(P{VM*#P}&Y8Zt@M7BWF6v5R5t)NNo%?qFR*8I)arxQT6 z?Uo>g^v(Vl9?>s6IbtkX>B6%$OTxZ2-)UT9ilnt#u*9`0GSB}CqS;b?=|?^rpKG@` zmh_9DnEi~XHW-*MX%51>-siG>DRVuzT9Mk)l45iX3KpspWvnKcTH!1ERL?^8)oln_d4QYq0%{~ zNeP32RSJql3nDGzgJ~?)*B63n-7u!Rm!k3?$(G_=W&Oc2d*WPHujLyWO*yu6|Lv=o zal!R>VvR-;1s2yLLqod=Tvqj}t@WFCn(TH+xLi&a00Yk#9nmT}UUT}Mi2r2Cd}$y9 z$FvM;KUu9bB=8D?usc#P=JLdl()oM{K|Z|2_KVQq@myKb_{2Iin{ZdSJUnH4p&vHh zE1mr1n?k9Hz>nsoC+&$b{-**<^ce4vp&^2E?XfA!Nuor>u17VT!RAzG3{qSBdwyxHK>DS*7dS$^sg`~7XnR81FYqh1o|6HB^&i!hJgE(gArvpz8 z#L)~YibLN*_!naNCACm;iBcb#q2 zAS!^$Q;#lluTPG|?8Fk5(;gKu3qPoAcHYBl#rbt|8`kTmK%u`dyj-a_IS< zHm@lLvab9%rIGFDza82vELgQ&X!6l@Cj^orXnxa}*Ym;lXzye}$EOb@I-ia2=R2tU zDFN8FH~vYjQ?=o6d9?BZq);AR?Cf;J7;4Qbwvo=7PIsn3m=)m2hblkUH0t3>h9m}o zWa}mAQ@L97{@_wOSS5S}@Vt5OoJKy^aJtK^CT!&Z`o%78?p zz4xEdnB)CiTt*6r_P!4Y1Ji(w0injDMo9Ss!hzxBfhy7DIA(h-v9`cH03X60i( zhyjT-5u%S7*F4{vP>f*Y&Wg%*wVMUH_x0Ad@>O1>`}<;(fG+#H+uM_!kdFH7(aJ;v zf)p@Q?_5Fo%k`x!Q0;;h5CQq02gt75pnR$ z)txlGS4L@jn8XNl6l5xM?$|JpV-|&2+>LMO zlIO(oG?h%=!+yq*>r2L(DQT>}e&SW*h1ZH?PB&>+3oA1A384~|P+xuJp6__4_IFfQ z!C;MQ6{->8@>sD3!9nXJU#|;LD21EMYgnl?ZUVBZ%*Wri+A>6cS~9?$xB!2?d5Z$# zO5;>NFEhtYaCfUUP@>t!R3p&|m{>p7EpanUzJ$kUPu?->x!0n;-U% zd#8|zVLu&>TT-TZJse%{L;P2KZVL%&*UE4E5D$g5pBHGaateF78z(DR{uy;xbL;8u zj0)7~iH{FTKg{dZQxs!K4V}-H=Q=mE$#D^4+w#?@21MAd>7{bUyM6dp^KaqBE~19j z)NoWWSmZ&l2w{3X-8lpH*tMdxy*i{dgP+QQ3MHVgFKxX}nYcE(LKYf(bff99rni6H zFfOm2-$&;nfM(Qtf(7Wz$&I7h(fIO@857|ri@l3JIr`K0Igts_P)v^FD-oysZm~Yg zVCV@^$t!(8D3w~UmN}$sioL#b$h)d*{@V6z1lvMO*$>f*fKmSH&apFrrFrFQBL>-E z`|FhGx!%;j^|^I3PG#+KqCs>zl~dOiG?0eq*!j-09prNEAn1FD8I{jX#)OH2-AAOtAg*|dzcrY3>4EKe|amXIwxKdRHK}Roc?qf>f_RL z5uH|OCVTw^&GnU={|B$gQlXr-{8C*bYKe?Xv+jl>d0T~zmOOO1>oCHF7-ycWhdix< z^Z8g>wycc_kZ%CN2AApAG)q?lwlqQO6PLJ|r-mE>bnE9w4y<4GsLtQPL1-hM^25ix z?J*fnudjEX);*uJ%Q@po3v3a91P1kHD(7zyLDAe#zkW5z%Afn6pT`9qT`c)nWksXF z?O8W%zxfy+q&3}7MQ0BQsL}WAM6e6(SufbS2OZY12e`a3fUmqWx>91pZ8H5WD8&lk z3uaKks?*;RS;Mf%HPVxR5WmT!v+Mn$W@W_$MSPr~_FIIPzM&F@B4h%B$PFL1R+(Xv zb|WMfMg^9fOlxC(42-X0ClyFCecb{e#DOt;1k73r=JVk$Bov%lLdvB8Z`n#8#2C=( zF3-AtDQI#$5E9}ypyPx+)X*&ZL^sP8ttvrkI3?HJ@MPGtJ>0E0J#G!lNO`^~1Y8R^ zeVSNf5CyDSp_CWTU;x=}1gOJQ66rC*3FUjqj~c0tg=;Y{pOTl@OR`>sjg$@U`GWDj zxoALcAmXeg`g9m6>sfm&nXU)8K7LWb^TvgJ$@)8$!Tk!B6T#xF-+x||0_8*N6F-IxP+~DPIuH8&5R3GO>SDDXIj%BfXpO8iz zGr+vhj*ojC)ixqH+t58)5Gk7%S@ASGa-oi&=GUE6-$;Qsyh$o!X^K|ft!ZYs`vwC%Y3?=)HI>HU+GBOh4m9>dtM(GJ z@qwMB=xmT8zra2|c1)OH8tZoI%?M~`i4kY*d5#~X9|Q+6U2tSM-0AQ6FR6asTdg}p zko0xC4(3aOQB?e0$iCr58`7@!T8gfJ3)pA)0npv~QGf1_(gqj-#N03_>4hG`E*Jd{d9U8FWFm-c=A2>0ax3=O=&7Ke5MT>o(4Ncg9I4NnPjA488 z$$6j`@#n*Dcpkxx1>%=ie4zPp#)?HTbP!9E|B{0P^#Gx|#pF2r;bxG@nI|DU!a|p< zyAR*}MXo~-?P!1^lDB<@bU@fEVoI*J`h<>y*O-yTP83#a%jU^Xc`R6S3#ZhKU`Vr< z6WrDzdW?Ov2)?Wi*6g-Vgz3hQrT%8!?$p9~qi#(xLm8GyUJWxBOttX3MIh zvl3e@4uEL^*h>2!D-So8U46nblqJ~^78wtqs2S$o3Lg2s=w;oKuzu&WvY2V_8N@s`pR11GN3g7@$n-!@AGAmsOUG%A<{S zMVbQXFzphS4IxhKz3Q9uU24k*yWmE>+hZ$s{6-Wy(Uk6nmBei%hd?$K%)AW-FS3~R z77J&h-eZ7@!AjTKgQ0o+RqO#@9~G$SlRn`aBr=cL$965W@VV%9^8?ePR$NhOk#grY zK=s)r;wbjUQnKrYJ4wn2c}3|Wj;uvn{9KnrR(aQ!Rargfnf48=6lw1Z`TEqdC8hLu zm+B~+)9*-cuWIAV`qG&#c53g#bC;Pj)5l5xSF*}dT2sv32v{CF)@x+l4@bXJsFtuc;{xmP=+PQ|swvCi+;hGnn) z;>UnzgJk-02ySXK9%NbrN1Prgt{7L!8Q0$0_JS{4dpk<;>v;^KGG6dIdAa$}wrJ^m zfUk%JbkFS?hMbECHyxgsTgnDJA4S)LRmju8F{m29%%R6q@-(Kp3Ez^PeXCFoP`osN ztw~9uyTcAPHpR*j%6B98Q4ZxF5luk)EECT7(M92Yf4eeLr96oCTQACH)ZEY zr-#;0T7K{!BVLzvnWC;tI(E0l+U%n&2qK7dZ;R~mGkmzz2t7Fm9?p5Z=gA^Y)~Ds&E)v(S_7mL-H;+&)U)B~qjuQp$Q>V*&WedHQ zqgII@%-Sgcu#G+3o8y@YS-3MJNtS%lNjcb~0Z+%ksn>Qkh6{$B=gZ*&jUP2Wi3RM+ zRGF98-=3(Z**!_ty$rZ>bMq!i?ju-vtCaLB|5BDt7F2q^Vbv+$vPW(Q-qJ<${$oD~ zXb>`4$!CCjg#E>OTQY|OnPLlhmZ!XXWGZ;#LUHD@5%5K~y);R7E%(Z@4V>Y;!iQLW zc(&OAjiRyIuiom4+Te(!xOtxmC#;KmzPys~iSHW4xDV1|ppiy1+QIpl;i@*$^}R&z zaIuo~W%OWKLd9OC?i0opjJo>xRnJC$>U&htreV+##AGu~vJo6mv;!@?=&7;RPzj*r z<4Gbrh21lMQtb|?4lF@+Wus+qeQFQ+3ZZtwX&(P+GCPr^H*1idEbjAlB49sxKHzF7 zj13t@?zf@oi1yCqSou}qjxrym9BWx^{an~H#zg1}diDpXZ*mHdiXR&5M4;T1WVLTa zYKn@A<{j8bNui9OT$Hqp_SBZ^@z($j%z6ohCzt_4jck6mMTe`a;p_=7RLYfX-c4DI zKd1Zvp}fh*`81SiN_@KS7h7d?DdNj!!yHfA!r0;Z4(huKSq2vybV7Y?VG~e>aaA8} zB2M%^eXxG)WA*nU8}ilS;B-tdStqhaIOR|dpgJ-Xxu*)_Q7Sh)uf)dA2hUKs+*3E$ ztaTN&DQDVT7h6NNjiY)Rwz)Jufn@C3GLe|)nsxsoBk9{wuz+z|?cFjSKZ=h%PUD?w zX)<^*Y|r&Pr`mBcF~Z)+XxYot*rNNmp6grFw!6#=3w*nejupF4r*%8;VPwdj4{%HB z+J`5(Sqh)WyNuRYo018NGS!Fk=UWmfYMV%Z7V1A>A+LLLtNGAj)SQ!aRLyfDRBy-g zteCs^s*;rv!PD{f6Q2blyrHRUgCnV^v}iuBHE0QB)M|C=5#0Q z6;@K>l~SI~vqVIe*vey%Rf+vj!U-)ZtLsI_ny$FKW2iY|&eYPDCN>P7cj6$96XkAJ*cKs&r z4zcw0;p`r9$F`5o8xkL_nhl$(L0pat`+C|t>Qi)<@oJj`Y2nBS&@tTo6WaR*h-7)vD{lOC#=q{axQGMNdA-nZ$RA=-H3nok3t>2NR?u1ICC zTrR)K7`9D&C3@ z{O{~{BY@nI8z!59N*6{ead6eL{zGkw6S}nK{6^v3u~>xF+}7qxW-fh15QLFkzbPbr zNl$)VPJkVL?x?-OYiB8p3KfMOSuvk_t|I9tHrPvtqb1N^?~&}dA7Gxz@=v%aGT8HU zFkYj7D+qP<)Fg_(Rq0Ak<@?)hA49#ozM>!_XGi9hk)z2KMjx(y>GKLa!aLeW6H4`RWl2BQ< zI*7)*j&Gm8OW~_rL*ig9)G@J_S13j6l*MaTOsceq+N_w=1HKy7a<(16mNOnuf|Lpj zj=jP+I$AbVVt(|elI;7c(}GrFX6vz}`7Knf+jhM|kxWY5@{6LI^1`;Mc!4<}%cSTB zC=m1s)^oitaKh%0#--(I9K>$EH#4{@ywLF_yifJ7r>u~2WNmlAUT$c|=>juhxQki% z9?wwq%#P#Wf_-feu3>|VOd*|e6ne{Q7sb>)Z$|)*+=4uzrg}&xWnuW96y*sgvG_)MOnb?C2 zYkS?Ob1qb6qXpW<&O{+$Q366|X;55<(yF6-ziqDVHDwAKX!RJ$h z++Ar~!b{j>t37wU_zLTc-O3$u22R+T8{X0Mk~ki)hc)UJKTz%h&LU0U zt8FT8gjSu2RvOi2Z4qW3>&awh8>mu*ZIB9B7oEh6_^C!Quzk7e8iW$nr>ub8U1qw+ zg66h?vQcuyo{90c+{xlY+4eJxe4a^QO0`8-vsUetNjgPmg}lUCt_{4Hc&mo3!_17U z>-DD5>3Px zB@&L=rNZf5x>*T#T}qdLHRwrq=MSWuI94xLho5r_y*@bE*9P+GvFe+o2DDBk-_KAT z&P-3YNrEdp*IzkTbc~FTNew(wLwMpo5d$aa&u#~6ZN757(w%DZAjwK;>S9xs>x8=B(8o`6oVj;@x&MSL zSMKG0UISkkanG-k#fgn^cKp;c z_tpk0e$VVlyVzyn7d#(=W z`KQ*ZDth*70deWvLU15R=!De3w3U8?F5ghHLEsK!N!&9JatLJ=`dAFUYna~CEcsZB zHHobSy;js#2>hZWo+a78U+~^Pixin<+5YfF8yuK1>vF$5Ek~yMeE3tlkmiJlgPpuK z=E(D9Q$^i&j?=Ba0(z$!hbX6`IyhuZNim}~pJx**8&SLa-UcKCZS)pnpFWwJI87I5 zEo1l16WA#kyQ%e4Ph7kU*&iy^nAppp<}=w+gy$Ji?Kz$o{uL~!lV-PnT5t1cl+F+m z6r|Vhg{e5GAGA{>^Wqx{O*eB zM~S8eeAUqJY*M*8A18GcKqT*dC*`^SDmiLtA$8_Ae4^-kJQAZp<2rtCuF z2iBU@_OSwDah#spB>>7b4e|sz2RDR~@Wc@|D%3xt02%%cGyCMM5~fWlc=GJAAivgu z(j|fRmkZPeDaJMh&PUx&dbU7s#h|iJc?`gM2HzI1EVFNc5mXNf{W>9XJH*f<4}%h< zbaQO07|T6iNpL@|xk_S>sA|O`?ci~C8g{?l&h*343UAvDfLD>;eDj(}6;kP)#Hd)2 z{P5z!Wx(aS(ACIbZOdiSsZ|y+ut3!8A8IdS3QgfqZC4bjQX1f`o+8)WRoE{UOZ*2CX$>w*QEL&jw!mbY`)4$1gLFY$UYS_c3dGEe@;h zb&X6cuVT!p&F+N1x!XM@#9Oz8Z|Jh{s7=Q!YoSz%KdaSpeCiq~sj90p@2;E56C0V~WCe}Y@c_(`u&Mn9;(HZ` zTJogtxH=wJjWw)D=+XkGm=sAEKUN2C@JlL$xr3e2pOzr~YA+yOkLfwMC%5n$Jr^P^ zZj}{n%Xt6Nz_SkDR<56OJlnK%uR9uC+JzMm8JqQ38%?od{{91?$CXLrJ!zz&nrBaz2Y%*zQ;<46M|?s#*684 ztqVKi#MJwIOtmlYg0Gfyq6zR~)H?4z=k4JNI!*vZiN(IYaQz^_yQNPv+5Rn{)`#8% zDwVp-R>~#|?eP)zN6Nu6_v^Q-0U}cWR!YF}r%T+{*Az*INbkybD>_61iQXKcU}`{b zuh3%cl?C^l1eq{X0spqO8#}yo$*_MS165-k$LUlyGu1|2X~~w9ma}-Tr_dO64i@`-NX|(glBj)Nt$q9{9V+Hul|NWnuY9a@ z&2M(2^HEmb)G52JSNr9J;Hb)}?TvM2^bKtIg2-hdXln5L@PMSrSSZ}plC=T9)1=lp zw%NMX+tEQ-BQ*BKMPU}g@?!12(A;%x4&uhWqJZhj-q03fR|;kSPw-K8Gmmo022}5) z&Zb_gSHZPQA})h{UXED;6hEg;svVTHB%aoYgX|o<_Iic=gA2PxPZ*{w2ZJ|YVBiMQ zm#Jf~JaQXzWt8c!6mk?|vgt7Cr}_Oy!iV1(;l;jfrd9s=;^!>u$Q z9jg%9p2Svjpn{d;=D54|L%uYcx~aXRP1~SHsHjF)Yiai-_SOz2u;~Zu`g|*JN=v;`B#0 z#>ujS%;b17O_~>@idm7x{JA1N9JIl_dOFiPv22i;lqoKsb$0py%aO`(M z9HwI!KwtZG?hrmNS-;)nWg-`Z{ZgF~cQ=lKq% zIZmIRv{oOtzsDhEca%4li>*HY8Uk}Q`Hk(_Mx~af03IY6qVYz1?(Wq_T47YMYE+v( zgz>kQ^nshx`fm4#?CtMwgW74b=J%@*&r?$rf4P7_>{T1BGu)E)iiXR$Z{SsB9Bunv zoQea6sA8mWKPQb90Xz|i!{3b4?w8*p+#%>9J~5o#h+Ljs4`~{UFOV%Y_uzv!@Q^Bg z{V+3d=lfjRhKkgDHDR$o~q_?Kaz zATP9H?i-9jSYN{1_rzj3ii=b7uMSa7$MryU(P&K(H$~aSR>6OidRw0NTBCCdvAXaB z)i@bmp=4J(Hhg&)bJS}0D??(}OUxnt9bnyDccXJRjYYza207H!ejc)SqIoQjTnul4 zrwI2z#Sjz(H@%D(=ip&dj{yolJA8TT!1!2>Ul2EN#WQu347O4bTzOrqspDSUVLj8@ zbeIl9aRXq(soI*L8k}j~oJX`i{FbG~Xn4)Z2Z#l;5Tj1#k@n7f%y1{y#^$OS-45by zM5iKxP10^9(tRoa!e~+t6q1b#oi9w_ra3O4APwf3^zT{AM+3OqB#}BYx;Ng^S8BYU zY!DEQ;zoa2PI6Ya`)=t6R_w6>n)LvDreeX7m9E%>7e^fv}y*hA@Ab!2>R8N23hx29rNp6MJSrc zz)eU#<2+tyT7HlDTwWGF`l+JF3u2YQ&KK{=qH9gC_iR{57N=X!0k&F@n?WU7_8yC$ z?m%?)I4Cq=l|c&11>SIV?X3AwJ4V+N>RjuEuG#fA3EXuk-;P*%Po+z%?e}U1jge>1 z^vz-e>SIG1q7?=o`Bt>2uun#`AuMB~rPgjyWtKZNk?h1Mm7WRj0RaCIAH|T{Wc7@) ziOQNW^Ws1(LWIJbCK&E;Hxzt1kVCWkT%hnkh-Z1b4wR8Cz5t_JOkZ6FA z$6(M!J#r9|+ra?lYThRb_bObsxU(e91h;2JTILb8l8T#$iVNK zin}~Z{CQ}O(;@)qBkkP}&!ZsD*>2)3)~O!< zgk=vN$h1yb@hEPKZtT_i5da_Q7|2Nb`0?h4DPk5dWIPc#7L5CKzt4r9VpyhaM2+>9 z#3bP%CA>fD@3UcIsM(|uP~CHO85g6%H$ z&-NOo9H~@E^&j!<==wdgx(EmzIi<#_g*fdGE#h-GQpSmV*R40w!b<0Jfl4EIxXz{o zto2SnwH>#1O1dME=VR(eLufGx?xiCl^hvv)o%0+uxrhvFEp6L(xbV1NcYlt{ z$VeC(KR(i(JV8d5L(itc5F8&G9Q@S~^rCcqEy$Ddts+xp$!CRjDA35=jZl(Y2o0MX zkC?ymKruZ2WL1qKC;nubG}|s)=g7yzNHy%!V>+3>JUJ1hs&OdVNXPgW9ThRnKc|3l<-1-MY0Q${0IY8p!-Et?{}T@sY?$k;m%uHTe)Ia z$s$v;X+T}NuTQtNOFGLzarKbjsS4-3ksd!=Kb0;vTvR~6K+L(yIAdl6kFKv{J9D}$ zNodZ~5&*qk_mq@z3G?__V0SU}n;{*kS}YSc^KlWk-03_>nt%tK2|H3j*dV_z>7TDc zM~etIm~ZqJ8F?|j-e5D0Xl#3Hxcpp|SiX&Q`HStk-;=3>lU3obs9O)blpLU5xnhfe zYWfL=*@@fsb3+-1G88$)Zh;m1oh(ns2P;eMG+73gHaeo`xyZNm$|t5R=4Xf{{l)pY-zzTnh+2W=CsEZ89%ya99b*ssU5d~CE4`iWS?S} zv~Z5dr>$o6y@*l-GrbaSd@C%6{_EAV)?gUBkzrvi#?hkl)>iYG|3!x135DE&yLS3Q zC9hu^c&UqX`!SW0KLXe2eOyQP*mKzjZ}t|^N2G0Ak; z#6DKIJu41$>2M`u`3dd&(!KGA-nN#)u)@-j$|)3k{qt6tfpwF%KIR~bL*76BJS)XB z%#@OrRtDx!rb*_Y_h*McaC-?l1f(&YYGu=KPT&zHx}>!)Cl%36`uzyn+zHqk&D{_*hv|1<<}5NUWjKrKPFXq36K z6sXpfGKD2SSgk0xrPD|)$H)HNR4&HjfG}SJpb(sZ$pIO`)YR1a{oEw1R#1>;I9Xb} zf8|#k0CNqGj0k#zzg^qj&H-~Aq}LX9EbXcO!<76Ghje1-{Pt2uCBI<6(!`ZkBXzB` zkRGI&@qDCe= zuwQQ|X53>Z>{)D52+2>``6-wHQvda80q4m!A_E)i^*3<;*>-@U{f)U}PyA1@_-nj> zI!X74g8bNiNXYEf^CNTmpQHSdPt^bdcNnDTz4{fRe{G)3?*XJi((s-V{CA!FkcW@$ zsv5MpdA}WKjS#qTx=4{-L`DhrZ_fVDIgy6HaXyO`3O4+yKK^wsKcHZV^bpISl$66k z`GubVbokenSV&s%8W0)rA^-lv--z(2f&!q&#NKE7M^NZLr}Cq}we&Xt=J`a6{<{V| zK>CPU9<%c&kb{4kr+=HqAN{uT0+5XDqeJ(5z(K27VId8`lraJS18BOgebo#8D>xUo^tc{~;r5!LI}j zoOAqM$;~Qz0IeLlfH*k*z73!okGf1Nj_}{t8NX?1!6Z#DSr7ywA8Tt8ETU|3I<(uvEii#-Ya1>QP&#=E+l21rh^6%># z;t5(eVx39f?8MZPSHrbuE-DEsJK!j`reaTfYn|4GXnhl%^4&A5zXDh5BxSsWV9gHNk!A^@ISTwAKYI*4oZ%^W;&J*)M$6Cq8$ZD0`qRm+V!tdh3L9O;N4B^lqQcg=T#d(^D0=u6n z&~NQ>UUqz_x~%V*Z|rt3@BD7bHCFENl?8}D1}l$=f(op^PjYQz+oipP+Dio@OQNb0 zWJuXCFETMTZ0ORk9+r8kL>2e#GbRCPex!uQMEa=L%7nEN1THT6SbKzT7L$>hQMJgq|ugcf&Vfxye|Aob=LWvEc@)+>!ey<1K~;0 zw;3O84pbe=y{A9GDhI~9jq)kEjjIJk-EDqBF{4r75^}xLvE(|=XQlt}h(LEW;XkYO zj)L8`l!tTrS%Nbv>@oqhrV{FHooz!YV*ucuW@xs# zDOKnQG>jciyk*6OVm*^{OmF&177cW=AFk6mOHVsCJRZ@xY^4lvS%mBdMWwHS}D%?MvB zLQPy7hMKgPv&o^YQqZ-^KF23xZ|$s-<*E%SGA0WtX4qqeqX(3n<#(hslsxJzE*RB5 zztOQRSjEMCIc4NmX;2!m)FK*92Ny4g_IdDG3#{wazTX0it-9@d2J`VZT--iqZM7N<60O9B&xS>M=-UWh&!}&7hCKimEzofk$ink?Yi*MA809$KS%vsE|%7suD`Q`ji#e{-p%f z?9hVVEAef22o3lGHegC?4j zb_(4zp{h_^!(DWLToywP0&Sd=G%e@K0zUGv(j>pf)k_#W_}V92a}5TZ%BDCDjL*rIMqyY~Lceq6`pdY`?1GS~Wy>qvB@GFfpZs3|ez zq!aEbOUa_{+2eiuM=tP|;$UxVhv#5Uy)TNm_F=gGk(|K|gvI{eJ!FygCwjUO(;xVV z_w?!mvz%Lds?1F*MNp9plH*I7xkAm8JVZuO=*w2o!v|VLq__i9hfAE`7S+xVt{Jks z=Rxh|&6gd;rdkriXlHI69q3N@#T}{Cf&!IW4)d92{l(8 z#zjge@x)Lv{nXB(&|I9#T7SJm{Mr)r^cD4tgcDqcFyCISP^1n#Us`|>Wgl>yE;RY^-Ef&^%M zB9=WZC9LliCF2@HUiX67t!e42lAnguXivza#I~cu$J~{d2%>Tqontt)c%*{qE~eKV z2jh8sen#246B(PN_ulT*%uR$kr{|PDi-!-!Ng{zIJ+gvYmVqrZ>`v)zWUChs48d4l zk9zgn-OXuw--BIy#glHjwUI16?_OxJu)zQ{F*egw;fdTC-pRC?^$x(Ke+JmSaCK}nI zb|81DNcXEGZ@_+CP9_(V=lT)HIF+kqb!zI1gee_svkQjbU=go@KJ4BKd{*ic2|gR> zd`&fLsgcJeNJ|Ub%`5>vFk(jijS-%*@YKQU9heoAsLdso zVYk`}kobz6qC}d^Tf zEIksIO2(z2IecqRonuyK5pH%8QARfIgVhv{OWl!kam>mxNswNS-)Iz-ssAwR*LaCDxLXB=;*!n;Wg^J*Is?zYm#eTnKy~TNU^VCR`QUj-eS7Yt6 z8m29p*ZcRAXQ|T;P2@Fgph_@mSHdE%P4a-R31BeM zRKT71X$_E=Ybn*A)4KC*DDKg(>mG_96>BPFu}3R$6yFja@m8*I)RvvMhr#Qf9qCLu zG`^SG$eX|Ai`i453{|P+tNI{Rs({S(IcqX5-EY};D@bo<)dDCGimuo1XA6f_$*1x?nBt0q<(8M(O+X+ru;2T?gU%?CE@_LTB#}7-DD5XUW7N`&Sd<`j z6W5+~U3uKnraSZC@MS;ry_`1}jQ@vrU`;hyeUap|v_W4Ljljie0eyiS5u0W!pJebiEZ?z%+#1q^&;25X<>7JsDBo&;Q+N56#K5XNU3 z0xrp`8Fi*LAWpT}=RI~ylHw@LiT~K=_@Az>M->bUP70;I#1LX>!6Z3-1%?ftgX6fE zFdF;t$XuZvw7fegm)nlrB#=}{DidZKoGbL=kO&R2PM4rH&sZ}(=&RG$05Z8nq!xds zKC7aYYRnx;K+H60^fNa-O`%yE7ahL0?HNKjHrcF=pA<{(D3<@aEK0oHcZYTo6Og6_ z&(zViz(t9uJQ0#vEHNW}rGofy@&{=3()*VBvb_H4Et!+wL}qzHk@92lFoqC}IvKAEk2dDNLbe7z$r0g5BFa3@dY6$&wTp0GmchW zZUS=do;+Urq^dMqh1Xm>s1?&H6E1W7zsud8i=GwSn|pmF#9oyB z7Sm}yIj6{&J*cEz1#q&rCK1_Z+!4IHDGGrN*2Y)($5&)IEK3;c%14FHI3*IX=PEV8KLjT@;jy> z#6EsmpDKXeduw#0i2VfEn+q~Jk>%!B48vke&6h+oT$P%pJ6kV$UIKquHDPhGL`3DD zQi=oYg~7U84m;ypt5Q}U4g`+QH}8OHb4Iy|qoh9KDZiQR7)RaD8T+VOronl4$G{V# zcgh15@ADgJn*E%<14vipz?BL7C;$Dm&~6U|E3^#&Cn;qP{%;Z5KjSg!y)b|b!lpSM z#eWaP1HPS707Nc~4^#A8euFh0PYfDBP9(I=xA^Zh{=cg>Y1JQrmJ<8a>)!~?8!)40 zK=_;S1*P&gFayr6n*Ip-)UG(rB>k>U{y89SJ#(k}{WsPW5PPnX2SiyGoOYGXf7j;! zbfnbCXm#ycpYN`4)>Ir6)BlXDA*Fl708prark3*y9e5<(v($e@hoef-lnolR`r};V zpFhh*0a{Ok4x0Q*SLqaB+s>vei5*Lm2Qy_`4L`?U)}1U3fPYB3c`N-Ayz@*XJ6kkw z<83K4M!&g*ntR!WLYtxDdAPrvwng6&`-eT(wtl1(?%(R1{~Bb`l1i6IVqzJ;`dWQ$ z+qHl`c|^3%^$%e@RXzY%i>kiA82=wI+e3*gw4R-gUJoevB^)5RO;*EI>ELxoW2u-G zh?Ft^`8SaNkDNn5VBoJKu3-T?>&^Kijy`eQs9j@UaXfx!D7y<2yOCw4cSuaEbEPY9eY_e3-|Klm`I_TP zGOKqLDz)*hsYV8RaE3@PN zbo{O6r{{`crVjO21?|1@CK~XYkM7+i!*J# zeGtoOE(qKM{KkkU$KFJ*x8!DrlMp8-3Q`2}xfVZY7*&?FtFir{S9^-pB50f2BziyS zyq{YK*K=k!L4FNi&01pv!D;tgz4)zmGM#o0b3u4y#uL;pd%%vS~ZS_2z!k=-mFyHgiBfX+cQ_ zB?2YF5^Dwi;Pvf#U~qD?vcsk$*+gw>mzRbno#(M&bY!~sMPC1_zD9IH{x!lFBUNdt z!35zuPl+c{P`~R36Irfr3Td8%`#{MbwuRYZy+TaH;C7Nt+;{Zw8MosVPO^0izB8bz zm8PVW#C@~;xw=YcfYBq&z{J+&`v@OI4AY68Y5xLgYc0tU!2>GWF$P zmZrs3zgaDHWiEGsp0V(vGG{nG!XumBNSeAv*!iRCg4GUaZ%V8?jJPSCvwu39=+bJ5IBtv$2tO2jN;_;`|L8ECY1YTW$SKOBtQ2I2zH(nMv{(E--Ft zRqyVrjYv&qpX(M(5O&7#kCPh;I_EQ+0_FGpN)LvWCXYIIcN$9E?kO|Y9m#8VJ2~-H z%3-1e4lb)73?Fg3I$4VVcUaKCCfq@KNnnRgZPM6WWMBOHmY^ zK&-6fb4lS2P!l@`IPGH}xxeHTCSHO?SIxh~W@1YF-c>cpKaG^9GaTk!lG0T{uS72|pJeV$2B|V2 zP}Nhk1h}F?atr>a(Km`{&oO-!RM^q0cXDbM*o8)=p;0rO0Y#Nc=^m%5qCFa3%x1c0 zxp4h<)qTac8&zaUT*Rezd12WT;os>+VPR{@JG1fdFmWE6xGQB^e?JpZIP_wDoZr8O&A3Nb}C{VX6Sc5LLA@+tJ zQ!FD4`O?;iO8Amjr4(VQwT#cL;-0|>@TbdXFGs7V<~UbkCNeVADGsAm^&4fMg+9qU_Ag2nV zir$WH?c;@SlB9LMq_y>xLwSDImr`T3hcj+v4Mh&yW&hw|@!|4cH-5 ze%`{=1e%0PFq<;k?YZVZMEB4=V|&Cd1lzWbp|!(x(qj39p_+R>ZIqIh5Vm#4Vxm+7 zn>Kon7OY^_gKmuq9fMB~)FhX#5WBn(~>E`f~YlvuxH5X z>Uyy3>g4KiEN8nyNGP87q|@?Abg0*e0S~vFehL@m5HTEx=l)wk^8%N~eS!2V7GGK# zgRq^TUvmXGdwp;OETr(B>WLx}Ru!j71mBA%5$Zs}+Aa3!8mo=`I$c0qE@0~$JmDpA zSv{Gqo6qh7Qs)v0Gy#`6gj={v9o@M>bb2Rtwxqu)Rd!lJ*#lg1B}uToXV~XcIw2@J zS2GwNu1c}QuHdI54Vc;YCopYS-LbluiqJT>Gk8Fm>=RomP8nM|k>niif+k!i_~1|0 znwoc_@M~g5O4L+o6N?6lrmN`IZpLa-}XSa8p9ln(689-Ho;=q%Xc#Kl}ns zRj{RJ6Nv;rH2ZyV(70&9jj};pAIJvd8}1+E z07YN~N*#e2_l?&xnu@RMrSaCH5$R@B^0zaXbYpwx%2}P+xnYiuD2(F(7vGs2@v21+ zAcrQ3eZMnlmjeeEgIFCo$jx=M*Hp0B?&;x0Rt~?&N{P#RPdxlhVBMwk%s|#QLU&0Sj2_@1rz4?C4>X0yoffN&2vliRX8fe^*lV4vwG;YIs z$JS}@zK%3GFf~@4Hv@xonj0w##0nf^OiM0e9r6SZ(W4Q7`)QwpPsYu&?hA92^EnrM za=fqoGz;`RMwUMpcNs%jVScEf2WP!zaWk##mehV^X(I8xdu<~H35z=a*~ifk>P}mr zcpB86u6vFLU57HHL;X=_>8}uhg&R=QIq5D9lbAym(UZsE}_j8XGl6&#sGw z?X94efqjVSBmxDuCO9KLpW6Z1J_zPlM6~N`bJ)=dx1=0J^OlEL|JUT;5v{oKWnx8K$$Mg9u1oQL*r)$u)NV6yv->**n}yWBAWw0#JRg9@y~Yl?C1r&;T$F2 z#jxoVVi{KfR5mfTP-hrcS1NB58NYcY-D}@1*AevRL781%3VAc7L|Q#?sDR#m%2nkn z1^rg!5(%Y?IZNT1t>m47!yyjR%?OPdAkv{8VZ(yc-xo$ebWJI4JvoqE)mP!zUJsy%E~Q;#PR9%p;& zZm3^YUV8luhRFH1mquHEkHKk}xJTJ1Yuhv@0G95nqlTuhs&JbZxvMn@zhi&`PV4s( zCG=)c3R?dxM648bxy3Dfr0|?q;()TVb~nx!z>VakO>o+=WGFjELC7;vfYSzEf2c-l zg6m&3wiqGJcc_-}s&7(EM-NO8!D2AUI4 z!Ai;3_E*J;KLyYFVFar-fnnpILpBj)A!iRx;pYnAh{+9`j}n`%hl5NU4R_p>KtW=v z9f9zy&{E#H#4t&kU$)X^2{b)_Ty1*N-H< z%eM}`2a%DHo&v$GpYm#{-Z>geEj?dZkL{D+zHhG$-3qKd&K5u+xqM6b8su{%?St~D z@jT}Tri5J9I%e(*K`cOEJZi6G$+N&)$thSn39eTOhBhd_VBhn3@as70DeEr%7xwSOA`|A1CbiSO2}Trc+$uE;;A)rxumatlQj8GE zOF`FA#{ju(4#9}+a~;d@MeU{LTkEhd|MLgq91km zUWobwHQpQ7Q@8R?-!a>EvHIGv^-}{aDFOF|5ff}r znO!-R7syefI=6}Oo<$^B+>&DC7veMGFaUOI^ilVXxh;>Wv@UTaM*A>z)S4z0?WihX z_A|qdbHeG)!<7gS9~b$SPvY~*X(kXVqKu|P`Z!{H1U>E~hjdl?TG~;c`i)@6cpH)( zK77kAzDa?ei>;|{%;AhE9*FP;sSpF4T01x(Yr79(G8MOMX&O9LD&(Ax36nS8Jd1~& zmY%U_&SBjPOZ%t^9GDc>KUk@3(o?9eb_B`%i)($eX1n4n>nys16R_8Z+u0tV@bMhN^&zOqqe4 z<}42^#uSuBEJyaOaNzu+jlh0zDY-FQQV9a4f@7hs1b!B+8^1ZjZ4KwzVl$#2>?u{w z?%z=|Qv_j2NuQM#s&Dq7Yv03BEeOjf%nqOHL=k_Sv^veTASA>VdbHyeo@*?9a@5EP zMM7gm&;_^kY%{GAYzkAn{1}4v`o@cjkNHk#%^N;ljQvtqt+vPV(%jecR+1qZGY zK$UvM^}(!;7IB_-)?d6BaMK>nF6Ebv%PPO?dL$%RrN&WAHJi4&t;D2;Y>+D!pJ}`L ziO=Ch1=l;>X9cF^ae&AByCi(f2}{aTI2{6^pmnc0SU#j^3GTymMpe8s;)wid7!L7% zq&*DdT8?WMu^OLdVYmd@N4;4r1;gvJ6hGeI-;U*1$kQnJTUDfyGBP5s4!I!1!|nwR z`YKvXRk50dW9V>O65Sr?Aut+7cGP@;06S?tyQXAyOxQ*MbNoIs606;iomSG+6vZ*^FIG}8_w5e;-6tyNxZc^#Rf?kC6r7i$okI3vdCs~ zX+=dTr~?d;j#RjAZwd=va&{sTJqQ12}U{NBQ9#in!qDp4m}`H(nqDv(!7{+huCUj!ZwN z&Zq>)uwb}cDqST~tf3-Nh6APYx5{bvO|S~X?fWEo14~DwdaZ6CNJMo3;VrYz!->dP z7Y%*;nV6FH^Ki;P8Rd>z+nELn=e%T4Q&Y1LNIj9^p4NF$7DQq4AA;X6NZV1oYSAsf z*|eu6t)vI|d|G{BRMQA+HDr*?fRlxAz==rSJW%j@6(3%; z%=tt@M3E3gWs3Q|sop9z;{|*VRXfAzUJ@pYh=YgPj)e$BmU#g`0AnOXq4WzD8O;3j z?xR+eXv7C7S}2jGHu@%Zc0lwvYIzv(V7FOmha>IOF^#?|>C|PHosb9k&nHFF1A0YL zXb4ES-G@gL=IgR0&f*G+xg}!8gO9w+)iu6(N*H^VT%Pd*U9PYP=$}t3D<~3}iLNdI zoSRZOx^?c_-T5XYBqUFSLxW10MvD)TdHI>fSXb(8%Bf3V=@BEji#$e-?$iCGHFO9l)T@#c@mqYSk9x?^!^ zDP%B?W-+EYDD$Ri^_dz;uQ+o`K1@_a;fUOJ2&p;9MKmFE>pAaECgZAz)F~$Go5F&N z-3_JaOEY|yIZk-};;F@9-z;k-*Uydt-SgZcrhC;r&UzsHHP|0Rx`04A6s^hUma43g zTgp2Y;lyybdry=hOl3{z3tb(SahM4G0~g`gIcWacu|w`r0H2SpLH-15a z`voI^lkT6FvGpH)S!5qY{LjAp-XJ%tcO_)~I_ZCZ=kfmmT32KxAHoNq-#&X7z;b7H zZ}r3e=e)3g@D`lV0G!BA#`zVrzuwc0A*DMO8IxI|e?jn{B>d&mh6RY+fgOvCo$&XQ z`ruiL#{*_QYq3N64{Cn$`9I$PkOj=vEMzAP?A0Ep=_UgA&rko_7{L>8$&E0Xph&td z>U5zg$(P5{{=||PZJcNw`ItAp#Rm(1|9%#lRvu<(fJ3ht1)vdi(>8j+*+- z6@H&2`J)f>tzpXysoY`Q$4kEUE9?PM#c3PB4;iZx@6p(efOVoydez7IB<9_)cMrZb zbmKzd#N$PZs?XnUi&RP1lD`Ff)$MGt z2KkJ=6grpf-oz`R6}d4I3--J-0JaRgy0Uo` zKAb7xOXG5F)RKea{YQ7Kn*89jyRTA_RgX^Z3_4Z>JcX4=#4v-O83h9NDRx;mZ1q?n zvD?O!-vxhLz~}d`pM9*|iEeoU?HwI4a($>kCv|;B520UQbm}UquP%kP%-EDULZW4A z0u=}ss*_z~+v@SvX`4<(gtNnS~G&aN-J-Aw_rd-F5*I!uAN}SI=rsbGB|jD7D(!W2)>5vcsmbv zmjYOIX;*f@qs5+1QOyE%@gthlNNTw6Wsm;4OS5|h@2ucx%x)7L93nt}thxYeBc3lM zDmOafsMKHyOh3?^#TMdwTcHX&p0|}@Y@f8fT+uTsrxZMPPT$;4 zW{|&A6i@8Z3IMYKR|q4APmqUC=H?O;%w$p^1p~=}$N1wUXW%p`a269|_3i075SF%>1zoAvlEg$sdAB`n)7(&> zw7t5K;u2wTIJ^@@2l)s85gxCgdo@$;b|&-Mpwv32xJyI!*e$3u-L0|(@vL&rYE9GH zn1>b>=#tmcA3lAmwxh}{=8b0cYK5f=&6kDoa`n#!3s^=TeDP>JZW8 zb^BD1#p1TZk?1S#+Uc(|1&JHq$aWYX&{=bF38RDSO18Bh`r=y3*LDgo=&-7wpgfOB z!rd`Xp7VCB$(_(I94n~xVx}s1mwlE=Kj!MgOj!!SaYvkm%dBXKxXQYbirN$HL2O^Z zG9_5q2)ckq1&~ppc1$9Zo3eMWU_e9AWf!QJR(bxC1CCHQR^6#TPsOMi78i76DwxGK?x_c#PZZ-##C!Gq4o=eQr=XHCN)0zv7 zoF*T*%D&Nv`7Y!P)4VejWDDcrM)cWQQ;lE36kFw8Ncq^is|GG$kWbrr`B$bKIWKp8 z+?na^mK5AvTsVO02{no(rKN(}+8He-fEu^dIc0fn@lyY2IKnkgj}7lQPte9F9%}(W z5>P4}cU@>&qFDt{^_sh1L4~4_A+0*+@S(p}zDZ?ffr+SJH-PDF=_l3w2c-h;Ija&; ze^G3ixeM4O7sQH7!Xm#6(u{x2 zJRFNRAgzJIR5uLyqAF6O;kPruSv!^eMb*ftkV+ZLT8#X&w0UW83I`l1 zw$SX%V9|xii(Asw@+bXc(^4yhWVpT@7l!oTf+I@%q zEG4#{b&pQ*;;wRR6&HbloUDCWO$Lt_&7wAziXjB zI_Q&OTE-#_9XD0#!Irskk)~sEwgh#L3k5&Fj1Z{<1+245lzJbztv?`|J|J7EQTB0W z+IED9QS~|P{)9@N)_m~p35*jl`E@laZ z56%HJtH#!hhTeVAWU@!lOPVhiakT({r)gT@BAQMBs(4E8UrbEO8`m@FP$)iT1Oryz zIFx}E`$s~~2sV4`8+@hF^Z?&DD{oUt#gFc%?h(_-@Lr`F` zqH=d*_JrkMxSMU(kBc{i>8}=_6VP+XN57+ca{fy1l6ENxlH}eJ1&!Ty3E%6v_~k6kI%%2dCz``s!I0G&jUw9-(pX)I0!i>OlB#OC906jKz0ruH z6&(@vKmjBn=^`>M3CNL=X-Kkn<#NMP)!Zwph*@6U)+p&iGf}{0hQJddweUz<=b%)8 zt)=|C$T?wHX_TQ^YvMbnZ1V-5Cy9hcRwDf!9ibOu6BQgP9B3x^*e%$t(or96`HJgf zsboMI=p{N-?}i`#$yg2Or#_v6bzfsapq-X_$gE=xq=WOk;`uou`GC|KP-Cl^4^*_G z1Iu20sWcUNs)b?rd1@3vX?5UD!NKPvCoYtcize~H7J;DHl$|t>)=nf?yq$g z%TtRbh2Qd`$!z`JV(uu6!2asw&_LdmB7Ox3EDJCr(V^{3Q6W2ci*IT99Aa!}<#s{q z(talI`=VO@-OToHt$ox-6)uQkYT6wIXqgJ9v#MjIb&t;Pz`%=Ev(@@Pf36;i!Ig=l zB)HZ49=@(>QkVpu#i(7{Rpw)=S!>df{AE#FemKlDlX5vWIgwJ0jBuXL{`=-)-u8N% zk*Ztc%C=8E(jH5Zp_o(a;rXLq)O{mcf8+gvi8I|67xqkF(3?IRg^`P_2Vm_2z%{Yd zFd+(=K`s^Owa+9)rf=WY)Pn0(B!#0>ddp&$vLsh=HSo3N&J%T-da1Tm)N+1H3UN)i z!1_XArk|JVf1`RfgTX?mhcZ_vPhrrbCs=8bfX5}$pCE~f_7>duRBre|IZ~#c@qLiQ ztP@e)*A9~{BQ_zj$I75&osUOrYAiA=ia#p0gGpc{dv+QiV${~WWVqk+eeuiCLG)s z<=SrFWtB0Na(Lu;@5@sZF}myxxZq298P2{BqhI+_N#!-h6kJO&ga+Z(CMWZxKbIpb zThYr1aV8KyZ7}o9xag-T963A^BJ9G_D;<2*8eq{&7F6RRQvHa!h7TTJ%z&LG=HRHgpwlEbgMRu*WYX$fA|xUQtA z>&|g-I@T+^eVC2jpDYBK*G(Nxqp(6SXHf}uwmOKpyuOv66bst+$YA9^AmwpUcFg#) zu}i7HKJZs+J-}w>)Y(j9Q;jQw=k0LAvD4{yWlW|C_NO+T&+8FwLi@p5)m>L-b5pP2 z=9R*J+*n0&mNH#WOl+xku?^cl#j8(yA+iIb_$K`UwvI}qKOQ~KW2-N%*M~N8A$UlI z{5-=Nez7m)*w?$vq_-oN*qIKpFGu;!q7T?TjCJpxrq$Q7{gLTaRtFQ*CLF01?oOGb zT~=?D?P*ID10w~E_Fco#;KS}R;)W4sAJumh;)%iXFoxQeM@rMNk}P!$=;#E0*a(7; zZ`$rmQ52*GEfDc`_tP3QitX07pwt~#2leLeMHB9Nkuv&f@(0)nV@^LA_E_;>W0P(J z`Le5N}rFUN#DG@|>(!6Y0@0c98#vKQ^BXiEV-5Iu)TS(y&n z9;N&wLgPtrN70pa4+f~Photir&eU5T^5q!kys*afF!_3=8=_A?QyjbRTgX&W@tkzJ zfp=|2@sZ_|I)_s}xY)z!M{za^l|s06dnrAweejYnmD$Ph(qunrNp=d8#RvUni!^Dr zBD#R5>}z^#vkyK9hovMpAME(NB()(jzAe{E-m|d1r(y1@P41)3r2?xXl)H;Z8-420A>lo+VeQveF)o2Y%YrrIPKQp-L$ebCzVtJgULssI@A{{c@=` z-%Q8~q)W72n)Qt@p@0dE`Kt$pEf&ZFfs-gcRvl=vfSo3=N==d=zX8;Hm_zeDKE(sp zjJR%VPAT_uaDK_2)FCCp;;~bLqI$#(7pxAiA?(F+mmRe9bp)FtnP#}lYl-Vm_0fqB zCh=B`Q|IhtNF|3=75JvmTc-P?teZ)YC@$RmY$CoY&SMgp5b_-qnPC$n>~$_taew+~ zn^0FcsR5y6NwoH24MmrGMECWVf)dwG6%Wxx;(;d0&)L+bv5=5yA)$uj@ks;}?w%K& z>Nnw85=awPZoy#oaLk8Zj=zSsmU%q59GZaG{B7Q`T+dKMm@3Ao9s1dRS0Uk2{>C%~ zWzq#>ZuDZ3;`r77yK0_@8*7PDoe|ZFjY9R13uO4yg_&YisBxM=Yc@*~r*89CbcgeI zI6?7yrAsy4_>7GG)BetcSd-Si=oJ`2w`<#qcEYWuRCjvcb_5tdSV0FR8vB45Q%^)8 z!^qxhE!QH&rtQ|SzS29V^~_aR1_TEq_CZL0xgB0Wd?pb3V79|i?79hASOV4=Rl+rQ zOd?SopBO#zfC59gkp=1>rB`Tpd6R6V01uN z!ymOmCzm!+Sc3y2xJAktn9ifzkzZ@pktak*m6d49ebXJiEs)Qu?6ovxW9U8*Q>>V3Y-!H`S?WK}0T@+AZ>!2aFsuZD7FVRRg|F{lul8 zeSRcE-)e+S^UM-_fbD2jkaa2i!;evf@O%S{^*#r2yHte0*3iG8luK;&Q?!6UpnKR7 z&Rywu$WKFFB`i-PMGoXt0`7>@)AFoV6S>-lLN%pWg1^Q|8{+_Rf#tzhRgMRN=6f$A zy+b0u4c|HY0j*5Knd;Z@k7CnW(8r4BOxi^I#rC&8v}o3YEJ6d);O9=&cS(;;PR)1g zX+$~24&idWaB@>j?RMFaj*ew`niK1d7_0=kxq+zp4RkOcU-70>6~FYpfYU}M0v~Q9 zY_d2VVU=QXf+hSv?VV**+|9P92_X=IyIXK~cMlL~+@0VqjY~*ych}(7xLa^{cXxM( z>AYv|J@38e&RO$)rdNNcRn4ldF6v*kYyY0TixHz^Ry#Drh`e8~+$R~+@7KK;UQ-zy zC1R%qy+mb&be(3s!Bgf~L#{mOOCQ$%0{*E}eXrRPrmKH&G3$6K?3jV#Y5;T-X<+C0) z2F`Qjvwp{+F63c~f2_)O;)2)lyFQ03kE>*YH|YN1FoTsPE%-a2F-a=4_UO9cv5l@k zX2%-WcbYQva+zGdjG7|nTONcRstWbTFNQ5m_6WN@M>Adid6@^jS8=Y}(UNv=FLztd zZhKl{vNSZt(&;{LcRrld#+ZL|!w6}C6#(l{0|oB=w4}02m5b)e$kzfF5CUl*qF>3>Z))AHg8`x3^%>`s=@bkvkDQI5ckR|ZyA=D z(DzvO<;@iGhKPgRKD8gawy|Vt2MGvDZrZw>(^)5(dd>)ej}!4MUg6H?jXOu@rGNP_ zR%G>sw(X?+97i^jcJY;34|!MWoNV;Omy4ydI;>x>@cvwXrn8juZ;?-|h1?Sow}4 zUe>yO2*|wh_N*gv-~nk8Dq46_67~ri!B6fh@0=2~mUE0WhkY90LvMG*fJ-XX$Z7Aq zU7iV<;nWRgvb0U_n0Kion%xUhAML`ZzIbOTWktz81mf{v*L4t7t2dW&SplJ_UEec2 zI|)Ioot{;0!s&w-*%zMWO^jHkmta100;Cpg3JW4O<2{`T`5Ukhq=+HoBleX4#xBk9A9ynFsBLt zeb)^aNj1vO=h4CDMvLD`fhU%1EX)MGGs|^su%x8W#B6NnC<#l;x=o2`?eIovckiJA zL9qcfn|1G|qVE=U(NDZ^LcP6HZ5VrA?V<{o(3G%tjuw|$S2I=tg1^)&r*Xo+6lFk%x=aM!K-hS5skX#-@IM*K_XJE1_f+d5H?Ed(mH ziF>Y5GW=!*vt#6NsL@e%;dlXv3$jXZTHPOCU7hE4Yy?sVEPs6}mph z%Z=OH-|PuJ@h<>m_JXsxPE7uKiBj66RW1Flm=9G8;_rC-`!>-+Nle&o0@lFD$32HB zjxDVX6L|!|UX+;(vBElIt!eiNBWJ1Fve^{cT0m9&H11M!Jz8YrQcC&^%_o-JTWRni z=!HE%z~Xkv?{r3fOms2Ex`d6bo;_|cCj4XyPMf80#3%X6aU<<*+{${#O+Aya=yy0f!(i@J6v(ZzI7?}MQ^TJ# zHYB*a3}Jm;vc_3a#>mdl1j8)md{Rlkkv-%*a4d!C;cd|-yXF{ftyG0&O`lu`zA09k zGp~+Y*Xq_=cAsOnT*ZpqWVybwk)wWUNXgM>EIGjFbT6cn$GJ7#$lNBTyTi3@Zn4`7 zqD(ucK4**`FlF+WcAj#P5+B5j0a`b zL0iKNuOpLn@h$mP;dZ+*iI|TvDe(1+_O?^~da2!c)Wn|FM|7yuw7tUKe{W=}&0F~k4h#iPBdQ*95xHg7c~T#F#Usmd$kQADkBtOlmZ2tF?>V3*!8 z^PYROPD=zBaZRok_OxmL6f`s~o8PvIQ|4HIMUgvN;dn$i&a1yfU1zNv<=&Meus5h;g??dCQ(~uwfwip^&QvKgPo(EHySTxTL}MWb*uV9Q{+Uhc8`H)8h#xqG2&JgVB*?UQ}S zAsEzB=|8rXj$BsYLQr;2w7D0I#ue&_G)iU_eDj)Kq*mwjru?wF7q(VL%H9Nls9}m- zbdAxw&Yd+R3rD0rz2{A=FVYUrs#J7z-QZlkCq(2RsJyQi9YcQ?g4c@91=2oO&9YEFXVgE0waC?ZAea%xY zjpUz3(!KQe`av)9-bb==Xy5VFVHz@$BN7^yk$$Gg|G*~JmxP5Bwn3V;pTI=xZRBMq zCyh%xf#sOSA5ZyTZzl%d*^HsX<>&=oSWhL`{gsx_)a+=XnO`JpNZwL;yhcO+qpj~y zd?@=<1xOsWtH(u24}U+3c_U|qLJMY9!v3>!fA`${qpXbpwf}3wLjA+w-)G~$l)-KN zm_stN`nM#8+IuifP#&O?1^@SM{P*%Y%)7R4BjC0sIeh=z7;z$9`gfPH>gr-;I+0Uf%!w7a8%Zk_-Od{op7e7}Tr}wlJOF)c$+V{`S>maY(O+d9DKP8;${nfv*S`F-ChI*cl#{;RamYU>$)69w)zgFvA+UFC0fmJQ z@Y2gaZh}eXpH>TmkoJgMZ8}fvb5OQ9Owa^)&KU$+r;n*F)W^y;=2*J=4;~uPnr#T_ zXSgcv-s}bhHZiVouhF{03D$e=>ACFniR9+z`V*aXolZ&(d-Hb1U|0Ds{4L>dJm8Lj z1fVx>GMkVVlE~CUK4nekO7A#@BWAC8i9k z(qRd>amV~YjK>J*50oL6G7VZ+#YiFYmk(--++I7MK$)(bHn2z{k|6TAtf#r())&db zkf`9vM~)r#pU=>r(f>Uongx5--Zsb}=f=%s|Ai}Mv^}&3CxXvIc$x*DL+N~$Nb+`> z4PTLRt0+VBwN{|X@o@5hExqpyi_y6F5p-IU}(s zK}|SFF~Pc<6secECfuFwln(OdejU(!abZ01hM}+gJa#PI9Dz4t6*&#Q4Wlu=f$=4J zmCBR|odM8EW3O{f?)1#PJG5^CTvA`lA3OiyWbNxfuy&Jm>RHOL5Hue*6EM1G07!Ol z$-k*B6?}T<-2PPg60i|(DQPe}t@YIgvslLw;h3xmh%jBd9t|}Nw$Lp{{TtKevQUhh zoi`gUjl^L$VTQ(eO^A6~3P|Z+*3?x3(rt-c)Qf8r0TvE3;$uYgovD(7^=m4vKP*vr zNl~J0lx{MlTmTitR*b6xx<>6KJ}2Y6QP?Nml#RUKtoVHJeHI(x&RSfMGt~uqK3t2o zr+#K~o`&kCSP5_b6}Xw|Ix#gX${i2tA-JRX+>(wKe^>gTc`G!ZMWt>-7|BS z0F)Mbj~k~Eczyoraj}RjE3da`UZxrBwvIY4not*i`^#*oxN%;w0jkF*Y3Xzgvn;Y( zQ3>*fGP?R+YHs8w`PSK?`iJrZ!lF4kVT^3g`LTS*{h+U$+%VamQTy|fAI?;mv63aV zjx1XukS6ucEW6PNnI(2?lyO%V8X-7&7)6_G;kz_dTT*{g&ZP2-*mJkCe_)*ZUFopW z2z|L~kOuYjqwCvIS1xn)m)|~+BU~*u9J^SS{NEC%lW1H8y-%2jbqiIll>tWwhoN4o z&uoM-d-9q;`}%I!KOXXrel~70BXSb z&cnyGy}~b#H`hOE?s&egR!_c$h!3!3ugAaYUz@V@@-Jx;9W*{gvok{5+|g-Te$!M9 ze9zb();=5DNwF>|MI97<>gUwUC)=Y>@n^l#CWjhrYahy+7asHqI<#)*(C*MkhAE1$6=Mk>Uj0?{BR z%%{4>M_bQ>DYm*Lk+W4SSULssemAwrhk>WaOGobl?;&#((E@PXU3Mo8aHM(&ac~LD zU-oCRHn*k+a(cjr$fT!z?qIh=J?Ld;0>Ms^>5~b4D3AU)r6WPye_pCKCUlkk{^w_> z4;hCCG&;tx_1vyeV_Fo1DJ~k$`?7{q1wS~iY1E@wJ@~0y!o@%-b6Zk)pP zs(z;_$tdxspl=+LK(u z#f9$mE5;8lk3gcmO|rKdZtXzaTCZgq8`!tapA0Qir6>v&xg zOV)x7wPH65ta1{ADSjlL@=%>!-RbZ;QMb`CM96-tgA$6OGH8djJ(xWaUeLn2D?TBL zbTuxvHPfN|cEgP@BGCH%6(k=W<%){xKJ@|8#D9AE_;C+vruwz-2^U5I`c=J}@C$ml zxAmP#0}ybC`3cMW8FJf{NsB0ZVl{z@<79G0nAe!_$`G4{{faKszFx9P{?E8!CY`+}*s8l)9zdQTPd4&{zTbvb)IhsVga4z#Tt5 zAnic*%TWK+H^*KR;}fynu!*MUgp|mr#CYE{on0$rb~wCDt&k+VaA{;o&dm5%*fIQm zG!TN*3ZzKH3M0dByDO7`K|`C9s4g_DdSQ5+5C=9-CwLZ@V+p_zo0WZhqLJf4U zI!g9pe(a^Ag>z=n7W#(=?$^nlCqitx^h+U?Ajdkwx3?}_yc~db`JP#nczW={W3ln= z)^E!RWt|^^Xp`4R)@r8$AtXFIpvg{8`mwfJ$ohiD=QpkZ{B03tE6}K6^v%JxsXwW z9~rKJbcaA?1}hIV%DN5TW%kBG%O8d8P3w@_=FZ&3ByKrhHmq%_d%1zfMrquPr%o^- zM9-p<24W3~x7oTw^KxxKEMi>*Vt@E!H*;>~8|$yypEo7>zF%C`Hy*~R3%>t~-ZCig z2nOanH5i@!l5DrfH?VP6A9J)<9dEoc!m&!u2ZEKjY!fMwU!K;2I=VhjBn&P6)@PpS zz2b7;ZfLd6Qa{2Aj1>81x%V?P!tLAa;7t;z1aGEpF>ge?AWkrboQ%!J$M;0us+8z0 z?rX@a7Yg%1u`=q?jZI4V=f()QcqF4^$bM#`%~03C3~wDu?HuO6PChy^>pArEMFBlnsi~xM3N142%&XyjkXBOnyVO||of5|$Aj@A;k#_P{!LS7R?r#o$wOa${A1pV6>S zIVnwJ#j%C&))CnZeQPg2JWLrzp(2v1aE3mgs~r%m^jG<)!~mw`w-{YfZC87IR7 zGBUr<`Hho`Pm!Y6RWmFyaRq#y3tG8xP@J9P$QVtxrt^N{Yj!5`vK$NT(*_{c&Lo`Fu)LP<1 zS?!xEc55E-@~z|R=I9Nux#d_hOlyUqBV-4Z>@YV`PFLQP0NkVb3O!S0aTvFf7rwfv zJE57}-2m-OMNkP%;gl>6X#>@^mCyx`de4K>;EGOL5GlU@y#w1ECsHcbrpr2(qSFv3 zIH*GZtIwwKh3QWZt~Od2qxYiBuDNE8sb-`CX!Kpkm|U1+r#!{B@}}a7W>g~a0ClQ5 zb%4Ry;K9dm?111wSESa1+b7{QUk{poP%_J|M*-K3lryu~&;v`Ur;q`~zo9vK)dSeSbL(8`YY+SZh^_>mo(o=NqM zrSYm|SnTY)who0eA`--2UGD70f`kIv8`4kReWMiE*+0>oKCR}}Bh(2Z;yoTEl3cgg zbR4-ox~1FUMdAEqE~$X`qs@_-VwG>`-h@Om*`)Q#!Dp=wWimaQE-v=Ws|)Fow$D2UC(XwcrGLW zy$l+M+v9n!2L>ptx@QSExUwHtP;o`zKY}?t#lGxc#kvXI(1+|F$F|YOMlaq6(=U3O zDd%C8hgl(B_vn0yHRq;1;^dGm2&8+}caWax;Q^CCs8+Y9UdcA_L+L?v zdquDpj2m4W(iip-k}k(SgSiP;nh|L5`=+a%6S7G%OxL!Tm1M_NkZF+xJ zBJmQ{GxmX80Hh|pc13OI;TeI?1$+9#LKFARos*LuD({OX|LyRp#AE$-|zJ5+%XhX_5xq|Qekv9}Y*$CM%S?cP{j%4ZlhN6_- zhV5~t)$%=Y5rC{cl)e;Th)Y$d)2$a7yIA)P>DZY#z9?$>9k?! zX`3~7$n0n0$F6LnHi`Gu(O$^3v?)D9MTk~9`Yuu|dl3^!_RY5+TW(8=az=leS@E62 zbi>Qbfnb=u*MEMozo6@$kT#&?_k`Fq8AistgnCWOZq&o}c^b1TaNmRlRLrelGd{}O zLc<+o2m06#~%M?R8BmTt284j4z$G}`w>Ut3-`i`@tG<36FK7uegI)>Alm#}tMA1Ig zs%;}KhBzWDYX7W5u1bSah9r2hR5*#3MBRskq&G!8iffj_c~Iv*TN^LArPKAfMWI{S zosP0r5B>=lt9gR9=4)F3FAn0n`Hk3%*CbVYrj4aAoM1Y046NzJz3QfY-rT|0ot{J+ zXoA_QzSgkLdyamYKIKaXLnQoI?OxwLfu6vstLcVyzeA(KO2E&|mha3)ahVl+mFC&P zjd-ac%P_V*d8D@cFAKPSw~`W~Sf>MZ_qX_gJqwjo>W3*|t&u+vXyw-@t+b+2E@+1z z{pVqfI`fzh&o6V_cWkVR&kQ9au1y3(YZ=(JG(bHV9ku4BlVi1816Cc_crrC;`=%} zEIv6-tbNL?*mB#)SHlkQ0io@ZGAI5yw={(a!3nr}#amXyamVs}?Z|V+op#B>ttEnU zB$P0SIQPQ+FYwsn`zHE5JHy0jvL?#G)#czST10Czq%>!0nRw0STd$xuRB7IVsrN+& z;JBI#g@MdGdF)4;&=O8PE^&iV{(7_aIcPBwo=?c`I+BIWn^j*4SV+-!Hc8nm3(Jc- z{0GtQvmVd5JT`4zyRK1u5`{FssUqfoiE#EnYR9ugWvx&*b}dAYm%VF5-_{hR-)#p! zWfQ%uk-^PV4t5qiM`QTFRY*JZ{1z@rQ(_y+$Ni zhDJ~TxX;_?SJ)+UTd68DI|nvo!AJU`R5NFoZ?_>fQL1R*CuS~74uwJ= z`#Ea=!SJE;Xpvj;kZf?%j_2;sj!L4QP}|sC+kT5rD`E?YM;u5aZFzNUL}TqO0Ky7b$*E%O zeWGOeOtGlbg_qihWXl*?{YJuTy9?DVPSz0D(j%@qofnQ)thv@XDfNE5>BO1lw1Uh1 zCK({ag}OJ4;$w^7Lnzhp6nj9ORFo2*8gw+mw{ZpR{6=i@M^FF@2gq$|dM*dQH|<@V z8oZra+a&e!Ws+(4;%MGxuXYqlJJRidGO*zs>ZCL%v|m72A!T$juKZiy=JK#&~q zhGcEu`dw<&xrf}ph!zk`_KHpluc?=5tiWp(8ml?x9oG4K^)i#c)&A#ZnbhREd87&F zMozu1$n8}L`-cipc^%qCj?HA;!0n+hwt#@HS(}RSvYMIQTrM`xtrB8p19ku#nO1i! zPqEyhE*YVunpMJ2NcVjU#C?}C2KrSiUwh_Yk+pp*ul@4-uMGqr(JafK%y9)jz27lL zY4uy-!nEIG<^fvuEjOn2gZn7Qm-OJuKiLam6=B95dfBh=5(d#TF83#)8I9efvmjuMIxdC=_p( z5YFwDXpE)mdsUFYfxR&Gxsl%Ob*4B0)(?9EkMe5Ituio`+-F%Yc^Dr*b>NJ2YfLW% z+zAYHG}wD>AgQeg-TFQ89YrMUH{|aMo+_Tw$Og#y;F5cs_L?TfPh-I2pqGllcl@~4 zlbw96fUghY+ze66(nz1NNIiDhLkM3?A6|VUli+Gsc;21*HK)n-{OaE1wYkD4>e(t+ zlM2QAN7C}WMJeRD&cFQ)92{fToRp&Ghq)ojQu>WYaFzjRlsuaGL6IG!z6goa#Zfc9=(lG(c@Sv)_o zD$F^lu(n4kMp>+Vl2S{`>w3_~us4>cHow)`>6y+C-DYk!KVD!sA$rZ*~a*6eHj;s2Y=Lm(@tMdX2q?eIU zh<%Jv3&CEa3c0T@wh>XtFTuMyk%mx$qwlpRBCUU$qc26x%8D$P26xOxB5w7+zV)Pf zykcoNAE>lSZ(Zt?FrO^fa<$wDJUu?NO2YqmtUA@9Dez4y$bGo!S6@2t9!|23xiyVA zUD=WrOH6tqGCubxcjac7`wcPp z4FR4#iOg3O*7bhYsulrGDogYu*L1P2Q=X4p_Y5(y1I_KX-p>a9s}@t56$pcUD0KbH zl?X$o5$|vrHjLCPQK)qTA0G1^sMn=X64Jg`p0wPXoRxRy)CUAhuB3~Gthv4GV0Sez zfx=8_1l(xdO2oA${OnU@m_he^)M&~R;MXSG>Df#mfrgv-?u$*29$I(nvDFJd&Iz|4xlbKz!}vC?R<-^_a+${u_0tBGoeavMsIBabT1Jsa6{PLk&3q|v z03l!<0-kzaZE`i-477g)B_UJP!k*U=xoTeK;VQC$vhR-b5C(3AtOE3W-*edS3=ETW zLDl3w6UJfy+`LA2v!b4ZsmxF!B9i+H{>b&V4}gy@P%2yx3OKi|Rqx#VQ#R`=u37oG znivB?7drAs5YoggiVNMT(8qKA zdL7&#N+j$!6|5_f<}j@5=;VD>-GTA<;CejZ-r!YJrJ4CvBcEdH#Af4g2mppmbt)=< zRKq|d>WlCeoWCbFDR-u!Myr;ubBA^hT^p)uYB=9&hr2nmn;SWX^AS8{h#TV`ZC%oA zUIm*IH5?Ln#z2HYTX9(&9W5YkW+>hpNf(8|#^KYsm@0qqXolE=$5VxH1G;_sKp;5u z;_=DP1O4qbC|SL&)Wc=aoT8b3?AV>`Qwp%T44-{a=1jYvFoIjueFLkH>?w)ufAA>VvV2X$4_iZytV67q%kJaB@BlA%*Jjg7IF#1 zoU8jzh><5&jY$f{D0t*YnF?NF;^X3C+jGZtcDI9?S8~l~P4tSZY*bTSd0}dKuO))i%{?nZY3@ z$yhA7&A0Jq$*p6CrLGD>+S+Mc0^fgKg|zVdmqefhrE()#u5taau=HK>JgGw)EXNjQ zQdHrh^$>wAt~d5+zzy&%znWg;o$dOjlBUvq6`PLC0_WHMJLFRupWEbX5%R_1A~e36 zQJHWfoD&pLANnu|_Her;-4sTfa+>5HQG0R}@PFp`?B{pF)kY^PnU2)~$fw4?ia{V!r8+tQ2W5V21X& z3!02vJ6i2=z}-8xSsJ_;TqOlD$UU1^pD$aXJ}2c!F^-p&YkVlk)Rt7BB78oP zs6PTOp5Q4M&(@W-T)uEAl%@BbEYMmb%&2nDWzmb!QNDA8EPJ`JTK&;THckQsiHu^7 z!f!sfbmH-9Qr|WWTyurD1x+1(+^fHzATG$$9}>qGymh%~nm4&s!EmBClhiZl~P$!CaKNHw~^3_6D)qATb9SOC~@= z*nSApdOi^`-{f@|6a1R6r&xJn8ly8S{JPfU-jUec`L#7_sVpSfxiyKr?tPPG@x{<- zZ@5YO^n07`G%Tfx9RrP`S^}iC61Dv%o+?R`jSaDpP8P$kU-#5bs?=DYHb1!1>c&vR_P)NyhaMS&i0qcX}5_#;e^rR~yk@1bj^sM7*Yq~_q@O)0= zib>g(adZh(&S@^$-(a%Bh@3x2qZ^R$tjt%}y!xvS zuG*GfaFz+ zlHjC!%7}EcXPqHTi^)g?)+BE^tlfG6WSJ8(&)o-}@_d6!zv^`b;$nYHaBsGtt6uxK zI+eO&{rtVToDk?>VkA7JlGwMv1T|p=OPX%%mcIs^92&tl;eOue!#~lfX=-C;papK(fv&$}muj)foE5+0 zzK`6?sRVY=gDf&WyIbyvehF3ptxU|TKg0L|XMwY^Oo{?vUILnV!$c60Og=2;yDtR2 zD}qqcF%M)mrze0@{bEweb$vmHOBYB81Of|rH2o+~tM)x(M4oXbB%dLaJ15@UK9c$y z@AOBLGSeAWRV<@NQr<+OM^>ywnsSr@)i-#GdESVE?9poBEoT*5oVBYUOaFrEKcZnW z44Phs=4tNOL_UysteE51Sp3jpnY3#MuU*@iHY>##(v|Dp(LBx?uz-!IA&90A;@rs$ z@rBc0#a^Nm)_nDfynkYlmtk@&v%-&^@`p4@T!c#bVP@9|{)sZEmMzKYMnAJg5xmci zGw+$4DHmQu;d)&}_2an{Ri7VIC`NxAP{HjtV%vrhl&DhH{1aCYxM;aKgx5_@>%+BYj@EieU zusNpWWGzN(b*Ip1x%@r=5h!)_lC56pJnEK(r!5Bs)in%voZaq4gAePs33?ljvCLsQ zlgb^I>)2GeL?9-%-Wp?qI9VfoE%TTgpl-gD|GkfI>&gWHLWqAh>lbS{snapE^-w%r zIGjwHZIqf z8Xtr(GL=rgnD3@wP|w|@D@Kk2IXNi;z6*OPfx_ThH-sxAxgZ3@rz|NkVP(I7fX|%{ zcur>xsKD4Bq)ujjg780~0Ge9%OWzOrJ**i+Zrj4d1>0q-5HOwF1@XxaZ@Z_(ADTGs z!r*(Z-9jtA@crQAnEnA&Jh;@@%2Rfce2L=!hTKalF*&L~(u5Ft=^QNS6H@UIeDH;- z6SzvMx@ywO26i{%bAD|1HmWq+kseMZ3|dMk#w>RrAU_QZ>t%TUBDs1)Jc)3UK%tI| ziyveOYJ~O`F}Ib{EG{t^_*&iGLZ`nEzb)ff`!5j_izo*N*yYRG=dmSn5P;weqY5fD z=Zs2(Sm>~x>w_s~ej7fDgLzZKMeMy5M8FGB(%a*n4eq6p%<&>*37Kk^=`^_2#wST; zMl<};JKe077xS;I5CtfOf)}9MecNf4fwas0`-89&qO9_pQt(8HVwp1DrMLkWdSBU*UHcWE&!>X-HW^84k1 znXt3H<#nXG%*{8QdkL|8=ZSM!&3Bx=uc3Z*`0k>!V8h0`%f=1a_{^$<4^1NEW<4fN z&0r+0xY_|RQY-Aa;F=Trm_Ra5f2#x!$UsUKHaXv_SsQSSQdr-6N@A&kK;V4shCF_y zDJ8eBhl9li^;WwlZl8g7r-Vd>8i<(b+vUOGu1`%PMjisB>iv;qci$@MbaiLD(kv!t z&^7`1jKkhgKIE{RQN|1CiS^8%Bh546!o;%epd)oqU^yxZ-)y2j5j|Mk7{WifqP#3;9+@AOFT5)+|Tb#z$*ziQXNxUxHIiL z?6nSe+-;M#=9qc1uAEGhUDCj>!gaf+$tin(&yhxFrlkuMFceISyZu>~Kb=sV;KoO> zaI0qN`n-8~!$%8FsdD}FJIv>`QZrEq>@}U_p1GA$H|KS&HywGp z2#uUn>0(YDueYo!zL>wcbzEzAdvr->%EWQ}E49(pM>aiBjo1A~V4cGSH&{;uyw_=y zH@8*u&YyHH*1onco^&pnSYk-APGr=nN-yCv;#JZxZzkRhzr4#0v|-TWF)iUET?_*3 zG|F%=rnv4)joN4>+}g!RTF|a~KEzmh?=jqSw)YNU;cNFaX*V1bk=X9+(=hBV<60+< zsx01h;uwQgZ3R+0`)ea<80Qz4>%vD{i;sB)=XyL5Uw6O@raps=qhobhW#z;xibDPP zvg?BLK_~Yfw_AtvK~;uH-`PK;Mkmo7V8Ec#!kmDWrIwZ>8(LP`4ZG!(z1xV0No9R^ zhB>+(1VOvnbK><<6Z9*Z(U2|cJI_?gu|_>|C=0;iC99S~lV*%{>TUWpu~oRWo=t!x zxxIr)TiLIUR`Z@$*}889KNJi2s>v2+vD9yTa@o}8S#q0-9{QGftw{R{>CoDgWHs5A zZjBEys!VP?V^&nHm!e5%-;{SWV(57}wF=PLX)ID{Wvpj5mz5PH1Bb+;&n*2$^2I5wzUiP;;u)=B}ou;r(rxz{%wmbDjZHALE8DW+w8UIeLW z*0e>_wud47IFmeii=|DQ&`S}@<^-|NO6Msj;<2*F!V8^bE{wI!Jn;#0KAchhG4~@F-4H=GQpTBw5K)gJ1-OPZD#lW7kcC#k=t$~w~ z-$FRu6-QOa8UeTay?RJ!U{qAwevt?Mm{$l8t3+ODO5f+1_pg|UQjU}cmq}Ufw*FN` z7nJxJ_FFgTzyJdwpR>K9FeQ3y>PXgrPlB>9qAir$pO{OJ7NAwk{C+&^lx(p%2E7IECsUYJO4f3 z(!b#B>Ed9Vef0B?_rJ~E7z>7*mj=Es@c$3J`Q!EZ>-~S*EpYBe%J(LB|CasN_{09w zRUQAORQ;2y|F8Kad6I>MDDTEj+w-8>o-c=N4~Ds||7XCTiE_LEKy(GU8zaQ~C&wef z`TSq(-v6oPrr6)?94Jjn5E0-b(7%k?KWo;B-f5mc_Jx=IpBnm8pSAR7^iE`i-u=73 m`Yi}HYW{yy{|}7Sv*3V|LPysE1p);4B_%E=1`^T#@xK6b%<~Tb From 910fc32443ed4c285b006c801c504df3bc1cf286 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 14:12:14 -0700 Subject: [PATCH 072/126] Update README and documentation --- README.md | 50 +++++++++++++++------------------- TrustKit/TSKPinningValidator.h | 11 ++++---- docs/getting-started.md | 5 ++-- 3 files changed, 31 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index d70213c3..24abd82d 100644 --- a/README.md +++ b/README.md @@ -33,31 +33,7 @@ Getting Started Sample Usage ------------ -**TrustKit** can be deployed using CocoaPods, by adding the following line to your Podfile: - -```ruby -pod 'TrustKit' -``` - -Then run: - -```sh -$ pod install -``` - -Alternatively, Carthage can be used by adding the following line to your Cartfile: - -``` -github "datatheorem/TrustKit" -``` - -Then run: - -```sh -carthage build --platform iOS -``` - -Then, deploying SSL pinning in the App requires initializing **TrustKit** with a pinning policy (domains, Subject Public Key Info hashes, and additional settings). +Deploying SSL pinning in the App requires initializing **TrustKit** with a pinning policy (domains, Subject Public Key Info hashes, and additional settings). The policy can be configured within the App's `Info.plist`: @@ -110,9 +86,27 @@ The policy can also be set programmatically in Swift Apps: TrustKit.initialize(withConfiguration:trustKitConfig) ``` -Once **TrustKit** has been initialized and if `kTSKSwizzleNetworkDelegates` is enabled in the policy, TrustKit will automatically swizzle the App's _NSURLSession_ and _NSURLConnection_ delegates to verify the server's certificate against the configured pinning policy, whenever an HTTPS connection is initiated. If report URIs have been configured, the App will also send reports to the specified URIs whenever a pin validation failure occurred. +After TrustKit has been initialized, a +[`TSKPinningValidator` instance](https://datatheorem.github.io/TrustKit/documentation/Classes/TSKPinningValidator.html) +can be retrieved from the TrustKit singleton, and can be used to perform SSL pinning validation +in the App's network delegates. For example in an NSURLSessionDelegate: -The swizzling behavior should only be used for simple Apps. When swizzling is disabled, a server's certificate chain can easily be manually checked against the App's SSL pinning policy using the `TSKPinningValidator` class, for example to implement an authentication handler. +```objc +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { +{ + TSKPinningValidator *pinningValidator = [[TrustKit sharedInstance] pinningValidator]; + // Pass the authentication challenge to the validator; if the validation fails, the connection will be blocked + if (![pinningValidator handleChallenge:challenge completionHandler:completionHandler]) + { + // TrustKit did not handle this challenge: perhaps it was not for server trust + // or the domain was not pinned. Fall back to the default behavior + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } +} +``` For more information, see the [Getting Started][getting-started] guide. @@ -120,7 +114,7 @@ For more information, see the [Getting Started][getting-started] guide. Credits ------- -**TrustKit** is a joint-effort between the security teams at Data Theorem and Yahoo. See `AUTHORS` for details. +**TrustKit** is a joint-effort between the mobile teams at Data Theorem and Yahoo. See `AUTHORS` for details. License diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 71da6a1e..fd654236 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -36,14 +36,15 @@ /** Helper method for handling authentication challenges received within a `NSURLSessionDelegate`, `NSURLSessionTaskDelegate` or `WKNavigationDelegate`. - This method will evaluate the server trust within the authentication challenge against the global SSL pinning policy previously configured, and then call the `completionHandler` with the corresponding `disposition` and `credential`. For example, this method can be leveraged in a `WKNavigationDelegate` challenge handler method: + This method will evaluate the server trust within the authentication challenge against the global SSL pinning policy previously configured, and then call the `completionHandler` with the corresponding `disposition` and `credential`. For example, this method can be leveraged in a NSURLSessionDelegate challenge handler method: - - (void)webView:(WKWebView *)webView - didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge - completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, - NSURLCredential *credential))completionHandler + - (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { { TSKPinningValidator *pinningValidator = [[TrustKit sharedInstance] pinningValidator]; + // Pass the authentication challenge to the validator; if the validation fails, the connection will be blocked if (![pinningValidator handleChallenge:challenge completionHandler:completionHandler]) { // TrustKit did not handle this challenge: perhaps it was not for server trust diff --git a/docs/getting-started.md b/docs/getting-started.md index 25a101a6..40f56f30 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -92,7 +92,7 @@ A pinning policy is a dictionary of domain names and pinning configuration keys. At a minimum, the configuration should specify a list of SSL pins and the corresponding certificates' public key algorithms. For example: -``` +```objc #import NSDictionary *trustKitConfig = @@ -143,13 +143,14 @@ After TrustKit has been initialized, a can be retrieved from the TrustKit singleton, and can be used to perform SSL pinning validation in the App's network delegates. For example in an NSURLSessionDelegate: -``` +```objc - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { { TSKPinningValidator *pinningValidator = [[TrustKit sharedInstance] pinningValidator]; + // Pass the authentication challenge to the validator; if the validation fails, the connection will be blocked if (![pinningValidator handleChallenge:challenge completionHandler:completionHandler]) { // TrustKit did not handle this challenge: perhaps it was not for server trust From e8b9d6b08f246be67b1c084031613c609b5cd1ac Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 14:39:26 -0700 Subject: [PATCH 073/126] Fix build errors after merge conflict --- TrustKit/TrustKit.m | 21 ++++++++++++++------- TrustKitTests/TSKPinConfigurationTests.m | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 7fe093f8..5b6e3186 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -24,12 +24,10 @@ #pragma mark TrustKit Global State + // Shared TrustKit singleton instance static TrustKit *sharedTrustKit = nil; -// The identifier we use for the singleton instance of TrustKit -static NSString * const kTSKSharedInstanceIdentifier = @"TSKSharedInstanceIdentifier"; - // A shared hash cache for use by all TrustKit instances static TSKSPKIHashCache *sharedHashCache; @@ -42,6 +40,7 @@ #pragma mark TrustKit Initialization Helper Functions @interface TrustKit () +- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig isSingleton:(BOOL)isSingleton; @property (nonatomic) TSKBackgroundReporter *pinFailureReporter; @property (nonatomic) dispatch_queue_t pinFailureReporterQueue; @end @@ -66,7 +65,7 @@ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - sharedTrustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + sharedTrustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig isSingleton:YES]; // Hook network APIs if needed if ([sharedTrustKit.configuration[kTSKSwizzleNetworkDelegates] boolValue]) { @@ -82,7 +81,14 @@ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig #pragma mark Instance + - (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig +{ + return [self initWithConfiguration:trustKitConfig isSingleton:NO]; +} + + +- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig isSingleton:(BOOL)isSingleton { NSParameterAssert(trustKitConfig); if (!trustKitConfig) { @@ -112,8 +118,8 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo BOOL userTrustAnchorBypass = [_configuration[kTSKIgnorePinningForUserDefinedTrustAnchors] boolValue]; #endif - // TSKSwizzleNetworkDelegates - check if we are initializing the shared instance - if (![uniqueIdentifier isEqualToString:kTSKSharedInstanceIdentifier]) + // TSKSwizzleNetworkDelegates - check if we are initializing the singleton / shared instance + if (!isSingleton) { if ([_configuration[kTSKSwizzleNetworkDelegates] boolValue] == YES) { @@ -126,7 +132,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo { // Local instances cannot be used if a shared instance with swizzling enabled is used, to avoid double pinning validation [NSException raise:@"TrustKit configuration invalid" - format:@"Cannot use local TrustKit instances when the TrustKit sharedInstance has been initialized with kTSKSwizzleNetworkDelegates enabled"]; + format:@"Cannot use local TrustKit instances when the TrustKit sharedInstance has been initialized with kTSKSwizzleNetworkDelegates enabled"]; } } @@ -165,6 +171,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo return self; } + #pragma mark Notification Handlers // The block which receives pin validation results and turns them into pin validation reports diff --git a/TrustKitTests/TSKPinConfigurationTests.m b/TrustKitTests/TSKPinConfigurationTests.m index 279698e5..2049515a 100644 --- a/TrustKitTests/TSKPinConfigurationTests.m +++ b/TrustKitTests/TSKPinConfigurationTests.m @@ -399,7 +399,7 @@ - (void)testSwizzleNetworkDelegatesInLocalInstance ]}}}; - XCTAssertThrows([[TrustKit alloc] initWithConfiguration:trustKitConfig identifier:@"local"]); + XCTAssertThrows([[TrustKit alloc] initWithConfiguration:trustKitConfig]); } From 5a0763a1086f2a223ec2b42737c2d48cb8c6b469 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 15:29:23 -0700 Subject: [PATCH 074/126] Update documentation --- TrustKit/TSKPinningValidator.h | 2 +- TrustKit/TSKPinningValidatorResult.h | 4 ++++ TrustKit/TrustKit.h | 27 +++++++++++++++++++-------- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 6a6360c0..86114062 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -17,7 +17,7 @@ /** - `TSKPinningValidator` is a class for manually verifying a server's identity against an SSL pinning policy. + A `TSKPinningValidator` instance can be used to verify a server's identity against an SSL pinning policy. In specific scenarios, TrustKit cannot intercept outgoing SSL connections and automatically validate the server's identity against the pinning policy: diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h index b60821f0..3f480afc 100644 --- a/TrustKit/TSKPinningValidatorResult.h +++ b/TrustKit/TSKPinningValidatorResult.h @@ -12,6 +12,10 @@ #import "TSKTrustDecision.h" @import Foundation; +/** + A `TSKPinningValidatorResult` instance contains all the details regarding a pinning validation + performed against a specific server. + */ @interface TSKPinningValidatorResult : NSObject /** diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index e6d86f7d..338e0bcc 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN /** - `TrustKit` is a class for programmatically configuring an SSL pinning policy within an App. + `TrustKit` is the main class for configuring an SSL pinning policy within an App. For most Apps, TrustKit should be used as a singleton, where a global SSL pinning policy is configured for the App. In singleton mode, the policy can be set either: @@ -69,7 +69,6 @@ NS_ASSUME_NONNULL_BEGIN ``` NSDictionary *trustKitConfig = @{ - kTSKSwizzleNetworkDelegates: @NO, kTSKPinnedDomains : @{ @"www.datatheorem.com" : @{ kTSKExpirationDate: @"2017-12-01", @@ -114,6 +113,9 @@ NS_ASSUME_NONNULL_BEGIN The various configuration keys that can be specified in the policy are described in the "Constants" section of the documentation. + + After initialization, the `TrustKit` instance's `pinningValidator` should be used to implement + pinning validation within the App's network authentication handlers. */ @interface TrustKit : NSObject @@ -140,6 +142,8 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)sharedInstance; +#pragma mark Pinning Validation + /** Retrieve the validator instance conforming to the pinning policy of this TrustKit instance. @@ -148,6 +152,19 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, nonnull) TSKPinningValidator *pinningValidator; + +/** + Retrieve the SSL pinning policy configured for this TrustKit instance. + + @return A dictionary with the current TrustKit configuration + */ +@property (nonatomic, readonly, nullable) NSDictionary *configuration; + + + +#pragma mark Validation Callback + + /** Register a block to be invoked for every request that is going through TrustKit's pinning validation mechanism. @@ -172,12 +189,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, null_resettable) dispatch_queue_t validationDelegateQueue; -/** - Retrieve the SSL pinning policy configured for this TrustKit instance. - - @return A dictionary with the current TrustKit configuration - */ -@property (nonatomic, readonly, nullable) NSDictionary *configuration; #pragma mark Multi-Instance Mode From df6382874e1d4e77880595b8b0acab23fe6576a0 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 17:08:15 -0700 Subject: [PATCH 075/126] Make private properties truly private --- TrustKit/TSKPinningValidator.h | 35 ---------------------------------- TrustKit/TSKPinningValidator.m | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index 86114062..d046491f 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -90,41 +90,6 @@ #pragma mark Internal Methods -/** - /// :nodoc: - If this property returns YES, pinning may include any additional trust anchors - provided in a domain configuration under the kTSKAdditionalTrustAnchors key. - - This property is YES only when the preprocessor flag DEBUG is set to 1 (the - default behavior for the "Debug" configuration of an Xcode project). Subclasses - may override this method – with extreme caution – to alter this behavior. - */ -@property (nonatomic, class, readonly) BOOL allowsAdditionalTrustAnchors; - -/** - /// :nodoc: - Domain pinning configuration, typically obtained by parseTrustKitConfiguration() - */ -@property (nonatomic, readonly, nullable) NSDictionary *pinnedDomains; - -/** - /// :nodoc: - Set to true to ignore the trust anchors in the user trust store. Only applicable - to platforms that support a user trust store (Mac OS). - */ -@property (nonatomic, readonly) BOOL ignorePinsForUserTrustAnchors; - -/** - /// :nodoc: - The queue use when invoking the validationResultHandler - */ -@property (nonatomic, readonly, nonnull) dispatch_queue_t validationResultQueue; - -/** - /// :nodoc: - The callback invoked with validation results - */ -@property (nonatomic, readonly, nonnull) void(^validationResultHandler)(TSKPinningValidatorResult * _Nonnull result); /** /// :nodoc: diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index 8b1abe39..e660a608 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -20,7 +20,40 @@ #import "TSKLog.h" @interface TSKPinningValidator () + @property (nonatomic) TSKSPKIHashCache *spkiHashCache; + +/** + If this property returns YES, pinning may include any additional trust anchors + provided in a domain configuration under the kTSKAdditionalTrustAnchors key. + + This property is YES only when the preprocessor flag DEBUG is set to 1 (the + default behavior for the "Debug" configuration of an Xcode project). Subclasses + may override this method – with extreme caution – to alter this behavior. + */ +@property (nonatomic, class, readonly) BOOL allowsAdditionalTrustAnchors; + +/** + Domain pinning configuration, typically obtained by parseTrustKitConfiguration() + */ +@property (nonatomic, readonly, nullable) NSDictionary *pinnedDomains; + +/** + Set to true to ignore the trust anchors in the user trust store. Only applicable + to platforms that support a user trust store (Mac OS). + */ +@property (nonatomic, readonly) BOOL ignorePinsForUserTrustAnchors; + +/** + The queue use when invoking the validationResultHandler + */ +@property (nonatomic, readonly, nonnull) dispatch_queue_t validationResultQueue; + +/** + The callback invoked with validation results + */ +@property (nonatomic, readonly, nonnull) void(^validationResultHandler)(TSKPinningValidatorResult * _Nonnull result); + @end @implementation TSKPinningValidator From 3656f89cd039205f14b4765cca6dd163b263dac5 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 17:16:35 -0700 Subject: [PATCH 076/126] Clarify documentation --- TrustKit/TrustKit.h | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 338e0bcc..b8e51100 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -120,7 +120,7 @@ NS_ASSUME_NONNULL_BEGIN @interface TrustKit : NSObject -#pragma mark Singleton Mode +#pragma mark Usage in Singleton Mode /** Initialize the global TrustKit singleton with the supplied pinning policy. @@ -142,7 +142,8 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)sharedInstance; -#pragma mark Pinning Validation +#pragma mark Implementing Pinning Validation + /** Retrieve the validator instance conforming to the pinning policy of this TrustKit instance. @@ -153,16 +154,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nonnull) TSKPinningValidator *pinningValidator; -/** - Retrieve the SSL pinning policy configured for this TrustKit instance. - - @return A dictionary with the current TrustKit configuration - */ -@property (nonatomic, readonly, nullable) NSDictionary *configuration; - - - -#pragma mark Validation Callback +#pragma mark Configuring a Validation Callback /** @@ -189,9 +181,19 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, null_resettable) dispatch_queue_t validationDelegateQueue; +#pragma mark Additional Settings + + +/** + Retrieve the SSL pinning policy configured for this TrustKit instance. + + @return A dictionary with the current TrustKit configuration + */ +@property (nonatomic, readonly, nullable) NSDictionary *configuration; + -#pragma mark Multi-Instance Mode +#pragma mark Usage in Multi-Instance Mode /** Initialize a local TrustKit instance with the supplied SSL pinning policy configuration. From 2e30087be869a5d208f2822dc4817417951af830 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 17:24:14 -0700 Subject: [PATCH 077/126] Make internal methods truly private --- TrustKit/TSKPinningValidator.h | 21 +---------- TrustKit/TSKPinningValidator.m | 2 ++ TrustKit/TSKPinningValidatorResult.h | 8 ----- TrustKit/TSKPinningValidator_Private.h | 49 ++++++++++++++++++++++++++ TrustKit/TrustKit.m | 2 ++ 5 files changed, 54 insertions(+), 28 deletions(-) create mode 100644 TrustKit/TSKPinningValidator_Private.h diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index d046491f..eca7e272 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -79,7 +79,7 @@ @param serverHostname The hostname of the server whose identity is being validated. - @return A `TSKTrustDecision` which describes whether the SSL connection should be allowed or blocked, based on the global pinning policy. + @return A `TSKTrustDecision` which describes whether the SSL connection should be allowed or blocked, based on the configured pinning policy. @warning If no SSL pinning policy was configured for the supplied _serverHostname_, this method has no effect and will return `TSKTrustDecisionDomainNotPinned` without validating the supplied _serverTrust_ at all. This means that the server's _serverTrust_ object __must__ be verified against the device's trust store using `SecTrustEvaluate()`. Failing to do so will __disable SSL certificate validation__. @@ -88,24 +88,5 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname:(NSString * _Nonnull)serverHostname; -#pragma mark Internal Methods - - -/** - /// :nodoc: - Initialize an instance of TSKPinningValidatorResult - should only be used within TrustKit. - - @param pinnedDomains Domain pinning configuration, typically obtained by parseTrustKitConfiguration() - @param hashCache The hash cache to use. If nil, no caching is performed, performance may suffer. - @param ignorePinsForUserTrustAnchors Set to true to ignore the trust anchors in the user trust store - @param validationResultQueue The queue used when invoking the validationResultHandler - @param validationResultHandler The callback invoked with validation results - @return Initialized instance - */ -- (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains - hashCache:(TSKSPKIHashCache * _Nonnull)hashCache - ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors - validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue - validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler; @end diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index e660a608..b3e24052 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -18,6 +18,8 @@ #import "configuration_utils.h" #import "TrustKit.h" #import "TSKLog.h" +#import "TSKPinningValidator_Private.h" + @interface TSKPinningValidator () diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h index 3f480afc..d5e50b8d 100644 --- a/TrustKit/TSKPinningValidatorResult.h +++ b/TrustKit/TSKPinningValidatorResult.h @@ -66,12 +66,4 @@ */ @property (nonatomic, readonly, nullable) NSArray *certificateChain; -/// :nodoc: -- (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHostname - serverTrust:(SecTrustRef _Nonnull)serverTrust - notedHostname:(NSString * _Nonnull)notedHostname - validationResult:(TSKTrustEvaluationResult)validationResult - finalTrustDecision:(TSKTrustDecision)finalTrustDecision - validationDuration:(NSTimeInterval)validationDuration; - @end diff --git a/TrustKit/TSKPinningValidator_Private.h b/TrustKit/TSKPinningValidator_Private.h new file mode 100644 index 00000000..54f9f5f6 --- /dev/null +++ b/TrustKit/TSKPinningValidator_Private.h @@ -0,0 +1,49 @@ +// +// TSKPinningValidator_Private.h +// TrustKit +// +// Created by Alban Diquet on 6/23/17. +// Copyright © 2017 TrustKit. All rights reserved. +// + +#ifndef TSKPinningValidator_Private_h +#define TSKPinningValidator_Private_h + + +/* Methods that are internal to TrustKit */ +@interface TSKPinningValidator (Internal) + +/** + Initialize an instance of TSKPinningValidator. + + @param pinnedDomains Domain pinning configuration, typically obtained by parseTrustKitConfiguration() + @param hashCache The hash cache to use. If nil, no caching is performed, performance may suffer. + @param ignorePinsForUserTrustAnchors Set to true to ignore the trust anchors in the user trust store + @param validationResultQueue The queue used when invoking the validationResultHandler + @param validationResultHandler The callback invoked with validation results + @return Initialized instance + */ +- (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains +hashCache:(TSKSPKIHashCache * _Nonnull)hashCache +ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors +validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue +validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler; + +@end + + + +@interface TSKPinningValidatorResult (Internal) + +- (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHostname +serverTrust:(SecTrustRef _Nonnull)serverTrust +notedHostname:(NSString * _Nonnull)notedHostname +validationResult:(TSKTrustEvaluationResult)validationResult +finalTrustDecision:(TSKTrustDecision)finalTrustDecision +validationDuration:(NSTimeInterval)validationDuration; + +@end + + +#endif /* TSKPinningValidator_Private_h */ + diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 5b6e3186..6783f326 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -18,6 +18,8 @@ #import "parse_configuration.h" #import "TSKPinningValidatorResult.h" #import "TSKLog.h" +#import "TSKPinningValidator_Private.h" + // Info.plist key we read the public key hashes from static NSString * const kTSKConfiguration = @"TSKConfiguration"; From 4ae67dd084584822442b7fde216143b4b12e52ba Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 23 Jun 2017 17:26:01 -0700 Subject: [PATCH 078/126] Add missing change --- TrustKit.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 2e470716..8b969355 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -393,6 +393,7 @@ 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ssl_pin_verifier.m; path = Pinning/ssl_pin_verifier.m; sourceTree = ""; }; 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ssl_pin_verifier.h; path = Pinning/ssl_pin_verifier.h; sourceTree = ""; }; 8CE919291AEA0F7E002B29AE /* domain_registry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = domain_registry.h; path = Dependencies/domain_registry/domain_registry.h; sourceTree = ""; }; + 8CF27A911EFDE7D9009369B0 /* TSKPinningValidator_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidator_Private.h; sourceTree = ""; }; FC1A08FD1E579F630055B12C /* TSKTrustDecision.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustDecision.h; sourceTree = ""; }; FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; @@ -573,6 +574,7 @@ FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */, 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */, 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */, + 8CF27A911EFDE7D9009369B0 /* TSKPinningValidator_Private.h */, FC1A08FD1E579F630055B12C /* TSKTrustDecision.h */, FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */, FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */, From 3781fc339d4321a9142779e3ab42ff6e8bac4aa1 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 09:57:56 -0700 Subject: [PATCH 079/126] Modify the validation callback's interface to clarify intent --- TrustKit/TSKPinningValidator.m | 7 ++-- TrustKit/TSKPinningValidatorResult.h | 10 ----- TrustKit/TSKPinningValidatorResult.m | 3 -- TrustKit/TSKPinningValidator_Private.h | 17 ++++---- TrustKit/TrustKit.h | 25 ++++-------- TrustKit/TrustKit.m | 31 +++++++------- TrustKitTests/TSKPinningValidatorTests.m | 51 ++++++++++++------------ TrustKitTests/TSKReporterTests.m | 14 +++---- 8 files changed, 69 insertions(+), 89 deletions(-) diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index b3e24052..768bd45f 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -54,7 +54,7 @@ to platforms that support a user trust store (Mac OS). /** The callback invoked with validation results */ -@property (nonatomic, readonly, nonnull) void(^validationResultHandler)(TSKPinningValidatorResult * _Nonnull result); +@property (nonatomic, readonly, nonnull) void(^validationResultHandler)(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy); @end @@ -78,7 +78,7 @@ - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)p hashCache:(TSKSPKIHashCache * _Nonnull)hashCache ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue - validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler + validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy))validationResultHandler { self = [super init]; if (self) { @@ -197,12 +197,11 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: NSTimeInterval validationDuration = [NSDate timeIntervalSinceReferenceDate] - validationStartTime; TSKPinningValidatorResult *result = [[TSKPinningValidatorResult alloc] initWithServerHostname:serverHostname serverTrust:serverTrust - notedHostname:domainConfigKey validationResult:validationResult finalTrustDecision:finalTrustDecision validationDuration:validationDuration]; dispatch_async(self.validationResultQueue, ^{ - self.validationResultHandler(result); + self.validationResultHandler(result, domainConfigKey, domainConfig); }); } } diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h index d5e50b8d..c14fddcd 100644 --- a/TrustKit/TSKPinningValidatorResult.h +++ b/TrustKit/TSKPinningValidatorResult.h @@ -28,16 +28,6 @@ */ @property (nonatomic, readonly, nonnull) SecTrustRef serverTrust; -/** - The entry within the SSL pinning configuration that was used as the pinning policy for the - server being validated. It will be the same as the `serverHostname` unless the server is a - subdomain of the domain configured in the pinning policy with `kTSKIncludeSubdomains` enabled. - The corresponding pinning configuration that was used for validation can be retrieved using: - - NSDictionary *hostnameConfiguration = [trustKit configuration][kTSKPinnedDomains][notedHostname]; - */ -@property (nonatomic, readonly, nonnull) NSString *notedHostname; - /** The result of validating the server's certificate chain against the set of SSL pins configured for the `notedHostname`. diff --git a/TrustKit/TSKPinningValidatorResult.m b/TrustKit/TSKPinningValidatorResult.m index b8454621..f06e95e8 100644 --- a/TrustKit/TSKPinningValidatorResult.m +++ b/TrustKit/TSKPinningValidatorResult.m @@ -28,20 +28,17 @@ - (NSArray * _Nullable)certificateChain - (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHostname serverTrust:(SecTrustRef _Nonnull)serverTrust - notedHostname:(NSString * _Nonnull)notedHostname validationResult:(TSKTrustEvaluationResult)validationResult finalTrustDecision:(TSKTrustDecision)finalTrustDecision validationDuration:(NSTimeInterval)validationDuration { NSParameterAssert(serverHostname); NSParameterAssert(serverTrust); - NSParameterAssert(notedHostname); self = [super init]; if (self) { _serverHostname = serverHostname; _serverTrust = serverTrust; - _notedHostname = notedHostname; _validationResult = validationResult; _finalTrustDecision = finalTrustDecision; _validationDuration = validationDuration; diff --git a/TrustKit/TSKPinningValidator_Private.h b/TrustKit/TSKPinningValidator_Private.h index 54f9f5f6..52bd2c68 100644 --- a/TrustKit/TSKPinningValidator_Private.h +++ b/TrustKit/TSKPinningValidator_Private.h @@ -24,10 +24,10 @@ @return Initialized instance */ - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains -hashCache:(TSKSPKIHashCache * _Nonnull)hashCache -ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors -validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue -validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result))validationResultHandler; + hashCache:(TSKSPKIHashCache * _Nonnull)hashCache + ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors + validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue + validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy))validationResultHandler; @end @@ -36,11 +36,10 @@ validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull r @interface TSKPinningValidatorResult (Internal) - (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHostname -serverTrust:(SecTrustRef _Nonnull)serverTrust -notedHostname:(NSString * _Nonnull)notedHostname -validationResult:(TSKTrustEvaluationResult)validationResult -finalTrustDecision:(TSKTrustDecision)finalTrustDecision -validationDuration:(NSTimeInterval)validationDuration; + serverTrust:(SecTrustRef _Nonnull)serverTrust + validationResult:(TSKTrustEvaluationResult)validationResult + finalTrustDecision:(TSKTrustDecision)finalTrustDecision + validationDuration:(NSTimeInterval)validationDuration; @end diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index b8e51100..a42a6a7d 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -165,15 +165,18 @@ NS_ASSUME_NONNULL_BEGIN certificate chain; if the server's hostname is not defined in the pinning policy, no invocations will result as no pinning validation was performed. - The callback provides the `TSKPinningValidatorResult` resulting from the validation, and can be - used for advanced features such as performance measurement or customizing the reporting mechanism. - Hence, most Apps should not have to use this callback. If set, the callback may be invoked very - frequently and is not a suitable place for expensive tasks. + The callback provides the `TSKPinningValidatorResult` resulting from the validation of the server's + identity. It also returns the notedHostname, which is the entry within the SSL pinning configuration + that was used for the server being validated. This policy is provided as the notedHostnamePinningPolicy. + + The callback can be used for advanced features such as performance measurement or customizing the + reporting mechanism. Hence, most Apps should not have to use this callback. If set, the callback may + be invoked very frequently and is not a suitable place for expensive tasks. Lastly, the callback is always invoked after the validation has been completed, and therefore cannot be used to modify the result of the validation (for example to accept invalid certificates). */ -@property (nonatomic, nullable) void(^validationDelegateCallback)(TSKPinningValidatorResult * _Nonnull result); +@property (nonatomic, nullable) void(^validationDelegateCallback)(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy); /** Queue on which to invoke `validationDelegateCallback` (if set). Default value is the main queue. @@ -181,18 +184,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, null_resettable) dispatch_queue_t validationDelegateQueue; -#pragma mark Additional Settings - - -/** - Retrieve the SSL pinning policy configured for this TrustKit instance. - - @return A dictionary with the current TrustKit configuration - */ -@property (nonatomic, readonly, nullable) NSDictionary *configuration; - - - #pragma mark Usage in Multi-Instance Mode /** diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 6783f326..5aecb433 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -39,14 +39,20 @@ // Email info@datatheorem.com if you need a free dashboard to see your App's reports static NSString * const kTSKDefaultReportUri = @"https://overmind.datatheorem.com/trustkit/report"; + #pragma mark TrustKit Initialization Helper Functions @interface TrustKit () - (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig isSingleton:(BOOL)isSingleton; + @property (nonatomic) TSKBackgroundReporter *pinFailureReporter; @property (nonatomic) dispatch_queue_t pinFailureReporterQueue; + +@property (nonatomic, readonly, nullable) NSDictionary *configuration; + @end + @implementation TrustKit #pragma mark Shared TrustKit Explicit Initialization @@ -150,22 +156,22 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo hashCache:sharedHashCache ignorePinsForUserTrustAnchors:userTrustAnchorBypass validationResultQueue:_pinFailureReporterQueue - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { typeof(self) strongSelf = weakSelf; if (!strongSelf) { return; } // Invoke client handler if set - void(^callback)(TSKPinningValidatorResult *) = strongSelf.validationDelegateCallback; + void(^callback)(TSKPinningValidatorResult *, NSString *, NSDictionary *) = strongSelf.validationDelegateCallback; if (callback) { dispatch_async(self.validationDelegateQueue, ^{ - callback(result); + callback(result, notedHostname, notedHostnamePinningPolicy); }); } // Send analytics report - [strongSelf sendValidationReport:result]; + [strongSelf sendValidationReport:result notedHostname:notedHostname pinningPolicy:notedHostnamePinningPolicy]; }]; TSKLog(@"Successfully initialized with configuration %@", _configuration); @@ -177,7 +183,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo #pragma mark Notification Handlers // The block which receives pin validation results and turns them into pin validation reports -- (void)sendValidationReport:(TSKPinningValidatorResult *)result +- (void)sendValidationReport:(TSKPinningValidatorResult *)result notedHostname:(NSString *)notedHostname pinningPolicy:(NSDictionary *)notedHostnamePinningPolicy { TSKTrustEvaluationResult validationResult = result.validationResult; @@ -188,14 +194,11 @@ - (void)sendValidationReport:(TSKPinningValidatorResult *)result if (validationResult != TSKPinValidationResultFailedUserDefinedTrustAnchor) #endif { - NSString *notedHostname = result.notedHostname; - NSDictionary *notedHostnameConfig = self.configuration[kTSKPinnedDomains][notedHostname]; - // Pin validation failed: retrieve the list of configured report URLs - NSMutableArray *reportUris = [NSMutableArray arrayWithArray:notedHostnameConfig[kTSKReportUris]]; + NSMutableArray *reportUris = [NSMutableArray arrayWithArray:notedHostnamePinningPolicy[kTSKReportUris]]; // Also enable the default reporting URL - if ([notedHostnameConfig[kTSKDisableDefaultReportUri] boolValue] == NO) + if ([notedHostnamePinningPolicy[kTSKDisableDefaultReportUri] boolValue] == NO) { [reportUris addObject:[NSURL URLWithString:kTSKDefaultReportUri]]; } @@ -208,11 +211,11 @@ - (void)sendValidationReport:(TSKPinningValidatorResult *)result certificateChain:result.certificateChain notedHostname:notedHostname reportURIs:reportUris - includeSubdomains:[notedHostnameConfig[kTSKIncludeSubdomains] boolValue] - enforcePinning:[notedHostnameConfig[kTSKEnforcePinning] boolValue] - knownPins:notedHostnameConfig[kTSKPublicKeyHashes] + includeSubdomains:[notedHostnamePinningPolicy[kTSKIncludeSubdomains] boolValue] + enforcePinning:[notedHostnamePinningPolicy[kTSKEnforcePinning] boolValue] + knownPins:notedHostnamePinningPolicy[kTSKPublicKeyHashes] validationResult:validationResult - expirationDate:notedHostnameConfig[kTSKExpirationDate]]; + expirationDate:notedHostnamePinningPolicy[kTSKExpirationDate]]; } } } diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 13338d70..268ada52 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -15,6 +15,7 @@ #import "../TrustKit/TSKTrustKitConfig.h" #import "../TrustKit/parse_configuration.h" #import "../TrustKit/TSKPinningValidatorResult.h" +#import "../TrustKit/TSKPinningValidator_Private.h" #import "../TrustKit/Pinning/TSKSPKIHashCache.h" #import "../TrustKit/Pinning/ssl_pin_verifier.h" @@ -147,14 +148,14 @@ - (void)testVerifyAgainstAnyPublicKey hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); - XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + XCTAssertEqualObjects(notedHostname, @"www.good.com"); [expectation fulfill]; }]; @@ -220,14 +221,14 @@ - (void)testVerifyAgainstIntermediateCAPublicKey hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); - XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + XCTAssertEqualObjects(notedHostname, @"www.good.com"); [expectation fulfill]; }]; @@ -287,14 +288,14 @@ - (void)testVerifyAgainstCAPublicKey hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); - XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + XCTAssertEqualObjects(notedHostname, @"www.good.com"); [expectation fulfill]; }]; @@ -350,14 +351,14 @@ - (void)testVerifyAgainstLeafPublicKey hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); - XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + XCTAssertEqualObjects(notedHostname, @"www.good.com"); [expectation fulfill]; }]; @@ -413,14 +414,14 @@ - (void)testVerifyAgainstBadPublicKey hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); - XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + XCTAssertEqualObjects(notedHostname, @"www.good.com"); [expectation fulfill]; }]; @@ -465,7 +466,7 @@ - (void)testVerifyAgainstBadPublicKeyPinsExpired hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTFail(@"Should not be invoked"); }]; @@ -516,14 +517,14 @@ - (void)testVerifyAgainstBadPublicKeyPinningNotEnforced hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); - XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + XCTAssertEqualObjects(notedHostname, @"www.good.com"); [expectation fulfill]; }]; @@ -581,14 +582,14 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); - XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + XCTAssertEqualObjects(notedHostname, @"www.good.com"); [expectation fulfill]; }]; @@ -646,14 +647,14 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedInvalidCertificateChain); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); - XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + XCTAssertEqualObjects(notedHostname, @"www.good.com"); [expectation fulfill]; }]; @@ -712,14 +713,14 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedInvalidCertificateChain); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); - XCTAssertEqualObjects(result.notedHostname, @"www.bad.com"); + XCTAssertEqualObjects(notedHostname, @"www.bad.com"); [expectation fulfill]; }]; @@ -777,14 +778,14 @@ - (void)testVerifyAgainstInjectedCaPublicKey hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); - XCTAssertEqualObjects(result.notedHostname, @"www.good.com"); + XCTAssertEqualObjects(notedHostname, @"www.good.com"); [expectation fulfill]; }]; @@ -826,7 +827,7 @@ - (void)testDomainNotPinned hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTFail(@"Should not invoke callback"); }]; @@ -915,7 +916,7 @@ -(void) testHandleChallengeCompletionHandlerPinningFailed hashCache:spkiCache ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { // }]; @@ -972,7 +973,7 @@ -(void) testHandleChallengeCompletionHandlerPinningSuccessful hashCache:spkiCache ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result) { + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { // }]; @@ -1110,7 +1111,7 @@ - (void)testAdditionalTrustAnchors hashCache:spkiCache ignorePinsForUserTrustAnchors:YES validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult *x) {}]; + validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) {}]; SecTrustRef(^createTestServerTrust)(void) = ^() { // Create the server trust for this chain diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m index 4a5a3864..a51b9afc 100644 --- a/TrustKitTests/TSKReporterTests.m +++ b/TrustKitTests/TSKReporterTests.m @@ -15,6 +15,7 @@ #import "../TrustKit/TSKTrustKitConfig.h" #import "../TrustKit/TSKPinningValidatorResult.h" +#import "../TrustKit/TSKPinningValidator_Private.h" #import "../TrustKit/Reporting/TSKBackgroundReporter.h" #import "../TrustKit/Reporting/TSKPinFailureReport.h" #import "../TrustKit/Reporting/reporting_utils.h" @@ -27,7 +28,9 @@ @interface TrustKit (TestSupport) @property (nonatomic) TSKBackgroundReporter *pinFailureReporter; -- (void)sendValidationReport:(TSKPinningValidatorResult *)result; +@property (nonatomic, readonly, nullable) NSDictionary *configuration; + +- (void)sendValidationReport:(TSKPinningValidatorResult *)result notedHostname:(NSString *)notedHostname pinningPolicy:(NSDictionary *)notedHostnamePinningPolicy; @end @@ -130,11 +133,10 @@ - (void)testSendReportFromValidationReport res = [[TSKPinningValidatorResult alloc] initWithServerHostname:@"www.test.com" serverTrust:_testTrust - notedHostname:@"www.test.com" validationResult:TSKTrustEvaluationErrorCouldNotGenerateSpkiHash finalTrustDecision:TSKTrustDecisionShouldBlockConnection validationDuration:1.0]; - [_trustKit sendValidationReport:res]; + [_trustKit sendValidationReport:res notedHostname:@"www.test.com" pinningPolicy:_trustKit.configuration[kTSKPinnedDomains][@"www.test.com"]]; // Ensure that the reporter was called [pinReporterMock verify]; @@ -149,13 +151,12 @@ - (void)testSendReportFromValidationReport res = [[TSKPinningValidatorResult alloc] initWithServerHostname:@"www.test.com" serverTrust:_testTrust - notedHostname:@"www.test.com" validationResult:TSKTrustEvaluationSuccess finalTrustDecision:TSKTrustDecisionShouldAllowConnection validationDuration:1.0]; // Ensure that the reporter was NOT called - [_trustKit sendValidationReport:res]; + [_trustKit sendValidationReport:res notedHostname:@"www.test.com" pinningPolicy:_trustKit.configuration[kTSKPinnedDomains][@"www.test.com"]]; [pinReporterMock verify]; [pinReporterMock stopMocking]; @@ -169,13 +170,12 @@ - (void)testSendReportFromValidationReport res = [[TSKPinningValidatorResult alloc] initWithServerHostname:@"www.test.com" serverTrust:_testTrust - notedHostname:@"www.test.com" validationResult:TSKTrustEvaluationFailedUserDefinedTrustAnchor finalTrustDecision:TSKTrustDecisionShouldAllowConnection validationDuration:1.0]; // Ensure that the reporter was NOT called - [_trustKit sendValidationReport:res]; + [_trustKit sendValidationReport:res notedHostname:@"www.test.com" pinningPolicy:_trustKit.configuration[kTSKPinnedDomains][@"www.test.com"]]; [pinReporterMock verify]; [pinReporterMock stopMocking]; pinReporterMock = nil; From f3aea09231c7a7018f8871f367435380ed0b5f6f Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 11:49:08 -0700 Subject: [PATCH 080/126] Move the callback stuff to a separate file and clarify names --- .jazzy.yaml | 2 + TrustKit.xcodeproj/project.pbxproj | 2 + TrustKit/TSKPinningValidator.h | 4 +- TrustKit/TSKPinningValidator.m | 22 ++++----- TrustKit/TSKPinningValidatorCallback.h | 45 ++++++++++++++++++ TrustKit/TSKPinningValidatorResult.h | 1 + TrustKit/TSKPinningValidator_Private.h | 9 ++-- TrustKit/TrustKit.h | 27 ++--------- TrustKit/TrustKit.m | 42 ++++++++--------- TrustKitTests/TSKPinningValidatorTests.m | 60 ++++++++++++------------ 10 files changed, 123 insertions(+), 91 deletions(-) create mode 100644 TrustKit/TSKPinningValidatorCallback.h diff --git a/.jazzy.yaml b/.jazzy.yaml index 65bb975f..efe7cfff 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -25,6 +25,8 @@ custom_categories: - name: Receiving Validation Notifications children: + - TSKPinningValidatorCallback + - TKSDomainPinningPolicy - TSKPinningValidatorResult - TSKTrustEvaluationResult diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 8b969355..aab92c9c 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -394,6 +394,7 @@ 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ssl_pin_verifier.h; path = Pinning/ssl_pin_verifier.h; sourceTree = ""; }; 8CE919291AEA0F7E002B29AE /* domain_registry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = domain_registry.h; path = Dependencies/domain_registry/domain_registry.h; sourceTree = ""; }; 8CF27A911EFDE7D9009369B0 /* TSKPinningValidator_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidator_Private.h; sourceTree = ""; }; + 8CF27A921F0185EC009369B0 /* TSKPinningValidatorCallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorCallback.h; sourceTree = ""; }; FC1A08FD1E579F630055B12C /* TSKTrustDecision.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustDecision.h; sourceTree = ""; }; FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; @@ -574,6 +575,7 @@ FCE7D62C1EE9F6610081EEEF /* TSKTrustKitConfig.m */, 8C0C90471E3C41F3003851A8 /* TSKPinningValidator.h */, 8C0C90481E3C41F3003851A8 /* TSKPinningValidator.m */, + 8CF27A921F0185EC009369B0 /* TSKPinningValidatorCallback.h */, 8CF27A911EFDE7D9009369B0 /* TSKPinningValidator_Private.h */, FC1A08FD1E579F630055B12C /* TSKTrustDecision.h */, FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */, diff --git a/TrustKit/TSKPinningValidator.h b/TrustKit/TSKPinningValidator.h index eca7e272..d4dd4615 100644 --- a/TrustKit/TSKPinningValidator.h +++ b/TrustKit/TSKPinningValidator.h @@ -31,7 +31,7 @@ */ @interface TSKPinningValidator : NSObject -#pragma mark High Level Validation Method +#pragma mark High-level Validation Method /** Helper method for handling authentication challenges received within a `NSURLSessionDelegate`, `NSURLSessionTaskDelegate` or `WKNavigationDelegate`. @@ -66,7 +66,7 @@ NSURLCredential * _Nullable credential))completionHandler; -#pragma mark Low Level Validation Method +#pragma mark Low-level Validation Method /** Evaluate the supplied server trust against the SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent. diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index 768bd45f..7bbdb89e 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -47,14 +47,14 @@ to platforms that support a user trust store (Mac OS). @property (nonatomic, readonly) BOOL ignorePinsForUserTrustAnchors; /** - The queue use when invoking the validationResultHandler + The callback invoked with validation results. */ -@property (nonatomic, readonly, nonnull) dispatch_queue_t validationResultQueue; +@property (nonatomic, readonly, nonnull) TSKPinningValidatorCallback validationCallback; /** - The callback invoked with validation results + The queue use when invoking the `validationCallback`. */ -@property (nonatomic, readonly, nonnull) void(^validationResultHandler)(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy); +@property (nonatomic, readonly, nonnull) dispatch_queue_t validationCallbackQueue; @end @@ -77,15 +77,15 @@ + (BOOL)allowsAdditionalTrustAnchors - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains hashCache:(TSKSPKIHashCache * _Nonnull)hashCache ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors - validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue - validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy))validationResultHandler + validationCallbackQueue:(dispatch_queue_t _Nonnull)validationCallbackQueue + validationCallback:(TSKPinningValidatorCallback)validationCallback { self = [super init]; if (self) { _pinnedDomains = pinnedDomains; _ignorePinsForUserTrustAnchors = ignorePinsForUserTrustAnchors; - _validationResultQueue = validationResultQueue; - _validationResultHandler = validationResultHandler; + _validationCallbackQueue = validationCallbackQueue; + _validationCallback = validationCallback; _spkiHashCache = hashCache; } return self; @@ -193,15 +193,15 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: } // Send a notification after all validation is done; this will also trigger a report if pin validation failed - if (self.validationResultQueue && self.validationResultHandler) { + if (self.validationCallbackQueue && self.validationCallback) { NSTimeInterval validationDuration = [NSDate timeIntervalSinceReferenceDate] - validationStartTime; TSKPinningValidatorResult *result = [[TSKPinningValidatorResult alloc] initWithServerHostname:serverHostname serverTrust:serverTrust validationResult:validationResult finalTrustDecision:finalTrustDecision validationDuration:validationDuration]; - dispatch_async(self.validationResultQueue, ^{ - self.validationResultHandler(result, domainConfigKey, domainConfig); + dispatch_async(self.validationCallbackQueue, ^{ + self.validationCallback(result, domainConfigKey, domainConfig); }); } } diff --git a/TrustKit/TSKPinningValidatorCallback.h b/TrustKit/TSKPinningValidatorCallback.h new file mode 100644 index 00000000..667a52b7 --- /dev/null +++ b/TrustKit/TSKPinningValidatorCallback.h @@ -0,0 +1,45 @@ +// +// TSKPinningValidatorCallback.h +// TrustKit +// +// Created by Alban Diquet on 6/26/17. +// Copyright © 2017 TrustKit. All rights reserved. +// + +#ifndef TSKPinningValidatorCallback_h +#define TSKPinningValidatorCallback_h + + +/** + The pinning policy set for a specific hostname. + */ +typedef NSDictionary TKSDomainPinningPolicy; + + +/** + A block that can be set in a `TrustKit` instance to be invoked for every request that is going through + instance's pinning validation logic. + + The callback will be invoked every time the validator performs pinning validation against a server's + certificate chain; if the server's hostname is not defined in the pinning policy, no invocations will + result as no pinning validation was performed. + + The callback provides the following arguments: + + * The `TSKPinningValidatorResult` resulting from the validation of the server's identity. + * The `notedHostname`, which is the entry within the SSL pinning configuration that was used for the + server being validated. + * The `notedHostname`'s pinning policy, which was used for the server being validated. + + The callback can be used for advanced features such as performance measurement or customizing the + reporting mechanism. Hence, most Apps should not have to use this callback. If set, the callback may + be invoked very frequently and is not a suitable place for expensive tasks. + + Lastly, the callback is always invoked after the validation has been completed, and therefore + cannot be used to modify the result of the validation (for example to accept invalid certificates). + */ +typedef void (^TSKPinningValidatorCallback)(TSKPinningValidatorResult * _Nonnull, NSString * _Nonnull, TKSDomainPinningPolicy * _Nonnull); + + + +#endif /* TSKPinningValidatorCallback_h */ diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h index c14fddcd..a2798e1c 100644 --- a/TrustKit/TSKPinningValidatorResult.h +++ b/TrustKit/TSKPinningValidatorResult.h @@ -12,6 +12,7 @@ #import "TSKTrustDecision.h" @import Foundation; + /** A `TSKPinningValidatorResult` instance contains all the details regarding a pinning validation performed against a specific server. diff --git a/TrustKit/TSKPinningValidator_Private.h b/TrustKit/TSKPinningValidator_Private.h index 52bd2c68..6158a803 100644 --- a/TrustKit/TSKPinningValidator_Private.h +++ b/TrustKit/TSKPinningValidator_Private.h @@ -19,20 +19,19 @@ @param pinnedDomains Domain pinning configuration, typically obtained by parseTrustKitConfiguration() @param hashCache The hash cache to use. If nil, no caching is performed, performance may suffer. @param ignorePinsForUserTrustAnchors Set to true to ignore the trust anchors in the user trust store - @param validationResultQueue The queue used when invoking the validationResultHandler - @param validationResultHandler The callback invoked with validation results + @param validationCallbackQueue The queue used when invoking the validationResultHandler + @param validationCallback The callback invoked with validation results @return Initialized instance */ - (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains hashCache:(TSKSPKIHashCache * _Nonnull)hashCache ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors - validationResultQueue:(dispatch_queue_t _Nonnull)validationResultQueue - validationResultHandler:(void(^ _Nonnull)(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy))validationResultHandler; + validationCallbackQueue:(dispatch_queue_t _Nonnull)validationCallbackQueue + validationCallback:(TSKPinningValidatorCallback _Nonnull)validationCallback; @end - @interface TSKPinningValidatorResult (Internal) - (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHostname diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index a42a6a7d..c6f4463f 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -15,6 +15,7 @@ #define _TRUSTKIT_ #import "TSKTrustKitConfig.h" #import "TSKPinningValidatorResult.h" + #import "TSKPinningValidatorCallback.h" #import "TSKPinningValidator.h" #import "TSKTrustDecision.h" #endif /* _TRUSTKIT_ */ @@ -111,9 +112,6 @@ NS_ASSUME_NONNULL_BEGIN TrustKit.initialize(withConfiguration:trustKitConfig) ``` - The various configuration keys that can be specified in the policy are described in the - "Constants" section of the documentation. - After initialization, the `TrustKit` instance's `pinningValidator` should be used to implement pinning validation within the App's network authentication handlers. */ @@ -159,29 +157,14 @@ NS_ASSUME_NONNULL_BEGIN /** Register a block to be invoked for every request that is going through TrustKit's pinning - validation mechanism. - - The callback will be invoked every time the validator performs pinning validation against a server's - certificate chain; if the server's hostname is not defined in the pinning policy, no invocations will - result as no pinning validation was performed. - - The callback provides the `TSKPinningValidatorResult` resulting from the validation of the server's - identity. It also returns the notedHostname, which is the entry within the SSL pinning configuration - that was used for the server being validated. This policy is provided as the notedHostnamePinningPolicy. - - The callback can be used for advanced features such as performance measurement or customizing the - reporting mechanism. Hence, most Apps should not have to use this callback. If set, the callback may - be invoked very frequently and is not a suitable place for expensive tasks. - - Lastly, the callback is always invoked after the validation has been completed, and therefore - cannot be used to modify the result of the validation (for example to accept invalid certificates). + validation mechanism. See `TSKPinningValidatorCallback` for more information. */ -@property (nonatomic, nullable) void(^validationDelegateCallback)(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy); +@property (nonatomic, nullable) TSKPinningValidatorCallback pinningValidatorCallback; /** - Queue on which to invoke `validationDelegateCallback` (if set). Default value is the main queue. + Queue on which to invoke the `pinningValidatorCallback`; default value is the main queue. */ -@property (nonatomic, null_resettable) dispatch_queue_t validationDelegateQueue; +@property (nonatomic, null_resettable) dispatch_queue_t pinningValidatorCallbackQueue; #pragma mark Usage in Multi-Instance Mode diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 5aecb433..74672bab 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -108,7 +108,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo // Convert and store the SSL pins in our global variable _configuration = parseTrustKitConfiguration(trustKitConfig); - _validationDelegateQueue = dispatch_get_main_queue(); + _pinningValidatorCallbackQueue = dispatch_get_main_queue(); // Create a dispatch queue for activating the reporter // We use a serial queue targetting the global default queue in order to ensure reports are sent one by one @@ -155,24 +155,24 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo _pinningValidator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:_configuration hashCache:sharedHashCache ignorePinsForUserTrustAnchors:userTrustAnchorBypass - validationResultQueue:_pinFailureReporterQueue - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { - typeof(self) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - - // Invoke client handler if set - void(^callback)(TSKPinningValidatorResult *, NSString *, NSDictionary *) = strongSelf.validationDelegateCallback; - if (callback) { - dispatch_async(self.validationDelegateQueue, ^{ - callback(result, notedHostname, notedHostnamePinningPolicy); - }); - } - - // Send analytics report - [strongSelf sendValidationReport:result notedHostname:notedHostname pinningPolicy:notedHostnamePinningPolicy]; - }]; + validationCallbackQueue:_pinFailureReporterQueue + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, TKSDomainPinningPolicy *_Nonnull notedHostnamePinningPolicy) { + typeof(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + + // Invoke client handler if set + TSKPinningValidatorCallback userDefinedCallback = strongSelf.pinningValidatorCallback; + if (userDefinedCallback) { + dispatch_async(self.pinningValidatorCallbackQueue, ^{ + userDefinedCallback(result, notedHostname, notedHostnamePinningPolicy); + }); + } + + // Send analytics report + [strongSelf sendValidationReport:result notedHostname:notedHostname pinningPolicy:notedHostnamePinningPolicy]; + }]; TSKLog(@"Successfully initialized with configuration %@", _configuration); } @@ -221,9 +221,9 @@ - (void)sendValidationReport:(TSKPinningValidatorResult *)result notedHostname:( } } -- (void)setValidationDelegateQueue:(dispatch_queue_t)validationDelegateQueue +- (void)setPinningValidatorCallbackQueue:(dispatch_queue_t)pinningValidatorCallbackQueue { - _validationDelegateQueue = validationDelegateQueue ?: dispatch_get_main_queue(); + _pinningValidatorCallbackQueue = pinningValidatorCallbackQueue ?: dispatch_get_main_queue(); } @end diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 268ada52..576bda28 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -147,8 +147,8 @@ - (void)testVerifyAgainstAnyPublicKey validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:NO - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); @@ -220,8 +220,8 @@ - (void)testVerifyAgainstIntermediateCAPublicKey validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:NO - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); @@ -287,8 +287,8 @@ - (void)testVerifyAgainstCAPublicKey validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:NO - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); @@ -350,8 +350,8 @@ - (void)testVerifyAgainstLeafPublicKey validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:NO - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); @@ -413,8 +413,8 @@ - (void)testVerifyAgainstBadPublicKey validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:NO - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); @@ -465,8 +465,8 @@ - (void)testVerifyAgainstBadPublicKeyPinsExpired validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:NO - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTFail(@"Should not be invoked"); }]; @@ -516,8 +516,8 @@ - (void)testVerifyAgainstBadPublicKeyPinningNotEnforced validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:NO - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); @@ -581,8 +581,8 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:NO - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); @@ -646,8 +646,8 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:NO - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedInvalidCertificateChain); @@ -712,8 +712,8 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:NO - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedInvalidCertificateChain); @@ -777,8 +777,8 @@ - (void)testVerifyAgainstInjectedCaPublicKey validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:NO - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); @@ -826,8 +826,8 @@ - (void)testDomainNotPinned validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:NO - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTFail(@"Should not invoke callback"); }]; @@ -915,8 +915,8 @@ -(void) testHandleChallengeCompletionHandlerPinningFailed TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:YES - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { // }]; @@ -972,8 +972,8 @@ -(void) testHandleChallengeCompletionHandlerPinningSuccessful TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig hashCache:spkiCache ignorePinsForUserTrustAnchors:YES - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { // }]; @@ -1110,8 +1110,8 @@ - (void)testAdditionalTrustAnchors TestPinningValidator *pinningValidator = [[TestPinningValidator alloc] initWithPinnedDomainConfig:config hashCache:spkiCache ignorePinsForUserTrustAnchors:YES - validationResultQueue:dispatch_get_main_queue() - validationResultHandler:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) {}]; + validationCallbackQueue:dispatch_get_main_queue() + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) {}]; SecTrustRef(^createTestServerTrust)(void) = ^() { // Create the server trust for this chain From 07e54886765dc9502c2365c4e40b801af2f33759 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 12:54:09 -0700 Subject: [PATCH 081/126] Simplify TSKPinningValidator API and remove redundant properties --- TrustKit/TSKPinningValidator.m | 20 ++++---- TrustKit/TSKPinningValidatorCallback.h | 2 + TrustKit/TSKPinningValidator_Private.h | 12 ++--- TrustKit/TrustKit.m | 42 +++++++-------- TrustKitTests/TSKPinConfigurationTests.m | 65 +++++++++--------------- TrustKitTests/TSKPinningValidatorTests.m | 36 ++++++++----- 6 files changed, 86 insertions(+), 91 deletions(-) diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index 7bbdb89e..f7b6c6bf 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -36,9 +36,9 @@ @interface TSKPinningValidator () @property (nonatomic, class, readonly) BOOL allowsAdditionalTrustAnchors; /** - Domain pinning configuration, typically obtained by parseTrustKitConfiguration() + The dictionary of domains that were configured and their corresponding pinning policy. */ -@property (nonatomic, readonly, nullable) NSDictionary *pinnedDomains; +@property (nonatomic, readonly, nonnull) NSDictionary *domainPinningPolicies; /** Set to true to ignore the trust anchors in the user trust store. Only applicable @@ -74,15 +74,15 @@ + (BOOL)allowsAdditionalTrustAnchors #pragma mark Instance Methods -- (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains - hashCache:(TSKSPKIHashCache * _Nonnull)hashCache - ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors - validationCallbackQueue:(dispatch_queue_t _Nonnull)validationCallbackQueue - validationCallback:(TSKPinningValidatorCallback)validationCallback +- (instancetype _Nullable)initWithDomainPinningPolicies:(NSDictionary * _Nonnull)domainPinningPolicies + hashCache:(TSKSPKIHashCache * _Nonnull)hashCache + ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors + validationCallbackQueue:(dispatch_queue_t _Nonnull)validationCallbackQueue + validationCallback:(TSKPinningValidatorCallback)validationCallback { self = [super init]; if (self) { - _pinnedDomains = pinnedDomains; + _domainPinningPolicies = domainPinningPolicies; _ignorePinsForUserTrustAnchors = ignorePinsForUserTrustAnchors; _validationCallbackQueue = validationCallbackQueue; _validationCallback = validationCallback; @@ -106,7 +106,7 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: NSTimeInterval validationStartTime = [NSDate timeIntervalSinceReferenceDate]; // Retrieve the pinning configuration for this specific domain, if there is one - NSString *domainConfigKey = getPinningConfigurationKeyForDomain(serverHostname, self.pinnedDomains); + NSString *domainConfigKey = getPinningConfigurationKeyForDomain(serverHostname, self.domainPinningPolicies); if (domainConfigKey == nil) { // The domain has no pinning policy: nothing to do/validate @@ -115,7 +115,7 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: else { // This domain has a pinning policy - NSDictionary *domainConfig = self.pinnedDomains[kTSKPinnedDomains][domainConfigKey]; + NSDictionary *domainConfig = self.domainPinningPolicies[domainConfigKey]; // Has the pinning policy expired? NSDate *expirationDate = domainConfig[kTSKExpirationDate]; diff --git a/TrustKit/TSKPinningValidatorCallback.h b/TrustKit/TSKPinningValidatorCallback.h index 667a52b7..14139718 100644 --- a/TrustKit/TSKPinningValidatorCallback.h +++ b/TrustKit/TSKPinningValidatorCallback.h @@ -9,6 +9,8 @@ #ifndef TSKPinningValidatorCallback_h #define TSKPinningValidatorCallback_h +#import "TSKPinningValidatorResult.h" +#import "TSKTrustKitConfig.h" /** The pinning policy set for a specific hostname. diff --git a/TrustKit/TSKPinningValidator_Private.h b/TrustKit/TSKPinningValidator_Private.h index 6158a803..9251d244 100644 --- a/TrustKit/TSKPinningValidator_Private.h +++ b/TrustKit/TSKPinningValidator_Private.h @@ -16,18 +16,18 @@ /** Initialize an instance of TSKPinningValidator. - @param pinnedDomains Domain pinning configuration, typically obtained by parseTrustKitConfiguration() + @param domainPinningPolicies A dictionnary of domains and the corresponding pinning policy. @param hashCache The hash cache to use. If nil, no caching is performed, performance may suffer. @param ignorePinsForUserTrustAnchors Set to true to ignore the trust anchors in the user trust store @param validationCallbackQueue The queue used when invoking the validationResultHandler @param validationCallback The callback invoked with validation results @return Initialized instance */ -- (instancetype _Nullable)initWithPinnedDomainConfig:(NSDictionary * _Nullable)pinnedDomains - hashCache:(TSKSPKIHashCache * _Nonnull)hashCache - ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors - validationCallbackQueue:(dispatch_queue_t _Nonnull)validationCallbackQueue - validationCallback:(TSKPinningValidatorCallback _Nonnull)validationCallback; +- (instancetype _Nullable)initWithDomainPinningPolicies:(NSDictionary *_Nonnull)domainPinningPolicies + hashCache:(TSKSPKIHashCache * _Nonnull)hashCache + ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors + validationCallbackQueue:(dispatch_queue_t _Nonnull)validationCallbackQueue + validationCallback:(TSKPinningValidatorCallback _Nonnull)validationCallback; @end diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 74672bab..d4879d11 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -152,27 +152,27 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo }); __weak typeof(self) weakSelf = self; - _pinningValidator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:_configuration - hashCache:sharedHashCache - ignorePinsForUserTrustAnchors:userTrustAnchorBypass - validationCallbackQueue:_pinFailureReporterQueue - validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, TKSDomainPinningPolicy *_Nonnull notedHostnamePinningPolicy) { - typeof(self) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - - // Invoke client handler if set - TSKPinningValidatorCallback userDefinedCallback = strongSelf.pinningValidatorCallback; - if (userDefinedCallback) { - dispatch_async(self.pinningValidatorCallbackQueue, ^{ - userDefinedCallback(result, notedHostname, notedHostnamePinningPolicy); - }); - } - - // Send analytics report - [strongSelf sendValidationReport:result notedHostname:notedHostname pinningPolicy:notedHostnamePinningPolicy]; - }]; + _pinningValidator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:_configuration[kTSKPinnedDomains] + hashCache:sharedHashCache + ignorePinsForUserTrustAnchors:userTrustAnchorBypass + validationCallbackQueue:_pinFailureReporterQueue + validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, TKSDomainPinningPolicy *_Nonnull notedHostnamePinningPolicy) { + typeof(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + + // Invoke client handler if set + TSKPinningValidatorCallback userDefinedCallback = strongSelf.pinningValidatorCallback; + if (userDefinedCallback) { + dispatch_async(self.pinningValidatorCallbackQueue, ^{ + userDefinedCallback(result, notedHostname, notedHostnamePinningPolicy); + }); + } + + // Send analytics report + [strongSelf sendValidationReport:result notedHostname:notedHostname pinningPolicy:notedHostnamePinningPolicy]; + }]; TSKLog(@"Successfully initialized with configuration %@", _configuration); } diff --git a/TrustKitTests/TSKPinConfigurationTests.m b/TrustKitTests/TSKPinConfigurationTests.m index 2049515a..08294c1c 100644 --- a/TrustKitTests/TSKPinConfigurationTests.m +++ b/TrustKitTests/TSKPinConfigurationTests.m @@ -38,8 +38,7 @@ - (void)tearDown // Pin to only one key and ensure it fails; TrustKit requires at least two pins (which should include a backup pin) - (void)testPinOnePublicKey { - XCTAssertThrows(parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : @{ + XCTAssertThrows(parseTrustKitConfiguration(@{kTSKPinnedDomains : @{ @"www.good.com" : @{ kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", @@ -52,8 +51,7 @@ - (void)testPinOnePublicKey - (void)testDisablePinningForSubdomainAndNoPublicKey { NSDictionary *trustKitConfig; - trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : @{ + trustKitConfig = parseTrustKitConfiguration(@{kTSKPinnedDomains : @{ @"good.com" : @{ kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", @@ -67,15 +65,14 @@ - (void)testDisablePinningForSubdomainAndNoPublicKey } }); - NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"unsecured.good.com", trustKitConfig); + NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"unsecured.good.com", trustKitConfig[kTSKPinnedDomains]); XCTAssertEqualObjects(serverConfigKey, @"unsecured.good.com", @"Did not receive a configuration for pinned subdomain"); } - (void)testDisablePinningForSubdomainWithoutParentAndNoPublicKey { - XCTAssertThrows(parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : @{ + XCTAssertThrows(parseTrustKitConfiguration(@{kTSKPinnedDomains : @{ @"good.com" : @{ kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", @@ -94,8 +91,7 @@ - (void)testDisablePinningForSubdomainWithoutParentAndNoPublicKey - (void)testDisablePinningForSubdomainAdditionalDomainKeys { - XCTAssertThrows(parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : @{ + XCTAssertThrows(parseTrustKitConfiguration(@{kTSKPinnedDomains : @{ @"good.com" : @{ kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", @@ -128,15 +124,14 @@ - (void)testNokTSKSwizzleNetworkDelegates - (void)testGetConfigurationPinningEnabled { NSDictionary *trustKitConfig; - trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : @{ + trustKitConfig = parseTrustKitConfiguration(@{kTSKPinnedDomains : @{ @"www.good.com" : @{ kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key ]}}}); - NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig); + NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig[kTSKPinnedDomains]); XCTAssertEqualObjects(serverConfigKey, @"www.good.com", @"Did not receive a configuration for a pinned domain"); // Validate the content of the config @@ -151,8 +146,7 @@ - (void)testGetConfigurationPinningEnabledWithExpirationDate { NSString *expirationDateStr = @"2015-01-01"; NSDictionary *trustKitConfig; - trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : @{ + trustKitConfig = parseTrustKitConfiguration(@{kTSKPinnedDomains : @{ @"www.good.com" : @{ kTSKExpirationDate: expirationDateStr, kTSKPublicKeyAlgorithms : @[kTSKAlgorithmEcDsaSecp384r1], @@ -160,7 +154,7 @@ - (void)testGetConfigurationPinningEnabledWithExpirationDate @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key ]}}}); - NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig); + NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig[kTSKPinnedDomains]); XCTAssertEqualObjects(serverConfigKey, @"www.good.com", @"Did not receive a configuration for a pinned domain"); // Validate the content of the config @@ -178,8 +172,7 @@ - (void)testGetConfigurationPinningEnabledWithExpirationDate - (void)testGetConfigurationPinningDisabled { NSDictionary *trustKitConfig; - trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : @{ + trustKitConfig = parseTrustKitConfiguration(@{kTSKPinnedDomains : @{ @"good.com" : @{ kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", @@ -187,7 +180,7 @@ - (void)testGetConfigurationPinningDisabled ]}}}); // Ensure www.datatheorem.com gets no configuration - NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.datatheorem.com", trustKitConfig); + NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.datatheorem.com", trustKitConfig[kTSKPinnedDomains]); XCTAssertNil(serverConfigKey, @"Received a configuration a non-pinned domain"); } @@ -195,8 +188,7 @@ - (void)testGetConfigurationPinningDisabled - (void)testIncludeSubdomainsEnabled { NSDictionary *trustKitConfig; - trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : @{ + trustKitConfig = parseTrustKitConfiguration(@{kTSKPinnedDomains : @{ @"good.com" : @{ kTSKIncludeSubdomains : @YES, kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], @@ -205,7 +197,7 @@ - (void)testIncludeSubdomainsEnabled ]}}}); // Ensure www.good.com gets the configuration set for good.com as includeSubdomains is enabled - NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig); + NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig[kTSKPinnedDomains]); XCTAssertEqualObjects(serverConfigKey, @"good.com", @"IncludeSubdomains did not work"); } @@ -213,8 +205,7 @@ - (void)testIncludeSubdomainsEnabled - (void)testIncludeSubdomainsEnabledSameDomain { NSDictionary *trustKitConfig; - trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : @{@"good.com" : @{ + trustKitConfig = parseTrustKitConfiguration(@{kTSKPinnedDomains : @{@"good.com" : @{ kTSKIncludeSubdomains : @YES, kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", @@ -222,7 +213,7 @@ - (void)testIncludeSubdomainsEnabledSameDomain ]}}}); // Ensure good.com gets the configuration set for good.com as includeSubdomains is enabled - NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"good.com", trustKitConfig); + NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"good.com", trustKitConfig[kTSKPinnedDomains]); XCTAssertEqualObjects(serverConfigKey, @"good.com", @"IncludeSubdomains did not work"); } @@ -230,8 +221,7 @@ - (void)testIncludeSubdomainsEnabledSameDomain - (void)testIncludeSubdomainsEnabledSubSubDomain { NSDictionary *trustKitConfig; - trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : + trustKitConfig = parseTrustKitConfiguration(@{kTSKPinnedDomains : @{@"www.good.com" : @{ kTSKIncludeSubdomains : @YES, kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], @@ -239,7 +229,7 @@ - (void)testIncludeSubdomainsEnabledSubSubDomain @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key ]}}}); - NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"sub.www.good.com.www.good.com", trustKitConfig); + NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"sub.www.good.com.www.good.com", trustKitConfig[kTSKPinnedDomains]); XCTAssertEqualObjects(serverConfigKey, @"www.good.com", @"IncludeSubdomains did not work"); } @@ -247,8 +237,7 @@ - (void)testIncludeSubdomainsEnabledSubSubDomain - (void)testIncludeSubdomainsEnabledNotSubdomain { NSDictionary *trustKitConfig; - trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : + trustKitConfig = parseTrustKitConfiguration(@{kTSKPinnedDomains : @{@"good.com" : @{ kTSKIncludeSubdomains : @YES, kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], @@ -257,7 +246,7 @@ - (void)testIncludeSubdomainsEnabledNotSubdomain ]}}}); // Corner case to ensure two different domains with similar strings don't get returned as subdomains - NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"good.com.otherdomain.com", trustKitConfig); + NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"good.com.otherdomain.com", trustKitConfig[kTSKPinnedDomains]); XCTAssertNil(serverConfigKey); } @@ -279,8 +268,7 @@ - (void)testIncludeSubdomainsEnabledForSuffix - (void)testIncludeSubdomainsDisabled { NSDictionary *trustKitConfig; - trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : + trustKitConfig = parseTrustKitConfiguration(@{kTSKPinnedDomains : @{@"good.com" : @{ kTSKIncludeSubdomains : @NO, kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], @@ -289,7 +277,7 @@ - (void)testIncludeSubdomainsDisabled ]}}}); // Ensure www.good.com does not get the configuration set for good.com - NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig); + NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig[kTSKPinnedDomains]); XCTAssertNil(serverConfigKey, @"IncludeSubdomains did not work"); } @@ -297,8 +285,7 @@ - (void)testIncludeSubdomainsDisabled - (void)testIncludeSubdomainsEnabledAndSpecificConfiguration { NSDictionary *trustKitConfig; - trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : + trustKitConfig = parseTrustKitConfiguration(@{kTSKPinnedDomains : @{@"good.com" : @{ kTSKIncludeSubdomains : @YES, kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], @@ -312,7 +299,7 @@ - (void)testIncludeSubdomainsEnabledAndSpecificConfiguration ]}}}); // Ensure the configuration specific to www.good.com takes precedence over the more general config for good.com - NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig); + NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig[kTSKPinnedDomains]); XCTAssertEqualObjects(serverConfigKey, @"www.good.com", @"IncludeSubdomains took precedence over a more specialized configuration"); } @@ -328,8 +315,7 @@ - (void)testNoPinnedDomains - (void)testGlobalSettings { NSDictionary *trustKitConfig; - trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : + trustKitConfig = parseTrustKitConfiguration(@{kTSKPinnedDomains : @{@"good.com" : @{ kTSKIncludeSubdomains : @YES, kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], @@ -352,8 +338,7 @@ - (void)testAdditionalTrustAnchors XCTAssertNotNil(base64pem); NSDictionary *trustKitConfig; - trustKitConfig = parseTrustKitConfiguration(@{kTSKSwizzleNetworkDelegates: @NO, - kTSKPinnedDomains : @{ + trustKitConfig = parseTrustKitConfiguration(@{kTSKPinnedDomains : @{ @"fake.yahoo.com": @{ kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 576bda28..3a713ea3 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -144,7 +144,7 @@ - (void)testVerifyAgainstAnyPublicKey XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; - validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationCallbackQueue:dispatch_get_main_queue() @@ -217,7 +217,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey // Then test TSKPinningValidator XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; - validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationCallbackQueue:dispatch_get_main_queue() @@ -284,7 +284,7 @@ - (void)testVerifyAgainstCAPublicKey // Then test TSKPinningValidator XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; - validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationCallbackQueue:dispatch_get_main_queue() @@ -347,7 +347,7 @@ - (void)testVerifyAgainstLeafPublicKey // Then test TSKPinningValidator XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; - validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationCallbackQueue:dispatch_get_main_queue() @@ -410,7 +410,7 @@ - (void)testVerifyAgainstBadPublicKey // Then test TSKPinningValidator XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; - validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationCallbackQueue:dispatch_get_main_queue() @@ -460,9 +460,11 @@ - (void)testVerifyAgainstBadPublicKeyPinsExpired @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Bad key 2 ]}}}; + NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); + // Test TSKPinningValidator TSKPinningValidator *validator; - validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig + validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationCallbackQueue:dispatch_get_main_queue() @@ -513,7 +515,7 @@ - (void)testVerifyAgainstBadPublicKeyPinningNotEnforced // Then test TSKPinningValidator XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; - validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationCallbackQueue:dispatch_get_main_queue() @@ -578,7 +580,7 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey // Then test TSKPinningValidator XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; - validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationCallbackQueue:dispatch_get_main_queue() @@ -643,7 +645,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain // Then test TSKPinningValidator XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; - validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationCallbackQueue:dispatch_get_main_queue() @@ -709,7 +711,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname // Then test TSKPinningValidator XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; - validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationCallbackQueue:dispatch_get_main_queue() @@ -774,7 +776,7 @@ - (void)testVerifyAgainstInjectedCaPublicKey // Then test TSKPinningValidator XCTestExpectation *expectation = [self expectationWithDescription:@"ValidationResultHandler"]; TSKPinningValidator *validator; - validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:parsedTrustKitConfig + validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationCallbackQueue:dispatch_get_main_queue() @@ -820,10 +822,11 @@ - (void)testDomainNotPinned @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key ]}}}; + NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); // Then test TSKPinningValidator TSKPinningValidator *validator; - validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig + validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:NO validationCallbackQueue:dispatch_get_main_queue() @@ -912,7 +915,9 @@ -(void) testHandleChallengeCompletionHandlerPinningFailed @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Fake key ]}}}; - TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig + NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); + + TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:YES validationCallbackQueue:dispatch_get_main_queue() @@ -969,7 +974,10 @@ -(void) testHandleChallengeCompletionHandlerPinningSuccessful @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Fake key ]}}}; - TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithPinnedDomainConfig:trustKitConfig + + NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(trustKitConfig); + + TSKPinningValidator *validator = [[TSKPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:YES validationCallbackQueue:dispatch_get_main_queue() From a79d56aabbfccf3c8a8f06b1f2bc31037f5b664a Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 13:00:26 -0700 Subject: [PATCH 082/126] Fix tests that could not work --- TrustKitTests/TSKPinningValidatorTests.m | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 3a713ea3..3fdb2d24 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -453,7 +453,7 @@ - (void)testVerifyAgainstBadPublicKeyPinsExpired kTSKPinnedDomains : @{@"www.good.com" : @{ // Totally expired - kTSKExpirationDate: [NSDate dateWithTimeIntervalSinceReferenceDate:0], + kTSKExpirationDate: @"2015-01-01", kTSKEnforcePinning: @YES, kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Bad Key @@ -1105,17 +1105,21 @@ - (void)testExcludedSubdomain - (void)testAdditionalTrustAnchors { // Load and set custom trust anchor - SecCertificateRef anchor = [TSKCertificateUtils createCertificateFromPem:@"anchor-ca.cert"]; - CFArrayRef anchors = CFArrayCreate(NULL, (const void **)&anchor, 1, &kCFTypeArrayCallBacks); - CFRelease(anchor); + NSBundle *bundle = [NSBundle bundleForClass:self.class]; + NSString *pemFilepath = [bundle pathForResource:@"anchor-ca.cert" ofType:@"pem"]; + NSString *anchorAsPem = [NSString stringWithContentsOfFile:pemFilepath usedEncoding:nil error:nil]; + NSArray *anchors = [NSArray arrayWithObject:anchorAsPem]; NSDictionary *config = @{ kTSKPinnedDomains : @{ @"fake.yahoo.com" : @{ - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0="], - kTSKAdditionalTrustAnchors : (__bridge_transfer NSArray *)anchors } } }; + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"H6ts7uvqaU077Y8GlTM7nP31ir2ykBr61+oKjuvKSt0=", + @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="], + kTSKAdditionalTrustAnchors : anchors} } }; - TestPinningValidator *pinningValidator = [[TestPinningValidator alloc] initWithPinnedDomainConfig:config + NSDictionary *parsedTrustKitConfig = parseTrustKitConfiguration(config); + + TestPinningValidator *pinningValidator = [[TestPinningValidator alloc] initWithDomainPinningPolicies:parsedTrustKitConfig[kTSKPinnedDomains] hashCache:spkiCache ignorePinsForUserTrustAnchors:YES validationCallbackQueue:dispatch_get_main_queue() From d8e5871aa05da038358d5782aa5d7be6066d6179 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 14:14:31 -0700 Subject: [PATCH 083/126] Rename variables --- TrustKit/TrustKit.m | 1 + TrustKit/configuration_utils.h | 4 +++- TrustKit/configuration_utils.m | 20 +++++++++----------- TrustKit/parse_configuration.h | 2 +- TrustKit/parse_configuration.m | 15 ++++++++------- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index d4879d11..7b3ad6aa 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -139,6 +139,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo if ((sharedTrustKit) && ([[sharedTrustKit configuration][kTSKSwizzleNetworkDelegates] boolValue] == YES)) { // Local instances cannot be used if a shared instance with swizzling enabled is used, to avoid double pinning validation + // There is a race condition here on whether the shared VS local instance get instantiated first, but better than nothing [NSException raise:@"TrustKit configuration invalid" format:@"Cannot use local TrustKit instances when the TrustKit sharedInstance has been initialized with kTSKSwizzleNetworkDelegates enabled"]; } diff --git a/TrustKit/configuration_utils.h b/TrustKit/configuration_utils.h index 163350cd..a5ba2255 100644 --- a/TrustKit/configuration_utils.h +++ b/TrustKit/configuration_utils.h @@ -6,7 +6,9 @@ // Copyright © 2017 TrustKit. All rights reserved. // +#import "TSKPinningValidatorCallback.h" + @import Foundation; // Figure out if a specific domain is pinned and retrieve this domain's configuration key; returns nil if no configuration was found -NSString * _Nullable getPinningConfigurationKeyForDomain(NSString * _Nonnull hostname, NSDictionary * _Nonnull trustKitConfiguration); +NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *domainPinningPolicies); diff --git a/TrustKit/configuration_utils.m b/TrustKit/configuration_utils.m index 61e690b5..3ded237d 100644 --- a/TrustKit/configuration_utils.m +++ b/TrustKit/configuration_utils.m @@ -39,19 +39,17 @@ static BOOL isSubdomain(NSString *domain, NSString *subdomain) } -NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *trustKitConfiguration) +NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *domainPinningPolicies) { - NSString *configHostname = nil; - NSDictionary *domainsPinningPolicy = trustKitConfiguration[kTSKPinnedDomains]; - - if (domainsPinningPolicy[hostname] == nil) + NSString *notedHostname = nil; + if (domainPinningPolicies[hostname] == nil) { // No pins explicitly configured for this domain // Look for an includeSubdomain pin that applies - for (NSString *pinnedServerName in domainsPinningPolicy) + for (NSString *pinnedServerName in domainPinningPolicies) { // Check each domain configured with the includeSubdomain flag - if ([domainsPinningPolicy[pinnedServerName][kTSKIncludeSubdomains] boolValue]) + if ([domainPinningPolicies[pinnedServerName][kTSKIncludeSubdomains] boolValue]) { // Is the server a subdomain of this pinned server? TSKLog(@"Checking includeSubdomains configuration for %@", pinnedServerName); @@ -59,7 +57,7 @@ static BOOL isSubdomain(NSString *domain, NSString *subdomain) { // Yes; let's use the parent domain's pinning configuration TSKLog(@"Applying includeSubdomains configuration from %@ to %@", pinnedServerName, hostname); - configHostname = pinnedServerName; + notedHostname = pinnedServerName; break; } } @@ -68,12 +66,12 @@ static BOOL isSubdomain(NSString *domain, NSString *subdomain) else { // This hostname has a pinnning configuration - configHostname = hostname; + notedHostname = hostname; } - if (configHostname == nil) + if (notedHostname == nil) { TSKLog(@"Domain %@ is not pinned", hostname); } - return configHostname; + return notedHostname; } diff --git a/TrustKit/parse_configuration.h b/TrustKit/parse_configuration.h index 633dec36..4a29fffd 100644 --- a/TrustKit/parse_configuration.h +++ b/TrustKit/parse_configuration.h @@ -11,6 +11,6 @@ @import Foundation; -NSDictionary *parseTrustKitConfiguration(NSDictionary *TrustKitArguments); +NSDictionary *parseTrustKitConfiguration(NSDictionary *trustKitArguments); #endif /* parse_configuration_h */ diff --git a/TrustKit/parse_configuration.m b/TrustKit/parse_configuration.m index 18417dc6..aa389d10 100644 --- a/TrustKit/parse_configuration.m +++ b/TrustKit/parse_configuration.m @@ -44,7 +44,7 @@ static SecCertificateRef certificateFromPEM(NSString *pem) } -NSDictionary *parseTrustKitConfiguration(NSDictionary *TrustKitArguments) +NSDictionary *parseTrustKitConfiguration(NSDictionary *trustKitArguments) { // Convert settings supplied by the user to a configuration dictionary that can be used by TrustKit // This includes checking the sanity of the settings and converting public key hashes/pins from an @@ -60,7 +60,7 @@ static SecCertificateRef certificateFromPEM(NSString *pem) // Retrieve global settings // Should we auto-swizzle network delegates - NSNumber *shouldSwizzleNetworkDelegates = TrustKitArguments[kTSKSwizzleNetworkDelegates]; + NSNumber *shouldSwizzleNetworkDelegates = trustKitArguments[kTSKSwizzleNetworkDelegates]; if (shouldSwizzleNetworkDelegates == nil) { // Default setting is NO @@ -74,7 +74,7 @@ static SecCertificateRef certificateFromPEM(NSString *pem) #if !TARGET_OS_IPHONE // OS X only: extract the optional ignorePinningForUserDefinedTrustAnchors setting - NSNumber *shouldIgnorePinningForUserDefinedTrustAnchors = TrustKitArguments[kTSKIgnorePinningForUserDefinedTrustAnchors]; + NSNumber *shouldIgnorePinningForUserDefinedTrustAnchors = trustKitArguments[kTSKIgnorePinningForUserDefinedTrustAnchors]; if (shouldIgnorePinningForUserDefinedTrustAnchors == nil) { // Default setting is YES @@ -87,14 +87,14 @@ static SecCertificateRef certificateFromPEM(NSString *pem) #endif // Retrieve the pinning policy for each domains - if ((TrustKitArguments[kTSKPinnedDomains] == nil) || ([TrustKitArguments[kTSKPinnedDomains] count] < 1)) + if ((trustKitArguments[kTSKPinnedDomains] == nil) || ([trustKitArguments[kTSKPinnedDomains] count] < 1)) { [NSException raise:@"TrustKit configuration invalid" format:@"TrustKit was initialized with no pinned domains. The configuration format has changed: ensure your domain pinning policies are under the TSKPinnedDomains key within TSKConfiguration."]; } - for (NSString *domainName in TrustKitArguments[kTSKPinnedDomains]) + for (NSString *domainName in trustKitArguments[kTSKPinnedDomains]) { // Sanity checks on the domain name if (GetRegistryLength([domainName UTF8String]) == 0) @@ -105,7 +105,7 @@ static SecCertificateRef certificateFromPEM(NSString *pem) // Retrieve the supplied arguments for this domain - NSDictionary *domainPinningPolicy = TrustKitArguments[kTSKPinnedDomains][domainName]; + NSDictionary *domainPinningPolicy = trustKitArguments[kTSKPinnedDomains][domainName]; NSMutableDictionary *domainFinalConfiguration = [[NSMutableDictionary alloc]init]; @@ -193,6 +193,7 @@ static SecCertificateRef certificateFromPEM(NSString *pem) domainFinalConfiguration[kTSKDisableDefaultReportUri] = @(NO); } + // Extract the optional additionalTrustAnchors setting NSArray *additionalTrustAnchors = domainPinningPolicy[kTSKAdditionalTrustAnchors]; if (additionalTrustAnchors) { @@ -303,7 +304,7 @@ static SecCertificateRef certificateFromPEM(NSString *pem) if ([finalConfiguration[kTSKPinnedDomains][domainName][kTSKExcludeSubdomainFromParentPolicy] boolValue]) { // To force the lookup of a parent domain, we append 'a' to this subdomain so we don't retrieve its policy - NSString *parentDomainConfigKey = getPinningConfigurationKeyForDomain([@"a" stringByAppendingString:domainName], finalConfiguration); + NSString *parentDomainConfigKey = getPinningConfigurationKeyForDomain([@"a" stringByAppendingString:domainName], finalConfiguration[kTSKPinnedDomains]); if (parentDomainConfigKey == nil) { [NSException raise:@"TrustKit configuration invalid" From d4f68a319a9210f506ee9174a4c4d47efcf97f70 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 14:14:55 -0700 Subject: [PATCH 084/126] Bring back ligther end-to-end tests --- TrustKit.xcodeproj/project.pbxproj | 8 + TrustKitTests/TSKEndToEndNSURLSessionTests.m | 305 +++++++++++++++++++ 2 files changed, 313 insertions(+) create mode 100644 TrustKitTests/TSKEndToEndNSURLSessionTests.m diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index aab92c9c..853e00be 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -221,6 +221,9 @@ 8CE919221AEA077F002B29AE /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */; }; 8CE919251AEA07C5002B29AE /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */; }; 8CE9192D1AEA0F7E002B29AE /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE919291AEA0F7E002B29AE /* domain_registry.h */; }; + 8CF27A941F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */; }; + 8CF27A951F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */; }; + 8CF27A961F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */; }; FC049B3A1EECD1B000FDC5F4 /* anchor-ca.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD051EECD19E00AB3D81 /* anchor-ca.cert.pem */; }; FC049B3B1EECD1B000FDC5F4 /* anchor-fake.yahoo.com.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD061EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem */; }; FC049B3C1EECD1B000FDC5F4 /* anchor-intermediate.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD071EECD19E00AB3D81 /* anchor-intermediate.cert.pem */; }; @@ -395,6 +398,7 @@ 8CE919291AEA0F7E002B29AE /* domain_registry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = domain_registry.h; path = Dependencies/domain_registry/domain_registry.h; sourceTree = ""; }; 8CF27A911EFDE7D9009369B0 /* TSKPinningValidator_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidator_Private.h; sourceTree = ""; }; 8CF27A921F0185EC009369B0 /* TSKPinningValidatorCallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorCallback.h; sourceTree = ""; }; + 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKEndToEndNSURLSessionTests.m; sourceTree = ""; }; FC1A08FD1E579F630055B12C /* TSKTrustDecision.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustDecision.h; sourceTree = ""; }; FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; @@ -593,6 +597,7 @@ FC4CAC791E958DC600DAC41E /* Reporting */, 070868B31ADFF68200E5AFDC /* Certificates */, 8CD5F7371BCB02A7005801D8 /* TSKNSURLConnectionTests.m */, + 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */, 8CD5F7561BCB7219005801D8 /* TSKNSURLSessionTests.m */, 2FA2868CAFECA46ADE0B6E3E /* TSKPinningValidatorTests.m */, 8C15F9A31B17564400F06C0E /* TSKPinConfigurationTests.m */, @@ -1246,6 +1251,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8CF27A941F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */, 8CD5F7381BCB02A7005801D8 /* TSKNSURLConnectionTests.m in Sources */, 8C15F9A41B17564400F06C0E /* TSKPinConfigurationTests.m in Sources */, 075AA1091AC985FD00178223 /* TSKPinningValidatorTests.m in Sources */, @@ -1288,6 +1294,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8CF27A961F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */, 8C84CBC31D6E1718009B3E7D /* TSKNSURLConnectionTests.m in Sources */, FC4CAC7D1E96891B00DAC41E /* TSKReportsRateLimiterTests.m in Sources */, 8C84CBC41D6E1718009B3E7D /* TSKPinConfigurationTests.m in Sources */, @@ -1355,6 +1362,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8CF27A951F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */, 8CD5F7391BCB02A7005801D8 /* TSKNSURLConnectionTests.m in Sources */, FC4CAC7C1E96891A00DAC41E /* TSKReportsRateLimiterTests.m in Sources */, 8CA6CC3A1BAE2C7C00BDA419 /* TSKReporterTests.m in Sources */, diff --git a/TrustKitTests/TSKEndToEndNSURLSessionTests.m b/TrustKitTests/TSKEndToEndNSURLSessionTests.m new file mode 100644 index 00000000..2f04f56b --- /dev/null +++ b/TrustKitTests/TSKEndToEndNSURLSessionTests.m @@ -0,0 +1,305 @@ +// +// TSKEndToEndNSURLSessionTests.m +// TrustKit +// +// Created by Alban Diquet on 10/11/15. +// Copyright © 2015 TrustKit. All rights reserved. +// + +#import +#import "../TrustKit/TrustKit.h" +#import "../TrustKit/TSKPinningValidator.h" +#import "../TrustKit/configuration_utils.h" +#import "../TrustKit/TSKPinningValidatorResult.h" + + +#pragma mark Test NSURLSession delegate + +@interface TestNSURLSessionDelegate : NSObject +{ + XCTestExpectation *testExpectation; +} +@property TSKPinningValidator *validator; +@property NSError *lastError; +@property NSURLResponse *lastResponse; + +@property BOOL wasAuthHandlerCalled; // Used to validate that the delegate's auth handler was called + + +- (instancetype)initWithValidator:(TSKPinningValidator *)validator + expectation:(XCTestExpectation *)expectation; + +- (void)URLSession:(NSURLSession * _Nonnull)session + task:(NSURLSessionTask * _Nonnull)task +didCompleteWithError:(NSError * _Nullable)error; + +- (void)URLSession:(NSURLSession * _Nonnull)session + dataTask:(NSURLSessionDataTask * _Nonnull)dataTask +didReceiveResponse:(NSURLResponse * _Nonnull)response + completionHandler:(void (^ _Nonnull)(NSURLSessionResponseDisposition disposition))completionHandler; + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +willPerformHTTPRedirection:(NSHTTPURLResponse *)response + newRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLRequest *))completionHandler; + + +- (void)URLSession:(NSURLSession * _Nonnull)session + task:(NSURLSessionTask * _Nonnull)task +didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge + completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential * _Nullable credential))completionHandler; + +@end + + +@implementation TestNSURLSessionDelegate + +- (instancetype)initWithValidator:(TSKPinningValidator *)validator + expectation:(XCTestExpectation *)expectation +{ + self = [super init]; + if (self) + { + testExpectation = expectation; + _validator = validator; + } + return self; +} + +- (void)URLSession:(NSURLSession * _Nonnull)session + task:(NSURLSessionTask * _Nonnull)task +didCompleteWithError:(NSError * _Nullable)error +{ + NSLog(@"Received error, %@", error); + _lastError = error; + [testExpectation fulfill]; +} + +- (void)URLSession:(NSURLSession * _Nonnull)session + dataTask:(NSURLSessionDataTask * _Nonnull)dataTask +didReceiveResponse:(NSURLResponse * _Nonnull)response + completionHandler:(void (^ _Nonnull)(NSURLSessionResponseDisposition disposition))completionHandler +{ + _lastResponse = response; + [testExpectation fulfill]; +} + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler +{ + + NSLog(@"Received redirection"); + [testExpectation fulfill]; + + // Do not follow redirections as they cause two pinning validations + if (completionHandler) + { + completionHandler(nil); + } +} + + +- (void)URLSession:(NSURLSession * _Nonnull)session + task:(NSURLSessionTask * _Nonnull)task +didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge + completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition, + NSURLCredential * _Nullable credential))completionHandler +{ + _wasAuthHandlerCalled = [self.validator handleChallenge:challenge completionHandler:completionHandler]; + if (!_wasAuthHandlerCalled) + { + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } +} + + +@end + + + +#pragma mark Test suite +@interface TSKEndToEndNSURLSessionTests : XCTestCase + +@end + +@implementation TSKEndToEndNSURLSessionTests + +- (void)setUp { + [super setUp]; + [[NSURLCache sharedURLCache] removeAllCachedResponses]; +} + +- (void)tearDown { + [super tearDown]; +} + +- (void)testPinningValidationFailed +{ + NSDictionary *trustKitConfig = + @{ + kTSKPinnedDomains : + @{ + @"www.yahoo.com" : @{ + kTSKEnforcePinning : @YES, + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key + @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Fake key 2 + ]}}}; + + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + + // Configure a validation callback + XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; + trustKit.pinningValidatorCallback = ^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, TKSDomainPinningPolicy *_Nonnull notedHostnamePinningPolicy) { + // Check the received values + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); + + XCTAssertEqualObjects(result.serverHostname, @"www.yahoo.com"); + XCTAssertGreaterThan([result.certificateChain count], (unsigned long)1); + XCTAssertGreaterThan(result.validationDuration, 0); + + XCTAssertEqualObjects(notedHostname, @"www.yahoo.com"); + XCTAssertNotNil(notedHostnamePinningPolicy); + + [notifReceivedExpectation fulfill]; + }; + + XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLSessionTaskDelegate"]; + TestNSURLSessionDelegate* delegate = [[TestNSURLSessionDelegate alloc] initWithValidator:trustKit.pinningValidator expectation:expectation]; + + NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] + delegate:delegate + delegateQueue:nil]; + + NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.yahoo.com/"]]; + [task resume]; + + // Wait for the connection to succeed and ensure a notification was posted + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) + { + if (error) + { + NSLog(@"Timeout Error: %@", error); + } + }]; + XCTAssertNotNil(delegate.lastError, @"TrustKit did not trigger an error"); + XCTAssertNil(delegate.lastResponse, @"TrustKit returned a response although pin validation failed"); +} + + +- (void)testPinningValidationSucceeded +{ + NSDictionary *trustKitConfig = + @{ + kTSKPinnedDomains : + @{ + @"www.datatheorem.com" : @{ + kTSKEnforcePinning : @YES, + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmEcDsaSecp384r1], + kTSKPublicKeyHashes : @[@"58qRu/uxh4gFezqAcERupSkRYBlBAvfcw7mEjGPLnNU=", // CA key + @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key + ]}}}; + + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + + // Configure a validation callback + XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; + trustKit.pinningValidatorCallback = ^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, TKSDomainPinningPolicy *_Nonnull notedHostnamePinningPolicy) { + // Check the received values + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); + + XCTAssertEqualObjects(result.serverHostname, @"www.datatheorem.com"); + XCTAssertGreaterThan([result.certificateChain count], (unsigned long)1); + XCTAssertGreaterThan(result.validationDuration, 0); + + XCTAssertEqualObjects(notedHostname, @"www.datatheorem.com"); + XCTAssertNotNil(notedHostnamePinningPolicy); + + [notifReceivedExpectation fulfill]; + }; + + XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLSessionTaskDelegate"]; + TestNSURLSessionDelegate* delegate = [[TestNSURLSessionDelegate alloc] initWithValidator:trustKit.pinningValidator expectation:expectation]; + + NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] + delegate:delegate + delegateQueue:nil]; + + NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.datatheorem.com/"]]; + [task resume]; + + // Wait for the connection to succeed and ensure a notification was posted + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) + { + if (error) + { + NSLog(@"Timeout Error: %@", error); + } + }]; + XCTAssertNil(delegate.lastError, @"TrustKit triggered an error"); + XCTAssertNotNil(delegate.lastResponse, @"TrustKit prevented a response from being returned"); + XCTAssert([(NSHTTPURLResponse *)delegate.lastResponse statusCode] == 200, @"TrustKit prevented a response from being returned"); +} + + +// Test a secure connection to https://self-signed.badssl.com with an invalid certificate chain and ensure that TrustKit is not disabling default certificate validation +- (void)testPinningValidationFailedChainNotTrusted +{ + // This is not needed but to ensure TrustKit does get initialized + NSDictionary *trustKitConfig = + @{ + kTSKPinnedDomains : + @{ + @"self-signed.badssl.com" : @{ + kTSKEnforcePinning : @NO, // Do not enforce pinning to only test default SSL validation + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + kTSKPublicKeyHashes : @[@"9SLklscvzMYj8f+52lp5ze/hY0CFHyLSPQzSpYYIBm8=", // Leaf key + @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key + ]}}}; + + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + + // Configure a validation callback + XCTestExpectation *notifReceivedExpectation = [self expectationWithDescription:@"TestNotificationReceivedExpectation"]; + trustKit.pinningValidatorCallback = ^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, TKSDomainPinningPolicy *_Nonnull notedHostnamePinningPolicy) { + // Check the received values + XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); + XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedInvalidCertificateChain); + + XCTAssertEqualObjects(result.serverHostname, @"self-signed.badssl.com"); + XCTAssertEqual([result.certificateChain count], (unsigned long)1); + XCTAssertGreaterThan(result.validationDuration, 0); + + XCTAssertEqualObjects(notedHostname, @"self-signed.badssl.com"); + XCTAssertNotNil(notedHostnamePinningPolicy); + + [notifReceivedExpectation fulfill]; + }; + + XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLSessionTaskDelegate"]; + TestNSURLSessionDelegate* delegate = [[TestNSURLSessionDelegate alloc] initWithValidator:trustKit.pinningValidator expectation:expectation]; + + NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] + delegate:delegate + delegateQueue:nil]; + + NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://self-signed.badssl.com/"]]; + [task resume]; + + // Wait for the connection to succeed and ensure a notification was posted + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) + { + if (error) + { + NSLog(@"Timeout Error: %@", error); + } + }]; + XCTAssertNotNil(delegate.lastError, @"TrustKit did not trigger an error; TrustKit accepted an invalid certificate"); + XCTAssertNil(delegate.lastResponse, @"TrustKit returned a response although pin validation failed"); +} + + +@end From fd038fda5eab008d96d6968cc4b2cf897a632205 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 14:22:35 -0700 Subject: [PATCH 085/126] Add test to ensure TrustKit does not disable default SSL validation --- TrustKitTests/TSKEndToEndNSURLSessionTests.m | 47 ++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/TrustKitTests/TSKEndToEndNSURLSessionTests.m b/TrustKitTests/TSKEndToEndNSURLSessionTests.m index 2f04f56b..aec979bf 100644 --- a/TrustKitTests/TSKEndToEndNSURLSessionTests.m +++ b/TrustKitTests/TSKEndToEndNSURLSessionTests.m @@ -302,4 +302,51 @@ - (void)testPinningValidationFailedChainNotTrusted } +// Test a secure connection to https://self-signed.badssl.com with an invalid certificate chain and ensure that TrustKit is not disabling default certificate validation for domains that are not even pinned +- (void)testPinningValidationFailedChainNotTrustedAndNotPinned +{ + // This is not needed but to ensure TrustKit does get initialized + NSDictionary *trustKitConfig = + @{ + kTSKPinnedDomains : + @{ + @"www.yahoo.com" : @{ + kTSKEnforcePinning : @NO, // Do not enforce pinning to only test default SSL validation + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + kTSKPublicKeyHashes : @[@"9SLklscvzMYj8f+52lp5ze/hY0CFHyLSPQzSpYYIBm8=", // Leaf key + @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Fake key + ]}}}; + + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + + // Configure a validation callback + trustKit.pinningValidatorCallback = ^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, TKSDomainPinningPolicy *_Nonnull notedHostnamePinningPolicy) { + // Ensure the validation callback was NOT called + XCTFail(@"Callback should not have been called"); + }; + + XCTestExpectation *expectation = [self expectationWithDescription:@"TestNSURLSessionTaskDelegate"]; + TestNSURLSessionDelegate* delegate = [[TestNSURLSessionDelegate alloc] initWithValidator:trustKit.pinningValidator expectation:expectation]; + + NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] + delegate:delegate + delegateQueue:nil]; + + NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://wrong.host.badssl.com/"]]; + [task resume]; + + // Wait for the connection to succeed and ensure a notification was posted + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) + { + if (error) + { + NSLog(@"Timeout Error: %@", error); + } + }]; + XCTAssertNotNil(delegate.lastError, @"TrustKit did not trigger an error; TrustKit accepted an invalid certificate"); + XCTAssertNil(delegate.lastResponse, @"TrustKit returned a response although pin validation failed"); +} + + + @end From bbe6559a8ce07fc4feaf000f9dbe92f46f59cfa8 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 14:32:13 -0700 Subject: [PATCH 086/126] Add basic tests for the swizzling code --- TrustKit.xcodeproj/project.pbxproj | 8 +++ .../Swizzling/TSKNSURLSessionDelegateProxy.m | 14 +--- TrustKitTests/TSKSwizzlingTests.m | 70 +++++++++++++++++++ 3 files changed, 79 insertions(+), 13 deletions(-) create mode 100644 TrustKitTests/TSKSwizzlingTests.m diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 853e00be..497414a2 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -224,6 +224,9 @@ 8CF27A941F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */; }; 8CF27A951F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */; }; 8CF27A961F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */; }; + 8CF27A981F01B341009369B0 /* TSKSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */; }; + 8CF27A991F01B341009369B0 /* TSKSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */; }; + 8CF27A9A1F01B341009369B0 /* TSKSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */; }; FC049B3A1EECD1B000FDC5F4 /* anchor-ca.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD051EECD19E00AB3D81 /* anchor-ca.cert.pem */; }; FC049B3B1EECD1B000FDC5F4 /* anchor-fake.yahoo.com.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD061EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem */; }; FC049B3C1EECD1B000FDC5F4 /* anchor-intermediate.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD071EECD19E00AB3D81 /* anchor-intermediate.cert.pem */; }; @@ -399,6 +402,7 @@ 8CF27A911EFDE7D9009369B0 /* TSKPinningValidator_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidator_Private.h; sourceTree = ""; }; 8CF27A921F0185EC009369B0 /* TSKPinningValidatorCallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorCallback.h; sourceTree = ""; }; 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKEndToEndNSURLSessionTests.m; sourceTree = ""; }; + 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKSwizzlingTests.m; sourceTree = ""; }; FC1A08FD1E579F630055B12C /* TSKTrustDecision.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustDecision.h; sourceTree = ""; }; FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; @@ -597,6 +601,7 @@ FC4CAC791E958DC600DAC41E /* Reporting */, 070868B31ADFF68200E5AFDC /* Certificates */, 8CD5F7371BCB02A7005801D8 /* TSKNSURLConnectionTests.m */, + 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */, 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */, 8CD5F7561BCB7219005801D8 /* TSKNSURLSessionTests.m */, 2FA2868CAFECA46ADE0B6E3E /* TSKPinningValidatorTests.m */, @@ -1259,6 +1264,7 @@ 6B032D401AF1AEC200EAFA69 /* TSKReporterTests.m in Sources */, 8CD5F7571BCB7219005801D8 /* TSKNSURLSessionTests.m in Sources */, FC4CAC7B1E958E0500DAC41E /* TSKReportsRateLimiterTests.m in Sources */, + 8CF27A981F01B341009369B0 /* TSKSwizzlingTests.m in Sources */, 8CC78B201B1B586F00523A25 /* TSKCertificateUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1302,6 +1308,7 @@ 8C84CBC61D6E1718009B3E7D /* TSKPublicKeyAlgorithmTests.m in Sources */, 8C84CBC71D6E1718009B3E7D /* TSKReporterTests.m in Sources */, 8C84CBC81D6E1718009B3E7D /* TSKNSURLSessionTests.m in Sources */, + 8CF27A9A1F01B341009369B0 /* TSKSwizzlingTests.m in Sources */, 8C84CBC91D6E1718009B3E7D /* TSKCertificateUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1370,6 +1377,7 @@ 8CA6CC3C1BAE2C8100BDA419 /* TSKPublicKeyAlgorithmTests.m in Sources */, 8CA6CC3B1BAE2C7E00BDA419 /* TSKPinConfigurationTests.m in Sources */, 8CD5F7581BCB7219005801D8 /* TSKNSURLSessionTests.m in Sources */, + 8CF27A991F01B341009369B0 /* TSKSwizzlingTests.m in Sources */, 8CA6CC3D1BAE2C8500BDA419 /* TSKCertificateUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m index 54c7e992..2cdb2a89 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m @@ -25,22 +25,10 @@ @implementation TSKNSURLSessionDelegateProxy + (void)swizzleNSURLSessionConstructors:(TrustKit *)trustKit { - // Figure out NSURLSession's "real" class - // Pre iOS 8, for some reason hooking NSURLSession doesn't work. We need to use the real/private class __NSCFURLSession - // TODO: Remove support for iOS < 8? - Class NSURLSessionClass = (NSClassFromString(@"NSURLSession") /* iOS 8+ */ - ?: NSClassFromString(@"__NSCFURLSession") /* iOS <8 */); - if (NSURLSessionClass == nil) - { - NSAssert(false, @"ERROR: Could not find NSURLSession's class"); - TSKLog(@"ERROR: Could not find NSURLSession's class"); - return; - } - // + sessionWithConfiguration:delegate:delegateQueue: #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wshadow" - RSSwizzleClassMethod(NSURLSessionClass, + RSSwizzleClassMethod(NSClassFromString(@"NSURLSession"), @selector(sessionWithConfiguration:delegate:delegateQueue:), RSSWReturnType(NSURLSession *), RSSWArguments(NSURLSessionConfiguration * _Nonnull configuration, id _Nullable delegate, NSOperationQueue * _Nullable queue), diff --git a/TrustKitTests/TSKSwizzlingTests.m b/TrustKitTests/TSKSwizzlingTests.m new file mode 100644 index 00000000..919a064a --- /dev/null +++ b/TrustKitTests/TSKSwizzlingTests.m @@ -0,0 +1,70 @@ +// +// TSKSwizzlingTests.m +// TrustKit +// +// Created by Alban Diquet on 6/26/17. +// Copyright © 2017 TrustKit. All rights reserved. +// + +#import +#import "../TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h" +#import "../TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m" + +@interface TSKSwizzlingTests : XCTestCase + +@end + + +/* Basic tests to ensure RSSwizzle does not trigger a crash because a method we are hooking has changed + */ +@implementation TSKSwizzlingTests + +- (void)setUp { + [super setUp]; +} + +- (void)tearDown { + [super tearDown]; +} + + +- (void)testNSURLSession +{ + NSDictionary *trustKitConfig = + @{ + kTSKPinnedDomains : + @{ + @"www.datatheorem.com" : @{ + kTSKEnforcePinning : @YES, + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmEcDsaSecp384r1], + kTSKPublicKeyHashes : @[@"58qRu/uxh4gFezqAcERupSkRYBlBAvfcw7mEjGPLnNU=", // CA key + @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key + ]}}}; + + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + + [TSKNSURLSessionDelegateProxy swizzleNSURLSessionConstructors:trustKit]; + +} + + +- (void)testNSURLConnection +{ + NSDictionary *trustKitConfig = + @{ + kTSKPinnedDomains : + @{ + @"www.datatheorem.com" : @{ + kTSKEnforcePinning : @YES, + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmEcDsaSecp384r1], + kTSKPublicKeyHashes : @[@"58qRu/uxh4gFezqAcERupSkRYBlBAvfcw7mEjGPLnNU=", // CA key + @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key + ]}}}; + + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + + [TSKNSURLConnectionDelegateProxy swizzleNSURLConnectionConstructors:trustKit]; + +} + +@end From 381354f4a9e693f10b956f4cc7e66a7361b460ea Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 14:35:46 -0700 Subject: [PATCH 087/126] Rename validationResult to evaluationResult --- TrustKit/TSKPinningValidatorResult.h | 4 ++-- TrustKit/TSKPinningValidatorResult.m | 2 +- TrustKit/TrustKit.m | 2 +- TrustKitTests/TSKEndToEndNSURLSessionTests.m | 6 +++--- TrustKitTests/TSKPinningValidatorTests.m | 20 ++++++++++---------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/TrustKit/TSKPinningValidatorResult.h b/TrustKit/TSKPinningValidatorResult.h index a2798e1c..043e2885 100644 --- a/TrustKit/TSKPinningValidatorResult.h +++ b/TrustKit/TSKPinningValidatorResult.h @@ -33,11 +33,11 @@ The result of validating the server's certificate chain against the set of SSL pins configured for the `notedHostname`. */ -@property (nonatomic, readonly) TSKTrustEvaluationResult validationResult; +@property (nonatomic, readonly) TSKTrustEvaluationResult evaluationResult; /** The trust decision returned for this connection, which describes whether the connection should be blocked - or allowed, based on the `validationResult` returned when evaluating the `serverTrust` and the SSL pining + or allowed, based on the `evaluationResult` returned when evaluating the `serverTrust` and the SSL pining policy configured for this server. For example, the pinning validation could have failed (ie. validationResult being diff --git a/TrustKit/TSKPinningValidatorResult.m b/TrustKit/TSKPinningValidatorResult.m index f06e95e8..ec46be85 100644 --- a/TrustKit/TSKPinningValidatorResult.m +++ b/TrustKit/TSKPinningValidatorResult.m @@ -39,7 +39,7 @@ - (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHost if (self) { _serverHostname = serverHostname; _serverTrust = serverTrust; - _validationResult = validationResult; + _evaluationResult = validationResult; _finalTrustDecision = finalTrustDecision; _validationDuration = validationDuration; _certificateChain = nil; diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 7b3ad6aa..83eaa0c2 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -186,7 +186,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo // The block which receives pin validation results and turns them into pin validation reports - (void)sendValidationReport:(TSKPinningValidatorResult *)result notedHostname:(NSString *)notedHostname pinningPolicy:(NSDictionary *)notedHostnamePinningPolicy { - TSKTrustEvaluationResult validationResult = result.validationResult; + TSKTrustEvaluationResult validationResult = result.evaluationResult; // Send a report only if the there was a pinning failure if (validationResult != TSKTrustEvaluationSuccess) diff --git a/TrustKitTests/TSKEndToEndNSURLSessionTests.m b/TrustKitTests/TSKEndToEndNSURLSessionTests.m index aec979bf..8bccecef 100644 --- a/TrustKitTests/TSKEndToEndNSURLSessionTests.m +++ b/TrustKitTests/TSKEndToEndNSURLSessionTests.m @@ -154,7 +154,7 @@ - (void)testPinningValidationFailed trustKit.pinningValidatorCallback = ^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, TKSDomainPinningPolicy *_Nonnull notedHostnamePinningPolicy) { // Check the received values XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationFailedNoMatchingPin); XCTAssertEqualObjects(result.serverHostname, @"www.yahoo.com"); XCTAssertGreaterThan([result.certificateChain count], (unsigned long)1); @@ -209,7 +209,7 @@ - (void)testPinningValidationSucceeded trustKit.pinningValidatorCallback = ^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, TKSDomainPinningPolicy *_Nonnull notedHostnamePinningPolicy) { // Check the received values XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.serverHostname, @"www.datatheorem.com"); XCTAssertGreaterThan([result.certificateChain count], (unsigned long)1); @@ -267,7 +267,7 @@ - (void)testPinningValidationFailedChainNotTrusted trustKit.pinningValidatorCallback = ^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, TKSDomainPinningPolicy *_Nonnull notedHostnamePinningPolicy) { // Check the received values XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedInvalidCertificateChain); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationFailedInvalidCertificateChain); XCTAssertEqualObjects(result.serverHostname, @"self-signed.badssl.com"); XCTAssertEqual([result.certificateChain count], (unsigned long)1); diff --git a/TrustKitTests/TSKPinningValidatorTests.m b/TrustKitTests/TSKPinningValidatorTests.m index 3fdb2d24..53fae2da 100644 --- a/TrustKitTests/TSKPinningValidatorTests.m +++ b/TrustKitTests/TSKPinningValidatorTests.m @@ -151,7 +151,7 @@ - (void)testVerifyAgainstAnyPublicKey validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -224,7 +224,7 @@ - (void)testVerifyAgainstIntermediateCAPublicKey validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -291,7 +291,7 @@ - (void)testVerifyAgainstCAPublicKey validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -354,7 +354,7 @@ - (void)testVerifyAgainstLeafPublicKey validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -417,7 +417,7 @@ - (void)testVerifyAgainstBadPublicKey validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationFailedNoMatchingPin); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -522,7 +522,7 @@ - (void)testVerifyAgainstBadPublicKeyPinningNotEnforced validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationFailedNoMatchingPin); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -587,7 +587,7 @@ - (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldAllowConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationSuccess); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationSuccess); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -652,7 +652,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedInvalidCertificateChain); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationFailedInvalidCertificateChain); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -718,7 +718,7 @@ - (void)testVerifyAgainstCaPublicKeyAndBadHostname validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedInvalidCertificateChain); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationFailedInvalidCertificateChain); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); @@ -783,7 +783,7 @@ - (void)testVerifyAgainstInjectedCaPublicKey validationCallback:^(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, NSDictionary *_Nonnull notedHostnamePinningPolicy) { XCTAssertEqual(result.finalTrustDecision, TSKTrustDecisionShouldBlockConnection); - XCTAssertEqual(result.validationResult, TSKTrustEvaluationFailedNoMatchingPin); + XCTAssertEqual(result.evaluationResult, TSKTrustEvaluationFailedNoMatchingPin); XCTAssertEqualObjects(result.certificateChain, convertTrustToPemArray(trust)); From 97688c588b9c7d2ffeaec0defc4abb5d00142425 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 14:41:06 -0700 Subject: [PATCH 088/126] Tweak documentation --- .jazzy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.jazzy.yaml b/.jazzy.yaml index efe7cfff..afaddf71 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -23,11 +23,11 @@ custom_categories: - TSKPinningValidator - TSKTrustDecision - - name: Receiving Validation Notifications + - name: Setting up a Validation Callback children: - TSKPinningValidatorCallback - - TKSDomainPinningPolicy - TSKPinningValidatorResult + - TKSDomainPinningPolicy - TSKTrustEvaluationResult - name: Global Configuration Keys From e07fe3c5e62c2631b56eda3f8a3f7eec0f362bff Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 15:04:29 -0700 Subject: [PATCH 089/126] Bring back +setLoggerBlock: --- TrustKit.xcodeproj/project.pbxproj | 8 +++++ TrustKit/TSKLog.h | 7 ++--- TrustKit/TrustKit.h | 14 +++++++++ TrustKit/TrustKit.m | 35 +++++++++++++++++++-- TrustKitTests/TSKLoggerTests.m | 50 ++++++++++++++++++++++++++++++ 5 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 TrustKitTests/TSKLoggerTests.m diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 497414a2..a86ec67f 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -227,6 +227,9 @@ 8CF27A981F01B341009369B0 /* TSKSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */; }; 8CF27A991F01B341009369B0 /* TSKSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */; }; 8CF27A9A1F01B341009369B0 /* TSKSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */; }; + 8CF27AA21F01BB7B009369B0 /* TSKLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */; }; + 8CF27AA31F01BB7B009369B0 /* TSKLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */; }; + 8CF27AA41F01BB7B009369B0 /* TSKLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */; }; FC049B3A1EECD1B000FDC5F4 /* anchor-ca.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD051EECD19E00AB3D81 /* anchor-ca.cert.pem */; }; FC049B3B1EECD1B000FDC5F4 /* anchor-fake.yahoo.com.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD061EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem */; }; FC049B3C1EECD1B000FDC5F4 /* anchor-intermediate.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD071EECD19E00AB3D81 /* anchor-intermediate.cert.pem */; }; @@ -403,6 +406,7 @@ 8CF27A921F0185EC009369B0 /* TSKPinningValidatorCallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorCallback.h; sourceTree = ""; }; 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKEndToEndNSURLSessionTests.m; sourceTree = ""; }; 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKSwizzlingTests.m; sourceTree = ""; }; + 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKLoggerTests.m; sourceTree = ""; }; FC1A08FD1E579F630055B12C /* TSKTrustDecision.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustDecision.h; sourceTree = ""; }; FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; FC1A08FF1E57A4BB0055B12C /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; @@ -602,6 +606,7 @@ 070868B31ADFF68200E5AFDC /* Certificates */, 8CD5F7371BCB02A7005801D8 /* TSKNSURLConnectionTests.m */, 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */, + 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */, 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */, 8CD5F7561BCB7219005801D8 /* TSKNSURLSessionTests.m */, 2FA2868CAFECA46ADE0B6E3E /* TSKPinningValidatorTests.m */, @@ -1265,6 +1270,7 @@ 8CD5F7571BCB7219005801D8 /* TSKNSURLSessionTests.m in Sources */, FC4CAC7B1E958E0500DAC41E /* TSKReportsRateLimiterTests.m in Sources */, 8CF27A981F01B341009369B0 /* TSKSwizzlingTests.m in Sources */, + 8CF27AA21F01BB7B009369B0 /* TSKLoggerTests.m in Sources */, 8CC78B201B1B586F00523A25 /* TSKCertificateUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1309,6 +1315,7 @@ 8C84CBC71D6E1718009B3E7D /* TSKReporterTests.m in Sources */, 8C84CBC81D6E1718009B3E7D /* TSKNSURLSessionTests.m in Sources */, 8CF27A9A1F01B341009369B0 /* TSKSwizzlingTests.m in Sources */, + 8CF27AA41F01BB7B009369B0 /* TSKLoggerTests.m in Sources */, 8C84CBC91D6E1718009B3E7D /* TSKCertificateUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1378,6 +1385,7 @@ 8CA6CC3B1BAE2C7E00BDA419 /* TSKPinConfigurationTests.m in Sources */, 8CD5F7581BCB7219005801D8 /* TSKNSURLSessionTests.m in Sources */, 8CF27A991F01B341009369B0 /* TSKSwizzlingTests.m in Sources */, + 8CF27AA31F01BB7B009369B0 /* TSKLoggerTests.m in Sources */, 8CA6CC3D1BAE2C8500BDA419 /* TSKCertificateUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/TrustKit/TSKLog.h b/TrustKit/TSKLog.h index 38ed5031..2ddde3e7 100644 --- a/TrustKit/TSKLog.h +++ b/TrustKit/TSKLog.h @@ -15,10 +15,7 @@ #define TSKLog_h // The logging function we use within TrustKit -#ifdef DEBUG -#define TSKLog(format, ...) NSLog(@"=== TrustKit: " format, ##__VA_ARGS__); -#else -#define TSKLog(format, ...) -#endif +void TSKLog(NSString *format, ...); + #endif /* TSKLog_h */ diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index c6f4463f..6ba9f5b5 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -182,5 +182,19 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithConfiguration:(NSDictionary * _Nullable)trustKitConfig; + +#pragma mark Other Settings + +/** + Set the global logger. + + This method sets the global logger, used when TrustKit needs to display a message to the developer. + + If a global logger is not set, the default logger will be used, which will print TrustKit log messages + (using `NSLog()`) when the App is built in Debug mode. If the App was built for Release, the default + logger will not print any messages at all. + */ ++ (void)setLoggerBlock:(void (^)(NSString *))block; + @end NS_ASSUME_NONNULL_END diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 83eaa0c2..fd1076ac 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -40,6 +40,26 @@ static NSString * const kTSKDefaultReportUri = @"https://overmind.datatheorem.com/trustkit/report"; +// Default logger block: only log in debug builds and add TrustKit at the beginning of the line +void (^_loggerBlock)(NSString *) = ^void(NSString *message) +{ +#if DEBUG + NSLog(@"=== TrustKit: %@", message); +#endif +}; + +// The logging function we use within TrustKit +void TSKLog(NSString *format, ...) +{ + va_list args; + va_start(args, format); + NSString *message = [[NSString alloc] initWithFormat: format arguments:args]; + va_end(args); + _loggerBlock(message); +} + + + #pragma mark TrustKit Initialization Helper Functions @interface TrustKit () @@ -55,7 +75,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo @implementation TrustKit -#pragma mark Shared TrustKit Explicit Initialization +#pragma mark Singleton Initialization + (instancetype)sharedInstance { @@ -87,7 +107,7 @@ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig } -#pragma mark Instance +#pragma mark Instance Initialization - (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig @@ -181,7 +201,7 @@ - (instancetype)initWithConfiguration:(NSDictionary *)trustKitCo } -#pragma mark Notification Handlers +#pragma mark Validation Callback // The block which receives pin validation results and turns them into pin validation reports - (void)sendValidationReport:(TSKPinningValidatorResult *)result notedHostname:(NSString *)notedHostname pinningPolicy:(NSDictionary *)notedHostnamePinningPolicy @@ -227,6 +247,15 @@ - (void)setPinningValidatorCallbackQueue:(dispatch_queue_t)pinningValidatorCallb _pinningValidatorCallbackQueue = pinningValidatorCallbackQueue ?: dispatch_get_main_queue(); } + +#pragma mark Configuring Logging + + ++ (void)setLoggerBlock:(void (^)(NSString *))block +{ + _loggerBlock = block; +} + @end diff --git a/TrustKitTests/TSKLoggerTests.m b/TrustKitTests/TSKLoggerTests.m new file mode 100644 index 00000000..1ca2119f --- /dev/null +++ b/TrustKitTests/TSKLoggerTests.m @@ -0,0 +1,50 @@ +// +// TSKLoggerTests.m +// TrustKit +// +// Created by Alban Diquet on 8/29/16. +// Copyright © 2016 TrustKit. All rights reserved. +// + +#import +#import "../TrustKit/TrustKit.h" +#import "../TrustKit/TSKLog.h" + + +@interface TSKLoggerTests : XCTestCase + +@end + +@implementation TSKLoggerTests + +- (void)setUp +{ + [super setUp]; +} + +- (void)tearDown +{ + [super tearDown]; +} + +- (void)testDefaultLoggerBlock +{ + TSKLog(@"test %@", @"test"); +} + + +- (void)testSetLoggerBlock +{ + __block bool wasBlockCalled = false; + void (^loggerBlock)(NSString *) = ^void(NSString *message) + { + XCTAssert(message, @"test test"); + wasBlockCalled = true; + }; + + [TrustKit setLoggerBlock:loggerBlock]; + TSKLog(@"test %@", @"test"); + XCTAssertTrue(wasBlockCalled); +} + +@end From d71d6c25a5bcefa1dc63d32ec74ce4cb76727425 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 16:03:45 -0700 Subject: [PATCH 090/126] Update documentation --- TrustKit/TrustKit.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 6ba9f5b5..bf205d1d 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -188,10 +188,11 @@ NS_ASSUME_NONNULL_BEGIN /** Set the global logger. - This method sets the global logger, used when TrustKit needs to display a message to the developer. + This method sets the global logger, used when any `TrustKit` instance needs to display a message to + the developer. - If a global logger is not set, the default logger will be used, which will print TrustKit log messages - (using `NSLog()`) when the App is built in Debug mode. If the App was built for Release, the default + If a global logger is not set, the default logger will be used, which will only print TrustKit log + messages (using `NSLog()`) when the App is built in Debug mode. If the App was built for Release, the default logger will not print any messages at all. */ + (void)setLoggerBlock:(void (^)(NSString *))block; From 76462ef3936c6926f2398b1f02abc864cef1c8ec Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 16:32:47 -0700 Subject: [PATCH 091/126] CircleCI --- circle.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/circle.yml b/circle.yml index 59397b56..2547da2b 100644 --- a/circle.yml +++ b/circle.yml @@ -4,7 +4,7 @@ machine: test: override: # First run Xcode 8.3 tests - - sudo xcode-select --switch /Applications/Xcode-8.3.app + - sudo xcode-select --switch /Applications/Xcode-8.3.3.app # iOS 8 platform with iOS 9 SDK - xcodebuild clean test CODE_SIGNING_REQUIRED=NO @@ -22,21 +22,21 @@ test: # Switch to Xcode 9.0 - sudo xcode-select --switch /Applications/Xcode-9.0.app - # iOS 10 platform with iOS 10 SDK + # iOS 11 platform with iOS 11 SDK - xcodebuild clean test CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= - -destination 'platform=iOS Simulator,name=iPhone 6,OS=10.0' - -sdk iphonesimulator10.0 + -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.0' + -sdk iphonesimulator11.0 -scheme "TrustKit" - # tvOS 10 platform with tvOS 10 SDK + # tvOS 11 platform with tvOS 11 SDK - xcodebuild clean test CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= - -destination 'platform=tvOS Simulator,name=Apple TV 1080p,OS=10.0' - -sdk appletvsimulator10.0 + -destination 'platform=tvOS Simulator,name=Apple TV 1080p,OS=11.0' + -sdk appletvsimulator11.0 -scheme "TrustKit tvOS" - # OS X 10.11 platform with OS X 10.12 SDK + # OS X 10.12 platform with OS X 10.12 SDK - xcodebuild clean test CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= From ecabdbce2785f402e888d154c85fb99bc6ab69a0 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 17:13:26 -0700 Subject: [PATCH 092/126] Fi macOS builds --- TrustKit/Pinning/ssl_pin_verifier.m | 2 +- TrustKit/TSKPinningValidator.m | 2 +- TrustKit/TrustKit.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/TrustKit/Pinning/ssl_pin_verifier.m b/TrustKit/Pinning/ssl_pin_verifier.m index 3179f72e..519f2b9b 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.m +++ b/TrustKit/Pinning/ssl_pin_verifier.m @@ -126,7 +126,7 @@ TSKTrustEvaluationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *s { TSKLog(@"Detected user-defined trust anchor in the certificate chain"); CFRelease(serverTrust); - return TSKPinValidationResultFailedUserDefinedTrustAnchor; + return TSKTrustEvaluationFailedUserDefinedTrustAnchor; } } } diff --git a/TrustKit/TSKPinningValidator.m b/TrustKit/TSKPinningValidator.m index f7b6c6bf..17f139d2 100644 --- a/TrustKit/TSKPinningValidator.m +++ b/TrustKit/TSKPinningValidator.m @@ -161,7 +161,7 @@ - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust forHostname: // Pin validation failed TSKLog(@"Pin validation failed for %@", serverHostname); #if !TARGET_OS_IPHONE - if ((validationResult == TSKPinValidationResultFailedUserDefinedTrustAnchor) + if ((validationResult == TSKTrustEvaluationFailedUserDefinedTrustAnchor) && (self.ignorePinsForUserTrustAnchors)) { // OS-X only: user-defined trust anchors can be whitelisted (for corporate proxies, etc.) so don't send reports diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index fd1076ac..7b78a94c 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -212,7 +212,7 @@ - (void)sendValidationReport:(TSKPinningValidatorResult *)result notedHostname:( if (validationResult != TSKTrustEvaluationSuccess) { #if !TARGET_OS_IPHONE - if (validationResult != TSKPinValidationResultFailedUserDefinedTrustAnchor) + if (validationResult != TSKTrustEvaluationFailedUserDefinedTrustAnchor) #endif { // Pin validation failed: retrieve the list of configured report URLs From bd219df4acb057f5be46ec294322f15aad28b660 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 17:18:22 -0700 Subject: [PATCH 093/126] CircleCI --- circle.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 2547da2b..529a2378 100644 --- a/circle.yml +++ b/circle.yml @@ -4,6 +4,7 @@ machine: test: override: # First run Xcode 8.3 tests + - ls /Applications/ - sudo xcode-select --switch /Applications/Xcode-8.3.3.app # iOS 8 platform with iOS 9 SDK - xcodebuild clean test @@ -41,6 +42,6 @@ test: CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= -destination 'platform=OS X' - -sdk macosx10.12 + -sdk macosx10.13 -scheme "TrustKit OS X" From c6107326e0abecddeead3875ea246ba49d0c2019 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 17:30:21 -0700 Subject: [PATCH 094/126] Fix identifier_for_vendor() on macOS --- TrustKit/Reporting/vendor_identifier.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TrustKit/Reporting/vendor_identifier.m b/TrustKit/Reporting/vendor_identifier.m index feadbe68..6fec2e99 100644 --- a/TrustKit/Reporting/vendor_identifier.m +++ b/TrustKit/Reporting/vendor_identifier.m @@ -23,7 +23,6 @@ #pragma mark Vendor identifier - macOS, watchOS -#include static NSString * const kTSKVendorIdentifierKey = @"TSKVendorIdentifier"; @@ -36,11 +35,13 @@ if (vendorId == nil) { // Generate and store a new UUID - [preferences setObject:NSUUID.UUID.UUIDString forKey:kTSKVendorIdentifierKey]; + vendorId = NSUUID.UUID.UUIDString; + [preferences setObject:vendorId forKey:kTSKVendorIdentifierKey]; [preferences synchronize]; } return vendorId; } + #endif From 2dd6b41479913aff64c884e3aa18ab9db5f7e6b7 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 17:43:33 -0700 Subject: [PATCH 095/126] CircleCI --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 529a2378..adc847ff 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,6 @@ machine: xcode: - version: 9.0 + version: 8.3 test: override: # First run Xcode 8.3 tests From fdfcd9004bfc8820a9e7aef565e6f6ac4b857a03 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 21:27:01 -0700 Subject: [PATCH 096/126] CircleCI --- circle.yml | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/circle.yml b/circle.yml index adc847ff..2b7dff70 100644 --- a/circle.yml +++ b/circle.yml @@ -3,45 +3,35 @@ machine: version: 8.3 test: override: - # First run Xcode 8.3 tests - - ls /Applications/ - - sudo xcode-select --switch /Applications/Xcode-8.3.3.app - # iOS 8 platform with iOS 9 SDK + # iOS 9.0 platform with iOS 10.3 SDK - xcodebuild clean test CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= - -destination 'platform=iOS Simulator,name=iPhone 4S,OS=8.4' - -sdk iphonesimulator9.3 + -destination 'platform=iOS Simulator,name=iPhone 5,OS=9.0' + -sdk iphonesimulator10.3 -scheme "TrustKit" - # OS X 10.11 platform with OS X 10.11 SDK + + # iOS 10.3 platform with iOS 10.3 SDK - xcodebuild clean test CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= - -destination 'platform=OS X' - -sdk macosx10.11 - -scheme "TrustKit OS X" - - # Switch to Xcode 9.0 - - sudo xcode-select --switch /Applications/Xcode-9.0.app - # iOS 11 platform with iOS 11 SDK - - xcodebuild clean test - CODE_SIGNING_REQUIRED=NO - CODE_SIGN_IDENTITY= - -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.0' + -destination 'platform=iOS Simulator,name=iPhone 7,OS=10.3' -sdk iphonesimulator11.0 -scheme "TrustKit" - # tvOS 11 platform with tvOS 11 SDK - - xcodebuild clean test - CODE_SIGNING_REQUIRED=NO - CODE_SIGN_IDENTITY= - -destination 'platform=tvOS Simulator,name=Apple TV 1080p,OS=11.0' - -sdk appletvsimulator11.0 - -scheme "TrustKit tvOS" - # OS X 10.12 platform with OS X 10.12 SDK + + # macOS 10.12 platform with macOS 10.12 SDK - xcodebuild clean test CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= -destination 'platform=OS X' - -sdk macosx10.13 + -sdk macosx10.12 -scheme "TrustKit OS X" + # tvOS 10.3 platform with tvOS 10.3 SDK + - xcodebuild clean test + CODE_SIGNING_REQUIRED=NO + CODE_SIGN_IDENTITY= + -destination 'platform=tvOS Simulator,name=Apple TV 1080p,OS=10.3' + -sdk appletvsimulator10.3 + -scheme "TrustKit tvOS" + \ No newline at end of file From 25ae046c326f12b896279902bd605507bb678ad6 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 21:35:04 -0700 Subject: [PATCH 097/126] Remove swizzling tests --- TrustKit.xcodeproj/project.pbxproj | 8 ---- TrustKitTests/TSKSwizzlingTests.m | 70 ------------------------------ 2 files changed, 78 deletions(-) delete mode 100644 TrustKitTests/TSKSwizzlingTests.m diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index a86ec67f..cc803ab5 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -224,9 +224,6 @@ 8CF27A941F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */; }; 8CF27A951F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */; }; 8CF27A961F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */; }; - 8CF27A981F01B341009369B0 /* TSKSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */; }; - 8CF27A991F01B341009369B0 /* TSKSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */; }; - 8CF27A9A1F01B341009369B0 /* TSKSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */; }; 8CF27AA21F01BB7B009369B0 /* TSKLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */; }; 8CF27AA31F01BB7B009369B0 /* TSKLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */; }; 8CF27AA41F01BB7B009369B0 /* TSKLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */; }; @@ -405,7 +402,6 @@ 8CF27A911EFDE7D9009369B0 /* TSKPinningValidator_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidator_Private.h; sourceTree = ""; }; 8CF27A921F0185EC009369B0 /* TSKPinningValidatorCallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorCallback.h; sourceTree = ""; }; 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKEndToEndNSURLSessionTests.m; sourceTree = ""; }; - 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKSwizzlingTests.m; sourceTree = ""; }; 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKLoggerTests.m; sourceTree = ""; }; FC1A08FD1E579F630055B12C /* TSKTrustDecision.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustDecision.h; sourceTree = ""; }; FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; @@ -605,7 +601,6 @@ FC4CAC791E958DC600DAC41E /* Reporting */, 070868B31ADFF68200E5AFDC /* Certificates */, 8CD5F7371BCB02A7005801D8 /* TSKNSURLConnectionTests.m */, - 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */, 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */, 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */, 8CD5F7561BCB7219005801D8 /* TSKNSURLSessionTests.m */, @@ -1269,7 +1264,6 @@ 6B032D401AF1AEC200EAFA69 /* TSKReporterTests.m in Sources */, 8CD5F7571BCB7219005801D8 /* TSKNSURLSessionTests.m in Sources */, FC4CAC7B1E958E0500DAC41E /* TSKReportsRateLimiterTests.m in Sources */, - 8CF27A981F01B341009369B0 /* TSKSwizzlingTests.m in Sources */, 8CF27AA21F01BB7B009369B0 /* TSKLoggerTests.m in Sources */, 8CC78B201B1B586F00523A25 /* TSKCertificateUtils.m in Sources */, ); @@ -1314,7 +1308,6 @@ 8C84CBC61D6E1718009B3E7D /* TSKPublicKeyAlgorithmTests.m in Sources */, 8C84CBC71D6E1718009B3E7D /* TSKReporterTests.m in Sources */, 8C84CBC81D6E1718009B3E7D /* TSKNSURLSessionTests.m in Sources */, - 8CF27A9A1F01B341009369B0 /* TSKSwizzlingTests.m in Sources */, 8CF27AA41F01BB7B009369B0 /* TSKLoggerTests.m in Sources */, 8C84CBC91D6E1718009B3E7D /* TSKCertificateUtils.m in Sources */, ); @@ -1384,7 +1377,6 @@ 8CA6CC3C1BAE2C8100BDA419 /* TSKPublicKeyAlgorithmTests.m in Sources */, 8CA6CC3B1BAE2C7E00BDA419 /* TSKPinConfigurationTests.m in Sources */, 8CD5F7581BCB7219005801D8 /* TSKNSURLSessionTests.m in Sources */, - 8CF27A991F01B341009369B0 /* TSKSwizzlingTests.m in Sources */, 8CF27AA31F01BB7B009369B0 /* TSKLoggerTests.m in Sources */, 8CA6CC3D1BAE2C8500BDA419 /* TSKCertificateUtils.m in Sources */, ); diff --git a/TrustKitTests/TSKSwizzlingTests.m b/TrustKitTests/TSKSwizzlingTests.m deleted file mode 100644 index 919a064a..00000000 --- a/TrustKitTests/TSKSwizzlingTests.m +++ /dev/null @@ -1,70 +0,0 @@ -// -// TSKSwizzlingTests.m -// TrustKit -// -// Created by Alban Diquet on 6/26/17. -// Copyright © 2017 TrustKit. All rights reserved. -// - -#import -#import "../TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h" -#import "../TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m" - -@interface TSKSwizzlingTests : XCTestCase - -@end - - -/* Basic tests to ensure RSSwizzle does not trigger a crash because a method we are hooking has changed - */ -@implementation TSKSwizzlingTests - -- (void)setUp { - [super setUp]; -} - -- (void)tearDown { - [super tearDown]; -} - - -- (void)testNSURLSession -{ - NSDictionary *trustKitConfig = - @{ - kTSKPinnedDomains : - @{ - @"www.datatheorem.com" : @{ - kTSKEnforcePinning : @YES, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmEcDsaSecp384r1], - kTSKPublicKeyHashes : @[@"58qRu/uxh4gFezqAcERupSkRYBlBAvfcw7mEjGPLnNU=", // CA key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key - ]}}}; - - TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; - - [TSKNSURLSessionDelegateProxy swizzleNSURLSessionConstructors:trustKit]; - -} - - -- (void)testNSURLConnection -{ - NSDictionary *trustKitConfig = - @{ - kTSKPinnedDomains : - @{ - @"www.datatheorem.com" : @{ - kTSKEnforcePinning : @YES, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmEcDsaSecp384r1], - kTSKPublicKeyHashes : @[@"58qRu/uxh4gFezqAcERupSkRYBlBAvfcw7mEjGPLnNU=", // CA key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key - ]}}}; - - TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; - - [TSKNSURLConnectionDelegateProxy swizzleNSURLConnectionConstructors:trustKit]; - -} - -@end From cfa082555a86df93a6d91f3e4f1c1087b6f7800e Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 21:35:13 -0700 Subject: [PATCH 098/126] CircleCI --- circle.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index 2b7dff70..2f312548 100644 --- a/circle.yml +++ b/circle.yml @@ -16,7 +16,7 @@ test: CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= -destination 'platform=iOS Simulator,name=iPhone 7,OS=10.3' - -sdk iphonesimulator11.0 + -sdk iphonesimulator10.2 -scheme "TrustKit" # macOS 10.12 platform with macOS 10.12 SDK @@ -31,7 +31,7 @@ test: - xcodebuild clean test CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= - -destination 'platform=tvOS Simulator,name=Apple TV 1080p,OS=10.3' - -sdk appletvsimulator10.3 + -destination 'platform=tvOS Simulator,name=Apple TV 1080p,OS=10.2' + -sdk appletvsimulator10.2 -scheme "TrustKit tvOS" \ No newline at end of file From 4a058f3fe41abaedd6cfd910fc8297f8ff05ed10 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 21:35:04 -0700 Subject: [PATCH 099/126] Revert "Remove swizzling tests" This reverts commit 25ae046c326f12b896279902bd605507bb678ad6. --- TrustKit.xcodeproj/project.pbxproj | 8 ++++ TrustKitTests/TSKSwizzlingTests.m | 70 ++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 TrustKitTests/TSKSwizzlingTests.m diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index cc803ab5..a86ec67f 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -224,6 +224,9 @@ 8CF27A941F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */; }; 8CF27A951F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */; }; 8CF27A961F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */; }; + 8CF27A981F01B341009369B0 /* TSKSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */; }; + 8CF27A991F01B341009369B0 /* TSKSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */; }; + 8CF27A9A1F01B341009369B0 /* TSKSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */; }; 8CF27AA21F01BB7B009369B0 /* TSKLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */; }; 8CF27AA31F01BB7B009369B0 /* TSKLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */; }; 8CF27AA41F01BB7B009369B0 /* TSKLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */; }; @@ -402,6 +405,7 @@ 8CF27A911EFDE7D9009369B0 /* TSKPinningValidator_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidator_Private.h; sourceTree = ""; }; 8CF27A921F0185EC009369B0 /* TSKPinningValidatorCallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorCallback.h; sourceTree = ""; }; 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKEndToEndNSURLSessionTests.m; sourceTree = ""; }; + 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKSwizzlingTests.m; sourceTree = ""; }; 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKLoggerTests.m; sourceTree = ""; }; FC1A08FD1E579F630055B12C /* TSKTrustDecision.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSKTrustDecision.h; sourceTree = ""; }; FC1A08FE1E57A4BB0055B12C /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; @@ -601,6 +605,7 @@ FC4CAC791E958DC600DAC41E /* Reporting */, 070868B31ADFF68200E5AFDC /* Certificates */, 8CD5F7371BCB02A7005801D8 /* TSKNSURLConnectionTests.m */, + 8CF27A971F01B341009369B0 /* TSKSwizzlingTests.m */, 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */, 8CF27A931F01A0B8009369B0 /* TSKEndToEndNSURLSessionTests.m */, 8CD5F7561BCB7219005801D8 /* TSKNSURLSessionTests.m */, @@ -1264,6 +1269,7 @@ 6B032D401AF1AEC200EAFA69 /* TSKReporterTests.m in Sources */, 8CD5F7571BCB7219005801D8 /* TSKNSURLSessionTests.m in Sources */, FC4CAC7B1E958E0500DAC41E /* TSKReportsRateLimiterTests.m in Sources */, + 8CF27A981F01B341009369B0 /* TSKSwizzlingTests.m in Sources */, 8CF27AA21F01BB7B009369B0 /* TSKLoggerTests.m in Sources */, 8CC78B201B1B586F00523A25 /* TSKCertificateUtils.m in Sources */, ); @@ -1308,6 +1314,7 @@ 8C84CBC61D6E1718009B3E7D /* TSKPublicKeyAlgorithmTests.m in Sources */, 8C84CBC71D6E1718009B3E7D /* TSKReporterTests.m in Sources */, 8C84CBC81D6E1718009B3E7D /* TSKNSURLSessionTests.m in Sources */, + 8CF27A9A1F01B341009369B0 /* TSKSwizzlingTests.m in Sources */, 8CF27AA41F01BB7B009369B0 /* TSKLoggerTests.m in Sources */, 8C84CBC91D6E1718009B3E7D /* TSKCertificateUtils.m in Sources */, ); @@ -1377,6 +1384,7 @@ 8CA6CC3C1BAE2C8100BDA419 /* TSKPublicKeyAlgorithmTests.m in Sources */, 8CA6CC3B1BAE2C7E00BDA419 /* TSKPinConfigurationTests.m in Sources */, 8CD5F7581BCB7219005801D8 /* TSKNSURLSessionTests.m in Sources */, + 8CF27A991F01B341009369B0 /* TSKSwizzlingTests.m in Sources */, 8CF27AA31F01BB7B009369B0 /* TSKLoggerTests.m in Sources */, 8CA6CC3D1BAE2C8500BDA419 /* TSKCertificateUtils.m in Sources */, ); diff --git a/TrustKitTests/TSKSwizzlingTests.m b/TrustKitTests/TSKSwizzlingTests.m new file mode 100644 index 00000000..919a064a --- /dev/null +++ b/TrustKitTests/TSKSwizzlingTests.m @@ -0,0 +1,70 @@ +// +// TSKSwizzlingTests.m +// TrustKit +// +// Created by Alban Diquet on 6/26/17. +// Copyright © 2017 TrustKit. All rights reserved. +// + +#import +#import "../TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h" +#import "../TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m" + +@interface TSKSwizzlingTests : XCTestCase + +@end + + +/* Basic tests to ensure RSSwizzle does not trigger a crash because a method we are hooking has changed + */ +@implementation TSKSwizzlingTests + +- (void)setUp { + [super setUp]; +} + +- (void)tearDown { + [super tearDown]; +} + + +- (void)testNSURLSession +{ + NSDictionary *trustKitConfig = + @{ + kTSKPinnedDomains : + @{ + @"www.datatheorem.com" : @{ + kTSKEnforcePinning : @YES, + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmEcDsaSecp384r1], + kTSKPublicKeyHashes : @[@"58qRu/uxh4gFezqAcERupSkRYBlBAvfcw7mEjGPLnNU=", // CA key + @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key + ]}}}; + + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + + [TSKNSURLSessionDelegateProxy swizzleNSURLSessionConstructors:trustKit]; + +} + + +- (void)testNSURLConnection +{ + NSDictionary *trustKitConfig = + @{ + kTSKPinnedDomains : + @{ + @"www.datatheorem.com" : @{ + kTSKEnforcePinning : @YES, + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmEcDsaSecp384r1], + kTSKPublicKeyHashes : @[@"58qRu/uxh4gFezqAcERupSkRYBlBAvfcw7mEjGPLnNU=", // CA key + @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key + ]}}}; + + TrustKit *trustKit = [[TrustKit alloc] initWithConfiguration:trustKitConfig]; + + [TSKNSURLConnectionDelegateProxy swizzleNSURLConnectionConstructors:trustKit]; + +} + +@end From e9436529de044c9cf98d66b8f905e93d085c47b6 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 22:36:08 -0700 Subject: [PATCH 100/126] Restore previous code to fix SPKI generation on iOS 9 --- TrustKit/Pinning/TSKSPKIHashCache.m | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/TrustKit/Pinning/TSKSPKIHashCache.m b/TrustKit/Pinning/TSKSPKIHashCache.m index 57f128fd..d8f4a8a5 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -11,13 +11,13 @@ #import "TSKSPKIHashCache.h" #import "../TSKLog.h" +#include #import #if TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED <= 100000 // Need to support iOS before 10.0 // The one and only way to get a key's data in a buffer on iOS is to put it in the Keychain and then ask for the data back... #define LEGACY_IOS_KEY_EXTRACTION 1 -static const NSString *kTSKKeychainPublicKeyTag = @"TSKKeychainPublicKeyTag"; // Used to add and find the public key in the Keychain #endif #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 @@ -85,9 +85,9 @@ @interface TSKSPKIHashCache () @end #if LEGACY_IOS_KEY_EXTRACTION -@interface TSKSPKIHashCache () -@property (nonatomic) dispatch_queue_t keychainQueue; -@end + +static const NSString *kTSKKeychainPublicKeyTag = @"TSKKeychainPublicKeyTag"; // Used to add and find the public key in the Keychain +static pthread_mutex_t _keychainLock; // Used to lock access to our Keychain item @interface TSKSPKIHashCache (LegacyIos) - (NSData *)getPublicKeyDataFromCertificate_legacy_ios:(SecCertificateRef)certificate; @@ -137,15 +137,17 @@ - (instancetype)initWithIdentifier:(NSString *)uniqueIdentifier } #if LEGACY_IOS_KEY_EXTRACTION - _keychainQueue = dispatch_queue_create("TSKSPKIKeychainLock", DISPATCH_QUEUE_SERIAL); + pthread_mutex_init(&_keychainLock, NULL); // Cleanup the Keychain in case the App previously crashed NSMutableDictionary * publicKeyGet = [[NSMutableDictionary alloc] init]; [publicKeyGet setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; [publicKeyGet setObject:(kTSKKeychainPublicKeyTag) forKey:(__bridge id)kSecAttrApplicationTag]; [publicKeyGet setObject:(__bridge id)(kCFBooleanTrue) forKey:(__bridge id)kSecReturnData]; - dispatch_sync(_keychainQueue, ^{ + pthread_mutex_lock(&_keychainLock); + { SecItemDelete((__bridge CFDictionaryRef)(publicKeyGet)); - }); + } + pthread_mutex_unlock(&_keychainLock); #endif } return self; @@ -352,12 +354,13 @@ - (NSData *)getPublicKeyDataFromCertificate_legacy_ios:(SecCertificateRef)certif [publicKeyGet setObject:(kTSKKeychainPublicKeyTag) forKey:(__bridge id)kSecAttrApplicationTag]; [publicKeyGet setObject:(__bridge id)(kCFBooleanTrue) forKey:(__bridge id)kSecReturnData]; - // Get the key bytes from the Keychain atomically - dispatch_sync(self.keychainQueue, ^{ + pthread_mutex_lock(&_keychainLock); + { resultAdd = SecItemAdd((__bridge CFDictionaryRef) peerPublicKeyAdd, (void *)&publicKeyData); resultDel = SecItemDelete((__bridge CFDictionaryRef)(publicKeyGet)); - }); + } + pthread_mutex_unlock(&_keychainLock); CFRelease(publicKey); if ((resultAdd != errSecSuccess) || (resultDel != errSecSuccess)) From 779f34b08cb901198e00428652c0b6acc0c8c5f2 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 22:39:01 -0700 Subject: [PATCH 101/126] CircleCI --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 2f312548..4c50d00b 100644 --- a/circle.yml +++ b/circle.yml @@ -16,7 +16,7 @@ test: CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= -destination 'platform=iOS Simulator,name=iPhone 7,OS=10.3' - -sdk iphonesimulator10.2 + -sdk iphonesimulator10.3 -scheme "TrustKit" # macOS 10.12 platform with macOS 10.12 SDK From b694e750ab35fadc93e7b203d80c3c165cad63dc Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 26 Jun 2017 23:03:00 -0700 Subject: [PATCH 102/126] Revert back to dispatch_sync() and fix the bug --- TrustKit/Pinning/TSKSPKIHashCache.m | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/TrustKit/Pinning/TSKSPKIHashCache.m b/TrustKit/Pinning/TSKSPKIHashCache.m index d8f4a8a5..bc610732 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -11,7 +11,6 @@ #import "TSKSPKIHashCache.h" #import "../TSKLog.h" -#include #import #if TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED <= 100000 @@ -87,7 +86,10 @@ @interface TSKSPKIHashCache () #if LEGACY_IOS_KEY_EXTRACTION static const NSString *kTSKKeychainPublicKeyTag = @"TSKKeychainPublicKeyTag"; // Used to add and find the public key in the Keychain -static pthread_mutex_t _keychainLock; // Used to lock access to our Keychain item + +@interface TSKSPKIHashCache () +@property (nonatomic) dispatch_queue_t keychainQueue; +@end @interface TSKSPKIHashCache (LegacyIos) - (NSData *)getPublicKeyDataFromCertificate_legacy_ios:(SecCertificateRef)certificate; @@ -137,17 +139,15 @@ - (instancetype)initWithIdentifier:(NSString *)uniqueIdentifier } #if LEGACY_IOS_KEY_EXTRACTION - pthread_mutex_init(&_keychainLock, NULL); + _keychainQueue = dispatch_queue_create("TSKSPKIKeychainLock", DISPATCH_QUEUE_SERIAL); // Cleanup the Keychain in case the App previously crashed NSMutableDictionary * publicKeyGet = [[NSMutableDictionary alloc] init]; [publicKeyGet setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; [publicKeyGet setObject:(kTSKKeychainPublicKeyTag) forKey:(__bridge id)kSecAttrApplicationTag]; [publicKeyGet setObject:(__bridge id)(kCFBooleanTrue) forKey:(__bridge id)kSecReturnData]; - pthread_mutex_lock(&_keychainLock); - { + dispatch_sync(_keychainQueue, ^{ SecItemDelete((__bridge CFDictionaryRef)(publicKeyGet)); - } - pthread_mutex_unlock(&_keychainLock); + }); #endif } return self; @@ -321,7 +321,7 @@ @implementation TSKSPKIHashCache (LegacyIOS) - (NSData *)getPublicKeyDataFromCertificate_legacy_ios:(SecCertificateRef)certificate { - NSData *publicKeyData = nil; + __block NSData *publicKeyData = nil; __block OSStatus resultAdd, __block resultDel = noErr; SecKeyRef publicKey; SecTrustRef tempTrust; @@ -354,13 +354,12 @@ - (NSData *)getPublicKeyDataFromCertificate_legacy_ios:(SecCertificateRef)certif [publicKeyGet setObject:(kTSKKeychainPublicKeyTag) forKey:(__bridge id)kSecAttrApplicationTag]; [publicKeyGet setObject:(__bridge id)(kCFBooleanTrue) forKey:(__bridge id)kSecReturnData]; + // Get the key bytes from the Keychain atomically - pthread_mutex_lock(&_keychainLock); - { + dispatch_sync(self.keychainQueue, ^{ resultAdd = SecItemAdd((__bridge CFDictionaryRef) peerPublicKeyAdd, (void *)&publicKeyData); resultDel = SecItemDelete((__bridge CFDictionaryRef)(publicKeyGet)); - } - pthread_mutex_unlock(&_keychainLock); + }); CFRelease(publicKey); if ((resultAdd != errSecSuccess) || (resultDel != errSecSuccess)) From fc867fcc4585aef92f0cbc1116b7ca741fb79a4e Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 3 Jul 2017 10:35:12 -0700 Subject: [PATCH 103/126] Clarify type --- TrustKit/TrustKit.h | 4 ++-- TrustKit/TrustKit.m | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index bf205d1d..1c1e25cb 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -128,7 +128,7 @@ NS_ASSUME_NONNULL_BEGIN already been initialized. */ -+ (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig; ++ (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig; /** @@ -179,7 +179,7 @@ NS_ASSUME_NONNULL_BEGIN @param trustKitConfig A dictionary containing various keys for configuring the SSL pinning policy. */ -- (instancetype)initWithConfiguration:(NSDictionary * _Nullable)trustKitConfig; +- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig; diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 7b78a94c..00329b0d 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -87,7 +87,7 @@ + (instancetype)sharedInstance return sharedTrustKit; } -+ (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig ++ (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig { TSKLog(@"Configuration passed via explicit call to initializeWithConfiguration:"); @@ -110,13 +110,13 @@ + (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig #pragma mark Instance Initialization -- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig +- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig { return [self initWithConfiguration:trustKitConfig isSingleton:NO]; } -- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig isSingleton:(BOOL)isSingleton +- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig isSingleton:(BOOL)isSingleton { NSParameterAssert(trustKitConfig); if (!trustKitConfig) { From 5d67c2360a6afac9792c6b325d9a58aaa9287e44 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Mon, 3 Jul 2017 10:45:33 -0700 Subject: [PATCH 104/126] Clarify type --- TrustKit/TrustKit.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 00329b0d..a461bd41 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -63,7 +63,7 @@ void TSKLog(NSString *format, ...) #pragma mark TrustKit Initialization Helper Functions @interface TrustKit () -- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig isSingleton:(BOOL)isSingleton; +- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig isSingleton:(BOOL)isSingleton; @property (nonatomic) TSKBackgroundReporter *pinFailureReporter; @property (nonatomic) dispatch_queue_t pinFailureReporterQueue; From 6793ace94ea544b68c4749933691e9d047c786b6 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Thu, 6 Jul 2017 23:23:21 -0400 Subject: [PATCH 105/126] Some minor improvements. If setLoggerBlock is not called, and not compiled with -DDEBUG, no longer create unneeded format strings. --- TrustKit/TSKPinningValidatorResult.m | 1 - TrustKit/TSKPinningValidator_Private.h | 20 +++++------ TrustKit/TrustKit.m | 50 +++++++++++++------------- 3 files changed, 34 insertions(+), 37 deletions(-) diff --git a/TrustKit/TSKPinningValidatorResult.m b/TrustKit/TSKPinningValidatorResult.m index ec46be85..363b1a4b 100644 --- a/TrustKit/TSKPinningValidatorResult.m +++ b/TrustKit/TSKPinningValidatorResult.m @@ -42,7 +42,6 @@ - (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHost _evaluationResult = validationResult; _finalTrustDecision = finalTrustDecision; _validationDuration = validationDuration; - _certificateChain = nil; } return self; } diff --git a/TrustKit/TSKPinningValidator_Private.h b/TrustKit/TSKPinningValidator_Private.h index 9251d244..084242f2 100644 --- a/TrustKit/TSKPinningValidator_Private.h +++ b/TrustKit/TSKPinningValidator_Private.h @@ -6,9 +6,7 @@ // Copyright © 2017 TrustKit. All rights reserved. // -#ifndef TSKPinningValidator_Private_h -#define TSKPinningValidator_Private_h - +NS_ASSUME_NONNULL_BEGIN /* Methods that are internal to TrustKit */ @interface TSKPinningValidator (Internal) @@ -23,25 +21,23 @@ @param validationCallback The callback invoked with validation results @return Initialized instance */ -- (instancetype _Nullable)initWithDomainPinningPolicies:(NSDictionary *_Nonnull)domainPinningPolicies - hashCache:(TSKSPKIHashCache * _Nonnull)hashCache +- (instancetype _Nullable)initWithDomainPinningPolicies:(NSDictionary *)domainPinningPolicies + hashCache:(TSKSPKIHashCache *)hashCache ignorePinsForUserTrustAnchors:(BOOL)ignorePinsForUserTrustAnchors - validationCallbackQueue:(dispatch_queue_t _Nonnull)validationCallbackQueue - validationCallback:(TSKPinningValidatorCallback _Nonnull)validationCallback; + validationCallbackQueue:(dispatch_queue_t)validationCallbackQueue + validationCallback:(TSKPinningValidatorCallback)validationCallback; @end @interface TSKPinningValidatorResult (Internal) -- (instancetype _Nullable)initWithServerHostname:(NSString * _Nonnull)serverHostname - serverTrust:(SecTrustRef _Nonnull)serverTrust +- (instancetype _Nullable)initWithServerHostname:(NSString *)serverHostname + serverTrust:(SecTrustRef)serverTrust validationResult:(TSKTrustEvaluationResult)validationResult finalTrustDecision:(TSKTrustDecision)finalTrustDecision validationDuration:(NSTimeInterval)validationDuration; @end - -#endif /* TSKPinningValidator_Private_h */ - +NS_ASSUME_NONNULL_END diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index a461bd41..4e653cae 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -24,59 +24,58 @@ // Info.plist key we read the public key hashes from static NSString * const kTSKConfiguration = @"TSKConfiguration"; +// Default report URI - can be disabled with TSKDisableDefaultReportUri +// Email info@datatheorem.com if you need a free dashboard to see your App's reports +static NSString * const kTSKDefaultReportUri = @"https://overmind.datatheorem.com/trustkit/report"; + +static const char kTSKPinFailureReporterQueueLabel[] = "com.datatheorem.trustkit.reporterqueue"; + #pragma mark TrustKit Global State + // Shared TrustKit singleton instance -static TrustKit *sharedTrustKit = nil; +static TrustKit *sharedTrustKit; // A shared hash cache for use by all TrustKit instances static TSKSPKIHashCache *sharedHashCache; -static char kTSKPinFailureReporterQueueLabel[] = "com.datatheorem.trustkit.reporterqueue"; - -// Default report URI - can be disabled with TSKDisableDefaultReportUri -// Email info@datatheorem.com if you need a free dashboard to see your App's reports -static NSString * const kTSKDefaultReportUri = @"https://overmind.datatheorem.com/trustkit/report"; - - // Default logger block: only log in debug builds and add TrustKit at the beginning of the line -void (^_loggerBlock)(NSString *) = ^void(NSString *message) -{ #if DEBUG - NSLog(@"=== TrustKit: %@", message); +void (^_loggerBlock)(NSString *) = ^void(NSString *message) { NSLog(@"=== TrustKit: %@", message); }; +#else +void (^_loggerBlock)(NSString *) = NULL; #endif -}; // The logging function we use within TrustKit void TSKLog(NSString *format, ...) { - va_list args; - va_start(args, format); - NSString *message = [[NSString alloc] initWithFormat: format arguments:args]; - va_end(args); - _loggerBlock(message); + if (_loggerBlock) { + va_list args; + va_start(args, format); + NSString *message = [[NSString alloc] initWithFormat:format arguments:args]; + va_end(args); + _loggerBlock(message); + } } +#pragma mark TrustKit Private API -#pragma mark TrustKit Initialization Helper Functions @interface TrustKit () -- (instancetype)initWithConfiguration:(NSDictionary *)trustKitConfig isSingleton:(BOOL)isSingleton; - @property (nonatomic) TSKBackgroundReporter *pinFailureReporter; @property (nonatomic) dispatch_queue_t pinFailureReporterQueue; - @property (nonatomic, readonly, nullable) NSDictionary *configuration; - @end @implementation TrustKit + #pragma mark Singleton Initialization + + (instancetype)sharedInstance { if (!sharedTrustKit) { @@ -186,13 +185,15 @@ - (instancetype)initWithConfiguration:(NSDictionary *)notedHostnamePinningPolicy { From 97f9115f574e5b53042a52bf4d859a070c602e08 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Fri, 7 Jul 2017 00:33:14 -0400 Subject: [PATCH 106/126] Copyright notices --- TrustKit/Reporting/vendor_identifier.h | 17 ++++++++++------- TrustKit/Reporting/vendor_identifier.m | 17 ++++++++++------- .../TSKNSURLConnectionDelegateProxy.h | 17 ++++++++++------- .../TSKNSURLConnectionDelegateProxy.m | 17 ++++++++++------- .../Swizzling/TSKNSURLSessionDelegateProxy.h | 17 ++++++++++------- .../Swizzling/TSKNSURLSessionDelegateProxy.m | 17 ++++++++++------- TrustKit/TSKPinningValidatorCallback.h | 17 ++++++++++------- TrustKit/TSKPinningValidator_Private.h | 17 ++++++++++------- TrustKit/TSKTrustDecision.h | 1 + TrustKit/TSKTrustKitConfig.h | 18 ++++++++++-------- TrustKit/TSKTrustKitConfig.m | 18 +++++++++++------- TrustKit/configuration_utils.h | 17 ++++++++++------- TrustKit/configuration_utils.m | 17 ++++++++++------- TrustKit/parse_configuration.h | 17 ++++++++++------- TrustKit/parse_configuration.m | 17 ++++++++++------- 15 files changed, 142 insertions(+), 99 deletions(-) diff --git a/TrustKit/Reporting/vendor_identifier.h b/TrustKit/Reporting/vendor_identifier.h index c61ef6db..4a8bd6f2 100644 --- a/TrustKit/Reporting/vendor_identifier.h +++ b/TrustKit/Reporting/vendor_identifier.h @@ -1,10 +1,13 @@ -// -// vendor_identifier.h -// TrustKit -// -// Created by Alban Diquet on 8/24/16. -// Copyright © 2016 TrustKit. All rights reserved. -// +/* + + vendor_identifier.h + TrustKit + + Copyright 2016 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ @import Foundation; diff --git a/TrustKit/Reporting/vendor_identifier.m b/TrustKit/Reporting/vendor_identifier.m index 6fec2e99..54d52dd8 100644 --- a/TrustKit/Reporting/vendor_identifier.m +++ b/TrustKit/Reporting/vendor_identifier.m @@ -1,10 +1,13 @@ -// -// vendor_identifier.m -// TrustKit -// -// Created by Alban Diquet on 8/24/16. -// Copyright © 2016 TrustKit. All rights reserved. -// +/* + + vendor_identifier.m + TrustKit + + Copyright 2016 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ #import "vendor_identifier.h" diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h index 68ef9465..010c8a4b 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h @@ -1,10 +1,13 @@ -// -// TSKNSURLConnectionDelegateProxy.h -// TrustKit -// -// Created by Alban Diquet on 10/7/15. -// Copyright © 2015 TrustKit. All rights reserved. -// +/* + + TSKNSURLConnectionDelegateProxy.h + TrustKit + + Copyright 2015 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ @import Foundation; diff --git a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m index ac26a84f..859cde5b 100644 --- a/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.m @@ -1,10 +1,13 @@ -// -// TSKNSURLConnectionDelegateProxy.m -// TrustKit -// -// Created by Alban Diquet on 10/7/15. -// Copyright © 2015 TrustKit. All rights reserved. -// +/* + + TSKNSURLConnectionDelegateProxy.h + TrustKit + + Copyright 2015 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ #import "TSKNSURLConnectionDelegateProxy.h" #import "../TrustKit.h" diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h index 7bf1b3dc..436d1e5b 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h @@ -1,10 +1,13 @@ -// -// TSKNSURLSessionDelegateProxy.h -// TrustKit -// -// Created by Alban Diquet on 10/11/15. -// Copyright © 2015 TrustKit. All rights reserved. -// +/* + + TSKNSURLSessionDelegateProxy.h + TrustKit + + Copyright 2015 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ @import Foundation; diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m index 2cdb2a89..72c46e46 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m @@ -1,10 +1,13 @@ -// -// TSKNSURLSessionDelegateProxy.m -// TrustKit -// -// Created by Alban Diquet on 10/11/15. -// Copyright © 2015 TrustKit. All rights reserved. -// +/* + + TSKNSURLSessionDelegateProxy.m + TrustKit + + Copyright 2015 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ #import "TSKNSURLSessionDelegateProxy.h" #import "../TrustKit.h" diff --git a/TrustKit/TSKPinningValidatorCallback.h b/TrustKit/TSKPinningValidatorCallback.h index 14139718..6ca320a8 100644 --- a/TrustKit/TSKPinningValidatorCallback.h +++ b/TrustKit/TSKPinningValidatorCallback.h @@ -1,10 +1,13 @@ -// -// TSKPinningValidatorCallback.h -// TrustKit -// -// Created by Alban Diquet on 6/26/17. -// Copyright © 2017 TrustKit. All rights reserved. -// +/* + + TSKPinningValidatorCallback.h + TrustKit + + Copyright 2017 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ #ifndef TSKPinningValidatorCallback_h #define TSKPinningValidatorCallback_h diff --git a/TrustKit/TSKPinningValidator_Private.h b/TrustKit/TSKPinningValidator_Private.h index 084242f2..3eb0b498 100644 --- a/TrustKit/TSKPinningValidator_Private.h +++ b/TrustKit/TSKPinningValidator_Private.h @@ -1,10 +1,13 @@ -// -// TSKPinningValidator_Private.h -// TrustKit -// -// Created by Alban Diquet on 6/23/17. -// Copyright © 2017 TrustKit. All rights reserved. -// +/* + + TSKPinningValidator_Private.h + TrustKit + + Copyright 2017 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ NS_ASSUME_NONNULL_BEGIN diff --git a/TrustKit/TSKTrustDecision.h b/TrustKit/TSKTrustDecision.h index 8fd0e883..50a684e2 100644 --- a/TrustKit/TSKTrustDecision.h +++ b/TrustKit/TSKTrustDecision.h @@ -8,6 +8,7 @@ See AUTHORS file for the list of project authors. */ + @import Foundation; /** diff --git a/TrustKit/TSKTrustKitConfig.h b/TrustKit/TSKTrustKitConfig.h index 87dd6533..c7946a55 100644 --- a/TrustKit/TSKTrustKitConfig.h +++ b/TrustKit/TSKTrustKitConfig.h @@ -1,11 +1,13 @@ -// -// TSKTrustKitConfig.h -// TrustKit -// -// Created by Adam Kaplan on 4/6/17. -// Copyright © 2017 TrustKit. All rights reserved. -// - +/* + + TSKTrustKitConfig.h + TrustKit + + Copyright 2017 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ @import Foundation; diff --git a/TrustKit/TSKTrustKitConfig.m b/TrustKit/TSKTrustKitConfig.m index ef2269a0..e03d5a31 100644 --- a/TrustKit/TSKTrustKitConfig.m +++ b/TrustKit/TSKTrustKitConfig.m @@ -1,10 +1,14 @@ -// -// TSKTrustKitConfig.h -// TrustKit -// -// Created by Adam Kaplan on 4/6/17. -// Copyright © 2017 TrustKit. All rights reserved. -// +/* + + TSKTrustKitConfig.m + TrustKit + + Copyright 2017 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ + #import "TSKTrustKitConfig.h" NSString * const TrustKitVersion = @"1.5.0"; diff --git a/TrustKit/configuration_utils.h b/TrustKit/configuration_utils.h index a5ba2255..c07ca2ac 100644 --- a/TrustKit/configuration_utils.h +++ b/TrustKit/configuration_utils.h @@ -1,10 +1,13 @@ -// -// configuration_utils.h -// TrustKit -// -// Created by Alban Diquet on 2/20/17. -// Copyright © 2017 TrustKit. All rights reserved. -// +/* + + configuration_utils.h + TrustKit + + Copyright 2017 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ #import "TSKPinningValidatorCallback.h" diff --git a/TrustKit/configuration_utils.m b/TrustKit/configuration_utils.m index 3ded237d..1211d4ac 100644 --- a/TrustKit/configuration_utils.m +++ b/TrustKit/configuration_utils.m @@ -1,10 +1,13 @@ -// -// configuration_utils.m -// TrustKit -// -// Created by Alban Diquet on 2/20/17. -// Copyright © 2017 TrustKit. All rights reserved. -// +/* + + configuration_utils.m + TrustKit + + Copyright 2017 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ #import "configuration_utils.h" #import "TSKTrustKitConfig.h" diff --git a/TrustKit/parse_configuration.h b/TrustKit/parse_configuration.h index 4a29fffd..a39d1507 100644 --- a/TrustKit/parse_configuration.h +++ b/TrustKit/parse_configuration.h @@ -1,10 +1,13 @@ -// -// parse_configuration.h -// TrustKit -// -// Created by Alban Diquet on 5/20/16. -// Copyright © 2016 TrustKit. All rights reserved. -// +/* + + parse_configuration.h + TrustKit + + Copyright 2016 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ #ifndef parse_configuration_h #define parse_configuration_h diff --git a/TrustKit/parse_configuration.m b/TrustKit/parse_configuration.m index aa389d10..3e205984 100644 --- a/TrustKit/parse_configuration.m +++ b/TrustKit/parse_configuration.m @@ -1,10 +1,13 @@ -// -// parse_configuration.m -// TrustKit -// -// Created by Alban Diquet on 5/20/16. -// Copyright © 2016 TrustKit. All rights reserved. -// +/* + + parse_configuration.m + TrustKit + + Copyright 2016 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ #import "TSKTrustKitConfig.h" #import "Dependencies/domain_registry/domain_registry.h" From 2da57d971eb4dc812bcdf3193b772a4cf4ca63b3 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Fri, 7 Jul 2017 00:49:12 -0400 Subject: [PATCH 107/126] Fix demo project after migration to TSKPinningValidatorCallback.h --- TrustKit.xcodeproj/project.pbxproj | 2 ++ TrustKit/TSKPinningValidatorCallback.h | 2 +- TrustKitDemo/TrustKitDemo/AppDelegate.m | 11 +++++------ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index a86ec67f..ed6c5e2d 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -255,6 +255,7 @@ FC4CAC7B1E958E0500DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; FC4CAC7C1E96891A00DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; FC4CAC7D1E96891B00DAC41E /* TSKReportsRateLimiterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC4CAC7A1E958E0500DAC41E /* TSKReportsRateLimiterTests.m */; }; + FCBF75E41F0F4C97004A74AB /* TSKPinningValidatorCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CF27A921F0185EC009369B0 /* TSKPinningValidatorCallback.h */; settings = {ATTRIBUTES = (Public, ); }; }; FCC1DD081EECD19E00AB3D81 /* anchor-ca.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD051EECD19E00AB3D81 /* anchor-ca.cert.pem */; }; FCC1DD091EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD061EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem */; }; FCC1DD0A1EECD19E00AB3D81 /* anchor-intermediate.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD071EECD19E00AB3D81 /* anchor-intermediate.cert.pem */; }; @@ -831,6 +832,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + FCBF75E41F0F4C97004A74AB /* TSKPinningValidatorCallback.h in Headers */, 8C84CCDA1D6E5D5A009B3E7D /* registry_types.h in Headers */, FC1A09001E57A4BB0055B12C /* TSKPinningValidatorResult.h in Headers */, FCE7D6321EE9FDFD0081EEEF /* TSKPublicKeyAlgorithm.h in Headers */, diff --git a/TrustKit/TSKPinningValidatorCallback.h b/TrustKit/TSKPinningValidatorCallback.h index 6ca320a8..3e67d94c 100644 --- a/TrustKit/TSKPinningValidatorCallback.h +++ b/TrustKit/TSKPinningValidatorCallback.h @@ -43,7 +43,7 @@ typedef NSDictionary TKSDomainPinningPolicy; Lastly, the callback is always invoked after the validation has been completed, and therefore cannot be used to modify the result of the validation (for example to accept invalid certificates). */ -typedef void (^TSKPinningValidatorCallback)(TSKPinningValidatorResult * _Nonnull, NSString * _Nonnull, TKSDomainPinningPolicy * _Nonnull); +typedef void (^TSKPinningValidatorCallback)(TSKPinningValidatorResult * _Nonnull result, NSString * _Nonnull notedHostname, TKSDomainPinningPolicy * _Nonnull policy); diff --git a/TrustKitDemo/TrustKitDemo/AppDelegate.m b/TrustKitDemo/TrustKitDemo/AppDelegate.m index fddc7d01..13140861 100644 --- a/TrustKitDemo/TrustKitDemo/AppDelegate.m +++ b/TrustKitDemo/TrustKitDemo/AppDelegate.m @@ -12,7 +12,7 @@ #import "AppDelegate.h" #import #import -#import +#import @interface AppDelegate () @@ -31,7 +31,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // }; // [TrustKit setLoggerBlock:loggerBlock]; - // Initialize TrustKit NSDictionary *trustKitConfig = @{ @@ -71,14 +70,14 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( }}}; [TrustKit initializeWithConfiguration:trustKitConfig]; - + // Demonstrate how to receive pin validation notifications (only useful for performance/metrics) - [TrustKit sharedInstance].validationDelegateQueue =dispatch_get_main_queue(); - [TrustKit sharedInstance].validationDelegateCallback = ^(TSKPinningValidatorResult * _Nonnull result) { + [TrustKit sharedInstance].pinningValidatorCallbackQueue =dispatch_get_main_queue(); + [TrustKit sharedInstance].pinningValidatorCallback = ^(TSKPinningValidatorResult *result, NSString *notedHostname, TKSDomainPinningPolicy *policy) { NSLog(@"Received pinning validation notification:\n\tDuration: %0.4f\n\tDecision: %ld\n\tResult: %ld\n\tHostname: %@", result.validationDuration, (long)result.finalTrustDecision, - (long)result.validationResult, + (long)result.evaluationResult, result.serverHostname); }; From 8662743f69990a67d9850c6b577a5a834904e136 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 7 Jul 2017 09:51:48 +0200 Subject: [PATCH 108/126] No need for reflection --- TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m index 2cdb2a89..16a6a59e 100644 --- a/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m +++ b/TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.m @@ -28,7 +28,7 @@ + (void)swizzleNSURLSessionConstructors:(TrustKit *)trustKit // + sessionWithConfiguration:delegate:delegateQueue: #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wshadow" - RSSwizzleClassMethod(NSClassFromString(@"NSURLSession"), + RSSwizzleClassMethod(NSURLSession.class, @selector(sessionWithConfiguration:delegate:delegateQueue:), RSSWReturnType(NSURLSession *), RSSWArguments(NSURLSessionConfiguration * _Nonnull configuration, id _Nullable delegate, NSOperationQueue * _Nullable queue), From 8d0f7ef76d65bae8ba7df64927bc78f3bbecd1f8 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 7 Jul 2017 09:54:36 +0200 Subject: [PATCH 109/126] Remove check that's too agressive --- TrustKit/TrustKit.m | 8 -------- 1 file changed, 8 deletions(-) diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index a461bd41..fbc35e50 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -155,14 +155,6 @@ - (instancetype)initWithConfiguration:(NSDictionary Date: Fri, 7 Jul 2017 09:56:24 +0200 Subject: [PATCH 110/126] Re-add type annotations --- TrustKit/configuration_utils.h | 2 +- TrustKit/configuration_utils.m | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/TrustKit/configuration_utils.h b/TrustKit/configuration_utils.h index a5ba2255..4d179e76 100644 --- a/TrustKit/configuration_utils.h +++ b/TrustKit/configuration_utils.h @@ -11,4 +11,4 @@ @import Foundation; // Figure out if a specific domain is pinned and retrieve this domain's configuration key; returns nil if no configuration was found -NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *domainPinningPolicies); +NSString * _Nullable getPinningConfigurationKeyForDomain(NSString * _Nonnull hostname , NSDictionary * _Nonnull domainPinningPolicies); diff --git a/TrustKit/configuration_utils.m b/TrustKit/configuration_utils.m index 3ded237d..1cb7e807 100644 --- a/TrustKit/configuration_utils.m +++ b/TrustKit/configuration_utils.m @@ -38,8 +38,7 @@ static BOOL isSubdomain(NSString *domain, NSString *subdomain) return NO; } - -NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *domainPinningPolicies) +NSString * _Nullable getPinningConfigurationKeyForDomain(NSString * _Nonnull hostname , NSDictionary * _Nonnull domainPinningPolicies) { NSString *notedHostname = nil; if (domainPinningPolicies[hostname] == nil) From ff590c93d8724c345a47599dabe69f99d33e23c3 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 7 Jul 2017 10:17:47 +0200 Subject: [PATCH 111/126] Fix CI --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 4c50d00b..58a40ef4 100644 --- a/circle.yml +++ b/circle.yml @@ -15,7 +15,7 @@ test: - xcodebuild clean test CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= - -destination 'platform=iOS Simulator,name=iPhone 7,OS=10.3' + -destination 'platform=iOS Simulator,name=iPhone 7,OS=10.3.1' -sdk iphonesimulator10.3 -scheme "TrustKit" From ad10eace7f63929a3e3b9bfcb8f69a982a666346 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 7 Jul 2017 10:41:18 +0200 Subject: [PATCH 112/126] Update podpsec --- TrustKit.podspec | 1 + 1 file changed, 1 insertion(+) diff --git a/TrustKit.podspec b/TrustKit.podspec index 9cc4d2b8..de6d1ab7 100644 --- a/TrustKit.podspec +++ b/TrustKit.podspec @@ -18,6 +18,7 @@ Pod::Spec.new do |s| 'TrustKit/TrustKit.h', 'TrustKit/TSKTrustKitConfig.h', 'TrustKit/TSKPinningValidator.h', + 'TrustKit/TSKPinningValidatorCallback.h', 'TrustKit/TSKPinValidatorResult.h', 'TrustKit/Pinning/TSKPublicKeyAlgorithm.h' ] From 624fc5a9a9dadfa59e46a7aa51604f546022afbe Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 7 Jul 2017 11:36:12 +0200 Subject: [PATCH 113/126] Update documentation --- .jazzy.yaml | 1 + docs/documentation-readme.md | 18 +++++------------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/.jazzy.yaml b/.jazzy.yaml index afaddf71..e76b8144 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -11,6 +11,7 @@ objc: true umbrella_header: "TrustKit/TrustKit.h" framework_root: "./TrustKit" +readme: "docs/documentation-readme.md" output: "docs/documentation" custom_categories: diff --git a/docs/documentation-readme.md b/docs/documentation-readme.md index 19619f03..70784e99 100755 --- a/docs/documentation-readme.md +++ b/docs/documentation-readme.md @@ -1,18 +1,10 @@ # TrustKit Documentation -TrustKit is an open source framework that makes it easy to deploy SSL public key -pinning in any iOS, macOS, tvOS or watchOS App. +TrustKit is an open source framework that makes it easy to deploy SSL public key pinning in any iOS, macOS, tvOS or watchOS App. -This is the API documentation for TrustKit. For an overview of the framework and -a more general guide to using it, see the project's page at -https://datatheorem.github.io/TrustKit . +This is the API documentation for TrustKit. A "Getting Started" guide is available at https://github.com/datatheorem/TrustKit/blob/master/docs/getting-started.md. -TrustKit requires iOS 7.0, macOS 10.9, tvOS 10.0 or watchOS 3.0 as the minimum -deployment target. +TrustKit exposes two core classes for enabling SSL pinning in an App: -Two classes are available enabling SSL pinning in an App: - -* `TrustKit`, for programmatically configuring the global SSL pinning policy within an -App. -* `TSKPinningValidator`, for manually validating a certificate chain against the App's -configured pinning policy. +* `TrustKit` for configuring an SSL pinning policy and initializing the framework. +* `TSKPinningValidator`, for validating a server's certificate chain against an SSL pinning policy. From b48c84f2cfaa12dd4ee92fe548d6e1537faa78a0 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 7 Jul 2017 11:43:45 +0200 Subject: [PATCH 114/126] Update documentation --- docs/getting-started.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 40f56f30..b0d8f14c 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -77,15 +77,7 @@ folder on disk. ### Configuring a Pinning Policy Enabling TrustKit within an App requires generating a pinning policy and then -initializing TrustKit with this policy. There are two different ways to supply -a pinning policy to TrustKit: - -* By adding configuration keys to the App's _Info.plist_ file under a -`TSKConfiguration` dictionary key: - - ![](https://datatheorem.github.io/TrustKit/images/linking3_dynamic.png) - -* Programmatically, by calling the `initializeWithConfiguration:` method with your +initializing TrustKit by calling the `initializeWithConfiguration:` method with your pinning policy. A pinning policy is a dictionary of domain names and pinning configuration keys. @@ -212,10 +204,19 @@ However, this approach still requires some testing as it seems like the #### Consider leveraging auto-swizzling for simple Apps -By setting `kTSKSwizzleNetworkDelegates` to `YES`, TrustKit will perform method -swizzling on the App's `NSURLSession` and `NSURLConnection` delegates in order -to automatically perform SSL pinning validation against the server's certificate -chain, based on the configured pinning policy. This allows deploying TrustKit +For simple Apps, TrustKit can be deployed without having to modify the App's +source code. + +First, TrustKit should be initialized by adding configuration keys to the App's +_Info.plist_ file under a `TSKConfiguration` dictionary key, instead of using +`TrustKit`'s initialization method: + + ![](https://datatheorem.github.io/TrustKit/images/linking3_dynamic.png) + +By setting the `kTSKSwizzleNetworkDelegates` key to `YES`, TrustKit will then +perform method swizzling on the App's `NSURLSession` and `NSURLConnection` +delegates in order to automatically perform SSL pinning validation against the server's +certificate chain, based on the configured pinning policy. This allows deploying TrustKit without changing the App's source code. Auto-swizzling should only be enabled for simple Apps, as it may not work properly From a74d5c0db90e43d6f7d7c185b2dfd9e8e89667c0 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 7 Jul 2017 11:48:13 +0200 Subject: [PATCH 115/126] Re-generate documentation --- docs/documentation/Classes.html | 335 ---- .../Classes/TSKPinningValidator.html | 311 ++-- .../Classes/TSKPinningValidatorResult.html | 405 +++++ docs/documentation/Classes/TrustKit.html | 465 +++-- docs/documentation/Constants.html | 1118 ------------ .../Domain Configuration Keys.html | 621 +++++++ docs/documentation/Enums.html | 200 --- .../documentation/Enums/TSKTrustDecision.html | 190 +- .../Enums/TSKTrustEvaluationResult.html | 396 +++++ .../Global Configuration Keys.html | 388 +++++ .../Implementing Pinning Validation.html | 281 +++ docs/documentation/Initalizing TrustKit.html | 314 ++++ docs/documentation/Other Constants.html | 229 +++ .../Public Key Algorithm Keys.html | 363 ++++ .../Setting up a Validation Callback.html | 353 ++++ docs/documentation/Type Definitions.html | 340 ---- docs/documentation/css/jazzy.css | 430 ++--- .../Contents/Resources/Documents/Classes.html | 335 ---- .../Classes/TSKPinningValidator.html | 311 ++-- .../Classes/TSKPinningValidatorResult.html | 405 +++++ .../Resources/Documents/Classes/TrustKit.html | 465 +++-- .../Resources/Documents/Constants.html | 1118 ------------ .../Documents/Domain Configuration Keys.html | 621 +++++++ .../Contents/Resources/Documents/Enums.html | 200 --- .../Documents/Enums/TSKTrustDecision.html | 190 +- .../Enums/TSKTrustEvaluationResult.html | 396 +++++ .../Documents/Global Configuration Keys.html | 388 +++++ .../Implementing Pinning Validation.html | 281 +++ .../Documents/Initalizing TrustKit.html | 314 ++++ .../Resources/Documents/Other Constants.html | 229 +++ .../Documents/Public Key Algorithm Keys.html | 363 ++++ .../Setting up a Validation Callback.html | 353 ++++ .../Resources/Documents/Type Definitions.html | 340 ---- .../Resources/Documents/css/jazzy.css | 430 ++--- .../Resources/Documents/img/spinner.gif | Bin 0 -> 1849 bytes .../Contents/Resources/Documents/index.html | 185 +- .../Contents/Resources/Documents/js/jazzy.js | 9 +- .../Resources/Documents/js/jazzy.search.js | 62 + .../Resources/Documents/js/lunr.min.js | 6 + .../Documents/js/typeahead.jquery.js | 1538 +++++++++++++++++ .../Contents/Resources/Documents/search.json | 1 + .../Resources/Documents/undocumented.json | 6 - .../Contents/Resources/docSet.dsidx | Bin 12288 -> 28672 bytes docs/documentation/img/spinner.gif | Bin 0 -> 1849 bytes docs/documentation/index.html | 185 +- docs/documentation/js/jazzy.js | 9 +- docs/documentation/js/jazzy.search.js | 62 + docs/documentation/js/lunr.min.js | 6 + docs/documentation/js/typeahead.jquery.js | 1538 +++++++++++++++++ docs/documentation/search.json | 1 + 50 files changed, 11854 insertions(+), 5232 deletions(-) delete mode 100644 docs/documentation/Classes.html create mode 100644 docs/documentation/Classes/TSKPinningValidatorResult.html delete mode 100644 docs/documentation/Constants.html create mode 100644 docs/documentation/Domain Configuration Keys.html delete mode 100644 docs/documentation/Enums.html create mode 100644 docs/documentation/Enums/TSKTrustEvaluationResult.html create mode 100644 docs/documentation/Global Configuration Keys.html create mode 100644 docs/documentation/Implementing Pinning Validation.html create mode 100644 docs/documentation/Initalizing TrustKit.html create mode 100644 docs/documentation/Other Constants.html create mode 100644 docs/documentation/Public Key Algorithm Keys.html create mode 100644 docs/documentation/Setting up a Validation Callback.html delete mode 100644 docs/documentation/Type Definitions.html delete mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes.html create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidatorResult.html delete mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Constants.html create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Domain Configuration Keys.html delete mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums.html create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustEvaluationResult.html create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Global Configuration Keys.html create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Implementing Pinning Validation.html create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Initalizing TrustKit.html create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Other Constants.html create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Public Key Algorithm Keys.html create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Setting up a Validation Callback.html delete mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Type Definitions.html create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/img/spinner.gif create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/jazzy.search.js create mode 100755 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/lunr.min.js create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/typeahead.jquery.js create mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/search.json delete mode 100644 docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/undocumented.json create mode 100644 docs/documentation/img/spinner.gif create mode 100644 docs/documentation/js/jazzy.search.js create mode 100755 docs/documentation/js/lunr.min.js create mode 100644 docs/documentation/js/typeahead.jquery.js create mode 100644 docs/documentation/search.json diff --git a/docs/documentation/Classes.html b/docs/documentation/Classes.html deleted file mode 100644 index 65e6ecb1..00000000 --- a/docs/documentation/Classes.html +++ /dev/null @@ -1,335 +0,0 @@ - - - - Classes Reference - - - - - - - - - -
- -
-
- -
-
- -
-
-
-

Classes

-

The following classes are available globally.

- -
-
-
-
    -
  • -
    - - - - TSKPinningValidator - -
    -
    -
    -
    -
    -
    -

    TSKPinningValidator is a class for manually verifying a server’s identity against the global SSL pinning policy.

    - -

    In specific scenarios, TrustKit cannot intercept outgoing SSL connections and automatically validate the server’s identity against the pinning policy:

    - -
      -
    • All connections within an App that disables TrustKit’s network delegate swizzling by setting the kTSKSwizzleNetworkDelegates configuration key to NO.
    • -
    • Connections that do not rely on the NSURLConnection or NSURLSession APIs: - -
        -
      • WKWebView connections.
      • -
      • Connections leveraging low-level network APIs (such as NSStream).
      • -
      • Connections initiated using a third-party SSL library such as OpenSSL.
      • -
    • -
    - -

    For these connections, pin validation must be manually triggered using one of the two available methods:

    - -
      -
    • evaluateTrust:forHostname: which evaluates the server’s certificate chain against the global SSL pinning policy.
    • -
    • handleChallenge:completionHandler: a helper method to be used for implementing pinning validation in challenge handler methods within NSURLSession and WKWebView delegates.
    • -
    - - See more -
    -
    -

    Declaration

    -
    -

    Objective-C

    -
    @interface TSKPinningValidator : NSObject
    - -
    -
    -

    Swift

    -
    class TSKPinningValidator : NSObject
    - -
    -
    - -
    -
    -
  • -
-
-
- -
    -
  • -
    - - - - TrustKit - -
    -
    -
    -
    -
    -
    -

    TrustKit is a class for programmatically configuring the global SSL pinning policy within an App.

    - -

    The policy can be set either by adding it to the App’s Info.plist under the TSKConfiguration key, or by programmatically supplying it using the TrustKit class described here. Throughout the App’s lifecycle, TrustKit can only be initialized once so only one of the two techniques should be used.

    - -

    A TrustKit pinning policy is a dictionary which contains some global, App-wide settings (of type TSKGlobalConfigurationKey) as well as domain-specific configuration keys (of type TSKDomainConfigurationKey) to be defined under the kTSKPinnedDomains entry. The following table shows the keys and the types of the corresponding values, and uses indentation to indicate structure:

    - -
    | Key                                          | Type       |
    -|----------------------------------------------|------------|
    -| TSKSwizzleNetworkDelegates                   | Boolean    |
    -| TSKIgnorePinningForUserDefinedTrustAnchors   | Boolean    |
    -| TSKPinnedDomains                             | Dictionary |
    -| __ <domain-name-to-pin-as-string>            | Dictionary |
    -| ____ TSKPublicKeyHashes                      | Array      |
    -| ____ TSKPublicKeyAlgorithms                  | Array      |
    -| ____ TSKIncludeSubdomains                    | Boolean    |
    -| ____ TSKExcludeSubdomainFromParentPolicy     | Boolean    |
    -| ____ TSKEnforcePinning                       | Boolean    |
    -| ____ TSKReportUris                           | Array      |
    -| ____ TSKDisableDefaultReportUri              | Boolean    |
    -
    - -

    When setting the pinning policy programmatically, it has to be supplied to the initializeWithConfiguration: method as a dictionary. For example:

    - -
       NSDictionary *trustKitConfig =
    - @{
    -   kTSKSwizzleNetworkDelegates: @NO,
    -   kTSKPinnedDomains : @{
    -           @"www.datatheorem.com" : @{
    -                   kTSKExpirationDate: @"2017-12-01",
    -                   kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048],
    -                   kTSKPublicKeyHashes : @[
    -                           @"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=",
    -                           @"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8="
    -                           ],
    -                   kTSKEnforcePinning : @NO,
    -                   kTSKReportUris : @[@"http://report.datatheorem.com/log_report"],
    -                   },
    -           @"yahoo.com" : @{
    -                   kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096],
    -                   kTSKPublicKeyHashes : @[
    -                           @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=",
    -                           @"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=",
    -                           ],
    -                   kTSKIncludeSubdomains : @YES
    -                   }
    -           }};
    -
    -   [TrustKit initializeWithConfiguration:trustKitConfig];
    -
    - -

    Similarly, TrustKit can be initialized in Swift:

    - -
           let trustKitConfig = [
    -           kTSKSwizzleNetworkDelegates: false,
    -           kTSKPinnedDomains: [
    -               "yahoo.com": [
    -                   kTSKExpirationDate: "2017-12-01",
    -                   kTSKPublicKeyAlgorithms: [kTSKAlgorithmRsa2048],
    -                   kTSKPublicKeyHashes: [
    -                       "JbQbUG5JMJUoI6brnx0x3vZF6jilxsapbXGVfjhN8Fg=",
    -                       "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="
    -                   ],]]] as [String : Any]
    -
    -       TrustKit.initialize(withConfiguration:trustKitConfig)
    -
    - -

    The various configuration keys that can be specified in the policy are described in the Constants section of the documentation.

    - -

    Lastly, once TrustKit has been initialized, kTSKValidationCompletedNotification notifications will be posted every time TrustKit validates the certificate chain of a server; these notifications provide some information about the validation that was done and can be used for example for performance measurement.

    - - See more -
    -
    -

    Declaration

    -
    -

    Objective-C

    -
    @interface TrustKit : NSObject
    - -
    -
    -

    Swift

    -
    class TrustKit : NSObject
    - -
    -
    - -
    -
    -
  • -
-
-
-
- -
-
- - - diff --git a/docs/documentation/Classes/TSKPinningValidator.html b/docs/documentation/Classes/TSKPinningValidator.html index 81e3161a..1a8ab487 100644 --- a/docs/documentation/Classes/TSKPinningValidator.html +++ b/docs/documentation/Classes/TSKPinningValidator.html @@ -4,145 +4,174 @@ TSKPinningValidator Class Reference - + + + + + + -
- -
-
-
+

+ + TrustKit Docs + +

-
+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ + + + +
-
+ + +
+ +
  • @@ -282,23 +323,16 @@

    Return Value

    -

    Helper method for handling authentication challenges received within a NSURLSessionDelegate, NSURLSessionTaskDelegate or WKNavigationDelegate.

    +

    Evaluate the supplied server trust against the SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent.

    -

    This method will evaluate the server trust within the authentication challenge against the global SSL pinning policy previously configured, and then call the completionHandler with the corresponding disposition and credential. For example, this method can be leveraged in a WKNavigationDelegate challenge handler method:

    +

    When using the NSURLSession or WKWebView network APIs, the handleChallenge:completionHandler: method should be called instead, as it is simpler to use.

    -
    - (void)webView:(WKWebView *)webView
    -didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    -completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
    -NSURLCredential *credential))completionHandler
    -{
    -    if (![TSKPinningValidator handleChallenge:challenge completionHandler:completionHandler]) 
    -    {
    -        // TrustKit did not handle this challenge: perhaps it was not for server trust
    -        // or the domain was not pinned. Fall back to the default behavior
    -        completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
    -    }
    -}
    -
    +

    When using low-level network APIs (such as NSStream), instructions on how to retrieve the connection’s serverTrust are available at https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html .

    +
    +

    Warning

    +

    If no SSL pinning policy was configured for the supplied serverHostname, this method has no effect and will return TSKTrustDecisionDomainNotPinned without validating the supplied serverTrust at all. This means that the server’s serverTrust object must be verified against the device’s trust store using SecTrustEvaluate(). Failing to do so will disable SSL certificate validation.

    + +

    @exception NSException Thrown when TrustKit has not been initialized with a pinning policy.

    @@ -307,15 +341,13 @@

    Return Value

    Declaration

    Objective-C

    -
    + (BOOL)handleChallenge:(NSURLAuthenticationChallenge *_Nonnull)challenge
    -      completionHandler:
    -          (void (^_Nonnull)(NSURLSessionAuthChallengeDisposition,
    -                            NSURLCredential *_Nullable))completionHandler;
    +
    - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust
    +                      forHostname:(NSString *_Nonnull)serverHostname;

    Swift

    -
    class func handle(_ challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Bool
    +
    func evaluateTrust(_ serverTrust: SecTrust, forHostname serverHostname: String) -> TSKTrustDecision
    @@ -326,26 +358,24 @@

    Parameters

    - challenge + serverTrust
    -

    The authentication challenge, supplied by the URL loading system to the delegate’s challenge handler method.

    - +

    The trust object representing the server’s certificate chain. The trust’s evaluation policy is always overridden using SecTrustSetPolicies() to ensure all the proper SSL checks (expiration, hostname validation, etc.) are enabled.

    - completionHandler + serverHostname
    -

    A block to invoke to respond to the challenge, supplied by the URL loading system to the delegate’s challenge handler method.

    - +

    The hostname of the server whose identity is being validated.

    @@ -354,25 +384,22 @@

    Parameters

    Return Value

    -

    YES if the challenge was handled and the completionHandler was successfuly invoked. NO if the challenge could not be handled because it was not for server certificate validation (ie. the challenge’s authenticationMethod was not NSURLAuthenticationMethodServerTrust).

    - -
    -
    - Show on GitHub +

    A TSKTrustDecision which describes whether the SSL connection should be allowed or blocked, based on the configured pinning policy.

- - - + + diff --git a/docs/documentation/Classes/TSKPinningValidatorResult.html b/docs/documentation/Classes/TSKPinningValidatorResult.html new file mode 100644 index 00000000..4286d221 --- /dev/null +++ b/docs/documentation/Classes/TSKPinningValidatorResult.html @@ -0,0 +1,405 @@ + + + + TSKPinningValidatorResult Class Reference + + + + + + + + + + + + + + + + +
+

+ + TrustKit Docs + + +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

TSKPinningValidatorResult

+
+
+
@interface TSKPinningValidatorResult : NSObject
+ +
+
+

A TSKPinningValidatorResult instance contains all the details regarding a pinning validation +performed against a specific server.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + serverHostname + +
    +
    +
    +
    +
    +
    +

    The hostname of the server SSL pinning validation was performed against.

    + +
    +
    +

    Declaration

    +
    +

    Objective-C

    +
    @property (readonly, nonatomic, nonnull) NSString *serverHostname;
    + +
    +
    +

    Swift

    +
    var serverHostname: String { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + serverTrust + +
    +
    +
    +
    +
    +
    +

    The original SecTrustRef that validation was performed against.

    + +
    +
    +

    Declaration

    +
    +

    Objective-C

    +
    @property (readonly, nonatomic, nonnull) SecTrustRef serverTrust;
    + +
    +
    +

    Swift

    +
    var serverTrust: SecTrust { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + evaluationResult + +
    +
    +
    +
    +
    +
    +

    The result of validating the server’s certificate chain against the set of SSL pins configured for +the notedHostname.

    + +
    +
    +

    Declaration

    +
    +

    Objective-C

    +
    @property (readonly, nonatomic) TSKTrustEvaluationResult evaluationResult;
    + +
    +
    +

    Swift

    +
    var evaluationResult: TSKTrustEvaluationResult { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + finalTrustDecision + +
    +
    +
    +
    +
    +
    +

    The trust decision returned for this connection, which describes whether the connection should be blocked +or allowed, based on the evaluationResult returned when evaluating the serverTrust and the SSL pining +policy configured for this server.

    + +

    For example, the pinning validation could have failed (ie. validationResult being +TSKTrustEvaluationFailedNoMatchingPin) but the policy might be set to ignore pinning validation failures +for this server, thereby returning TSKTrustDecisionShouldAllowConnection.

    + +
    +
    +

    Declaration

    +
    +

    Objective-C

    +
    @property (readonly, nonatomic) TSKTrustDecision finalTrustDecision;
    + +
    +
    +

    Swift

    +
    var finalTrustDecision: TSKTrustDecision { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + validationDuration + +
    +
    +
    +
    +
    +
    +

    The time it took for the SSL pinning validation to be performed.

    + +
    +
    +

    Declaration

    +
    +

    Objective-C

    +
    @property (readonly, nonatomic) NSTimeInterval validationDuration;
    + +
    +
    +

    Swift

    +
    var validationDuration: TimeInterval { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + certificateChain + +
    +
    +
    +
    +
    +
    +

    The certificate chain extracted from the serverTrust as PEM-formatted certificates. This is the +certificate chain sent by the server when establishing the connection.

    + +
    +
    +

    Declaration

    +
    +

    Objective-C

    +
    @property (readonly, nonatomic, nullable) NSArray *certificateChain;
    + +
    +
    +

    Swift

    +
    var certificateChain: [Any]? { get }
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/documentation/Classes/TrustKit.html b/docs/documentation/Classes/TrustKit.html index b8817dff..3dbbfa25 100644 --- a/docs/documentation/Classes/TrustKit.html +++ b/docs/documentation/Classes/TrustKit.html @@ -4,145 +4,174 @@ TrustKit Class Reference - + + + + + + -
- -
-
-
+

+ + TrustKit Docs + +

-
+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ + + + +
-
+ +
  • +
    + + + + +sharedInstance + +
    +
    +
    +
    +
    +
    +

    Retrieve the global TrustKit singleton instance. Raises an exception if +initializeWithConfiguration: +has not yet been invoked.

    + +
    +
    +

    Declaration

    +
    +

    Objective-C

    +
    + (nonnull instancetype)sharedInstance;
    + +
    +
    +

    Swift

    +
    class func sharedInstance() -> Self
    + +
    +
    +
    +

    Return Value

    +

    the shared TrustKit singleton

    @@ -292,19 +375,19 @@

    Parameters

    -
      +
      • @@ -312,33 +395,186 @@

        Current Configuration

        -

        Retrieve a copy of the global SSL pinning policy.

        +

        Retrieve the validator instance conforming to the pinning policy of this TrustKit instance.

        + +

        The validator should be used to implement pinning validation within the App’s network +authentication handlers.

        Declaration

        Objective-C

        -
        + (nullable NSDictionary *)configuration;
        +
        @property (assign, readwrite, nonatomic, nonnull)
        +    TSKPinningValidator *pinningValidator;

        Swift

        -
        class func configuration() -> [AnyHashable : Any]?
        +
        var pinningValidator: TSKPinningValidator { get set }
        -
        -

        Return Value

        -

        A dictionary with a copy of the current TrustKit configuration, or nil if TrustKit has not been initialized.

        +
        +
        +
      • +
      +
    +
    + +
      +
    • + +
      +
      +
      +
      +
      +

      Register a block to be invoked for every request that is going through TrustKit’s pinning +validation mechanism. See TSKPinningValidatorCallback for more information.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @property (assign, readwrite, nonatomic, nullable)
      +    TSKPinningValidatorCallback pinningValidatorCallback;
      + +
      +
      +

      Swift

      +
      var pinningValidatorCallback: TSKPinningValidatorCallback? { get set }
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Queue on which to invoke the pinningValidatorCallback; default value is the main queue.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @property (assign, readwrite, nonatomic, null_resettable)
      +    dispatch_queue_t pinningValidatorCallbackQueue;
      + +
      +
      +

      Swift

      +
      var pinningValidatorCallbackQueue: DispatchQueue! { get set }
      + +
      +
      +
      +
      +
    • +
    +
    +
    + +
      +
    • + +
      +
      +
      +
      +
      +

      Initialize a local TrustKit instance with the supplied SSL pinning policy configuration.

      + +

      This method is useful in scenarios where the TrustKit singleton cannot be used, for example within +larger Apps that have split some of their functionality into multiple framework/SDK. Each +framework can initialize its own instance of TrustKit and use it for pinning validation independently +of the App’s other components.

      -
      - Show on GitHub +
      +

      Declaration

      +
      +

      Objective-C

      +
      - (nonnull instancetype)initWithConfiguration:
      +    (nonnull NSDictionary<TSKGlobalConfigurationKey, id> *)trustKitConfig;
      + +
      +
      +

      Swift

      +
      init(configuration trustKitConfig: [String : Any])
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + trustKitConfig + + +
      +

      A dictionary containing various keys for configuring the SSL pinning policy.

      +
      +
    • +
    +
    +
    + +
    • @@ -354,9 +590,12 @@

      Return Value

      Set the global logger.

      -

      This method sets the global logger, used when TrustKit needs to display a message to the developer.

      +

      This method sets the global logger, used when any TrustKit instance needs to display a message to +the developer.

      -

      If a global logger is not set, the default logger will be used, which will print TrustKit log messages (using NSLog()) when the App is built in Debug mode. If the App was built for Release, the default logger will not print any messages at all.

      +

      If a global logger is not set, the default logger will be used, which will only print TrustKit log +messages (using NSLog()) when the App is built in Debug mode. If the App was built for Release, the default +logger will not print any messages at all.

      @@ -372,22 +611,20 @@

      Declaration

      -
  • - - - + + diff --git a/docs/documentation/Constants.html b/docs/documentation/Constants.html deleted file mode 100644 index f38eb7a7..00000000 --- a/docs/documentation/Constants.html +++ /dev/null @@ -1,1118 +0,0 @@ - - - - Constants Reference - - - - - - - - - -
    - -
    -
    - -
    -
    - -
    -
    -
    -

    Constants

    -

    The following constants are available globally.

    - -
    -
    -
    - -
      -
    • -
      - - - - TrustKitVersion - -
      -
      -
      -
      -
      -
      -

      The version of TrustKit, such as 1.4.0.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern NSString *const _Nonnull TrustKitVersion
      - -
      -
      -

      Swift

      -
      let TrustKitVersion: String
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - -
      -
    • - -
      -
      -
      -
      -
      -

      A boolean. If set to YES, TrustKit will perform method swizzling on the App’s NSURLConnection and NSURLSession delegates in order to automatically add SSL pinning validation to the App’s connections.

      - -

      Swizzling allows enabling pinning within an App without having to find and modify each and every instance of NSURLConnection or NSURLSession delegates. -However, it should only be enabled for simple Apps, as it may not work properly in several scenarios including:

      - -
        -
      • Apps with complex connection delegates, for example to handle client authentication via certificates or basic authentication.
      • -
      • Apps where method swizzling of the connection delegates is already performed by another module or library (such as Analytics SDKs).
      • -
      • Apps that do no use NSURLSession or NSURLConnection for their connections.
      • -
      - -

      In such scenarios or if the developer wants a tigher control on the App’s networking behavior, kTSKSwizzleNetworkDelegates should be set to NO; the developer should then manually add pinning validation to the App’s authentication handlers.

      - -

      See the TSKPinningValidator class for instructions on how to do so.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKGlobalConfigurationKey _Nonnull kTSKSwizzleNetworkDelegates
      - -
      -
      -

      Swift

      -
      let kTSKSwizzleNetworkDelegates: String
      - -
      -
      - -
      -
      -
    • -
    • -
      - - - - kTSKPinnedDomains - -
      -
      -
      -
      -
      -
      -

      A dictionary with domains (such as www.domain.com) as keys and dictionaries as values.

      - -

      Each entry should contain domain-specific settings for performing pinning validation when connecting to the domain, including for example the domain’s public key hashes. A list of all domain-specific keys is available in the Domain-specific Keys sections.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKGlobalConfigurationKey _Nonnull kTSKPinnedDomains
      - -
      -
      -

      Swift

      -
      let kTSKPinnedDomains: String
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - -
      -
    • - -
      -
      -
      -
      -
      -

      A boolean. If set to YES, pinning validation will be skipped if the server’s certificate chain terminates at a user-defined trust anchor (such as a root CA that isn’t part of OS X’s default trust store) and no pin failure reports will be sent; default value is YES.

      - -

      This is useful for allowing SSL connections through corporate proxies or firewalls. See How does key pinning interact with local proxies and filters? within the Chromium security FAQ at https://www.chromium.org/Home/chromium-security/security-faq for more information.

      - -

      Only available on macOS.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKGlobalConfigurationKey _Nonnull kTSKIgnorePinningForUserDefinedTrustAnchors
      - -
      -
      -

      Swift

      -
      let kTSKIgnorePinningForUserDefinedTrustAnchors: String
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - -
      -
    • -
      - - - - kTSKPublicKeyHashes - -
      -
      -
      -
      -
      -
      -

      An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate’s Subject Public Key Info.

      - -

      TrustKit will verify that at least one of the specified pins is found in the server’s evaluated certificate chain.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKPublicKeyHashes
      - -
      -
      -

      Swift

      -
      let kTSKPublicKeyHashes: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      An array of TSKSupportedAlgorithm constants to specify the public key algorithms for the keys to be pinned.

      - -

      TrustKit requires this information in order to compute SSL pins when validating a server’s certificate chain, because the Security framework does not provide APIs to extract the key’s algorithm from an SSL certificate. To minimize the performance impact of Trustkit, only one algorithm should be enabled.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKPublicKeyAlgorithms
      - -
      -
      -

      Swift

      -
      let kTSKPublicKeyAlgorithms: String
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - -
      -
    • -
      - - - - kTSKEnforcePinning - -
      -
      -
      -
      -
      -
      -

      A boolean. If set to NO, TrustKit will not block SSL connections that caused a pin or certificate validation error; default value is YES.

      - -

      When a pinning failure occurs, pin failure reports will always be sent to the configured report URIs regardless of the value of kTSKEnforcePinning.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKEnforcePinning
      - -
      -
      -

      Swift

      -
      let kTSKEnforcePinning: String
      - -
      -
      - -
      -
      -
    • -
    • -
      - - - - kTSKIncludeSubdomains - -
      -
      -
      -
      -
      -
      -

      A boolean. If set to YES, also pin all the subdomains of the specified domain; default value is NO.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKIncludeSubdomains
      - -
      -
      -

      Swift

      -
      let kTSKIncludeSubdomains: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      A boolean. If set to YES, TrustKit will not pin this specific domain if kTSKIncludeSubdomains was set for this domain’s parent domain.

      - -

      This allows excluding specific subdomains from a pinning policy that was applied to a parent domain.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKExcludeSubdomainFromParentPolicy
      - -
      -
      -

      Swift

      -
      let kTSKExcludeSubdomainFromParentPolicy: String
      - -
      -
      - -
      -
      -
    • -
    • -
      - - - - kTSKReportUris - -
      -
      -
      -
      -
      -
      -

      An array of URLs to which pin validation failures should be reported.

      - -

      To minimize the performance impact of sending reports on each validation failure, the reports are uploaded using the background transfer service and are also rate-limited to one per day and per type of failure. For HTTPS report URLs, the HTTPS connections will ignore the SSL pinning policy and use the default certificate validation mechanisms, in order to maximize the chance of the reports reaching the server. The format of the reports is similar to the one described in RFC 7469 for the HPKP specification:

      - -

      { - app-bundle-id: com.datatheorem.testtrustkit2, - app-version: 1, - app-vendor-id: 599F9C00-92DC-4B5C-9464-7971F01F8370, - app-platform: IOS, - app-platform-version: 10.2.0, - trustkit-version: 1.3.1, - hostname: www.datatheorem.com, - port: 0, - noted-hostname: datatheorem.com, - include-subdomains: true, - enforce-pinning: true, - validated-certificate-chain: [ - pem1, … pemN - ], - known-pins: [ - pin-sha256=\d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\, - “pin-sha256=“E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\ - ], - validation-result:1 - }

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKReportUris
      - -
      -
      -

      Swift

      -
      let kTSKReportUris: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      A boolean. If set to YES, the default report URL for sending pin failure reports will be disabled; default value is NO.

      - -

      By default, pin failure reports are sent to a report server hosted by Data Theorem, for detecting potential CA compromises and man-in-the-middle attacks, as well as providing a free dashboard for developers; email info@datatheorem.com if you’d like a dashboard for your App. Only pin failure reports are sent, which contain the App’s bundle ID, the IDFV, and the server’s hostname and certificate chain that failed validation.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKDisableDefaultReportUri
      - -
      -
      -

      Swift

      -
      let kTSKDisableDefaultReportUri: String
      - -
      -
      - -
      -
      -
    • -
    • -
      - - - - kTSKExpirationDate - -
      -
      -
      -
      -
      -
      -

      A string containing the date, in yyyy-MM-dd format, on which the domain’s configured SSL pins expire, thus disabling pinning validation. If the key is not set, then the pins do not expire.

      - -

      Expiration helps prevent connectivity issues in Apps which do not get updates to their pin set, such as when the user disables App updates.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKExpirationDate
      - -
      -
      -

      Swift

      -
      let kTSKExpirationDate: String
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - - -
    -
    - -
      -
    • - -
      -
      -
      -
      -
      -

      The name of the notification to be posted for every request that is going through TrustKit’s pinning validation mechanism.

      - -

      Once TrustKit has been initialized, notifications will be posted with this name every time TrustKit validates the certificate chain for a server configured in the SSL pinning policy; if the server’s hostname does not have an entry in the pinning policy, no notifications get posted as no pinning validation was performed.

      - -

      These notifications can be used for performance measurement or to act upon any pinning validation performed by TrustKit (for example to customize the reporting mechanism). The notifications provide details about TrustKit’s inner-workings which most Apps should not need to process. Hence, these notifications can be ignored unless the App requires some advanced customization in regards to pinning validation.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern NSString *_Nonnull kTSKValidationCompletedNotification
      - -
      -
      -

      Swift

      -
      static let tskValidationCompleted: NSNotification.Name
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - -
      -
    • - -
      -
      -
      -
      -
      -

      The time in seconds it took for the SSL pinning validation to be performed.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKNotificationUserInfoKey _Nonnull kTSKValidationDurationNotificationKey
      - -
      -
      -

      Swift

      -
      let kTSKValidationDurationNotificationKey: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      The TSKPinningValidationResult returned when validating the server’s certificate chain, which represents the result of evaluating the certificate chain against the configured SSL pins for this server.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKNotificationUserInfoKey _Nonnull kTSKValidationResultNotificationKey
      - -
      -
      -

      Swift

      -
      let kTSKValidationResultNotificationKey: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      The TSKTrustDecision returned when validating the certificate’s chain, which describes whether the connection should be blocked or allowed, based on the TSKPinningValidationResult returned when evaluating the server’s certificate chain and the SSL pining policy configured for this server.

      - -

      For example, the pinning validation could have failed (returning TSKPinningValidationFailed) but the policy might be set to ignore pinning validation failures for this server, thereby returning TSKTrustDecisionShouldAllowConnection.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKNotificationUserInfoKey _Nonnull kTSKValidationDecisionNotificationKey
      - -
      -
      -

      Swift

      -
      let kTSKValidationDecisionNotificationKey: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      The certificate chain returned by the server as an array of PEM-formatted certificates.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKNotificationUserInfoKey _Nonnull kTSKValidationCertificateChainNotificationKey
      - -
      -
      -

      Swift

      -
      let kTSKValidationCertificateChainNotificationKey: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      The entry within the SSL pinning configuration that was used as the pinning policy for the server being validated. It will be the same as the kTSKValidationServerHostnameNotificationKey entry unless the server is a subdomain of a domain configured in the pinning policy with kTSKIncludeSubdomains enabled. The corresponding pinning configuration that was used for validation can be retrieved using:

      - -
      NSString *notedHostname = userInfo[kTSKValidationNotedHostnameNotificationKey];
      -NSDictionary *hostnameConfiguration = [TrustKit configuration][kTSKPinnedDomains][notedHostname];
      -
      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKNotificationUserInfoKey _Nonnull kTSKValidationNotedHostnameNotificationKey
      - -
      -
      -

      Swift

      -
      let kTSKValidationNotedHostnameNotificationKey: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      The hostname of the server SSL pinning validation was performed against.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKNotificationUserInfoKey _Nonnull kTSKValidationServerHostnameNotificationKey
      - -
      -
      -

      Swift

      -
      let kTSKValidationServerHostnameNotificationKey: String
      - -
      -
      - -
      -
      -
    • -
    -
    -
    -
    - -
    -
    - - - diff --git a/docs/documentation/Domain Configuration Keys.html b/docs/documentation/Domain Configuration Keys.html new file mode 100644 index 00000000..c0f6ecba --- /dev/null +++ b/docs/documentation/Domain Configuration Keys.html @@ -0,0 +1,621 @@ + + + + Domain Configuration Keys Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Domain Configuration Keys

    + +
    +
    + +
    +
    +
    + +
      +
    • + +
      +
      +
      +
      +
      +

      A domain-specific configuration key (to defined for a domain under the kTSKPinnedDomains +key) that can be set in the pinning policy.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      typedef NSString *TSKDomainConfigurationKey
      + +
      +
      +

      Swift

      +
      typealias TSKDomainConfigurationKey = NSString
      + +
      +
      +
      +
      +
    • +
    +
    +
    + +
      +
    • +
      + + + + kTSKPublicKeyHashes + +
      +
      +
      +
      +
      +
      +

      An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate’s +Subject Public Key Info.

      + +

      TrustKit will verify that at least one of the specified pins is found in the server’s +evaluated certificate chain.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKPublicKeyHashes
      + +
      +
      +

      Swift

      +
      let kTSKPublicKeyHashes: String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      An array of TSKSupportedAlgorithm constants to specify the public key algorithms for the +keys to be pinned.

      + +

      TrustKit requires this information in order to compute SSL pins when validating a server’s +certificate chain, because the Security framework does not provide APIs to extract the +key’s algorithm from an SSL certificate. To minimize the performance impact of Trustkit, +only one algorithm should be enabled.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKPublicKeyAlgorithms
      + +
      +
      +

      Swift

      +
      let kTSKPublicKeyAlgorithms: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    + +
      +
    • +
      + + + + kTSKEnforcePinning + +
      +
      +
      +
      +
      +
      +

      A boolean. If set to NO, TrustKit will not block SSL connections that caused a pin or +certificate validation error; default value is YES.

      + +

      When a pinning failure occurs, pin failure reports will always be sent to the configured +report URIs regardless of the value of kTSKEnforcePinning.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKEnforcePinning
      + +
      +
      +

      Swift

      +
      let kTSKEnforcePinning: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + kTSKIncludeSubdomains + +
      +
      +
      +
      +
      +
      +

      A boolean. If set to YES, also pin all the subdomains of the specified domain; default +value is NO.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKIncludeSubdomains
      + +
      +
      +

      Swift

      +
      let kTSKIncludeSubdomains: String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      A boolean. If set to YES, TrustKit will not pin this specific domain if kTSKIncludeSubdomains +was set for this domain’s parent domain.

      + +

      This allows excluding specific subdomains from a pinning policy that was applied to a +parent domain.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKExcludeSubdomainFromParentPolicy
      + +
      +
      +

      Swift

      +
      let kTSKExcludeSubdomainFromParentPolicy: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + kTSKReportUris + +
      +
      +
      +
      +
      +
      +

      An array of URLs to which pin validation failures should be reported.

      + +

      To minimize the performance impact of sending reports on each validation failure, the reports +are uploaded using the background transfer service and are also rate-limited to one per day +and per type of failure. For HTTPS report URLs, the HTTPS connections will ignore the SSL +pinning policy and use the default certificate validation mechanisms, in order to maximize +the chance of the reports reaching the server. The format of the reports is similar to the +one described in RFC 7469 for the HPKP specification:

      + +

      { +app-bundle-id: com.datatheorem.testtrustkit2, +app-version: 1, +app-vendor-id: 599F9C00-92DC-4B5C-9464-7971F01F8370, +app-platform: IOS, +app-platform-version: 10.2.0, +trustkit-version: 1.3.1, +hostname: www.datatheorem.com, +port: 0, +noted-hostname: datatheorem.com, +include-subdomains: true, +enforce-pinning: true, +validated-certificate-chain: [ +pem1, … pemN +], +known-pins: [ +pin-sha256=\d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\, +“pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\ +], +validation-result:1 +}

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKReportUris
      + +
      +
      +

      Swift

      +
      let kTSKReportUris: String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      A boolean. If set to YES, the default report URL for sending pin failure reports will +be disabled; default value is NO.

      + +

      By default, pin failure reports are sent to a report server hosted by Data Theorem, for +detecting potential CA compromises and man-in-the-middle attacks, as well as providing a +free dashboard for developers; email info@datatheorem.com if you’d like a dashboard for +your App. Only pin failure reports are sent, which contain the App’s bundle ID, the IDFV, +and the server’s hostname and certificate chain that failed validation.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKDisableDefaultReportUri
      + +
      +
      +

      Swift

      +
      let kTSKDisableDefaultReportUri: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + kTSKExpirationDate + +
      +
      +
      +
      +
      +
      +

      A string containing the date, in yyyy-MM-dd format, on which the domain’s configured SSL +pins expire, thus disabling pinning validation. If the key is not set, then the pins do +not expire.

      + +

      Expiration helps prevent connectivity issues in Apps which do not get updates to their +pin set, such as when the user disables App updates.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKExpirationDate
      + +
      +
      +

      Swift

      +
      let kTSKExpirationDate: String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      An array of strings representing additional trust anchors usable for validating +the trust chain of pinned certificates that do not end in an OS trust anchor.

      + +

      The default behavior of TrustKit is to ignore these trust anchors unless compiled +in debug mode (pass -DDEBUG=1 to the preprocessor). This behavior can be modified +by subclassing TSKPinningValidator and overriding +allowsAdditionalTrustAnchors.

      + +

      The entries in the array should each be a single PEM-encoded public certificate. +Standard RFC-7468 PEM format is supported (see https://tools.ietf.org/html/rfc7468#section-2 ). +Note that the header, footer and any newlines are optional, but aid in readability.

      + +

      SECURITY WARNING: +Misuse of this configuration option could potentially render your application +vulnerable to exploits since it bypasses the normal operating system trust store. +It is intended for enterprise scenarios where a company might be running their +own internal production-grade certificate authority for debugging purposes.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKAdditionalTrustAnchors
      + +
      +
      +

      Swift

      +
      let kTSKAdditionalTrustAnchors: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/Enums.html b/docs/documentation/Enums.html deleted file mode 100644 index 83376200..00000000 --- a/docs/documentation/Enums.html +++ /dev/null @@ -1,200 +0,0 @@ - - - - Enums Reference - - - - - - - - - -
    - -
    -
    - -
    -
    - -
    -
    -
    -

    Enums

    -

    The following enums are available globally.

    - -
    -
    -
    -
      -
    • -
      - - - - TSKTrustDecision - -
      -
      -
      -
      -
      -
      -

      Possible return values when verifying a server’s identity against the global SSL pinning policy using TSKPinningValidator.

      - - See more -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      enum TSKTrustDecision : NSInteger {}
      - -
      -
      -

      Swift

      -
      enum TSKTrustDecision : Int
      - -
      -
      - -
      -
      -
    • -
    -
    -
    -
    - -
    -
    - - - diff --git a/docs/documentation/Enums/TSKTrustDecision.html b/docs/documentation/Enums/TSKTrustDecision.html index 33b181d9..ad417a89 100644 --- a/docs/documentation/Enums/TSKTrustDecision.html +++ b/docs/documentation/Enums/TSKTrustDecision.html @@ -4,145 +4,174 @@ TSKTrustDecision Enum Reference - + + + + + + -
    - -
    -
    -
    +

    + + TrustKit Docs + +

    -
    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + + + + +
    -
    @@ -205,7 +234,7 @@

    Declaration

    -

    Based on the server’s certificate chain and the global pinning policy for this domain, the SSL connection should be blocked. +

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be blocked. A pinning validation failure occured and if a report URI was configured, a pin failure report was sent.

    @@ -222,9 +251,6 @@

    Declaration

    -
    @@ -258,22 +284,20 @@

    Declaration

    - - - - + + diff --git a/docs/documentation/Enums/TSKTrustEvaluationResult.html b/docs/documentation/Enums/TSKTrustEvaluationResult.html new file mode 100644 index 00000000..21a40fc0 --- /dev/null +++ b/docs/documentation/Enums/TSKTrustEvaluationResult.html @@ -0,0 +1,396 @@ + + + + TSKTrustEvaluationResult Enum Reference + + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    TSKTrustEvaluationResult

    +
    +
    +
    enum TSKTrustEvaluationResult : NSInteger {}
    + +
    +
    +

    Possible return values when verifying a server’s identity against a set of pins.

    + +
    +
    + +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      The server trust was succesfully evaluated and contained at least one of the configured pins.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      TSKTrustEvaluationSuccess
      + +
      +
      +

      Swift

      +
      case success = 0
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The server trust was succesfully evaluated but did not contain any of the configured pins.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      TSKTrustEvaluationFailedNoMatchingPin
      + +
      +
      +

      Swift

      +
      case failedNoMatchingPin = 1
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The server trust’s evaluation failed: the server’s certificate chain is not trusted.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      TSKTrustEvaluationFailedInvalidCertificateChain
      + +
      +
      +

      Swift

      +
      case failedInvalidCertificateChain = 2
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The server trust could not be evaluated due to invalid parameters.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      TSKTrustEvaluationErrorInvalidParameters
      + +
      +
      +

      Swift

      +
      case errorInvalidParameters = 3
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to the macOS trust store). Only available on macOS.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      TSKTrustEvaluationFailedUserDefinedTrustAnchor
      + +
      +
      +

      Swift

      +
      case failedUserDefinedTrustAnchor = 4
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The server trust could not be evaluated due to an error when trying to generate the certificate’s subject public key info hash. On iOS 9 or below, this could be caused by a Keychain failure when trying to extract the certificate’s public key bytes.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      TSKTrustEvaluationErrorCouldNotGenerateSpkiHash
      + +
      +
      +

      Swift

      +
      case errorCouldNotGenerateSpkiHash = 5
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/Global Configuration Keys.html b/docs/documentation/Global Configuration Keys.html new file mode 100644 index 00000000..29d0b84d --- /dev/null +++ b/docs/documentation/Global Configuration Keys.html @@ -0,0 +1,388 @@ + + + + Global Configuration Keys Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Global Configuration Keys

    + +
    +
    + +
    +
    +
    + +
      +
    • + +
      +
      +
      +
      +
      +

      A global, App-wide configuration key that can be set in the pinning policy.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      typedef NSString *TSKGlobalConfigurationKey
      + +
      +
      +

      Swift

      +
      typealias TSKGlobalConfigurationKey = NSString
      + +
      +
      +
      +
      +
    • +
    +
    +
    + +
      +
    • + +
      +
      +
      +
      +
      +

      A boolean. If set to YES, TrustKit will perform method swizzling on the App’s +NSURLConnection and NSURLSession delegates in order to automatically add SSL pinning +validation to the App’s connections. This option can only be used if TrustKit is +initialized in singleton mode; default value is NO.

      + +

      Swizzling allows enabling pinning within an App without having to find and modify each +and every instance of NSURLConnection or NSURLSession delegates. +However, it should only be enabled for simple Apps, as it may not work properly in several +scenarios including:

      + +
        +
      • Apps with complex connection delegates, for example to handle client authentication +via certificates or basic authentication.
      • +
      • Apps where method swizzling of the connection delegates is already performed by another +module or library (such as Analytics SDKs).
      • +
      • Apps that do no use NSURLSession or NSURLConnection for their connections.
      • +
      + +

      In such scenarios or if the developer wants a tigher control on the App’s networking +behavior, kTSKSwizzleNetworkDelegates should be set to NO; the developer should then +manually add pinning validation to the App’s authentication handlers.

      + +

      See the TSKPinningValidator class for instructions on how to do so.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKGlobalConfigurationKey kTSKSwizzleNetworkDelegates
      + +
      +
      +

      Swift

      +
      let kTSKSwizzleNetworkDelegates: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + kTSKPinnedDomains + +
      +
      +
      +
      +
      +
      +

      A dictionary with domains (such as www.domain.com) as keys and dictionaries as values.

      + +

      Each entry should contain domain-specific settings for performing pinning validation when +connecting to the domain, including for example the domain’s public key hashes. A list of +all domain-specific keys is available in the Domain-specific Keys sections.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKGlobalConfigurationKey kTSKPinnedDomains
      + +
      +
      +

      Swift

      +
      let kTSKPinnedDomains: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    + +
      +
    • + +
      +
      +
      +
      +
      +

      A boolean. If set to YES, pinning validation will be skipped if the server’s certificate +chain terminates at a user-defined trust anchor (such as a root CA that isn’t part of OS X’s +default trust store) and no pin failure reports will be sent; default value is YES.

      + +

      This is useful for allowing SSL connections through corporate proxies or firewalls. See +How does key pinning interact with local proxies and filters? within the Chromium security +FAQ at https://www.chromium.org/Home/chromium-security/security-faq for more information.

      + +

      Only available on macOS.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKGlobalConfigurationKey
      +    kTSKIgnorePinningForUserDefinedTrustAnchors
      + +
      +
      +

      Swift

      +
      let kTSKIgnorePinningForUserDefinedTrustAnchors: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/Implementing Pinning Validation.html b/docs/documentation/Implementing Pinning Validation.html new file mode 100644 index 00000000..b09a3e1d --- /dev/null +++ b/docs/documentation/Implementing Pinning Validation.html @@ -0,0 +1,281 @@ + + + + Implementing Pinning Validation Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Implementing Pinning Validation

    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + TSKPinningValidator + +
      +
      +
      +
      +
      +
      +

      A TSKPinningValidator instance can be used to verify a server’s identity against an SSL pinning policy.

      + +

      In specific scenarios, TrustKit cannot intercept outgoing SSL connections and automatically validate the server’s identity against the pinning policy:

      + +
        +
      • All connections within an App that disables TrustKit’s network delegate swizzling by setting the kTSKSwizzleNetworkDelegates configuration key to NO.
      • +
      • Connections that do not rely on the NSURLConnection or NSURLSession APIs: + +
          +
        • WKWebView connections.
        • +
        • Connections leveraging low-level network APIs (such as NSStream).
        • +
        • Connections initiated using a third-party SSL library such as OpenSSL.
        • +
      • +
      + +

      For these connections, pin validation must be manually triggered using one of the two available methods within TSKPinningValidator.

      + + See more +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @interface TSKPinningValidator : NSObject
      + +
      +
      +

      Swift

      +
      class TSKPinningValidator : NSObject
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + TSKTrustDecision + +
      +
      +
      +
      +
      +
      +

      Possible return values when verifying a server’s identity against an SSL pinning policy.

      + + See more +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      enum TSKTrustDecision : NSInteger {}
      + +
      +
      +

      Swift

      +
      enum TSKTrustDecision : Int
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/Initalizing TrustKit.html b/docs/documentation/Initalizing TrustKit.html new file mode 100644 index 00000000..b45493cf --- /dev/null +++ b/docs/documentation/Initalizing TrustKit.html @@ -0,0 +1,314 @@ + + + + Initalizing TrustKit Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Initalizing TrustKit

    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + TrustKit + +
      +
      +
      +
      +
      +
      +

      TrustKit is the main class for configuring an SSL pinning policy within an App.

      + +

      For most Apps, TrustKit should be used as a singleton, where a global SSL pinning policy is +configured for the App. In singleton mode, the policy can be set either:

      + +
        +
      • By adding it to the App’s Info.plist under the TSKConfiguration key, or
      • +
      • By programmatically supplying it using the +initializeWithConfiguration: method.
      • +
      + +

      In singleton mode, TrustKit can only be initialized once so only one of the two techniques +should be used.

      + +

      For more complex Apps where multiple SSL pinning policies need to be used independently +(for example within different frameworks), TrustKit can be used in multi-instance mode +by leveraging the -initWithConfiguration:identifier: method described at the end of this +page.

      + +

      A TrustKit pinning policy is a dictionary which contains some global, App-wide settings +(of type TSKGlobalConfigurationKey) as well as domain-specific configuration keys +(of type TSKDomainConfigurationKey) to be defined under the kTSKPinnedDomains entry. +The following table shows the keys and the types of the corresponding values, and uses +indentation to indicate structure:

      +
      | Key                                          | Type       |
      +|----------------------------------------------|------------|
      +| TSKSwizzleNetworkDelegates                   | Boolean    |
      +| TSKIgnorePinningForUserDefinedTrustAnchors   | Boolean    |
      +| TSKPinnedDomains                             | Dictionary |
      +| __ <domain-name-to-pin-as-string>            | Dictionary |
      +| ____ TSKPublicKeyHashes                      | Array      |
      +| ____ TSKPublicKeyAlgorithms                  | Array      |
      +| ____ TSKIncludeSubdomains                    | Boolean    |
      +| ____ TSKExcludeSubdomainFromParentPolicy     | Boolean    |
      +| ____ TSKEnforcePinning                       | Boolean    |
      +| ____ TSKReportUris                           | Array      |
      +| ____ TSKDisableDefaultReportUri              | Boolean    |
      +| ____ TSKAdditionalTrustAnchors               | Array      |
      +
      + +

      When setting the pinning policy programmatically, it has to be supplied to the +initializeWithConfiguration: method as a dictionary in order to initialize TrustKit. +For example:

      +
         NSDictionary *trustKitConfig =
      + @{
      +   kTSKPinnedDomains : @{
      +           @"www.datatheorem.com" : @{
      +                   kTSKExpirationDate: @"2017-12-01",
      +                   kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048],
      +                   kTSKPublicKeyHashes : @[
      +                           @"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=",
      +                           @"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8="
      +                           ],
      +                   kTSKEnforcePinning : @NO,
      +                   kTSKReportUris : @[@"http://report.datatheorem.com/log_report"],
      +                   },
      +           @"yahoo.com" : @{
      +                   kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096],
      +                   kTSKPublicKeyHashes : @[
      +                           @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=",
      +                           @"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=",
      +                           ],
      +                   kTSKIncludeSubdomains : @YES
      +                   }
      +           }};
      +
      +   [TrustKit initializeWithConfiguration:trustKitConfig];
      +   trustKit = [TrustKit sharedInstance];
      +
      + +

      Similarly, the TrustKit singleton can be initialized in Swift:

      +
             let trustKitConfig = [
      +           kTSKSwizzleNetworkDelegates: false,
      +           kTSKPinnedDomains: [
      +               "yahoo.com": [
      +                   kTSKExpirationDate: "2017-12-01",
      +                   kTSKPublicKeyAlgorithms: [kTSKAlgorithmRsa2048],
      +                   kTSKPublicKeyHashes: [
      +                       "JbQbUG5JMJUoI6brnx0x3vZF6jilxsapbXGVfjhN8Fg=",
      +                       "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="
      +                   ],]]] as [String : Any]
      +
      +       TrustKit.initialize(withConfiguration:trustKitConfig)
      +
      + +

      After initialization, the TrustKit instance’s pinningValidator should be used to implement +pinning validation within the App’s network authentication handlers.

      + + See more +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @interface TrustKit : NSObject
      + +
      +
      +

      Swift

      +
      class TrustKit : NSObject
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/Other Constants.html b/docs/documentation/Other Constants.html new file mode 100644 index 00000000..108f58b6 --- /dev/null +++ b/docs/documentation/Other Constants.html @@ -0,0 +1,229 @@ + + + + Other Constants Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Other Constants

    +

    The following constants are available globally.

    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + TrustKitVersion + +
      +
      +
      +
      +
      +
      +

      The version of TrustKit, such as 1.4.0.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern NSString *const TrustKitVersion
      + +
      +
      +

      Swift

      +
      let TrustKitVersion: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/Public Key Algorithm Keys.html b/docs/documentation/Public Key Algorithm Keys.html new file mode 100644 index 00000000..cf4e626c --- /dev/null +++ b/docs/documentation/Public Key Algorithm Keys.html @@ -0,0 +1,363 @@ + + + + Public Key Algorithm Keys Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Public Key Algorithm Keys

    + +
    +
    + +
    +
    +
    + +
      +
    • +
      + + + + TSKSupportedAlgorithm + +
      +
      +
      +
      +
      +
      +

      A public key algorithm supported by TrustKit for generating the SSL pin for a certificate.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      typedef NSString *TSKSupportedAlgorithm
      + +
      +
      +

      Swift

      +
      typealias TSKSupportedAlgorithm = NSString
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + kTSKAlgorithmRsa2048 + +
      +
      +
      +
      +
      +
      +

      RSA 2048.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKSupportedAlgorithm kTSKAlgorithmRsa2048
      + +
      +
      +

      Swift

      +
      let kTSKAlgorithmRsa2048: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + kTSKAlgorithmRsa4096 + +
      +
      +
      +
      +
      +
      +

      RSA 4096.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKSupportedAlgorithm kTSKAlgorithmRsa4096
      + +
      +
      +

      Swift

      +
      let kTSKAlgorithmRsa4096: String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      ECDSA with secp256r1 curve.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp256r1
      + +
      +
      +

      Swift

      +
      let kTSKAlgorithmEcDsaSecp256r1: String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      ECDSA with secp384r1 curve.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp384r1
      + +
      +
      +

      Swift

      +
      let kTSKAlgorithmEcDsaSecp384r1: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/Setting up a Validation Callback.html b/docs/documentation/Setting up a Validation Callback.html new file mode 100644 index 00000000..969e1971 --- /dev/null +++ b/docs/documentation/Setting up a Validation Callback.html @@ -0,0 +1,353 @@ + + + + Setting up a Validation Callback Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Setting up a Validation Callback

    + +
    +
    + +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      A block that can be set in a TrustKit instance to be invoked for every request that is going through +instance’s pinning validation logic.

      + +

      The callback will be invoked every time the validator performs pinning validation against a server’s +certificate chain; if the server’s hostname is not defined in the pinning policy, no invocations will +result as no pinning validation was performed.

      + +

      The callback provides the following arguments:

      + +
        +
      • The TSKPinningValidatorResult resulting from the validation of the server’s identity.
      • +
      • The notedHostname, which is the entry within the SSL pinning configuration that was used for the +server being validated.
      • +
      • The notedHostname‘s pinning policy, which was used for the server being validated.
      • +
      + +

      The callback can be used for advanced features such as performance measurement or customizing the +reporting mechanism. Hence, most Apps should not have to use this callback. If set, the callback may +be invoked very frequently and is not a suitable place for expensive tasks.

      + +

      Lastly, the callback is always invoked after the validation has been completed, and therefore +cannot be used to modify the result of the validation (for example to accept invalid certificates).

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      typedef void (^TSKPinningValidatorCallback)(TSKPinningValidatorResult *_Nonnull,
      +                                            NSString *_Nonnull,
      +                                            TKSDomainPinningPolicy *_Nonnull)
      + +
      +
      +

      Swift

      +
      typealias TSKPinningValidatorCallback = (TSKPinningValidatorResult, String, [String : Any]) -> Void
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The pinning policy set for a specific hostname.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      typedef NSDictionary<TSKDomainConfigurationKey, id> TKSDomainPinningPolicy
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      A TSKPinningValidatorResult instance contains all the details regarding a pinning validation +performed against a specific server.

      + + See more +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @interface TSKPinningValidatorResult : NSObject
      + +
      +
      +

      Swift

      +
      class TSKPinningValidatorResult : NSObject
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      Possible return values when verifying a server’s identity against a set of pins.

      + + See more +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      enum TSKTrustEvaluationResult : NSInteger {}
      + +
      +
      +

      Swift

      +
      enum TSKTrustEvaluationResult : Int
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/Type Definitions.html b/docs/documentation/Type Definitions.html deleted file mode 100644 index 32a76ec8..00000000 --- a/docs/documentation/Type Definitions.html +++ /dev/null @@ -1,340 +0,0 @@ - - - - Type Definitions Reference - - - - - - - - - -
    - -
    -
    - -
    -
    - -
    -
    -
    -

    Type Definitions

    -

    The following type definitions are available globally.

    - -
    -
    -
    - -
      -
    • - -
      -
      -
      -
      -
      -

      A global, App-wide configuration key that can be set in the pinning policy.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      typedef NSString *TSKGlobalConfigurationKey
      - -
      -
      -

      Swift

      -
      typealias TSKGlobalConfigurationKey = NSString
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      A domain-specific configuration key (to defined for a domain under the kTSKPinnedDomains key) that can be set in the pinning policy.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      typedef NSString *TSKDomainConfigurationKey
      - -
      -
      -

      Swift

      -
      typealias TSKDomainConfigurationKey = NSString
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - - -
    -
    - -
      -
    • - -
      -
      -
      -
      -
      -

      A key to be used to retrieve data about the pinning validation that occured, from the userInfo dictionary attached to a kTSKValidationCompletedNotification notification.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      typedef NSString *TSKNotificationUserInfoKey
      - -
      -
      -

      Swift

      -
      typealias TSKNotificationUserInfoKey = NSString
      - -
      -
      - -
      -
      -
    • -
    -
    -
    -
    - -
    -
    - - - diff --git a/docs/documentation/css/jazzy.css b/docs/documentation/css/jazzy.css index d6d65b7f..c83db5bf 100644 --- a/docs/documentation/css/jazzy.css +++ b/docs/documentation/css/jazzy.css @@ -1,203 +1,222 @@ -html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { - background: transparent; - border: 0; - margin: 0; - outline: 0; - padding: 0; - vertical-align: baseline; } +*, *:before, *:after { + box-sizing: inherit; } body { - background-color: #f2f2f2; - font-family: Helvetica, freesans, Arial, sans-serif; - font-size: 14px; - -webkit-font-smoothing: subpixel-antialiased; - word-wrap: break-word; } - -h1, h2, h3 { - margin-top: 0.8em; - margin-bottom: 0.3em; - font-weight: 100; - color: black; } + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } h1 { - font-size: 2.5em; } + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } h2 { - font-size: 2em; - border-bottom: 1px solid #e2e2e2; } + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } h4 { - font-size: 13px; - line-height: 1.5; - margin-top: 21px; } + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } h5 { - font-size: 1.1em; } + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } h6 { - font-size: 1.1em; + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; color: #777; } -.section-name { - color: gray; - display: block; - font-family: Helvetica; - font-size: 22px; - font-weight: 100; - margin-bottom: 15px; } +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } -pre, code { - font: 0.95em Menlo, monospace; - color: #777; - word-wrap: normal; } +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } -p code, li code { - background-color: #eee; - padding: 2px 4px; - border-radius: 4px; } +img { + max-width: 100%; } a { - color: #0088cc; + color: #4183c4; text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } -ul { - padding-left: 15px; } +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +p > code, li > code { + background: #f7f7f7; + padding: .2em; } + p > code:before, p > code:after, li > code:before, li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } -li { - line-height: 1.8em; } +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } + +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } -img { - max-width: 100%; } +.header-col--primary { + flex: 1; } -blockquote { - margin-left: 0; - padding: 0 10px; - border-left: 4px solid #ccc; } +.header-link { + color: #fff; } -.content-wrapper { - margin: 0 auto; - width: 980px; } +.header-icon { + padding-right: 6px; + vertical-align: -4px; + height: 16px; } -header { - font-size: 0.85em; - line-height: 26px; - background-color: #414141; - position: fixed; - width: 100%; - z-index: 1; } - header img { - padding-right: 6px; - vertical-align: -4px; - height: 16px; } - header a { - color: #fff; } - header p { - float: left; - color: #999; } - header .header-right { - float: right; - margin-left: 16px; } - -#breadcrumbs { - background-color: #f2f2f2; - height: 27px; - padding-top: 17px; - position: fixed; - width: 100%; - z-index: 1; - margin-top: 26px; } - #breadcrumbs #carat { - height: 10px; - margin: 0 5px; } - -.sidebar { - background-color: #f9f9f9; - border: 1px solid #e2e2e2; - overflow-y: auto; - overflow-x: hidden; - position: fixed; - top: 70px; - bottom: 0; - width: 230px; - word-wrap: normal; } +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } .nav-groups { list-style-type: none; - background: #fff; padding-left: 0; } .nav-group-name { - border-bottom: 1px solid #e2e2e2; - font-size: 1.1em; - font-weight: 100; - padding: 15px 0 15px 20px; } - .nav-group-name > a { - color: #333; } + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } .nav-group-tasks { - margin-top: 5px; } + margin: 8px 0; + padding: 0 0 0 8px; } .nav-group-task { - font-size: 0.9em; + font-size: 1em; list-style-type: none; white-space: nowrap; } - .nav-group-task a { - color: #888; } + +.nav-group-task-link { + color: #808080; } .main-content { - background-color: #fff; - border: 1px solid #e2e2e2; - margin-left: 246px; - position: absolute; - overflow: hidden; - padding-bottom: 60px; - top: 70px; - width: 734px; } - .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { - margin-bottom: 1em; } - .main-content p { - line-height: 1.8em; } - .main-content section .section:first-child { - margin-top: 0; - padding-top: 0; } - .main-content section .task-group-section .task-group:first-of-type { - padding-top: 10px; } - .main-content section .task-group-section .task-group:first-of-type .section-name { - padding-top: 15px; } + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } .section { - padding: 0 25px; } + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } -.highlight { - background-color: #eee; - padding: 10px 12px; - border: 1px solid #e2e2e2; - border-radius: 4px; - overflow-x: auto; } +.section-name { + color: #666; + display: block; } .declaration .highlight { overflow-x: initial; - padding: 0 40px 40px 0; - margin-bottom: -25px; + padding: 8px 0; + margin: 0; background-color: transparent; border: none; } -.section-name { - margin: 0; - margin-left: 18px; } - .task-group-section { - padding-left: 6px; - border-top: 1px solid #e2e2e2; } + border-top: 1px solid #ddd; } .task-group { padding-top: 0px; } .task-name-container a[name]:before { content: ""; - display: block; - padding-top: 70px; - margin: -70px 0 0; } + display: block; } + +.item-container { + padding: 0; } .item { padding-top: 8px; @@ -205,57 +224,47 @@ header { list-style-type: none; } .item a[name]:before { content: ""; - display: block; - padding-top: 70px; - margin: -70px 0 0; } - .item code { - background-color: transparent; - padding: 0; } + display: block; } .item .token { padding-left: 3px; - margin-left: 15px; - font-size: 11.9px; } + margin-left: 0px; + font-size: 1rem; } .item .declaration-note { font-size: .85em; - color: gray; + color: #808080; font-style: italic; } .pointer-container { - border-bottom: 1px solid #e2e2e2; + border-bottom: 1px solid #ddd; left: -23px; padding-bottom: 13px; position: relative; width: 110%; } .pointer { - background: #f9f9f9; - border-left: 1px solid #e2e2e2; - border-top: 1px solid #e2e2e2; - height: 12px; left: 21px; - top: -7px; - -webkit-transform: rotate(45deg); - -moz-transform: rotate(45deg); - -o-transform: rotate(45deg); - transform: rotate(45deg); + top: 7px; + display: block; position: absolute; - width: 12px; } + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } .height-container { display: none; - left: -25px; - padding: 0 25px; position: relative; width: 100%; overflow: hidden; } .height-container .section { - background: #f9f9f9; - border-bottom: 1px solid #e2e2e2; - left: -25px; - position: relative; - width: 100%; + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; padding-top: 10px; - padding-bottom: 5px; } + padding-bottom: 5px; + padding: 8px 16px; } .aside, .language { padding: 6px 12px; @@ -276,7 +285,7 @@ header { .language { border-left: 5px solid #cde9f4; } .language .aside-title { - color: #4b8afb; } + color: #4183c4; } .aside-warning { border-left: 5px solid #ff6666; } @@ -291,7 +300,7 @@ header { word-break: break-word; min-width: 50px; } .graybox td { - border: 1px solid #e2e2e2; + border: 1px solid #ddd; padding: 5px 25px 5px 10px; vertical-align: middle; } .graybox tr td:first-of-type { @@ -304,29 +313,56 @@ header { .slightly-smaller { font-size: 0.9em; } -#footer { - position: absolute; - bottom: 10px; - margin-left: 25px; } - #footer p { - margin: 0; - color: #aaa; - font-size: 0.8em; } +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } -html.dash header, html.dash #breadcrumbs, html.dash .sidebar { +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { display: none; } -html.dash .main-content { - width: 980px; - margin-left: 0; - border: none; - width: 100%; - top: 0; - padding-bottom: 0; } html.dash .height-container { display: block; } -html.dash .item .token { - margin-left: 0; } -html.dash .content-wrapper { - width: auto; } -html.dash #footer { - position: static; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(../img/spinner.gif) center right 4px no-repeat; } +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } +form[role=search] .tt-highlight { + font-weight: bold; } +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes.html deleted file mode 100644 index 65e6ecb1..00000000 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes.html +++ /dev/null @@ -1,335 +0,0 @@ - - - - Classes Reference - - - - - - - - - -
    - -
    -
    - -
    -
    - -
    -
    -
    -

    Classes

    -

    The following classes are available globally.

    - -
    -
    -
    -
      -
    • -
      - - - - TSKPinningValidator - -
      -
      -
      -
      -
      -
      -

      TSKPinningValidator is a class for manually verifying a server’s identity against the global SSL pinning policy.

      - -

      In specific scenarios, TrustKit cannot intercept outgoing SSL connections and automatically validate the server’s identity against the pinning policy:

      - -
        -
      • All connections within an App that disables TrustKit’s network delegate swizzling by setting the kTSKSwizzleNetworkDelegates configuration key to NO.
      • -
      • Connections that do not rely on the NSURLConnection or NSURLSession APIs: - -
          -
        • WKWebView connections.
        • -
        • Connections leveraging low-level network APIs (such as NSStream).
        • -
        • Connections initiated using a third-party SSL library such as OpenSSL.
        • -
      • -
      - -

      For these connections, pin validation must be manually triggered using one of the two available methods:

      - -
        -
      • evaluateTrust:forHostname: which evaluates the server’s certificate chain against the global SSL pinning policy.
      • -
      • handleChallenge:completionHandler: a helper method to be used for implementing pinning validation in challenge handler methods within NSURLSession and WKWebView delegates.
      • -
      - - See more -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      @interface TSKPinningValidator : NSObject
      - -
      -
      -

      Swift

      -
      class TSKPinningValidator : NSObject
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - -
      -
    • -
      - - - - TrustKit - -
      -
      -
      -
      -
      -
      -

      TrustKit is a class for programmatically configuring the global SSL pinning policy within an App.

      - -

      The policy can be set either by adding it to the App’s Info.plist under the TSKConfiguration key, or by programmatically supplying it using the TrustKit class described here. Throughout the App’s lifecycle, TrustKit can only be initialized once so only one of the two techniques should be used.

      - -

      A TrustKit pinning policy is a dictionary which contains some global, App-wide settings (of type TSKGlobalConfigurationKey) as well as domain-specific configuration keys (of type TSKDomainConfigurationKey) to be defined under the kTSKPinnedDomains entry. The following table shows the keys and the types of the corresponding values, and uses indentation to indicate structure:

      - -
      | Key                                          | Type       |
      -|----------------------------------------------|------------|
      -| TSKSwizzleNetworkDelegates                   | Boolean    |
      -| TSKIgnorePinningForUserDefinedTrustAnchors   | Boolean    |
      -| TSKPinnedDomains                             | Dictionary |
      -| __ <domain-name-to-pin-as-string>            | Dictionary |
      -| ____ TSKPublicKeyHashes                      | Array      |
      -| ____ TSKPublicKeyAlgorithms                  | Array      |
      -| ____ TSKIncludeSubdomains                    | Boolean    |
      -| ____ TSKExcludeSubdomainFromParentPolicy     | Boolean    |
      -| ____ TSKEnforcePinning                       | Boolean    |
      -| ____ TSKReportUris                           | Array      |
      -| ____ TSKDisableDefaultReportUri              | Boolean    |
      -
      - -

      When setting the pinning policy programmatically, it has to be supplied to the initializeWithConfiguration: method as a dictionary. For example:

      - -
         NSDictionary *trustKitConfig =
      - @{
      -   kTSKSwizzleNetworkDelegates: @NO,
      -   kTSKPinnedDomains : @{
      -           @"www.datatheorem.com" : @{
      -                   kTSKExpirationDate: @"2017-12-01",
      -                   kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048],
      -                   kTSKPublicKeyHashes : @[
      -                           @"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=",
      -                           @"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8="
      -                           ],
      -                   kTSKEnforcePinning : @NO,
      -                   kTSKReportUris : @[@"http://report.datatheorem.com/log_report"],
      -                   },
      -           @"yahoo.com" : @{
      -                   kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096],
      -                   kTSKPublicKeyHashes : @[
      -                           @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=",
      -                           @"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=",
      -                           ],
      -                   kTSKIncludeSubdomains : @YES
      -                   }
      -           }};
      -
      -   [TrustKit initializeWithConfiguration:trustKitConfig];
      -
      - -

      Similarly, TrustKit can be initialized in Swift:

      - -
             let trustKitConfig = [
      -           kTSKSwizzleNetworkDelegates: false,
      -           kTSKPinnedDomains: [
      -               "yahoo.com": [
      -                   kTSKExpirationDate: "2017-12-01",
      -                   kTSKPublicKeyAlgorithms: [kTSKAlgorithmRsa2048],
      -                   kTSKPublicKeyHashes: [
      -                       "JbQbUG5JMJUoI6brnx0x3vZF6jilxsapbXGVfjhN8Fg=",
      -                       "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="
      -                   ],]]] as [String : Any]
      -
      -       TrustKit.initialize(withConfiguration:trustKitConfig)
      -
      - -

      The various configuration keys that can be specified in the policy are described in the Constants section of the documentation.

      - -

      Lastly, once TrustKit has been initialized, kTSKValidationCompletedNotification notifications will be posted every time TrustKit validates the certificate chain of a server; these notifications provide some information about the validation that was done and can be used for example for performance measurement.

      - - See more -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      @interface TrustKit : NSObject
      - -
      -
      -

      Swift

      -
      class TrustKit : NSObject
      - -
      -
      - -
      -
      -
    • -
    -
    -
    -
    - -
    -
    - - - diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidator.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidator.html index 81e3161a..1a8ab487 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidator.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidator.html @@ -4,145 +4,174 @@ TSKPinningValidator Class Reference - + + + + + + -
    - -
    -
    -
    +

    + + TrustKit Docs + +

    -
    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + + + + +
    -
    + + +
    + +
    • @@ -282,23 +323,16 @@

      Return Value

      -

      Helper method for handling authentication challenges received within a NSURLSessionDelegate, NSURLSessionTaskDelegate or WKNavigationDelegate.

      +

      Evaluate the supplied server trust against the SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent.

      -

      This method will evaluate the server trust within the authentication challenge against the global SSL pinning policy previously configured, and then call the completionHandler with the corresponding disposition and credential. For example, this method can be leveraged in a WKNavigationDelegate challenge handler method:

      +

      When using the NSURLSession or WKWebView network APIs, the handleChallenge:completionHandler: method should be called instead, as it is simpler to use.

      -
      - (void)webView:(WKWebView *)webView
      -didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
      -completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
      -NSURLCredential *credential))completionHandler
      -{
      -    if (![TSKPinningValidator handleChallenge:challenge completionHandler:completionHandler]) 
      -    {
      -        // TrustKit did not handle this challenge: perhaps it was not for server trust
      -        // or the domain was not pinned. Fall back to the default behavior
      -        completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
      -    }
      -}
      -
      +

      When using low-level network APIs (such as NSStream), instructions on how to retrieve the connection’s serverTrust are available at https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html .

      +
      +

      Warning

      +

      If no SSL pinning policy was configured for the supplied serverHostname, this method has no effect and will return TSKTrustDecisionDomainNotPinned without validating the supplied serverTrust at all. This means that the server’s serverTrust object must be verified against the device’s trust store using SecTrustEvaluate(). Failing to do so will disable SSL certificate validation.

      + +

      @exception NSException Thrown when TrustKit has not been initialized with a pinning policy.

      @@ -307,15 +341,13 @@

      Return Value

      Declaration

      Objective-C

      -
      + (BOOL)handleChallenge:(NSURLAuthenticationChallenge *_Nonnull)challenge
      -      completionHandler:
      -          (void (^_Nonnull)(NSURLSessionAuthChallengeDisposition,
      -                            NSURLCredential *_Nullable))completionHandler;
      +
      - (TSKTrustDecision)evaluateTrust:(SecTrustRef _Nonnull)serverTrust
      +                      forHostname:(NSString *_Nonnull)serverHostname;

      Swift

      -
      class func handle(_ challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Bool
      +
      func evaluateTrust(_ serverTrust: SecTrust, forHostname serverHostname: String) -> TSKTrustDecision
      @@ -326,26 +358,24 @@

      Parameters

      - challenge + serverTrust
      -

      The authentication challenge, supplied by the URL loading system to the delegate’s challenge handler method.

      - +

      The trust object representing the server’s certificate chain. The trust’s evaluation policy is always overridden using SecTrustSetPolicies() to ensure all the proper SSL checks (expiration, hostname validation, etc.) are enabled.

      - completionHandler + serverHostname
      -

      A block to invoke to respond to the challenge, supplied by the URL loading system to the delegate’s challenge handler method.

      - +

      The hostname of the server whose identity is being validated.

      @@ -354,25 +384,22 @@

      Parameters

      Return Value

      -

      YES if the challenge was handled and the completionHandler was successfuly invoked. NO if the challenge could not be handled because it was not for server certificate validation (ie. the challenge’s authenticationMethod was not NSURLAuthenticationMethodServerTrust).

      - -
      -
      - Show on GitHub +

      A TSKTrustDecision which describes whether the SSL connection should be allowed or blocked, based on the configured pinning policy.

    - - - + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidatorResult.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidatorResult.html new file mode 100644 index 00000000..4286d221 --- /dev/null +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidatorResult.html @@ -0,0 +1,405 @@ + + + + TSKPinningValidatorResult Class Reference + + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    TSKPinningValidatorResult

    +
    +
    +
    @interface TSKPinningValidatorResult : NSObject
    + +
    +
    +

    A TSKPinningValidatorResult instance contains all the details regarding a pinning validation +performed against a specific server.

    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + serverHostname + +
      +
      +
      +
      +
      +
      +

      The hostname of the server SSL pinning validation was performed against.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @property (readonly, nonatomic, nonnull) NSString *serverHostname;
      + +
      +
      +

      Swift

      +
      var serverHostname: String { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + serverTrust + +
      +
      +
      +
      +
      +
      +

      The original SecTrustRef that validation was performed against.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @property (readonly, nonatomic, nonnull) SecTrustRef serverTrust;
      + +
      +
      +

      Swift

      +
      var serverTrust: SecTrust { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + evaluationResult + +
      +
      +
      +
      +
      +
      +

      The result of validating the server’s certificate chain against the set of SSL pins configured for +the notedHostname.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @property (readonly, nonatomic) TSKTrustEvaluationResult evaluationResult;
      + +
      +
      +

      Swift

      +
      var evaluationResult: TSKTrustEvaluationResult { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + finalTrustDecision + +
      +
      +
      +
      +
      +
      +

      The trust decision returned for this connection, which describes whether the connection should be blocked +or allowed, based on the evaluationResult returned when evaluating the serverTrust and the SSL pining +policy configured for this server.

      + +

      For example, the pinning validation could have failed (ie. validationResult being +TSKTrustEvaluationFailedNoMatchingPin) but the policy might be set to ignore pinning validation failures +for this server, thereby returning TSKTrustDecisionShouldAllowConnection.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @property (readonly, nonatomic) TSKTrustDecision finalTrustDecision;
      + +
      +
      +

      Swift

      +
      var finalTrustDecision: TSKTrustDecision { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + validationDuration + +
      +
      +
      +
      +
      +
      +

      The time it took for the SSL pinning validation to be performed.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @property (readonly, nonatomic) NSTimeInterval validationDuration;
      + +
      +
      +

      Swift

      +
      var validationDuration: TimeInterval { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + certificateChain + +
      +
      +
      +
      +
      +
      +

      The certificate chain extracted from the serverTrust as PEM-formatted certificates. This is the +certificate chain sent by the server when establishing the connection.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @property (readonly, nonatomic, nullable) NSArray *certificateChain;
      + +
      +
      +

      Swift

      +
      var certificateChain: [Any]? { get }
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TrustKit.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TrustKit.html index b8817dff..3dbbfa25 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TrustKit.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TrustKit.html @@ -4,145 +4,174 @@ TrustKit Class Reference - + + + + + + -
    - -
    -
    -
    +

    + + TrustKit Docs + +

    -
    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + + + + +
    -
    + +
  • +
    + + + + +sharedInstance + +
    +
    +
    +
    +
    +
    +

    Retrieve the global TrustKit singleton instance. Raises an exception if +initializeWithConfiguration: +has not yet been invoked.

    + +
    +
    +

    Declaration

    +
    +

    Objective-C

    +
    + (nonnull instancetype)sharedInstance;
    + +
    +
    +

    Swift

    +
    class func sharedInstance() -> Self
    + +
    +
    +
    +

    Return Value

    +

    the shared TrustKit singleton

    @@ -292,19 +375,19 @@

    Parameters

    -
      +
      • @@ -312,33 +395,186 @@

        Current Configuration

        -

        Retrieve a copy of the global SSL pinning policy.

        +

        Retrieve the validator instance conforming to the pinning policy of this TrustKit instance.

        + +

        The validator should be used to implement pinning validation within the App’s network +authentication handlers.

        Declaration

        Objective-C

        -
        + (nullable NSDictionary *)configuration;
        +
        @property (assign, readwrite, nonatomic, nonnull)
        +    TSKPinningValidator *pinningValidator;

        Swift

        -
        class func configuration() -> [AnyHashable : Any]?
        +
        var pinningValidator: TSKPinningValidator { get set }
        -
        -

        Return Value

        -

        A dictionary with a copy of the current TrustKit configuration, or nil if TrustKit has not been initialized.

        +
        +
        +
      • +
      +
    +
    + +
      +
    • + +
      +
      +
      +
      +
      +

      Register a block to be invoked for every request that is going through TrustKit’s pinning +validation mechanism. See TSKPinningValidatorCallback for more information.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @property (assign, readwrite, nonatomic, nullable)
      +    TSKPinningValidatorCallback pinningValidatorCallback;
      + +
      +
      +

      Swift

      +
      var pinningValidatorCallback: TSKPinningValidatorCallback? { get set }
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Queue on which to invoke the pinningValidatorCallback; default value is the main queue.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @property (assign, readwrite, nonatomic, null_resettable)
      +    dispatch_queue_t pinningValidatorCallbackQueue;
      + +
      +
      +

      Swift

      +
      var pinningValidatorCallbackQueue: DispatchQueue! { get set }
      + +
      +
      +
      +
      +
    • +
    +
    +
    + +
      +
    • + +
      +
      +
      +
      +
      +

      Initialize a local TrustKit instance with the supplied SSL pinning policy configuration.

      + +

      This method is useful in scenarios where the TrustKit singleton cannot be used, for example within +larger Apps that have split some of their functionality into multiple framework/SDK. Each +framework can initialize its own instance of TrustKit and use it for pinning validation independently +of the App’s other components.

      -
      - Show on GitHub +
      +

      Declaration

      +
      +

      Objective-C

      +
      - (nonnull instancetype)initWithConfiguration:
      +    (nonnull NSDictionary<TSKGlobalConfigurationKey, id> *)trustKitConfig;
      + +
      +
      +

      Swift

      +
      init(configuration trustKitConfig: [String : Any])
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + trustKitConfig + + +
      +

      A dictionary containing various keys for configuring the SSL pinning policy.

      +
      +
    • +
    +
    +
    + +
    • @@ -354,9 +590,12 @@

      Return Value

      Set the global logger.

      -

      This method sets the global logger, used when TrustKit needs to display a message to the developer.

      +

      This method sets the global logger, used when any TrustKit instance needs to display a message to +the developer.

      -

      If a global logger is not set, the default logger will be used, which will print TrustKit log messages (using NSLog()) when the App is built in Debug mode. If the App was built for Release, the default logger will not print any messages at all.

      +

      If a global logger is not set, the default logger will be used, which will only print TrustKit log +messages (using NSLog()) when the App is built in Debug mode. If the App was built for Release, the default +logger will not print any messages at all.

      @@ -372,22 +611,20 @@

      Declaration

      -
  • - - - + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Constants.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Constants.html deleted file mode 100644 index f38eb7a7..00000000 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Constants.html +++ /dev/null @@ -1,1118 +0,0 @@ - - - - Constants Reference - - - - - - - - - -
    - -
    -
    - -
    -
    - -
    -
    -
    -

    Constants

    -

    The following constants are available globally.

    - -
    -
    -
    - -
      -
    • -
      - - - - TrustKitVersion - -
      -
      -
      -
      -
      -
      -

      The version of TrustKit, such as 1.4.0.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern NSString *const _Nonnull TrustKitVersion
      - -
      -
      -

      Swift

      -
      let TrustKitVersion: String
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - -
      -
    • - -
      -
      -
      -
      -
      -

      A boolean. If set to YES, TrustKit will perform method swizzling on the App’s NSURLConnection and NSURLSession delegates in order to automatically add SSL pinning validation to the App’s connections.

      - -

      Swizzling allows enabling pinning within an App without having to find and modify each and every instance of NSURLConnection or NSURLSession delegates. -However, it should only be enabled for simple Apps, as it may not work properly in several scenarios including:

      - -
        -
      • Apps with complex connection delegates, for example to handle client authentication via certificates or basic authentication.
      • -
      • Apps where method swizzling of the connection delegates is already performed by another module or library (such as Analytics SDKs).
      • -
      • Apps that do no use NSURLSession or NSURLConnection for their connections.
      • -
      - -

      In such scenarios or if the developer wants a tigher control on the App’s networking behavior, kTSKSwizzleNetworkDelegates should be set to NO; the developer should then manually add pinning validation to the App’s authentication handlers.

      - -

      See the TSKPinningValidator class for instructions on how to do so.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKGlobalConfigurationKey _Nonnull kTSKSwizzleNetworkDelegates
      - -
      -
      -

      Swift

      -
      let kTSKSwizzleNetworkDelegates: String
      - -
      -
      - -
      -
      -
    • -
    • -
      - - - - kTSKPinnedDomains - -
      -
      -
      -
      -
      -
      -

      A dictionary with domains (such as www.domain.com) as keys and dictionaries as values.

      - -

      Each entry should contain domain-specific settings for performing pinning validation when connecting to the domain, including for example the domain’s public key hashes. A list of all domain-specific keys is available in the Domain-specific Keys sections.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKGlobalConfigurationKey _Nonnull kTSKPinnedDomains
      - -
      -
      -

      Swift

      -
      let kTSKPinnedDomains: String
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - -
      -
    • - -
      -
      -
      -
      -
      -

      A boolean. If set to YES, pinning validation will be skipped if the server’s certificate chain terminates at a user-defined trust anchor (such as a root CA that isn’t part of OS X’s default trust store) and no pin failure reports will be sent; default value is YES.

      - -

      This is useful for allowing SSL connections through corporate proxies or firewalls. See How does key pinning interact with local proxies and filters? within the Chromium security FAQ at https://www.chromium.org/Home/chromium-security/security-faq for more information.

      - -

      Only available on macOS.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKGlobalConfigurationKey _Nonnull kTSKIgnorePinningForUserDefinedTrustAnchors
      - -
      -
      -

      Swift

      -
      let kTSKIgnorePinningForUserDefinedTrustAnchors: String
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - -
      -
    • -
      - - - - kTSKPublicKeyHashes - -
      -
      -
      -
      -
      -
      -

      An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate’s Subject Public Key Info.

      - -

      TrustKit will verify that at least one of the specified pins is found in the server’s evaluated certificate chain.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKPublicKeyHashes
      - -
      -
      -

      Swift

      -
      let kTSKPublicKeyHashes: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      An array of TSKSupportedAlgorithm constants to specify the public key algorithms for the keys to be pinned.

      - -

      TrustKit requires this information in order to compute SSL pins when validating a server’s certificate chain, because the Security framework does not provide APIs to extract the key’s algorithm from an SSL certificate. To minimize the performance impact of Trustkit, only one algorithm should be enabled.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKPublicKeyAlgorithms
      - -
      -
      -

      Swift

      -
      let kTSKPublicKeyAlgorithms: String
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - -
      -
    • -
      - - - - kTSKEnforcePinning - -
      -
      -
      -
      -
      -
      -

      A boolean. If set to NO, TrustKit will not block SSL connections that caused a pin or certificate validation error; default value is YES.

      - -

      When a pinning failure occurs, pin failure reports will always be sent to the configured report URIs regardless of the value of kTSKEnforcePinning.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKEnforcePinning
      - -
      -
      -

      Swift

      -
      let kTSKEnforcePinning: String
      - -
      -
      - -
      -
      -
    • -
    • -
      - - - - kTSKIncludeSubdomains - -
      -
      -
      -
      -
      -
      -

      A boolean. If set to YES, also pin all the subdomains of the specified domain; default value is NO.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKIncludeSubdomains
      - -
      -
      -

      Swift

      -
      let kTSKIncludeSubdomains: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      A boolean. If set to YES, TrustKit will not pin this specific domain if kTSKIncludeSubdomains was set for this domain’s parent domain.

      - -

      This allows excluding specific subdomains from a pinning policy that was applied to a parent domain.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKExcludeSubdomainFromParentPolicy
      - -
      -
      -

      Swift

      -
      let kTSKExcludeSubdomainFromParentPolicy: String
      - -
      -
      - -
      -
      -
    • -
    • -
      - - - - kTSKReportUris - -
      -
      -
      -
      -
      -
      -

      An array of URLs to which pin validation failures should be reported.

      - -

      To minimize the performance impact of sending reports on each validation failure, the reports are uploaded using the background transfer service and are also rate-limited to one per day and per type of failure. For HTTPS report URLs, the HTTPS connections will ignore the SSL pinning policy and use the default certificate validation mechanisms, in order to maximize the chance of the reports reaching the server. The format of the reports is similar to the one described in RFC 7469 for the HPKP specification:

      - -

      { - app-bundle-id: com.datatheorem.testtrustkit2, - app-version: 1, - app-vendor-id: 599F9C00-92DC-4B5C-9464-7971F01F8370, - app-platform: IOS, - app-platform-version: 10.2.0, - trustkit-version: 1.3.1, - hostname: www.datatheorem.com, - port: 0, - noted-hostname: datatheorem.com, - include-subdomains: true, - enforce-pinning: true, - validated-certificate-chain: [ - pem1, … pemN - ], - known-pins: [ - pin-sha256=\d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\, - “pin-sha256=“E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\ - ], - validation-result:1 - }

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKReportUris
      - -
      -
      -

      Swift

      -
      let kTSKReportUris: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      A boolean. If set to YES, the default report URL for sending pin failure reports will be disabled; default value is NO.

      - -

      By default, pin failure reports are sent to a report server hosted by Data Theorem, for detecting potential CA compromises and man-in-the-middle attacks, as well as providing a free dashboard for developers; email info@datatheorem.com if you’d like a dashboard for your App. Only pin failure reports are sent, which contain the App’s bundle ID, the IDFV, and the server’s hostname and certificate chain that failed validation.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKDisableDefaultReportUri
      - -
      -
      -

      Swift

      -
      let kTSKDisableDefaultReportUri: String
      - -
      -
      - -
      -
      -
    • -
    • -
      - - - - kTSKExpirationDate - -
      -
      -
      -
      -
      -
      -

      A string containing the date, in yyyy-MM-dd format, on which the domain’s configured SSL pins expire, thus disabling pinning validation. If the key is not set, then the pins do not expire.

      - -

      Expiration helps prevent connectivity issues in Apps which do not get updates to their pin set, such as when the user disables App updates.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKDomainConfigurationKey _Nonnull kTSKExpirationDate
      - -
      -
      -

      Swift

      -
      let kTSKExpirationDate: String
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - - -
    -
    - -
      -
    • - -
      -
      -
      -
      -
      -

      The name of the notification to be posted for every request that is going through TrustKit’s pinning validation mechanism.

      - -

      Once TrustKit has been initialized, notifications will be posted with this name every time TrustKit validates the certificate chain for a server configured in the SSL pinning policy; if the server’s hostname does not have an entry in the pinning policy, no notifications get posted as no pinning validation was performed.

      - -

      These notifications can be used for performance measurement or to act upon any pinning validation performed by TrustKit (for example to customize the reporting mechanism). The notifications provide details about TrustKit’s inner-workings which most Apps should not need to process. Hence, these notifications can be ignored unless the App requires some advanced customization in regards to pinning validation.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern NSString *_Nonnull kTSKValidationCompletedNotification
      - -
      -
      -

      Swift

      -
      static let tskValidationCompleted: NSNotification.Name
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - -
      -
    • - -
      -
      -
      -
      -
      -

      The time in seconds it took for the SSL pinning validation to be performed.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKNotificationUserInfoKey _Nonnull kTSKValidationDurationNotificationKey
      - -
      -
      -

      Swift

      -
      let kTSKValidationDurationNotificationKey: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      The TSKPinningValidationResult returned when validating the server’s certificate chain, which represents the result of evaluating the certificate chain against the configured SSL pins for this server.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKNotificationUserInfoKey _Nonnull kTSKValidationResultNotificationKey
      - -
      -
      -

      Swift

      -
      let kTSKValidationResultNotificationKey: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      The TSKTrustDecision returned when validating the certificate’s chain, which describes whether the connection should be blocked or allowed, based on the TSKPinningValidationResult returned when evaluating the server’s certificate chain and the SSL pining policy configured for this server.

      - -

      For example, the pinning validation could have failed (returning TSKPinningValidationFailed) but the policy might be set to ignore pinning validation failures for this server, thereby returning TSKTrustDecisionShouldAllowConnection.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKNotificationUserInfoKey _Nonnull kTSKValidationDecisionNotificationKey
      - -
      -
      -

      Swift

      -
      let kTSKValidationDecisionNotificationKey: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      The certificate chain returned by the server as an array of PEM-formatted certificates.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKNotificationUserInfoKey _Nonnull kTSKValidationCertificateChainNotificationKey
      - -
      -
      -

      Swift

      -
      let kTSKValidationCertificateChainNotificationKey: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      The entry within the SSL pinning configuration that was used as the pinning policy for the server being validated. It will be the same as the kTSKValidationServerHostnameNotificationKey entry unless the server is a subdomain of a domain configured in the pinning policy with kTSKIncludeSubdomains enabled. The corresponding pinning configuration that was used for validation can be retrieved using:

      - -
      NSString *notedHostname = userInfo[kTSKValidationNotedHostnameNotificationKey];
      -NSDictionary *hostnameConfiguration = [TrustKit configuration][kTSKPinnedDomains][notedHostname];
      -
      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKNotificationUserInfoKey _Nonnull kTSKValidationNotedHostnameNotificationKey
      - -
      -
      -

      Swift

      -
      let kTSKValidationNotedHostnameNotificationKey: String
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      The hostname of the server SSL pinning validation was performed against.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      extern const TSKNotificationUserInfoKey _Nonnull kTSKValidationServerHostnameNotificationKey
      - -
      -
      -

      Swift

      -
      let kTSKValidationServerHostnameNotificationKey: String
      - -
      -
      - -
      -
      -
    • -
    -
    -
    -
    - -
    -
    - - - diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Domain Configuration Keys.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Domain Configuration Keys.html new file mode 100644 index 00000000..c0f6ecba --- /dev/null +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Domain Configuration Keys.html @@ -0,0 +1,621 @@ + + + + Domain Configuration Keys Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Domain Configuration Keys

    + +
    +
    + +
    +
    +
    + +
      +
    • + +
      +
      +
      +
      +
      +

      A domain-specific configuration key (to defined for a domain under the kTSKPinnedDomains +key) that can be set in the pinning policy.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      typedef NSString *TSKDomainConfigurationKey
      + +
      +
      +

      Swift

      +
      typealias TSKDomainConfigurationKey = NSString
      + +
      +
      +
      +
      +
    • +
    +
    +
    + +
      +
    • +
      + + + + kTSKPublicKeyHashes + +
      +
      +
      +
      +
      +
      +

      An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate’s +Subject Public Key Info.

      + +

      TrustKit will verify that at least one of the specified pins is found in the server’s +evaluated certificate chain.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKPublicKeyHashes
      + +
      +
      +

      Swift

      +
      let kTSKPublicKeyHashes: String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      An array of TSKSupportedAlgorithm constants to specify the public key algorithms for the +keys to be pinned.

      + +

      TrustKit requires this information in order to compute SSL pins when validating a server’s +certificate chain, because the Security framework does not provide APIs to extract the +key’s algorithm from an SSL certificate. To minimize the performance impact of Trustkit, +only one algorithm should be enabled.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKPublicKeyAlgorithms
      + +
      +
      +

      Swift

      +
      let kTSKPublicKeyAlgorithms: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    + +
      +
    • +
      + + + + kTSKEnforcePinning + +
      +
      +
      +
      +
      +
      +

      A boolean. If set to NO, TrustKit will not block SSL connections that caused a pin or +certificate validation error; default value is YES.

      + +

      When a pinning failure occurs, pin failure reports will always be sent to the configured +report URIs regardless of the value of kTSKEnforcePinning.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKEnforcePinning
      + +
      +
      +

      Swift

      +
      let kTSKEnforcePinning: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + kTSKIncludeSubdomains + +
      +
      +
      +
      +
      +
      +

      A boolean. If set to YES, also pin all the subdomains of the specified domain; default +value is NO.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKIncludeSubdomains
      + +
      +
      +

      Swift

      +
      let kTSKIncludeSubdomains: String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      A boolean. If set to YES, TrustKit will not pin this specific domain if kTSKIncludeSubdomains +was set for this domain’s parent domain.

      + +

      This allows excluding specific subdomains from a pinning policy that was applied to a +parent domain.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKExcludeSubdomainFromParentPolicy
      + +
      +
      +

      Swift

      +
      let kTSKExcludeSubdomainFromParentPolicy: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + kTSKReportUris + +
      +
      +
      +
      +
      +
      +

      An array of URLs to which pin validation failures should be reported.

      + +

      To minimize the performance impact of sending reports on each validation failure, the reports +are uploaded using the background transfer service and are also rate-limited to one per day +and per type of failure. For HTTPS report URLs, the HTTPS connections will ignore the SSL +pinning policy and use the default certificate validation mechanisms, in order to maximize +the chance of the reports reaching the server. The format of the reports is similar to the +one described in RFC 7469 for the HPKP specification:

      + +

      { +app-bundle-id: com.datatheorem.testtrustkit2, +app-version: 1, +app-vendor-id: 599F9C00-92DC-4B5C-9464-7971F01F8370, +app-platform: IOS, +app-platform-version: 10.2.0, +trustkit-version: 1.3.1, +hostname: www.datatheorem.com, +port: 0, +noted-hostname: datatheorem.com, +include-subdomains: true, +enforce-pinning: true, +validated-certificate-chain: [ +pem1, … pemN +], +known-pins: [ +pin-sha256=\d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\, +“pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\ +], +validation-result:1 +}

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKReportUris
      + +
      +
      +

      Swift

      +
      let kTSKReportUris: String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      A boolean. If set to YES, the default report URL for sending pin failure reports will +be disabled; default value is NO.

      + +

      By default, pin failure reports are sent to a report server hosted by Data Theorem, for +detecting potential CA compromises and man-in-the-middle attacks, as well as providing a +free dashboard for developers; email info@datatheorem.com if you’d like a dashboard for +your App. Only pin failure reports are sent, which contain the App’s bundle ID, the IDFV, +and the server’s hostname and certificate chain that failed validation.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKDisableDefaultReportUri
      + +
      +
      +

      Swift

      +
      let kTSKDisableDefaultReportUri: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + kTSKExpirationDate + +
      +
      +
      +
      +
      +
      +

      A string containing the date, in yyyy-MM-dd format, on which the domain’s configured SSL +pins expire, thus disabling pinning validation. If the key is not set, then the pins do +not expire.

      + +

      Expiration helps prevent connectivity issues in Apps which do not get updates to their +pin set, such as when the user disables App updates.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKExpirationDate
      + +
      +
      +

      Swift

      +
      let kTSKExpirationDate: String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      An array of strings representing additional trust anchors usable for validating +the trust chain of pinned certificates that do not end in an OS trust anchor.

      + +

      The default behavior of TrustKit is to ignore these trust anchors unless compiled +in debug mode (pass -DDEBUG=1 to the preprocessor). This behavior can be modified +by subclassing TSKPinningValidator and overriding +allowsAdditionalTrustAnchors.

      + +

      The entries in the array should each be a single PEM-encoded public certificate. +Standard RFC-7468 PEM format is supported (see https://tools.ietf.org/html/rfc7468#section-2 ). +Note that the header, footer and any newlines are optional, but aid in readability.

      + +

      SECURITY WARNING: +Misuse of this configuration option could potentially render your application +vulnerable to exploits since it bypasses the normal operating system trust store. +It is intended for enterprise scenarios where a company might be running their +own internal production-grade certificate authority for debugging purposes.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKDomainConfigurationKey kTSKAdditionalTrustAnchors
      + +
      +
      +

      Swift

      +
      let kTSKAdditionalTrustAnchors: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums.html deleted file mode 100644 index 83376200..00000000 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums.html +++ /dev/null @@ -1,200 +0,0 @@ - - - - Enums Reference - - - - - - - - - -
    - -
    -
    - -
    -
    - -
    -
    -
    -

    Enums

    -

    The following enums are available globally.

    - -
    -
    -
    -
      -
    • -
      - - - - TSKTrustDecision - -
      -
      -
      -
      -
      -
      -

      Possible return values when verifying a server’s identity against the global SSL pinning policy using TSKPinningValidator.

      - - See more -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      enum TSKTrustDecision : NSInteger {}
      - -
      -
      -

      Swift

      -
      enum TSKTrustDecision : Int
      - -
      -
      - -
      -
      -
    • -
    -
    -
    -
    - -
    -
    - - - diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustDecision.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustDecision.html index 33b181d9..ad417a89 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustDecision.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustDecision.html @@ -4,145 +4,174 @@ TSKTrustDecision Enum Reference - + + + + + + -
    - -
    -
    -
    +

    + + TrustKit Docs + +

    -
    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + + + + +
    -
    @@ -205,7 +234,7 @@

    Declaration

    -

    Based on the server’s certificate chain and the global pinning policy for this domain, the SSL connection should be blocked. +

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be blocked. A pinning validation failure occured and if a report URI was configured, a pin failure report was sent.

    @@ -222,9 +251,6 @@

    Declaration

    -
    @@ -258,22 +284,20 @@

    Declaration

    - - - - + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustEvaluationResult.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustEvaluationResult.html new file mode 100644 index 00000000..21a40fc0 --- /dev/null +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustEvaluationResult.html @@ -0,0 +1,396 @@ + + + + TSKTrustEvaluationResult Enum Reference + + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    TSKTrustEvaluationResult

    +
    +
    +
    enum TSKTrustEvaluationResult : NSInteger {}
    + +
    +
    +

    Possible return values when verifying a server’s identity against a set of pins.

    + +
    +
    + +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      The server trust was succesfully evaluated and contained at least one of the configured pins.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      TSKTrustEvaluationSuccess
      + +
      +
      +

      Swift

      +
      case success = 0
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The server trust was succesfully evaluated but did not contain any of the configured pins.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      TSKTrustEvaluationFailedNoMatchingPin
      + +
      +
      +

      Swift

      +
      case failedNoMatchingPin = 1
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The server trust’s evaluation failed: the server’s certificate chain is not trusted.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      TSKTrustEvaluationFailedInvalidCertificateChain
      + +
      +
      +

      Swift

      +
      case failedInvalidCertificateChain = 2
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The server trust could not be evaluated due to invalid parameters.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      TSKTrustEvaluationErrorInvalidParameters
      + +
      +
      +

      Swift

      +
      case errorInvalidParameters = 3
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to the macOS trust store). Only available on macOS.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      TSKTrustEvaluationFailedUserDefinedTrustAnchor
      + +
      +
      +

      Swift

      +
      case failedUserDefinedTrustAnchor = 4
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The server trust could not be evaluated due to an error when trying to generate the certificate’s subject public key info hash. On iOS 9 or below, this could be caused by a Keychain failure when trying to extract the certificate’s public key bytes.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      TSKTrustEvaluationErrorCouldNotGenerateSpkiHash
      + +
      +
      +

      Swift

      +
      case errorCouldNotGenerateSpkiHash = 5
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Global Configuration Keys.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Global Configuration Keys.html new file mode 100644 index 00000000..29d0b84d --- /dev/null +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Global Configuration Keys.html @@ -0,0 +1,388 @@ + + + + Global Configuration Keys Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Global Configuration Keys

    + +
    +
    + +
    +
    +
    + +
      +
    • + +
      +
      +
      +
      +
      +

      A global, App-wide configuration key that can be set in the pinning policy.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      typedef NSString *TSKGlobalConfigurationKey
      + +
      +
      +

      Swift

      +
      typealias TSKGlobalConfigurationKey = NSString
      + +
      +
      +
      +
      +
    • +
    +
    +
    + +
      +
    • + +
      +
      +
      +
      +
      +

      A boolean. If set to YES, TrustKit will perform method swizzling on the App’s +NSURLConnection and NSURLSession delegates in order to automatically add SSL pinning +validation to the App’s connections. This option can only be used if TrustKit is +initialized in singleton mode; default value is NO.

      + +

      Swizzling allows enabling pinning within an App without having to find and modify each +and every instance of NSURLConnection or NSURLSession delegates. +However, it should only be enabled for simple Apps, as it may not work properly in several +scenarios including:

      + +
        +
      • Apps with complex connection delegates, for example to handle client authentication +via certificates or basic authentication.
      • +
      • Apps where method swizzling of the connection delegates is already performed by another +module or library (such as Analytics SDKs).
      • +
      • Apps that do no use NSURLSession or NSURLConnection for their connections.
      • +
      + +

      In such scenarios or if the developer wants a tigher control on the App’s networking +behavior, kTSKSwizzleNetworkDelegates should be set to NO; the developer should then +manually add pinning validation to the App’s authentication handlers.

      + +

      See the TSKPinningValidator class for instructions on how to do so.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKGlobalConfigurationKey kTSKSwizzleNetworkDelegates
      + +
      +
      +

      Swift

      +
      let kTSKSwizzleNetworkDelegates: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + kTSKPinnedDomains + +
      +
      +
      +
      +
      +
      +

      A dictionary with domains (such as www.domain.com) as keys and dictionaries as values.

      + +

      Each entry should contain domain-specific settings for performing pinning validation when +connecting to the domain, including for example the domain’s public key hashes. A list of +all domain-specific keys is available in the Domain-specific Keys sections.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKGlobalConfigurationKey kTSKPinnedDomains
      + +
      +
      +

      Swift

      +
      let kTSKPinnedDomains: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    + +
      +
    • + +
      +
      +
      +
      +
      +

      A boolean. If set to YES, pinning validation will be skipped if the server’s certificate +chain terminates at a user-defined trust anchor (such as a root CA that isn’t part of OS X’s +default trust store) and no pin failure reports will be sent; default value is YES.

      + +

      This is useful for allowing SSL connections through corporate proxies or firewalls. See +How does key pinning interact with local proxies and filters? within the Chromium security +FAQ at https://www.chromium.org/Home/chromium-security/security-faq for more information.

      + +

      Only available on macOS.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKGlobalConfigurationKey
      +    kTSKIgnorePinningForUserDefinedTrustAnchors
      + +
      +
      +

      Swift

      +
      let kTSKIgnorePinningForUserDefinedTrustAnchors: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Implementing Pinning Validation.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Implementing Pinning Validation.html new file mode 100644 index 00000000..b09a3e1d --- /dev/null +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Implementing Pinning Validation.html @@ -0,0 +1,281 @@ + + + + Implementing Pinning Validation Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Implementing Pinning Validation

    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + TSKPinningValidator + +
      +
      +
      +
      +
      +
      +

      A TSKPinningValidator instance can be used to verify a server’s identity against an SSL pinning policy.

      + +

      In specific scenarios, TrustKit cannot intercept outgoing SSL connections and automatically validate the server’s identity against the pinning policy:

      + +
        +
      • All connections within an App that disables TrustKit’s network delegate swizzling by setting the kTSKSwizzleNetworkDelegates configuration key to NO.
      • +
      • Connections that do not rely on the NSURLConnection or NSURLSession APIs: + +
          +
        • WKWebView connections.
        • +
        • Connections leveraging low-level network APIs (such as NSStream).
        • +
        • Connections initiated using a third-party SSL library such as OpenSSL.
        • +
      • +
      + +

      For these connections, pin validation must be manually triggered using one of the two available methods within TSKPinningValidator.

      + + See more +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @interface TSKPinningValidator : NSObject
      + +
      +
      +

      Swift

      +
      class TSKPinningValidator : NSObject
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + TSKTrustDecision + +
      +
      +
      +
      +
      +
      +

      Possible return values when verifying a server’s identity against an SSL pinning policy.

      + + See more +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      enum TSKTrustDecision : NSInteger {}
      + +
      +
      +

      Swift

      +
      enum TSKTrustDecision : Int
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Initalizing TrustKit.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Initalizing TrustKit.html new file mode 100644 index 00000000..b45493cf --- /dev/null +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Initalizing TrustKit.html @@ -0,0 +1,314 @@ + + + + Initalizing TrustKit Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Initalizing TrustKit

    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + TrustKit + +
      +
      +
      +
      +
      +
      +

      TrustKit is the main class for configuring an SSL pinning policy within an App.

      + +

      For most Apps, TrustKit should be used as a singleton, where a global SSL pinning policy is +configured for the App. In singleton mode, the policy can be set either:

      + +
        +
      • By adding it to the App’s Info.plist under the TSKConfiguration key, or
      • +
      • By programmatically supplying it using the +initializeWithConfiguration: method.
      • +
      + +

      In singleton mode, TrustKit can only be initialized once so only one of the two techniques +should be used.

      + +

      For more complex Apps where multiple SSL pinning policies need to be used independently +(for example within different frameworks), TrustKit can be used in multi-instance mode +by leveraging the -initWithConfiguration:identifier: method described at the end of this +page.

      + +

      A TrustKit pinning policy is a dictionary which contains some global, App-wide settings +(of type TSKGlobalConfigurationKey) as well as domain-specific configuration keys +(of type TSKDomainConfigurationKey) to be defined under the kTSKPinnedDomains entry. +The following table shows the keys and the types of the corresponding values, and uses +indentation to indicate structure:

      +
      | Key                                          | Type       |
      +|----------------------------------------------|------------|
      +| TSKSwizzleNetworkDelegates                   | Boolean    |
      +| TSKIgnorePinningForUserDefinedTrustAnchors   | Boolean    |
      +| TSKPinnedDomains                             | Dictionary |
      +| __ <domain-name-to-pin-as-string>            | Dictionary |
      +| ____ TSKPublicKeyHashes                      | Array      |
      +| ____ TSKPublicKeyAlgorithms                  | Array      |
      +| ____ TSKIncludeSubdomains                    | Boolean    |
      +| ____ TSKExcludeSubdomainFromParentPolicy     | Boolean    |
      +| ____ TSKEnforcePinning                       | Boolean    |
      +| ____ TSKReportUris                           | Array      |
      +| ____ TSKDisableDefaultReportUri              | Boolean    |
      +| ____ TSKAdditionalTrustAnchors               | Array      |
      +
      + +

      When setting the pinning policy programmatically, it has to be supplied to the +initializeWithConfiguration: method as a dictionary in order to initialize TrustKit. +For example:

      +
         NSDictionary *trustKitConfig =
      + @{
      +   kTSKPinnedDomains : @{
      +           @"www.datatheorem.com" : @{
      +                   kTSKExpirationDate: @"2017-12-01",
      +                   kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048],
      +                   kTSKPublicKeyHashes : @[
      +                           @"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=",
      +                           @"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8="
      +                           ],
      +                   kTSKEnforcePinning : @NO,
      +                   kTSKReportUris : @[@"http://report.datatheorem.com/log_report"],
      +                   },
      +           @"yahoo.com" : @{
      +                   kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096],
      +                   kTSKPublicKeyHashes : @[
      +                           @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=",
      +                           @"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=",
      +                           ],
      +                   kTSKIncludeSubdomains : @YES
      +                   }
      +           }};
      +
      +   [TrustKit initializeWithConfiguration:trustKitConfig];
      +   trustKit = [TrustKit sharedInstance];
      +
      + +

      Similarly, the TrustKit singleton can be initialized in Swift:

      +
             let trustKitConfig = [
      +           kTSKSwizzleNetworkDelegates: false,
      +           kTSKPinnedDomains: [
      +               "yahoo.com": [
      +                   kTSKExpirationDate: "2017-12-01",
      +                   kTSKPublicKeyAlgorithms: [kTSKAlgorithmRsa2048],
      +                   kTSKPublicKeyHashes: [
      +                       "JbQbUG5JMJUoI6brnx0x3vZF6jilxsapbXGVfjhN8Fg=",
      +                       "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="
      +                   ],]]] as [String : Any]
      +
      +       TrustKit.initialize(withConfiguration:trustKitConfig)
      +
      + +

      After initialization, the TrustKit instance’s pinningValidator should be used to implement +pinning validation within the App’s network authentication handlers.

      + + See more +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @interface TrustKit : NSObject
      + +
      +
      +

      Swift

      +
      class TrustKit : NSObject
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Other Constants.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Other Constants.html new file mode 100644 index 00000000..108f58b6 --- /dev/null +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Other Constants.html @@ -0,0 +1,229 @@ + + + + Other Constants Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Other Constants

    +

    The following constants are available globally.

    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + TrustKitVersion + +
      +
      +
      +
      +
      +
      +

      The version of TrustKit, such as 1.4.0.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern NSString *const TrustKitVersion
      + +
      +
      +

      Swift

      +
      let TrustKitVersion: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Public Key Algorithm Keys.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Public Key Algorithm Keys.html new file mode 100644 index 00000000..cf4e626c --- /dev/null +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Public Key Algorithm Keys.html @@ -0,0 +1,363 @@ + + + + Public Key Algorithm Keys Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Public Key Algorithm Keys

    + +
    +
    + +
    +
    +
    + +
      +
    • +
      + + + + TSKSupportedAlgorithm + +
      +
      +
      +
      +
      +
      +

      A public key algorithm supported by TrustKit for generating the SSL pin for a certificate.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      typedef NSString *TSKSupportedAlgorithm
      + +
      +
      +

      Swift

      +
      typealias TSKSupportedAlgorithm = NSString
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + kTSKAlgorithmRsa2048 + +
      +
      +
      +
      +
      +
      +

      RSA 2048.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKSupportedAlgorithm kTSKAlgorithmRsa2048
      + +
      +
      +

      Swift

      +
      let kTSKAlgorithmRsa2048: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + kTSKAlgorithmRsa4096 + +
      +
      +
      +
      +
      +
      +

      RSA 4096.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKSupportedAlgorithm kTSKAlgorithmRsa4096
      + +
      +
      +

      Swift

      +
      let kTSKAlgorithmRsa4096: String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      ECDSA with secp256r1 curve.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp256r1
      + +
      +
      +

      Swift

      +
      let kTSKAlgorithmEcDsaSecp256r1: String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      ECDSA with secp384r1 curve.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      extern const TSKSupportedAlgorithm kTSKAlgorithmEcDsaSecp384r1
      + +
      +
      +

      Swift

      +
      let kTSKAlgorithmEcDsaSecp384r1: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Setting up a Validation Callback.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Setting up a Validation Callback.html new file mode 100644 index 00000000..969e1971 --- /dev/null +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Setting up a Validation Callback.html @@ -0,0 +1,353 @@ + + + + Setting up a Validation Callback Reference + + + + + + + + + + + + + + + +
    +

    + + TrustKit Docs + + +

    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + +
    + + + +
    + +
    + +
    +
    +

    Setting up a Validation Callback

    + +
    +
    + +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      A block that can be set in a TrustKit instance to be invoked for every request that is going through +instance’s pinning validation logic.

      + +

      The callback will be invoked every time the validator performs pinning validation against a server’s +certificate chain; if the server’s hostname is not defined in the pinning policy, no invocations will +result as no pinning validation was performed.

      + +

      The callback provides the following arguments:

      + +
        +
      • The TSKPinningValidatorResult resulting from the validation of the server’s identity.
      • +
      • The notedHostname, which is the entry within the SSL pinning configuration that was used for the +server being validated.
      • +
      • The notedHostname‘s pinning policy, which was used for the server being validated.
      • +
      + +

      The callback can be used for advanced features such as performance measurement or customizing the +reporting mechanism. Hence, most Apps should not have to use this callback. If set, the callback may +be invoked very frequently and is not a suitable place for expensive tasks.

      + +

      Lastly, the callback is always invoked after the validation has been completed, and therefore +cannot be used to modify the result of the validation (for example to accept invalid certificates).

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      typedef void (^TSKPinningValidatorCallback)(TSKPinningValidatorResult *_Nonnull,
      +                                            NSString *_Nonnull,
      +                                            TKSDomainPinningPolicy *_Nonnull)
      + +
      +
      +

      Swift

      +
      typealias TSKPinningValidatorCallback = (TSKPinningValidatorResult, String, [String : Any]) -> Void
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The pinning policy set for a specific hostname.

      + +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      typedef NSDictionary<TSKDomainConfigurationKey, id> TKSDomainPinningPolicy
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      A TSKPinningValidatorResult instance contains all the details regarding a pinning validation +performed against a specific server.

      + + See more +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      @interface TSKPinningValidatorResult : NSObject
      + +
      +
      +

      Swift

      +
      class TSKPinningValidatorResult : NSObject
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      Possible return values when verifying a server’s identity against a set of pins.

      + + See more +
      +
      +

      Declaration

      +
      +

      Objective-C

      +
      enum TSKTrustEvaluationResult : NSInteger {}
      + +
      +
      +

      Swift

      +
      enum TSKTrustEvaluationResult : Int
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Type Definitions.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Type Definitions.html deleted file mode 100644 index 32a76ec8..00000000 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Type Definitions.html +++ /dev/null @@ -1,340 +0,0 @@ - - - - Type Definitions Reference - - - - - - - - - -
    - -
    -
    - -
    -
    - -
    -
    -
    -

    Type Definitions

    -

    The following type definitions are available globally.

    - -
    -
    -
    - -
      -
    • - -
      -
      -
      -
      -
      -

      A global, App-wide configuration key that can be set in the pinning policy.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      typedef NSString *TSKGlobalConfigurationKey
      - -
      -
      -

      Swift

      -
      typealias TSKGlobalConfigurationKey = NSString
      - -
      -
      - -
      -
      -
    • -
    • - -
      -
      -
      -
      -
      -

      A domain-specific configuration key (to defined for a domain under the kTSKPinnedDomains key) that can be set in the pinning policy.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      typedef NSString *TSKDomainConfigurationKey
      - -
      -
      -

      Swift

      -
      typealias TSKDomainConfigurationKey = NSString
      - -
      -
      - -
      -
      -
    • -
    -
    -
    - - -
    -
    - -
      -
    • - -
      -
      -
      -
      -
      -

      A key to be used to retrieve data about the pinning validation that occured, from the userInfo dictionary attached to a kTSKValidationCompletedNotification notification.

      - -
      -
      -

      Declaration

      -
      -

      Objective-C

      -
      typedef NSString *TSKNotificationUserInfoKey
      - -
      -
      -

      Swift

      -
      typealias TSKNotificationUserInfoKey = NSString
      - -
      -
      - -
      -
      -
    • -
    -
    -
    -
    - -
    -
    - - - diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/css/jazzy.css b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/css/jazzy.css index d6d65b7f..c83db5bf 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/css/jazzy.css +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/css/jazzy.css @@ -1,203 +1,222 @@ -html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { - background: transparent; - border: 0; - margin: 0; - outline: 0; - padding: 0; - vertical-align: baseline; } +*, *:before, *:after { + box-sizing: inherit; } body { - background-color: #f2f2f2; - font-family: Helvetica, freesans, Arial, sans-serif; - font-size: 14px; - -webkit-font-smoothing: subpixel-antialiased; - word-wrap: break-word; } - -h1, h2, h3 { - margin-top: 0.8em; - margin-bottom: 0.3em; - font-weight: 100; - color: black; } + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } h1 { - font-size: 2.5em; } + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } h2 { - font-size: 2em; - border-bottom: 1px solid #e2e2e2; } + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } h4 { - font-size: 13px; - line-height: 1.5; - margin-top: 21px; } + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } h5 { - font-size: 1.1em; } + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } h6 { - font-size: 1.1em; + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; color: #777; } -.section-name { - color: gray; - display: block; - font-family: Helvetica; - font-size: 22px; - font-weight: 100; - margin-bottom: 15px; } +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } -pre, code { - font: 0.95em Menlo, monospace; - color: #777; - word-wrap: normal; } +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } -p code, li code { - background-color: #eee; - padding: 2px 4px; - border-radius: 4px; } +img { + max-width: 100%; } a { - color: #0088cc; + color: #4183c4; text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } -ul { - padding-left: 15px; } +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +p > code, li > code { + background: #f7f7f7; + padding: .2em; } + p > code:before, p > code:after, li > code:before, li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } -li { - line-height: 1.8em; } +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } + +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } -img { - max-width: 100%; } +.header-col--primary { + flex: 1; } -blockquote { - margin-left: 0; - padding: 0 10px; - border-left: 4px solid #ccc; } +.header-link { + color: #fff; } -.content-wrapper { - margin: 0 auto; - width: 980px; } +.header-icon { + padding-right: 6px; + vertical-align: -4px; + height: 16px; } -header { - font-size: 0.85em; - line-height: 26px; - background-color: #414141; - position: fixed; - width: 100%; - z-index: 1; } - header img { - padding-right: 6px; - vertical-align: -4px; - height: 16px; } - header a { - color: #fff; } - header p { - float: left; - color: #999; } - header .header-right { - float: right; - margin-left: 16px; } - -#breadcrumbs { - background-color: #f2f2f2; - height: 27px; - padding-top: 17px; - position: fixed; - width: 100%; - z-index: 1; - margin-top: 26px; } - #breadcrumbs #carat { - height: 10px; - margin: 0 5px; } - -.sidebar { - background-color: #f9f9f9; - border: 1px solid #e2e2e2; - overflow-y: auto; - overflow-x: hidden; - position: fixed; - top: 70px; - bottom: 0; - width: 230px; - word-wrap: normal; } +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } .nav-groups { list-style-type: none; - background: #fff; padding-left: 0; } .nav-group-name { - border-bottom: 1px solid #e2e2e2; - font-size: 1.1em; - font-weight: 100; - padding: 15px 0 15px 20px; } - .nav-group-name > a { - color: #333; } + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } .nav-group-tasks { - margin-top: 5px; } + margin: 8px 0; + padding: 0 0 0 8px; } .nav-group-task { - font-size: 0.9em; + font-size: 1em; list-style-type: none; white-space: nowrap; } - .nav-group-task a { - color: #888; } + +.nav-group-task-link { + color: #808080; } .main-content { - background-color: #fff; - border: 1px solid #e2e2e2; - margin-left: 246px; - position: absolute; - overflow: hidden; - padding-bottom: 60px; - top: 70px; - width: 734px; } - .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { - margin-bottom: 1em; } - .main-content p { - line-height: 1.8em; } - .main-content section .section:first-child { - margin-top: 0; - padding-top: 0; } - .main-content section .task-group-section .task-group:first-of-type { - padding-top: 10px; } - .main-content section .task-group-section .task-group:first-of-type .section-name { - padding-top: 15px; } + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } .section { - padding: 0 25px; } + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } -.highlight { - background-color: #eee; - padding: 10px 12px; - border: 1px solid #e2e2e2; - border-radius: 4px; - overflow-x: auto; } +.section-name { + color: #666; + display: block; } .declaration .highlight { overflow-x: initial; - padding: 0 40px 40px 0; - margin-bottom: -25px; + padding: 8px 0; + margin: 0; background-color: transparent; border: none; } -.section-name { - margin: 0; - margin-left: 18px; } - .task-group-section { - padding-left: 6px; - border-top: 1px solid #e2e2e2; } + border-top: 1px solid #ddd; } .task-group { padding-top: 0px; } .task-name-container a[name]:before { content: ""; - display: block; - padding-top: 70px; - margin: -70px 0 0; } + display: block; } + +.item-container { + padding: 0; } .item { padding-top: 8px; @@ -205,57 +224,47 @@ header { list-style-type: none; } .item a[name]:before { content: ""; - display: block; - padding-top: 70px; - margin: -70px 0 0; } - .item code { - background-color: transparent; - padding: 0; } + display: block; } .item .token { padding-left: 3px; - margin-left: 15px; - font-size: 11.9px; } + margin-left: 0px; + font-size: 1rem; } .item .declaration-note { font-size: .85em; - color: gray; + color: #808080; font-style: italic; } .pointer-container { - border-bottom: 1px solid #e2e2e2; + border-bottom: 1px solid #ddd; left: -23px; padding-bottom: 13px; position: relative; width: 110%; } .pointer { - background: #f9f9f9; - border-left: 1px solid #e2e2e2; - border-top: 1px solid #e2e2e2; - height: 12px; left: 21px; - top: -7px; - -webkit-transform: rotate(45deg); - -moz-transform: rotate(45deg); - -o-transform: rotate(45deg); - transform: rotate(45deg); + top: 7px; + display: block; position: absolute; - width: 12px; } + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } .height-container { display: none; - left: -25px; - padding: 0 25px; position: relative; width: 100%; overflow: hidden; } .height-container .section { - background: #f9f9f9; - border-bottom: 1px solid #e2e2e2; - left: -25px; - position: relative; - width: 100%; + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; padding-top: 10px; - padding-bottom: 5px; } + padding-bottom: 5px; + padding: 8px 16px; } .aside, .language { padding: 6px 12px; @@ -276,7 +285,7 @@ header { .language { border-left: 5px solid #cde9f4; } .language .aside-title { - color: #4b8afb; } + color: #4183c4; } .aside-warning { border-left: 5px solid #ff6666; } @@ -291,7 +300,7 @@ header { word-break: break-word; min-width: 50px; } .graybox td { - border: 1px solid #e2e2e2; + border: 1px solid #ddd; padding: 5px 25px 5px 10px; vertical-align: middle; } .graybox tr td:first-of-type { @@ -304,29 +313,56 @@ header { .slightly-smaller { font-size: 0.9em; } -#footer { - position: absolute; - bottom: 10px; - margin-left: 25px; } - #footer p { - margin: 0; - color: #aaa; - font-size: 0.8em; } +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } -html.dash header, html.dash #breadcrumbs, html.dash .sidebar { +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { display: none; } -html.dash .main-content { - width: 980px; - margin-left: 0; - border: none; - width: 100%; - top: 0; - padding-bottom: 0; } html.dash .height-container { display: block; } -html.dash .item .token { - margin-left: 0; } -html.dash .content-wrapper { - width: auto; } -html.dash #footer { - position: static; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(../img/spinner.gif) center right 4px no-repeat; } +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } +form[role=search] .tt-highlight { + font-weight: bold; } +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/img/spinner.gif b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/img/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..e3038d0a42c55b1a07caecb7c7a6cbac982ec09d GIT binary patch literal 1849 zcmb8wZBSF$83*voz31lM+?V7Kkqwb`LI|3K#DupH#kDs91c6du6?SMB!bsTr6|ge_{#vAVj^!DyNA-l zJ&$jDFNv;BTZXX@Qk-7+S5ErF>mkOcZ@lQv>F1VyCEMe2Ud@f<|L%#&QJi${E`2lR zqKFaW2Y$aTRxUY&ae$IHsN;Z;rdZ%CjYLTv!tMi234j-ON=CnvK-1QU|MG$YErn{gHZ@0Q6&?xSyply?S$EVNXH;gp?S5kV2-)$ga^gw`(f4Mm_Y(`RbgRkQTHF2@zL}dCiLk$RoZIc{xZL z_J*d5)Kb;#oKCFyfL*NGSs?y;e(QKvPJe1#G)h5*6E(?L9$nt?UaQJfP^$GDL0PU; z?r}C|);JQ4HES3w5VMlY7x6xfJAzDKlHE~>x;D`Fa=WygYot{pfFehH69o9pK|72W zwC6?t^AnATIJa=kewn=ep?Nk(aZ*pZo}51`S=^)jPRb`~l^VE}08>P3OJtQlXx1K8 z8Q}_u=F*fS;=k=?(fIv#+%811NTx8^}rHwvH%LbYmpFl9p1A{Idh@2x$ zuVp7)VD9}Uc(*(C**!QOdS(6B)$5^Tq5p3q*7un&_Z-NKEiEYg$D{Uq&sa>wj|za5 zJ6M~p)z+E6*X${8j6Ci+sqZ}zxeCAo0gZmZuhl+)Q%1U$Br_`NXcA-3yBdYMha+{o z{?q0Q(kaR2n`M29{!pwpgX6+CPQEgIO%x*0#!TC=c-ZPSkLO>OcmQUao5%-3w)U`F zRz?uGCEKQDh!TQPDmyd;iDX$TkMIe)%61q51Y2b-ie4r00!csilXgKL$txqj|6D(# z@(#!nQ}3R1JGeB3B5Tuqdvyg@*!-bq`9`pmasNGvy9^*+cd1Y*g>HK#rl7i79QQAG zl4SL_wW@WY1d+F?j0gFInGhsRrqvV3SKl{oqW+;9!fu|u@J)h4WM!0Cu02l@p60b#5M9c{dKh=_eRw~yl zWT0gw8RePzf%i8X&twiB|LF0bI@CYE{x1PI;Ylr4RJzU#Zc0j!c07g&q7=_eSd(sH z9VKChd?}^52IKcMqolAWiQH;HSp1Ploa$t zQhg|2sK;%Eb!By`)j9G1w?>`Wt6IK3gB}~uoue(MlRiIoZ#d{pgJZ8b{^{HO8)@%= zX)og3`*D5v1g;*Lz8@Sm(Q|&}PUytlb@Q_dzKFOzKK!Z_&?GO4+JO-)iPH=fs{(`& zZ9{oNn~LUZaeN!>i9p*0N^sHye8nw4xSi!REaP@@^Jy66|)Y9_AFoLlrlkg(42 zVq2J??I(+1*BcSKsTyO7LCho{8tVQm1b>*GQ*H~Mn71Lhy`alw%;D@CU^0)5Ng{cHz@LS7QZ o8uGHYt7)tmZjae5ge5$b`e_;HIklOseoIbqeod19BU-8d00{dbSpWb4 literal 0 HcmV?d00001 diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/index.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/index.html index 18abd8c1..9ff1db6c 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/index.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/index.html @@ -4,174 +4,197 @@ TrustKit Reference - + + + + + + -
    - -
    -
    -
    +

    + + TrustKit Docs + +

    -
    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + + + + +
    -
    + + + diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/jazzy.js b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/jazzy.js index 4ff9455b..009c80d3 100755 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/jazzy.js +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/jazzy.js @@ -23,9 +23,6 @@ $(".token").click(function(event) { } var link = $(this); var animationDuration = 300; - var tokenOffset = "15px"; - var original = link.css('marginLeft') == tokenOffset; - link.animate({'margin-left':original ? "0px" : tokenOffset}, animationDuration); $content = link.parent().parent().next(); $content.slideToggle(animationDuration); @@ -38,3 +35,9 @@ $(".token").click(function(event) { } event.preventDefault(); }); + +// Dumb down quotes within code blocks that delimit strings instead of quotations +// https://github.com/realm/jazzy/issues/714 +$("code q").replaceWith(function () { + return ["\"", $(this).contents(), "\""]; +}); diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/jazzy.search.js b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/jazzy.search.js new file mode 100644 index 00000000..54be83cf --- /dev/null +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/jazzy.search.js @@ -0,0 +1,62 @@ +$(function(){ + var searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + }); + + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
    '; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
    '; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + $.each(searchData, function (url, doc) { + searchIndex.add({url: url, name: doc.name}); + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3 + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + var results = searchIndex.search(query).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/lunr.min.js b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/lunr.min.js new file mode 100755 index 00000000..22776bb8 --- /dev/null +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/js/lunr.min.js @@ -0,0 +1,6 @@ +/** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.7.2 + * Copyright (C) 2016 Oliver Nightingale + * @license MIT + */ +!function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.7.2",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.utils.asString=function(t){return void 0===t||null===t?"":t.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(e){if(!arguments.length||null==e||void 0==e)return[];if(Array.isArray(e))return e.map(function(e){return t.utils.asString(e).toLowerCase()});var n=t.tokenizer.seperator||t.tokenizer.separator;return e.toString().trim().toLowerCase().split(n)},t.tokenizer.seperator=!1,t.tokenizer.separator=/[\s\-]+/,t.tokenizer.load=function(t){var e=this.registeredFunctions[t];if(!e)throw new Error("Cannot load un-registered function: "+t);return e},t.tokenizer.label="default",t.tokenizer.registeredFunctions={"default":t.tokenizer},t.tokenizer.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing tokenizer: "+n),e.label=n,this.registeredFunctions[n]=e},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,r=0;n>r;r++){for(var o=t[r],s=0;i>s&&(o=this._stack[s](o,r,t),void 0!==o&&""!==o);s++);void 0!==o&&""!==o&&e.push(o)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(o===t)return r;t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r]}return o===t?r:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,r=e+Math.floor(i/2),o=this.elements[r];i>1;)t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r];return o>t?r:t>o?r+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,r=0,o=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>o-1||r>s-1)break;a[i]!==h[r]?a[i]h[r]&&r++:(n.add(a[i]),i++,r++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone();for(var r=0,o=n.toArray();rp;p++)c[p]===a&&d++;h+=d/f*l.boost}}this.tokenStore.add(a,{ref:o,tf:h})}n&&this.eventEmitter.emit("add",e,this)},t.Index.prototype.remove=function(t,e){var n=t[this._ref],e=void 0===e?!0:e;if(this.documentStore.has(n)){var i=this.documentStore.get(n);this.documentStore.remove(n),i.forEach(function(t){this.tokenStore.remove(t,n)},this),e&&this.eventEmitter.emit("remove",t,this)}},t.Index.prototype.update=function(t,e){var e=void 0===e?!0:e;this.remove(t,!1),this.add(t,!1),e&&this.eventEmitter.emit("update",t,this)},t.Index.prototype.idf=function(t){var e="@"+t;if(Object.prototype.hasOwnProperty.call(this._idfCache,e))return this._idfCache[e];var n=this.tokenStore.count(t),i=1;return n>0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(this.tokenizerFn(e)),i=new t.Vector,r=[],o=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*o,h=this,u=this.tokenStore.expand(e).reduce(function(n,r){var o=h.corpusTokens.indexOf(r),s=h.idf(r),u=1,l=new t.SortedSet;if(r!==e){var c=Math.max(3,r.length-e.length);u=1/Math.log(c)}o>-1&&i.insert(o,a*s*u);for(var f=h.tokenStore.get(r),d=Object.keys(f),p=d.length,v=0;p>v;v++)l.add(f[d[v]].ref);return n.union(l)},new t.SortedSet);r.push(u)},this);var a=r.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,r=new t.Vector,o=0;i>o;o++){var s=n.elements[o],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);r.insert(this.corpusTokens.indexOf(s),a*h)}return r},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,tokenizer:this.tokenizerFn.label,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",r=n+"[^aeiouy]*",o=i+"[aeiou]*",s="^("+r+")?"+o+r,a="^("+r+")?"+o+r+"("+o+")?$",h="^("+r+")?"+o+r+o+r,u="^("+r+")?"+i,l=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(u),p=/^(.+?)(ss|i)es$/,v=/^(.+?)([^s])s$/,g=/^(.+?)eed$/,m=/^(.+?)(ed|ing)$/,y=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),k=new RegExp("^"+r+i+"[^aeiouwxy]$"),x=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,F=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,_=/^(.+?)(s|t)(ion)$/,z=/^(.+?)e$/,O=/ll$/,P=new RegExp("^"+r+i+"[^aeiouwxy]$"),T=function(n){var i,r,o,s,a,h,u;if(n.length<3)return n;if(o=n.substr(0,1),"y"==o&&(n=o.toUpperCase()+n.substr(1)),s=p,a=v,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=g,a=m,s.test(n)){var T=s.exec(n);s=l,s.test(T[1])&&(s=y,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,u=k,a.test(n)?n+="e":h.test(n)?(s=y,n=n.replace(s,"")):u.test(n)&&(n+="e"))}if(s=x,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+t[r])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+e[r])}if(s=F,a=_,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=z,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=P,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=O,a=c,s.test(n)&&a.test(n)&&(s=y,n=n.replace(s,"")),"y"==o&&(n=o.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.generateStopWordFilter=function(t){var e=t.reduce(function(t,e){return t[e]=e,t},{});return function(t){return t&&e[t]!==t?t:void 0}},t.stopWordFilter=t.generateStopWordFilter(["a","able","about","across","after","all","almost","also","am","among","an","and","any","are","as","at","be","because","been","but","by","can","cannot","could","dear","did","do","does","either","else","ever","every","for","from","get","got","had","has","have","he","her","hers","him","his","how","however","i","if","in","into","is","it","its","just","least","let","like","likely","may","me","might","most","must","my","neither","no","nor","not","of","off","often","on","only","or","other","our","own","rather","said","say","says","she","should","since","so","some","than","that","the","their","them","then","there","these","they","this","tis","to","too","twas","us","wants","was","we","were","what","when","where","which","while","who","whom","why","will","with","would","yet","you","your"]),t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){return t.replace(/^\W+/,"").replace(/\W+$/,"")},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t.charAt(0),r=t.slice(1);return i in n||(n[i]={docs:{}}),0===r.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(r,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n', + menu: '
    ' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e; + $e = $.Event(namespace + type); + (args = args || []).unshift($e); + this.$el.trigger.apply(this.$el, args); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function getRegex(patterns, caseSensitive, wordsOnly) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + escapedPatterns.push(_.escapeRegExChars(patterns[i])); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
    "); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = o.name || nameGenerator(); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", this.name, suggestions, false); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", this.name, suggestions, true); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + rendered += suggestions.length; + that._append(query, suggestions.slice(0, that.limit - rendered)); + that.async && that.trigger("asyncReceived", query); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
    "); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion || suggestionTemplate + }; + function suggestionTemplate(context) { + return $("
    ").text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
    ").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, isDatasetEmpty); + function isDatasetEmpty(dataset) { + return dataset.isEmpty(); + } + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
    "); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
    "); + $menu = this.menu.$node || $("
    "); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) { + this._updateHint(); + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, payload, cancelMove; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + payload = data ? data.obj : null; + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", payload)) { + this.menu.setCursor($candidate); + if (data) { + this.input.setInputValue(data.val); + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", payload); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(newVal); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required").attr({ + autocomplete: "off", + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + autocomplete: "off", + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/search.json b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/search.json new file mode 100644 index 00000000..acc0fc86 --- /dev/null +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/search.json @@ -0,0 +1 @@ +{"Other Constants.html#/c:@TrustKitVersion":{"name":"TrustKitVersion","abstract":"

    The version of TrustKit, such as 1.4.0.

    "},"Public Key Algorithm Keys.html#/c:TSKTrustKitConfig.h@T@TSKSupportedAlgorithm":{"name":"TSKSupportedAlgorithm","abstract":"

    A public key algorithm supported by TrustKit for generating the SSL pin for a certificate.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmRsa2048":{"name":"kTSKAlgorithmRsa2048","abstract":"

    RSA 2048.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmRsa4096":{"name":"kTSKAlgorithmRsa4096","abstract":"

    RSA 4096.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmEcDsaSecp256r1":{"name":"kTSKAlgorithmEcDsaSecp256r1","abstract":"

    ECDSA with secp256r1 curve.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmEcDsaSecp384r1":{"name":"kTSKAlgorithmEcDsaSecp384r1","abstract":"

    ECDSA with secp384r1 curve.

    "},"Domain Configuration Keys.html#/c:TSKTrustKitConfig.h@T@TSKDomainConfigurationKey":{"name":"TSKDomainConfigurationKey","abstract":"

    A domain-specific configuration key (to defined for a domain under the kTSKPinnedDomains"},"Domain Configuration Keys.html#/c:@kTSKPublicKeyHashes":{"name":"kTSKPublicKeyHashes","abstract":"

    An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate’s"},"Domain Configuration Keys.html#/c:@kTSKPublicKeyAlgorithms":{"name":"kTSKPublicKeyAlgorithms","abstract":"

    An array of TSKSupportedAlgorithm constants to specify the public key algorithms for the"},"Domain Configuration Keys.html#/c:@kTSKEnforcePinning":{"name":"kTSKEnforcePinning","abstract":"

    A boolean. If set to NO, TrustKit will not block SSL connections that caused a pin or"},"Domain Configuration Keys.html#/c:@kTSKIncludeSubdomains":{"name":"kTSKIncludeSubdomains","abstract":"

    A boolean. If set to YES, also pin all the subdomains of the specified domain; default"},"Domain Configuration Keys.html#/c:@kTSKExcludeSubdomainFromParentPolicy":{"name":"kTSKExcludeSubdomainFromParentPolicy","abstract":"

    A boolean. If set to YES, TrustKit will not pin this specific domain if kTSKIncludeSubdomains"},"Domain Configuration Keys.html#/c:@kTSKReportUris":{"name":"kTSKReportUris","abstract":"

    An array of URLs to which pin validation failures should be reported.

    "},"Domain Configuration Keys.html#/c:@kTSKDisableDefaultReportUri":{"name":"kTSKDisableDefaultReportUri","abstract":"

    A boolean. If set to YES, the default report URL for sending pin failure reports will"},"Domain Configuration Keys.html#/c:@kTSKExpirationDate":{"name":"kTSKExpirationDate","abstract":"

    A string containing the date, in yyyy-MM-dd format, on which the domain’s configured SSL"},"Domain Configuration Keys.html#/c:@kTSKAdditionalTrustAnchors":{"name":"kTSKAdditionalTrustAnchors","abstract":"

    An array of strings representing additional trust anchors usable for validating"},"Global Configuration Keys.html#/c:TSKTrustKitConfig.h@T@TSKGlobalConfigurationKey":{"name":"TSKGlobalConfigurationKey","abstract":"

    A global, App-wide configuration key that can be set in the pinning policy.

    "},"Global Configuration Keys.html#/c:@kTSKSwizzleNetworkDelegates":{"name":"kTSKSwizzleNetworkDelegates","abstract":"

    A boolean. If set to YES, TrustKit will perform method swizzling on the App’s"},"Global Configuration Keys.html#/c:@kTSKPinnedDomains":{"name":"kTSKPinnedDomains","abstract":"

    A dictionary with domains (such as www.domain.com) as keys and dictionaries as values.

    "},"Global Configuration Keys.html#/c:@kTSKIgnorePinningForUserDefinedTrustAnchors":{"name":"kTSKIgnorePinningForUserDefinedTrustAnchors","abstract":"

    A boolean. If set to YES, pinning validation will be skipped if the server’s certificate"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationSuccess":{"name":"TSKTrustEvaluationSuccess","abstract":"

    The server trust was succesfully evaluated and contained at least one of the configured pins.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedNoMatchingPin":{"name":"TSKTrustEvaluationFailedNoMatchingPin","abstract":"

    The server trust was succesfully evaluated but did not contain any of the configured pins.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedInvalidCertificateChain":{"name":"TSKTrustEvaluationFailedInvalidCertificateChain","abstract":"

    The server trust’s evaluation failed: the server’s certificate chain is not trusted.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationErrorInvalidParameters":{"name":"TSKTrustEvaluationErrorInvalidParameters","abstract":"

    The server trust could not be evaluated due to invalid parameters.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedUserDefinedTrustAnchor":{"name":"TSKTrustEvaluationFailedUserDefinedTrustAnchor","abstract":"

    The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to the macOS trust store). Only available on macOS.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationErrorCouldNotGenerateSpkiHash":{"name":"TSKTrustEvaluationErrorCouldNotGenerateSpkiHash","abstract":"

    The server trust could not be evaluated due to an error when trying to generate the certificate’s subject public key info hash. On iOS 9 or below, this could be caused by a Keychain failure when trying to extract the certificate’s public key bytes.

    ","parent_name":"TSKTrustEvaluationResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)serverHostname":{"name":"serverHostname","abstract":"

    The hostname of the server SSL pinning validation was performed against.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)serverTrust":{"name":"serverTrust","abstract":"

    The original SecTrustRef that validation was performed against.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)evaluationResult":{"name":"evaluationResult","abstract":"

    The result of validating the server’s certificate chain against the set of SSL pins configured for","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)finalTrustDecision":{"name":"finalTrustDecision","abstract":"

    The trust decision returned for this connection, which describes whether the connection should be blocked","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)validationDuration":{"name":"validationDuration","abstract":"

    The time it took for the SSL pinning validation to be performed.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)certificateChain":{"name":"certificateChain","abstract":"

    The certificate chain extracted from the serverTrust as PEM-formatted certificates. This is the","parent_name":"TSKPinningValidatorResult"},"Setting up a Validation Callback.html#/c:TSKPinningValidatorCallback.h@T@TSKPinningValidatorCallback":{"name":"TSKPinningValidatorCallback","abstract":"

    A block that can be set in a TrustKit instance to be invoked for every request that is going through"},"Classes/TSKPinningValidatorResult.html":{"name":"TSKPinningValidatorResult","abstract":"

    A TSKPinningValidatorResult instance contains all the details regarding a pinning validation"},"Setting up a Validation Callback.html#/c:TSKPinningValidatorCallback.h@T@TKSDomainPinningPolicy":{"name":"TKSDomainPinningPolicy","abstract":"

    The pinning policy set for a specific hostname.

    "},"Enums/TSKTrustEvaluationResult.html":{"name":"TSKTrustEvaluationResult","abstract":"

    Possible return values when verifying a server’s identity against a set of pins.

    "},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionShouldAllowConnection":{"name":"TSKTrustDecisionShouldAllowConnection","abstract":"

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be allowed.","parent_name":"TSKTrustDecision"},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionShouldBlockConnection":{"name":"TSKTrustDecisionShouldBlockConnection","abstract":"

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be blocked.","parent_name":"TSKTrustDecision"},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionDomainNotPinned":{"name":"TSKTrustDecisionDomainNotPinned","abstract":"

    No pinning policy was configured for this domain and TrustKit did not validate the server’s identity.","parent_name":"TSKTrustDecision"},"Classes/TSKPinningValidator.html#/c:objc(cs)TSKPinningValidator(im)handleChallenge:completionHandler:":{"name":"-handleChallenge:completionHandler:","abstract":"

    Helper method for handling authentication challenges received within a NSURLSessionDelegate, NSURLSessionTaskDelegate or WKNavigationDelegate.

    ","parent_name":"TSKPinningValidator"},"Classes/TSKPinningValidator.html#/c:objc(cs)TSKPinningValidator(im)evaluateTrust:forHostname:":{"name":"-evaluateTrust:forHostname:","abstract":"

    Evaluate the supplied server trust against the SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent.

    ","parent_name":"TSKPinningValidator"},"Classes/TSKPinningValidator.html":{"name":"TSKPinningValidator","abstract":"

    A TSKPinningValidator instance can be used to verify a server’s identity against an SSL pinning policy.

    "},"Enums/TSKTrustDecision.html":{"name":"TSKTrustDecision","abstract":"

    Possible return values when verifying a server’s identity against an SSL pinning policy.

    "},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)initializeWithConfiguration:":{"name":"+initializeWithConfiguration:","abstract":"

    Initialize the global TrustKit singleton with the supplied pinning policy.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)sharedInstance":{"name":"+sharedInstance","abstract":"

    Retrieve the global TrustKit singleton instance. Raises an exception if +initializeWithConfiguration:","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidator":{"name":"pinningValidator","abstract":"

    Retrieve the validator instance conforming to the pinning policy of this TrustKit instance.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidatorCallback":{"name":"pinningValidatorCallback","abstract":"

    Register a block to be invoked for every request that is going through TrustKit’s pinning","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidatorCallbackQueue":{"name":"pinningValidatorCallbackQueue","abstract":"

    Queue on which to invoke the pinningValidatorCallback; default value is the main queue.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(im)initWithConfiguration:":{"name":"-initWithConfiguration:","abstract":"

    Initialize a local TrustKit instance with the supplied SSL pinning policy configuration.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)setLoggerBlock:":{"name":"+setLoggerBlock:","abstract":"

    Set the global logger.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html":{"name":"TrustKit","abstract":"

    TrustKit is the main class for configuring an SSL pinning policy within an App.

    "},"Initalizing TrustKit.html":{"name":"Initalizing TrustKit"},"Implementing Pinning Validation.html":{"name":"Implementing Pinning Validation"},"Setting up a Validation Callback.html":{"name":"Setting up a Validation Callback"},"Global Configuration Keys.html":{"name":"Global Configuration Keys"},"Domain Configuration Keys.html":{"name":"Domain Configuration Keys"},"Public Key Algorithm Keys.html":{"name":"Public Key Algorithm Keys"},"Other Constants.html":{"name":"Other Constants","abstract":"

    The following constants are available globally.

    "}} \ No newline at end of file diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/undocumented.json b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/undocumented.json deleted file mode 100644 index 4a696f51..00000000 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/undocumented.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "warnings": [ - - ], - "source_directory": "/Users/nabla/Documents/ios/TrustKit" -} \ No newline at end of file diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/docSet.dsidx b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/docSet.dsidx index b58428fae3bfab874080f7bd459050382006074d..d6c85207a54158f54ace7d78966b5322e4023f7f 100644 GIT binary patch literal 28672 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 MBa-^OWtq?a13_t^QUCw| literal 12288 zcmeI0%X8bt9mf}>7JN&7F*0MrhNCzZE!(nUJ9gaKmQ2!;DqEICO7dfR0KpXr3j{a- zX!(@`dTM)XZtb))?Ik%Te?vRd^wJ)h9@}G+P9J|llkS3Cl2`(yMKfs*p1p1JOj@Dx0b+H z0~6=K#82_p=riiTr~{)8j5;vtz^DVG4vac5>cFT2qYjKZFzUc}z5@>fp~=(~XG29R zp{J52S9HTfk|9@?$%$ZQJ-1NEi5siS_cn6k@@h7BUu=vL@2-j+BgJV=s-iQZ^{j@! zYm!w-TU=;z3Tw|=Qc*=6V>=E5%>adko40eFdece?hb!b3bL-;T`tqHH^#|fg?!g(6 zgb@q5`-MiV<{=5_9Ht4IpXRDy;+GRw#{WA0B=PS=G5+`XCviPK8T&l;Fm@pN+vv;a z`N;1hKaQLa|1JbQ*%=Af3Tp zmZVu`>%h5s_!I<3z{!j%nITxwpQr6-+%*G@ukVe(EP zzp|!inxd6AB~>X&mTtIxy01I#_Mx|VkWblLGZjfIsVGyCR26AubXC@?H5HM1mz*oc z)jPRX~Se};6v`1Fcay8vQlsz`ZgG|a^oIy_{wJur6Df%k@x7U(x zT4a$7FCG67_ut~d*^~`uOk~~G%VlKTRCRfW4c)v+PD5SgH!qNy8HK_L9;8z?HzVuX zwoPTr}pWF13ulkJsj858Zb!y*GfaEikK-=hgq>z8PA_)^kS z>D}`euTTYQdnl3N*+US_30owm+|!!UctU3JtAP4b%jmcVZys!QEUTDgr-onT5}w2B zsHPj%hM`ayY8g2nukRm#AT8V_*|n5fZ+9Q5_Q1-12sVTljoM_jUPAeLv7}e=ljD}5 zSJxy1Y1W#qDssPvW$tNj_aE$o;Ii;T5@}g;5ABz3*ZAy2`;KV%RV<_Co`cp)Ez`oB zYw+qg1m6=RlF?edh)cw8gw}NLmy?Ibtpwfy2@hO)OOjc^8>;56i&`eFmR4i*ZoK^> zTP|z5L94r^8yhAvoDCo%YcJb$jDRb=hz#!eQgsEij)KQ$k(>k$ZMgnM+$xrq$&rlIvx+fg?&@?se8ppzMV zubm6@RYNw&&L(-5z2a;IUW9nClChQk?f`15n8CV-T7Sy;uai4O(n0SBU zSL3^Z{o{X&e;WTH@t=gu{UcuEw9wa~kHfzSZ$;*!A4EP2|10(=Iv4v(^mnm?k)Om5 z!ykoiz_0nwVT|7y`_K))qhA2bK#cz!JFnT_&NJ(D5@P)4oad4A<(y3m5aU0GCxRFM zPp2Tpf3AttP2>2TnuZwvIl6G%EZ@n6?_7r%|2aIZ2jc&ec9JLf&waP}|7{!c1pm4J zFaJ+H;0gY7!{`6EHh6;nL+1YzCQtC6`!@Mse99C2=g9WuTjl@bPk4g=Okn(K`DXe5 z*h`+^KNGeOx6}1cZO4Ym-M2nE!Vbjv&%~yis?N9SUjEDCl?|_JGBKd{yV*EZD5AM|A%|&z6SqKE<=p} z?tBO1|2Mpq2H^i>4r2Uwa&jX*y(QE#x5hpAP`hJ3Bu{0iW#oLk4qk&8|DEjGSWhLW zWu)2z4=51hf3r3>l@FQ!_pd;V|4yVXo~LP2%cR>ieqYBOaUnewpq3GG^Z&$Mi1FXa zs72tdMbZybJv_!2A;y2V-XQ#+=y;L6BDvFgsb%VZMU{94V*GCu(8=^vm0CuRT X?|ahsmIxG`7!e%98{YR6!&~Bir)eNX diff --git a/docs/documentation/img/spinner.gif b/docs/documentation/img/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..e3038d0a42c55b1a07caecb7c7a6cbac982ec09d GIT binary patch literal 1849 zcmb8wZBSF$83*voz31lM+?V7Kkqwb`LI|3K#DupH#kDs91c6du6?SMB!bsTr6|ge_{#vAVj^!DyNA-l zJ&$jDFNv;BTZXX@Qk-7+S5ErF>mkOcZ@lQv>F1VyCEMe2Ud@f<|L%#&QJi${E`2lR zqKFaW2Y$aTRxUY&ae$IHsN;Z;rdZ%CjYLTv!tMi234j-ON=CnvK-1QU|MG$YErn{gHZ@0Q6&?xSyply?S$EVNXH;gp?S5kV2-)$ga^gw`(f4Mm_Y(`RbgRkQTHF2@zL}dCiLk$RoZIc{xZL z_J*d5)Kb;#oKCFyfL*NGSs?y;e(QKvPJe1#G)h5*6E(?L9$nt?UaQJfP^$GDL0PU; z?r}C|);JQ4HES3w5VMlY7x6xfJAzDKlHE~>x;D`Fa=WygYot{pfFehH69o9pK|72W zwC6?t^AnATIJa=kewn=ep?Nk(aZ*pZo}51`S=^)jPRb`~l^VE}08>P3OJtQlXx1K8 z8Q}_u=F*fS;=k=?(fIv#+%811NTx8^}rHwvH%LbYmpFl9p1A{Idh@2x$ zuVp7)VD9}Uc(*(C**!QOdS(6B)$5^Tq5p3q*7un&_Z-NKEiEYg$D{Uq&sa>wj|za5 zJ6M~p)z+E6*X${8j6Ci+sqZ}zxeCAo0gZmZuhl+)Q%1U$Br_`NXcA-3yBdYMha+{o z{?q0Q(kaR2n`M29{!pwpgX6+CPQEgIO%x*0#!TC=c-ZPSkLO>OcmQUao5%-3w)U`F zRz?uGCEKQDh!TQPDmyd;iDX$TkMIe)%61q51Y2b-ie4r00!csilXgKL$txqj|6D(# z@(#!nQ}3R1JGeB3B5Tuqdvyg@*!-bq`9`pmasNGvy9^*+cd1Y*g>HK#rl7i79QQAG zl4SL_wW@WY1d+F?j0gFInGhsRrqvV3SKl{oqW+;9!fu|u@J)h4WM!0Cu02l@p60b#5M9c{dKh=_eRw~yl zWT0gw8RePzf%i8X&twiB|LF0bI@CYE{x1PI;Ylr4RJzU#Zc0j!c07g&q7=_eSd(sH z9VKChd?}^52IKcMqolAWiQH;HSp1Ploa$t zQhg|2sK;%Eb!By`)j9G1w?>`Wt6IK3gB}~uoue(MlRiIoZ#d{pgJZ8b{^{HO8)@%= zX)og3`*D5v1g;*Lz8@Sm(Q|&}PUytlb@Q_dzKFOzKK!Z_&?GO4+JO-)iPH=fs{(`& zZ9{oNn~LUZaeN!>i9p*0N^sHye8nw4xSi!REaP@@^Jy66|)Y9_AFoLlrlkg(42 zVq2J??I(+1*BcSKsTyO7LCho{8tVQm1b>*GQ*H~Mn71Lhy`alw%;D@CU^0)5Ng{cHz@LS7QZ o8uGHYt7)tmZjae5ge5$b`e_;HIklOseoIbqeod19BU-8d00{dbSpWb4 literal 0 HcmV?d00001 diff --git a/docs/documentation/index.html b/docs/documentation/index.html index 18abd8c1..9ff1db6c 100644 --- a/docs/documentation/index.html +++ b/docs/documentation/index.html @@ -4,174 +4,197 @@ TrustKit Reference - + + + + + + -
    - -
    -
    -
    +

    + + TrustKit Docs + +

    -
    + +

    +

    + +
    +

    + +

    + + + View on GitHub + +

    + + + + +
    -
    + +
    +
    diff --git a/docs/documentation/js/jazzy.js b/docs/documentation/js/jazzy.js index 4ff9455b..009c80d3 100755 --- a/docs/documentation/js/jazzy.js +++ b/docs/documentation/js/jazzy.js @@ -23,9 +23,6 @@ $(".token").click(function(event) { } var link = $(this); var animationDuration = 300; - var tokenOffset = "15px"; - var original = link.css('marginLeft') == tokenOffset; - link.animate({'margin-left':original ? "0px" : tokenOffset}, animationDuration); $content = link.parent().parent().next(); $content.slideToggle(animationDuration); @@ -38,3 +35,9 @@ $(".token").click(function(event) { } event.preventDefault(); }); + +// Dumb down quotes within code blocks that delimit strings instead of quotations +// https://github.com/realm/jazzy/issues/714 +$("code q").replaceWith(function () { + return ["\"", $(this).contents(), "\""]; +}); diff --git a/docs/documentation/js/jazzy.search.js b/docs/documentation/js/jazzy.search.js new file mode 100644 index 00000000..54be83cf --- /dev/null +++ b/docs/documentation/js/jazzy.search.js @@ -0,0 +1,62 @@ +$(function(){ + var searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + }); + + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
    '; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
    '; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + $.each(searchData, function (url, doc) { + searchIndex.add({url: url, name: doc.name}); + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3 + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + var results = searchIndex.search(query).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/documentation/js/lunr.min.js b/docs/documentation/js/lunr.min.js new file mode 100755 index 00000000..22776bb8 --- /dev/null +++ b/docs/documentation/js/lunr.min.js @@ -0,0 +1,6 @@ +/** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.7.2 + * Copyright (C) 2016 Oliver Nightingale + * @license MIT + */ +!function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.7.2",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.utils.asString=function(t){return void 0===t||null===t?"":t.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(e){if(!arguments.length||null==e||void 0==e)return[];if(Array.isArray(e))return e.map(function(e){return t.utils.asString(e).toLowerCase()});var n=t.tokenizer.seperator||t.tokenizer.separator;return e.toString().trim().toLowerCase().split(n)},t.tokenizer.seperator=!1,t.tokenizer.separator=/[\s\-]+/,t.tokenizer.load=function(t){var e=this.registeredFunctions[t];if(!e)throw new Error("Cannot load un-registered function: "+t);return e},t.tokenizer.label="default",t.tokenizer.registeredFunctions={"default":t.tokenizer},t.tokenizer.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing tokenizer: "+n),e.label=n,this.registeredFunctions[n]=e},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,r=0;n>r;r++){for(var o=t[r],s=0;i>s&&(o=this._stack[s](o,r,t),void 0!==o&&""!==o);s++);void 0!==o&&""!==o&&e.push(o)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(o===t)return r;t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r]}return o===t?r:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,r=e+Math.floor(i/2),o=this.elements[r];i>1;)t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r];return o>t?r:t>o?r+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,r=0,o=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>o-1||r>s-1)break;a[i]!==h[r]?a[i]h[r]&&r++:(n.add(a[i]),i++,r++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone();for(var r=0,o=n.toArray();rp;p++)c[p]===a&&d++;h+=d/f*l.boost}}this.tokenStore.add(a,{ref:o,tf:h})}n&&this.eventEmitter.emit("add",e,this)},t.Index.prototype.remove=function(t,e){var n=t[this._ref],e=void 0===e?!0:e;if(this.documentStore.has(n)){var i=this.documentStore.get(n);this.documentStore.remove(n),i.forEach(function(t){this.tokenStore.remove(t,n)},this),e&&this.eventEmitter.emit("remove",t,this)}},t.Index.prototype.update=function(t,e){var e=void 0===e?!0:e;this.remove(t,!1),this.add(t,!1),e&&this.eventEmitter.emit("update",t,this)},t.Index.prototype.idf=function(t){var e="@"+t;if(Object.prototype.hasOwnProperty.call(this._idfCache,e))return this._idfCache[e];var n=this.tokenStore.count(t),i=1;return n>0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(this.tokenizerFn(e)),i=new t.Vector,r=[],o=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*o,h=this,u=this.tokenStore.expand(e).reduce(function(n,r){var o=h.corpusTokens.indexOf(r),s=h.idf(r),u=1,l=new t.SortedSet;if(r!==e){var c=Math.max(3,r.length-e.length);u=1/Math.log(c)}o>-1&&i.insert(o,a*s*u);for(var f=h.tokenStore.get(r),d=Object.keys(f),p=d.length,v=0;p>v;v++)l.add(f[d[v]].ref);return n.union(l)},new t.SortedSet);r.push(u)},this);var a=r.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,r=new t.Vector,o=0;i>o;o++){var s=n.elements[o],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);r.insert(this.corpusTokens.indexOf(s),a*h)}return r},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,tokenizer:this.tokenizerFn.label,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",r=n+"[^aeiouy]*",o=i+"[aeiou]*",s="^("+r+")?"+o+r,a="^("+r+")?"+o+r+"("+o+")?$",h="^("+r+")?"+o+r+o+r,u="^("+r+")?"+i,l=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(u),p=/^(.+?)(ss|i)es$/,v=/^(.+?)([^s])s$/,g=/^(.+?)eed$/,m=/^(.+?)(ed|ing)$/,y=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),k=new RegExp("^"+r+i+"[^aeiouwxy]$"),x=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,F=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,_=/^(.+?)(s|t)(ion)$/,z=/^(.+?)e$/,O=/ll$/,P=new RegExp("^"+r+i+"[^aeiouwxy]$"),T=function(n){var i,r,o,s,a,h,u;if(n.length<3)return n;if(o=n.substr(0,1),"y"==o&&(n=o.toUpperCase()+n.substr(1)),s=p,a=v,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=g,a=m,s.test(n)){var T=s.exec(n);s=l,s.test(T[1])&&(s=y,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,u=k,a.test(n)?n+="e":h.test(n)?(s=y,n=n.replace(s,"")):u.test(n)&&(n+="e"))}if(s=x,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+t[r])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+e[r])}if(s=F,a=_,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=z,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=P,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=O,a=c,s.test(n)&&a.test(n)&&(s=y,n=n.replace(s,"")),"y"==o&&(n=o.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.generateStopWordFilter=function(t){var e=t.reduce(function(t,e){return t[e]=e,t},{});return function(t){return t&&e[t]!==t?t:void 0}},t.stopWordFilter=t.generateStopWordFilter(["a","able","about","across","after","all","almost","also","am","among","an","and","any","are","as","at","be","because","been","but","by","can","cannot","could","dear","did","do","does","either","else","ever","every","for","from","get","got","had","has","have","he","her","hers","him","his","how","however","i","if","in","into","is","it","its","just","least","let","like","likely","may","me","might","most","must","my","neither","no","nor","not","of","off","often","on","only","or","other","our","own","rather","said","say","says","she","should","since","so","some","than","that","the","their","them","then","there","these","they","this","tis","to","too","twas","us","wants","was","we","were","what","when","where","which","while","who","whom","why","will","with","would","yet","you","your"]),t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){return t.replace(/^\W+/,"").replace(/\W+$/,"")},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t.charAt(0),r=t.slice(1);return i in n||(n[i]={docs:{}}),0===r.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(r,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n', + menu: '
    ' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e; + $e = $.Event(namespace + type); + (args = args || []).unshift($e); + this.$el.trigger.apply(this.$el, args); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function getRegex(patterns, caseSensitive, wordsOnly) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + escapedPatterns.push(_.escapeRegExChars(patterns[i])); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
    "); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = o.name || nameGenerator(); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", this.name, suggestions, false); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", this.name, suggestions, true); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + rendered += suggestions.length; + that._append(query, suggestions.slice(0, that.limit - rendered)); + that.async && that.trigger("asyncReceived", query); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
    "); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion || suggestionTemplate + }; + function suggestionTemplate(context) { + return $("
    ").text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
    ").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, isDatasetEmpty); + function isDatasetEmpty(dataset) { + return dataset.isEmpty(); + } + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
    "); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
    "); + $menu = this.menu.$node || $("
    "); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) { + this._updateHint(); + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, payload, cancelMove; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + payload = data ? data.obj : null; + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", payload)) { + this.menu.setCursor($candidate); + if (data) { + this.input.setInputValue(data.val); + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", payload); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(newVal); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required").attr({ + autocomplete: "off", + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + autocomplete: "off", + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/documentation/search.json b/docs/documentation/search.json new file mode 100644 index 00000000..acc0fc86 --- /dev/null +++ b/docs/documentation/search.json @@ -0,0 +1 @@ +{"Other Constants.html#/c:@TrustKitVersion":{"name":"TrustKitVersion","abstract":"

    The version of TrustKit, such as 1.4.0.

    "},"Public Key Algorithm Keys.html#/c:TSKTrustKitConfig.h@T@TSKSupportedAlgorithm":{"name":"TSKSupportedAlgorithm","abstract":"

    A public key algorithm supported by TrustKit for generating the SSL pin for a certificate.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmRsa2048":{"name":"kTSKAlgorithmRsa2048","abstract":"

    RSA 2048.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmRsa4096":{"name":"kTSKAlgorithmRsa4096","abstract":"

    RSA 4096.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmEcDsaSecp256r1":{"name":"kTSKAlgorithmEcDsaSecp256r1","abstract":"

    ECDSA with secp256r1 curve.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmEcDsaSecp384r1":{"name":"kTSKAlgorithmEcDsaSecp384r1","abstract":"

    ECDSA with secp384r1 curve.

    "},"Domain Configuration Keys.html#/c:TSKTrustKitConfig.h@T@TSKDomainConfigurationKey":{"name":"TSKDomainConfigurationKey","abstract":"

    A domain-specific configuration key (to defined for a domain under the kTSKPinnedDomains"},"Domain Configuration Keys.html#/c:@kTSKPublicKeyHashes":{"name":"kTSKPublicKeyHashes","abstract":"

    An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate’s"},"Domain Configuration Keys.html#/c:@kTSKPublicKeyAlgorithms":{"name":"kTSKPublicKeyAlgorithms","abstract":"

    An array of TSKSupportedAlgorithm constants to specify the public key algorithms for the"},"Domain Configuration Keys.html#/c:@kTSKEnforcePinning":{"name":"kTSKEnforcePinning","abstract":"

    A boolean. If set to NO, TrustKit will not block SSL connections that caused a pin or"},"Domain Configuration Keys.html#/c:@kTSKIncludeSubdomains":{"name":"kTSKIncludeSubdomains","abstract":"

    A boolean. If set to YES, also pin all the subdomains of the specified domain; default"},"Domain Configuration Keys.html#/c:@kTSKExcludeSubdomainFromParentPolicy":{"name":"kTSKExcludeSubdomainFromParentPolicy","abstract":"

    A boolean. If set to YES, TrustKit will not pin this specific domain if kTSKIncludeSubdomains"},"Domain Configuration Keys.html#/c:@kTSKReportUris":{"name":"kTSKReportUris","abstract":"

    An array of URLs to which pin validation failures should be reported.

    "},"Domain Configuration Keys.html#/c:@kTSKDisableDefaultReportUri":{"name":"kTSKDisableDefaultReportUri","abstract":"

    A boolean. If set to YES, the default report URL for sending pin failure reports will"},"Domain Configuration Keys.html#/c:@kTSKExpirationDate":{"name":"kTSKExpirationDate","abstract":"

    A string containing the date, in yyyy-MM-dd format, on which the domain’s configured SSL"},"Domain Configuration Keys.html#/c:@kTSKAdditionalTrustAnchors":{"name":"kTSKAdditionalTrustAnchors","abstract":"

    An array of strings representing additional trust anchors usable for validating"},"Global Configuration Keys.html#/c:TSKTrustKitConfig.h@T@TSKGlobalConfigurationKey":{"name":"TSKGlobalConfigurationKey","abstract":"

    A global, App-wide configuration key that can be set in the pinning policy.

    "},"Global Configuration Keys.html#/c:@kTSKSwizzleNetworkDelegates":{"name":"kTSKSwizzleNetworkDelegates","abstract":"

    A boolean. If set to YES, TrustKit will perform method swizzling on the App’s"},"Global Configuration Keys.html#/c:@kTSKPinnedDomains":{"name":"kTSKPinnedDomains","abstract":"

    A dictionary with domains (such as www.domain.com) as keys and dictionaries as values.

    "},"Global Configuration Keys.html#/c:@kTSKIgnorePinningForUserDefinedTrustAnchors":{"name":"kTSKIgnorePinningForUserDefinedTrustAnchors","abstract":"

    A boolean. If set to YES, pinning validation will be skipped if the server’s certificate"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationSuccess":{"name":"TSKTrustEvaluationSuccess","abstract":"

    The server trust was succesfully evaluated and contained at least one of the configured pins.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedNoMatchingPin":{"name":"TSKTrustEvaluationFailedNoMatchingPin","abstract":"

    The server trust was succesfully evaluated but did not contain any of the configured pins.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedInvalidCertificateChain":{"name":"TSKTrustEvaluationFailedInvalidCertificateChain","abstract":"

    The server trust’s evaluation failed: the server’s certificate chain is not trusted.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationErrorInvalidParameters":{"name":"TSKTrustEvaluationErrorInvalidParameters","abstract":"

    The server trust could not be evaluated due to invalid parameters.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedUserDefinedTrustAnchor":{"name":"TSKTrustEvaluationFailedUserDefinedTrustAnchor","abstract":"

    The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to the macOS trust store). Only available on macOS.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationErrorCouldNotGenerateSpkiHash":{"name":"TSKTrustEvaluationErrorCouldNotGenerateSpkiHash","abstract":"

    The server trust could not be evaluated due to an error when trying to generate the certificate’s subject public key info hash. On iOS 9 or below, this could be caused by a Keychain failure when trying to extract the certificate’s public key bytes.

    ","parent_name":"TSKTrustEvaluationResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)serverHostname":{"name":"serverHostname","abstract":"

    The hostname of the server SSL pinning validation was performed against.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)serverTrust":{"name":"serverTrust","abstract":"

    The original SecTrustRef that validation was performed against.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)evaluationResult":{"name":"evaluationResult","abstract":"

    The result of validating the server’s certificate chain against the set of SSL pins configured for","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)finalTrustDecision":{"name":"finalTrustDecision","abstract":"

    The trust decision returned for this connection, which describes whether the connection should be blocked","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)validationDuration":{"name":"validationDuration","abstract":"

    The time it took for the SSL pinning validation to be performed.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)certificateChain":{"name":"certificateChain","abstract":"

    The certificate chain extracted from the serverTrust as PEM-formatted certificates. This is the","parent_name":"TSKPinningValidatorResult"},"Setting up a Validation Callback.html#/c:TSKPinningValidatorCallback.h@T@TSKPinningValidatorCallback":{"name":"TSKPinningValidatorCallback","abstract":"

    A block that can be set in a TrustKit instance to be invoked for every request that is going through"},"Classes/TSKPinningValidatorResult.html":{"name":"TSKPinningValidatorResult","abstract":"

    A TSKPinningValidatorResult instance contains all the details regarding a pinning validation"},"Setting up a Validation Callback.html#/c:TSKPinningValidatorCallback.h@T@TKSDomainPinningPolicy":{"name":"TKSDomainPinningPolicy","abstract":"

    The pinning policy set for a specific hostname.

    "},"Enums/TSKTrustEvaluationResult.html":{"name":"TSKTrustEvaluationResult","abstract":"

    Possible return values when verifying a server’s identity against a set of pins.

    "},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionShouldAllowConnection":{"name":"TSKTrustDecisionShouldAllowConnection","abstract":"

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be allowed.","parent_name":"TSKTrustDecision"},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionShouldBlockConnection":{"name":"TSKTrustDecisionShouldBlockConnection","abstract":"

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be blocked.","parent_name":"TSKTrustDecision"},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionDomainNotPinned":{"name":"TSKTrustDecisionDomainNotPinned","abstract":"

    No pinning policy was configured for this domain and TrustKit did not validate the server’s identity.","parent_name":"TSKTrustDecision"},"Classes/TSKPinningValidator.html#/c:objc(cs)TSKPinningValidator(im)handleChallenge:completionHandler:":{"name":"-handleChallenge:completionHandler:","abstract":"

    Helper method for handling authentication challenges received within a NSURLSessionDelegate, NSURLSessionTaskDelegate or WKNavigationDelegate.

    ","parent_name":"TSKPinningValidator"},"Classes/TSKPinningValidator.html#/c:objc(cs)TSKPinningValidator(im)evaluateTrust:forHostname:":{"name":"-evaluateTrust:forHostname:","abstract":"

    Evaluate the supplied server trust against the SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent.

    ","parent_name":"TSKPinningValidator"},"Classes/TSKPinningValidator.html":{"name":"TSKPinningValidator","abstract":"

    A TSKPinningValidator instance can be used to verify a server’s identity against an SSL pinning policy.

    "},"Enums/TSKTrustDecision.html":{"name":"TSKTrustDecision","abstract":"

    Possible return values when verifying a server’s identity against an SSL pinning policy.

    "},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)initializeWithConfiguration:":{"name":"+initializeWithConfiguration:","abstract":"

    Initialize the global TrustKit singleton with the supplied pinning policy.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)sharedInstance":{"name":"+sharedInstance","abstract":"

    Retrieve the global TrustKit singleton instance. Raises an exception if +initializeWithConfiguration:","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidator":{"name":"pinningValidator","abstract":"

    Retrieve the validator instance conforming to the pinning policy of this TrustKit instance.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidatorCallback":{"name":"pinningValidatorCallback","abstract":"

    Register a block to be invoked for every request that is going through TrustKit’s pinning","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidatorCallbackQueue":{"name":"pinningValidatorCallbackQueue","abstract":"

    Queue on which to invoke the pinningValidatorCallback; default value is the main queue.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(im)initWithConfiguration:":{"name":"-initWithConfiguration:","abstract":"

    Initialize a local TrustKit instance with the supplied SSL pinning policy configuration.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)setLoggerBlock:":{"name":"+setLoggerBlock:","abstract":"

    Set the global logger.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html":{"name":"TrustKit","abstract":"

    TrustKit is the main class for configuring an SSL pinning policy within an App.

    "},"Initalizing TrustKit.html":{"name":"Initalizing TrustKit"},"Implementing Pinning Validation.html":{"name":"Implementing Pinning Validation"},"Setting up a Validation Callback.html":{"name":"Setting up a Validation Callback"},"Global Configuration Keys.html":{"name":"Global Configuration Keys"},"Domain Configuration Keys.html":{"name":"Domain Configuration Keys"},"Public Key Algorithm Keys.html":{"name":"Public Key Algorithm Keys"},"Other Constants.html":{"name":"Other Constants","abstract":"

    The following constants are available globally.

    "}} \ No newline at end of file From 271518d7a96517f3f26e5c95dce2ee653fbb8938 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 7 Jul 2017 12:44:09 +0200 Subject: [PATCH 116/126] Fix podspec --- TrustKit.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TrustKit.podspec b/TrustKit.podspec index de6d1ab7..dc6fcb5e 100644 --- a/TrustKit.podspec +++ b/TrustKit.podspec @@ -19,8 +19,8 @@ Pod::Spec.new do |s| 'TrustKit/TSKTrustKitConfig.h', 'TrustKit/TSKPinningValidator.h', 'TrustKit/TSKPinningValidatorCallback.h', - 'TrustKit/TSKPinValidatorResult.h', - 'TrustKit/Pinning/TSKPublicKeyAlgorithm.h' + 'TrustKit/TSKPinningValidatorResult.h', + 'TrustKit/TSKTrustDecision.h', ] s.frameworks = ['Foundation', 'Security'] s.requires_arc = true From 6ad3c9147ed2f179235f847816c2cf585d5d726c Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 7 Jul 2017 12:44:36 +0200 Subject: [PATCH 117/126] Fix umbrella header --- TrustKit/Framework/TrustKit-umbrella.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/TrustKit/Framework/TrustKit-umbrella.h b/TrustKit/Framework/TrustKit-umbrella.h index 5809f4e2..0e479784 100644 --- a/TrustKit/Framework/TrustKit-umbrella.h +++ b/TrustKit/Framework/TrustKit-umbrella.h @@ -1,17 +1,21 @@ -// -// TrustKit.h -// TrustKit -// -// Created by Adam Kaplan on 6/8/17. -// Copyright © 2017 TrustKit. All rights reserved. -// +/* + + TrustKit.h + TrustKit + + Copyright 2017 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ #ifndef _TRUSTKIT_ #define _TRUSTKIT_ #import -#import #import -#import +#import +#import +#import #endif /* _TRUSTKIT_ */ From 47b64d833248d86faa2af77050d78926ff6785c0 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 7 Jul 2017 12:45:23 +0200 Subject: [PATCH 118/126] Fix demo Swift code --- TrustKitDemo/TrustKitDemoInSwift/AppDelegate.swift | 2 +- TrustKitDemo/TrustKitDemoInSwift/DTViewController.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TrustKitDemo/TrustKitDemoInSwift/AppDelegate.swift b/TrustKitDemo/TrustKitDemoInSwift/AppDelegate.swift index 8e243e68..f755fb59 100644 --- a/TrustKitDemo/TrustKitDemoInSwift/AppDelegate.swift +++ b/TrustKitDemo/TrustKitDemoInSwift/AppDelegate.swift @@ -50,7 +50,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ] ]] as [String : Any] - TrustKit.initialize(withConfiguration:trustKitConfig) + TrustKit.initialize(withConfiguration: trustKitConfig) return true } diff --git a/TrustKitDemo/TrustKitDemoInSwift/DTViewController.swift b/TrustKitDemo/TrustKitDemoInSwift/DTViewController.swift index c677db74..fe1d360b 100644 --- a/TrustKitDemo/TrustKitDemoInSwift/DTViewController.swift +++ b/TrustKitDemo/TrustKitDemoInSwift/DTViewController.swift @@ -72,7 +72,7 @@ class DTViewController: UIViewController, URLSessionDelegate { func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { // Call into TrustKit here to do pinning validation - if TSKPinningValidator.handle(challenge, completionHandler: completionHandler) == false { + if TrustKit.sharedInstance().pinningValidator.handle(challenge, completionHandler: completionHandler) == false { // TrustKit did not handle this challenge: perhaps it was not for server trust // or the domain was not pinned. Fall back to the default behavior completionHandler(.performDefaultHandling, nil) From 6c741dcf92b357ac55b20c34eade099d5a68e7d1 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Fri, 7 Jul 2017 12:45:51 +0200 Subject: [PATCH 119/126] Enable CocoaPods for the demo App --- TrustKitDemo/Podfile | 21 + TrustKitDemo/Podfile.lock | 16 + .../Pods/Headers/Private/TrustKit/RSSwizzle.h | 1 + .../Private/TrustKit/TSKBackgroundReporter.h | 1 + .../Pods/Headers/Private/TrustKit/TSKLog.h | 1 + .../TSKNSURLConnectionDelegateProxy.h | 1 + .../TrustKit/TSKNSURLSessionDelegateProxy.h | 1 + .../Private/TrustKit/TSKPinFailureReport.h | 1 + .../Private/TrustKit/TSKPinningValidator.h | 1 + .../TrustKit/TSKPinningValidatorCallback.h | 1 + .../TrustKit/TSKPinningValidatorResult.h | 1 + .../TrustKit/TSKPinningValidator_Private.h | 1 + .../Private/TrustKit/TSKPublicKeyAlgorithm.h | 1 + .../Private/TrustKit/TSKReportsRateLimiter.h | 1 + .../Private/TrustKit/TSKSPKIHashCache.h | 1 + .../Private/TrustKit/TSKTrustDecision.h | 1 + .../Private/TrustKit/TSKTrustKitConfig.h | 1 + .../Private/TrustKit/TrustKit-umbrella.h | 1 + .../Pods/Headers/Private/TrustKit/TrustKit.h | 1 + .../Pods/Headers/Private/TrustKit/assert.h | 1 + .../Private/TrustKit/configuration_utils.h | 1 + .../Private/TrustKit/domain_registry.h | 1 + .../Private/TrustKit/parse_configuration.h | 1 + .../Private/TrustKit/registry_tables.h | 1 + .../Headers/Private/TrustKit/registry_types.h | 1 + .../Private/TrustKit/reporting_utils.h | 1 + .../Private/TrustKit/ssl_pin_verifier.h | 1 + .../Headers/Private/TrustKit/string_util.h | 1 + .../Pods/Headers/Private/TrustKit/trie_node.h | 1 + .../Headers/Private/TrustKit/trie_search.h | 1 + .../Private/TrustKit/vendor_identifier.h | 1 + .../Public/TrustKit/TSKPinningValidator.h | 1 + .../TrustKit/TSKPinningValidatorCallback.h | 1 + .../TrustKit/TSKPinningValidatorResult.h | 1 + .../Public/TrustKit/TSKTrustDecision.h | 1 + .../Public/TrustKit/TSKTrustKitConfig.h | 1 + .../Pods/Headers/Public/TrustKit/TrustKit.h | 1 + .../Pods/Local Podspecs/TrustKit.podspec.json | 43 + TrustKitDemo/Pods/Manifest.lock | 16 + .../Pods/Pods.xcodeproj/project.pbxproj | 1165 +++++++++++++++++ ...ods-TrustKitDemo-acknowledgements.markdown | 28 + .../Pods-TrustKitDemo-acknowledgements.plist | 60 + .../Pods-TrustKitDemo-dummy.m | 5 + .../Pods-TrustKitDemo-frameworks.sh | 92 ++ .../Pods-TrustKitDemo-resources.sh | 102 ++ .../Pods-TrustKitDemo.debug.xcconfig | 9 + .../Pods-TrustKitDemo.release.xcconfig | 9 + .../Pods-TrustKitDemoInSwift/Info.plist | 26 + ...stKitDemoInSwift-acknowledgements.markdown | 28 + ...TrustKitDemoInSwift-acknowledgements.plist | 60 + .../Pods-TrustKitDemoInSwift-dummy.m | 5 + .../Pods-TrustKitDemoInSwift-frameworks.sh | 99 ++ .../Pods-TrustKitDemoInSwift-resources.sh | 102 ++ .../Pods-TrustKitDemoInSwift-umbrella.h | 16 + .../Pods-TrustKitDemoInSwift.debug.xcconfig | 9 + .../Pods-TrustKitDemoInSwift.modulemap | 6 + .../Pods-TrustKitDemoInSwift.release.xcconfig | 9 + .../TrustKit-framework/Info.plist | 26 + .../TrustKit-framework-dummy.m | 5 + .../TrustKit-framework-prefix.pch | 12 + .../TrustKit-framework-umbrella.h | 22 + .../TrustKit-framework.modulemap | 6 + .../TrustKit-framework.xcconfig | 10 + .../TrustKit-library/TrustKit-library-dummy.m | 5 + .../TrustKit-library-prefix.pch | 12 + .../TrustKit-library.xcconfig | 10 + .../TrustKitDemo.xcodeproj/project.pbxproj | 266 ++-- .../contents.xcworkspacedata | 10 + 68 files changed, 2209 insertions(+), 136 deletions(-) create mode 100644 TrustKitDemo/Podfile create mode 100644 TrustKitDemo/Podfile.lock create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/RSSwizzle.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKBackgroundReporter.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKLog.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKNSURLConnectionDelegateProxy.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKNSURLSessionDelegateProxy.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinFailureReport.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidator.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidatorCallback.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidatorResult.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidator_Private.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPublicKeyAlgorithm.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKReportsRateLimiter.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKSPKIHashCache.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKTrustDecision.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TSKTrustKitConfig.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TrustKit-umbrella.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/TrustKit.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/assert.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/configuration_utils.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/domain_registry.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/parse_configuration.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/registry_tables.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/registry_types.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/reporting_utils.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/ssl_pin_verifier.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/string_util.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/trie_node.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/trie_search.h create mode 120000 TrustKitDemo/Pods/Headers/Private/TrustKit/vendor_identifier.h create mode 120000 TrustKitDemo/Pods/Headers/Public/TrustKit/TSKPinningValidator.h create mode 120000 TrustKitDemo/Pods/Headers/Public/TrustKit/TSKPinningValidatorCallback.h create mode 120000 TrustKitDemo/Pods/Headers/Public/TrustKit/TSKPinningValidatorResult.h create mode 120000 TrustKitDemo/Pods/Headers/Public/TrustKit/TSKTrustDecision.h create mode 120000 TrustKitDemo/Pods/Headers/Public/TrustKit/TSKTrustKitConfig.h create mode 120000 TrustKitDemo/Pods/Headers/Public/TrustKit/TrustKit.h create mode 100644 TrustKitDemo/Pods/Local Podspecs/TrustKit.podspec.json create mode 100644 TrustKitDemo/Pods/Manifest.lock create mode 100644 TrustKitDemo/Pods/Pods.xcodeproj/project.pbxproj create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.markdown create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.plist create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-dummy.m create mode 100755 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-frameworks.sh create mode 100755 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-resources.sh create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.debug.xcconfig create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.release.xcconfig create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Info.plist create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.markdown create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.plist create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-dummy.m create mode 100755 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-frameworks.sh create mode 100755 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-resources.sh create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-umbrella.h create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.debug.xcconfig create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.modulemap create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.release.xcconfig create mode 100644 TrustKitDemo/Pods/Target Support Files/TrustKit-framework/Info.plist create mode 100644 TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework-dummy.m create mode 100644 TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework-prefix.pch create mode 100644 TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework-umbrella.h create mode 100644 TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework.modulemap create mode 100644 TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework.xcconfig create mode 100644 TrustKitDemo/Pods/Target Support Files/TrustKit-library/TrustKit-library-dummy.m create mode 100644 TrustKitDemo/Pods/Target Support Files/TrustKit-library/TrustKit-library-prefix.pch create mode 100644 TrustKitDemo/Pods/Target Support Files/TrustKit-library/TrustKit-library.xcconfig create mode 100644 TrustKitDemo/TrustKitDemo.xcworkspace/contents.xcworkspacedata diff --git a/TrustKitDemo/Podfile b/TrustKitDemo/Podfile new file mode 100644 index 00000000..c50addf1 --- /dev/null +++ b/TrustKitDemo/Podfile @@ -0,0 +1,21 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +target 'TrustKitDemo' do + # Uncomment the next line if you're using Swift or would like to use dynamic frameworks + # use_frameworks! + + # Pods for TrustKitDemo + pod 'TrustKit', :path => '../' + +end + + +target 'TrustKitDemoInSwift' do + # Uncomment the next line if you're using Swift or would like to use dynamic frameworks + use_frameworks! + + # Pods for TrustKitDemo + pod 'TrustKit', :path => '../' + +end \ No newline at end of file diff --git a/TrustKitDemo/Podfile.lock b/TrustKitDemo/Podfile.lock new file mode 100644 index 00000000..d75a91f9 --- /dev/null +++ b/TrustKitDemo/Podfile.lock @@ -0,0 +1,16 @@ +PODS: + - TrustKit (1.5.0) + +DEPENDENCIES: + - TrustKit (from `../`) + +EXTERNAL SOURCES: + TrustKit: + :path: ../ + +SPEC CHECKSUMS: + TrustKit: 59a0878569585d9b1adb7d76c341426deb57a5b8 + +PODFILE CHECKSUM: 86e19c8ce4662ee6d0981982609efb580466dfcc + +COCOAPODS: 1.2.1 diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/RSSwizzle.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/RSSwizzle.h new file mode 120000 index 00000000..d751768b --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/RSSwizzle.h @@ -0,0 +1 @@ +../../../../../TrustKit/Dependencies/RSSwizzle/RSSwizzle.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKBackgroundReporter.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKBackgroundReporter.h new file mode 120000 index 00000000..22db5306 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKBackgroundReporter.h @@ -0,0 +1 @@ +../../../../../TrustKit/Reporting/TSKBackgroundReporter.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKLog.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKLog.h new file mode 120000 index 00000000..efdb0e38 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKLog.h @@ -0,0 +1 @@ +../../../../../TrustKit/TSKLog.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKNSURLConnectionDelegateProxy.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKNSURLConnectionDelegateProxy.h new file mode 120000 index 00000000..cfa9b8c2 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKNSURLConnectionDelegateProxy.h @@ -0,0 +1 @@ +../../../../../TrustKit/Swizzling/TSKNSURLConnectionDelegateProxy.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKNSURLSessionDelegateProxy.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKNSURLSessionDelegateProxy.h new file mode 120000 index 00000000..3633c5f2 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKNSURLSessionDelegateProxy.h @@ -0,0 +1 @@ +../../../../../TrustKit/Swizzling/TSKNSURLSessionDelegateProxy.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinFailureReport.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinFailureReport.h new file mode 120000 index 00000000..6a8d07cd --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinFailureReport.h @@ -0,0 +1 @@ +../../../../../TrustKit/Reporting/TSKPinFailureReport.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidator.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidator.h new file mode 120000 index 00000000..fd45852b --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidator.h @@ -0,0 +1 @@ +../../../../../TrustKit/TSKPinningValidator.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidatorCallback.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidatorCallback.h new file mode 120000 index 00000000..5e288cd3 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidatorCallback.h @@ -0,0 +1 @@ +../../../../../TrustKit/TSKPinningValidatorCallback.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidatorResult.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidatorResult.h new file mode 120000 index 00000000..c9c087bd --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidatorResult.h @@ -0,0 +1 @@ +../../../../../TrustKit/TSKPinningValidatorResult.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidator_Private.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidator_Private.h new file mode 120000 index 00000000..2c2a1c84 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPinningValidator_Private.h @@ -0,0 +1 @@ +../../../../../TrustKit/TSKPinningValidator_Private.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPublicKeyAlgorithm.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPublicKeyAlgorithm.h new file mode 120000 index 00000000..eaadde96 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKPublicKeyAlgorithm.h @@ -0,0 +1 @@ +../../../../../TrustKit/Pinning/TSKPublicKeyAlgorithm.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKReportsRateLimiter.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKReportsRateLimiter.h new file mode 120000 index 00000000..24ef898e --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKReportsRateLimiter.h @@ -0,0 +1 @@ +../../../../../TrustKit/Reporting/TSKReportsRateLimiter.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKSPKIHashCache.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKSPKIHashCache.h new file mode 120000 index 00000000..62f4dae7 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKSPKIHashCache.h @@ -0,0 +1 @@ +../../../../../TrustKit/Pinning/TSKSPKIHashCache.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKTrustDecision.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKTrustDecision.h new file mode 120000 index 00000000..cfeaf48d --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKTrustDecision.h @@ -0,0 +1 @@ +../../../../../TrustKit/TSKTrustDecision.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKTrustKitConfig.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKTrustKitConfig.h new file mode 120000 index 00000000..49889ebd --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TSKTrustKitConfig.h @@ -0,0 +1 @@ +../../../../../TrustKit/TSKTrustKitConfig.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TrustKit-umbrella.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TrustKit-umbrella.h new file mode 120000 index 00000000..4ff23c99 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TrustKit-umbrella.h @@ -0,0 +1 @@ +../../../../../TrustKit/Framework/TrustKit-umbrella.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/TrustKit.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/TrustKit.h new file mode 120000 index 00000000..4fb90225 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/TrustKit.h @@ -0,0 +1 @@ +../../../../../TrustKit/TrustKit.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/assert.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/assert.h new file mode 120000 index 00000000..a29a80ea --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/assert.h @@ -0,0 +1 @@ +../../../../../TrustKit/Dependencies/domain_registry/private/assert.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/configuration_utils.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/configuration_utils.h new file mode 120000 index 00000000..4ecfa043 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/configuration_utils.h @@ -0,0 +1 @@ +../../../../../TrustKit/configuration_utils.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/domain_registry.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/domain_registry.h new file mode 120000 index 00000000..d77e043f --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/domain_registry.h @@ -0,0 +1 @@ +../../../../../TrustKit/Dependencies/domain_registry/domain_registry.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/parse_configuration.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/parse_configuration.h new file mode 120000 index 00000000..60ca2bb6 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/parse_configuration.h @@ -0,0 +1 @@ +../../../../../TrustKit/parse_configuration.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/registry_tables.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/registry_tables.h new file mode 120000 index 00000000..c8bda28c --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/registry_tables.h @@ -0,0 +1 @@ +../../../../../TrustKit/Dependencies/domain_registry/registry_tables_genfiles/registry_tables.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/registry_types.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/registry_types.h new file mode 120000 index 00000000..a58cb19a --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/registry_types.h @@ -0,0 +1 @@ +../../../../../TrustKit/Dependencies/domain_registry/private/registry_types.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/reporting_utils.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/reporting_utils.h new file mode 120000 index 00000000..af071f26 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/reporting_utils.h @@ -0,0 +1 @@ +../../../../../TrustKit/Reporting/reporting_utils.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/ssl_pin_verifier.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/ssl_pin_verifier.h new file mode 120000 index 00000000..d8e98e3f --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/ssl_pin_verifier.h @@ -0,0 +1 @@ +../../../../../TrustKit/Pinning/ssl_pin_verifier.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/string_util.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/string_util.h new file mode 120000 index 00000000..efe9cf7d --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/string_util.h @@ -0,0 +1 @@ +../../../../../TrustKit/Dependencies/domain_registry/private/string_util.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/trie_node.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/trie_node.h new file mode 120000 index 00000000..ace8150f --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/trie_node.h @@ -0,0 +1 @@ +../../../../../TrustKit/Dependencies/domain_registry/private/trie_node.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/trie_search.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/trie_search.h new file mode 120000 index 00000000..ecbea385 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/trie_search.h @@ -0,0 +1 @@ +../../../../../TrustKit/Dependencies/domain_registry/private/trie_search.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Private/TrustKit/vendor_identifier.h b/TrustKitDemo/Pods/Headers/Private/TrustKit/vendor_identifier.h new file mode 120000 index 00000000..286c7b7e --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Private/TrustKit/vendor_identifier.h @@ -0,0 +1 @@ +../../../../../TrustKit/Reporting/vendor_identifier.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKPinningValidator.h b/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKPinningValidator.h new file mode 120000 index 00000000..fd45852b --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKPinningValidator.h @@ -0,0 +1 @@ +../../../../../TrustKit/TSKPinningValidator.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKPinningValidatorCallback.h b/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKPinningValidatorCallback.h new file mode 120000 index 00000000..5e288cd3 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKPinningValidatorCallback.h @@ -0,0 +1 @@ +../../../../../TrustKit/TSKPinningValidatorCallback.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKPinningValidatorResult.h b/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKPinningValidatorResult.h new file mode 120000 index 00000000..c9c087bd --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKPinningValidatorResult.h @@ -0,0 +1 @@ +../../../../../TrustKit/TSKPinningValidatorResult.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKTrustDecision.h b/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKTrustDecision.h new file mode 120000 index 00000000..cfeaf48d --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKTrustDecision.h @@ -0,0 +1 @@ +../../../../../TrustKit/TSKTrustDecision.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKTrustKitConfig.h b/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKTrustKitConfig.h new file mode 120000 index 00000000..49889ebd --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Public/TrustKit/TSKTrustKitConfig.h @@ -0,0 +1 @@ +../../../../../TrustKit/TSKTrustKitConfig.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Headers/Public/TrustKit/TrustKit.h b/TrustKitDemo/Pods/Headers/Public/TrustKit/TrustKit.h new file mode 120000 index 00000000..4fb90225 --- /dev/null +++ b/TrustKitDemo/Pods/Headers/Public/TrustKit/TrustKit.h @@ -0,0 +1 @@ +../../../../../TrustKit/TrustKit.h \ No newline at end of file diff --git a/TrustKitDemo/Pods/Local Podspecs/TrustKit.podspec.json b/TrustKitDemo/Pods/Local Podspecs/TrustKit.podspec.json new file mode 100644 index 00000000..73994a62 --- /dev/null +++ b/TrustKitDemo/Pods/Local Podspecs/TrustKit.podspec.json @@ -0,0 +1,43 @@ +{ + "name": "TrustKit", + "version": "1.5.0", + "summary": "TrustKit is an open source framework that makes it easy to deploy SSL pinning in any iOS, macOS, tvOS or watchOS App.", + "homepage": "https://datatheorem.github.io/TrustKit", + "documentation_url": "https://datatheorem.github.io/TrustKit/documentation/", + "license": { + "type": "MIT", + "file": "LICENSE" + }, + "authors": [ + "Alban Diquet", + "Angela Chow", + "Eric Castro" + ], + "source": { + "git": "https://github.com/datatheorem/TrustKit.git", + "tag": "1.5.0" + }, + "platforms": { + "ios": "8.0", + "osx": "10.10", + "tvos": "10.0", + "watchos": "3.0" + }, + "source_files": [ + "TrustKit", + "TrustKit/**/*.{h,m,c}" + ], + "public_header_files": [ + "TrustKit/TrustKit.h", + "TrustKit/TSKTrustKitConfig.h", + "TrustKit/TSKPinningValidator.h", + "TrustKit/TSKPinningValidatorCallback.h", + "TrustKit/TSKPinningValidatorResult.h", + "TrustKit/TSKTrustDecision.h" + ], + "frameworks": [ + "Foundation", + "Security" + ], + "requires_arc": true +} diff --git a/TrustKitDemo/Pods/Manifest.lock b/TrustKitDemo/Pods/Manifest.lock new file mode 100644 index 00000000..d75a91f9 --- /dev/null +++ b/TrustKitDemo/Pods/Manifest.lock @@ -0,0 +1,16 @@ +PODS: + - TrustKit (1.5.0) + +DEPENDENCIES: + - TrustKit (from `../`) + +EXTERNAL SOURCES: + TrustKit: + :path: ../ + +SPEC CHECKSUMS: + TrustKit: 59a0878569585d9b1adb7d76c341426deb57a5b8 + +PODFILE CHECKSUM: 86e19c8ce4662ee6d0981982609efb580466dfcc + +COCOAPODS: 1.2.1 diff --git a/TrustKitDemo/Pods/Pods.xcodeproj/project.pbxproj b/TrustKitDemo/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 00000000..c5ca0bc9 --- /dev/null +++ b/TrustKitDemo/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,1165 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 00851FF4E5F19A667BF0C3D50912A1D5 /* vendor_identifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A38032B035203051E2B243A38998AFD /* vendor_identifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 00A1CE305307E9CC1C56E14BD3969596 /* RSSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D825CA602AD8C4F0DC92C5C2F315F80 /* RSSwizzle.m */; }; + 0130E26071450C6CBA439373428DC4CF /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = AF127C3A13A8630FD92058669BDFF057 /* TrustKit.m */; }; + 01D711A6F5D6E57DCCE8946388BEC7EB /* assert.h in Headers */ = {isa = PBXBuildFile; fileRef = 04C9AAFF75BACF2953B727E819D0D4E4 /* assert.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0496FF3E58A206D1EFD7394E6456E700 /* string_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 287C7E0B6B509A541BC1525CF4C29A22 /* string_util.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 094A47B3A015A857FD9902D9DE4ACDE5 /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FFBF768AACDC0BA4BFEF5281933FFAA7 /* TSKPinningValidatorResult.m */; }; + 09E7DF568807E63A6676DB2AB69939FC /* registry_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 7474D1481157E1DB7C600DA9295073F4 /* registry_types.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 10484F688180E9B2CA3F443C47D9A9F5 /* trie_search.c in Sources */ = {isa = PBXBuildFile; fileRef = 6D8DD333AC5AD1ABEAEDA6B5979E196E /* trie_search.c */; }; + 10C5AC1D48A7A2087833A1EFFCCDD65A /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C64C309645EA64B17D511CF7075A4B5 /* TSKTrustKitConfig.m */; }; + 14469A265E74524CBC0448214CDB5A90 /* TSKNSURLSessionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = A54DFB58902F4C4023515556B6B09E3B /* TSKNSURLSessionDelegateProxy.m */; }; + 15F3A1BEBE2F0554D51CAE47B09088A4 /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C64C309645EA64B17D511CF7075A4B5 /* TSKTrustKitConfig.m */; }; + 160C63E448CBB115119659EFBF5C84E0 /* Pods-TrustKitDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D556BFA8C03C6783987680194DA1ABB /* Pods-TrustKitDemo-dummy.m */; }; + 173654C8C63381BFAC72BFA6BB2CEBB9 /* TSKReportsRateLimiter.h in Headers */ = {isa = PBXBuildFile; fileRef = B4BB3E7BC15983F00341A2742C1913D5 /* TSKReportsRateLimiter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1A43A1DCDCD6E62E13B2A735586524D7 /* RSSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D825CA602AD8C4F0DC92C5C2F315F80 /* RSSwizzle.m */; }; + 1B1A76932E64219790E2E06E572F4149 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */; }; + 1D1B754DB932A2FF2C34C4E5ECBBAB04 /* reporting_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 1727DE17A4DF27E0A32BC0DAB30A80D1 /* reporting_utils.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1DA5ED746B8D8FF91F7077A79CB37823 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C085E02FF89804F50ADDA04F5A586E94 /* Security.framework */; }; + 203488EF10B0D10B0854259A0BF35A27 /* trie_search.c in Sources */ = {isa = PBXBuildFile; fileRef = 6D8DD333AC5AD1ABEAEDA6B5979E196E /* trie_search.c */; }; + 20F2F8326AC10F0EDB95A5C120A42C9C /* reporting_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 1727DE17A4DF27E0A32BC0DAB30A80D1 /* reporting_utils.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 219DE7255483B7DDDE0FD5BD790D95A0 /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B83693BA9D3DD38777DB7CCDD9A5B1A /* ssl_pin_verifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 29C6CB0A3A3842D9B704E0CC8B834731 /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DF10471A2E6B2E0AC9FAC943DA4F8B7 /* TSKBackgroundReporter.m */; }; + 2A0DC2F329F20A507F8EEC15F3472667 /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = C7AB4A501B02D26F8C5A92B7A3AC21F5 /* reporting_utils.m */; }; + 2B886EF817237F7DAD4AC1AB80F7CF87 /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 390AED1F5816C13CBAC37BC498AED5E4 /* domain_registry.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2F5C97F11E3C988C45B14C04EF182AFD /* RSSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = 1863A28B3A36F0B9EE4582862E1056D4 /* RSSwizzle.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 304C39AEAB37E4B3ADE44D6985DE6AEE /* registry_search.c in Sources */ = {isa = PBXBuildFile; fileRef = DEADB11810EFB649591A4C39A6FCF739 /* registry_search.c */; }; + 345E1E08EB0E9881B89FDF6728B5CD4F /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 702B9532D5B1159EF60B335D307B9C45 /* TSKSPKIHashCache.m */; }; + 36F5F40C0CA5EE29258B2AAD78A8D806 /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = C7AB4A501B02D26F8C5A92B7A3AC21F5 /* reporting_utils.m */; }; + 3A27EAA7E79CBD53890906565E1C0734 /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6CE1FAD8BB620AAF2879A0AC238BC6 /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 41EDB6EBC0B1F4135AC577C87C1C62CC /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DF10471A2E6B2E0AC9FAC943DA4F8B7 /* TSKBackgroundReporter.m */; }; + 4550A31BDF3B61E7267B06F119CC6908 /* TSKNSURLConnectionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 2342803E73DA3E02533A2B9B7616F0C7 /* TSKNSURLConnectionDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 48F95C232FF22C9E855EB8DE6C5D3962 /* TSKNSURLConnectionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 46C0B0ADDA1FAF1CF76BBA4C55725C04 /* TSKNSURLConnectionDelegateProxy.m */; }; + 49634D47E0EDD9B498FDE2F5F4AA485E /* trie_search.h in Headers */ = {isa = PBXBuildFile; fileRef = 103565E750423591CB9BD33EE2F4A32E /* trie_search.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 498E8D9DEB07EB14E153CD47D176A452 /* TrustKit-framework-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA9D6A4C087F7256F7C1FC0976DEB80E /* TrustKit-framework-dummy.m */; }; + 4C904BF58040C4F69A02BB90BDF6A202 /* TSKLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 91F770C36742BA6AEE960724BEC192F5 /* TSKLog.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5167C676C50CB1FE15039D4BD956F2F8 /* TSKNSURLSessionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 72AE90ECEDFD50BADED53717FBB3892D /* TSKNSURLSessionDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5448F6FAD1663B8B906AD4FEAE95C3BA /* TSKTrustDecision.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BCBC95D7B4F66D37FBA5785D4040B60 /* TSKTrustDecision.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 54FA5C9512FCE077B16C79562E6003E4 /* registry_tables.h in Headers */ = {isa = PBXBuildFile; fileRef = A42809F0183618D391CF0950F836D339 /* registry_tables.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 557E4214E44510E8A529A1C9AE1C2107 /* TSKPinningValidator_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E3CD6B1F3DDAF522B22000B826EF88 /* TSKPinningValidator_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 56FF6CCDD07BEC720EFEF68856486006 /* TSKLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 91F770C36742BA6AEE960724BEC192F5 /* TSKLog.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5A6ED2C021536CAD9C5323764E651092 /* TSKPinningValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = C193DFCC3956EDDD7FFE7D491459865D /* TSKPinningValidator.m */; }; + 5B1BB80A066D28AAE7D8C6124EC9B155 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */; }; + 5B3232D66E17FA89AA1114E851BB1260 /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 702B9532D5B1159EF60B335D307B9C45 /* TSKSPKIHashCache.m */; }; + 5CEDC8B868290CC7CC1F128A5C6746E6 /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6CE1FAD8BB620AAF2879A0AC238BC6 /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 61F5F24D2CB7BCE8126273DEBA08CC73 /* TSKPinningValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = C193DFCC3956EDDD7FFE7D491459865D /* TSKPinningValidator.m */; }; + 6444B457833221D3A3E74BF00BD47035 /* TSKTrustKitConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = DEDE844CB2D002704EE36BB2178EB726 /* TSKTrustKitConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6C33C70511B2472C8E61AE03DEA53DC3 /* TSKPinFailureReport.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B09B7AB0DE384F8DDC19A2B2B5ACAD5 /* TSKPinFailureReport.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6EE94D82A953FE4FB494AE05B1FA9AF5 /* assert.c in Sources */ = {isa = PBXBuildFile; fileRef = 214B2EAAAB4DEA645B6A1B65D1F2F386 /* assert.c */; }; + 706DFFF92262AD3A654930ADAC7372F7 /* RSSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = 1863A28B3A36F0B9EE4582862E1056D4 /* RSSwizzle.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 709C87892B7603F4AF2C250980453B11 /* TSKNSURLSessionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 72AE90ECEDFD50BADED53717FBB3892D /* TSKNSURLSessionDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 70DF98043F0FC61A986D90A016C32011 /* TrustKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CA3CFD14971562FA66B218152D30F35 /* TrustKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 72C555F342E19A2EC42F8F977F34391F /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C085E02FF89804F50ADDA04F5A586E94 /* Security.framework */; }; + 72D0920BDCF2DF9583CD74CE0EE4E7E5 /* TSKReportsRateLimiter.h in Headers */ = {isa = PBXBuildFile; fileRef = B4BB3E7BC15983F00341A2742C1913D5 /* TSKReportsRateLimiter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7783BE79333283119B7E04ADE59D4D85 /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = F56169ECAF90EECAEB4D0EAA6C9D9F89 /* TSKPinFailureReport.m */; }; + 77FB5D09E1047C484301DD8A7CC193E0 /* TSKPinFailureReport.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B09B7AB0DE384F8DDC19A2B2B5ACAD5 /* TSKPinFailureReport.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 789FDC0611E1E6643501301907740355 /* trie_search.h in Headers */ = {isa = PBXBuildFile; fileRef = 103565E750423591CB9BD33EE2F4A32E /* trie_search.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7DF75D25C7A2C29B05D5787938C112ED /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 954A0787F64D38E08D65EBC46C1CB9AE /* parse_configuration.m */; }; + 7F4626F2CD390F14D8980C333830EF5D /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 390AED1F5816C13CBAC37BC498AED5E4 /* domain_registry.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 816D06005E4A6D71BBF791548E785E32 /* TSKNSURLConnectionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 46C0B0ADDA1FAF1CF76BBA4C55725C04 /* TSKNSURLConnectionDelegateProxy.m */; }; + 819293E2A79D94FADDCB7F6ABB9D9D18 /* init_registry_tables.c in Sources */ = {isa = PBXBuildFile; fileRef = 49D1BCE0D2BFC86604429C1DB287EEE9 /* init_registry_tables.c */; }; + 833FAAB1D09078D8B6F7F9EA9516C1C1 /* parse_configuration.h in Headers */ = {isa = PBXBuildFile; fileRef = BCE910A2438B2E8646192F907B37E559 /* parse_configuration.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8BC37767B9A244EE8600DBB66DBECFF6 /* TSKPinningValidator_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E3CD6B1F3DDAF522B22000B826EF88 /* TSKPinningValidator_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 912296DA496E76C4592D2BB27525DC58 /* TSKSPKIHashCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A0A26583325B45FDF95C9957F01F02 /* TSKSPKIHashCache.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 91645A1F75435D30D1C0258EE9C3DDAE /* TSKPinningValidatorCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A22411D7297D4377E43FD163A4B1314 /* TSKPinningValidatorCallback.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 919B8CA0E63E044A39600170B1544B6B /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 9144C1D132FDE48C1F12DC01A5CE33EE /* configuration_utils.m */; }; + 96DF97DF055B8C04D3FE99DD46CA0AFB /* Pods-TrustKitDemoInSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F05E7CAB631D2576B200F63D1347D3C /* Pods-TrustKitDemoInSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9AAC9AC75846966966330B3C091B58F5 /* Pods-TrustKitDemoInSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7CBC78276DB001BCA4D874C083A7CF5C /* Pods-TrustKitDemoInSwift-dummy.m */; }; + 9AB598D92256251C141732F7A987C16C /* vendor_identifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A38032B035203051E2B243A38998AFD /* vendor_identifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9C15CA27397D72DBEA030967D4B507E8 /* TSKTrustKitConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = DEDE844CB2D002704EE36BB2178EB726 /* TSKTrustKitConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9D69A757AB397230EF3145ADA6B10188 /* trie_node.h in Headers */ = {isa = PBXBuildFile; fileRef = 099F2B4E7779209E04365EE6FE079763 /* trie_node.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A075C1FA71C3000765BE026D38199163 /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FFBF768AACDC0BA4BFEF5281933FFAA7 /* TSKPinningValidatorResult.m */; }; + A0B8CEAF67460C1A2D0C2C2A1A990F67 /* configuration_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5DC8912CA18313277EF115E15BBE46 /* configuration_utils.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A30AFCE398CB19D1AB2F17FFB7015A39 /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = AF127C3A13A8630FD92058669BDFF057 /* TrustKit.m */; }; + A56A25BC5E423091DC2564DBD867FDA3 /* TSKNSURLSessionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = A54DFB58902F4C4023515556B6B09E3B /* TSKNSURLSessionDelegateProxy.m */; }; + A818444547D3C148B87A15A0DF7EDA09 /* TSKPublicKeyAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D72855E63D1CD2D36950CCBD3D83279 /* TSKPublicKeyAlgorithm.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AA3DB3E374D6FE6C0D014CDBEED6D2A7 /* TSKPinningValidatorCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A22411D7297D4377E43FD163A4B1314 /* TSKPinningValidatorCallback.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AB6C9A86369041598BEDDB4299A62528 /* vendor_identifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 758A7792A0090736AA3ABBCB6FA8FAC4 /* vendor_identifier.m */; }; + AC4668E1382E6BCF1D1C0734115BA63E /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 288A1FEC0658D8FB688BBAE9B97E2B7F /* ssl_pin_verifier.m */; }; + AD1A357B9A460872C06FAC8C81227E75 /* parse_configuration.h in Headers */ = {isa = PBXBuildFile; fileRef = BCE910A2438B2E8646192F907B37E559 /* parse_configuration.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B33CE417C5345363CD8E3F45CE44871B /* TrustKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C781175B244EAD62240FA815899F2C7C /* TrustKit-umbrella.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B68CAC1D63D2A0D9BA930C508E48AE57 /* trie_node.h in Headers */ = {isa = PBXBuildFile; fileRef = 099F2B4E7779209E04365EE6FE079763 /* trie_node.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B769BE1CE6DDDC128E3F160968194EAE /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B83693BA9D3DD38777DB7CCDD9A5B1A /* ssl_pin_verifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; + BAC36417573685B654AAB2A415F87DCB /* TSKPinningValidator.h in Headers */ = {isa = PBXBuildFile; fileRef = B8F41D8A5EA2A86E468C52EA4956695F /* TSKPinningValidator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BD8D9989CE8BC3F93EA53EE58D17619C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */; }; + BE1276DBEB9F2E09D061430025283640 /* string_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 287C7E0B6B509A541BC1525CF4C29A22 /* string_util.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C9199DCE6F188384A1A3EB729854EBF1 /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 954A0787F64D38E08D65EBC46C1CB9AE /* parse_configuration.m */; }; + C99670E8EF0E6428E51A39F0A918EE59 /* TSKTrustDecision.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BCBC95D7B4F66D37FBA5785D4040B60 /* TSKTrustDecision.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CC0B39B3704D36B21E5ACA9F565FBE9E /* init_registry_tables.c in Sources */ = {isa = PBXBuildFile; fileRef = 49D1BCE0D2BFC86604429C1DB287EEE9 /* init_registry_tables.c */; }; + D0D9F705F65EEC35793699EE0630E8C3 /* registry_search.c in Sources */ = {isa = PBXBuildFile; fileRef = DEADB11810EFB649591A4C39A6FCF739 /* registry_search.c */; }; + D2FB40D0D6591DD87164BCFDED4A872B /* assert.h in Headers */ = {isa = PBXBuildFile; fileRef = 04C9AAFF75BACF2953B727E819D0D4E4 /* assert.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D3C5909E5B8CA9C393309391B4DDF1E0 /* registry_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 7474D1481157E1DB7C600DA9295073F4 /* registry_types.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D5430056619E1C24EC30A1E3C5101E4B /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = F56169ECAF90EECAEB4D0EAA6C9D9F89 /* TSKPinFailureReport.m */; }; + D59B2ACF7184AA8A53037C4096629788 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */; }; + D5C5AAAD941E06DA3AAB2EB4D02A3F3D /* TSKPublicKeyAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D72855E63D1CD2D36950CCBD3D83279 /* TSKPublicKeyAlgorithm.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DBA72238E5490710B7FDFD0BC03C25B2 /* TrustKit-library-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = C60EE265DEB50B45F4E601CC49B209EC /* TrustKit-library-dummy.m */; }; + DDA29052B0CEC856600037D272593BBE /* TSKBackgroundReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = CF15A12A9DDFF9EF237480A92FB6635E /* TSKBackgroundReporter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DEB1C3B236A303CDBE1A28F1C168BFAC /* TSKSPKIHashCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A0A26583325B45FDF95C9957F01F02 /* TSKSPKIHashCache.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E167E2DFDC1302944D5105477C99C410 /* TSKReportsRateLimiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1267CA13389D6978F9B17ABC4B1D1DAB /* TSKReportsRateLimiter.m */; }; + E5BDBD5846B9BF559F9D4A790981E21D /* TSKBackgroundReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = CF15A12A9DDFF9EF237480A92FB6635E /* TSKBackgroundReporter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E9B3C163FC82567554CD8565481A0C08 /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 9144C1D132FDE48C1F12DC01A5CE33EE /* configuration_utils.m */; }; + E9DA954AAEAB44DCD066B35D746A8383 /* TSKPinningValidator.h in Headers */ = {isa = PBXBuildFile; fileRef = B8F41D8A5EA2A86E468C52EA4956695F /* TSKPinningValidator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EB6F5E04F094338BBD32B9C9A8715DE8 /* vendor_identifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 758A7792A0090736AA3ABBCB6FA8FAC4 /* vendor_identifier.m */; }; + EBB2FD29B682B615CC8D640704424C39 /* assert.c in Sources */ = {isa = PBXBuildFile; fileRef = 214B2EAAAB4DEA645B6A1B65D1F2F386 /* assert.c */; }; + EC5EA69669DCCD19AB04FECEE50644F7 /* TSKReportsRateLimiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1267CA13389D6978F9B17ABC4B1D1DAB /* TSKReportsRateLimiter.m */; }; + F005E6B1ED453CEEE86179016DD2E13F /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 288A1FEC0658D8FB688BBAE9B97E2B7F /* ssl_pin_verifier.m */; }; + F30A67B156D94EAD3CA444BE6C6C22A2 /* registry_tables.h in Headers */ = {isa = PBXBuildFile; fileRef = A42809F0183618D391CF0950F836D339 /* registry_tables.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F52DAB9D11F101AA30E8466DCFA2D8DB /* configuration_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5DC8912CA18313277EF115E15BBE46 /* configuration_utils.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F5DB7A40C7181F6A1B0C2C0193786A08 /* TSKNSURLConnectionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 2342803E73DA3E02533A2B9B7616F0C7 /* TSKNSURLConnectionDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FC5DF4AF969196C79FD959A2C8DD2DC2 /* TrustKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CA3CFD14971562FA66B218152D30F35 /* TrustKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FCAE6A2720A8FA62F35340372D7BE36D /* TrustKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C781175B244EAD62240FA815899F2C7C /* TrustKit-umbrella.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FF3AB846370F0C93EF33D433416810BB /* TrustKit-framework-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B4B06F0C8E473C389BA9B4A3A93875B9 /* TrustKit-framework-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 8B87672562EA830C7D0B9598BC3E2568 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = B83210985026774772228F688CCC95C6; + remoteInfo = "TrustKit-framework"; + }; + 8F8E6B6301DB3263048BCA55327BB7EC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9FEA5104EB31D1BB74EFD21D7B2F5C64; + remoteInfo = "TrustKit-library"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0123B07E490339BB8BAD0ACF37E6A605 /* Pods-TrustKitDemo-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-TrustKitDemo-acknowledgements.markdown"; sourceTree = ""; }; + 016D7DEAA7EC198B0F5C41B1FB7C4FB5 /* Pods-TrustKitDemoInSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TrustKitDemoInSwift.release.xcconfig"; sourceTree = ""; }; + 04C9AAFF75BACF2953B727E819D0D4E4 /* assert.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = assert.h; sourceTree = ""; }; + 099F2B4E7779209E04365EE6FE079763 /* trie_node.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = trie_node.h; sourceTree = ""; }; + 09BC6E8AAC9C761AE184F9B5BF603D94 /* Pods-TrustKitDemo-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TrustKitDemo-acknowledgements.plist"; sourceTree = ""; }; + 0D17B1927DAF9BE7FD80DCBF0F6F537E /* Pods-TrustKitDemoInSwift-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TrustKitDemoInSwift-frameworks.sh"; sourceTree = ""; }; + 0F24CF15538FFB4930681D9FFAEB7E4A /* Pods_TrustKitDemoInSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_TrustKitDemoInSwift.framework; path = "Pods-TrustKitDemoInSwift.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 103565E750423591CB9BD33EE2F4A32E /* trie_search.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = trie_search.h; sourceTree = ""; }; + 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 1267CA13389D6978F9B17ABC4B1D1DAB /* TSKReportsRateLimiter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKReportsRateLimiter.m; sourceTree = ""; }; + 12AEDEF058A1EB1FA200B4D41EE3D700 /* Pods-TrustKitDemo-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TrustKitDemo-frameworks.sh"; sourceTree = ""; }; + 14E737FFF2A1232FF4058D088804405F /* TrustKit-framework.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "TrustKit-framework.xcconfig"; sourceTree = ""; }; + 1727DE17A4DF27E0A32BC0DAB30A80D1 /* reporting_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = reporting_utils.h; sourceTree = ""; }; + 1863A28B3A36F0B9EE4582862E1056D4 /* RSSwizzle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = RSSwizzle.h; sourceTree = ""; }; + 1A22411D7297D4377E43FD163A4B1314 /* TSKPinningValidatorCallback.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorCallback.h; sourceTree = ""; }; + 1B09B7AB0DE384F8DDC19A2B2B5ACAD5 /* TSKPinFailureReport.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKPinFailureReport.h; sourceTree = ""; }; + 1B83693BA9D3DD38777DB7CCDD9A5B1A /* ssl_pin_verifier.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = ssl_pin_verifier.h; sourceTree = ""; }; + 214B2EAAAB4DEA645B6A1B65D1F2F386 /* assert.c */ = {isa = PBXFileReference; includeInIndex = 1; path = assert.c; sourceTree = ""; }; + 2342803E73DA3E02533A2B9B7616F0C7 /* TSKNSURLConnectionDelegateProxy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKNSURLConnectionDelegateProxy.h; sourceTree = ""; }; + 287C7E0B6B509A541BC1525CF4C29A22 /* string_util.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = string_util.h; sourceTree = ""; }; + 288A1FEC0658D8FB688BBAE9B97E2B7F /* ssl_pin_verifier.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = ssl_pin_verifier.m; sourceTree = ""; }; + 2D825CA602AD8C4F0DC92C5C2F315F80 /* RSSwizzle.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = RSSwizzle.m; sourceTree = ""; }; + 30A0A26583325B45FDF95C9957F01F02 /* TSKSPKIHashCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKSPKIHashCache.h; sourceTree = ""; }; + 390AED1F5816C13CBAC37BC498AED5E4 /* domain_registry.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = domain_registry.h; sourceTree = ""; }; + 3E428D9FBEF4C1CD9DF056ADF8F2E2EB /* Pods-TrustKitDemoInSwift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = "Pods-TrustKitDemoInSwift.modulemap"; sourceTree = ""; }; + 3ECD098C376738289FBB30A9DF5E9F6E /* Pods-TrustKitDemo-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TrustKitDemo-resources.sh"; sourceTree = ""; }; + 3F5091830E79DD11EB3A6934CE0FAF5E /* TrustKit-library.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "TrustKit-library.xcconfig"; path = "../TrustKit-library/TrustKit-library.xcconfig"; sourceTree = ""; }; + 46C0B0ADDA1FAF1CF76BBA4C55725C04 /* TSKNSURLConnectionDelegateProxy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKNSURLConnectionDelegateProxy.m; sourceTree = ""; }; + 49D1BCE0D2BFC86604429C1DB287EEE9 /* init_registry_tables.c */ = {isa = PBXFileReference; includeInIndex = 1; path = init_registry_tables.c; sourceTree = ""; }; + 4CA3CFD14971562FA66B218152D30F35 /* TrustKit.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TrustKit.h; sourceTree = ""; }; + 5142815B08CFBAB5C0A32B7DE5259253 /* Pods-TrustKitDemoInSwift-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-TrustKitDemoInSwift-acknowledgements.markdown"; sourceTree = ""; }; + 51E3CD6B1F3DDAF522B22000B826EF88 /* TSKPinningValidator_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidator_Private.h; sourceTree = ""; }; + 5A38032B035203051E2B243A38998AFD /* vendor_identifier.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = vendor_identifier.h; sourceTree = ""; }; + 5BCBC95D7B4F66D37FBA5785D4040B60 /* TSKTrustDecision.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKTrustDecision.h; sourceTree = ""; }; + 613C850C57E8EE6B749CD2218C6AC1DD /* TrustKit-framework.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = "TrustKit-framework.modulemap"; sourceTree = ""; }; + 693C37A08B6303393A039F004657B2A8 /* Pods-TrustKitDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TrustKitDemo.release.xcconfig"; sourceTree = ""; }; + 6D8DD333AC5AD1ABEAEDA6B5979E196E /* trie_search.c */ = {isa = PBXFileReference; includeInIndex = 1; path = trie_search.c; sourceTree = ""; }; + 702B9532D5B1159EF60B335D307B9C45 /* TSKSPKIHashCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKSPKIHashCache.m; sourceTree = ""; }; + 72AE90ECEDFD50BADED53717FBB3892D /* TSKNSURLSessionDelegateProxy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKNSURLSessionDelegateProxy.h; sourceTree = ""; }; + 7474D1481157E1DB7C600DA9295073F4 /* registry_types.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = registry_types.h; sourceTree = ""; }; + 758A7792A0090736AA3ABBCB6FA8FAC4 /* vendor_identifier.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = vendor_identifier.m; sourceTree = ""; }; + 78E80D386FCEA4D031D86D86D5FA5020 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7C64C309645EA64B17D511CF7075A4B5 /* TSKTrustKitConfig.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKTrustKitConfig.m; sourceTree = ""; }; + 7CBC78276DB001BCA4D874C083A7CF5C /* Pods-TrustKitDemoInSwift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-TrustKitDemoInSwift-dummy.m"; sourceTree = ""; }; + 7D72855E63D1CD2D36950CCBD3D83279 /* TSKPublicKeyAlgorithm.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKPublicKeyAlgorithm.h; sourceTree = ""; }; + 7F05E7CAB631D2576B200F63D1347D3C /* Pods-TrustKitDemoInSwift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-TrustKitDemoInSwift-umbrella.h"; sourceTree = ""; }; + 8D556BFA8C03C6783987680194DA1ABB /* Pods-TrustKitDemo-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-TrustKitDemo-dummy.m"; sourceTree = ""; }; + 8F3CBC263BB58417499B0695F72AE703 /* TrustKit-framework-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TrustKit-framework-prefix.pch"; sourceTree = ""; }; + 9144C1D132FDE48C1F12DC01A5CE33EE /* configuration_utils.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = configuration_utils.m; sourceTree = ""; }; + 91F770C36742BA6AEE960724BEC192F5 /* TSKLog.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKLog.h; sourceTree = ""; }; + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 954A0787F64D38E08D65EBC46C1CB9AE /* parse_configuration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = parse_configuration.m; sourceTree = ""; }; + 9DF10471A2E6B2E0AC9FAC943DA4F8B7 /* TSKBackgroundReporter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKBackgroundReporter.m; sourceTree = ""; }; + 9EBB7E04712E8D9773923B6900DE4219 /* Pods-TrustKitDemoInSwift-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TrustKitDemoInSwift-resources.sh"; sourceTree = ""; }; + A42809F0183618D391CF0950F836D339 /* registry_tables.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = registry_tables.h; sourceTree = ""; }; + A54DFB58902F4C4023515556B6B09E3B /* TSKNSURLSessionDelegateProxy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKNSURLSessionDelegateProxy.m; sourceTree = ""; }; + AF127C3A13A8630FD92058669BDFF057 /* TrustKit.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TrustKit.m; sourceTree = ""; }; + B4B06F0C8E473C389BA9B4A3A93875B9 /* TrustKit-framework-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TrustKit-framework-umbrella.h"; sourceTree = ""; }; + B4BB3E7BC15983F00341A2742C1913D5 /* TSKReportsRateLimiter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKReportsRateLimiter.h; sourceTree = ""; }; + B6357BE7D3B70E53DE8B97D000753691 /* TrustKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = TrustKit.framework; path = "TrustKit-framework.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + B8F41D8A5EA2A86E468C52EA4956695F /* TSKPinningValidator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidator.h; sourceTree = ""; }; + BCE910A2438B2E8646192F907B37E559 /* parse_configuration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = parse_configuration.h; sourceTree = ""; }; + C085E02FF89804F50ADDA04F5A586E94 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; + C193DFCC3956EDDD7FFE7D491459865D /* TSKPinningValidator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidator.m; sourceTree = ""; }; + C261E3C0582E5545F335945EA1504529 /* TrustKit-library-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "TrustKit-library-prefix.pch"; path = "../TrustKit-library/TrustKit-library-prefix.pch"; sourceTree = ""; }; + C60EE265DEB50B45F4E601CC49B209EC /* TrustKit-library-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "TrustKit-library-dummy.m"; path = "../TrustKit-library/TrustKit-library-dummy.m"; sourceTree = ""; }; + C781175B244EAD62240FA815899F2C7C /* TrustKit-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TrustKit-umbrella.h"; sourceTree = ""; }; + C7AB4A501B02D26F8C5A92B7A3AC21F5 /* reporting_utils.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = reporting_utils.m; sourceTree = ""; }; + CF15A12A9DDFF9EF237480A92FB6635E /* TSKBackgroundReporter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKBackgroundReporter.h; sourceTree = ""; }; + CF6CE1FAD8BB620AAF2879A0AC238BC6 /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; + DA9D6A4C087F7256F7C1FC0976DEB80E /* TrustKit-framework-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "TrustKit-framework-dummy.m"; sourceTree = ""; }; + DEADB11810EFB649591A4C39A6FCF739 /* registry_search.c */ = {isa = PBXFileReference; includeInIndex = 1; path = registry_search.c; sourceTree = ""; }; + DEDE844CB2D002704EE36BB2178EB726 /* TSKTrustKitConfig.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKTrustKitConfig.h; sourceTree = ""; }; + E2073B4A7BB39C0B655B258E2805B398 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E801D6CAA748B5F29CB8103D244D1FF6 /* Pods-TrustKitDemoInSwift-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TrustKitDemoInSwift-acknowledgements.plist"; sourceTree = ""; }; + EB5A620362CBE2FB1ECDCE58124DAAD7 /* libTrustKit-library.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libTrustKit-library.a"; path = "libTrustKit-library.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + ED54221395BDC8F9A7313801F3269B5E /* Pods-TrustKitDemoInSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TrustKitDemoInSwift.debug.xcconfig"; sourceTree = ""; }; + F1F1FF3F256A4766D61F73362872F323 /* Pods-TrustKitDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TrustKitDemo.debug.xcconfig"; sourceTree = ""; }; + F56169ECAF90EECAEB4D0EAA6C9D9F89 /* TSKPinFailureReport.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKPinFailureReport.m; sourceTree = ""; }; + FA1D62A70B82BE6CD3AA55E6B01CFEE2 /* libPods-TrustKitDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-TrustKitDemo.a"; path = "libPods-TrustKitDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + FF5DC8912CA18313277EF115E15BBE46 /* configuration_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = configuration_utils.h; sourceTree = ""; }; + FFBF768AACDC0BA4BFEF5281933FFAA7 /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2E843DC24B5E78C662AD4501BDF315DE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5B1BB80A066D28AAE7D8C6124EC9B155 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 381A1E592BA214AFBCE620C6F2AF4928 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BD8D9989CE8BC3F93EA53EE58D17619C /* Foundation.framework in Frameworks */, + 72C555F342E19A2EC42F8F977F34391F /* Security.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A1A9E0D00EB77C8A9B6F766DABF173ED /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D59B2ACF7184AA8A53037C4096629788 /* Foundation.framework in Frameworks */, + 1DA5ED746B8D8FF91F7077A79CB37823 /* Security.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AA5364440FE74A5EB2549E648523110B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1B1A76932E64219790E2E06E572F4149 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0FBACCF3F441602CFC8EDB2F2C0521AF /* domain_registry */ = { + isa = PBXGroup; + children = ( + 390AED1F5816C13CBAC37BC498AED5E4 /* domain_registry.h */, + 950A90C120DA7744EFBCBBF36D729E65 /* private */, + 72DEC79CEFA6BB004F2F8B07DF09B2DE /* registry_tables_genfiles */, + ); + name = domain_registry; + path = domain_registry; + sourceTree = ""; + }; + 3C83A6AB31F47CA29D795C027611AD7C /* Reporting */ = { + isa = PBXGroup; + children = ( + 1727DE17A4DF27E0A32BC0DAB30A80D1 /* reporting_utils.h */, + C7AB4A501B02D26F8C5A92B7A3AC21F5 /* reporting_utils.m */, + CF15A12A9DDFF9EF237480A92FB6635E /* TSKBackgroundReporter.h */, + 9DF10471A2E6B2E0AC9FAC943DA4F8B7 /* TSKBackgroundReporter.m */, + 1B09B7AB0DE384F8DDC19A2B2B5ACAD5 /* TSKPinFailureReport.h */, + F56169ECAF90EECAEB4D0EAA6C9D9F89 /* TSKPinFailureReport.m */, + B4BB3E7BC15983F00341A2742C1913D5 /* TSKReportsRateLimiter.h */, + 1267CA13389D6978F9B17ABC4B1D1DAB /* TSKReportsRateLimiter.m */, + 5A38032B035203051E2B243A38998AFD /* vendor_identifier.h */, + 758A7792A0090736AA3ABBCB6FA8FAC4 /* vendor_identifier.m */, + ); + name = Reporting; + path = Reporting; + sourceTree = ""; + }; + 433CD3331B6C3787F473C941B61FC68F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4F990579C2E52276FD3C28227B49D60B /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 4F990579C2E52276FD3C28227B49D60B /* iOS */ = { + isa = PBXGroup; + children = ( + 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */, + C085E02FF89804F50ADDA04F5A586E94 /* Security.framework */, + ); + name = iOS; + sourceTree = ""; + }; + 5728737062D6BDD3A905B832832701D4 /* TrustKit */ = { + isa = PBXGroup; + children = ( + FF5DC8912CA18313277EF115E15BBE46 /* configuration_utils.h */, + 9144C1D132FDE48C1F12DC01A5CE33EE /* configuration_utils.m */, + BCE910A2438B2E8646192F907B37E559 /* parse_configuration.h */, + 954A0787F64D38E08D65EBC46C1CB9AE /* parse_configuration.m */, + 4CA3CFD14971562FA66B218152D30F35 /* TrustKit.h */, + AF127C3A13A8630FD92058669BDFF057 /* TrustKit.m */, + 91F770C36742BA6AEE960724BEC192F5 /* TSKLog.h */, + B8F41D8A5EA2A86E468C52EA4956695F /* TSKPinningValidator.h */, + C193DFCC3956EDDD7FFE7D491459865D /* TSKPinningValidator.m */, + 51E3CD6B1F3DDAF522B22000B826EF88 /* TSKPinningValidator_Private.h */, + 1A22411D7297D4377E43FD163A4B1314 /* TSKPinningValidatorCallback.h */, + CF6CE1FAD8BB620AAF2879A0AC238BC6 /* TSKPinningValidatorResult.h */, + FFBF768AACDC0BA4BFEF5281933FFAA7 /* TSKPinningValidatorResult.m */, + 5BCBC95D7B4F66D37FBA5785D4040B60 /* TSKTrustDecision.h */, + DEDE844CB2D002704EE36BB2178EB726 /* TSKTrustKitConfig.h */, + 7C64C309645EA64B17D511CF7075A4B5 /* TSKTrustKitConfig.m */, + F1ECC4254DC2A61EF8CF094D38C4FC2F /* Dependencies */, + B34275C890402B959F66E4AE3587D995 /* Framework */, + F30441CCFFF5D7B4BC60250AE07A9A1C /* Pinning */, + 3C83A6AB31F47CA29D795C027611AD7C /* Reporting */, + 95EAADAB5C0C85FD774D5B047C44EF7C /* Swizzling */, + ); + name = TrustKit; + path = TrustKit; + sourceTree = ""; + }; + 62B87C472BE56B2B5272EA220E6D70A0 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + EBE295A7B2F47FC05D6F114036C318DE /* Pods-TrustKitDemo */, + CAD29949506F5214AA1E4195617DEF21 /* Pods-TrustKitDemoInSwift */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + 72DEC79CEFA6BB004F2F8B07DF09B2DE /* registry_tables_genfiles */ = { + isa = PBXGroup; + children = ( + A42809F0183618D391CF0950F836D339 /* registry_tables.h */, + ); + name = registry_tables_genfiles; + path = registry_tables_genfiles; + sourceTree = ""; + }; + 7740D5BE604E7603DC28A50FEDA01593 /* Development Pods */ = { + isa = PBXGroup; + children = ( + C299194C544D4A4F96B68C3B17C88BAE /* TrustKit */, + ); + name = "Development Pods"; + sourceTree = ""; + }; + 7DB346D0F39D3F0E887471402A8071AB = { + isa = PBXGroup; + children = ( + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, + 7740D5BE604E7603DC28A50FEDA01593 /* Development Pods */, + 433CD3331B6C3787F473C941B61FC68F /* Frameworks */, + DC40BB506A8976636DE9686F76485507 /* Products */, + 62B87C472BE56B2B5272EA220E6D70A0 /* Targets Support Files */, + ); + sourceTree = ""; + }; + 950A90C120DA7744EFBCBBF36D729E65 /* private */ = { + isa = PBXGroup; + children = ( + 214B2EAAAB4DEA645B6A1B65D1F2F386 /* assert.c */, + 04C9AAFF75BACF2953B727E819D0D4E4 /* assert.h */, + 49D1BCE0D2BFC86604429C1DB287EEE9 /* init_registry_tables.c */, + DEADB11810EFB649591A4C39A6FCF739 /* registry_search.c */, + 7474D1481157E1DB7C600DA9295073F4 /* registry_types.h */, + 287C7E0B6B509A541BC1525CF4C29A22 /* string_util.h */, + 099F2B4E7779209E04365EE6FE079763 /* trie_node.h */, + 6D8DD333AC5AD1ABEAEDA6B5979E196E /* trie_search.c */, + 103565E750423591CB9BD33EE2F4A32E /* trie_search.h */, + ); + name = private; + path = private; + sourceTree = ""; + }; + 95EAADAB5C0C85FD774D5B047C44EF7C /* Swizzling */ = { + isa = PBXGroup; + children = ( + 2342803E73DA3E02533A2B9B7616F0C7 /* TSKNSURLConnectionDelegateProxy.h */, + 46C0B0ADDA1FAF1CF76BBA4C55725C04 /* TSKNSURLConnectionDelegateProxy.m */, + 72AE90ECEDFD50BADED53717FBB3892D /* TSKNSURLSessionDelegateProxy.h */, + A54DFB58902F4C4023515556B6B09E3B /* TSKNSURLSessionDelegateProxy.m */, + ); + name = Swizzling; + path = Swizzling; + sourceTree = ""; + }; + B34275C890402B959F66E4AE3587D995 /* Framework */ = { + isa = PBXGroup; + children = ( + C781175B244EAD62240FA815899F2C7C /* TrustKit-umbrella.h */, + ); + name = Framework; + path = Framework; + sourceTree = ""; + }; + C299194C544D4A4F96B68C3B17C88BAE /* TrustKit */ = { + isa = PBXGroup; + children = ( + F43D9EAD9EC189005A50ECBE7A95FBAC /* Support Files */, + 5728737062D6BDD3A905B832832701D4 /* TrustKit */, + ); + name = TrustKit; + path = ../..; + sourceTree = ""; + }; + CAD29949506F5214AA1E4195617DEF21 /* Pods-TrustKitDemoInSwift */ = { + isa = PBXGroup; + children = ( + 78E80D386FCEA4D031D86D86D5FA5020 /* Info.plist */, + 3E428D9FBEF4C1CD9DF056ADF8F2E2EB /* Pods-TrustKitDemoInSwift.modulemap */, + 5142815B08CFBAB5C0A32B7DE5259253 /* Pods-TrustKitDemoInSwift-acknowledgements.markdown */, + E801D6CAA748B5F29CB8103D244D1FF6 /* Pods-TrustKitDemoInSwift-acknowledgements.plist */, + 7CBC78276DB001BCA4D874C083A7CF5C /* Pods-TrustKitDemoInSwift-dummy.m */, + 0D17B1927DAF9BE7FD80DCBF0F6F537E /* Pods-TrustKitDemoInSwift-frameworks.sh */, + 9EBB7E04712E8D9773923B6900DE4219 /* Pods-TrustKitDemoInSwift-resources.sh */, + 7F05E7CAB631D2576B200F63D1347D3C /* Pods-TrustKitDemoInSwift-umbrella.h */, + ED54221395BDC8F9A7313801F3269B5E /* Pods-TrustKitDemoInSwift.debug.xcconfig */, + 016D7DEAA7EC198B0F5C41B1FB7C4FB5 /* Pods-TrustKitDemoInSwift.release.xcconfig */, + ); + name = "Pods-TrustKitDemoInSwift"; + path = "Target Support Files/Pods-TrustKitDemoInSwift"; + sourceTree = ""; + }; + D1E3067266D8D7A707815574E5377439 /* RSSwizzle */ = { + isa = PBXGroup; + children = ( + 1863A28B3A36F0B9EE4582862E1056D4 /* RSSwizzle.h */, + 2D825CA602AD8C4F0DC92C5C2F315F80 /* RSSwizzle.m */, + ); + name = RSSwizzle; + path = RSSwizzle; + sourceTree = ""; + }; + DC40BB506A8976636DE9686F76485507 /* Products */ = { + isa = PBXGroup; + children = ( + FA1D62A70B82BE6CD3AA55E6B01CFEE2 /* libPods-TrustKitDemo.a */, + EB5A620362CBE2FB1ECDCE58124DAAD7 /* libTrustKit-library.a */, + 0F24CF15538FFB4930681D9FFAEB7E4A /* Pods_TrustKitDemoInSwift.framework */, + B6357BE7D3B70E53DE8B97D000753691 /* TrustKit.framework */, + ); + name = Products; + sourceTree = ""; + }; + EBE295A7B2F47FC05D6F114036C318DE /* Pods-TrustKitDemo */ = { + isa = PBXGroup; + children = ( + 0123B07E490339BB8BAD0ACF37E6A605 /* Pods-TrustKitDemo-acknowledgements.markdown */, + 09BC6E8AAC9C761AE184F9B5BF603D94 /* Pods-TrustKitDemo-acknowledgements.plist */, + 8D556BFA8C03C6783987680194DA1ABB /* Pods-TrustKitDemo-dummy.m */, + 12AEDEF058A1EB1FA200B4D41EE3D700 /* Pods-TrustKitDemo-frameworks.sh */, + 3ECD098C376738289FBB30A9DF5E9F6E /* Pods-TrustKitDemo-resources.sh */, + F1F1FF3F256A4766D61F73362872F323 /* Pods-TrustKitDemo.debug.xcconfig */, + 693C37A08B6303393A039F004657B2A8 /* Pods-TrustKitDemo.release.xcconfig */, + ); + name = "Pods-TrustKitDemo"; + path = "Target Support Files/Pods-TrustKitDemo"; + sourceTree = ""; + }; + F1ECC4254DC2A61EF8CF094D38C4FC2F /* Dependencies */ = { + isa = PBXGroup; + children = ( + 0FBACCF3F441602CFC8EDB2F2C0521AF /* domain_registry */, + D1E3067266D8D7A707815574E5377439 /* RSSwizzle */, + ); + name = Dependencies; + path = Dependencies; + sourceTree = ""; + }; + F30441CCFFF5D7B4BC60250AE07A9A1C /* Pinning */ = { + isa = PBXGroup; + children = ( + 1B83693BA9D3DD38777DB7CCDD9A5B1A /* ssl_pin_verifier.h */, + 288A1FEC0658D8FB688BBAE9B97E2B7F /* ssl_pin_verifier.m */, + 7D72855E63D1CD2D36950CCBD3D83279 /* TSKPublicKeyAlgorithm.h */, + 30A0A26583325B45FDF95C9957F01F02 /* TSKSPKIHashCache.h */, + 702B9532D5B1159EF60B335D307B9C45 /* TSKSPKIHashCache.m */, + ); + name = Pinning; + path = Pinning; + sourceTree = ""; + }; + F43D9EAD9EC189005A50ECBE7A95FBAC /* Support Files */ = { + isa = PBXGroup; + children = ( + E2073B4A7BB39C0B655B258E2805B398 /* Info.plist */, + 613C850C57E8EE6B749CD2218C6AC1DD /* TrustKit-framework.modulemap */, + 14E737FFF2A1232FF4058D088804405F /* TrustKit-framework.xcconfig */, + DA9D6A4C087F7256F7C1FC0976DEB80E /* TrustKit-framework-dummy.m */, + 8F3CBC263BB58417499B0695F72AE703 /* TrustKit-framework-prefix.pch */, + B4B06F0C8E473C389BA9B4A3A93875B9 /* TrustKit-framework-umbrella.h */, + 3F5091830E79DD11EB3A6934CE0FAF5E /* TrustKit-library.xcconfig */, + C60EE265DEB50B45F4E601CC49B209EC /* TrustKit-library-dummy.m */, + C261E3C0582E5545F335945EA1504529 /* TrustKit-library-prefix.pch */, + ); + name = "Support Files"; + path = "TrustKitDemo/Pods/Target Support Files/TrustKit-framework"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 167F5F7F1F1B77DB8DFB9CD8668D4BE6 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D2FB40D0D6591DD87164BCFDED4A872B /* assert.h in Headers */, + A0B8CEAF67460C1A2D0C2C2A1A990F67 /* configuration_utils.h in Headers */, + 7F4626F2CD390F14D8980C333830EF5D /* domain_registry.h in Headers */, + AD1A357B9A460872C06FAC8C81227E75 /* parse_configuration.h in Headers */, + 54FA5C9512FCE077B16C79562E6003E4 /* registry_tables.h in Headers */, + 09E7DF568807E63A6676DB2AB69939FC /* registry_types.h in Headers */, + 20F2F8326AC10F0EDB95A5C120A42C9C /* reporting_utils.h in Headers */, + 706DFFF92262AD3A654930ADAC7372F7 /* RSSwizzle.h in Headers */, + B769BE1CE6DDDC128E3F160968194EAE /* ssl_pin_verifier.h in Headers */, + 0496FF3E58A206D1EFD7394E6456E700 /* string_util.h in Headers */, + 9D69A757AB397230EF3145ADA6B10188 /* trie_node.h in Headers */, + 789FDC0611E1E6643501301907740355 /* trie_search.h in Headers */, + FF3AB846370F0C93EF33D433416810BB /* TrustKit-framework-umbrella.h in Headers */, + FCAE6A2720A8FA62F35340372D7BE36D /* TrustKit-umbrella.h in Headers */, + FC5DF4AF969196C79FD959A2C8DD2DC2 /* TrustKit.h in Headers */, + E5BDBD5846B9BF559F9D4A790981E21D /* TSKBackgroundReporter.h in Headers */, + 56FF6CCDD07BEC720EFEF68856486006 /* TSKLog.h in Headers */, + F5DB7A40C7181F6A1B0C2C0193786A08 /* TSKNSURLConnectionDelegateProxy.h in Headers */, + 709C87892B7603F4AF2C250980453B11 /* TSKNSURLSessionDelegateProxy.h in Headers */, + 77FB5D09E1047C484301DD8A7CC193E0 /* TSKPinFailureReport.h in Headers */, + BAC36417573685B654AAB2A415F87DCB /* TSKPinningValidator.h in Headers */, + 8BC37767B9A244EE8600DBB66DBECFF6 /* TSKPinningValidator_Private.h in Headers */, + 91645A1F75435D30D1C0258EE9C3DDAE /* TSKPinningValidatorCallback.h in Headers */, + 5CEDC8B868290CC7CC1F128A5C6746E6 /* TSKPinningValidatorResult.h in Headers */, + D5C5AAAD941E06DA3AAB2EB4D02A3F3D /* TSKPublicKeyAlgorithm.h in Headers */, + 72D0920BDCF2DF9583CD74CE0EE4E7E5 /* TSKReportsRateLimiter.h in Headers */, + 912296DA496E76C4592D2BB27525DC58 /* TSKSPKIHashCache.h in Headers */, + 5448F6FAD1663B8B906AD4FEAE95C3BA /* TSKTrustDecision.h in Headers */, + 9C15CA27397D72DBEA030967D4B507E8 /* TSKTrustKitConfig.h in Headers */, + 00851FF4E5F19A667BF0C3D50912A1D5 /* vendor_identifier.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7B16FD05B9E92AFB474A63796FD5DF89 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 96DF97DF055B8C04D3FE99DD46CA0AFB /* Pods-TrustKitDemoInSwift-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9ED12E1A29377B6CEA4CA413059A40EF /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 01D711A6F5D6E57DCCE8946388BEC7EB /* assert.h in Headers */, + F52DAB9D11F101AA30E8466DCFA2D8DB /* configuration_utils.h in Headers */, + 2B886EF817237F7DAD4AC1AB80F7CF87 /* domain_registry.h in Headers */, + 833FAAB1D09078D8B6F7F9EA9516C1C1 /* parse_configuration.h in Headers */, + F30A67B156D94EAD3CA444BE6C6C22A2 /* registry_tables.h in Headers */, + D3C5909E5B8CA9C393309391B4DDF1E0 /* registry_types.h in Headers */, + 1D1B754DB932A2FF2C34C4E5ECBBAB04 /* reporting_utils.h in Headers */, + 2F5C97F11E3C988C45B14C04EF182AFD /* RSSwizzle.h in Headers */, + 219DE7255483B7DDDE0FD5BD790D95A0 /* ssl_pin_verifier.h in Headers */, + BE1276DBEB9F2E09D061430025283640 /* string_util.h in Headers */, + B68CAC1D63D2A0D9BA930C508E48AE57 /* trie_node.h in Headers */, + 49634D47E0EDD9B498FDE2F5F4AA485E /* trie_search.h in Headers */, + B33CE417C5345363CD8E3F45CE44871B /* TrustKit-umbrella.h in Headers */, + 70DF98043F0FC61A986D90A016C32011 /* TrustKit.h in Headers */, + DDA29052B0CEC856600037D272593BBE /* TSKBackgroundReporter.h in Headers */, + 4C904BF58040C4F69A02BB90BDF6A202 /* TSKLog.h in Headers */, + 4550A31BDF3B61E7267B06F119CC6908 /* TSKNSURLConnectionDelegateProxy.h in Headers */, + 5167C676C50CB1FE15039D4BD956F2F8 /* TSKNSURLSessionDelegateProxy.h in Headers */, + 6C33C70511B2472C8E61AE03DEA53DC3 /* TSKPinFailureReport.h in Headers */, + E9DA954AAEAB44DCD066B35D746A8383 /* TSKPinningValidator.h in Headers */, + 557E4214E44510E8A529A1C9AE1C2107 /* TSKPinningValidator_Private.h in Headers */, + AA3DB3E374D6FE6C0D014CDBEED6D2A7 /* TSKPinningValidatorCallback.h in Headers */, + 3A27EAA7E79CBD53890906565E1C0734 /* TSKPinningValidatorResult.h in Headers */, + A818444547D3C148B87A15A0DF7EDA09 /* TSKPublicKeyAlgorithm.h in Headers */, + 173654C8C63381BFAC72BFA6BB2CEBB9 /* TSKReportsRateLimiter.h in Headers */, + DEB1C3B236A303CDBE1A28F1C168BFAC /* TSKSPKIHashCache.h in Headers */, + C99670E8EF0E6428E51A39F0A918EE59 /* TSKTrustDecision.h in Headers */, + 6444B457833221D3A3E74BF00BD47035 /* TSKTrustKitConfig.h in Headers */, + 9AB598D92256251C141732F7A987C16C /* vendor_identifier.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 87D8BFF311952DE9568AECF82DA3DA72 /* Pods-TrustKitDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3F4D087FBB59C0C0031A8210D733DF6C /* Build configuration list for PBXNativeTarget "Pods-TrustKitDemo" */; + buildPhases = ( + 71D5DCE034544BD1275241C3C874D51F /* Sources */, + AA5364440FE74A5EB2549E648523110B /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 07B60ADCA15C20ABCC78424BB0C4DEC2 /* PBXTargetDependency */, + ); + name = "Pods-TrustKitDemo"; + productName = "Pods-TrustKitDemo"; + productReference = FA1D62A70B82BE6CD3AA55E6B01CFEE2 /* libPods-TrustKitDemo.a */; + productType = "com.apple.product-type.library.static"; + }; + 9FEA5104EB31D1BB74EFD21D7B2F5C64 /* TrustKit-library */ = { + isa = PBXNativeTarget; + buildConfigurationList = 14688539BFEE0ED5D00198F0BCE0AEFF /* Build configuration list for PBXNativeTarget "TrustKit-library" */; + buildPhases = ( + B2456AA8D24A0A7B757053678162822B /* Sources */, + 381A1E592BA214AFBCE620C6F2AF4928 /* Frameworks */, + 9ED12E1A29377B6CEA4CA413059A40EF /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "TrustKit-library"; + productName = "TrustKit-library"; + productReference = EB5A620362CBE2FB1ECDCE58124DAAD7 /* libTrustKit-library.a */; + productType = "com.apple.product-type.library.static"; + }; + B83210985026774772228F688CCC95C6 /* TrustKit-framework */ = { + isa = PBXNativeTarget; + buildConfigurationList = E7898298990891B5180B1E6C3AC85B49 /* Build configuration list for PBXNativeTarget "TrustKit-framework" */; + buildPhases = ( + C56F05143613836D4B7B7557756532C3 /* Sources */, + A1A9E0D00EB77C8A9B6F766DABF173ED /* Frameworks */, + 167F5F7F1F1B77DB8DFB9CD8668D4BE6 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "TrustKit-framework"; + productName = "TrustKit-framework"; + productReference = B6357BE7D3B70E53DE8B97D000753691 /* TrustKit.framework */; + productType = "com.apple.product-type.framework"; + }; + D88FD8F36049DD604B8668DFA98C68A5 /* Pods-TrustKitDemoInSwift */ = { + isa = PBXNativeTarget; + buildConfigurationList = DBD04FF12003F172DB8618D055D9FE26 /* Build configuration list for PBXNativeTarget "Pods-TrustKitDemoInSwift" */; + buildPhases = ( + A87B1C6EAB2A5F5B25D35C3F9D5FAD00 /* Sources */, + 2E843DC24B5E78C662AD4501BDF315DE /* Frameworks */, + 7B16FD05B9E92AFB474A63796FD5DF89 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + 4DBD6D901020E32C53A46C24B37A0492 /* PBXTargetDependency */, + ); + name = "Pods-TrustKitDemoInSwift"; + productName = "Pods-TrustKitDemoInSwift"; + productReference = 0F24CF15538FFB4930681D9FFAEB7E4A /* Pods_TrustKitDemoInSwift.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0700; + }; + buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 7DB346D0F39D3F0E887471402A8071AB; + productRefGroup = DC40BB506A8976636DE9686F76485507 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 87D8BFF311952DE9568AECF82DA3DA72 /* Pods-TrustKitDemo */, + D88FD8F36049DD604B8668DFA98C68A5 /* Pods-TrustKitDemoInSwift */, + B83210985026774772228F688CCC95C6 /* TrustKit-framework */, + 9FEA5104EB31D1BB74EFD21D7B2F5C64 /* TrustKit-library */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 71D5DCE034544BD1275241C3C874D51F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 160C63E448CBB115119659EFBF5C84E0 /* Pods-TrustKitDemo-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A87B1C6EAB2A5F5B25D35C3F9D5FAD00 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9AAC9AC75846966966330B3C091B58F5 /* Pods-TrustKitDemoInSwift-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B2456AA8D24A0A7B757053678162822B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EE94D82A953FE4FB494AE05B1FA9AF5 /* assert.c in Sources */, + E9B3C163FC82567554CD8565481A0C08 /* configuration_utils.m in Sources */, + 819293E2A79D94FADDCB7F6ABB9D9D18 /* init_registry_tables.c in Sources */, + 7DF75D25C7A2C29B05D5787938C112ED /* parse_configuration.m in Sources */, + 304C39AEAB37E4B3ADE44D6985DE6AEE /* registry_search.c in Sources */, + 2A0DC2F329F20A507F8EEC15F3472667 /* reporting_utils.m in Sources */, + 1A43A1DCDCD6E62E13B2A735586524D7 /* RSSwizzle.m in Sources */, + AC4668E1382E6BCF1D1C0734115BA63E /* ssl_pin_verifier.m in Sources */, + 10484F688180E9B2CA3F443C47D9A9F5 /* trie_search.c in Sources */, + DBA72238E5490710B7FDFD0BC03C25B2 /* TrustKit-library-dummy.m in Sources */, + A30AFCE398CB19D1AB2F17FFB7015A39 /* TrustKit.m in Sources */, + 41EDB6EBC0B1F4135AC577C87C1C62CC /* TSKBackgroundReporter.m in Sources */, + 48F95C232FF22C9E855EB8DE6C5D3962 /* TSKNSURLConnectionDelegateProxy.m in Sources */, + A56A25BC5E423091DC2564DBD867FDA3 /* TSKNSURLSessionDelegateProxy.m in Sources */, + 7783BE79333283119B7E04ADE59D4D85 /* TSKPinFailureReport.m in Sources */, + 5A6ED2C021536CAD9C5323764E651092 /* TSKPinningValidator.m in Sources */, + 094A47B3A015A857FD9902D9DE4ACDE5 /* TSKPinningValidatorResult.m in Sources */, + E167E2DFDC1302944D5105477C99C410 /* TSKReportsRateLimiter.m in Sources */, + 5B3232D66E17FA89AA1114E851BB1260 /* TSKSPKIHashCache.m in Sources */, + 15F3A1BEBE2F0554D51CAE47B09088A4 /* TSKTrustKitConfig.m in Sources */, + AB6C9A86369041598BEDDB4299A62528 /* vendor_identifier.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C56F05143613836D4B7B7557756532C3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EBB2FD29B682B615CC8D640704424C39 /* assert.c in Sources */, + 919B8CA0E63E044A39600170B1544B6B /* configuration_utils.m in Sources */, + CC0B39B3704D36B21E5ACA9F565FBE9E /* init_registry_tables.c in Sources */, + C9199DCE6F188384A1A3EB729854EBF1 /* parse_configuration.m in Sources */, + D0D9F705F65EEC35793699EE0630E8C3 /* registry_search.c in Sources */, + 36F5F40C0CA5EE29258B2AAD78A8D806 /* reporting_utils.m in Sources */, + 00A1CE305307E9CC1C56E14BD3969596 /* RSSwizzle.m in Sources */, + F005E6B1ED453CEEE86179016DD2E13F /* ssl_pin_verifier.m in Sources */, + 203488EF10B0D10B0854259A0BF35A27 /* trie_search.c in Sources */, + 498E8D9DEB07EB14E153CD47D176A452 /* TrustKit-framework-dummy.m in Sources */, + 0130E26071450C6CBA439373428DC4CF /* TrustKit.m in Sources */, + 29C6CB0A3A3842D9B704E0CC8B834731 /* TSKBackgroundReporter.m in Sources */, + 816D06005E4A6D71BBF791548E785E32 /* TSKNSURLConnectionDelegateProxy.m in Sources */, + 14469A265E74524CBC0448214CDB5A90 /* TSKNSURLSessionDelegateProxy.m in Sources */, + D5430056619E1C24EC30A1E3C5101E4B /* TSKPinFailureReport.m in Sources */, + 61F5F24D2CB7BCE8126273DEBA08CC73 /* TSKPinningValidator.m in Sources */, + A075C1FA71C3000765BE026D38199163 /* TSKPinningValidatorResult.m in Sources */, + EC5EA69669DCCD19AB04FECEE50644F7 /* TSKReportsRateLimiter.m in Sources */, + 345E1E08EB0E9881B89FDF6728B5CD4F /* TSKSPKIHashCache.m in Sources */, + 10C5AC1D48A7A2087833A1EFFCCDD65A /* TSKTrustKitConfig.m in Sources */, + EB6F5E04F094338BBD32B9C9A8715DE8 /* vendor_identifier.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 07B60ADCA15C20ABCC78424BB0C4DEC2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "TrustKit-library"; + target = 9FEA5104EB31D1BB74EFD21D7B2F5C64 /* TrustKit-library */; + targetProxy = 8F8E6B6301DB3263048BCA55327BB7EC /* PBXContainerItemProxy */; + }; + 4DBD6D901020E32C53A46C24B37A0492 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "TrustKit-framework"; + target = B83210985026774772228F688CCC95C6 /* TrustKit-framework */; + targetProxy = 8B87672562EA830C7D0B9598BC3E2568 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 136E01C4BCDC36FB052CBB0C85D48203 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 14E737FFF2A1232FF4058D088804405F /* TrustKit-framework.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/TrustKit-framework/TrustKit-framework-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/TrustKit-framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/TrustKit-framework/TrustKit-framework.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = TrustKit; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 2ED2764514E1BAED3E29ABC71A932FD2 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 016D7DEAA7EC198B0F5C41B1FB7C4FB5 /* Pods-TrustKitDemoInSwift.release.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "Target Support Files/Pods-TrustKitDemoInSwift/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_TrustKitDemoInSwift; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 2F1F1A7A75D75597C5E2D9C0AF168816 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = ED54221395BDC8F9A7313801F3269B5E /* Pods-TrustKitDemoInSwift.debug.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "Target Support Files/Pods-TrustKitDemoInSwift/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_TrustKitDemoInSwift; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 49033EF4FFF5CF59029FA65898307615 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F5091830E79DD11EB3A6934CE0FAF5E /* TrustKit-library.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/TrustKit-library/TrustKit-library-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 4A194430634FD712D30114322D9B2623 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F1F1FF3F256A4766D61F73362872F323 /* Pods-TrustKitDemo.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACH_O_TYPE = staticlib; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 4E487F173E6C9664F4E9E26B9635D23C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = NO; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + ONLY_ACTIVE_ARCH = YES; + PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + 66A3528BF8627746C81ABA098B6CE5A4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F5091830E79DD11EB3A6934CE0FAF5E /* TrustKit-library.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/TrustKit-library/TrustKit-library-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 9FF35976CBBCEA46ABEBBDBA6661BC69 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 14E737FFF2A1232FF4058D088804405F /* TrustKit-framework.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/TrustKit-framework/TrustKit-framework-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/TrustKit-framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/TrustKit-framework/TrustKit-framework.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = TrustKit; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + BA1DC5C4D666AECFF7AEBA258838550E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 693C37A08B6303393A039F004657B2A8 /* Pods-TrustKitDemo.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACH_O_TYPE = staticlib; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + BDD0139D6EB93FA375F887ABD62DAB2E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 14688539BFEE0ED5D00198F0BCE0AEFF /* Build configuration list for PBXNativeTarget "TrustKit-library" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 66A3528BF8627746C81ABA098B6CE5A4 /* Debug */, + 49033EF4FFF5CF59029FA65898307615 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4E487F173E6C9664F4E9E26B9635D23C /* Debug */, + BDD0139D6EB93FA375F887ABD62DAB2E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3F4D087FBB59C0C0031A8210D733DF6C /* Build configuration list for PBXNativeTarget "Pods-TrustKitDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4A194430634FD712D30114322D9B2623 /* Debug */, + BA1DC5C4D666AECFF7AEBA258838550E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DBD04FF12003F172DB8618D055D9FE26 /* Build configuration list for PBXNativeTarget "Pods-TrustKitDemoInSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2F1F1A7A75D75597C5E2D9C0AF168816 /* Debug */, + 2ED2764514E1BAED3E29ABC71A932FD2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E7898298990891B5180B1E6C3AC85B49 /* Build configuration list for PBXNativeTarget "TrustKit-framework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 136E01C4BCDC36FB052CBB0C85D48203 /* Debug */, + 9FF35976CBBCEA46ABEBBDBA6661BC69 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; +} diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.markdown b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.markdown new file mode 100644 index 00000000..c11791ba --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.markdown @@ -0,0 +1,28 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## TrustKit + +The MIT License (MIT) + +Copyright (c) 2015 Data Theorem, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Generated by CocoaPods - https://cocoapods.org diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.plist b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.plist new file mode 100644 index 00000000..c3bf1ca0 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.plist @@ -0,0 +1,60 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2015 Data Theorem, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + License + MIT + Title + TrustKit + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-dummy.m b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-dummy.m new file mode 100644 index 00000000..6c849e9b --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_TrustKitDemo : NSObject +@end +@implementation PodsDummy_Pods_TrustKitDemo +@end diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-frameworks.sh b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-frameworks.sh new file mode 100755 index 00000000..0f29f13c --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-frameworks.sh @@ -0,0 +1,92 @@ +#!/bin/sh +set -e + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # use filter instead of exclude so missing patterns dont' throw errors + echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identitiy + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # Get architectures for current file + archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" + stripped="" + for arch in $archs; do + if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" || exit 1 + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi +} + +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-resources.sh b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-resources.sh new file mode 100755 index 00000000..aed060f0 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-resources.sh @@ -0,0 +1,102 @@ +#!/bin/sh +set -e + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +case "${TARGETED_DEVICE_FAMILY}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + 3) + TARGET_DEVICE_ARGS="--target-device tv" + ;; + 4) + TARGET_DEVICE_ARGS="--target-device watch" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; +esac + +install_resource() +{ + if [[ "$1" = /* ]] ; then + RESOURCE_PATH="$1" + else + RESOURCE_PATH="${PODS_ROOT}/$1" + fi + if [[ ! -e "$RESOURCE_PATH" ]] ; then + cat << EOM +error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. +EOM + exit 1 + fi + case $RESOURCE_PATH in + *.storyboard) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.xib) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.framework) + echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" + xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + *) + echo "$RESOURCE_PATH" + echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" + ;; + esac +} + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] +then + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "${PODS_ROOT}*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.debug.xcconfig b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.debug.xcconfig new file mode 100644 index 00000000..be16e55c --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.debug.xcconfig @@ -0,0 +1,9 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/TrustKit" +LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/TrustKit-library" +OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/TrustKit" +OTHER_LDFLAGS = $(inherited) -ObjC -l"TrustKit-library" -framework "Foundation" -framework "Security" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.release.xcconfig b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.release.xcconfig new file mode 100644 index 00000000..be16e55c --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.release.xcconfig @@ -0,0 +1,9 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/TrustKit" +LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/TrustKit-library" +OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/TrustKit" +OTHER_LDFLAGS = $(inherited) -ObjC -l"TrustKit-library" -framework "Foundation" -framework "Security" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Info.plist b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Info.plist new file mode 100644 index 00000000..2243fe6e --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.markdown b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.markdown new file mode 100644 index 00000000..c11791ba --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.markdown @@ -0,0 +1,28 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## TrustKit + +The MIT License (MIT) + +Copyright (c) 2015 Data Theorem, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Generated by CocoaPods - https://cocoapods.org diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.plist b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.plist new file mode 100644 index 00000000..c3bf1ca0 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.plist @@ -0,0 +1,60 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2015 Data Theorem, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + License + MIT + Title + TrustKit + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-dummy.m b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-dummy.m new file mode 100644 index 00000000..cb7e272e --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_TrustKitDemoInSwift : NSObject +@end +@implementation PodsDummy_Pods_TrustKitDemoInSwift +@end diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-frameworks.sh b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-frameworks.sh new file mode 100755 index 00000000..248b1f75 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-frameworks.sh @@ -0,0 +1,99 @@ +#!/bin/sh +set -e + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # use filter instead of exclude so missing patterns dont' throw errors + echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identitiy + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # Get architectures for current file + archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" + stripped="" + for arch in $archs; do + if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" || exit 1 + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi +} + + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "$BUILT_PRODUCTS_DIR/TrustKit-framework/TrustKit.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "$BUILT_PRODUCTS_DIR/TrustKit-framework/TrustKit.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-resources.sh b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-resources.sh new file mode 100755 index 00000000..aed060f0 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-resources.sh @@ -0,0 +1,102 @@ +#!/bin/sh +set -e + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +case "${TARGETED_DEVICE_FAMILY}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + 3) + TARGET_DEVICE_ARGS="--target-device tv" + ;; + 4) + TARGET_DEVICE_ARGS="--target-device watch" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; +esac + +install_resource() +{ + if [[ "$1" = /* ]] ; then + RESOURCE_PATH="$1" + else + RESOURCE_PATH="${PODS_ROOT}/$1" + fi + if [[ ! -e "$RESOURCE_PATH" ]] ; then + cat << EOM +error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. +EOM + exit 1 + fi + case $RESOURCE_PATH in + *.storyboard) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.xib) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.framework) + echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" + xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + *) + echo "$RESOURCE_PATH" + echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" + ;; + esac +} + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] +then + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "${PODS_ROOT}*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-umbrella.h b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-umbrella.h new file mode 100644 index 00000000..e59c252e --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_TrustKitDemoInSwiftVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_TrustKitDemoInSwiftVersionString[]; + diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.debug.xcconfig b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.debug.xcconfig new file mode 100644 index 00000000..0716a23b --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.debug.xcconfig @@ -0,0 +1,9 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/TrustKit-framework" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/TrustKit-framework/TrustKit.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "TrustKit" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.modulemap b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.modulemap new file mode 100644 index 00000000..9097037a --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.modulemap @@ -0,0 +1,6 @@ +framework module Pods_TrustKitDemoInSwift { + umbrella header "Pods-TrustKitDemoInSwift-umbrella.h" + + export * + module * { export * } +} diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.release.xcconfig b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.release.xcconfig new file mode 100644 index 00000000..0716a23b --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.release.xcconfig @@ -0,0 +1,9 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/TrustKit-framework" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/TrustKit-framework/TrustKit.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "TrustKit" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/Info.plist b/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/Info.plist new file mode 100644 index 00000000..a7a6daf3 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.5.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework-dummy.m b/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework-dummy.m new file mode 100644 index 00000000..2fa8b4c5 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_TrustKit_framework : NSObject +@end +@implementation PodsDummy_TrustKit_framework +@end diff --git a/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework-prefix.pch b/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework-umbrella.h b/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework-umbrella.h new file mode 100644 index 00000000..ff3e6bf0 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework-umbrella.h @@ -0,0 +1,22 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "TrustKit.h" +#import "TSKTrustKitConfig.h" +#import "TSKPinningValidator.h" +#import "TSKPinningValidatorCallback.h" +#import "TSKPinningValidatorResult.h" +#import "TSKTrustDecision.h" + +FOUNDATION_EXPORT double TrustKitVersionNumber; +FOUNDATION_EXPORT const unsigned char TrustKitVersionString[]; + diff --git a/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework.modulemap b/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework.modulemap new file mode 100644 index 00000000..3184d418 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework.modulemap @@ -0,0 +1,6 @@ +framework module TrustKit { + umbrella header "TrustKit-framework-umbrella.h" + + export * + module * { export * } +} diff --git a/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework.xcconfig b/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework.xcconfig new file mode 100644 index 00000000..bb9c9027 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/TrustKit-framework/TrustKit-framework.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/TrustKit-framework +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/TrustKit" +OTHER_LDFLAGS = -framework "Foundation" -framework "Security" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/TrustKitDemo/Pods/Target Support Files/TrustKit-library/TrustKit-library-dummy.m b/TrustKitDemo/Pods/Target Support Files/TrustKit-library/TrustKit-library-dummy.m new file mode 100644 index 00000000..939bf674 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/TrustKit-library/TrustKit-library-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_TrustKit_library : NSObject +@end +@implementation PodsDummy_TrustKit_library +@end diff --git a/TrustKitDemo/Pods/Target Support Files/TrustKit-library/TrustKit-library-prefix.pch b/TrustKitDemo/Pods/Target Support Files/TrustKit-library/TrustKit-library-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/TrustKit-library/TrustKit-library-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/TrustKitDemo/Pods/Target Support Files/TrustKit-library/TrustKit-library.xcconfig b/TrustKitDemo/Pods/Target Support Files/TrustKit-library/TrustKit-library.xcconfig new file mode 100644 index 00000000..ee122379 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/TrustKit-library/TrustKit-library.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/TrustKit-library +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/TrustKit" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/TrustKit" +OTHER_LDFLAGS = -framework "Foundation" -framework "Security" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj b/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj index 36731b7f..da6bc592 100644 --- a/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj +++ b/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj @@ -18,67 +18,10 @@ 1942311D1EF9658800029B04 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1942311C1EF9658800029B04 /* Assets.xcassets */; }; 194231201EF9658800029B04 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1942311E1EF9658800029B04 /* LaunchScreen.storyboard */; }; 194231271EF9663F00029B04 /* MainStoryboardForSwift.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 194231261EF9663F00029B04 /* MainStoryboardForSwift.storyboard */; }; + 5C1DE70282AFDA569285EAC8 /* libPods-TrustKitDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ED236495884111AC895A025E /* libPods-TrustKitDemo.a */; }; + F563B5060C50A25EA583DA9A /* Pods_TrustKitDemoInSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F77E736E3DC0DB307912893 /* Pods_TrustKitDemoInSwift.framework */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 19ECC2621F028C9800D81F79 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 19ECC2571F028C9800D81F79 /* TrustKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8C8480471A896EE30017C155; - remoteInfo = TrustKit; - }; - 19ECC2641F028C9800D81F79 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 19ECC2571F028C9800D81F79 /* TrustKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8C8716961B23A91D00267E1D; - remoteInfo = TrustKit_Static; - }; - 19ECC2661F028C9800D81F79 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 19ECC2571F028C9800D81F79 /* TrustKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8C8480521A896EE30017C155; - remoteInfo = TrustKitTests; - }; - 19ECC2681F028C9800D81F79 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 19ECC2571F028C9800D81F79 /* TrustKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8CA6CBFD1BAE2ADD00BDA419; - remoteInfo = "TrustKit OS X"; - }; - 19ECC26A1F028C9800D81F79 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 19ECC2571F028C9800D81F79 /* TrustKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8CA6CC061BAE2ADD00BDA419; - remoteInfo = "TrustKit OS XTests"; - }; - 19ECC26C1F028C9800D81F79 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 19ECC2571F028C9800D81F79 /* TrustKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8C84CBB21D6E0981009B3E7D; - remoteInfo = "TrustKit tvOS"; - }; - 19ECC26E1F028C9800D81F79 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 19ECC2571F028C9800D81F79 /* TrustKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8C84CBDD1D6E1718009B3E7D; - remoteInfo = "TrustKit tvOS Tests"; - }; - 19ECC2701F028C9800D81F79 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 19ECC2571F028C9800D81F79 /* TrustKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8CC5D24E1D6E64D10074F515; - remoteInfo = "TrustKit watchOS"; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 1942310A1EF8431400029B04 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -93,6 +36,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 03B1C8789B008BB8F706D702 /* Pods-TrustKitDemoInSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemoInSwift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.debug.xcconfig"; sourceTree = ""; }; 1942309D1EF7C91A00029B04 /* TrustKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TrustKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 194230A11EF7C91A00029B04 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 194230A31EF7C91A00029B04 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -110,7 +54,11 @@ 1942311F1EF9658800029B04 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 194231211EF9658800029B04 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 194231261EF9663F00029B04 /* MainStoryboardForSwift.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainStoryboardForSwift.storyboard; sourceTree = ""; }; - 19ECC2571F028C9800D81F79 /* TrustKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TrustKit.xcodeproj; path = ../TrustKit.xcodeproj; sourceTree = ""; }; + 229FD9B70132921DEF151965 /* Pods-TrustKitDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.debug.xcconfig"; sourceTree = ""; }; + 756D66A433A7A1257803FD3D /* Pods-TrustKitDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.release.xcconfig"; sourceTree = ""; }; + 9F77E736E3DC0DB307912893 /* Pods_TrustKitDemoInSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TrustKitDemoInSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EC7EA2F77D7200BFD66DB095 /* Pods-TrustKitDemoInSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemoInSwift.release.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.release.xcconfig"; sourceTree = ""; }; + ED236495884111AC895A025E /* libPods-TrustKitDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-TrustKitDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -118,6 +66,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5C1DE70282AFDA569285EAC8 /* libPods-TrustKitDemo.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -125,6 +74,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + F563B5060C50A25EA583DA9A /* Pods_TrustKitDemoInSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -134,10 +84,11 @@ 194230941EF7C91A00029B04 = { isa = PBXGroup; children = ( - 19ECC2571F028C9800D81F79 /* TrustKit.xcodeproj */, 1942309F1EF7C91A00029B04 /* TrustKitDemo */, 194231141EF9658800029B04 /* TrustKitDemoInSwift */, 1942309E1EF7C91A00029B04 /* Products */, + D967AF3E12D8D8B5235F2256 /* Pods */, + F58942AA5E9A3C720DDF8006 /* Frameworks */, ); sourceTree = ""; }; @@ -187,19 +138,24 @@ path = TrustKitDemoInSwift; sourceTree = ""; }; - 19ECC2581F028C9800D81F79 /* Products */ = { + D967AF3E12D8D8B5235F2256 /* Pods */ = { isa = PBXGroup; children = ( - 19ECC2631F028C9800D81F79 /* TrustKit.framework */, - 19ECC2651F028C9800D81F79 /* libTrustKit_Static.a */, - 19ECC2671F028C9800D81F79 /* TrustKitTests.xctest */, - 19ECC2691F028C9800D81F79 /* TrustKit.framework */, - 19ECC26B1F028C9800D81F79 /* TrustKit OS XTests.xctest */, - 19ECC26D1F028C9800D81F79 /* TrustKit.framework */, - 19ECC26F1F028C9800D81F79 /* TrustKit tvOS Tests.xctest */, - 19ECC2711F028C9800D81F79 /* TrustKit.framework */, + 229FD9B70132921DEF151965 /* Pods-TrustKitDemo.debug.xcconfig */, + 756D66A433A7A1257803FD3D /* Pods-TrustKitDemo.release.xcconfig */, + 03B1C8789B008BB8F706D702 /* Pods-TrustKitDemoInSwift.debug.xcconfig */, + EC7EA2F77D7200BFD66DB095 /* Pods-TrustKitDemoInSwift.release.xcconfig */, ); - name = Products; + name = Pods; + sourceTree = ""; + }; + F58942AA5E9A3C720DDF8006 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ED236495884111AC895A025E /* libPods-TrustKitDemo.a */, + 9F77E736E3DC0DB307912893 /* Pods_TrustKitDemoInSwift.framework */, + ); + name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ @@ -209,10 +165,13 @@ isa = PBXNativeTarget; buildConfigurationList = 194230BF1EF7C91B00029B04 /* Build configuration list for PBXNativeTarget "TrustKitDemo" */; buildPhases = ( + FE484BC2D3DE5A98FB396530 /* [CP] Check Pods Manifest.lock */, 194230991EF7C91A00029B04 /* Sources */, 1942309A1EF7C91A00029B04 /* Frameworks */, 1942309B1EF7C91A00029B04 /* Resources */, 1942310A1EF8431400029B04 /* Embed Frameworks */, + F3519FE17357D96DE9D6DC8F /* [CP] Embed Pods Frameworks */, + B0B8C588FDD7DE19BF5403D9 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -227,9 +186,12 @@ isa = PBXNativeTarget; buildConfigurationList = 194231221EF9658800029B04 /* Build configuration list for PBXNativeTarget "TrustKitDemoInSwift" */; buildPhases = ( + 8CA07DBE8A9BC5BDBC638FC6 /* [CP] Check Pods Manifest.lock */, 1942310F1EF9658800029B04 /* Sources */, 194231101EF9658800029B04 /* Frameworks */, 194231111EF9658800029B04 /* Resources */, + BBC1A58CA4B22D26E361B87D /* [CP] Embed Pods Frameworks */, + 4103682D359FAAA8C92466D1 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -273,12 +235,6 @@ mainGroup = 194230941EF7C91A00029B04; productRefGroup = 1942309E1EF7C91A00029B04 /* Products */; projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 19ECC2581F028C9800D81F79 /* Products */; - ProjectRef = 19ECC2571F028C9800D81F79 /* TrustKit.xcodeproj */; - }, - ); projectRoot = ""; targets = ( 1942309C1EF7C91A00029B04 /* TrustKitDemo */, @@ -287,65 +243,6 @@ }; /* End PBXProject section */ -/* Begin PBXReferenceProxy section */ - 19ECC2631F028C9800D81F79 /* TrustKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = TrustKit.framework; - remoteRef = 19ECC2621F028C9800D81F79 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 19ECC2651F028C9800D81F79 /* libTrustKit_Static.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libTrustKit_Static.a; - remoteRef = 19ECC2641F028C9800D81F79 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 19ECC2671F028C9800D81F79 /* TrustKitTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = TrustKitTests.xctest; - remoteRef = 19ECC2661F028C9800D81F79 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 19ECC2691F028C9800D81F79 /* TrustKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = TrustKit.framework; - remoteRef = 19ECC2681F028C9800D81F79 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 19ECC26B1F028C9800D81F79 /* TrustKit OS XTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = "TrustKit OS XTests.xctest"; - remoteRef = 19ECC26A1F028C9800D81F79 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 19ECC26D1F028C9800D81F79 /* TrustKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = TrustKit.framework; - remoteRef = 19ECC26C1F028C9800D81F79 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 19ECC26F1F028C9800D81F79 /* TrustKit tvOS Tests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = "TrustKit tvOS Tests.xctest"; - remoteRef = 19ECC26E1F028C9800D81F79 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 19ECC2711F028C9800D81F79 /* TrustKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = TrustKit.framework; - remoteRef = 19ECC2701F028C9800D81F79 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - /* Begin PBXResourcesBuildPhase section */ 1942309B1EF7C91A00029B04 /* Resources */ = { isa = PBXResourcesBuildPhase; @@ -369,6 +266,99 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 4103682D359FAAA8C92466D1 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 8CA07DBE8A9BC5BDBC638FC6 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + B0B8C588FDD7DE19BF5403D9 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + BBC1A58CA4B22D26E361B87D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + F3519FE17357D96DE9D6DC8F /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + FE484BC2D3DE5A98FB396530 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 194230991EF7C91A00029B04 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -511,6 +501,7 @@ }; 194230C01EF7C91B00029B04 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 229FD9B70132921DEF151965 /* Pods-TrustKitDemo.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = B2EWK89Q2H; @@ -525,6 +516,7 @@ }; 194230C11EF7C91B00029B04 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 756D66A433A7A1257803FD3D /* Pods-TrustKitDemo.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = B2EWK89Q2H; @@ -539,6 +531,7 @@ }; 194231231EF9658800029B04 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 03B1C8789B008BB8F706D702 /* Pods-TrustKitDemoInSwift.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = B2EWK89Q2H; @@ -556,6 +549,7 @@ }; 194231241EF9658800029B04 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = EC7EA2F77D7200BFD66DB095 /* Pods-TrustKitDemoInSwift.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = B2EWK89Q2H; diff --git a/TrustKitDemo/TrustKitDemo.xcworkspace/contents.xcworkspacedata b/TrustKitDemo/TrustKitDemo.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..b61dec21 --- /dev/null +++ b/TrustKitDemo/TrustKitDemo.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + From 1d67ffac1f3a7d11194f3a434cce1be6d5aeaf9b Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Sat, 8 Jul 2017 15:52:39 -0400 Subject: [PATCH 120/126] Minor code scrub/simplification for demo app (Obj-C) --- TrustKitDemo/TrustKitDemo/AppDelegate.h | 3 +- TrustKitDemo/TrustKitDemo/AppDelegate.m | 6 +- .../TrustKitDemo/Base.lproj/Main.storyboard | 84 ++++++++++--------- TrustKitDemo/TrustKitDemo/ViewController.h | 2 + TrustKitDemo/TrustKitDemo/ViewController.m | 82 ++++++++---------- 5 files changed, 88 insertions(+), 89 deletions(-) diff --git a/TrustKitDemo/TrustKitDemo/AppDelegate.h b/TrustKitDemo/TrustKitDemo/AppDelegate.h index 3153d0d2..1f38c913 100644 --- a/TrustKitDemo/TrustKitDemo/AppDelegate.h +++ b/TrustKitDemo/TrustKitDemo/AppDelegate.h @@ -13,8 +13,9 @@ @interface AppDelegate : UIResponder -@property (strong, nonatomic) UIWindow *window; +@property (nonatomic) UIWindow *window; +@property (nonatomic, readonly) NSDictionary *trustKitConfig; @end diff --git a/TrustKitDemo/TrustKitDemo/AppDelegate.m b/TrustKitDemo/TrustKitDemo/AppDelegate.m index f1be1170..71703329 100644 --- a/TrustKitDemo/TrustKitDemo/AppDelegate.m +++ b/TrustKitDemo/TrustKitDemo/AppDelegate.m @@ -32,7 +32,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [TrustKit setLoggerBlock:loggerBlock]; // Initialize TrustKit - NSDictionary *trustKitConfig = + _trustKitConfig = @{ // Do not auto-swizzle NSURLSession delegates kTSKSwizzleNetworkDelegates: @NO, @@ -69,10 +69,10 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( ] }}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + [TrustKit initializeWithConfiguration:_trustKitConfig]; // Demonstrate how to receive pin validation notifications (only useful for performance/metrics) - [TrustKit sharedInstance].pinningValidatorCallbackQueue =dispatch_get_main_queue(); + [TrustKit sharedInstance].pinningValidatorCallbackQueue = dispatch_get_main_queue(); [TrustKit sharedInstance].pinningValidatorCallback = ^(TSKPinningValidatorResult *result, NSString *notedHostname, TKSDomainPinningPolicy *policy) { NSLog(@"Received pinning validation notification:\n\tDuration: %0.4f\n\tDecision: %ld\n\tResult: %ld\n\tHostname: %@", result.validationDuration, diff --git a/TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard b/TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard index 139d996a..e896b17c 100644 --- a/TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard +++ b/TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard @@ -1,12 +1,12 @@ - - + + - - + + @@ -19,49 +19,55 @@ - + - - + + + + + + + + - - - - - - - - + + + + + + diff --git a/TrustKitDemo/TrustKitDemo/ViewController.h b/TrustKitDemo/TrustKitDemo/ViewController.h index ba82698b..220b7487 100644 --- a/TrustKitDemo/TrustKitDemo/ViewController.h +++ b/TrustKitDemo/TrustKitDemo/ViewController.h @@ -15,6 +15,8 @@ @property (weak, nonatomic) IBOutlet UIButton *invalidPinBtn; @property (weak, nonatomic) IBOutlet UIButton *validPinBtn; +@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator; +@property (weak, nonatomic) IBOutlet UIWebView *destinationWebView; @end diff --git a/TrustKitDemo/TrustKitDemo/ViewController.m b/TrustKitDemo/TrustKitDemo/ViewController.m index 8667b008..675c6672 100644 --- a/TrustKitDemo/TrustKitDemo/ViewController.m +++ b/TrustKitDemo/TrustKitDemo/ViewController.m @@ -13,20 +13,17 @@ #import #import +static NSString *const baseURLYahoo = @"https://www.yahoo.com/"; +static NSString *const baseURLDT = @"https://www.datatheorem.com/"; + @interface ViewController () -{ - UIActivityIndicatorView *activityIndicator; -} -@property (nonatomic, strong) NSURLSession *session; +@property (nonatomic) NSURLSession *session; @end @implementation ViewController -static NSString *const baseURLYahoo = @"https://www.yahoo.com/"; -static NSString *const baseURLDT = @"https://www.datatheorem.com/"; - - (void)viewDidLoad { [super viewDidLoad]; @@ -34,20 +31,26 @@ - (void)viewDidLoad self.invalidPinBtn.layer.cornerRadius = 4; self.validPinBtn.layer.cornerRadius = 4; - self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] delegate:self delegateQueue:nil]; - - activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] + delegate:self + delegateQueue:NSOperationQueue.mainQueue]; } +#pragma mark TrustKit Pinning Reference -- (void)loadUrlWithPinningFailure: (NSURL *)url +- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler { - // Load a URL with a bad pinning configuration to demonstrate a pinning failure with a report being sent - NSURLSessionDataTask *task = [self.session dataTaskWithURL:url]; - [task resume]; - [self showActivityIndicatorInCurrentViewController]; + // Call into TrustKit here to do pinning validation + if (![TrustKit.sharedInstance.pinningValidator handleChallenge:challenge completionHandler:completionHandler]) + { + // TrustKit did not handle this challenge: perhaps it was not for server trust + // or the domain was not pinned. Fall back to the default behavior + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } } +#pragma mark Test Control + - (void)loadUrl:(NSURL *)url { // Show loading view @@ -56,17 +59,13 @@ - (void)loadUrl:(NSURL *)url // Load a URL with a good pinning configuration NSURLSessionDataTask *task = [self.session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - if (!error) { - // Display Success Alert - dispatch_async(dispatch_get_main_queue(), ^{ - [self displayAlertWithTitle:@"Test Result" andMessage:[NSString stringWithFormat:@"Pinning validation succeeded for %@", [url absoluteString]]]; - }); + if (error) { + // Display Error Alert + [self displayAlertWithTitle:@"Test Result" messageFormat:@"Pinning validation failed for %@\n\n%@", url.absoluteString, error.description]; } else { - // Display Error Alert - dispatch_async(dispatch_get_main_queue(), ^{ - [self displayAlertWithTitle:@"Test Result" andMessage:[NSString stringWithFormat:@"Pinning validation failed for [%@] : [%@]", [url absoluteString], error.description]]; - }); + // Display Success Alert + [self displayAlertWithTitle:@"Test Result" messageFormat:@"Pinning validation succeeded for %@", url.absoluteString]; } }]; [task resume]; @@ -82,42 +81,33 @@ - (IBAction)testValidPinning:(UIButton *)sender [self loadUrl:[NSURL URLWithString:baseURLDT]]; } -- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler -{ - // Call into TrustKit here to do pinning validation - if (![[TrustKit sharedInstance].pinningValidator handleChallenge:challenge completionHandler:completionHandler]) - { - // TrustKit did not handle this challenge: perhaps it was not for server trust - // or the domain was not pinned. Fall back to the default behavior - completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); - } -} - -- (void)displayAlertWithTitle:(NSString *)title andMessage:(NSString *)message +- (void)displayAlertWithTitle:(NSString *)title messageFormat:(NSString *)format, ... { // Hide Activity Indicator [self hideActivityIndicator]; - UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; + va_list args; + va_start(args, format); + NSString *message = [[NSString alloc] initWithFormat:format arguments:args]; + va_end(args); + + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title + message:message + preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; [self presentViewController:alertController animated:YES completion:nil]; } - (void)showActivityIndicatorInCurrentViewController { - [self.view setUserInteractionEnabled:NO]; - if (![activityIndicator isAnimating]) { - activityIndicator.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/2 + 100); - [self.view addSubview:activityIndicator]; - [activityIndicator startAnimating]; - } + self.view.userInteractionEnabled = NO; + [self.activityIndicator startAnimating]; } - (void)hideActivityIndicator { - [self.view setUserInteractionEnabled:YES]; - [activityIndicator stopAnimating]; - [activityIndicator removeFromSuperview]; + self.view.userInteractionEnabled = YES; + [self.activityIndicator stopAnimating]; } @end From 3e9110565bf0b178b7e524eaf3dc70dd63ce34be Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Sat, 8 Jul 2017 17:05:56 -0400 Subject: [PATCH 121/126] Code scrub/simplification for demo app (Swift) --- .../TrustKitDemo.xcodeproj/project.pbxproj | 32 +++-- .../AppIcon.appiconset/Contents.json | 15 +++ .../TrustKitDemo/Base.lproj/Main.storyboard | 10 +- .../TrustKitDemoInSwift/AppDelegate.swift | 5 +- .../AppIcon.appiconset/Contents.json | 5 + .../DTViewController.swift | 114 ++++++++---------- .../{Base.lproj => }/LaunchScreen.storyboard | 0 .../MainStoryboardForSwift.storyboard | 80 ++++++------ 8 files changed, 139 insertions(+), 122 deletions(-) rename TrustKitDemo/TrustKitDemoInSwift/{Base.lproj => }/LaunchScreen.storyboard (100%) diff --git a/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj b/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj index da6bc592..10140c7e 100644 --- a/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj +++ b/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj @@ -16,7 +16,6 @@ 194231161EF9658800029B04 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194231151EF9658800029B04 /* AppDelegate.swift */; }; 194231181EF9658800029B04 /* DTViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194231171EF9658800029B04 /* DTViewController.swift */; }; 1942311D1EF9658800029B04 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1942311C1EF9658800029B04 /* Assets.xcassets */; }; - 194231201EF9658800029B04 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1942311E1EF9658800029B04 /* LaunchScreen.storyboard */; }; 194231271EF9663F00029B04 /* MainStoryboardForSwift.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 194231261EF9663F00029B04 /* MainStoryboardForSwift.storyboard */; }; 5C1DE70282AFDA569285EAC8 /* libPods-TrustKitDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ED236495884111AC895A025E /* libPods-TrustKitDemo.a */; }; F563B5060C50A25EA583DA9A /* Pods_TrustKitDemoInSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F77E736E3DC0DB307912893 /* Pods_TrustKitDemoInSwift.framework */; }; @@ -51,14 +50,14 @@ 194231151EF9658800029B04 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 194231171EF9658800029B04 /* DTViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DTViewController.swift; sourceTree = ""; }; 1942311C1EF9658800029B04 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 1942311F1EF9658800029B04 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 194231211EF9658800029B04 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 194231261EF9663F00029B04 /* MainStoryboardForSwift.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainStoryboardForSwift.storyboard; sourceTree = ""; }; 229FD9B70132921DEF151965 /* Pods-TrustKitDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.debug.xcconfig"; sourceTree = ""; }; 756D66A433A7A1257803FD3D /* Pods-TrustKitDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.release.xcconfig"; sourceTree = ""; }; 9F77E736E3DC0DB307912893 /* Pods_TrustKitDemoInSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TrustKitDemoInSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EC7EA2F77D7200BFD66DB095 /* Pods-TrustKitDemoInSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemoInSwift.release.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.release.xcconfig"; sourceTree = ""; }; ED236495884111AC895A025E /* libPods-TrustKitDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-TrustKitDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + FCB0DD861F117CBE00741410 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + FCB0DD881F117D4C00741410 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -108,7 +107,6 @@ 194230A41EF7C91A00029B04 /* AppDelegate.m */, 194230A61EF7C91A00029B04 /* ViewController.h */, 194230A71EF7C91A00029B04 /* ViewController.m */, - 194230AC1EF7C91A00029B04 /* Assets.xcassets */, 194230A01EF7C91A00029B04 /* Supporting Files */, ); path = TrustKitDemo; @@ -120,6 +118,7 @@ 194230B11EF7C91A00029B04 /* Info.plist */, 194230AE1EF7C91A00029B04 /* LaunchScreen.storyboard */, 194230A91EF7C91A00029B04 /* Main.storyboard */, + 194230AC1EF7C91A00029B04 /* Assets.xcassets */, 194230A11EF7C91A00029B04 /* main.m */, ); name = "Supporting Files"; @@ -130,10 +129,7 @@ children = ( 194231151EF9658800029B04 /* AppDelegate.swift */, 194231171EF9658800029B04 /* DTViewController.swift */, - 194231261EF9663F00029B04 /* MainStoryboardForSwift.storyboard */, - 1942311C1EF9658800029B04 /* Assets.xcassets */, - 1942311E1EF9658800029B04 /* LaunchScreen.storyboard */, - 194231211EF9658800029B04 /* Info.plist */, + FCB0DD871F117CCD00741410 /* Supporting FIles */, ); path = TrustKitDemoInSwift; sourceTree = ""; @@ -158,6 +154,17 @@ name = Frameworks; sourceTree = ""; }; + FCB0DD871F117CCD00741410 /* Supporting FIles */ = { + isa = PBXGroup; + children = ( + FCB0DD861F117CBE00741410 /* Info.plist */, + FCB0DD881F117D4C00741410 /* LaunchScreen.storyboard */, + 194231261EF9663F00029B04 /* MainStoryboardForSwift.storyboard */, + 1942311C1EF9658800029B04 /* Assets.xcassets */, + ); + name = "Supporting FIles"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -258,7 +265,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 194231201EF9658800029B04 /* LaunchScreen.storyboard in Resources */, 1942311D1EF9658800029B04 /* Assets.xcassets in Resources */, 194231271EF9663F00029B04 /* MainStoryboardForSwift.storyboard in Resources */, ); @@ -398,14 +404,6 @@ name = LaunchScreen.storyboard; sourceTree = ""; }; - 1942311E1EF9658800029B04 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 1942311F1EF9658800029B04 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ diff --git a/TrustKitDemo/TrustKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/TrustKitDemo/TrustKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json index 118c98f7..19882d56 100644 --- a/TrustKitDemo/TrustKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/TrustKitDemo/TrustKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,5 +1,15 @@ { "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, { "idiom" : "iphone", "size" : "29x29", @@ -29,6 +39,11 @@ "idiom" : "iphone", "size" : "60x60", "scale" : "3x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" } ], "info" : { diff --git a/TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard b/TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard index e896b17c..00985cea 100644 --- a/TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard +++ b/TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard @@ -22,8 +22,8 @@ - - + + - + + + + + + + + - - - - - - - - + + + + + + From b7c971d21e5d2d5a23170cb549f8510b93354a17 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Sat, 8 Jul 2017 17:12:37 -0400 Subject: [PATCH 122/126] Consistent target naming for swift and obj-c demos --- TrustKitDemo/Podfile | 8 +- TrustKitDemo/Podfile.lock | 2 +- .../AppDelegate.h | 0 .../AppDelegate.m | 0 .../AppIcon.appiconset/Contents.json | 0 .../Base.lproj/LaunchScreen.storyboard | 0 .../Base.lproj/Main.storyboard | 0 .../Info.plist | 0 .../ViewController.h | 0 .../ViewController.m | 0 .../main.m | 0 .../AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../DTViewController.swift | 0 .../Info.plist | 2 +- .../LaunchScreen.storyboard | 0 .../Main.storyboard} | 0 .../TrustKitDemo.xcodeproj/project.pbxproj | 100 +++++++++-------- .../xcschemes/TrustKitDemo-ObjC.xcscheme | 101 ++++++++++++++++++ .../xcschemes/TrustKitDemo-Swift.xcscheme | 91 ++++++++++++++++ 20 files changed, 256 insertions(+), 48 deletions(-) rename TrustKitDemo/{TrustKitDemo => TrustKitDemo-ObjC}/AppDelegate.h (100%) rename TrustKitDemo/{TrustKitDemo => TrustKitDemo-ObjC}/AppDelegate.m (100%) rename TrustKitDemo/{TrustKitDemo => TrustKitDemo-ObjC}/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename TrustKitDemo/{TrustKitDemo => TrustKitDemo-ObjC}/Base.lproj/LaunchScreen.storyboard (100%) rename TrustKitDemo/{TrustKitDemo => TrustKitDemo-ObjC}/Base.lproj/Main.storyboard (100%) rename TrustKitDemo/{TrustKitDemo => TrustKitDemo-ObjC}/Info.plist (100%) rename TrustKitDemo/{TrustKitDemo => TrustKitDemo-ObjC}/ViewController.h (100%) rename TrustKitDemo/{TrustKitDemo => TrustKitDemo-ObjC}/ViewController.m (100%) rename TrustKitDemo/{TrustKitDemo => TrustKitDemo-ObjC}/main.m (100%) rename TrustKitDemo/{TrustKitDemoInSwift => TrustKitDemo-Swift}/AppDelegate.swift (100%) rename TrustKitDemo/{TrustKitDemoInSwift => TrustKitDemo-Swift}/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename TrustKitDemo/{TrustKitDemoInSwift => TrustKitDemo-Swift}/DTViewController.swift (100%) rename TrustKitDemo/{TrustKitDemoInSwift => TrustKitDemo-Swift}/Info.plist (96%) rename TrustKitDemo/{TrustKitDemoInSwift => TrustKitDemo-Swift}/LaunchScreen.storyboard (100%) rename TrustKitDemo/{TrustKitDemoInSwift/MainStoryboardForSwift.storyboard => TrustKitDemo-Swift/Main.storyboard} (100%) create mode 100644 TrustKitDemo/TrustKitDemo.xcodeproj/xcshareddata/xcschemes/TrustKitDemo-ObjC.xcscheme create mode 100644 TrustKitDemo/TrustKitDemo.xcodeproj/xcshareddata/xcschemes/TrustKitDemo-Swift.xcscheme diff --git a/TrustKitDemo/Podfile b/TrustKitDemo/Podfile index c50addf1..ec9339b9 100644 --- a/TrustKitDemo/Podfile +++ b/TrustKitDemo/Podfile @@ -1,7 +1,7 @@ # Uncomment the next line to define a global platform for your project -# platform :ios, '9.0' +platform :ios, '9.0' -target 'TrustKitDemo' do +target 'TrustKitDemo-ObjC' do # Uncomment the next line if you're using Swift or would like to use dynamic frameworks # use_frameworks! @@ -11,11 +11,11 @@ target 'TrustKitDemo' do end -target 'TrustKitDemoInSwift' do +target 'TrustKitDemo-Swift' do # Uncomment the next line if you're using Swift or would like to use dynamic frameworks use_frameworks! # Pods for TrustKitDemo pod 'TrustKit', :path => '../' -end \ No newline at end of file +end diff --git a/TrustKitDemo/Podfile.lock b/TrustKitDemo/Podfile.lock index d75a91f9..2b6b6caa 100644 --- a/TrustKitDemo/Podfile.lock +++ b/TrustKitDemo/Podfile.lock @@ -11,6 +11,6 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: TrustKit: 59a0878569585d9b1adb7d76c341426deb57a5b8 -PODFILE CHECKSUM: 86e19c8ce4662ee6d0981982609efb580466dfcc +PODFILE CHECKSUM: 4562cd01d4aad1fc62e9d31a2ca43aacdbb72dc9 COCOAPODS: 1.2.1 diff --git a/TrustKitDemo/TrustKitDemo/AppDelegate.h b/TrustKitDemo/TrustKitDemo-ObjC/AppDelegate.h similarity index 100% rename from TrustKitDemo/TrustKitDemo/AppDelegate.h rename to TrustKitDemo/TrustKitDemo-ObjC/AppDelegate.h diff --git a/TrustKitDemo/TrustKitDemo/AppDelegate.m b/TrustKitDemo/TrustKitDemo-ObjC/AppDelegate.m similarity index 100% rename from TrustKitDemo/TrustKitDemo/AppDelegate.m rename to TrustKitDemo/TrustKitDemo-ObjC/AppDelegate.m diff --git a/TrustKitDemo/TrustKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/TrustKitDemo/TrustKitDemo-ObjC/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from TrustKitDemo/TrustKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json rename to TrustKitDemo/TrustKitDemo-ObjC/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/TrustKitDemo/TrustKitDemo/Base.lproj/LaunchScreen.storyboard b/TrustKitDemo/TrustKitDemo-ObjC/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from TrustKitDemo/TrustKitDemo/Base.lproj/LaunchScreen.storyboard rename to TrustKitDemo/TrustKitDemo-ObjC/Base.lproj/LaunchScreen.storyboard diff --git a/TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard b/TrustKitDemo/TrustKitDemo-ObjC/Base.lproj/Main.storyboard similarity index 100% rename from TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard rename to TrustKitDemo/TrustKitDemo-ObjC/Base.lproj/Main.storyboard diff --git a/TrustKitDemo/TrustKitDemo/Info.plist b/TrustKitDemo/TrustKitDemo-ObjC/Info.plist similarity index 100% rename from TrustKitDemo/TrustKitDemo/Info.plist rename to TrustKitDemo/TrustKitDemo-ObjC/Info.plist diff --git a/TrustKitDemo/TrustKitDemo/ViewController.h b/TrustKitDemo/TrustKitDemo-ObjC/ViewController.h similarity index 100% rename from TrustKitDemo/TrustKitDemo/ViewController.h rename to TrustKitDemo/TrustKitDemo-ObjC/ViewController.h diff --git a/TrustKitDemo/TrustKitDemo/ViewController.m b/TrustKitDemo/TrustKitDemo-ObjC/ViewController.m similarity index 100% rename from TrustKitDemo/TrustKitDemo/ViewController.m rename to TrustKitDemo/TrustKitDemo-ObjC/ViewController.m diff --git a/TrustKitDemo/TrustKitDemo/main.m b/TrustKitDemo/TrustKitDemo-ObjC/main.m similarity index 100% rename from TrustKitDemo/TrustKitDemo/main.m rename to TrustKitDemo/TrustKitDemo-ObjC/main.m diff --git a/TrustKitDemo/TrustKitDemoInSwift/AppDelegate.swift b/TrustKitDemo/TrustKitDemo-Swift/AppDelegate.swift similarity index 100% rename from TrustKitDemo/TrustKitDemoInSwift/AppDelegate.swift rename to TrustKitDemo/TrustKitDemo-Swift/AppDelegate.swift diff --git a/TrustKitDemo/TrustKitDemoInSwift/Assets.xcassets/AppIcon.appiconset/Contents.json b/TrustKitDemo/TrustKitDemo-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from TrustKitDemo/TrustKitDemoInSwift/Assets.xcassets/AppIcon.appiconset/Contents.json rename to TrustKitDemo/TrustKitDemo-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/TrustKitDemo/TrustKitDemoInSwift/DTViewController.swift b/TrustKitDemo/TrustKitDemo-Swift/DTViewController.swift similarity index 100% rename from TrustKitDemo/TrustKitDemoInSwift/DTViewController.swift rename to TrustKitDemo/TrustKitDemo-Swift/DTViewController.swift diff --git a/TrustKitDemo/TrustKitDemoInSwift/Info.plist b/TrustKitDemo/TrustKitDemo-Swift/Info.plist similarity index 96% rename from TrustKitDemo/TrustKitDemoInSwift/Info.plist rename to TrustKitDemo/TrustKitDemo-Swift/Info.plist index 33428bfe..38e98af2 100644 --- a/TrustKitDemo/TrustKitDemoInSwift/Info.plist +++ b/TrustKitDemo/TrustKitDemo-Swift/Info.plist @@ -23,7 +23,7 @@ UILaunchStoryboardName LaunchScreen UIMainStoryboardFile - MainStoryboardForSwift + Main UIRequiredDeviceCapabilities armv7 diff --git a/TrustKitDemo/TrustKitDemoInSwift/LaunchScreen.storyboard b/TrustKitDemo/TrustKitDemo-Swift/LaunchScreen.storyboard similarity index 100% rename from TrustKitDemo/TrustKitDemoInSwift/LaunchScreen.storyboard rename to TrustKitDemo/TrustKitDemo-Swift/LaunchScreen.storyboard diff --git a/TrustKitDemo/TrustKitDemoInSwift/MainStoryboardForSwift.storyboard b/TrustKitDemo/TrustKitDemo-Swift/Main.storyboard similarity index 100% rename from TrustKitDemo/TrustKitDemoInSwift/MainStoryboardForSwift.storyboard rename to TrustKitDemo/TrustKitDemo-Swift/Main.storyboard diff --git a/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj b/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj index 10140c7e..48cfa6c1 100644 --- a/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj +++ b/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj @@ -16,8 +16,10 @@ 194231161EF9658800029B04 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194231151EF9658800029B04 /* AppDelegate.swift */; }; 194231181EF9658800029B04 /* DTViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194231171EF9658800029B04 /* DTViewController.swift */; }; 1942311D1EF9658800029B04 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1942311C1EF9658800029B04 /* Assets.xcassets */; }; - 194231271EF9663F00029B04 /* MainStoryboardForSwift.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 194231261EF9663F00029B04 /* MainStoryboardForSwift.storyboard */; }; + 194231271EF9663F00029B04 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 194231261EF9663F00029B04 /* Main.storyboard */; }; + 5A8D1A59570DDDCEA069122F /* Pods_TrustKitDemo_Swift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA439ED79C3CC872A6212881 /* Pods_TrustKitDemo_Swift.framework */; }; 5C1DE70282AFDA569285EAC8 /* libPods-TrustKitDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ED236495884111AC895A025E /* libPods-TrustKitDemo.a */; }; + E90DE1D752D726C6764F55E6 /* libPods-TrustKitDemo-ObjC.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4594D1F801A2D85113A8D4A7 /* libPods-TrustKitDemo-ObjC.a */; }; F563B5060C50A25EA583DA9A /* Pods_TrustKitDemoInSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F77E736E3DC0DB307912893 /* Pods_TrustKitDemoInSwift.framework */; }; /* End PBXBuildFile section */ @@ -36,7 +38,7 @@ /* Begin PBXFileReference section */ 03B1C8789B008BB8F706D702 /* Pods-TrustKitDemoInSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemoInSwift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.debug.xcconfig"; sourceTree = ""; }; - 1942309D1EF7C91A00029B04 /* TrustKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TrustKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1942309D1EF7C91A00029B04 /* TrustKitDemo-ObjC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TrustKitDemo-ObjC.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 194230A11EF7C91A00029B04 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 194230A31EF7C91A00029B04 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 194230A41EF7C91A00029B04 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -46,14 +48,20 @@ 194230AC1EF7C91A00029B04 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 194230AF1EF7C91A00029B04 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 194230B11EF7C91A00029B04 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 194231131EF9658800029B04 /* TrustKitDemoInSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TrustKitDemoInSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 194231131EF9658800029B04 /* TrustKitDemo-Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TrustKitDemo-Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 194231151EF9658800029B04 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 194231171EF9658800029B04 /* DTViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DTViewController.swift; sourceTree = ""; }; 1942311C1EF9658800029B04 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 194231261EF9663F00029B04 /* MainStoryboardForSwift.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainStoryboardForSwift.storyboard; sourceTree = ""; }; + 194231261EF9663F00029B04 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 229FD9B70132921DEF151965 /* Pods-TrustKitDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.debug.xcconfig"; sourceTree = ""; }; + 30535E1438B5A9A32F97C7F3 /* Pods-TrustKitDemo-ObjC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemo-ObjC.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC.debug.xcconfig"; sourceTree = ""; }; + 4594D1F801A2D85113A8D4A7 /* libPods-TrustKitDemo-ObjC.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-TrustKitDemo-ObjC.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 756D66A433A7A1257803FD3D /* Pods-TrustKitDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.release.xcconfig"; sourceTree = ""; }; 9F77E736E3DC0DB307912893 /* Pods_TrustKitDemoInSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TrustKitDemoInSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9FDCACA5A2721D9EF3F32921 /* Pods-TrustKitDemo-Swift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemo-Swift.release.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.release.xcconfig"; sourceTree = ""; }; + BE18B1D51351C2F2618BD2F7 /* Pods-TrustKitDemo-Swift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemo-Swift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.debug.xcconfig"; sourceTree = ""; }; + D2731A50771388BDCC19542B /* Pods-TrustKitDemo-ObjC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemo-ObjC.release.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC.release.xcconfig"; sourceTree = ""; }; + DA439ED79C3CC872A6212881 /* Pods_TrustKitDemo_Swift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TrustKitDemo_Swift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EC7EA2F77D7200BFD66DB095 /* Pods-TrustKitDemoInSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TrustKitDemoInSwift.release.xcconfig"; path = "Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.release.xcconfig"; sourceTree = ""; }; ED236495884111AC895A025E /* libPods-TrustKitDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-TrustKitDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; FCB0DD861F117CBE00741410 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -66,6 +74,7 @@ buildActionMask = 2147483647; files = ( 5C1DE70282AFDA569285EAC8 /* libPods-TrustKitDemo.a in Frameworks */, + E90DE1D752D726C6764F55E6 /* libPods-TrustKitDemo-ObjC.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -74,6 +83,7 @@ buildActionMask = 2147483647; files = ( F563B5060C50A25EA583DA9A /* Pods_TrustKitDemoInSwift.framework in Frameworks */, + 5A8D1A59570DDDCEA069122F /* Pods_TrustKitDemo_Swift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -83,8 +93,8 @@ 194230941EF7C91A00029B04 = { isa = PBXGroup; children = ( - 1942309F1EF7C91A00029B04 /* TrustKitDemo */, - 194231141EF9658800029B04 /* TrustKitDemoInSwift */, + 1942309F1EF7C91A00029B04 /* TrustKitDemo-ObjC */, + 194231141EF9658800029B04 /* TrustKitDemo-Swift */, 1942309E1EF7C91A00029B04 /* Products */, D967AF3E12D8D8B5235F2256 /* Pods */, F58942AA5E9A3C720DDF8006 /* Frameworks */, @@ -94,13 +104,13 @@ 1942309E1EF7C91A00029B04 /* Products */ = { isa = PBXGroup; children = ( - 1942309D1EF7C91A00029B04 /* TrustKitDemo.app */, - 194231131EF9658800029B04 /* TrustKitDemoInSwift.app */, + 1942309D1EF7C91A00029B04 /* TrustKitDemo-ObjC.app */, + 194231131EF9658800029B04 /* TrustKitDemo-Swift.app */, ); name = Products; sourceTree = ""; }; - 1942309F1EF7C91A00029B04 /* TrustKitDemo */ = { + 1942309F1EF7C91A00029B04 /* TrustKitDemo-ObjC */ = { isa = PBXGroup; children = ( 194230A31EF7C91A00029B04 /* AppDelegate.h */, @@ -109,7 +119,7 @@ 194230A71EF7C91A00029B04 /* ViewController.m */, 194230A01EF7C91A00029B04 /* Supporting Files */, ); - path = TrustKitDemo; + path = "TrustKitDemo-ObjC"; sourceTree = ""; }; 194230A01EF7C91A00029B04 /* Supporting Files */ = { @@ -124,14 +134,14 @@ name = "Supporting Files"; sourceTree = ""; }; - 194231141EF9658800029B04 /* TrustKitDemoInSwift */ = { + 194231141EF9658800029B04 /* TrustKitDemo-Swift */ = { isa = PBXGroup; children = ( 194231151EF9658800029B04 /* AppDelegate.swift */, 194231171EF9658800029B04 /* DTViewController.swift */, FCB0DD871F117CCD00741410 /* Supporting FIles */, ); - path = TrustKitDemoInSwift; + path = "TrustKitDemo-Swift"; sourceTree = ""; }; D967AF3E12D8D8B5235F2256 /* Pods */ = { @@ -141,6 +151,10 @@ 756D66A433A7A1257803FD3D /* Pods-TrustKitDemo.release.xcconfig */, 03B1C8789B008BB8F706D702 /* Pods-TrustKitDemoInSwift.debug.xcconfig */, EC7EA2F77D7200BFD66DB095 /* Pods-TrustKitDemoInSwift.release.xcconfig */, + 30535E1438B5A9A32F97C7F3 /* Pods-TrustKitDemo-ObjC.debug.xcconfig */, + D2731A50771388BDCC19542B /* Pods-TrustKitDemo-ObjC.release.xcconfig */, + BE18B1D51351C2F2618BD2F7 /* Pods-TrustKitDemo-Swift.debug.xcconfig */, + 9FDCACA5A2721D9EF3F32921 /* Pods-TrustKitDemo-Swift.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -150,6 +164,8 @@ children = ( ED236495884111AC895A025E /* libPods-TrustKitDemo.a */, 9F77E736E3DC0DB307912893 /* Pods_TrustKitDemoInSwift.framework */, + 4594D1F801A2D85113A8D4A7 /* libPods-TrustKitDemo-ObjC.a */, + DA439ED79C3CC872A6212881 /* Pods_TrustKitDemo_Swift.framework */, ); name = Frameworks; sourceTree = ""; @@ -159,7 +175,7 @@ children = ( FCB0DD861F117CBE00741410 /* Info.plist */, FCB0DD881F117D4C00741410 /* LaunchScreen.storyboard */, - 194231261EF9663F00029B04 /* MainStoryboardForSwift.storyboard */, + 194231261EF9663F00029B04 /* Main.storyboard */, 1942311C1EF9658800029B04 /* Assets.xcassets */, ); name = "Supporting FIles"; @@ -168,9 +184,9 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 1942309C1EF7C91A00029B04 /* TrustKitDemo */ = { + 1942309C1EF7C91A00029B04 /* TrustKitDemo-ObjC */ = { isa = PBXNativeTarget; - buildConfigurationList = 194230BF1EF7C91B00029B04 /* Build configuration list for PBXNativeTarget "TrustKitDemo" */; + buildConfigurationList = 194230BF1EF7C91B00029B04 /* Build configuration list for PBXNativeTarget "TrustKitDemo-ObjC" */; buildPhases = ( FE484BC2D3DE5A98FB396530 /* [CP] Check Pods Manifest.lock */, 194230991EF7C91A00029B04 /* Sources */, @@ -184,14 +200,14 @@ ); dependencies = ( ); - name = TrustKitDemo; + name = "TrustKitDemo-ObjC"; productName = TrustKitDemo; - productReference = 1942309D1EF7C91A00029B04 /* TrustKitDemo.app */; + productReference = 1942309D1EF7C91A00029B04 /* TrustKitDemo-ObjC.app */; productType = "com.apple.product-type.application"; }; - 194231121EF9658800029B04 /* TrustKitDemoInSwift */ = { + 194231121EF9658800029B04 /* TrustKitDemo-Swift */ = { isa = PBXNativeTarget; - buildConfigurationList = 194231221EF9658800029B04 /* Build configuration list for PBXNativeTarget "TrustKitDemoInSwift" */; + buildConfigurationList = 194231221EF9658800029B04 /* Build configuration list for PBXNativeTarget "TrustKitDemo-Swift" */; buildPhases = ( 8CA07DBE8A9BC5BDBC638FC6 /* [CP] Check Pods Manifest.lock */, 1942310F1EF9658800029B04 /* Sources */, @@ -204,9 +220,9 @@ ); dependencies = ( ); - name = TrustKitDemoInSwift; + name = "TrustKitDemo-Swift"; productName = TrustKitDemoInSwift; - productReference = 194231131EF9658800029B04 /* TrustKitDemoInSwift.app */; + productReference = 194231131EF9658800029B04 /* TrustKitDemo-Swift.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -244,8 +260,8 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 1942309C1EF7C91A00029B04 /* TrustKitDemo */, - 194231121EF9658800029B04 /* TrustKitDemoInSwift */, + 1942309C1EF7C91A00029B04 /* TrustKitDemo-ObjC */, + 194231121EF9658800029B04 /* TrustKitDemo-Swift */, ); }; /* End PBXProject section */ @@ -266,7 +282,7 @@ buildActionMask = 2147483647; files = ( 1942311D1EF9658800029B04 /* Assets.xcassets in Resources */, - 194231271EF9663F00029B04 /* MainStoryboardForSwift.storyboard in Resources */, + 194231271EF9663F00029B04 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -285,7 +301,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-resources.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-resources.sh\"\n"; showEnvVarsInLog = 0; }; 8CA07DBE8A9BC5BDBC638FC6 /* [CP] Check Pods Manifest.lock */ = { @@ -315,7 +331,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-resources.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-resources.sh\"\n"; showEnvVarsInLog = 0; }; BBC1A58CA4B22D26E361B87D /* [CP] Embed Pods Frameworks */ = { @@ -330,7 +346,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-frameworks.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; F3519FE17357D96DE9D6DC8F /* [CP] Embed Pods Frameworks */ = { @@ -345,7 +361,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-frameworks.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; FE484BC2D3DE5A98FB396530 /* [CP] Check Pods Manifest.lock */ = { @@ -499,14 +515,14 @@ }; 194230C01EF7C91B00029B04 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 229FD9B70132921DEF151965 /* Pods-TrustKitDemo.debug.xcconfig */; + baseConfigurationReference = 30535E1438B5A9A32F97C7F3 /* Pods-TrustKitDemo-ObjC.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = B2EWK89Q2H; - INFOPLIST_FILE = TrustKitDemo/Info.plist; + INFOPLIST_FILE = "TrustKitDemo-ObjC/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.datatheorem.TrustKitDemo; + PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.TrustKitDemo-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -514,14 +530,14 @@ }; 194230C11EF7C91B00029B04 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 756D66A433A7A1257803FD3D /* Pods-TrustKitDemo.release.xcconfig */; + baseConfigurationReference = D2731A50771388BDCC19542B /* Pods-TrustKitDemo-ObjC.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = B2EWK89Q2H; - INFOPLIST_FILE = TrustKitDemo/Info.plist; + INFOPLIST_FILE = "TrustKitDemo-ObjC/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.datatheorem.TrustKitDemo; + PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.TrustKitDemo-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -529,14 +545,14 @@ }; 194231231EF9658800029B04 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 03B1C8789B008BB8F706D702 /* Pods-TrustKitDemoInSwift.debug.xcconfig */; + baseConfigurationReference = BE18B1D51351C2F2618BD2F7 /* Pods-TrustKitDemo-Swift.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = B2EWK89Q2H; - INFOPLIST_FILE = TrustKitDemoInSwift/Info.plist; + INFOPLIST_FILE = "TrustKitDemo-Swift/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.datatheorem.TrustKitDemoInSwift; + PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.TrustKitDemo-Swift"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -547,14 +563,14 @@ }; 194231241EF9658800029B04 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = EC7EA2F77D7200BFD66DB095 /* Pods-TrustKitDemoInSwift.release.xcconfig */; + baseConfigurationReference = 9FDCACA5A2721D9EF3F32921 /* Pods-TrustKitDemo-Swift.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = B2EWK89Q2H; - INFOPLIST_FILE = TrustKitDemoInSwift/Info.plist; + INFOPLIST_FILE = "TrustKitDemo-Swift/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.datatheorem.TrustKitDemoInSwift; + PRODUCT_BUNDLE_IDENTIFIER = "com.datatheorem.TrustKitDemo-Swift"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 3.0; @@ -574,7 +590,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 194230BF1EF7C91B00029B04 /* Build configuration list for PBXNativeTarget "TrustKitDemo" */ = { + 194230BF1EF7C91B00029B04 /* Build configuration list for PBXNativeTarget "TrustKitDemo-ObjC" */ = { isa = XCConfigurationList; buildConfigurations = ( 194230C01EF7C91B00029B04 /* Debug */, @@ -583,7 +599,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 194231221EF9658800029B04 /* Build configuration list for PBXNativeTarget "TrustKitDemoInSwift" */ = { + 194231221EF9658800029B04 /* Build configuration list for PBXNativeTarget "TrustKitDemo-Swift" */ = { isa = XCConfigurationList; buildConfigurations = ( 194231231EF9658800029B04 /* Debug */, diff --git a/TrustKitDemo/TrustKitDemo.xcodeproj/xcshareddata/xcschemes/TrustKitDemo-ObjC.xcscheme b/TrustKitDemo/TrustKitDemo.xcodeproj/xcshareddata/xcschemes/TrustKitDemo-ObjC.xcscheme new file mode 100644 index 00000000..37dc71c8 --- /dev/null +++ b/TrustKitDemo/TrustKitDemo.xcodeproj/xcshareddata/xcschemes/TrustKitDemo-ObjC.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TrustKitDemo/TrustKitDemo.xcodeproj/xcshareddata/xcschemes/TrustKitDemo-Swift.xcscheme b/TrustKitDemo/TrustKitDemo.xcodeproj/xcshareddata/xcschemes/TrustKitDemo-Swift.xcscheme new file mode 100644 index 00000000..5c8170c1 --- /dev/null +++ b/TrustKitDemo/TrustKitDemo.xcodeproj/xcshareddata/xcschemes/TrustKitDemo-Swift.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2dce43f46c7e7f65ac0cd197a6d1a6d8c9348005 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Sat, 8 Jul 2017 17:13:15 -0400 Subject: [PATCH 123/126] Pod install after renaming targets --- TrustKitDemo/Pods/Manifest.lock | 2 +- .../Pods/Pods.xcodeproj/project.pbxproj | 926 +++++++++--------- ...ustKitDemo-ObjC-acknowledgements.markdown} | 0 ...-TrustKitDemo-ObjC-acknowledgements.plist} | 0 .../Pods-TrustKitDemo-ObjC-dummy.m | 5 + .../Pods-TrustKitDemo-ObjC-frameworks.sh} | 0 .../Pods-TrustKitDemo-ObjC-resources.sh} | 0 .../Pods-TrustKitDemo-ObjC.debug.xcconfig} | 0 .../Pods-TrustKitDemo-ObjC.release.xcconfig} | 0 .../Info.plist | 0 ...stKitDemo-Swift-acknowledgements.markdown} | 0 ...TrustKitDemo-Swift-acknowledgements.plist} | 0 .../Pods-TrustKitDemo-Swift-dummy.m | 5 + .../Pods-TrustKitDemo-Swift-frameworks.sh} | 0 .../Pods-TrustKitDemo-Swift-resources.sh} | 0 .../Pods-TrustKitDemo-Swift-umbrella.h} | 4 +- .../Pods-TrustKitDemo-Swift.debug.xcconfig} | 0 .../Pods-TrustKitDemo-Swift.modulemap | 6 + .../Pods-TrustKitDemo-Swift.release.xcconfig} | 0 .../Pods-TrustKitDemo-dummy.m | 5 - .../Pods-TrustKitDemoInSwift-dummy.m | 5 - .../Pods-TrustKitDemoInSwift.modulemap | 6 - 22 files changed, 482 insertions(+), 482 deletions(-) rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.markdown => Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-acknowledgements.markdown} (100%) rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.plist => Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-acknowledgements.plist} (100%) create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-dummy.m rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemo/Pods-TrustKitDemo-frameworks.sh => Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-frameworks.sh} (100%) rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemo/Pods-TrustKitDemo-resources.sh => Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-resources.sh} (100%) rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemo/Pods-TrustKitDemo.debug.xcconfig => Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC.debug.xcconfig} (100%) rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemo/Pods-TrustKitDemo.release.xcconfig => Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC.release.xcconfig} (100%) rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemoInSwift => Pods-TrustKitDemo-Swift}/Info.plist (100%) rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.markdown => Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-acknowledgements.markdown} (100%) rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.plist => Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-acknowledgements.plist} (100%) create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-dummy.m rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-frameworks.sh => Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-frameworks.sh} (100%) rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-resources.sh => Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-resources.sh} (100%) rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-umbrella.h => Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-umbrella.h} (57%) rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.debug.xcconfig => Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.debug.xcconfig} (100%) create mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.modulemap rename TrustKitDemo/Pods/Target Support Files/{Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.release.xcconfig => Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.release.xcconfig} (100%) delete mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-dummy.m delete mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-dummy.m delete mode 100644 TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.modulemap diff --git a/TrustKitDemo/Pods/Manifest.lock b/TrustKitDemo/Pods/Manifest.lock index d75a91f9..2b6b6caa 100644 --- a/TrustKitDemo/Pods/Manifest.lock +++ b/TrustKitDemo/Pods/Manifest.lock @@ -11,6 +11,6 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: TrustKit: 59a0878569585d9b1adb7d76c341426deb57a5b8 -PODFILE CHECKSUM: 86e19c8ce4662ee6d0981982609efb580466dfcc +PODFILE CHECKSUM: 4562cd01d4aad1fc62e9d31a2ca43aacdbb72dc9 COCOAPODS: 1.2.1 diff --git a/TrustKitDemo/Pods/Pods.xcodeproj/project.pbxproj b/TrustKitDemo/Pods/Pods.xcodeproj/project.pbxproj index c5ca0bc9..2ca5ff19 100644 --- a/TrustKitDemo/Pods/Pods.xcodeproj/project.pbxproj +++ b/TrustKitDemo/Pods/Pods.xcodeproj/project.pbxproj @@ -7,196 +7,195 @@ objects = { /* Begin PBXBuildFile section */ - 00851FF4E5F19A667BF0C3D50912A1D5 /* vendor_identifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A38032B035203051E2B243A38998AFD /* vendor_identifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 00A1CE305307E9CC1C56E14BD3969596 /* RSSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D825CA602AD8C4F0DC92C5C2F315F80 /* RSSwizzle.m */; }; - 0130E26071450C6CBA439373428DC4CF /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = AF127C3A13A8630FD92058669BDFF057 /* TrustKit.m */; }; - 01D711A6F5D6E57DCCE8946388BEC7EB /* assert.h in Headers */ = {isa = PBXBuildFile; fileRef = 04C9AAFF75BACF2953B727E819D0D4E4 /* assert.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 0496FF3E58A206D1EFD7394E6456E700 /* string_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 287C7E0B6B509A541BC1525CF4C29A22 /* string_util.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 094A47B3A015A857FD9902D9DE4ACDE5 /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FFBF768AACDC0BA4BFEF5281933FFAA7 /* TSKPinningValidatorResult.m */; }; - 09E7DF568807E63A6676DB2AB69939FC /* registry_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 7474D1481157E1DB7C600DA9295073F4 /* registry_types.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 10484F688180E9B2CA3F443C47D9A9F5 /* trie_search.c in Sources */ = {isa = PBXBuildFile; fileRef = 6D8DD333AC5AD1ABEAEDA6B5979E196E /* trie_search.c */; }; - 10C5AC1D48A7A2087833A1EFFCCDD65A /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C64C309645EA64B17D511CF7075A4B5 /* TSKTrustKitConfig.m */; }; - 14469A265E74524CBC0448214CDB5A90 /* TSKNSURLSessionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = A54DFB58902F4C4023515556B6B09E3B /* TSKNSURLSessionDelegateProxy.m */; }; - 15F3A1BEBE2F0554D51CAE47B09088A4 /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C64C309645EA64B17D511CF7075A4B5 /* TSKTrustKitConfig.m */; }; - 160C63E448CBB115119659EFBF5C84E0 /* Pods-TrustKitDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D556BFA8C03C6783987680194DA1ABB /* Pods-TrustKitDemo-dummy.m */; }; - 173654C8C63381BFAC72BFA6BB2CEBB9 /* TSKReportsRateLimiter.h in Headers */ = {isa = PBXBuildFile; fileRef = B4BB3E7BC15983F00341A2742C1913D5 /* TSKReportsRateLimiter.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 1A43A1DCDCD6E62E13B2A735586524D7 /* RSSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D825CA602AD8C4F0DC92C5C2F315F80 /* RSSwizzle.m */; }; - 1B1A76932E64219790E2E06E572F4149 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */; }; - 1D1B754DB932A2FF2C34C4E5ECBBAB04 /* reporting_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 1727DE17A4DF27E0A32BC0DAB30A80D1 /* reporting_utils.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 1DA5ED746B8D8FF91F7077A79CB37823 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C085E02FF89804F50ADDA04F5A586E94 /* Security.framework */; }; - 203488EF10B0D10B0854259A0BF35A27 /* trie_search.c in Sources */ = {isa = PBXBuildFile; fileRef = 6D8DD333AC5AD1ABEAEDA6B5979E196E /* trie_search.c */; }; - 20F2F8326AC10F0EDB95A5C120A42C9C /* reporting_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 1727DE17A4DF27E0A32BC0DAB30A80D1 /* reporting_utils.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 219DE7255483B7DDDE0FD5BD790D95A0 /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B83693BA9D3DD38777DB7CCDD9A5B1A /* ssl_pin_verifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 29C6CB0A3A3842D9B704E0CC8B834731 /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DF10471A2E6B2E0AC9FAC943DA4F8B7 /* TSKBackgroundReporter.m */; }; - 2A0DC2F329F20A507F8EEC15F3472667 /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = C7AB4A501B02D26F8C5A92B7A3AC21F5 /* reporting_utils.m */; }; - 2B886EF817237F7DAD4AC1AB80F7CF87 /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 390AED1F5816C13CBAC37BC498AED5E4 /* domain_registry.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 2F5C97F11E3C988C45B14C04EF182AFD /* RSSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = 1863A28B3A36F0B9EE4582862E1056D4 /* RSSwizzle.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 304C39AEAB37E4B3ADE44D6985DE6AEE /* registry_search.c in Sources */ = {isa = PBXBuildFile; fileRef = DEADB11810EFB649591A4C39A6FCF739 /* registry_search.c */; }; - 345E1E08EB0E9881B89FDF6728B5CD4F /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 702B9532D5B1159EF60B335D307B9C45 /* TSKSPKIHashCache.m */; }; - 36F5F40C0CA5EE29258B2AAD78A8D806 /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = C7AB4A501B02D26F8C5A92B7A3AC21F5 /* reporting_utils.m */; }; - 3A27EAA7E79CBD53890906565E1C0734 /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6CE1FAD8BB620AAF2879A0AC238BC6 /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 41EDB6EBC0B1F4135AC577C87C1C62CC /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DF10471A2E6B2E0AC9FAC943DA4F8B7 /* TSKBackgroundReporter.m */; }; - 4550A31BDF3B61E7267B06F119CC6908 /* TSKNSURLConnectionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 2342803E73DA3E02533A2B9B7616F0C7 /* TSKNSURLConnectionDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 48F95C232FF22C9E855EB8DE6C5D3962 /* TSKNSURLConnectionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 46C0B0ADDA1FAF1CF76BBA4C55725C04 /* TSKNSURLConnectionDelegateProxy.m */; }; - 49634D47E0EDD9B498FDE2F5F4AA485E /* trie_search.h in Headers */ = {isa = PBXBuildFile; fileRef = 103565E750423591CB9BD33EE2F4A32E /* trie_search.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 498E8D9DEB07EB14E153CD47D176A452 /* TrustKit-framework-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA9D6A4C087F7256F7C1FC0976DEB80E /* TrustKit-framework-dummy.m */; }; - 4C904BF58040C4F69A02BB90BDF6A202 /* TSKLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 91F770C36742BA6AEE960724BEC192F5 /* TSKLog.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 5167C676C50CB1FE15039D4BD956F2F8 /* TSKNSURLSessionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 72AE90ECEDFD50BADED53717FBB3892D /* TSKNSURLSessionDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 5448F6FAD1663B8B906AD4FEAE95C3BA /* TSKTrustDecision.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BCBC95D7B4F66D37FBA5785D4040B60 /* TSKTrustDecision.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 54FA5C9512FCE077B16C79562E6003E4 /* registry_tables.h in Headers */ = {isa = PBXBuildFile; fileRef = A42809F0183618D391CF0950F836D339 /* registry_tables.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 557E4214E44510E8A529A1C9AE1C2107 /* TSKPinningValidator_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E3CD6B1F3DDAF522B22000B826EF88 /* TSKPinningValidator_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 56FF6CCDD07BEC720EFEF68856486006 /* TSKLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 91F770C36742BA6AEE960724BEC192F5 /* TSKLog.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 5A6ED2C021536CAD9C5323764E651092 /* TSKPinningValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = C193DFCC3956EDDD7FFE7D491459865D /* TSKPinningValidator.m */; }; - 5B1BB80A066D28AAE7D8C6124EC9B155 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */; }; - 5B3232D66E17FA89AA1114E851BB1260 /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 702B9532D5B1159EF60B335D307B9C45 /* TSKSPKIHashCache.m */; }; - 5CEDC8B868290CC7CC1F128A5C6746E6 /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6CE1FAD8BB620AAF2879A0AC238BC6 /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 61F5F24D2CB7BCE8126273DEBA08CC73 /* TSKPinningValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = C193DFCC3956EDDD7FFE7D491459865D /* TSKPinningValidator.m */; }; - 6444B457833221D3A3E74BF00BD47035 /* TSKTrustKitConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = DEDE844CB2D002704EE36BB2178EB726 /* TSKTrustKitConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 6C33C70511B2472C8E61AE03DEA53DC3 /* TSKPinFailureReport.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B09B7AB0DE384F8DDC19A2B2B5ACAD5 /* TSKPinFailureReport.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 6EE94D82A953FE4FB494AE05B1FA9AF5 /* assert.c in Sources */ = {isa = PBXBuildFile; fileRef = 214B2EAAAB4DEA645B6A1B65D1F2F386 /* assert.c */; }; - 706DFFF92262AD3A654930ADAC7372F7 /* RSSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = 1863A28B3A36F0B9EE4582862E1056D4 /* RSSwizzle.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 709C87892B7603F4AF2C250980453B11 /* TSKNSURLSessionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 72AE90ECEDFD50BADED53717FBB3892D /* TSKNSURLSessionDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 70DF98043F0FC61A986D90A016C32011 /* TrustKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CA3CFD14971562FA66B218152D30F35 /* TrustKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 72C555F342E19A2EC42F8F977F34391F /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C085E02FF89804F50ADDA04F5A586E94 /* Security.framework */; }; - 72D0920BDCF2DF9583CD74CE0EE4E7E5 /* TSKReportsRateLimiter.h in Headers */ = {isa = PBXBuildFile; fileRef = B4BB3E7BC15983F00341A2742C1913D5 /* TSKReportsRateLimiter.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 7783BE79333283119B7E04ADE59D4D85 /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = F56169ECAF90EECAEB4D0EAA6C9D9F89 /* TSKPinFailureReport.m */; }; - 77FB5D09E1047C484301DD8A7CC193E0 /* TSKPinFailureReport.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B09B7AB0DE384F8DDC19A2B2B5ACAD5 /* TSKPinFailureReport.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 789FDC0611E1E6643501301907740355 /* trie_search.h in Headers */ = {isa = PBXBuildFile; fileRef = 103565E750423591CB9BD33EE2F4A32E /* trie_search.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 7DF75D25C7A2C29B05D5787938C112ED /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 954A0787F64D38E08D65EBC46C1CB9AE /* parse_configuration.m */; }; - 7F4626F2CD390F14D8980C333830EF5D /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 390AED1F5816C13CBAC37BC498AED5E4 /* domain_registry.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 816D06005E4A6D71BBF791548E785E32 /* TSKNSURLConnectionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 46C0B0ADDA1FAF1CF76BBA4C55725C04 /* TSKNSURLConnectionDelegateProxy.m */; }; - 819293E2A79D94FADDCB7F6ABB9D9D18 /* init_registry_tables.c in Sources */ = {isa = PBXBuildFile; fileRef = 49D1BCE0D2BFC86604429C1DB287EEE9 /* init_registry_tables.c */; }; - 833FAAB1D09078D8B6F7F9EA9516C1C1 /* parse_configuration.h in Headers */ = {isa = PBXBuildFile; fileRef = BCE910A2438B2E8646192F907B37E559 /* parse_configuration.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 8BC37767B9A244EE8600DBB66DBECFF6 /* TSKPinningValidator_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E3CD6B1F3DDAF522B22000B826EF88 /* TSKPinningValidator_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 912296DA496E76C4592D2BB27525DC58 /* TSKSPKIHashCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A0A26583325B45FDF95C9957F01F02 /* TSKSPKIHashCache.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 91645A1F75435D30D1C0258EE9C3DDAE /* TSKPinningValidatorCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A22411D7297D4377E43FD163A4B1314 /* TSKPinningValidatorCallback.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 919B8CA0E63E044A39600170B1544B6B /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 9144C1D132FDE48C1F12DC01A5CE33EE /* configuration_utils.m */; }; - 96DF97DF055B8C04D3FE99DD46CA0AFB /* Pods-TrustKitDemoInSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F05E7CAB631D2576B200F63D1347D3C /* Pods-TrustKitDemoInSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9AAC9AC75846966966330B3C091B58F5 /* Pods-TrustKitDemoInSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7CBC78276DB001BCA4D874C083A7CF5C /* Pods-TrustKitDemoInSwift-dummy.m */; }; - 9AB598D92256251C141732F7A987C16C /* vendor_identifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A38032B035203051E2B243A38998AFD /* vendor_identifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 9C15CA27397D72DBEA030967D4B507E8 /* TSKTrustKitConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = DEDE844CB2D002704EE36BB2178EB726 /* TSKTrustKitConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9D69A757AB397230EF3145ADA6B10188 /* trie_node.h in Headers */ = {isa = PBXBuildFile; fileRef = 099F2B4E7779209E04365EE6FE079763 /* trie_node.h */; settings = {ATTRIBUTES = (Project, ); }; }; - A075C1FA71C3000765BE026D38199163 /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FFBF768AACDC0BA4BFEF5281933FFAA7 /* TSKPinningValidatorResult.m */; }; - A0B8CEAF67460C1A2D0C2C2A1A990F67 /* configuration_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5DC8912CA18313277EF115E15BBE46 /* configuration_utils.h */; settings = {ATTRIBUTES = (Project, ); }; }; - A30AFCE398CB19D1AB2F17FFB7015A39 /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = AF127C3A13A8630FD92058669BDFF057 /* TrustKit.m */; }; - A56A25BC5E423091DC2564DBD867FDA3 /* TSKNSURLSessionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = A54DFB58902F4C4023515556B6B09E3B /* TSKNSURLSessionDelegateProxy.m */; }; - A818444547D3C148B87A15A0DF7EDA09 /* TSKPublicKeyAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D72855E63D1CD2D36950CCBD3D83279 /* TSKPublicKeyAlgorithm.h */; settings = {ATTRIBUTES = (Project, ); }; }; - AA3DB3E374D6FE6C0D014CDBEED6D2A7 /* TSKPinningValidatorCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A22411D7297D4377E43FD163A4B1314 /* TSKPinningValidatorCallback.h */; settings = {ATTRIBUTES = (Public, ); }; }; - AB6C9A86369041598BEDDB4299A62528 /* vendor_identifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 758A7792A0090736AA3ABBCB6FA8FAC4 /* vendor_identifier.m */; }; - AC4668E1382E6BCF1D1C0734115BA63E /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 288A1FEC0658D8FB688BBAE9B97E2B7F /* ssl_pin_verifier.m */; }; - AD1A357B9A460872C06FAC8C81227E75 /* parse_configuration.h in Headers */ = {isa = PBXBuildFile; fileRef = BCE910A2438B2E8646192F907B37E559 /* parse_configuration.h */; settings = {ATTRIBUTES = (Project, ); }; }; - B33CE417C5345363CD8E3F45CE44871B /* TrustKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C781175B244EAD62240FA815899F2C7C /* TrustKit-umbrella.h */; settings = {ATTRIBUTES = (Project, ); }; }; - B68CAC1D63D2A0D9BA930C508E48AE57 /* trie_node.h in Headers */ = {isa = PBXBuildFile; fileRef = 099F2B4E7779209E04365EE6FE079763 /* trie_node.h */; settings = {ATTRIBUTES = (Project, ); }; }; - B769BE1CE6DDDC128E3F160968194EAE /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B83693BA9D3DD38777DB7CCDD9A5B1A /* ssl_pin_verifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; - BAC36417573685B654AAB2A415F87DCB /* TSKPinningValidator.h in Headers */ = {isa = PBXBuildFile; fileRef = B8F41D8A5EA2A86E468C52EA4956695F /* TSKPinningValidator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BD8D9989CE8BC3F93EA53EE58D17619C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */; }; - BE1276DBEB9F2E09D061430025283640 /* string_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 287C7E0B6B509A541BC1525CF4C29A22 /* string_util.h */; settings = {ATTRIBUTES = (Project, ); }; }; - C9199DCE6F188384A1A3EB729854EBF1 /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 954A0787F64D38E08D65EBC46C1CB9AE /* parse_configuration.m */; }; - C99670E8EF0E6428E51A39F0A918EE59 /* TSKTrustDecision.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BCBC95D7B4F66D37FBA5785D4040B60 /* TSKTrustDecision.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CC0B39B3704D36B21E5ACA9F565FBE9E /* init_registry_tables.c in Sources */ = {isa = PBXBuildFile; fileRef = 49D1BCE0D2BFC86604429C1DB287EEE9 /* init_registry_tables.c */; }; - D0D9F705F65EEC35793699EE0630E8C3 /* registry_search.c in Sources */ = {isa = PBXBuildFile; fileRef = DEADB11810EFB649591A4C39A6FCF739 /* registry_search.c */; }; - D2FB40D0D6591DD87164BCFDED4A872B /* assert.h in Headers */ = {isa = PBXBuildFile; fileRef = 04C9AAFF75BACF2953B727E819D0D4E4 /* assert.h */; settings = {ATTRIBUTES = (Project, ); }; }; - D3C5909E5B8CA9C393309391B4DDF1E0 /* registry_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 7474D1481157E1DB7C600DA9295073F4 /* registry_types.h */; settings = {ATTRIBUTES = (Project, ); }; }; - D5430056619E1C24EC30A1E3C5101E4B /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = F56169ECAF90EECAEB4D0EAA6C9D9F89 /* TSKPinFailureReport.m */; }; - D59B2ACF7184AA8A53037C4096629788 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */; }; - D5C5AAAD941E06DA3AAB2EB4D02A3F3D /* TSKPublicKeyAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D72855E63D1CD2D36950CCBD3D83279 /* TSKPublicKeyAlgorithm.h */; settings = {ATTRIBUTES = (Project, ); }; }; - DBA72238E5490710B7FDFD0BC03C25B2 /* TrustKit-library-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = C60EE265DEB50B45F4E601CC49B209EC /* TrustKit-library-dummy.m */; }; - DDA29052B0CEC856600037D272593BBE /* TSKBackgroundReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = CF15A12A9DDFF9EF237480A92FB6635E /* TSKBackgroundReporter.h */; settings = {ATTRIBUTES = (Project, ); }; }; - DEB1C3B236A303CDBE1A28F1C168BFAC /* TSKSPKIHashCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A0A26583325B45FDF95C9957F01F02 /* TSKSPKIHashCache.h */; settings = {ATTRIBUTES = (Project, ); }; }; - E167E2DFDC1302944D5105477C99C410 /* TSKReportsRateLimiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1267CA13389D6978F9B17ABC4B1D1DAB /* TSKReportsRateLimiter.m */; }; - E5BDBD5846B9BF559F9D4A790981E21D /* TSKBackgroundReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = CF15A12A9DDFF9EF237480A92FB6635E /* TSKBackgroundReporter.h */; settings = {ATTRIBUTES = (Project, ); }; }; - E9B3C163FC82567554CD8565481A0C08 /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 9144C1D132FDE48C1F12DC01A5CE33EE /* configuration_utils.m */; }; - E9DA954AAEAB44DCD066B35D746A8383 /* TSKPinningValidator.h in Headers */ = {isa = PBXBuildFile; fileRef = B8F41D8A5EA2A86E468C52EA4956695F /* TSKPinningValidator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EB6F5E04F094338BBD32B9C9A8715DE8 /* vendor_identifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 758A7792A0090736AA3ABBCB6FA8FAC4 /* vendor_identifier.m */; }; - EBB2FD29B682B615CC8D640704424C39 /* assert.c in Sources */ = {isa = PBXBuildFile; fileRef = 214B2EAAAB4DEA645B6A1B65D1F2F386 /* assert.c */; }; - EC5EA69669DCCD19AB04FECEE50644F7 /* TSKReportsRateLimiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1267CA13389D6978F9B17ABC4B1D1DAB /* TSKReportsRateLimiter.m */; }; - F005E6B1ED453CEEE86179016DD2E13F /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 288A1FEC0658D8FB688BBAE9B97E2B7F /* ssl_pin_verifier.m */; }; - F30A67B156D94EAD3CA444BE6C6C22A2 /* registry_tables.h in Headers */ = {isa = PBXBuildFile; fileRef = A42809F0183618D391CF0950F836D339 /* registry_tables.h */; settings = {ATTRIBUTES = (Project, ); }; }; - F52DAB9D11F101AA30E8466DCFA2D8DB /* configuration_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5DC8912CA18313277EF115E15BBE46 /* configuration_utils.h */; settings = {ATTRIBUTES = (Project, ); }; }; - F5DB7A40C7181F6A1B0C2C0193786A08 /* TSKNSURLConnectionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 2342803E73DA3E02533A2B9B7616F0C7 /* TSKNSURLConnectionDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; - FC5DF4AF969196C79FD959A2C8DD2DC2 /* TrustKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CA3CFD14971562FA66B218152D30F35 /* TrustKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - FCAE6A2720A8FA62F35340372D7BE36D /* TrustKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C781175B244EAD62240FA815899F2C7C /* TrustKit-umbrella.h */; settings = {ATTRIBUTES = (Project, ); }; }; - FF3AB846370F0C93EF33D433416810BB /* TrustKit-framework-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B4B06F0C8E473C389BA9B4A3A93875B9 /* TrustKit-framework-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 004354FB4F41F0DD78234D819F28DDDC /* registry_tables.h in Headers */ = {isa = PBXBuildFile; fileRef = A42809F0183618D391CF0950F836D339 /* registry_tables.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 022EC1DC5F635233B57571B94B4476FD /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6CE1FAD8BB620AAF2879A0AC238BC6 /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 03AEEBD15BB9C84E81742690A23D1D18 /* TrustKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CA3CFD14971562FA66B218152D30F35 /* TrustKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 048FE4E64D659B08DDB4C59AA427A591 /* TSKTrustKitConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = DEDE844CB2D002704EE36BB2178EB726 /* TSKTrustKitConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0A8ED3AC1BFA68247F756DEDB82BE26F /* TrustKit-framework-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B4B06F0C8E473C389BA9B4A3A93875B9 /* TrustKit-framework-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0DBE54CE70012005EE5922063ED217C3 /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = AF127C3A13A8630FD92058669BDFF057 /* TrustKit.m */; }; + 0F2669E1BBD15A0AAA5E7354A52454DC /* reporting_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 1727DE17A4DF27E0A32BC0DAB30A80D1 /* reporting_utils.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 115CD60C3473965724C6E672A0524619 /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 288A1FEC0658D8FB688BBAE9B97E2B7F /* ssl_pin_verifier.m */; }; + 14523BA7C9E5817E55DAA66BC54D400C /* TSKNSURLSessionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 72AE90ECEDFD50BADED53717FBB3892D /* TSKNSURLSessionDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 15C98C5756DDB5F109292B770051B06F /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 288A1FEC0658D8FB688BBAE9B97E2B7F /* ssl_pin_verifier.m */; }; + 18513002955802A1B9EB947BD5273655 /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C64C309645EA64B17D511CF7075A4B5 /* TSKTrustKitConfig.m */; }; + 18547531B76466A24A3A2898EB1AFEEA /* TSKLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 91F770C36742BA6AEE960724BEC192F5 /* TSKLog.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1A1B50A1AFAFF4D749AA05F158742BF4 /* init_registry_tables.c in Sources */ = {isa = PBXBuildFile; fileRef = 49D1BCE0D2BFC86604429C1DB287EEE9 /* init_registry_tables.c */; }; + 1AF8CB709D79C21E3CE6A3BCA8FF685D /* TrustKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C781175B244EAD62240FA815899F2C7C /* TrustKit-umbrella.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1B356AEC046717BA497962422CEBE471 /* RSSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D825CA602AD8C4F0DC92C5C2F315F80 /* RSSwizzle.m */; }; + 1C57FAEA2D624D1B8311B76EF8011E41 /* TSKReportsRateLimiter.h in Headers */ = {isa = PBXBuildFile; fileRef = B4BB3E7BC15983F00341A2742C1913D5 /* TSKReportsRateLimiter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1F32FB8D5A9C5CF940B693B74A19DB74 /* TSKTrustDecision.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BCBC95D7B4F66D37FBA5785D4040B60 /* TSKTrustDecision.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2349CF2B44ECD4CD42215468689D6CBA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */; }; + 23AE9FA740A405E75E2FD0446AB5BAB6 /* TSKNSURLConnectionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 46C0B0ADDA1FAF1CF76BBA4C55725C04 /* TSKNSURLConnectionDelegateProxy.m */; }; + 26A4C8410DDF92FB1C0FCC778002DBB2 /* parse_configuration.h in Headers */ = {isa = PBXBuildFile; fileRef = BCE910A2438B2E8646192F907B37E559 /* parse_configuration.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 27D4120761F3A1F3D785372E70EB3511 /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = AF127C3A13A8630FD92058669BDFF057 /* TrustKit.m */; }; + 294A830DB1AFD306CACB637A9B035170 /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 954A0787F64D38E08D65EBC46C1CB9AE /* parse_configuration.m */; }; + 2ADB4BF4F54B049D36A1277A47E7027D /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B83693BA9D3DD38777DB7CCDD9A5B1A /* ssl_pin_verifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2D7B26D42AD755C9809921D25602B2DF /* Pods-TrustKitDemo-Swift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E1D35DBF9C615203135EEBECE77E9E0 /* Pods-TrustKitDemo-Swift-dummy.m */; }; + 3073DC96CCE3AEFE3DFE718450D918D1 /* TrustKit-framework-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA9D6A4C087F7256F7C1FC0976DEB80E /* TrustKit-framework-dummy.m */; }; + 3538C0BE09A9471CB21625ED10D2DAA0 /* TSKLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 91F770C36742BA6AEE960724BEC192F5 /* TSKLog.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3713E5566E7B3FB46DF59ED4E5083374 /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = C7AB4A501B02D26F8C5A92B7A3AC21F5 /* reporting_utils.m */; }; + 37F0041FC41E3B109947490027F69C4D /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DF10471A2E6B2E0AC9FAC943DA4F8B7 /* TSKBackgroundReporter.m */; }; + 384ED2EA9F15274964C0777285944766 /* TSKNSURLConnectionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 46C0B0ADDA1FAF1CF76BBA4C55725C04 /* TSKNSURLConnectionDelegateProxy.m */; }; + 3E112B6A3E04CA878502173CB6AB61B3 /* TrustKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C781175B244EAD62240FA815899F2C7C /* TrustKit-umbrella.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3FC0BF8D93F3C0C4334DD213E78A9DB7 /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = F56169ECAF90EECAEB4D0EAA6C9D9F89 /* TSKPinFailureReport.m */; }; + 3FDD67E3F112D4029E6E70B4A45C35FE /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 390AED1F5816C13CBAC37BC498AED5E4 /* domain_registry.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4131DE094E65970425BC8C798EA49DFF /* TSKPinningValidatorResult.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6CE1FAD8BB620AAF2879A0AC238BC6 /* TSKPinningValidatorResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4642FE6453432DC5396AE825543B6ABE /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 702B9532D5B1159EF60B335D307B9C45 /* TSKSPKIHashCache.m */; }; + 496F5B98C9C1D91D8D67BCD51DB289D8 /* registry_tables.h in Headers */ = {isa = PBXBuildFile; fileRef = A42809F0183618D391CF0950F836D339 /* registry_tables.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4FF4DC004448786D15BCBDF47A929DE3 /* TSKNSURLSessionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 72AE90ECEDFD50BADED53717FBB3892D /* TSKNSURLSessionDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 579FE0884C82E10079ACFDB543A8F0CC /* RSSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D825CA602AD8C4F0DC92C5C2F315F80 /* RSSwizzle.m */; }; + 59AD8F54F23028FCF903A3312A19302B /* TrustKit-library-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = C60EE265DEB50B45F4E601CC49B209EC /* TrustKit-library-dummy.m */; }; + 5A3CBF4D7DADA9D20C03F2554C148DE0 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C085E02FF89804F50ADDA04F5A586E94 /* Security.framework */; }; + 5C9851DF486E83D2B5D68A59CC5B7578 /* Pods-TrustKitDemo-Swift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F1169467FE49E2CED6F7256518E52431 /* Pods-TrustKitDemo-Swift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5E11FBCB572E46A9DCE52F5BC5464B6E /* TSKTrustDecision.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BCBC95D7B4F66D37FBA5785D4040B60 /* TSKTrustDecision.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6092D0B8473F670117BC212E17EFAA6B /* TSKSPKIHashCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A0A26583325B45FDF95C9957F01F02 /* TSKSPKIHashCache.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6295F9063CE4349E4851102A694B5741 /* assert.h in Headers */ = {isa = PBXBuildFile; fileRef = 04C9AAFF75BACF2953B727E819D0D4E4 /* assert.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 65C071A8E653C39833309BB920E9F912 /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = F56169ECAF90EECAEB4D0EAA6C9D9F89 /* TSKPinFailureReport.m */; }; + 66AC2FC03F52276F6AC9090463E4236C /* trie_search.h in Headers */ = {isa = PBXBuildFile; fileRef = 103565E750423591CB9BD33EE2F4A32E /* trie_search.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 66B9FD231AF2D88153405A813593C335 /* registry_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 7474D1481157E1DB7C600DA9295073F4 /* registry_types.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 66D8F0A1B715F96354DD9A639F74D938 /* TSKBackgroundReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = CF15A12A9DDFF9EF237480A92FB6635E /* TSKBackgroundReporter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6A42B8968AF803F5639AB1E42D3C6B01 /* TSKPinningValidator.h in Headers */ = {isa = PBXBuildFile; fileRef = B8F41D8A5EA2A86E468C52EA4956695F /* TSKPinningValidator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 75759A23AC605EF7116B49197CC23C89 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */; }; + 76C25CB355665666A63C0E730D1997B5 /* trie_node.h in Headers */ = {isa = PBXBuildFile; fileRef = 099F2B4E7779209E04365EE6FE079763 /* trie_node.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 76F350E09FD133150A23FAE8B1BBAA24 /* assert.h in Headers */ = {isa = PBXBuildFile; fileRef = 04C9AAFF75BACF2953B727E819D0D4E4 /* assert.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 832FC2CF46BB1D184E8AF8414BA21121 /* init_registry_tables.c in Sources */ = {isa = PBXBuildFile; fileRef = 49D1BCE0D2BFC86604429C1DB287EEE9 /* init_registry_tables.c */; }; + 840B89217F6CCAC8F1E4B0FF49A3F3E8 /* TSKPinningValidatorCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A22411D7297D4377E43FD163A4B1314 /* TSKPinningValidatorCallback.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 84B4FBF55A46EDD1E8DE67E92A1E8D51 /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = C7AB4A501B02D26F8C5A92B7A3AC21F5 /* reporting_utils.m */; }; + 85BFB5B01842C42D3C89F120E639C9CF /* TSKNSURLConnectionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 2342803E73DA3E02533A2B9B7616F0C7 /* TSKNSURLConnectionDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 887DDB38B72C236388C79AE99B8D1253 /* RSSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = 1863A28B3A36F0B9EE4582862E1056D4 /* RSSwizzle.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8B3846B38A519F3282E4E886B6AA24B2 /* trie_search.c in Sources */ = {isa = PBXBuildFile; fileRef = 6D8DD333AC5AD1ABEAEDA6B5979E196E /* trie_search.c */; }; + 8C89A567583B082A7EDA3B62983E0B1E /* trie_search.h in Headers */ = {isa = PBXBuildFile; fileRef = 103565E750423591CB9BD33EE2F4A32E /* trie_search.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8CD88CCF944E01EACEF7B296C8C630F0 /* TSKPublicKeyAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D72855E63D1CD2D36950CCBD3D83279 /* TSKPublicKeyAlgorithm.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8DD8797D5F2AF1B9372BFBC75DE37B79 /* TSKPinningValidator.h in Headers */ = {isa = PBXBuildFile; fileRef = B8F41D8A5EA2A86E468C52EA4956695F /* TSKPinningValidator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8DDD3513310B6E4CB4B86402513262EE /* RSSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = 1863A28B3A36F0B9EE4582862E1056D4 /* RSSwizzle.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8E75B100BEACB92BE67CEF219F5D5B68 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */; }; + 8F2D5ACAD8DDAD407E629B1E39CD1059 /* TSKNSURLSessionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = A54DFB58902F4C4023515556B6B09E3B /* TSKNSURLSessionDelegateProxy.m */; }; + 98278EC2C5D55B250F89C96C03587D2B /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C64C309645EA64B17D511CF7075A4B5 /* TSKTrustKitConfig.m */; }; + 9D368A7215A8771F5085752F432761EF /* TSKSPKIHashCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A0A26583325B45FDF95C9957F01F02 /* TSKSPKIHashCache.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9DCD3652D914E8B7322D163B61A1CD02 /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DF10471A2E6B2E0AC9FAC943DA4F8B7 /* TSKBackgroundReporter.m */; }; + A027DA49C8522BB65A184F6F03B896A4 /* registry_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 7474D1481157E1DB7C600DA9295073F4 /* registry_types.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A113AEF74DE30B4012818B65737DEA68 /* TSKTrustKitConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = DEDE844CB2D002704EE36BB2178EB726 /* TSKTrustKitConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A2DF2EBE59BE195E702DD3C43F1E1450 /* TSKPinningValidatorCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A22411D7297D4377E43FD163A4B1314 /* TSKPinningValidatorCallback.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A3FB9EA2AFD190DDF1D27573FFD37FC3 /* TSKPinningValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = C193DFCC3956EDDD7FFE7D491459865D /* TSKPinningValidator.m */; }; + A49F72F02F1B3F1680105F1E5F5D2C9F /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C085E02FF89804F50ADDA04F5A586E94 /* Security.framework */; }; + A62BE79189117843926EE52E08AAF10D /* TSKPinningValidator_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E3CD6B1F3DDAF522B22000B826EF88 /* TSKPinningValidator_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A6DE30D424508289034FA0719F5348EE /* TrustKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CA3CFD14971562FA66B218152D30F35 /* TrustKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A77D59CC112CF07E837AA86AD62FE2C4 /* assert.c in Sources */ = {isa = PBXBuildFile; fileRef = 214B2EAAAB4DEA645B6A1B65D1F2F386 /* assert.c */; }; + A7B589B664B0BBF87D408B2247283F54 /* TSKBackgroundReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = CF15A12A9DDFF9EF237480A92FB6635E /* TSKBackgroundReporter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A9167EBE53079B8655FB9ED2BE4FB866 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */; }; + A931975CC4FB799650EE03F16A011A44 /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 702B9532D5B1159EF60B335D307B9C45 /* TSKSPKIHashCache.m */; }; + AA1AA4B4057C098E6875A59533B8B67E /* TSKReportsRateLimiter.h in Headers */ = {isa = PBXBuildFile; fileRef = B4BB3E7BC15983F00341A2742C1913D5 /* TSKReportsRateLimiter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AA2BD97826D78BD2B1DBD491DA2CBEC2 /* assert.c in Sources */ = {isa = PBXBuildFile; fileRef = 214B2EAAAB4DEA645B6A1B65D1F2F386 /* assert.c */; }; + AC0283E15C35744D49C0BAEF5414AE13 /* trie_node.h in Headers */ = {isa = PBXBuildFile; fileRef = 099F2B4E7779209E04365EE6FE079763 /* trie_node.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AC3974B8BDA444509AC71ACE0FF42ED7 /* registry_search.c in Sources */ = {isa = PBXBuildFile; fileRef = DEADB11810EFB649591A4C39A6FCF739 /* registry_search.c */; }; + ADDAFB4B03B4B45F05BFFF3BD6B3575F /* vendor_identifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 758A7792A0090736AA3ABBCB6FA8FAC4 /* vendor_identifier.m */; }; + B52A096C4DA174370A66416CD916ACCA /* TSKReportsRateLimiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1267CA13389D6978F9B17ABC4B1D1DAB /* TSKReportsRateLimiter.m */; }; + B73C6060EFC50F93A195640CF48E56CC /* configuration_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5DC8912CA18313277EF115E15BBE46 /* configuration_utils.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B95D0ED839B9C23A7B89A7296B6F18DE /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 9144C1D132FDE48C1F12DC01A5CE33EE /* configuration_utils.m */; }; + BD482292CDA6DBDE47B74F019FDB3D06 /* TSKPublicKeyAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D72855E63D1CD2D36950CCBD3D83279 /* TSKPublicKeyAlgorithm.h */; settings = {ATTRIBUTES = (Project, ); }; }; + BF2BD62AC3ABC79F047D0CCA86F4C0BF /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FFBF768AACDC0BA4BFEF5281933FFAA7 /* TSKPinningValidatorResult.m */; }; + C147251354BE7B9F639BA9C319470D2B /* vendor_identifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 758A7792A0090736AA3ABBCB6FA8FAC4 /* vendor_identifier.m */; }; + C18C8E523F810329CAF593B320E7915A /* vendor_identifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A38032B035203051E2B243A38998AFD /* vendor_identifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C3BAAAD7474D9E0E906F9A565FAF2067 /* TSKNSURLConnectionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 2342803E73DA3E02533A2B9B7616F0C7 /* TSKNSURLConnectionDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C49C7DAC7BF663FE88BD7DEF94399A45 /* ssl_pin_verifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B83693BA9D3DD38777DB7CCDD9A5B1A /* ssl_pin_verifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C86BFE7D6CC160D267ABFC89AE53A8A4 /* registry_search.c in Sources */ = {isa = PBXBuildFile; fileRef = DEADB11810EFB649591A4C39A6FCF739 /* registry_search.c */; }; + C952AD463705F146CE6B71D8C3EB2210 /* parse_configuration.h in Headers */ = {isa = PBXBuildFile; fileRef = BCE910A2438B2E8646192F907B37E559 /* parse_configuration.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CEAB22F63315F54CB6DBA250366B05A2 /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 954A0787F64D38E08D65EBC46C1CB9AE /* parse_configuration.m */; }; + D30642390168B84C919BD660AEF18C46 /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 9144C1D132FDE48C1F12DC01A5CE33EE /* configuration_utils.m */; }; + D43FC0639EFE4053973DACF3FEF46B1C /* TSKPinningValidator_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E3CD6B1F3DDAF522B22000B826EF88 /* TSKPinningValidator_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DA318BDBCB897E80FD9C5FFCD7D552BE /* trie_search.c in Sources */ = {isa = PBXBuildFile; fileRef = 6D8DD333AC5AD1ABEAEDA6B5979E196E /* trie_search.c */; }; + DD35028DBD716A3F49B337278CA47E7B /* vendor_identifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A38032B035203051E2B243A38998AFD /* vendor_identifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DEB0B009BF02A5C12BF9360303CF55B4 /* TSKReportsRateLimiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1267CA13389D6978F9B17ABC4B1D1DAB /* TSKReportsRateLimiter.m */; }; + E37075AC852B142EEBF20C38381317D0 /* string_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 287C7E0B6B509A541BC1525CF4C29A22 /* string_util.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E6DAEB91F82BE71570475B5262285DC4 /* TSKPinningValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = C193DFCC3956EDDD7FFE7D491459865D /* TSKPinningValidator.m */; }; + E811B736C73595510E79B393C3AF95FD /* TSKPinFailureReport.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B09B7AB0DE384F8DDC19A2B2B5ACAD5 /* TSKPinFailureReport.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E8E93E1F3718C337EF9C78EE8D5BD13D /* domain_registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 390AED1F5816C13CBAC37BC498AED5E4 /* domain_registry.h */; settings = {ATTRIBUTES = (Project, ); }; }; + EA7E1AACE8EC98FA147556EEE1467DC4 /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FFBF768AACDC0BA4BFEF5281933FFAA7 /* TSKPinningValidatorResult.m */; }; + F20233DAD60FFBBFC7C35E7C3DAC892B /* TSKPinFailureReport.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B09B7AB0DE384F8DDC19A2B2B5ACAD5 /* TSKPinFailureReport.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F62B9BB971170E78ECA007763D2F4AFA /* configuration_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = FF5DC8912CA18313277EF115E15BBE46 /* configuration_utils.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FA3D5961789958AA16CB57C9F9D01776 /* string_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 287C7E0B6B509A541BC1525CF4C29A22 /* string_util.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FBE458571AA2B583FD7855C246D0612F /* reporting_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 1727DE17A4DF27E0A32BC0DAB30A80D1 /* reporting_utils.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FC913FD9925A648751454DBAB9FD7A37 /* Pods-TrustKitDemo-ObjC-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = ECEF86B6ACF895EB79CA50F3E84BDB8D /* Pods-TrustKitDemo-ObjC-dummy.m */; }; + FEE04AC30285F36C4764B8BA652F029E /* TSKNSURLSessionDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = A54DFB58902F4C4023515556B6B09E3B /* TSKNSURLSessionDelegateProxy.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 8B87672562EA830C7D0B9598BC3E2568 /* PBXContainerItemProxy */ = { + 259465232FCEBB5151A7F8FFCD2700F0 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; - remoteGlobalIDString = B83210985026774772228F688CCC95C6; + remoteGlobalIDString = 49AA0D077304E9B19578BE6D4D3A0957; remoteInfo = "TrustKit-framework"; }; - 8F8E6B6301DB3263048BCA55327BB7EC /* PBXContainerItemProxy */ = { + 5865D597AB012843366BF2B893359393 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; - remoteGlobalIDString = 9FEA5104EB31D1BB74EFD21D7B2F5C64; + remoteGlobalIDString = D0D24B967F83A557E2ADF69859044ED1; remoteInfo = "TrustKit-library"; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 0123B07E490339BB8BAD0ACF37E6A605 /* Pods-TrustKitDemo-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-TrustKitDemo-acknowledgements.markdown"; sourceTree = ""; }; - 016D7DEAA7EC198B0F5C41B1FB7C4FB5 /* Pods-TrustKitDemoInSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TrustKitDemoInSwift.release.xcconfig"; sourceTree = ""; }; 04C9AAFF75BACF2953B727E819D0D4E4 /* assert.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = assert.h; sourceTree = ""; }; 099F2B4E7779209E04365EE6FE079763 /* trie_node.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = trie_node.h; sourceTree = ""; }; - 09BC6E8AAC9C761AE184F9B5BF603D94 /* Pods-TrustKitDemo-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TrustKitDemo-acknowledgements.plist"; sourceTree = ""; }; - 0D17B1927DAF9BE7FD80DCBF0F6F537E /* Pods-TrustKitDemoInSwift-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TrustKitDemoInSwift-frameworks.sh"; sourceTree = ""; }; - 0F24CF15538FFB4930681D9FFAEB7E4A /* Pods_TrustKitDemoInSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_TrustKitDemoInSwift.framework; path = "Pods-TrustKitDemoInSwift.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 0D7102761C989E9458FBD5ABA3C7551D /* Pods-TrustKitDemo-Swift-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TrustKitDemo-Swift-frameworks.sh"; sourceTree = ""; }; 103565E750423591CB9BD33EE2F4A32E /* trie_search.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = trie_search.h; sourceTree = ""; }; 11C10A31CD5CFA3EFE266394471B152E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 1267CA13389D6978F9B17ABC4B1D1DAB /* TSKReportsRateLimiter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKReportsRateLimiter.m; sourceTree = ""; }; - 12AEDEF058A1EB1FA200B4D41EE3D700 /* Pods-TrustKitDemo-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TrustKitDemo-frameworks.sh"; sourceTree = ""; }; 14E737FFF2A1232FF4058D088804405F /* TrustKit-framework.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "TrustKit-framework.xcconfig"; sourceTree = ""; }; 1727DE17A4DF27E0A32BC0DAB30A80D1 /* reporting_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = reporting_utils.h; sourceTree = ""; }; 1863A28B3A36F0B9EE4582862E1056D4 /* RSSwizzle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = RSSwizzle.h; sourceTree = ""; }; 1A22411D7297D4377E43FD163A4B1314 /* TSKPinningValidatorCallback.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorCallback.h; sourceTree = ""; }; 1B09B7AB0DE384F8DDC19A2B2B5ACAD5 /* TSKPinFailureReport.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKPinFailureReport.h; sourceTree = ""; }; 1B83693BA9D3DD38777DB7CCDD9A5B1A /* ssl_pin_verifier.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = ssl_pin_verifier.h; sourceTree = ""; }; + 1D3FD733E6BA440C82893942A17B8B35 /* Pods-TrustKitDemo-ObjC-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-TrustKitDemo-ObjC-acknowledgements.markdown"; sourceTree = ""; }; + 1FBADC8D14D8CD8CFDD5FD6D1CBBC454 /* Pods-TrustKitDemo-ObjC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TrustKitDemo-ObjC.debug.xcconfig"; sourceTree = ""; }; 214B2EAAAB4DEA645B6A1B65D1F2F386 /* assert.c */ = {isa = PBXFileReference; includeInIndex = 1; path = assert.c; sourceTree = ""; }; 2342803E73DA3E02533A2B9B7616F0C7 /* TSKNSURLConnectionDelegateProxy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKNSURLConnectionDelegateProxy.h; sourceTree = ""; }; + 23FD97FF253F95F9CE10E5DCBB87F40D /* Pods-TrustKitDemo-Swift-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TrustKitDemo-Swift-acknowledgements.plist"; sourceTree = ""; }; 287C7E0B6B509A541BC1525CF4C29A22 /* string_util.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = string_util.h; sourceTree = ""; }; 288A1FEC0658D8FB688BBAE9B97E2B7F /* ssl_pin_verifier.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = ssl_pin_verifier.m; sourceTree = ""; }; 2D825CA602AD8C4F0DC92C5C2F315F80 /* RSSwizzle.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = RSSwizzle.m; sourceTree = ""; }; 30A0A26583325B45FDF95C9957F01F02 /* TSKSPKIHashCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKSPKIHashCache.h; sourceTree = ""; }; 390AED1F5816C13CBAC37BC498AED5E4 /* domain_registry.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = domain_registry.h; sourceTree = ""; }; - 3E428D9FBEF4C1CD9DF056ADF8F2E2EB /* Pods-TrustKitDemoInSwift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = "Pods-TrustKitDemoInSwift.modulemap"; sourceTree = ""; }; - 3ECD098C376738289FBB30A9DF5E9F6E /* Pods-TrustKitDemo-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TrustKitDemo-resources.sh"; sourceTree = ""; }; 3F5091830E79DD11EB3A6934CE0FAF5E /* TrustKit-library.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "TrustKit-library.xcconfig"; path = "../TrustKit-library/TrustKit-library.xcconfig"; sourceTree = ""; }; 46C0B0ADDA1FAF1CF76BBA4C55725C04 /* TSKNSURLConnectionDelegateProxy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKNSURLConnectionDelegateProxy.m; sourceTree = ""; }; 49D1BCE0D2BFC86604429C1DB287EEE9 /* init_registry_tables.c */ = {isa = PBXFileReference; includeInIndex = 1; path = init_registry_tables.c; sourceTree = ""; }; 4CA3CFD14971562FA66B218152D30F35 /* TrustKit.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TrustKit.h; sourceTree = ""; }; - 5142815B08CFBAB5C0A32B7DE5259253 /* Pods-TrustKitDemoInSwift-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-TrustKitDemoInSwift-acknowledgements.markdown"; sourceTree = ""; }; + 4FF52C90B3281BB5C652DD900C5882BD /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51E3CD6B1F3DDAF522B22000B826EF88 /* TSKPinningValidator_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidator_Private.h; sourceTree = ""; }; + 539A135928AAC32FE73DE08C24FA94C5 /* libTrustKit-library.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libTrustKit-library.a"; path = "libTrustKit-library.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 5A38032B035203051E2B243A38998AFD /* vendor_identifier.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = vendor_identifier.h; sourceTree = ""; }; + 5B79DF6A5660F802609DC1D68B94AB97 /* Pods-TrustKitDemo-Swift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = "Pods-TrustKitDemo-Swift.modulemap"; sourceTree = ""; }; 5BCBC95D7B4F66D37FBA5785D4040B60 /* TSKTrustDecision.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKTrustDecision.h; sourceTree = ""; }; 613C850C57E8EE6B749CD2218C6AC1DD /* TrustKit-framework.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = "TrustKit-framework.modulemap"; sourceTree = ""; }; - 693C37A08B6303393A039F004657B2A8 /* Pods-TrustKitDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TrustKitDemo.release.xcconfig"; sourceTree = ""; }; 6D8DD333AC5AD1ABEAEDA6B5979E196E /* trie_search.c */ = {isa = PBXFileReference; includeInIndex = 1; path = trie_search.c; sourceTree = ""; }; + 6E1D35DBF9C615203135EEBECE77E9E0 /* Pods-TrustKitDemo-Swift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-TrustKitDemo-Swift-dummy.m"; sourceTree = ""; }; 702B9532D5B1159EF60B335D307B9C45 /* TSKSPKIHashCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKSPKIHashCache.m; sourceTree = ""; }; 72AE90ECEDFD50BADED53717FBB3892D /* TSKNSURLSessionDelegateProxy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKNSURLSessionDelegateProxy.h; sourceTree = ""; }; 7474D1481157E1DB7C600DA9295073F4 /* registry_types.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = registry_types.h; sourceTree = ""; }; 758A7792A0090736AA3ABBCB6FA8FAC4 /* vendor_identifier.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = vendor_identifier.m; sourceTree = ""; }; - 78E80D386FCEA4D031D86D86D5FA5020 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 7C64C309645EA64B17D511CF7075A4B5 /* TSKTrustKitConfig.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKTrustKitConfig.m; sourceTree = ""; }; - 7CBC78276DB001BCA4D874C083A7CF5C /* Pods-TrustKitDemoInSwift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-TrustKitDemoInSwift-dummy.m"; sourceTree = ""; }; 7D72855E63D1CD2D36950CCBD3D83279 /* TSKPublicKeyAlgorithm.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKPublicKeyAlgorithm.h; sourceTree = ""; }; - 7F05E7CAB631D2576B200F63D1347D3C /* Pods-TrustKitDemoInSwift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-TrustKitDemoInSwift-umbrella.h"; sourceTree = ""; }; - 8D556BFA8C03C6783987680194DA1ABB /* Pods-TrustKitDemo-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-TrustKitDemo-dummy.m"; sourceTree = ""; }; + 8A9F63DE2372F36F4E380FBF56316812 /* Pods-TrustKitDemo-ObjC-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TrustKitDemo-ObjC-resources.sh"; sourceTree = ""; }; 8F3CBC263BB58417499B0695F72AE703 /* TrustKit-framework-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TrustKit-framework-prefix.pch"; sourceTree = ""; }; 9144C1D132FDE48C1F12DC01A5CE33EE /* configuration_utils.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = configuration_utils.m; sourceTree = ""; }; + 917910670F125C74B7AFE74F1E966BCC /* Pods-TrustKitDemo-ObjC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TrustKitDemo-ObjC.release.xcconfig"; sourceTree = ""; }; 91F770C36742BA6AEE960724BEC192F5 /* TSKLog.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKLog.h; sourceTree = ""; }; 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 93B85AE4F9AB5B90DA67D644B42B9E7F /* Pods-TrustKitDemo-Swift-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TrustKitDemo-Swift-resources.sh"; sourceTree = ""; }; 954A0787F64D38E08D65EBC46C1CB9AE /* parse_configuration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = parse_configuration.m; sourceTree = ""; }; + 9B9C78E0CA68FEADB7832B3C6076E6A1 /* Pods_TrustKitDemo_Swift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_TrustKitDemo_Swift.framework; path = "Pods-TrustKitDemo-Swift.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; 9DF10471A2E6B2E0AC9FAC943DA4F8B7 /* TSKBackgroundReporter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKBackgroundReporter.m; sourceTree = ""; }; - 9EBB7E04712E8D9773923B6900DE4219 /* Pods-TrustKitDemoInSwift-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TrustKitDemoInSwift-resources.sh"; sourceTree = ""; }; A42809F0183618D391CF0950F836D339 /* registry_tables.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = registry_tables.h; sourceTree = ""; }; A54DFB58902F4C4023515556B6B09E3B /* TSKNSURLSessionDelegateProxy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKNSURLSessionDelegateProxy.m; sourceTree = ""; }; + A6E8098F0BBE8D8D786963CA3D82D073 /* Pods-TrustKitDemo-ObjC-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TrustKitDemo-ObjC-acknowledgements.plist"; sourceTree = ""; }; + A85E2994F549A864147232CF089D5185 /* TrustKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = TrustKit.framework; path = "TrustKit-framework.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; AF127C3A13A8630FD92058669BDFF057 /* TrustKit.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TrustKit.m; sourceTree = ""; }; + B2DEA3DAFF1CE47881CB23DBC8571FB7 /* Pods-TrustKitDemo-Swift-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-TrustKitDemo-Swift-acknowledgements.markdown"; sourceTree = ""; }; B4B06F0C8E473C389BA9B4A3A93875B9 /* TrustKit-framework-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TrustKit-framework-umbrella.h"; sourceTree = ""; }; B4BB3E7BC15983F00341A2742C1913D5 /* TSKReportsRateLimiter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKReportsRateLimiter.h; sourceTree = ""; }; - B6357BE7D3B70E53DE8B97D000753691 /* TrustKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = TrustKit.framework; path = "TrustKit-framework.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; B8F41D8A5EA2A86E468C52EA4956695F /* TSKPinningValidator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidator.h; sourceTree = ""; }; BCE910A2438B2E8646192F907B37E559 /* parse_configuration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = parse_configuration.h; sourceTree = ""; }; C085E02FF89804F50ADDA04F5A586E94 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; @@ -207,52 +206,53 @@ C7AB4A501B02D26F8C5A92B7A3AC21F5 /* reporting_utils.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = reporting_utils.m; sourceTree = ""; }; CF15A12A9DDFF9EF237480A92FB6635E /* TSKBackgroundReporter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKBackgroundReporter.h; sourceTree = ""; }; CF6CE1FAD8BB620AAF2879A0AC238BC6 /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKPinningValidatorResult.h; sourceTree = ""; }; + D2DABCE77A3D6B77AAD5A50E6726C6ED /* libPods-TrustKitDemo-ObjC.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-TrustKitDemo-ObjC.a"; path = "libPods-TrustKitDemo-ObjC.a"; sourceTree = BUILT_PRODUCTS_DIR; }; DA9D6A4C087F7256F7C1FC0976DEB80E /* TrustKit-framework-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "TrustKit-framework-dummy.m"; sourceTree = ""; }; + DAF18BDC075CDEE859F80F4984D22421 /* Pods-TrustKitDemo-ObjC-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TrustKitDemo-ObjC-frameworks.sh"; sourceTree = ""; }; DEADB11810EFB649591A4C39A6FCF739 /* registry_search.c */ = {isa = PBXFileReference; includeInIndex = 1; path = registry_search.c; sourceTree = ""; }; DEDE844CB2D002704EE36BB2178EB726 /* TSKTrustKitConfig.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = TSKTrustKitConfig.h; sourceTree = ""; }; E2073B4A7BB39C0B655B258E2805B398 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - E801D6CAA748B5F29CB8103D244D1FF6 /* Pods-TrustKitDemoInSwift-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TrustKitDemoInSwift-acknowledgements.plist"; sourceTree = ""; }; - EB5A620362CBE2FB1ECDCE58124DAAD7 /* libTrustKit-library.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libTrustKit-library.a"; path = "libTrustKit-library.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - ED54221395BDC8F9A7313801F3269B5E /* Pods-TrustKitDemoInSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TrustKitDemoInSwift.debug.xcconfig"; sourceTree = ""; }; - F1F1FF3F256A4766D61F73362872F323 /* Pods-TrustKitDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TrustKitDemo.debug.xcconfig"; sourceTree = ""; }; + E41771CACF5DFD14A8D517C50380FBE6 /* Pods-TrustKitDemo-Swift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TrustKitDemo-Swift.release.xcconfig"; sourceTree = ""; }; + E7E8C736BEA68AEB661A21B8F5EF6FD0 /* Pods-TrustKitDemo-Swift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TrustKitDemo-Swift.debug.xcconfig"; sourceTree = ""; }; + ECEF86B6ACF895EB79CA50F3E84BDB8D /* Pods-TrustKitDemo-ObjC-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-TrustKitDemo-ObjC-dummy.m"; sourceTree = ""; }; + F1169467FE49E2CED6F7256518E52431 /* Pods-TrustKitDemo-Swift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-TrustKitDemo-Swift-umbrella.h"; sourceTree = ""; }; F56169ECAF90EECAEB4D0EAA6C9D9F89 /* TSKPinFailureReport.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKPinFailureReport.m; sourceTree = ""; }; - FA1D62A70B82BE6CD3AA55E6B01CFEE2 /* libPods-TrustKitDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-TrustKitDemo.a"; path = "libPods-TrustKitDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; FF5DC8912CA18313277EF115E15BBE46 /* configuration_utils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = configuration_utils.h; sourceTree = ""; }; FFBF768AACDC0BA4BFEF5281933FFAA7 /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = TSKPinningValidatorResult.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 2E843DC24B5E78C662AD4501BDF315DE /* Frameworks */ = { + 5039A2D7C88751F3D52D4D0329006EA5 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5B1BB80A066D28AAE7D8C6124EC9B155 /* Foundation.framework in Frameworks */, + 2349CF2B44ECD4CD42215468689D6CBA /* Foundation.framework in Frameworks */, + A49F72F02F1B3F1680105F1E5F5D2C9F /* Security.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 381A1E592BA214AFBCE620C6F2AF4928 /* Frameworks */ = { + 87B5CAC265BD5C2641FE9663AAA5DF64 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BD8D9989CE8BC3F93EA53EE58D17619C /* Foundation.framework in Frameworks */, - 72C555F342E19A2EC42F8F977F34391F /* Security.framework in Frameworks */, + 8E75B100BEACB92BE67CEF219F5D5B68 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - A1A9E0D00EB77C8A9B6F766DABF173ED /* Frameworks */ = { + 8B537CBB811735996D1D4FE0E5DA0A1E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D59B2ACF7184AA8A53037C4096629788 /* Foundation.framework in Frameworks */, - 1DA5ED746B8D8FF91F7077A79CB37823 /* Security.framework in Frameworks */, + 75759A23AC605EF7116B49197CC23C89 /* Foundation.framework in Frameworks */, + 5A3CBF4D7DADA9D20C03F2554C148DE0 /* Security.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - AA5364440FE74A5EB2549E648523110B /* Frameworks */ = { + A4E1507B1A57B0AF08496C460F56166F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1B1A76932E64219790E2E06E572F4149 /* Foundation.framework in Frameworks */, + A9167EBE53079B8655FB9ED2BE4FB866 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -296,6 +296,24 @@ name = Frameworks; sourceTree = ""; }; + 4A0CD426D65DFDD8A0B7CA76373E78C2 /* Pods-TrustKitDemo-Swift */ = { + isa = PBXGroup; + children = ( + 4FF52C90B3281BB5C652DD900C5882BD /* Info.plist */, + 5B79DF6A5660F802609DC1D68B94AB97 /* Pods-TrustKitDemo-Swift.modulemap */, + B2DEA3DAFF1CE47881CB23DBC8571FB7 /* Pods-TrustKitDemo-Swift-acknowledgements.markdown */, + 23FD97FF253F95F9CE10E5DCBB87F40D /* Pods-TrustKitDemo-Swift-acknowledgements.plist */, + 6E1D35DBF9C615203135EEBECE77E9E0 /* Pods-TrustKitDemo-Swift-dummy.m */, + 0D7102761C989E9458FBD5ABA3C7551D /* Pods-TrustKitDemo-Swift-frameworks.sh */, + 93B85AE4F9AB5B90DA67D644B42B9E7F /* Pods-TrustKitDemo-Swift-resources.sh */, + F1169467FE49E2CED6F7256518E52431 /* Pods-TrustKitDemo-Swift-umbrella.h */, + E7E8C736BEA68AEB661A21B8F5EF6FD0 /* Pods-TrustKitDemo-Swift.debug.xcconfig */, + E41771CACF5DFD14A8D517C50380FBE6 /* Pods-TrustKitDemo-Swift.release.xcconfig */, + ); + name = "Pods-TrustKitDemo-Swift"; + path = "Target Support Files/Pods-TrustKitDemo-Swift"; + sourceTree = ""; + }; 4F990579C2E52276FD3C28227B49D60B /* iOS */ = { isa = PBXGroup; children = ( @@ -334,11 +352,11 @@ path = TrustKit; sourceTree = ""; }; - 62B87C472BE56B2B5272EA220E6D70A0 /* Targets Support Files */ = { + 65A6FE5272A39DBED1947273C2814FD8 /* Targets Support Files */ = { isa = PBXGroup; children = ( - EBE295A7B2F47FC05D6F114036C318DE /* Pods-TrustKitDemo */, - CAD29949506F5214AA1E4195617DEF21 /* Pods-TrustKitDemoInSwift */, + C21C883A752C8D5D7FF8ADF94244E506 /* Pods-TrustKitDemo-ObjC */, + 4A0CD426D65DFDD8A0B7CA76373E78C2 /* Pods-TrustKitDemo-Swift */, ); name = "Targets Support Files"; sourceTree = ""; @@ -366,8 +384,8 @@ 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, 7740D5BE604E7603DC28A50FEDA01593 /* Development Pods */, 433CD3331B6C3787F473C941B61FC68F /* Frameworks */, - DC40BB506A8976636DE9686F76485507 /* Products */, - 62B87C472BE56B2B5272EA220E6D70A0 /* Targets Support Files */, + 991DF28C8A402B543F7219C7E01A734A /* Products */, + 65A6FE5272A39DBED1947273C2814FD8 /* Targets Support Files */, ); sourceTree = ""; }; @@ -400,6 +418,17 @@ path = Swizzling; sourceTree = ""; }; + 991DF28C8A402B543F7219C7E01A734A /* Products */ = { + isa = PBXGroup; + children = ( + D2DABCE77A3D6B77AAD5A50E6726C6ED /* libPods-TrustKitDemo-ObjC.a */, + 539A135928AAC32FE73DE08C24FA94C5 /* libTrustKit-library.a */, + 9B9C78E0CA68FEADB7832B3C6076E6A1 /* Pods_TrustKitDemo_Swift.framework */, + A85E2994F549A864147232CF089D5185 /* TrustKit.framework */, + ); + name = Products; + sourceTree = ""; + }; B34275C890402B959F66E4AE3587D995 /* Framework */ = { isa = PBXGroup; children = ( @@ -409,32 +438,29 @@ path = Framework; sourceTree = ""; }; - C299194C544D4A4F96B68C3B17C88BAE /* TrustKit */ = { + C21C883A752C8D5D7FF8ADF94244E506 /* Pods-TrustKitDemo-ObjC */ = { isa = PBXGroup; children = ( - F43D9EAD9EC189005A50ECBE7A95FBAC /* Support Files */, - 5728737062D6BDD3A905B832832701D4 /* TrustKit */, + 1D3FD733E6BA440C82893942A17B8B35 /* Pods-TrustKitDemo-ObjC-acknowledgements.markdown */, + A6E8098F0BBE8D8D786963CA3D82D073 /* Pods-TrustKitDemo-ObjC-acknowledgements.plist */, + ECEF86B6ACF895EB79CA50F3E84BDB8D /* Pods-TrustKitDemo-ObjC-dummy.m */, + DAF18BDC075CDEE859F80F4984D22421 /* Pods-TrustKitDemo-ObjC-frameworks.sh */, + 8A9F63DE2372F36F4E380FBF56316812 /* Pods-TrustKitDemo-ObjC-resources.sh */, + 1FBADC8D14D8CD8CFDD5FD6D1CBBC454 /* Pods-TrustKitDemo-ObjC.debug.xcconfig */, + 917910670F125C74B7AFE74F1E966BCC /* Pods-TrustKitDemo-ObjC.release.xcconfig */, ); - name = TrustKit; - path = ../..; + name = "Pods-TrustKitDemo-ObjC"; + path = "Target Support Files/Pods-TrustKitDemo-ObjC"; sourceTree = ""; }; - CAD29949506F5214AA1E4195617DEF21 /* Pods-TrustKitDemoInSwift */ = { + C299194C544D4A4F96B68C3B17C88BAE /* TrustKit */ = { isa = PBXGroup; children = ( - 78E80D386FCEA4D031D86D86D5FA5020 /* Info.plist */, - 3E428D9FBEF4C1CD9DF056ADF8F2E2EB /* Pods-TrustKitDemoInSwift.modulemap */, - 5142815B08CFBAB5C0A32B7DE5259253 /* Pods-TrustKitDemoInSwift-acknowledgements.markdown */, - E801D6CAA748B5F29CB8103D244D1FF6 /* Pods-TrustKitDemoInSwift-acknowledgements.plist */, - 7CBC78276DB001BCA4D874C083A7CF5C /* Pods-TrustKitDemoInSwift-dummy.m */, - 0D17B1927DAF9BE7FD80DCBF0F6F537E /* Pods-TrustKitDemoInSwift-frameworks.sh */, - 9EBB7E04712E8D9773923B6900DE4219 /* Pods-TrustKitDemoInSwift-resources.sh */, - 7F05E7CAB631D2576B200F63D1347D3C /* Pods-TrustKitDemoInSwift-umbrella.h */, - ED54221395BDC8F9A7313801F3269B5E /* Pods-TrustKitDemoInSwift.debug.xcconfig */, - 016D7DEAA7EC198B0F5C41B1FB7C4FB5 /* Pods-TrustKitDemoInSwift.release.xcconfig */, + F43D9EAD9EC189005A50ECBE7A95FBAC /* Support Files */, + 5728737062D6BDD3A905B832832701D4 /* TrustKit */, ); - name = "Pods-TrustKitDemoInSwift"; - path = "Target Support Files/Pods-TrustKitDemoInSwift"; + name = TrustKit; + path = ../..; sourceTree = ""; }; D1E3067266D8D7A707815574E5377439 /* RSSwizzle */ = { @@ -447,32 +473,6 @@ path = RSSwizzle; sourceTree = ""; }; - DC40BB506A8976636DE9686F76485507 /* Products */ = { - isa = PBXGroup; - children = ( - FA1D62A70B82BE6CD3AA55E6B01CFEE2 /* libPods-TrustKitDemo.a */, - EB5A620362CBE2FB1ECDCE58124DAAD7 /* libTrustKit-library.a */, - 0F24CF15538FFB4930681D9FFAEB7E4A /* Pods_TrustKitDemoInSwift.framework */, - B6357BE7D3B70E53DE8B97D000753691 /* TrustKit.framework */, - ); - name = Products; - sourceTree = ""; - }; - EBE295A7B2F47FC05D6F114036C318DE /* Pods-TrustKitDemo */ = { - isa = PBXGroup; - children = ( - 0123B07E490339BB8BAD0ACF37E6A605 /* Pods-TrustKitDemo-acknowledgements.markdown */, - 09BC6E8AAC9C761AE184F9B5BF603D94 /* Pods-TrustKitDemo-acknowledgements.plist */, - 8D556BFA8C03C6783987680194DA1ABB /* Pods-TrustKitDemo-dummy.m */, - 12AEDEF058A1EB1FA200B4D41EE3D700 /* Pods-TrustKitDemo-frameworks.sh */, - 3ECD098C376738289FBB30A9DF5E9F6E /* Pods-TrustKitDemo-resources.sh */, - F1F1FF3F256A4766D61F73362872F323 /* Pods-TrustKitDemo.debug.xcconfig */, - 693C37A08B6303393A039F004657B2A8 /* Pods-TrustKitDemo.release.xcconfig */, - ); - name = "Pods-TrustKitDemo"; - path = "Target Support Files/Pods-TrustKitDemo"; - sourceTree = ""; - }; F1ECC4254DC2A61EF8CF094D38C4FC2F /* Dependencies */ = { isa = PBXGroup; children = ( @@ -516,158 +516,158 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 167F5F7F1F1B77DB8DFB9CD8668D4BE6 /* Headers */ = { + 257A824C5AB43B68C53602A6DC0A7ECD /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - D2FB40D0D6591DD87164BCFDED4A872B /* assert.h in Headers */, - A0B8CEAF67460C1A2D0C2C2A1A990F67 /* configuration_utils.h in Headers */, - 7F4626F2CD390F14D8980C333830EF5D /* domain_registry.h in Headers */, - AD1A357B9A460872C06FAC8C81227E75 /* parse_configuration.h in Headers */, - 54FA5C9512FCE077B16C79562E6003E4 /* registry_tables.h in Headers */, - 09E7DF568807E63A6676DB2AB69939FC /* registry_types.h in Headers */, - 20F2F8326AC10F0EDB95A5C120A42C9C /* reporting_utils.h in Headers */, - 706DFFF92262AD3A654930ADAC7372F7 /* RSSwizzle.h in Headers */, - B769BE1CE6DDDC128E3F160968194EAE /* ssl_pin_verifier.h in Headers */, - 0496FF3E58A206D1EFD7394E6456E700 /* string_util.h in Headers */, - 9D69A757AB397230EF3145ADA6B10188 /* trie_node.h in Headers */, - 789FDC0611E1E6643501301907740355 /* trie_search.h in Headers */, - FF3AB846370F0C93EF33D433416810BB /* TrustKit-framework-umbrella.h in Headers */, - FCAE6A2720A8FA62F35340372D7BE36D /* TrustKit-umbrella.h in Headers */, - FC5DF4AF969196C79FD959A2C8DD2DC2 /* TrustKit.h in Headers */, - E5BDBD5846B9BF559F9D4A790981E21D /* TSKBackgroundReporter.h in Headers */, - 56FF6CCDD07BEC720EFEF68856486006 /* TSKLog.h in Headers */, - F5DB7A40C7181F6A1B0C2C0193786A08 /* TSKNSURLConnectionDelegateProxy.h in Headers */, - 709C87892B7603F4AF2C250980453B11 /* TSKNSURLSessionDelegateProxy.h in Headers */, - 77FB5D09E1047C484301DD8A7CC193E0 /* TSKPinFailureReport.h in Headers */, - BAC36417573685B654AAB2A415F87DCB /* TSKPinningValidator.h in Headers */, - 8BC37767B9A244EE8600DBB66DBECFF6 /* TSKPinningValidator_Private.h in Headers */, - 91645A1F75435D30D1C0258EE9C3DDAE /* TSKPinningValidatorCallback.h in Headers */, - 5CEDC8B868290CC7CC1F128A5C6746E6 /* TSKPinningValidatorResult.h in Headers */, - D5C5AAAD941E06DA3AAB2EB4D02A3F3D /* TSKPublicKeyAlgorithm.h in Headers */, - 72D0920BDCF2DF9583CD74CE0EE4E7E5 /* TSKReportsRateLimiter.h in Headers */, - 912296DA496E76C4592D2BB27525DC58 /* TSKSPKIHashCache.h in Headers */, - 5448F6FAD1663B8B906AD4FEAE95C3BA /* TSKTrustDecision.h in Headers */, - 9C15CA27397D72DBEA030967D4B507E8 /* TSKTrustKitConfig.h in Headers */, - 00851FF4E5F19A667BF0C3D50912A1D5 /* vendor_identifier.h in Headers */, + 5C9851DF486E83D2B5D68A59CC5B7578 /* Pods-TrustKitDemo-Swift-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; - 7B16FD05B9E92AFB474A63796FD5DF89 /* Headers */ = { + 7BADEFF08395E08683EECBABD43826AB /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 96DF97DF055B8C04D3FE99DD46CA0AFB /* Pods-TrustKitDemoInSwift-umbrella.h in Headers */, + 76F350E09FD133150A23FAE8B1BBAA24 /* assert.h in Headers */, + B73C6060EFC50F93A195640CF48E56CC /* configuration_utils.h in Headers */, + 3FDD67E3F112D4029E6E70B4A45C35FE /* domain_registry.h in Headers */, + 26A4C8410DDF92FB1C0FCC778002DBB2 /* parse_configuration.h in Headers */, + 496F5B98C9C1D91D8D67BCD51DB289D8 /* registry_tables.h in Headers */, + A027DA49C8522BB65A184F6F03B896A4 /* registry_types.h in Headers */, + 0F2669E1BBD15A0AAA5E7354A52454DC /* reporting_utils.h in Headers */, + 887DDB38B72C236388C79AE99B8D1253 /* RSSwizzle.h in Headers */, + 2ADB4BF4F54B049D36A1277A47E7027D /* ssl_pin_verifier.h in Headers */, + FA3D5961789958AA16CB57C9F9D01776 /* string_util.h in Headers */, + AC0283E15C35744D49C0BAEF5414AE13 /* trie_node.h in Headers */, + 8C89A567583B082A7EDA3B62983E0B1E /* trie_search.h in Headers */, + 1AF8CB709D79C21E3CE6A3BCA8FF685D /* TrustKit-umbrella.h in Headers */, + A6DE30D424508289034FA0719F5348EE /* TrustKit.h in Headers */, + A7B589B664B0BBF87D408B2247283F54 /* TSKBackgroundReporter.h in Headers */, + 18547531B76466A24A3A2898EB1AFEEA /* TSKLog.h in Headers */, + 85BFB5B01842C42D3C89F120E639C9CF /* TSKNSURLConnectionDelegateProxy.h in Headers */, + 4FF4DC004448786D15BCBDF47A929DE3 /* TSKNSURLSessionDelegateProxy.h in Headers */, + F20233DAD60FFBBFC7C35E7C3DAC892B /* TSKPinFailureReport.h in Headers */, + 8DD8797D5F2AF1B9372BFBC75DE37B79 /* TSKPinningValidator.h in Headers */, + D43FC0639EFE4053973DACF3FEF46B1C /* TSKPinningValidator_Private.h in Headers */, + 840B89217F6CCAC8F1E4B0FF49A3F3E8 /* TSKPinningValidatorCallback.h in Headers */, + 4131DE094E65970425BC8C798EA49DFF /* TSKPinningValidatorResult.h in Headers */, + BD482292CDA6DBDE47B74F019FDB3D06 /* TSKPublicKeyAlgorithm.h in Headers */, + AA1AA4B4057C098E6875A59533B8B67E /* TSKReportsRateLimiter.h in Headers */, + 6092D0B8473F670117BC212E17EFAA6B /* TSKSPKIHashCache.h in Headers */, + 1F32FB8D5A9C5CF940B693B74A19DB74 /* TSKTrustDecision.h in Headers */, + A113AEF74DE30B4012818B65737DEA68 /* TSKTrustKitConfig.h in Headers */, + C18C8E523F810329CAF593B320E7915A /* vendor_identifier.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; - 9ED12E1A29377B6CEA4CA413059A40EF /* Headers */ = { + DBF7EC9D44C8FEE427A12BC18CCDD324 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 01D711A6F5D6E57DCCE8946388BEC7EB /* assert.h in Headers */, - F52DAB9D11F101AA30E8466DCFA2D8DB /* configuration_utils.h in Headers */, - 2B886EF817237F7DAD4AC1AB80F7CF87 /* domain_registry.h in Headers */, - 833FAAB1D09078D8B6F7F9EA9516C1C1 /* parse_configuration.h in Headers */, - F30A67B156D94EAD3CA444BE6C6C22A2 /* registry_tables.h in Headers */, - D3C5909E5B8CA9C393309391B4DDF1E0 /* registry_types.h in Headers */, - 1D1B754DB932A2FF2C34C4E5ECBBAB04 /* reporting_utils.h in Headers */, - 2F5C97F11E3C988C45B14C04EF182AFD /* RSSwizzle.h in Headers */, - 219DE7255483B7DDDE0FD5BD790D95A0 /* ssl_pin_verifier.h in Headers */, - BE1276DBEB9F2E09D061430025283640 /* string_util.h in Headers */, - B68CAC1D63D2A0D9BA930C508E48AE57 /* trie_node.h in Headers */, - 49634D47E0EDD9B498FDE2F5F4AA485E /* trie_search.h in Headers */, - B33CE417C5345363CD8E3F45CE44871B /* TrustKit-umbrella.h in Headers */, - 70DF98043F0FC61A986D90A016C32011 /* TrustKit.h in Headers */, - DDA29052B0CEC856600037D272593BBE /* TSKBackgroundReporter.h in Headers */, - 4C904BF58040C4F69A02BB90BDF6A202 /* TSKLog.h in Headers */, - 4550A31BDF3B61E7267B06F119CC6908 /* TSKNSURLConnectionDelegateProxy.h in Headers */, - 5167C676C50CB1FE15039D4BD956F2F8 /* TSKNSURLSessionDelegateProxy.h in Headers */, - 6C33C70511B2472C8E61AE03DEA53DC3 /* TSKPinFailureReport.h in Headers */, - E9DA954AAEAB44DCD066B35D746A8383 /* TSKPinningValidator.h in Headers */, - 557E4214E44510E8A529A1C9AE1C2107 /* TSKPinningValidator_Private.h in Headers */, - AA3DB3E374D6FE6C0D014CDBEED6D2A7 /* TSKPinningValidatorCallback.h in Headers */, - 3A27EAA7E79CBD53890906565E1C0734 /* TSKPinningValidatorResult.h in Headers */, - A818444547D3C148B87A15A0DF7EDA09 /* TSKPublicKeyAlgorithm.h in Headers */, - 173654C8C63381BFAC72BFA6BB2CEBB9 /* TSKReportsRateLimiter.h in Headers */, - DEB1C3B236A303CDBE1A28F1C168BFAC /* TSKSPKIHashCache.h in Headers */, - C99670E8EF0E6428E51A39F0A918EE59 /* TSKTrustDecision.h in Headers */, - 6444B457833221D3A3E74BF00BD47035 /* TSKTrustKitConfig.h in Headers */, - 9AB598D92256251C141732F7A987C16C /* vendor_identifier.h in Headers */, + 6295F9063CE4349E4851102A694B5741 /* assert.h in Headers */, + F62B9BB971170E78ECA007763D2F4AFA /* configuration_utils.h in Headers */, + E8E93E1F3718C337EF9C78EE8D5BD13D /* domain_registry.h in Headers */, + C952AD463705F146CE6B71D8C3EB2210 /* parse_configuration.h in Headers */, + 004354FB4F41F0DD78234D819F28DDDC /* registry_tables.h in Headers */, + 66B9FD231AF2D88153405A813593C335 /* registry_types.h in Headers */, + FBE458571AA2B583FD7855C246D0612F /* reporting_utils.h in Headers */, + 8DDD3513310B6E4CB4B86402513262EE /* RSSwizzle.h in Headers */, + C49C7DAC7BF663FE88BD7DEF94399A45 /* ssl_pin_verifier.h in Headers */, + E37075AC852B142EEBF20C38381317D0 /* string_util.h in Headers */, + 76C25CB355665666A63C0E730D1997B5 /* trie_node.h in Headers */, + 66AC2FC03F52276F6AC9090463E4236C /* trie_search.h in Headers */, + 0A8ED3AC1BFA68247F756DEDB82BE26F /* TrustKit-framework-umbrella.h in Headers */, + 3E112B6A3E04CA878502173CB6AB61B3 /* TrustKit-umbrella.h in Headers */, + 03AEEBD15BB9C84E81742690A23D1D18 /* TrustKit.h in Headers */, + 66D8F0A1B715F96354DD9A639F74D938 /* TSKBackgroundReporter.h in Headers */, + 3538C0BE09A9471CB21625ED10D2DAA0 /* TSKLog.h in Headers */, + C3BAAAD7474D9E0E906F9A565FAF2067 /* TSKNSURLConnectionDelegateProxy.h in Headers */, + 14523BA7C9E5817E55DAA66BC54D400C /* TSKNSURLSessionDelegateProxy.h in Headers */, + E811B736C73595510E79B393C3AF95FD /* TSKPinFailureReport.h in Headers */, + 6A42B8968AF803F5639AB1E42D3C6B01 /* TSKPinningValidator.h in Headers */, + A62BE79189117843926EE52E08AAF10D /* TSKPinningValidator_Private.h in Headers */, + A2DF2EBE59BE195E702DD3C43F1E1450 /* TSKPinningValidatorCallback.h in Headers */, + 022EC1DC5F635233B57571B94B4476FD /* TSKPinningValidatorResult.h in Headers */, + 8CD88CCF944E01EACEF7B296C8C630F0 /* TSKPublicKeyAlgorithm.h in Headers */, + 1C57FAEA2D624D1B8311B76EF8011E41 /* TSKReportsRateLimiter.h in Headers */, + 9D368A7215A8771F5085752F432761EF /* TSKSPKIHashCache.h in Headers */, + 5E11FBCB572E46A9DCE52F5BC5464B6E /* TSKTrustDecision.h in Headers */, + 048FE4E64D659B08DDB4C59AA427A591 /* TSKTrustKitConfig.h in Headers */, + DD35028DBD716A3F49B337278CA47E7B /* vendor_identifier.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 87D8BFF311952DE9568AECF82DA3DA72 /* Pods-TrustKitDemo */ = { + 49AA0D077304E9B19578BE6D4D3A0957 /* TrustKit-framework */ = { isa = PBXNativeTarget; - buildConfigurationList = 3F4D087FBB59C0C0031A8210D733DF6C /* Build configuration list for PBXNativeTarget "Pods-TrustKitDemo" */; + buildConfigurationList = E1B3A339629E75F1F12085B29E3AD5F4 /* Build configuration list for PBXNativeTarget "TrustKit-framework" */; buildPhases = ( - 71D5DCE034544BD1275241C3C874D51F /* Sources */, - AA5364440FE74A5EB2549E648523110B /* Frameworks */, + D8C8DF8D46164094EB15D3CBE6CD8F65 /* Sources */, + 5039A2D7C88751F3D52D4D0329006EA5 /* Frameworks */, + DBF7EC9D44C8FEE427A12BC18CCDD324 /* Headers */, ); buildRules = ( ); dependencies = ( - 07B60ADCA15C20ABCC78424BB0C4DEC2 /* PBXTargetDependency */, ); - name = "Pods-TrustKitDemo"; - productName = "Pods-TrustKitDemo"; - productReference = FA1D62A70B82BE6CD3AA55E6B01CFEE2 /* libPods-TrustKitDemo.a */; - productType = "com.apple.product-type.library.static"; + name = "TrustKit-framework"; + productName = "TrustKit-framework"; + productReference = A85E2994F549A864147232CF089D5185 /* TrustKit.framework */; + productType = "com.apple.product-type.framework"; }; - 9FEA5104EB31D1BB74EFD21D7B2F5C64 /* TrustKit-library */ = { + 57755AE54E832EED33F33C10CC9C7129 /* Pods-TrustKitDemo-Swift */ = { isa = PBXNativeTarget; - buildConfigurationList = 14688539BFEE0ED5D00198F0BCE0AEFF /* Build configuration list for PBXNativeTarget "TrustKit-library" */; + buildConfigurationList = 71B9EF7BCF992D1AEA1A5807A365D523 /* Build configuration list for PBXNativeTarget "Pods-TrustKitDemo-Swift" */; buildPhases = ( - B2456AA8D24A0A7B757053678162822B /* Sources */, - 381A1E592BA214AFBCE620C6F2AF4928 /* Frameworks */, - 9ED12E1A29377B6CEA4CA413059A40EF /* Headers */, + 275B686362775F338C22CC19EC5EA69C /* Sources */, + A4E1507B1A57B0AF08496C460F56166F /* Frameworks */, + 257A824C5AB43B68C53602A6DC0A7ECD /* Headers */, ); buildRules = ( ); dependencies = ( + BD05BD101F36F9A32485DA575DDAB0FF /* PBXTargetDependency */, ); - name = "TrustKit-library"; - productName = "TrustKit-library"; - productReference = EB5A620362CBE2FB1ECDCE58124DAAD7 /* libTrustKit-library.a */; - productType = "com.apple.product-type.library.static"; + name = "Pods-TrustKitDemo-Swift"; + productName = "Pods-TrustKitDemo-Swift"; + productReference = 9B9C78E0CA68FEADB7832B3C6076E6A1 /* Pods_TrustKitDemo_Swift.framework */; + productType = "com.apple.product-type.framework"; }; - B83210985026774772228F688CCC95C6 /* TrustKit-framework */ = { + AC6509DFF53910D2B29C0DC665357441 /* Pods-TrustKitDemo-ObjC */ = { isa = PBXNativeTarget; - buildConfigurationList = E7898298990891B5180B1E6C3AC85B49 /* Build configuration list for PBXNativeTarget "TrustKit-framework" */; + buildConfigurationList = D8F5AC50BDBF62FE74B0CABC5EEC3615 /* Build configuration list for PBXNativeTarget "Pods-TrustKitDemo-ObjC" */; buildPhases = ( - C56F05143613836D4B7B7557756532C3 /* Sources */, - A1A9E0D00EB77C8A9B6F766DABF173ED /* Frameworks */, - 167F5F7F1F1B77DB8DFB9CD8668D4BE6 /* Headers */, + CBFC82857877779E191AA202685BDA53 /* Sources */, + 87B5CAC265BD5C2641FE9663AAA5DF64 /* Frameworks */, ); buildRules = ( ); dependencies = ( + 238AD276F32714A7F58DFF8FEBD69C5F /* PBXTargetDependency */, ); - name = "TrustKit-framework"; - productName = "TrustKit-framework"; - productReference = B6357BE7D3B70E53DE8B97D000753691 /* TrustKit.framework */; - productType = "com.apple.product-type.framework"; + name = "Pods-TrustKitDemo-ObjC"; + productName = "Pods-TrustKitDemo-ObjC"; + productReference = D2DABCE77A3D6B77AAD5A50E6726C6ED /* libPods-TrustKitDemo-ObjC.a */; + productType = "com.apple.product-type.library.static"; }; - D88FD8F36049DD604B8668DFA98C68A5 /* Pods-TrustKitDemoInSwift */ = { + D0D24B967F83A557E2ADF69859044ED1 /* TrustKit-library */ = { isa = PBXNativeTarget; - buildConfigurationList = DBD04FF12003F172DB8618D055D9FE26 /* Build configuration list for PBXNativeTarget "Pods-TrustKitDemoInSwift" */; + buildConfigurationList = 7553827D4AA4DFECBF102E3656B9FBBF /* Build configuration list for PBXNativeTarget "TrustKit-library" */; buildPhases = ( - A87B1C6EAB2A5F5B25D35C3F9D5FAD00 /* Sources */, - 2E843DC24B5E78C662AD4501BDF315DE /* Frameworks */, - 7B16FD05B9E92AFB474A63796FD5DF89 /* Headers */, + D8D3041874DE858267CEB080E020E665 /* Sources */, + 8B537CBB811735996D1D4FE0E5DA0A1E /* Frameworks */, + 7BADEFF08395E08683EECBABD43826AB /* Headers */, ); buildRules = ( ); dependencies = ( - 4DBD6D901020E32C53A46C24B37A0492 /* PBXTargetDependency */, ); - name = "Pods-TrustKitDemoInSwift"; - productName = "Pods-TrustKitDemoInSwift"; - productReference = 0F24CF15538FFB4930681D9FFAEB7E4A /* Pods_TrustKitDemoInSwift.framework */; - productType = "com.apple.product-type.framework"; + name = "TrustKit-library"; + productName = "TrustKit-library"; + productReference = 539A135928AAC32FE73DE08C24FA94C5 /* libTrustKit-library.a */; + productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ @@ -686,110 +686,110 @@ en, ); mainGroup = 7DB346D0F39D3F0E887471402A8071AB; - productRefGroup = DC40BB506A8976636DE9686F76485507 /* Products */; + productRefGroup = 991DF28C8A402B543F7219C7E01A734A /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 87D8BFF311952DE9568AECF82DA3DA72 /* Pods-TrustKitDemo */, - D88FD8F36049DD604B8668DFA98C68A5 /* Pods-TrustKitDemoInSwift */, - B83210985026774772228F688CCC95C6 /* TrustKit-framework */, - 9FEA5104EB31D1BB74EFD21D7B2F5C64 /* TrustKit-library */, + AC6509DFF53910D2B29C0DC665357441 /* Pods-TrustKitDemo-ObjC */, + 57755AE54E832EED33F33C10CC9C7129 /* Pods-TrustKitDemo-Swift */, + 49AA0D077304E9B19578BE6D4D3A0957 /* TrustKit-framework */, + D0D24B967F83A557E2ADF69859044ED1 /* TrustKit-library */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ - 71D5DCE034544BD1275241C3C874D51F /* Sources */ = { + 275B686362775F338C22CC19EC5EA69C /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 160C63E448CBB115119659EFBF5C84E0 /* Pods-TrustKitDemo-dummy.m in Sources */, + 2D7B26D42AD755C9809921D25602B2DF /* Pods-TrustKitDemo-Swift-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - A87B1C6EAB2A5F5B25D35C3F9D5FAD00 /* Sources */ = { + CBFC82857877779E191AA202685BDA53 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9AAC9AC75846966966330B3C091B58F5 /* Pods-TrustKitDemoInSwift-dummy.m in Sources */, + FC913FD9925A648751454DBAB9FD7A37 /* Pods-TrustKitDemo-ObjC-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - B2456AA8D24A0A7B757053678162822B /* Sources */ = { + D8C8DF8D46164094EB15D3CBE6CD8F65 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6EE94D82A953FE4FB494AE05B1FA9AF5 /* assert.c in Sources */, - E9B3C163FC82567554CD8565481A0C08 /* configuration_utils.m in Sources */, - 819293E2A79D94FADDCB7F6ABB9D9D18 /* init_registry_tables.c in Sources */, - 7DF75D25C7A2C29B05D5787938C112ED /* parse_configuration.m in Sources */, - 304C39AEAB37E4B3ADE44D6985DE6AEE /* registry_search.c in Sources */, - 2A0DC2F329F20A507F8EEC15F3472667 /* reporting_utils.m in Sources */, - 1A43A1DCDCD6E62E13B2A735586524D7 /* RSSwizzle.m in Sources */, - AC4668E1382E6BCF1D1C0734115BA63E /* ssl_pin_verifier.m in Sources */, - 10484F688180E9B2CA3F443C47D9A9F5 /* trie_search.c in Sources */, - DBA72238E5490710B7FDFD0BC03C25B2 /* TrustKit-library-dummy.m in Sources */, - A30AFCE398CB19D1AB2F17FFB7015A39 /* TrustKit.m in Sources */, - 41EDB6EBC0B1F4135AC577C87C1C62CC /* TSKBackgroundReporter.m in Sources */, - 48F95C232FF22C9E855EB8DE6C5D3962 /* TSKNSURLConnectionDelegateProxy.m in Sources */, - A56A25BC5E423091DC2564DBD867FDA3 /* TSKNSURLSessionDelegateProxy.m in Sources */, - 7783BE79333283119B7E04ADE59D4D85 /* TSKPinFailureReport.m in Sources */, - 5A6ED2C021536CAD9C5323764E651092 /* TSKPinningValidator.m in Sources */, - 094A47B3A015A857FD9902D9DE4ACDE5 /* TSKPinningValidatorResult.m in Sources */, - E167E2DFDC1302944D5105477C99C410 /* TSKReportsRateLimiter.m in Sources */, - 5B3232D66E17FA89AA1114E851BB1260 /* TSKSPKIHashCache.m in Sources */, - 15F3A1BEBE2F0554D51CAE47B09088A4 /* TSKTrustKitConfig.m in Sources */, - AB6C9A86369041598BEDDB4299A62528 /* vendor_identifier.m in Sources */, + AA2BD97826D78BD2B1DBD491DA2CBEC2 /* assert.c in Sources */, + B95D0ED839B9C23A7B89A7296B6F18DE /* configuration_utils.m in Sources */, + 832FC2CF46BB1D184E8AF8414BA21121 /* init_registry_tables.c in Sources */, + 294A830DB1AFD306CACB637A9B035170 /* parse_configuration.m in Sources */, + C86BFE7D6CC160D267ABFC89AE53A8A4 /* registry_search.c in Sources */, + 84B4FBF55A46EDD1E8DE67E92A1E8D51 /* reporting_utils.m in Sources */, + 579FE0884C82E10079ACFDB543A8F0CC /* RSSwizzle.m in Sources */, + 15C98C5756DDB5F109292B770051B06F /* ssl_pin_verifier.m in Sources */, + DA318BDBCB897E80FD9C5FFCD7D552BE /* trie_search.c in Sources */, + 3073DC96CCE3AEFE3DFE718450D918D1 /* TrustKit-framework-dummy.m in Sources */, + 0DBE54CE70012005EE5922063ED217C3 /* TrustKit.m in Sources */, + 9DCD3652D914E8B7322D163B61A1CD02 /* TSKBackgroundReporter.m in Sources */, + 384ED2EA9F15274964C0777285944766 /* TSKNSURLConnectionDelegateProxy.m in Sources */, + 8F2D5ACAD8DDAD407E629B1E39CD1059 /* TSKNSURLSessionDelegateProxy.m in Sources */, + 3FC0BF8D93F3C0C4334DD213E78A9DB7 /* TSKPinFailureReport.m in Sources */, + A3FB9EA2AFD190DDF1D27573FFD37FC3 /* TSKPinningValidator.m in Sources */, + BF2BD62AC3ABC79F047D0CCA86F4C0BF /* TSKPinningValidatorResult.m in Sources */, + B52A096C4DA174370A66416CD916ACCA /* TSKReportsRateLimiter.m in Sources */, + A931975CC4FB799650EE03F16A011A44 /* TSKSPKIHashCache.m in Sources */, + 18513002955802A1B9EB947BD5273655 /* TSKTrustKitConfig.m in Sources */, + ADDAFB4B03B4B45F05BFFF3BD6B3575F /* vendor_identifier.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - C56F05143613836D4B7B7557756532C3 /* Sources */ = { + D8D3041874DE858267CEB080E020E665 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - EBB2FD29B682B615CC8D640704424C39 /* assert.c in Sources */, - 919B8CA0E63E044A39600170B1544B6B /* configuration_utils.m in Sources */, - CC0B39B3704D36B21E5ACA9F565FBE9E /* init_registry_tables.c in Sources */, - C9199DCE6F188384A1A3EB729854EBF1 /* parse_configuration.m in Sources */, - D0D9F705F65EEC35793699EE0630E8C3 /* registry_search.c in Sources */, - 36F5F40C0CA5EE29258B2AAD78A8D806 /* reporting_utils.m in Sources */, - 00A1CE305307E9CC1C56E14BD3969596 /* RSSwizzle.m in Sources */, - F005E6B1ED453CEEE86179016DD2E13F /* ssl_pin_verifier.m in Sources */, - 203488EF10B0D10B0854259A0BF35A27 /* trie_search.c in Sources */, - 498E8D9DEB07EB14E153CD47D176A452 /* TrustKit-framework-dummy.m in Sources */, - 0130E26071450C6CBA439373428DC4CF /* TrustKit.m in Sources */, - 29C6CB0A3A3842D9B704E0CC8B834731 /* TSKBackgroundReporter.m in Sources */, - 816D06005E4A6D71BBF791548E785E32 /* TSKNSURLConnectionDelegateProxy.m in Sources */, - 14469A265E74524CBC0448214CDB5A90 /* TSKNSURLSessionDelegateProxy.m in Sources */, - D5430056619E1C24EC30A1E3C5101E4B /* TSKPinFailureReport.m in Sources */, - 61F5F24D2CB7BCE8126273DEBA08CC73 /* TSKPinningValidator.m in Sources */, - A075C1FA71C3000765BE026D38199163 /* TSKPinningValidatorResult.m in Sources */, - EC5EA69669DCCD19AB04FECEE50644F7 /* TSKReportsRateLimiter.m in Sources */, - 345E1E08EB0E9881B89FDF6728B5CD4F /* TSKSPKIHashCache.m in Sources */, - 10C5AC1D48A7A2087833A1EFFCCDD65A /* TSKTrustKitConfig.m in Sources */, - EB6F5E04F094338BBD32B9C9A8715DE8 /* vendor_identifier.m in Sources */, + A77D59CC112CF07E837AA86AD62FE2C4 /* assert.c in Sources */, + D30642390168B84C919BD660AEF18C46 /* configuration_utils.m in Sources */, + 1A1B50A1AFAFF4D749AA05F158742BF4 /* init_registry_tables.c in Sources */, + CEAB22F63315F54CB6DBA250366B05A2 /* parse_configuration.m in Sources */, + AC3974B8BDA444509AC71ACE0FF42ED7 /* registry_search.c in Sources */, + 3713E5566E7B3FB46DF59ED4E5083374 /* reporting_utils.m in Sources */, + 1B356AEC046717BA497962422CEBE471 /* RSSwizzle.m in Sources */, + 115CD60C3473965724C6E672A0524619 /* ssl_pin_verifier.m in Sources */, + 8B3846B38A519F3282E4E886B6AA24B2 /* trie_search.c in Sources */, + 59AD8F54F23028FCF903A3312A19302B /* TrustKit-library-dummy.m in Sources */, + 27D4120761F3A1F3D785372E70EB3511 /* TrustKit.m in Sources */, + 37F0041FC41E3B109947490027F69C4D /* TSKBackgroundReporter.m in Sources */, + 23AE9FA740A405E75E2FD0446AB5BAB6 /* TSKNSURLConnectionDelegateProxy.m in Sources */, + FEE04AC30285F36C4764B8BA652F029E /* TSKNSURLSessionDelegateProxy.m in Sources */, + 65C071A8E653C39833309BB920E9F912 /* TSKPinFailureReport.m in Sources */, + E6DAEB91F82BE71570475B5262285DC4 /* TSKPinningValidator.m in Sources */, + EA7E1AACE8EC98FA147556EEE1467DC4 /* TSKPinningValidatorResult.m in Sources */, + DEB0B009BF02A5C12BF9360303CF55B4 /* TSKReportsRateLimiter.m in Sources */, + 4642FE6453432DC5396AE825543B6ABE /* TSKSPKIHashCache.m in Sources */, + 98278EC2C5D55B250F89C96C03587D2B /* TSKTrustKitConfig.m in Sources */, + C147251354BE7B9F639BA9C319470D2B /* vendor_identifier.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 07B60ADCA15C20ABCC78424BB0C4DEC2 /* PBXTargetDependency */ = { + 238AD276F32714A7F58DFF8FEBD69C5F /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "TrustKit-library"; - target = 9FEA5104EB31D1BB74EFD21D7B2F5C64 /* TrustKit-library */; - targetProxy = 8F8E6B6301DB3263048BCA55327BB7EC /* PBXContainerItemProxy */; + target = D0D24B967F83A557E2ADF69859044ED1 /* TrustKit-library */; + targetProxy = 5865D597AB012843366BF2B893359393 /* PBXContainerItemProxy */; }; - 4DBD6D901020E32C53A46C24B37A0492 /* PBXTargetDependency */ = { + BD05BD101F36F9A32485DA575DDAB0FF /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "TrustKit-framework"; - target = B83210985026774772228F688CCC95C6 /* TrustKit-framework */; - targetProxy = 8B87672562EA830C7D0B9598BC3E2568 /* PBXContainerItemProxy */; + target = 49AA0D077304E9B19578BE6D4D3A0957 /* TrustKit-framework */; + targetProxy = 259465232FCEBB5151A7F8FFCD2700F0 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 136E01C4BCDC36FB052CBB0C85D48203 /* Debug */ = { + 0B36663AF9C36764A396A2E72929509D /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 14E737FFF2A1232FF4058D088804405F /* TrustKit-framework.xcconfig */; buildSettings = { @@ -822,123 +822,133 @@ }; name = Debug; }; - 2ED2764514E1BAED3E29ABC71A932FD2 /* Release */ = { + 0EB988C1D34271108FCF16D2D74F005A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 016D7DEAA7EC198B0F5C41B1FB7C4FB5 /* Pods-TrustKitDemoInSwift.release.xcconfig */; + baseConfigurationReference = E7E8C736BEA68AEB661A21B8F5EF6FD0 /* Pods-TrustKitDemo-Swift.debug.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "Target Support Files/Pods-TrustKitDemoInSwift/Info.plist"; + INFOPLIST_FILE = "Target Support Files/Pods-TrustKitDemo-Swift/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.modulemap"; - MTL_ENABLE_DEBUG_INFO = NO; + MODULEMAP_FILE = "Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; OTHER_LDFLAGS = ""; OTHER_LIBTOOLFLAGS = ""; PODS_ROOT = "$(SRCROOT)"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = Pods_TrustKitDemoInSwift; + PRODUCT_NAME = Pods_TrustKitDemo_Swift; SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Release; + name = Debug; }; - 2F1F1A7A75D75597C5E2D9C0AF168816 /* Debug */ = { + 3CBB581707D6F0CC31932FA2652809E4 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = ED54221395BDC8F9A7313801F3269B5E /* Pods-TrustKitDemoInSwift.debug.xcconfig */; + baseConfigurationReference = 14E737FFF2A1232FF4058D088804405F /* TrustKit-framework.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "Target Support Files/Pods-TrustKitDemoInSwift/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/TrustKit-framework/TrustKit-framework-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/TrustKit-framework/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.modulemap"; - MTL_ENABLE_DEBUG_INFO = YES; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = Pods_TrustKitDemoInSwift; + MODULEMAP_FILE = "Target Support Files/TrustKit-framework/TrustKit-framework.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = TrustKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; - 49033EF4FFF5CF59029FA65898307615 /* Release */ = { + 40DFD63DA7E7D90EEB95C8FDE22EA68D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3F5091830E79DD11EB3A6934CE0FAF5E /* TrustKit-library.xcconfig */; + baseConfigurationReference = 1FBADC8D14D8CD8CFDD5FD6D1CBBC454 /* Pods-TrustKitDemo-ObjC.debug.xcconfig */; buildSettings = { "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; - GCC_PREFIX_HEADER = "Target Support Files/TrustKit-library/TrustKit-library-prefix.pch"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACH_O_TYPE = staticlib; + MTL_ENABLE_DEBUG_INFO = YES; OTHER_LDFLAGS = ""; OTHER_LIBTOOLFLAGS = ""; - PRIVATE_HEADERS_FOLDER_PATH = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; - PUBLIC_HEADERS_FOLDER_PATH = ""; SDKROOT = iphoneos; SKIP_INSTALL = YES; }; - name = Release; + name = Debug; }; - 4A194430634FD712D30114322D9B2623 /* Debug */ = { + 4C373DA77D1A5DA6A3439DA5A69A8916 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F1F1FF3F256A4766D61F73362872F323 /* Pods-TrustKitDemo.debug.xcconfig */; + baseConfigurationReference = E41771CACF5DFD14A8D517C50380FBE6 /* Pods-TrustKitDemo-Swift.release.xcconfig */; buildSettings = { + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - DEBUG_INFORMATION_FORMAT = dwarf; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "Target Support Files/Pods-TrustKitDemo-Swift/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; - MTL_ENABLE_DEBUG_INFO = YES; + MODULEMAP_FILE = "Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = ""; OTHER_LIBTOOLFLAGS = ""; PODS_ROOT = "$(SRCROOT)"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = Pods_TrustKitDemo_Swift; SDKROOT = iphoneos; SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; 4E487F173E6C9664F4E9E26B9635D23C /* Debug */ = { isa = XCBuildConfiguration; @@ -988,7 +998,7 @@ }; name = Debug; }; - 66A3528BF8627746C81ABA098B6CE5A4 /* Debug */ = { + 5DBDC325F8AC68A4F03DEEF22BA61E09 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 3F5091830E79DD11EB3A6934CE0FAF5E /* TrustKit-library.xcconfig */; buildSettings = { @@ -1011,62 +1021,6 @@ }; name = Debug; }; - 9FF35976CBBCEA46ABEBBDBA6661BC69 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 14E737FFF2A1232FF4058D088804405F /* TrustKit-framework.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREFIX_HEADER = "Target Support Files/TrustKit-framework/TrustKit-framework-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/TrustKit-framework/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/TrustKit-framework/TrustKit-framework.modulemap"; - MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_NAME = TrustKit; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - BA1DC5C4D666AECFF7AEBA258838550E /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 693C37A08B6303393A039F004657B2A8 /* Pods-TrustKitDemo.release.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACH_O_TYPE = staticlib; - MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - }; - name = Release; - }; BDD0139D6EB93FA375F887ABD62DAB2E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1111,50 +1065,96 @@ }; name = Release; }; + E95613B79A686A4C6FFDEB89B85506EC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F5091830E79DD11EB3A6934CE0FAF5E /* TrustKit-library.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/TrustKit-library/TrustKit-library-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + EE77DC28745552AB2C2400FAEF9B6464 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 917910670F125C74B7AFE74F1E966BCC /* Pods-TrustKitDemo-ObjC.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACH_O_TYPE = staticlib; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 14688539BFEE0ED5D00198F0BCE0AEFF /* Build configuration list for PBXNativeTarget "TrustKit-library" */ = { + 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { isa = XCConfigurationList; buildConfigurations = ( - 66A3528BF8627746C81ABA098B6CE5A4 /* Debug */, - 49033EF4FFF5CF59029FA65898307615 /* Release */, + 4E487F173E6C9664F4E9E26B9635D23C /* Debug */, + BDD0139D6EB93FA375F887ABD62DAB2E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { + 71B9EF7BCF992D1AEA1A5807A365D523 /* Build configuration list for PBXNativeTarget "Pods-TrustKitDemo-Swift" */ = { isa = XCConfigurationList; buildConfigurations = ( - 4E487F173E6C9664F4E9E26B9635D23C /* Debug */, - BDD0139D6EB93FA375F887ABD62DAB2E /* Release */, + 0EB988C1D34271108FCF16D2D74F005A /* Debug */, + 4C373DA77D1A5DA6A3439DA5A69A8916 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 3F4D087FBB59C0C0031A8210D733DF6C /* Build configuration list for PBXNativeTarget "Pods-TrustKitDemo" */ = { + 7553827D4AA4DFECBF102E3656B9FBBF /* Build configuration list for PBXNativeTarget "TrustKit-library" */ = { isa = XCConfigurationList; buildConfigurations = ( - 4A194430634FD712D30114322D9B2623 /* Debug */, - BA1DC5C4D666AECFF7AEBA258838550E /* Release */, + 5DBDC325F8AC68A4F03DEEF22BA61E09 /* Debug */, + E95613B79A686A4C6FFDEB89B85506EC /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - DBD04FF12003F172DB8618D055D9FE26 /* Build configuration list for PBXNativeTarget "Pods-TrustKitDemoInSwift" */ = { + D8F5AC50BDBF62FE74B0CABC5EEC3615 /* Build configuration list for PBXNativeTarget "Pods-TrustKitDemo-ObjC" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2F1F1A7A75D75597C5E2D9C0AF168816 /* Debug */, - 2ED2764514E1BAED3E29ABC71A932FD2 /* Release */, + 40DFD63DA7E7D90EEB95C8FDE22EA68D /* Debug */, + EE77DC28745552AB2C2400FAEF9B6464 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - E7898298990891B5180B1E6C3AC85B49 /* Build configuration list for PBXNativeTarget "TrustKit-framework" */ = { + E1B3A339629E75F1F12085B29E3AD5F4 /* Build configuration list for PBXNativeTarget "TrustKit-framework" */ = { isa = XCConfigurationList; buildConfigurations = ( - 136E01C4BCDC36FB052CBB0C85D48203 /* Debug */, - 9FF35976CBBCEA46ABEBBDBA6661BC69 /* Release */, + 0B36663AF9C36764A396A2E72929509D /* Debug */, + 3CBB581707D6F0CC31932FA2652809E4 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.markdown b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-acknowledgements.markdown similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.markdown rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-acknowledgements.markdown diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.plist b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-acknowledgements.plist similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-acknowledgements.plist rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-acknowledgements.plist diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-dummy.m b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-dummy.m new file mode 100644 index 00000000..fef8cdf0 --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_TrustKitDemo_ObjC : NSObject +@end +@implementation PodsDummy_Pods_TrustKitDemo_ObjC +@end diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-frameworks.sh b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-frameworks.sh similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-frameworks.sh rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-frameworks.sh diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-resources.sh b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-resources.sh similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-resources.sh rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC-resources.sh diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.debug.xcconfig b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC.debug.xcconfig similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.debug.xcconfig rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC.debug.xcconfig diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.release.xcconfig b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC.release.xcconfig similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo.release.xcconfig rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-ObjC/Pods-TrustKitDemo-ObjC.release.xcconfig diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Info.plist b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Info.plist similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Info.plist rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Info.plist diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.markdown b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-acknowledgements.markdown similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.markdown rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-acknowledgements.markdown diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.plist b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-acknowledgements.plist similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-acknowledgements.plist rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-acknowledgements.plist diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-dummy.m b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-dummy.m new file mode 100644 index 00000000..ba6f463c --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_TrustKitDemo_Swift : NSObject +@end +@implementation PodsDummy_Pods_TrustKitDemo_Swift +@end diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-frameworks.sh b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-frameworks.sh similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-frameworks.sh rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-frameworks.sh diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-resources.sh b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-resources.sh similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-resources.sh rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-resources.sh diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-umbrella.h b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-umbrella.h similarity index 57% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-umbrella.h rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-umbrella.h index e59c252e..73e5586a 100644 --- a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-umbrella.h +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift-umbrella.h @@ -11,6 +11,6 @@ #endif -FOUNDATION_EXPORT double Pods_TrustKitDemoInSwiftVersionNumber; -FOUNDATION_EXPORT const unsigned char Pods_TrustKitDemoInSwiftVersionString[]; +FOUNDATION_EXPORT double Pods_TrustKitDemo_SwiftVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_TrustKitDemo_SwiftVersionString[]; diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.debug.xcconfig b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.debug.xcconfig similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.debug.xcconfig rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.debug.xcconfig diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.modulemap b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.modulemap new file mode 100644 index 00000000..f33d6d2f --- /dev/null +++ b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.modulemap @@ -0,0 +1,6 @@ +framework module Pods_TrustKitDemo_Swift { + umbrella header "Pods-TrustKitDemo-Swift-umbrella.h" + + export * + module * { export * } +} diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.release.xcconfig b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.release.xcconfig similarity index 100% rename from TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.release.xcconfig rename to TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo-Swift/Pods-TrustKitDemo-Swift.release.xcconfig diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-dummy.m b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-dummy.m deleted file mode 100644 index 6c849e9b..00000000 --- a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemo/Pods-TrustKitDemo-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_TrustKitDemo : NSObject -@end -@implementation PodsDummy_Pods_TrustKitDemo -@end diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-dummy.m b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-dummy.m deleted file mode 100644 index cb7e272e..00000000 --- a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_TrustKitDemoInSwift : NSObject -@end -@implementation PodsDummy_Pods_TrustKitDemoInSwift -@end diff --git a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.modulemap b/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.modulemap deleted file mode 100644 index 9097037a..00000000 --- a/TrustKitDemo/Pods/Target Support Files/Pods-TrustKitDemoInSwift/Pods-TrustKitDemoInSwift.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Pods_TrustKitDemoInSwift { - umbrella header "Pods-TrustKitDemoInSwift-umbrella.h" - - export * - module * { export * } -} From 3fbc214a4062c7b3553e682cb40ea71100e3dab0 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Sat, 8 Jul 2017 17:36:40 -0400 Subject: [PATCH 124/126] Minor code updates to use Clang literal syntax, switch to some toll-free bridged types, and consistent use of __bridge annotations. All aimed at just removing some extra code without sacrificing readability. --- TrustKit/Pinning/TSKSPKIHashCache.m | 65 +++++++++++++++-------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/TrustKit/Pinning/TSKSPKIHashCache.m b/TrustKit/Pinning/TSKSPKIHashCache.m index bc610732..fa815e8f 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -66,7 +66,6 @@ sizeof(ecDsaSecp384r1Asn1Header) }; - @interface TSKSPKIHashCache () // Dictionnary to cache SPKI hashes instead of having to compute them on every connection @@ -142,11 +141,11 @@ - (instancetype)initWithIdentifier:(NSString *)uniqueIdentifier _keychainQueue = dispatch_queue_create("TSKSPKIKeychainLock", DISPATCH_QUEUE_SERIAL); // Cleanup the Keychain in case the App previously crashed NSMutableDictionary * publicKeyGet = [[NSMutableDictionary alloc] init]; - [publicKeyGet setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; - [publicKeyGet setObject:(kTSKKeychainPublicKeyTag) forKey:(__bridge id)kSecAttrApplicationTag]; - [publicKeyGet setObject:(__bridge id)(kCFBooleanTrue) forKey:(__bridge id)kSecReturnData]; + publicKeyGet[(__bridge id)kSecClass] = (__bridge id)kSecClassKey; + publicKeyGet[(__bridge id)kSecAttrApplicationTag] = kTSKKeychainPublicKeyTag; + publicKeyGet[(__bridge id)kSecReturnData] = @YES; dispatch_sync(_keychainQueue, ^{ - SecItemDelete((__bridge CFDictionaryRef)(publicKeyGet)); + SecItemDelete((__bridge CFDictionaryRef)publicKeyGet); }); #endif } @@ -295,20 +294,22 @@ @implementation TSKSPKIHashCache (Unified) // Use the unified SecKey API (specifically SecKeyCopyExternalRepresentation()) - (NSData *)getPublicKeyDataFromCertificate_unified:(SecCertificateRef)certificate { - SecKeyRef publicKey; - SecTrustRef tempTrust; + // Create an X509 trust using the using the certificate + SecTrustRef trust; SecPolicyRef policy = SecPolicyCreateBasicX509(); + SecTrustCreateWithCertificates(certificate, policy, &trust); - // Get a public key reference from the certificate - SecTrustCreateWithCertificates(certificate, policy, &tempTrust); - SecTrustEvaluate(tempTrust, NULL); - publicKey = SecTrustCopyPublicKey(tempTrust); + // Get a public key reference for the certificate from the trust + SecTrustEvaluate(trust, NULL); + SecKeyRef publicKey = SecTrustCopyPublicKey(trust); CFRelease(policy); - CFRelease(tempTrust); + CFRelease(trust); + // Obtain the public key bytes from the key reference CFDataRef publicKeyData = SecKeyCopyExternalRepresentation(publicKey, NULL); CFRelease(publicKey); - return (NSData *)CFBridgingRelease(publicKeyData); + + return (__bridge_transfer NSData *)publicKeyData; } @end @@ -335,30 +336,30 @@ - (NSData *)getPublicKeyDataFromCertificate_legacy_ios:(SecCertificateRef)certif CFRelease(tempTrust); - // Extract the actual bytes from the key reference using the Keychain + /// Extract the actual bytes from the key reference using the Keychain // Prepare the dictionary to add the key NSMutableDictionary *peerPublicKeyAdd = [[NSMutableDictionary alloc] init]; - [peerPublicKeyAdd setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; - [peerPublicKeyAdd setObject:kTSKKeychainPublicKeyTag forKey:(__bridge id)kSecAttrApplicationTag]; - [peerPublicKeyAdd setObject:(__bridge id)(publicKey) forKey:(__bridge id)kSecValueRef]; + peerPublicKeyAdd[(__bridge id)kSecClass] = (__bridge id)kSecClassKey; + peerPublicKeyAdd[(__bridge id)kSecAttrApplicationTag] = kTSKKeychainPublicKeyTag; + peerPublicKeyAdd[(__bridge id)kSecValueRef] = (__bridge id)publicKey; // Avoid issues with background fetching while the device is locked - [peerPublicKeyAdd setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible]; - + peerPublicKeyAdd[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly; + // Request the key's data to be returned - [peerPublicKeyAdd setObject:(__bridge id)(kCFBooleanTrue) forKey:(__bridge id)kSecReturnData]; + peerPublicKeyAdd[(__bridge id)kSecReturnData] = @YES; // Prepare the dictionary to retrieve and delete the key NSMutableDictionary * publicKeyGet = [[NSMutableDictionary alloc] init]; - [publicKeyGet setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; - [publicKeyGet setObject:(kTSKKeychainPublicKeyTag) forKey:(__bridge id)kSecAttrApplicationTag]; - [publicKeyGet setObject:(__bridge id)(kCFBooleanTrue) forKey:(__bridge id)kSecReturnData]; + publicKeyGet[(__bridge id)kSecClass] = (__bridge id)kSecClassKey; + publicKeyGet[(__bridge id)kSecAttrApplicationTag] = kTSKKeychainPublicKeyTag; + publicKeyGet[(__bridge id)kSecReturnData] = @YES; // Get the key bytes from the Keychain atomically dispatch_sync(self.keychainQueue, ^{ resultAdd = SecItemAdd((__bridge CFDictionaryRef) peerPublicKeyAdd, (void *)&publicKeyData); - resultDel = SecItemDelete((__bridge CFDictionaryRef)(publicKeyGet)); + resultDel = SecItemDelete((__bridge CFDictionaryRef)publicKeyGet); }); CFRelease(publicKey); @@ -388,9 +389,9 @@ - (NSData *)getPublicKeyDataFromCertificate_legacy_macos:(SecCertificateRef)cert CFErrorRef error = NULL; // SecCertificateCopyValues() is macOS only - NSArray *oids = [NSArray arrayWithObject:(__bridge id)(kSecOIDX509V1SubjectPublicKey)]; - CFDictionaryRef certificateValues = SecCertificateCopyValues(certificate, (__bridge CFArrayRef)(oids), &error); - if (certificateValues == NULL) + NSArray *oids = @[ (__bridge id)kSecOIDX509V1SubjectPublicKey ]; + NSDictionary *certificateValues = (__bridge_transfer NSDictionary *)SecCertificateCopyValues(certificate, (__bridge CFArrayRef)(oids), &error); + if (certificateValues == nil) { CFStringRef errorDescription = CFErrorCopyDescription(error); TSKLog(@"SecCertificateCopyValues() error: %@", errorDescription); @@ -399,15 +400,15 @@ - (NSData *)getPublicKeyDataFromCertificate_legacy_macos:(SecCertificateRef)cert return nil; } - for (NSString* fieldName in (__bridge NSDictionary *)certificateValues) + for (NSString *fieldName in certificateValues) { - NSDictionary *fieldDict = CFDictionaryGetValue(certificateValues, (__bridge const void *)(fieldName)); - if ([fieldDict[(__bridge __strong id)(kSecPropertyKeyLabel)] isEqualToString:@"Public Key Data"]) + NSDictionary *fieldDict = certificateValues[fieldName]; + NSString *fieldLabel = fieldDict[(__bridge id)kSecPropertyKeyLabel]; + if ([fieldLabel isEqualToString:@"Public Key Data"]) { - publicKeyData = fieldDict[(__bridge __strong id)(kSecPropertyKeyValue)]; + publicKeyData = fieldDict[(__bridge id)kSecPropertyKeyValue]; } } - CFRelease(certificateValues); return publicKeyData; } From a09437df2925bfe82ab855ddf979b4cedbcc22d9 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Thu, 13 Jul 2017 15:23:29 +0200 Subject: [PATCH 125/126] Rename singleton constructor; fixes #124 --- README.md | 4 ++-- TrustKit/TrustKit.h | 17 ++++++++--------- TrustKit/TrustKit.m | 8 ++++---- TrustKitDemo/TrustKitDemo/AppDelegate.m | 2 +- .../TrustKitDemoInSwift/AppDelegate.swift | 2 +- docs/getting-started.md | 2 +- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 24abd82d..45b110e4 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Alternatively, the pinning policy can be set programmatically: } }}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + [TrustKit initSharedInstanceWithConfiguration:trustKitConfig]; ``` The policy can also be set programmatically in Swift Apps: @@ -83,7 +83,7 @@ The policy can also be set programmatically in Swift Apps: "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=" ],]]] as [String : Any] - TrustKit.initialize(withConfiguration:trustKitConfig) + TrustKit.initSharedInstance(withConfiguration:trustKitConfig) ``` After TrustKit has been initialized, a diff --git a/TrustKit/TrustKit.h b/TrustKit/TrustKit.h index 1c1e25cb..a43a0384 100644 --- a/TrustKit/TrustKit.h +++ b/TrustKit/TrustKit.h @@ -30,15 +30,14 @@ NS_ASSUME_NONNULL_BEGIN configured for the App. In singleton mode, the policy can be set either: * By adding it to the App's _Info.plist_ under the `TSKConfiguration` key, or - * By programmatically supplying it using the `+initializeWithConfiguration:` method. + * By programmatically supplying it using the `+initSharedInstanceWithConfiguration:` method. In singleton mode, TrustKit can only be initialized once so only one of the two techniques should be used. For more complex Apps where multiple SSL pinning policies need to be used independently (for example within different frameworks), TrustKit can be used in "multi-instance" mode - by leveraging the `-initWithConfiguration:identifier:` method described at the end of this - page. + by leveraging the `-initWithConfiguration:` method described at the end of this page. A TrustKit pinning policy is a dictionary which contains some global, App-wide settings (of type `TSKGlobalConfigurationKey`) as well as domain-specific configuration keys @@ -64,8 +63,8 @@ NS_ASSUME_NONNULL_BEGIN ``` When setting the pinning policy programmatically, it has to be supplied to the - `initializeWithConfiguration:` method as a dictionary in order to initialize TrustKit. - For example: + `initSharedInstanceWithConfiguration:` method as a dictionary in order to initialize + TrustKit. For example: ``` NSDictionary *trustKitConfig = @@ -91,7 +90,7 @@ NS_ASSUME_NONNULL_BEGIN } }}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + [TrustKit initSharedInstanceWithConfiguration:trustKitConfig]; trustKit = [TrustKit sharedInstance]; ``` @@ -109,7 +108,7 @@ NS_ASSUME_NONNULL_BEGIN "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=" ],]]] as [String : Any] - TrustKit.initialize(withConfiguration:trustKitConfig) + TrustKit.initSharedInstance(withConfiguration:trustKitConfig) ``` After initialization, the `TrustKit` instance's `pinningValidator` should be used to implement @@ -128,11 +127,11 @@ NS_ASSUME_NONNULL_BEGIN already been initialized. */ -+ (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig; ++ (void)initSharedInstanceWithConfiguration:(NSDictionary *)trustKitConfig; /** - Retrieve the global TrustKit singleton instance. Raises an exception if `+initializeWithConfiguration:` + Retrieve the global TrustKit singleton instance. Raises an exception if `+initSharedInstanceWithConfiguration:` has not yet been invoked. @return the shared TrustKit singleton diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 202bf548..8ca7a6ce 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -81,14 +81,14 @@ + (instancetype)sharedInstance if (!sharedTrustKit) { // TrustKit should only be initialized once so we don't double swizzle or get into anything unexpected [NSException raise:@"TrustKit was not initialized" - format:@"TrustKit must be initialized using +initializeWithConfiguration: prior to accessing sharedInstance"]; + format:@"TrustKit must be initialized using +initSharedInstanceWithConfiguration: prior to accessing sharedInstance"]; } return sharedTrustKit; } -+ (void)initializeWithConfiguration:(NSDictionary *)trustKitConfig ++ (void)initSharedInstanceWithConfiguration:(NSDictionary *)trustKitConfig { - TSKLog(@"Configuration passed via explicit call to initializeWithConfiguration:"); + TSKLog(@"Configuration passed via explicit call to initSharedInstanceWithConfiguration:"); static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @@ -266,6 +266,6 @@ + (void)setLoggerBlock:(void (^)(NSString *))block if (trustKitConfigFromInfoPlist) { TSKLog(@"Configuration supplied via the App's Info.plist"); - [TrustKit initializeWithConfiguration:trustKitConfigFromInfoPlist]; + [TrustKit initSharedInstanceWithConfiguration:trustKitConfigFromInfoPlist]; } } diff --git a/TrustKitDemo/TrustKitDemo/AppDelegate.m b/TrustKitDemo/TrustKitDemo/AppDelegate.m index f1be1170..344038ba 100644 --- a/TrustKitDemo/TrustKitDemo/AppDelegate.m +++ b/TrustKitDemo/TrustKitDemo/AppDelegate.m @@ -69,7 +69,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( ] }}}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + [TrustKit initSharedInstanceWithConfiguration:trustKitConfig]; // Demonstrate how to receive pin validation notifications (only useful for performance/metrics) [TrustKit sharedInstance].pinningValidatorCallbackQueue =dispatch_get_main_queue(); diff --git a/TrustKitDemo/TrustKitDemoInSwift/AppDelegate.swift b/TrustKitDemo/TrustKitDemoInSwift/AppDelegate.swift index f755fb59..791246c3 100644 --- a/TrustKitDemo/TrustKitDemoInSwift/AppDelegate.swift +++ b/TrustKitDemo/TrustKitDemoInSwift/AppDelegate.swift @@ -50,7 +50,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ] ]] as [String : Any] - TrustKit.initialize(withConfiguration: trustKitConfig) + TrustKit.initSharedInstance(withConfiguration: trustKitConfig) return true } diff --git a/docs/getting-started.md b/docs/getting-started.md index b0d8f14c..9c684434 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -77,7 +77,7 @@ folder on disk. ### Configuring a Pinning Policy Enabling TrustKit within an App requires generating a pinning policy and then -initializing TrustKit by calling the `initializeWithConfiguration:` method with your +initializing TrustKit by calling the `initSharedInstanceWithConfiguration:` method with your pinning policy. A pinning policy is a dictionary of domain names and pinning configuration keys. From f30dc5d87eab360c328c59bbadd68942f7309a8b Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Thu, 13 Jul 2017 15:23:44 +0200 Subject: [PATCH 126/126] Re-generate documentation --- .../Classes/TSKPinningValidator.html | 2 +- .../Classes/TSKPinningValidatorResult.html | 2 +- docs/documentation/Classes/TrustKit.html | 27 +++++++++--------- .../Domain Configuration Keys.html | 2 +- .../documentation/Enums/TSKTrustDecision.html | 2 +- .../Enums/TSKTrustEvaluationResult.html | 2 +- .../Global Configuration Keys.html | 2 +- .../Implementing Pinning Validation.html | 2 +- docs/documentation/Initalizing TrustKit.html | 15 +++++----- docs/documentation/Other Constants.html | 2 +- .../Public Key Algorithm Keys.html | 2 +- .../Setting up a Validation Callback.html | 2 +- .../Classes/TSKPinningValidator.html | 2 +- .../Classes/TSKPinningValidatorResult.html | 2 +- .../Resources/Documents/Classes/TrustKit.html | 27 +++++++++--------- .../Documents/Domain Configuration Keys.html | 2 +- .../Documents/Enums/TSKTrustDecision.html | 2 +- .../Enums/TSKTrustEvaluationResult.html | 2 +- .../Documents/Global Configuration Keys.html | 2 +- .../Implementing Pinning Validation.html | 2 +- .../Documents/Initalizing TrustKit.html | 15 +++++----- .../Resources/Documents/Other Constants.html | 2 +- .../Documents/Public Key Algorithm Keys.html | 2 +- .../Setting up a Validation Callback.html | 2 +- .../Contents/Resources/Documents/index.html | 2 +- .../Contents/Resources/Documents/search.json | 2 +- .../Contents/Resources/docSet.dsidx | Bin 28672 -> 28672 bytes docs/documentation/index.html | 2 +- docs/documentation/search.json | 2 +- 29 files changed, 64 insertions(+), 68 deletions(-) diff --git a/docs/documentation/Classes/TSKPinningValidator.html b/docs/documentation/Classes/TSKPinningValidator.html index 1a8ab487..ef6d0e4b 100644 --- a/docs/documentation/Classes/TSKPinningValidator.html +++ b/docs/documentation/Classes/TSKPinningValidator.html @@ -397,7 +397,7 @@

    Return Value

    diff --git a/docs/documentation/Classes/TSKPinningValidatorResult.html b/docs/documentation/Classes/TSKPinningValidatorResult.html index 4286d221..6668eba5 100644 --- a/docs/documentation/Classes/TSKPinningValidatorResult.html +++ b/docs/documentation/Classes/TSKPinningValidatorResult.html @@ -397,7 +397,7 @@

    Declaration

    diff --git a/docs/documentation/Classes/TrustKit.html b/docs/documentation/Classes/TrustKit.html index 3dbbfa25..61fddf9a 100644 --- a/docs/documentation/Classes/TrustKit.html +++ b/docs/documentation/Classes/TrustKit.html @@ -186,7 +186,7 @@

    TrustKit

    In singleton mode, TrustKit can only be initialized once so only one of the two techniques @@ -194,8 +194,7 @@

    TrustKit

    For more complex Apps where multiple SSL pinning policies need to be used independently (for example within different frameworks), TrustKit can be used in multi-instance mode -by leveraging the -initWithConfiguration:identifier: method described at the end of this -page.

    +by leveraging the -initWithConfiguration: method described at the end of this page.

    A TrustKit pinning policy is a dictionary which contains some global, App-wide settings (of type TSKGlobalConfigurationKey) as well as domain-specific configuration keys @@ -219,8 +218,8 @@

    TrustKit

    When setting the pinning policy programmatically, it has to be supplied to the -initializeWithConfiguration: method as a dictionary in order to initialize TrustKit. -For example:

    +initSharedInstanceWithConfiguration: method as a dictionary in order to initialize +TrustKit. For example:

       NSDictionary *trustKitConfig =
      @{
        kTSKPinnedDomains : @{
    @@ -244,7 +243,7 @@ 

    TrustKit

    } }}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + [TrustKit initSharedInstanceWithConfiguration:trustKitConfig]; trustKit = [TrustKit sharedInstance];
    @@ -260,7 +259,7 @@

    TrustKit

    "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=" ],]]] as [String : Any] - TrustKit.initialize(withConfiguration:trustKitConfig) + TrustKit.initSharedInstance(withConfiguration:trustKitConfig)

    After initialization, the TrustKit instance’s pinningValidator should be used to implement @@ -283,9 +282,9 @@

    Usage in Singleton Mode

  • @@ -300,13 +299,13 @@

    Usage in Singleton Mode

    Declaration

    Objective-C

    -
    + (void)initializeWithConfiguration:
    +                          
    + (void)initSharedInstanceWithConfiguration:
         (nonnull NSDictionary<TSKGlobalConfigurationKey, id> *)trustKitConfig;

    Swift

    -
    class func initialize(withConfiguration trustKitConfig: [String : Any])
    +
    class func initSharedInstance(withConfiguration trustKitConfig: [String : Any])
    @@ -347,7 +346,7 @@

    Parameters

    -

    Retrieve the global TrustKit singleton instance. Raises an exception if +initializeWithConfiguration: +

    Retrieve the global TrustKit singleton instance. Raises an exception if +initSharedInstanceWithConfiguration: has not yet been invoked.

    @@ -622,7 +621,7 @@

    Declaration

  • diff --git a/docs/documentation/Domain Configuration Keys.html b/docs/documentation/Domain Configuration Keys.html index c0f6ecba..52a3e6cb 100644 --- a/docs/documentation/Domain Configuration Keys.html +++ b/docs/documentation/Domain Configuration Keys.html @@ -613,7 +613,7 @@

    Declaration

    diff --git a/docs/documentation/Enums/TSKTrustDecision.html b/docs/documentation/Enums/TSKTrustDecision.html index ad417a89..7f222575 100644 --- a/docs/documentation/Enums/TSKTrustDecision.html +++ b/docs/documentation/Enums/TSKTrustDecision.html @@ -295,7 +295,7 @@

    Declaration

    diff --git a/docs/documentation/Enums/TSKTrustEvaluationResult.html b/docs/documentation/Enums/TSKTrustEvaluationResult.html index 21a40fc0..a9e8c271 100644 --- a/docs/documentation/Enums/TSKTrustEvaluationResult.html +++ b/docs/documentation/Enums/TSKTrustEvaluationResult.html @@ -388,7 +388,7 @@

    Declaration

    diff --git a/docs/documentation/Global Configuration Keys.html b/docs/documentation/Global Configuration Keys.html index 29d0b84d..7c1d1bb3 100644 --- a/docs/documentation/Global Configuration Keys.html +++ b/docs/documentation/Global Configuration Keys.html @@ -380,7 +380,7 @@

    Declaration

    diff --git a/docs/documentation/Implementing Pinning Validation.html b/docs/documentation/Implementing Pinning Validation.html index b09a3e1d..db593353 100644 --- a/docs/documentation/Implementing Pinning Validation.html +++ b/docs/documentation/Implementing Pinning Validation.html @@ -273,7 +273,7 @@

    Declaration

    diff --git a/docs/documentation/Initalizing TrustKit.html b/docs/documentation/Initalizing TrustKit.html index b45493cf..ffd662db 100644 --- a/docs/documentation/Initalizing TrustKit.html +++ b/docs/documentation/Initalizing TrustKit.html @@ -200,7 +200,7 @@

    Initalizing TrustKit

    In singleton mode, TrustKit can only be initialized once so only one of the two techniques @@ -208,8 +208,7 @@

    Initalizing TrustKit

    For more complex Apps where multiple SSL pinning policies need to be used independently (for example within different frameworks), TrustKit can be used in multi-instance mode -by leveraging the -initWithConfiguration:identifier: method described at the end of this -page.

    +by leveraging the -initWithConfiguration: method described at the end of this page.

    A TrustKit pinning policy is a dictionary which contains some global, App-wide settings (of type TSKGlobalConfigurationKey) as well as domain-specific configuration keys @@ -233,8 +232,8 @@

    Initalizing TrustKit

    When setting the pinning policy programmatically, it has to be supplied to the -initializeWithConfiguration: method as a dictionary in order to initialize TrustKit. -For example:

    +initSharedInstanceWithConfiguration: method as a dictionary in order to initialize +TrustKit. For example:

       NSDictionary *trustKitConfig =
      @{
        kTSKPinnedDomains : @{
    @@ -258,7 +257,7 @@ 

    Initalizing TrustKit

    } }}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + [TrustKit initSharedInstanceWithConfiguration:trustKitConfig]; trustKit = [TrustKit sharedInstance];
    @@ -274,7 +273,7 @@

    Initalizing TrustKit

    "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=" ],]]] as [String : Any] - TrustKit.initialize(withConfiguration:trustKitConfig) + TrustKit.initSharedInstance(withConfiguration:trustKitConfig)

    After initialization, the TrustKit instance’s pinningValidator should be used to implement @@ -306,7 +305,7 @@

    Declaration

    diff --git a/docs/documentation/Other Constants.html b/docs/documentation/Other Constants.html index 108f58b6..7ba63f30 100644 --- a/docs/documentation/Other Constants.html +++ b/docs/documentation/Other Constants.html @@ -221,7 +221,7 @@

    Declaration

    diff --git a/docs/documentation/Public Key Algorithm Keys.html b/docs/documentation/Public Key Algorithm Keys.html index cf4e626c..366b8d68 100644 --- a/docs/documentation/Public Key Algorithm Keys.html +++ b/docs/documentation/Public Key Algorithm Keys.html @@ -355,7 +355,7 @@

    Declaration

    diff --git a/docs/documentation/Setting up a Validation Callback.html b/docs/documentation/Setting up a Validation Callback.html index 969e1971..ce5d289c 100644 --- a/docs/documentation/Setting up a Validation Callback.html +++ b/docs/documentation/Setting up a Validation Callback.html @@ -345,7 +345,7 @@

    Declaration

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidator.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidator.html index 1a8ab487..ef6d0e4b 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidator.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidator.html @@ -397,7 +397,7 @@

    Return Value

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidatorResult.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidatorResult.html index 4286d221..6668eba5 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidatorResult.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TSKPinningValidatorResult.html @@ -397,7 +397,7 @@

    Declaration

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TrustKit.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TrustKit.html index 3dbbfa25..61fddf9a 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TrustKit.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Classes/TrustKit.html @@ -186,7 +186,7 @@

    TrustKit

    In singleton mode, TrustKit can only be initialized once so only one of the two techniques @@ -194,8 +194,7 @@

    TrustKit

    For more complex Apps where multiple SSL pinning policies need to be used independently (for example within different frameworks), TrustKit can be used in multi-instance mode -by leveraging the -initWithConfiguration:identifier: method described at the end of this -page.

    +by leveraging the -initWithConfiguration: method described at the end of this page.

    A TrustKit pinning policy is a dictionary which contains some global, App-wide settings (of type TSKGlobalConfigurationKey) as well as domain-specific configuration keys @@ -219,8 +218,8 @@

    TrustKit

    When setting the pinning policy programmatically, it has to be supplied to the -initializeWithConfiguration: method as a dictionary in order to initialize TrustKit. -For example:

    +initSharedInstanceWithConfiguration: method as a dictionary in order to initialize +TrustKit. For example:

       NSDictionary *trustKitConfig =
      @{
        kTSKPinnedDomains : @{
    @@ -244,7 +243,7 @@ 

    TrustKit

    } }}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + [TrustKit initSharedInstanceWithConfiguration:trustKitConfig]; trustKit = [TrustKit sharedInstance];
    @@ -260,7 +259,7 @@

    TrustKit

    "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=" ],]]] as [String : Any] - TrustKit.initialize(withConfiguration:trustKitConfig) + TrustKit.initSharedInstance(withConfiguration:trustKitConfig)

    After initialization, the TrustKit instance’s pinningValidator should be used to implement @@ -283,9 +282,9 @@

    Usage in Singleton Mode

  • @@ -300,13 +299,13 @@

    Usage in Singleton Mode

    Declaration

    Objective-C

    -
    + (void)initializeWithConfiguration:
    +                          
    + (void)initSharedInstanceWithConfiguration:
         (nonnull NSDictionary<TSKGlobalConfigurationKey, id> *)trustKitConfig;

    Swift

    -
    class func initialize(withConfiguration trustKitConfig: [String : Any])
    +
    class func initSharedInstance(withConfiguration trustKitConfig: [String : Any])
    @@ -347,7 +346,7 @@

    Parameters

    -

    Retrieve the global TrustKit singleton instance. Raises an exception if +initializeWithConfiguration: +

    Retrieve the global TrustKit singleton instance. Raises an exception if +initSharedInstanceWithConfiguration: has not yet been invoked.

    @@ -622,7 +621,7 @@

    Declaration

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Domain Configuration Keys.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Domain Configuration Keys.html index c0f6ecba..52a3e6cb 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Domain Configuration Keys.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Domain Configuration Keys.html @@ -613,7 +613,7 @@

    Declaration

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustDecision.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustDecision.html index ad417a89..7f222575 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustDecision.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustDecision.html @@ -295,7 +295,7 @@

    Declaration

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustEvaluationResult.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustEvaluationResult.html index 21a40fc0..a9e8c271 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustEvaluationResult.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Enums/TSKTrustEvaluationResult.html @@ -388,7 +388,7 @@

    Declaration

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Global Configuration Keys.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Global Configuration Keys.html index 29d0b84d..7c1d1bb3 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Global Configuration Keys.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Global Configuration Keys.html @@ -380,7 +380,7 @@

    Declaration

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Implementing Pinning Validation.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Implementing Pinning Validation.html index b09a3e1d..db593353 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Implementing Pinning Validation.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Implementing Pinning Validation.html @@ -273,7 +273,7 @@

    Declaration

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Initalizing TrustKit.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Initalizing TrustKit.html index b45493cf..ffd662db 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Initalizing TrustKit.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Initalizing TrustKit.html @@ -200,7 +200,7 @@

    Initalizing TrustKit

    In singleton mode, TrustKit can only be initialized once so only one of the two techniques @@ -208,8 +208,7 @@

    Initalizing TrustKit

    For more complex Apps where multiple SSL pinning policies need to be used independently (for example within different frameworks), TrustKit can be used in multi-instance mode -by leveraging the -initWithConfiguration:identifier: method described at the end of this -page.

    +by leveraging the -initWithConfiguration: method described at the end of this page.

    A TrustKit pinning policy is a dictionary which contains some global, App-wide settings (of type TSKGlobalConfigurationKey) as well as domain-specific configuration keys @@ -233,8 +232,8 @@

    Initalizing TrustKit

    When setting the pinning policy programmatically, it has to be supplied to the -initializeWithConfiguration: method as a dictionary in order to initialize TrustKit. -For example:

    +initSharedInstanceWithConfiguration: method as a dictionary in order to initialize +TrustKit. For example:

       NSDictionary *trustKitConfig =
      @{
        kTSKPinnedDomains : @{
    @@ -258,7 +257,7 @@ 

    Initalizing TrustKit

    } }}; - [TrustKit initializeWithConfiguration:trustKitConfig]; + [TrustKit initSharedInstanceWithConfiguration:trustKitConfig]; trustKit = [TrustKit sharedInstance];
    @@ -274,7 +273,7 @@

    Initalizing TrustKit

    "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=" ],]]] as [String : Any] - TrustKit.initialize(withConfiguration:trustKitConfig) + TrustKit.initSharedInstance(withConfiguration:trustKitConfig)

    After initialization, the TrustKit instance’s pinningValidator should be used to implement @@ -306,7 +305,7 @@

    Declaration

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Other Constants.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Other Constants.html index 108f58b6..7ba63f30 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Other Constants.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Other Constants.html @@ -221,7 +221,7 @@

    Declaration

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Public Key Algorithm Keys.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Public Key Algorithm Keys.html index cf4e626c..366b8d68 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Public Key Algorithm Keys.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Public Key Algorithm Keys.html @@ -355,7 +355,7 @@

    Declaration

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Setting up a Validation Callback.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Setting up a Validation Callback.html index 969e1971..ce5d289c 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Setting up a Validation Callback.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/Setting up a Validation Callback.html @@ -345,7 +345,7 @@

    Declaration

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/index.html b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/index.html index 9ff1db6c..942dc120 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/index.html +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/index.html @@ -192,7 +192,7 @@

    TrustKit Documentation

    diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/search.json b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/search.json index acc0fc86..26178705 100644 --- a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/search.json +++ b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/Documents/search.json @@ -1 +1 @@ -{"Other Constants.html#/c:@TrustKitVersion":{"name":"TrustKitVersion","abstract":"

    The version of TrustKit, such as 1.4.0.

    "},"Public Key Algorithm Keys.html#/c:TSKTrustKitConfig.h@T@TSKSupportedAlgorithm":{"name":"TSKSupportedAlgorithm","abstract":"

    A public key algorithm supported by TrustKit for generating the SSL pin for a certificate.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmRsa2048":{"name":"kTSKAlgorithmRsa2048","abstract":"

    RSA 2048.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmRsa4096":{"name":"kTSKAlgorithmRsa4096","abstract":"

    RSA 4096.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmEcDsaSecp256r1":{"name":"kTSKAlgorithmEcDsaSecp256r1","abstract":"

    ECDSA with secp256r1 curve.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmEcDsaSecp384r1":{"name":"kTSKAlgorithmEcDsaSecp384r1","abstract":"

    ECDSA with secp384r1 curve.

    "},"Domain Configuration Keys.html#/c:TSKTrustKitConfig.h@T@TSKDomainConfigurationKey":{"name":"TSKDomainConfigurationKey","abstract":"

    A domain-specific configuration key (to defined for a domain under the kTSKPinnedDomains"},"Domain Configuration Keys.html#/c:@kTSKPublicKeyHashes":{"name":"kTSKPublicKeyHashes","abstract":"

    An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate’s"},"Domain Configuration Keys.html#/c:@kTSKPublicKeyAlgorithms":{"name":"kTSKPublicKeyAlgorithms","abstract":"

    An array of TSKSupportedAlgorithm constants to specify the public key algorithms for the"},"Domain Configuration Keys.html#/c:@kTSKEnforcePinning":{"name":"kTSKEnforcePinning","abstract":"

    A boolean. If set to NO, TrustKit will not block SSL connections that caused a pin or"},"Domain Configuration Keys.html#/c:@kTSKIncludeSubdomains":{"name":"kTSKIncludeSubdomains","abstract":"

    A boolean. If set to YES, also pin all the subdomains of the specified domain; default"},"Domain Configuration Keys.html#/c:@kTSKExcludeSubdomainFromParentPolicy":{"name":"kTSKExcludeSubdomainFromParentPolicy","abstract":"

    A boolean. If set to YES, TrustKit will not pin this specific domain if kTSKIncludeSubdomains"},"Domain Configuration Keys.html#/c:@kTSKReportUris":{"name":"kTSKReportUris","abstract":"

    An array of URLs to which pin validation failures should be reported.

    "},"Domain Configuration Keys.html#/c:@kTSKDisableDefaultReportUri":{"name":"kTSKDisableDefaultReportUri","abstract":"

    A boolean. If set to YES, the default report URL for sending pin failure reports will"},"Domain Configuration Keys.html#/c:@kTSKExpirationDate":{"name":"kTSKExpirationDate","abstract":"

    A string containing the date, in yyyy-MM-dd format, on which the domain’s configured SSL"},"Domain Configuration Keys.html#/c:@kTSKAdditionalTrustAnchors":{"name":"kTSKAdditionalTrustAnchors","abstract":"

    An array of strings representing additional trust anchors usable for validating"},"Global Configuration Keys.html#/c:TSKTrustKitConfig.h@T@TSKGlobalConfigurationKey":{"name":"TSKGlobalConfigurationKey","abstract":"

    A global, App-wide configuration key that can be set in the pinning policy.

    "},"Global Configuration Keys.html#/c:@kTSKSwizzleNetworkDelegates":{"name":"kTSKSwizzleNetworkDelegates","abstract":"

    A boolean. If set to YES, TrustKit will perform method swizzling on the App’s"},"Global Configuration Keys.html#/c:@kTSKPinnedDomains":{"name":"kTSKPinnedDomains","abstract":"

    A dictionary with domains (such as www.domain.com) as keys and dictionaries as values.

    "},"Global Configuration Keys.html#/c:@kTSKIgnorePinningForUserDefinedTrustAnchors":{"name":"kTSKIgnorePinningForUserDefinedTrustAnchors","abstract":"

    A boolean. If set to YES, pinning validation will be skipped if the server’s certificate"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationSuccess":{"name":"TSKTrustEvaluationSuccess","abstract":"

    The server trust was succesfully evaluated and contained at least one of the configured pins.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedNoMatchingPin":{"name":"TSKTrustEvaluationFailedNoMatchingPin","abstract":"

    The server trust was succesfully evaluated but did not contain any of the configured pins.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedInvalidCertificateChain":{"name":"TSKTrustEvaluationFailedInvalidCertificateChain","abstract":"

    The server trust’s evaluation failed: the server’s certificate chain is not trusted.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationErrorInvalidParameters":{"name":"TSKTrustEvaluationErrorInvalidParameters","abstract":"

    The server trust could not be evaluated due to invalid parameters.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedUserDefinedTrustAnchor":{"name":"TSKTrustEvaluationFailedUserDefinedTrustAnchor","abstract":"

    The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to the macOS trust store). Only available on macOS.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationErrorCouldNotGenerateSpkiHash":{"name":"TSKTrustEvaluationErrorCouldNotGenerateSpkiHash","abstract":"

    The server trust could not be evaluated due to an error when trying to generate the certificate’s subject public key info hash. On iOS 9 or below, this could be caused by a Keychain failure when trying to extract the certificate’s public key bytes.

    ","parent_name":"TSKTrustEvaluationResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)serverHostname":{"name":"serverHostname","abstract":"

    The hostname of the server SSL pinning validation was performed against.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)serverTrust":{"name":"serverTrust","abstract":"

    The original SecTrustRef that validation was performed against.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)evaluationResult":{"name":"evaluationResult","abstract":"

    The result of validating the server’s certificate chain against the set of SSL pins configured for","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)finalTrustDecision":{"name":"finalTrustDecision","abstract":"

    The trust decision returned for this connection, which describes whether the connection should be blocked","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)validationDuration":{"name":"validationDuration","abstract":"

    The time it took for the SSL pinning validation to be performed.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)certificateChain":{"name":"certificateChain","abstract":"

    The certificate chain extracted from the serverTrust as PEM-formatted certificates. This is the","parent_name":"TSKPinningValidatorResult"},"Setting up a Validation Callback.html#/c:TSKPinningValidatorCallback.h@T@TSKPinningValidatorCallback":{"name":"TSKPinningValidatorCallback","abstract":"

    A block that can be set in a TrustKit instance to be invoked for every request that is going through"},"Classes/TSKPinningValidatorResult.html":{"name":"TSKPinningValidatorResult","abstract":"

    A TSKPinningValidatorResult instance contains all the details regarding a pinning validation"},"Setting up a Validation Callback.html#/c:TSKPinningValidatorCallback.h@T@TKSDomainPinningPolicy":{"name":"TKSDomainPinningPolicy","abstract":"

    The pinning policy set for a specific hostname.

    "},"Enums/TSKTrustEvaluationResult.html":{"name":"TSKTrustEvaluationResult","abstract":"

    Possible return values when verifying a server’s identity against a set of pins.

    "},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionShouldAllowConnection":{"name":"TSKTrustDecisionShouldAllowConnection","abstract":"

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be allowed.","parent_name":"TSKTrustDecision"},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionShouldBlockConnection":{"name":"TSKTrustDecisionShouldBlockConnection","abstract":"

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be blocked.","parent_name":"TSKTrustDecision"},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionDomainNotPinned":{"name":"TSKTrustDecisionDomainNotPinned","abstract":"

    No pinning policy was configured for this domain and TrustKit did not validate the server’s identity.","parent_name":"TSKTrustDecision"},"Classes/TSKPinningValidator.html#/c:objc(cs)TSKPinningValidator(im)handleChallenge:completionHandler:":{"name":"-handleChallenge:completionHandler:","abstract":"

    Helper method for handling authentication challenges received within a NSURLSessionDelegate, NSURLSessionTaskDelegate or WKNavigationDelegate.

    ","parent_name":"TSKPinningValidator"},"Classes/TSKPinningValidator.html#/c:objc(cs)TSKPinningValidator(im)evaluateTrust:forHostname:":{"name":"-evaluateTrust:forHostname:","abstract":"

    Evaluate the supplied server trust against the SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent.

    ","parent_name":"TSKPinningValidator"},"Classes/TSKPinningValidator.html":{"name":"TSKPinningValidator","abstract":"

    A TSKPinningValidator instance can be used to verify a server’s identity against an SSL pinning policy.

    "},"Enums/TSKTrustDecision.html":{"name":"TSKTrustDecision","abstract":"

    Possible return values when verifying a server’s identity against an SSL pinning policy.

    "},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)initializeWithConfiguration:":{"name":"+initializeWithConfiguration:","abstract":"

    Initialize the global TrustKit singleton with the supplied pinning policy.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)sharedInstance":{"name":"+sharedInstance","abstract":"

    Retrieve the global TrustKit singleton instance. Raises an exception if +initializeWithConfiguration:","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidator":{"name":"pinningValidator","abstract":"

    Retrieve the validator instance conforming to the pinning policy of this TrustKit instance.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidatorCallback":{"name":"pinningValidatorCallback","abstract":"

    Register a block to be invoked for every request that is going through TrustKit’s pinning","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidatorCallbackQueue":{"name":"pinningValidatorCallbackQueue","abstract":"

    Queue on which to invoke the pinningValidatorCallback; default value is the main queue.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(im)initWithConfiguration:":{"name":"-initWithConfiguration:","abstract":"

    Initialize a local TrustKit instance with the supplied SSL pinning policy configuration.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)setLoggerBlock:":{"name":"+setLoggerBlock:","abstract":"

    Set the global logger.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html":{"name":"TrustKit","abstract":"

    TrustKit is the main class for configuring an SSL pinning policy within an App.

    "},"Initalizing TrustKit.html":{"name":"Initalizing TrustKit"},"Implementing Pinning Validation.html":{"name":"Implementing Pinning Validation"},"Setting up a Validation Callback.html":{"name":"Setting up a Validation Callback"},"Global Configuration Keys.html":{"name":"Global Configuration Keys"},"Domain Configuration Keys.html":{"name":"Domain Configuration Keys"},"Public Key Algorithm Keys.html":{"name":"Public Key Algorithm Keys"},"Other Constants.html":{"name":"Other Constants","abstract":"

    The following constants are available globally.

    "}} \ No newline at end of file +{"Other Constants.html#/c:@TrustKitVersion":{"name":"TrustKitVersion","abstract":"

    The version of TrustKit, such as 1.4.0.

    "},"Public Key Algorithm Keys.html#/c:TSKTrustKitConfig.h@T@TSKSupportedAlgorithm":{"name":"TSKSupportedAlgorithm","abstract":"

    A public key algorithm supported by TrustKit for generating the SSL pin for a certificate.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmRsa2048":{"name":"kTSKAlgorithmRsa2048","abstract":"

    RSA 2048.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmRsa4096":{"name":"kTSKAlgorithmRsa4096","abstract":"

    RSA 4096.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmEcDsaSecp256r1":{"name":"kTSKAlgorithmEcDsaSecp256r1","abstract":"

    ECDSA with secp256r1 curve.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmEcDsaSecp384r1":{"name":"kTSKAlgorithmEcDsaSecp384r1","abstract":"

    ECDSA with secp384r1 curve.

    "},"Domain Configuration Keys.html#/c:TSKTrustKitConfig.h@T@TSKDomainConfigurationKey":{"name":"TSKDomainConfigurationKey","abstract":"

    A domain-specific configuration key (to defined for a domain under the kTSKPinnedDomains"},"Domain Configuration Keys.html#/c:@kTSKPublicKeyHashes":{"name":"kTSKPublicKeyHashes","abstract":"

    An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate’s"},"Domain Configuration Keys.html#/c:@kTSKPublicKeyAlgorithms":{"name":"kTSKPublicKeyAlgorithms","abstract":"

    An array of TSKSupportedAlgorithm constants to specify the public key algorithms for the"},"Domain Configuration Keys.html#/c:@kTSKEnforcePinning":{"name":"kTSKEnforcePinning","abstract":"

    A boolean. If set to NO, TrustKit will not block SSL connections that caused a pin or"},"Domain Configuration Keys.html#/c:@kTSKIncludeSubdomains":{"name":"kTSKIncludeSubdomains","abstract":"

    A boolean. If set to YES, also pin all the subdomains of the specified domain; default"},"Domain Configuration Keys.html#/c:@kTSKExcludeSubdomainFromParentPolicy":{"name":"kTSKExcludeSubdomainFromParentPolicy","abstract":"

    A boolean. If set to YES, TrustKit will not pin this specific domain if kTSKIncludeSubdomains"},"Domain Configuration Keys.html#/c:@kTSKReportUris":{"name":"kTSKReportUris","abstract":"

    An array of URLs to which pin validation failures should be reported.

    "},"Domain Configuration Keys.html#/c:@kTSKDisableDefaultReportUri":{"name":"kTSKDisableDefaultReportUri","abstract":"

    A boolean. If set to YES, the default report URL for sending pin failure reports will"},"Domain Configuration Keys.html#/c:@kTSKExpirationDate":{"name":"kTSKExpirationDate","abstract":"

    A string containing the date, in yyyy-MM-dd format, on which the domain’s configured SSL"},"Domain Configuration Keys.html#/c:@kTSKAdditionalTrustAnchors":{"name":"kTSKAdditionalTrustAnchors","abstract":"

    An array of strings representing additional trust anchors usable for validating"},"Global Configuration Keys.html#/c:TSKTrustKitConfig.h@T@TSKGlobalConfigurationKey":{"name":"TSKGlobalConfigurationKey","abstract":"

    A global, App-wide configuration key that can be set in the pinning policy.

    "},"Global Configuration Keys.html#/c:@kTSKSwizzleNetworkDelegates":{"name":"kTSKSwizzleNetworkDelegates","abstract":"

    A boolean. If set to YES, TrustKit will perform method swizzling on the App’s"},"Global Configuration Keys.html#/c:@kTSKPinnedDomains":{"name":"kTSKPinnedDomains","abstract":"

    A dictionary with domains (such as www.domain.com) as keys and dictionaries as values.

    "},"Global Configuration Keys.html#/c:@kTSKIgnorePinningForUserDefinedTrustAnchors":{"name":"kTSKIgnorePinningForUserDefinedTrustAnchors","abstract":"

    A boolean. If set to YES, pinning validation will be skipped if the server’s certificate"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationSuccess":{"name":"TSKTrustEvaluationSuccess","abstract":"

    The server trust was succesfully evaluated and contained at least one of the configured pins.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedNoMatchingPin":{"name":"TSKTrustEvaluationFailedNoMatchingPin","abstract":"

    The server trust was succesfully evaluated but did not contain any of the configured pins.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedInvalidCertificateChain":{"name":"TSKTrustEvaluationFailedInvalidCertificateChain","abstract":"

    The server trust’s evaluation failed: the server’s certificate chain is not trusted.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationErrorInvalidParameters":{"name":"TSKTrustEvaluationErrorInvalidParameters","abstract":"

    The server trust could not be evaluated due to invalid parameters.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedUserDefinedTrustAnchor":{"name":"TSKTrustEvaluationFailedUserDefinedTrustAnchor","abstract":"

    The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to the macOS trust store). Only available on macOS.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationErrorCouldNotGenerateSpkiHash":{"name":"TSKTrustEvaluationErrorCouldNotGenerateSpkiHash","abstract":"

    The server trust could not be evaluated due to an error when trying to generate the certificate’s subject public key info hash. On iOS 9 or below, this could be caused by a Keychain failure when trying to extract the certificate’s public key bytes.

    ","parent_name":"TSKTrustEvaluationResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)serverHostname":{"name":"serverHostname","abstract":"

    The hostname of the server SSL pinning validation was performed against.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)serverTrust":{"name":"serverTrust","abstract":"

    The original SecTrustRef that validation was performed against.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)evaluationResult":{"name":"evaluationResult","abstract":"

    The result of validating the server’s certificate chain against the set of SSL pins configured for","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)finalTrustDecision":{"name":"finalTrustDecision","abstract":"

    The trust decision returned for this connection, which describes whether the connection should be blocked","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)validationDuration":{"name":"validationDuration","abstract":"

    The time it took for the SSL pinning validation to be performed.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)certificateChain":{"name":"certificateChain","abstract":"

    The certificate chain extracted from the serverTrust as PEM-formatted certificates. This is the","parent_name":"TSKPinningValidatorResult"},"Setting up a Validation Callback.html#/c:TSKPinningValidatorCallback.h@T@TSKPinningValidatorCallback":{"name":"TSKPinningValidatorCallback","abstract":"

    A block that can be set in a TrustKit instance to be invoked for every request that is going through"},"Classes/TSKPinningValidatorResult.html":{"name":"TSKPinningValidatorResult","abstract":"

    A TSKPinningValidatorResult instance contains all the details regarding a pinning validation"},"Setting up a Validation Callback.html#/c:TSKPinningValidatorCallback.h@T@TKSDomainPinningPolicy":{"name":"TKSDomainPinningPolicy","abstract":"

    The pinning policy set for a specific hostname.

    "},"Enums/TSKTrustEvaluationResult.html":{"name":"TSKTrustEvaluationResult","abstract":"

    Possible return values when verifying a server’s identity against a set of pins.

    "},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionShouldAllowConnection":{"name":"TSKTrustDecisionShouldAllowConnection","abstract":"

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be allowed.","parent_name":"TSKTrustDecision"},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionShouldBlockConnection":{"name":"TSKTrustDecisionShouldBlockConnection","abstract":"

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be blocked.","parent_name":"TSKTrustDecision"},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionDomainNotPinned":{"name":"TSKTrustDecisionDomainNotPinned","abstract":"

    No pinning policy was configured for this domain and TrustKit did not validate the server’s identity.","parent_name":"TSKTrustDecision"},"Classes/TSKPinningValidator.html#/c:objc(cs)TSKPinningValidator(im)handleChallenge:completionHandler:":{"name":"-handleChallenge:completionHandler:","abstract":"

    Helper method for handling authentication challenges received within a NSURLSessionDelegate, NSURLSessionTaskDelegate or WKNavigationDelegate.

    ","parent_name":"TSKPinningValidator"},"Classes/TSKPinningValidator.html#/c:objc(cs)TSKPinningValidator(im)evaluateTrust:forHostname:":{"name":"-evaluateTrust:forHostname:","abstract":"

    Evaluate the supplied server trust against the SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent.

    ","parent_name":"TSKPinningValidator"},"Classes/TSKPinningValidator.html":{"name":"TSKPinningValidator","abstract":"

    A TSKPinningValidator instance can be used to verify a server’s identity against an SSL pinning policy.

    "},"Enums/TSKTrustDecision.html":{"name":"TSKTrustDecision","abstract":"

    Possible return values when verifying a server’s identity against an SSL pinning policy.

    "},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)initSharedInstanceWithConfiguration:":{"name":"+initSharedInstanceWithConfiguration:","abstract":"

    Initialize the global TrustKit singleton with the supplied pinning policy.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)sharedInstance":{"name":"+sharedInstance","abstract":"

    Retrieve the global TrustKit singleton instance. Raises an exception if +initSharedInstanceWithConfiguration:","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidator":{"name":"pinningValidator","abstract":"

    Retrieve the validator instance conforming to the pinning policy of this TrustKit instance.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidatorCallback":{"name":"pinningValidatorCallback","abstract":"

    Register a block to be invoked for every request that is going through TrustKit’s pinning","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidatorCallbackQueue":{"name":"pinningValidatorCallbackQueue","abstract":"

    Queue on which to invoke the pinningValidatorCallback; default value is the main queue.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(im)initWithConfiguration:":{"name":"-initWithConfiguration:","abstract":"

    Initialize a local TrustKit instance with the supplied SSL pinning policy configuration.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)setLoggerBlock:":{"name":"+setLoggerBlock:","abstract":"

    Set the global logger.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html":{"name":"TrustKit","abstract":"

    TrustKit is the main class for configuring an SSL pinning policy within an App.

    "},"Initalizing TrustKit.html":{"name":"Initalizing TrustKit"},"Implementing Pinning Validation.html":{"name":"Implementing Pinning Validation"},"Setting up a Validation Callback.html":{"name":"Setting up a Validation Callback"},"Global Configuration Keys.html":{"name":"Global Configuration Keys"},"Domain Configuration Keys.html":{"name":"Domain Configuration Keys"},"Public Key Algorithm Keys.html":{"name":"Public Key Algorithm Keys"},"Other Constants.html":{"name":"Other Constants","abstract":"

    The following constants are available globally.

    "}} \ No newline at end of file diff --git a/docs/documentation/docsets/TrustKit.docset/Contents/Resources/docSet.dsidx b/docs/documentation/docsets/TrustKit.docset/Contents/Resources/docSet.dsidx index d6c85207a54158f54ace7d78966b5322e4023f7f..670bff4169c3b41f4520f4060f7b5d28afcc538b 100644 GIT binary patch delta 211 zcmZp8z}WDBaRaA;0X3s6-<>V|GZe zw_{35W=Up#USdv2QR(J?mKzuai`m>I8|4|bGxLBli8+~7lUHYZv4UBf;taUBN5J)5YV>Bg(yy+mq`JS3MU8=K@YQPFaqZ9P>GXIYim7v(IGD-&knQ YzBw-4h()MKhm8SbJXX`0a{e&`07!*A@&Et; diff --git a/docs/documentation/index.html b/docs/documentation/index.html index 9ff1db6c..942dc120 100644 --- a/docs/documentation/index.html +++ b/docs/documentation/index.html @@ -192,7 +192,7 @@

    TrustKit Documentation

    diff --git a/docs/documentation/search.json b/docs/documentation/search.json index acc0fc86..26178705 100644 --- a/docs/documentation/search.json +++ b/docs/documentation/search.json @@ -1 +1 @@ -{"Other Constants.html#/c:@TrustKitVersion":{"name":"TrustKitVersion","abstract":"

    The version of TrustKit, such as 1.4.0.

    "},"Public Key Algorithm Keys.html#/c:TSKTrustKitConfig.h@T@TSKSupportedAlgorithm":{"name":"TSKSupportedAlgorithm","abstract":"

    A public key algorithm supported by TrustKit for generating the SSL pin for a certificate.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmRsa2048":{"name":"kTSKAlgorithmRsa2048","abstract":"

    RSA 2048.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmRsa4096":{"name":"kTSKAlgorithmRsa4096","abstract":"

    RSA 4096.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmEcDsaSecp256r1":{"name":"kTSKAlgorithmEcDsaSecp256r1","abstract":"

    ECDSA with secp256r1 curve.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmEcDsaSecp384r1":{"name":"kTSKAlgorithmEcDsaSecp384r1","abstract":"

    ECDSA with secp384r1 curve.

    "},"Domain Configuration Keys.html#/c:TSKTrustKitConfig.h@T@TSKDomainConfigurationKey":{"name":"TSKDomainConfigurationKey","abstract":"

    A domain-specific configuration key (to defined for a domain under the kTSKPinnedDomains"},"Domain Configuration Keys.html#/c:@kTSKPublicKeyHashes":{"name":"kTSKPublicKeyHashes","abstract":"

    An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate’s"},"Domain Configuration Keys.html#/c:@kTSKPublicKeyAlgorithms":{"name":"kTSKPublicKeyAlgorithms","abstract":"

    An array of TSKSupportedAlgorithm constants to specify the public key algorithms for the"},"Domain Configuration Keys.html#/c:@kTSKEnforcePinning":{"name":"kTSKEnforcePinning","abstract":"

    A boolean. If set to NO, TrustKit will not block SSL connections that caused a pin or"},"Domain Configuration Keys.html#/c:@kTSKIncludeSubdomains":{"name":"kTSKIncludeSubdomains","abstract":"

    A boolean. If set to YES, also pin all the subdomains of the specified domain; default"},"Domain Configuration Keys.html#/c:@kTSKExcludeSubdomainFromParentPolicy":{"name":"kTSKExcludeSubdomainFromParentPolicy","abstract":"

    A boolean. If set to YES, TrustKit will not pin this specific domain if kTSKIncludeSubdomains"},"Domain Configuration Keys.html#/c:@kTSKReportUris":{"name":"kTSKReportUris","abstract":"

    An array of URLs to which pin validation failures should be reported.

    "},"Domain Configuration Keys.html#/c:@kTSKDisableDefaultReportUri":{"name":"kTSKDisableDefaultReportUri","abstract":"

    A boolean. If set to YES, the default report URL for sending pin failure reports will"},"Domain Configuration Keys.html#/c:@kTSKExpirationDate":{"name":"kTSKExpirationDate","abstract":"

    A string containing the date, in yyyy-MM-dd format, on which the domain’s configured SSL"},"Domain Configuration Keys.html#/c:@kTSKAdditionalTrustAnchors":{"name":"kTSKAdditionalTrustAnchors","abstract":"

    An array of strings representing additional trust anchors usable for validating"},"Global Configuration Keys.html#/c:TSKTrustKitConfig.h@T@TSKGlobalConfigurationKey":{"name":"TSKGlobalConfigurationKey","abstract":"

    A global, App-wide configuration key that can be set in the pinning policy.

    "},"Global Configuration Keys.html#/c:@kTSKSwizzleNetworkDelegates":{"name":"kTSKSwizzleNetworkDelegates","abstract":"

    A boolean. If set to YES, TrustKit will perform method swizzling on the App’s"},"Global Configuration Keys.html#/c:@kTSKPinnedDomains":{"name":"kTSKPinnedDomains","abstract":"

    A dictionary with domains (such as www.domain.com) as keys and dictionaries as values.

    "},"Global Configuration Keys.html#/c:@kTSKIgnorePinningForUserDefinedTrustAnchors":{"name":"kTSKIgnorePinningForUserDefinedTrustAnchors","abstract":"

    A boolean. If set to YES, pinning validation will be skipped if the server’s certificate"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationSuccess":{"name":"TSKTrustEvaluationSuccess","abstract":"

    The server trust was succesfully evaluated and contained at least one of the configured pins.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedNoMatchingPin":{"name":"TSKTrustEvaluationFailedNoMatchingPin","abstract":"

    The server trust was succesfully evaluated but did not contain any of the configured pins.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedInvalidCertificateChain":{"name":"TSKTrustEvaluationFailedInvalidCertificateChain","abstract":"

    The server trust’s evaluation failed: the server’s certificate chain is not trusted.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationErrorInvalidParameters":{"name":"TSKTrustEvaluationErrorInvalidParameters","abstract":"

    The server trust could not be evaluated due to invalid parameters.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedUserDefinedTrustAnchor":{"name":"TSKTrustEvaluationFailedUserDefinedTrustAnchor","abstract":"

    The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to the macOS trust store). Only available on macOS.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationErrorCouldNotGenerateSpkiHash":{"name":"TSKTrustEvaluationErrorCouldNotGenerateSpkiHash","abstract":"

    The server trust could not be evaluated due to an error when trying to generate the certificate’s subject public key info hash. On iOS 9 or below, this could be caused by a Keychain failure when trying to extract the certificate’s public key bytes.

    ","parent_name":"TSKTrustEvaluationResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)serverHostname":{"name":"serverHostname","abstract":"

    The hostname of the server SSL pinning validation was performed against.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)serverTrust":{"name":"serverTrust","abstract":"

    The original SecTrustRef that validation was performed against.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)evaluationResult":{"name":"evaluationResult","abstract":"

    The result of validating the server’s certificate chain against the set of SSL pins configured for","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)finalTrustDecision":{"name":"finalTrustDecision","abstract":"

    The trust decision returned for this connection, which describes whether the connection should be blocked","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)validationDuration":{"name":"validationDuration","abstract":"

    The time it took for the SSL pinning validation to be performed.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)certificateChain":{"name":"certificateChain","abstract":"

    The certificate chain extracted from the serverTrust as PEM-formatted certificates. This is the","parent_name":"TSKPinningValidatorResult"},"Setting up a Validation Callback.html#/c:TSKPinningValidatorCallback.h@T@TSKPinningValidatorCallback":{"name":"TSKPinningValidatorCallback","abstract":"

    A block that can be set in a TrustKit instance to be invoked for every request that is going through"},"Classes/TSKPinningValidatorResult.html":{"name":"TSKPinningValidatorResult","abstract":"

    A TSKPinningValidatorResult instance contains all the details regarding a pinning validation"},"Setting up a Validation Callback.html#/c:TSKPinningValidatorCallback.h@T@TKSDomainPinningPolicy":{"name":"TKSDomainPinningPolicy","abstract":"

    The pinning policy set for a specific hostname.

    "},"Enums/TSKTrustEvaluationResult.html":{"name":"TSKTrustEvaluationResult","abstract":"

    Possible return values when verifying a server’s identity against a set of pins.

    "},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionShouldAllowConnection":{"name":"TSKTrustDecisionShouldAllowConnection","abstract":"

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be allowed.","parent_name":"TSKTrustDecision"},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionShouldBlockConnection":{"name":"TSKTrustDecisionShouldBlockConnection","abstract":"

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be blocked.","parent_name":"TSKTrustDecision"},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionDomainNotPinned":{"name":"TSKTrustDecisionDomainNotPinned","abstract":"

    No pinning policy was configured for this domain and TrustKit did not validate the server’s identity.","parent_name":"TSKTrustDecision"},"Classes/TSKPinningValidator.html#/c:objc(cs)TSKPinningValidator(im)handleChallenge:completionHandler:":{"name":"-handleChallenge:completionHandler:","abstract":"

    Helper method for handling authentication challenges received within a NSURLSessionDelegate, NSURLSessionTaskDelegate or WKNavigationDelegate.

    ","parent_name":"TSKPinningValidator"},"Classes/TSKPinningValidator.html#/c:objc(cs)TSKPinningValidator(im)evaluateTrust:forHostname:":{"name":"-evaluateTrust:forHostname:","abstract":"

    Evaluate the supplied server trust against the SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent.

    ","parent_name":"TSKPinningValidator"},"Classes/TSKPinningValidator.html":{"name":"TSKPinningValidator","abstract":"

    A TSKPinningValidator instance can be used to verify a server’s identity against an SSL pinning policy.

    "},"Enums/TSKTrustDecision.html":{"name":"TSKTrustDecision","abstract":"

    Possible return values when verifying a server’s identity against an SSL pinning policy.

    "},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)initializeWithConfiguration:":{"name":"+initializeWithConfiguration:","abstract":"

    Initialize the global TrustKit singleton with the supplied pinning policy.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)sharedInstance":{"name":"+sharedInstance","abstract":"

    Retrieve the global TrustKit singleton instance. Raises an exception if +initializeWithConfiguration:","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidator":{"name":"pinningValidator","abstract":"

    Retrieve the validator instance conforming to the pinning policy of this TrustKit instance.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidatorCallback":{"name":"pinningValidatorCallback","abstract":"

    Register a block to be invoked for every request that is going through TrustKit’s pinning","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidatorCallbackQueue":{"name":"pinningValidatorCallbackQueue","abstract":"

    Queue on which to invoke the pinningValidatorCallback; default value is the main queue.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(im)initWithConfiguration:":{"name":"-initWithConfiguration:","abstract":"

    Initialize a local TrustKit instance with the supplied SSL pinning policy configuration.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)setLoggerBlock:":{"name":"+setLoggerBlock:","abstract":"

    Set the global logger.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html":{"name":"TrustKit","abstract":"

    TrustKit is the main class for configuring an SSL pinning policy within an App.

    "},"Initalizing TrustKit.html":{"name":"Initalizing TrustKit"},"Implementing Pinning Validation.html":{"name":"Implementing Pinning Validation"},"Setting up a Validation Callback.html":{"name":"Setting up a Validation Callback"},"Global Configuration Keys.html":{"name":"Global Configuration Keys"},"Domain Configuration Keys.html":{"name":"Domain Configuration Keys"},"Public Key Algorithm Keys.html":{"name":"Public Key Algorithm Keys"},"Other Constants.html":{"name":"Other Constants","abstract":"

    The following constants are available globally.

    "}} \ No newline at end of file +{"Other Constants.html#/c:@TrustKitVersion":{"name":"TrustKitVersion","abstract":"

    The version of TrustKit, such as 1.4.0.

    "},"Public Key Algorithm Keys.html#/c:TSKTrustKitConfig.h@T@TSKSupportedAlgorithm":{"name":"TSKSupportedAlgorithm","abstract":"

    A public key algorithm supported by TrustKit for generating the SSL pin for a certificate.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmRsa2048":{"name":"kTSKAlgorithmRsa2048","abstract":"

    RSA 2048.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmRsa4096":{"name":"kTSKAlgorithmRsa4096","abstract":"

    RSA 4096.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmEcDsaSecp256r1":{"name":"kTSKAlgorithmEcDsaSecp256r1","abstract":"

    ECDSA with secp256r1 curve.

    "},"Public Key Algorithm Keys.html#/c:@kTSKAlgorithmEcDsaSecp384r1":{"name":"kTSKAlgorithmEcDsaSecp384r1","abstract":"

    ECDSA with secp384r1 curve.

    "},"Domain Configuration Keys.html#/c:TSKTrustKitConfig.h@T@TSKDomainConfigurationKey":{"name":"TSKDomainConfigurationKey","abstract":"

    A domain-specific configuration key (to defined for a domain under the kTSKPinnedDomains"},"Domain Configuration Keys.html#/c:@kTSKPublicKeyHashes":{"name":"kTSKPublicKeyHashes","abstract":"

    An array of SSL pins, where each pin is the base64-encoded SHA-256 hash of a certificate’s"},"Domain Configuration Keys.html#/c:@kTSKPublicKeyAlgorithms":{"name":"kTSKPublicKeyAlgorithms","abstract":"

    An array of TSKSupportedAlgorithm constants to specify the public key algorithms for the"},"Domain Configuration Keys.html#/c:@kTSKEnforcePinning":{"name":"kTSKEnforcePinning","abstract":"

    A boolean. If set to NO, TrustKit will not block SSL connections that caused a pin or"},"Domain Configuration Keys.html#/c:@kTSKIncludeSubdomains":{"name":"kTSKIncludeSubdomains","abstract":"

    A boolean. If set to YES, also pin all the subdomains of the specified domain; default"},"Domain Configuration Keys.html#/c:@kTSKExcludeSubdomainFromParentPolicy":{"name":"kTSKExcludeSubdomainFromParentPolicy","abstract":"

    A boolean. If set to YES, TrustKit will not pin this specific domain if kTSKIncludeSubdomains"},"Domain Configuration Keys.html#/c:@kTSKReportUris":{"name":"kTSKReportUris","abstract":"

    An array of URLs to which pin validation failures should be reported.

    "},"Domain Configuration Keys.html#/c:@kTSKDisableDefaultReportUri":{"name":"kTSKDisableDefaultReportUri","abstract":"

    A boolean. If set to YES, the default report URL for sending pin failure reports will"},"Domain Configuration Keys.html#/c:@kTSKExpirationDate":{"name":"kTSKExpirationDate","abstract":"

    A string containing the date, in yyyy-MM-dd format, on which the domain’s configured SSL"},"Domain Configuration Keys.html#/c:@kTSKAdditionalTrustAnchors":{"name":"kTSKAdditionalTrustAnchors","abstract":"

    An array of strings representing additional trust anchors usable for validating"},"Global Configuration Keys.html#/c:TSKTrustKitConfig.h@T@TSKGlobalConfigurationKey":{"name":"TSKGlobalConfigurationKey","abstract":"

    A global, App-wide configuration key that can be set in the pinning policy.

    "},"Global Configuration Keys.html#/c:@kTSKSwizzleNetworkDelegates":{"name":"kTSKSwizzleNetworkDelegates","abstract":"

    A boolean. If set to YES, TrustKit will perform method swizzling on the App’s"},"Global Configuration Keys.html#/c:@kTSKPinnedDomains":{"name":"kTSKPinnedDomains","abstract":"

    A dictionary with domains (such as www.domain.com) as keys and dictionaries as values.

    "},"Global Configuration Keys.html#/c:@kTSKIgnorePinningForUserDefinedTrustAnchors":{"name":"kTSKIgnorePinningForUserDefinedTrustAnchors","abstract":"

    A boolean. If set to YES, pinning validation will be skipped if the server’s certificate"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationSuccess":{"name":"TSKTrustEvaluationSuccess","abstract":"

    The server trust was succesfully evaluated and contained at least one of the configured pins.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedNoMatchingPin":{"name":"TSKTrustEvaluationFailedNoMatchingPin","abstract":"

    The server trust was succesfully evaluated but did not contain any of the configured pins.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedInvalidCertificateChain":{"name":"TSKTrustEvaluationFailedInvalidCertificateChain","abstract":"

    The server trust’s evaluation failed: the server’s certificate chain is not trusted.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationErrorInvalidParameters":{"name":"TSKTrustEvaluationErrorInvalidParameters","abstract":"

    The server trust could not be evaluated due to invalid parameters.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationFailedUserDefinedTrustAnchor":{"name":"TSKTrustEvaluationFailedUserDefinedTrustAnchor","abstract":"

    The server trust was succesfully evaluated but did not contain any of the configured pins. However, the certificate chain terminates at a user-defined trust anchor (ie. a custom/private CA that was manually added to the macOS trust store). Only available on macOS.

    ","parent_name":"TSKTrustEvaluationResult"},"Enums/TSKTrustEvaluationResult.html#/c:@E@TSKTrustEvaluationResult@TSKTrustEvaluationErrorCouldNotGenerateSpkiHash":{"name":"TSKTrustEvaluationErrorCouldNotGenerateSpkiHash","abstract":"

    The server trust could not be evaluated due to an error when trying to generate the certificate’s subject public key info hash. On iOS 9 or below, this could be caused by a Keychain failure when trying to extract the certificate’s public key bytes.

    ","parent_name":"TSKTrustEvaluationResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)serverHostname":{"name":"serverHostname","abstract":"

    The hostname of the server SSL pinning validation was performed against.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)serverTrust":{"name":"serverTrust","abstract":"

    The original SecTrustRef that validation was performed against.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)evaluationResult":{"name":"evaluationResult","abstract":"

    The result of validating the server’s certificate chain against the set of SSL pins configured for","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)finalTrustDecision":{"name":"finalTrustDecision","abstract":"

    The trust decision returned for this connection, which describes whether the connection should be blocked","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)validationDuration":{"name":"validationDuration","abstract":"

    The time it took for the SSL pinning validation to be performed.

    ","parent_name":"TSKPinningValidatorResult"},"Classes/TSKPinningValidatorResult.html#/c:objc(cs)TSKPinningValidatorResult(py)certificateChain":{"name":"certificateChain","abstract":"

    The certificate chain extracted from the serverTrust as PEM-formatted certificates. This is the","parent_name":"TSKPinningValidatorResult"},"Setting up a Validation Callback.html#/c:TSKPinningValidatorCallback.h@T@TSKPinningValidatorCallback":{"name":"TSKPinningValidatorCallback","abstract":"

    A block that can be set in a TrustKit instance to be invoked for every request that is going through"},"Classes/TSKPinningValidatorResult.html":{"name":"TSKPinningValidatorResult","abstract":"

    A TSKPinningValidatorResult instance contains all the details regarding a pinning validation"},"Setting up a Validation Callback.html#/c:TSKPinningValidatorCallback.h@T@TKSDomainPinningPolicy":{"name":"TKSDomainPinningPolicy","abstract":"

    The pinning policy set for a specific hostname.

    "},"Enums/TSKTrustEvaluationResult.html":{"name":"TSKTrustEvaluationResult","abstract":"

    Possible return values when verifying a server’s identity against a set of pins.

    "},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionShouldAllowConnection":{"name":"TSKTrustDecisionShouldAllowConnection","abstract":"

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be allowed.","parent_name":"TSKTrustDecision"},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionShouldBlockConnection":{"name":"TSKTrustDecisionShouldBlockConnection","abstract":"

    Based on the server’s certificate chain and the configured pinning policy for this domain, the SSL connection should be blocked.","parent_name":"TSKTrustDecision"},"Enums/TSKTrustDecision.html#/c:@E@TSKTrustDecision@TSKTrustDecisionDomainNotPinned":{"name":"TSKTrustDecisionDomainNotPinned","abstract":"

    No pinning policy was configured for this domain and TrustKit did not validate the server’s identity.","parent_name":"TSKTrustDecision"},"Classes/TSKPinningValidator.html#/c:objc(cs)TSKPinningValidator(im)handleChallenge:completionHandler:":{"name":"-handleChallenge:completionHandler:","abstract":"

    Helper method for handling authentication challenges received within a NSURLSessionDelegate, NSURLSessionTaskDelegate or WKNavigationDelegate.

    ","parent_name":"TSKPinningValidator"},"Classes/TSKPinningValidator.html#/c:objc(cs)TSKPinningValidator(im)evaluateTrust:forHostname:":{"name":"-evaluateTrust:forHostname:","abstract":"

    Evaluate the supplied server trust against the SSL pinning policy previously configured. If the validation fails, a pin failure report will be sent.

    ","parent_name":"TSKPinningValidator"},"Classes/TSKPinningValidator.html":{"name":"TSKPinningValidator","abstract":"

    A TSKPinningValidator instance can be used to verify a server’s identity against an SSL pinning policy.

    "},"Enums/TSKTrustDecision.html":{"name":"TSKTrustDecision","abstract":"

    Possible return values when verifying a server’s identity against an SSL pinning policy.

    "},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)initSharedInstanceWithConfiguration:":{"name":"+initSharedInstanceWithConfiguration:","abstract":"

    Initialize the global TrustKit singleton with the supplied pinning policy.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)sharedInstance":{"name":"+sharedInstance","abstract":"

    Retrieve the global TrustKit singleton instance. Raises an exception if +initSharedInstanceWithConfiguration:","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidator":{"name":"pinningValidator","abstract":"

    Retrieve the validator instance conforming to the pinning policy of this TrustKit instance.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidatorCallback":{"name":"pinningValidatorCallback","abstract":"

    Register a block to be invoked for every request that is going through TrustKit’s pinning","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(py)pinningValidatorCallbackQueue":{"name":"pinningValidatorCallbackQueue","abstract":"

    Queue on which to invoke the pinningValidatorCallback; default value is the main queue.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(im)initWithConfiguration:":{"name":"-initWithConfiguration:","abstract":"

    Initialize a local TrustKit instance with the supplied SSL pinning policy configuration.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html#/c:objc(cs)TrustKit(cm)setLoggerBlock:":{"name":"+setLoggerBlock:","abstract":"

    Set the global logger.

    ","parent_name":"TrustKit"},"Classes/TrustKit.html":{"name":"TrustKit","abstract":"

    TrustKit is the main class for configuring an SSL pinning policy within an App.

    "},"Initalizing TrustKit.html":{"name":"Initalizing TrustKit"},"Implementing Pinning Validation.html":{"name":"Implementing Pinning Validation"},"Setting up a Validation Callback.html":{"name":"Setting up a Validation Callback"},"Global Configuration Keys.html":{"name":"Global Configuration Keys"},"Domain Configuration Keys.html":{"name":"Domain Configuration Keys"},"Public Key Algorithm Keys.html":{"name":"Public Key Algorithm Keys"},"Other Constants.html":{"name":"Other Constants","abstract":"

    The following constants are available globally.

    "}} \ No newline at end of file