diff --git a/.gitmodules b/.gitmodules index 6059778a..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "TrustKit/fishhook"] - path = TrustKit/Dependencies/fishhook - url = git@github.com:facebook/fishhook.git diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..36a266fb --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +Original Authors +---------------- +Alban Diquet - Data Theorem Inc. +Angela Chow - Yahoo Inc. +Eric Castro - Data Theorem Inc. diff --git a/README.md b/README.md index b3b2c0b9..37ca7330 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,94 @@ TrustKit ======== -TrustKit is an iOS / OS X framework for easily and efficiently deploying SSL pinning in any App: - -* TrustKit will pin any connection performed using Apple Frameworks (`NSURLConnection`, `NSURLSession`, `NSStream`, etc.) even including connections performed within `UIWebViews`. -* For Apps targeting iOS 8+, TrustKit can be deployed without having to modify the App's source code. -* TrustKit follows the HTTP Public Key Pinning specification as closely as possible and provides HPKP functionality, such as pinning all subdomains of a given domain, as well as reporting pin violations to a server. - - -Generating SSL Pins -------------------- - -Before implementing SSL pinning within your App, you first need to figure out the list of server domains and public keys you would like to pin. - -In the context of TrustKit, an SSL pin is the base64-encoded SHA-256 of a certificate's public key info; this is the same as what is described in the HTTP Public Key Pinning specification (https://developer.mozilla.org/en-US/docs/Web/Security/Public_Key_Pinning). - -To generate such values, three bash scripts are available. The first two scripts can be used to generate the pin configuration from a PEM or DER certificate: - - $ ./get_pin_from_pem_certificate.sh ca.pem - $ ./get_pin_from_der_certificate.sh ca.der - -The second script can be used to generate the pin configuration for the highest certificate within the certificate chain returned by a given server: - - $ ./get_pin_from_server.sh www.google.com - - -Deploying TrustKit Through Static Linking ------------------------------------------ - -For Apps targeting iOS 7+, TrustKit should be statically linked; this can be achieved by dragging and dropping the TrustKit project file into your App's Xcode project. Then, to initialize the framework, build a dictionary containing the proper configuration keys for TrustKit. - -Such keys include: - -* `kTSKPublicKeyHashes`: Each element of this array should be the base64-encoded SHA 256 of a subject public key info that needs to be in the server's certificate chain. -* `kTSKPublicKeyAlgorithms`: The algorithms TrustKit needs to support when generating public key hashes. Should be an array containing one or multiple entries from `kTSKAlgorithmRsa2048`, `TSKAlgorithmRsa4096`, `TSKAlgorithmEcDsaSecp256r1`. Supporting multiple algorithms has a performance impact. -* `kTSKIncludeSubdomains` (optional): Pin all the subdomains of the specific domain. -* `kTSKReportUris` (optional): No effect at the moment. -* `kTSKEnforcePinning` (optional): If set to NO, a pinning failure will not cause the connection to fail; default value is YES. This is meant to be used with `kTSKReportUris` in order to report pin violations while still allowing connections to go through. - -Then, call the `initializeWithConfiguration:` method with the configuration dictionary: - - NSDictionary *trustKitConfig = - @{ - @"www.datatheorem.com" : @{ - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=", - @"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8=" - ]}}; +TrustKit is an open source framework that makes it easy to deploy SSL public key +pinning in any iOS or OS X App. + + +Overview +-------- + +At a high level, TrustKit intercepts all outgoing SSL connections initiated by +SecureTransport in order to perform additional validation against the server's +certificate chain, based on an App-wide SSL pinning policy. This novel approach +to SSL pinning gives us the following benefits: + +* Easy to use: TrustKit can be deployed in minutes in any App. For iOS8+ and OS +X Apps, TrustKit can be used without even modifying the App's source code. +* API-independent pinning by directly hooking Apple's SecureTransport: TrustKit +works on `NSURLSession`, `UIWebView`, `NSStream`, etc. all the way down to BSD +sockets. + +Additionally, TrustKit provides the following features: + +* Subject Public Key Info pinning, [as opposed to certificate pinning or pinning +the public key bits](https://www.imperialviolet.org/2011/05/04/pinning.html). +* Mechanism to report pinning failures, which allows Apps to send reports +when an unexpected certificate chain is detected, similarly to the _report-uri_ +directive described in the [HTTP Public Key Pinning +specification](https://tools.ietf.org/html/rfc7469). + +TrustKit will be open-sourced at [Black Hat 2015 USA][bh2015-conf]. + + +Getting Started +--------------- + +* Have a look at the Black Hat USA 2015 [presentation][bh2015-pdf]. +* Read the [Getting Started][getting-started] guide. +* Check out the [API documentation][api-doc]. + + +Sample Usage +------------ + +Enabling SSL pinning only requires initializing TrustKit with a pinning policy +(domains, Subject Public Key Info hashes, and additional settings): + + NSDictionary *trustKitConfig; + trustKitConfig = @{ + @"www.datatheorem.com" : @{ + 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]; +Once TrustKit has been initialized, all SSL connections initiated by Apple +frameworks within the App will verify the server' certificate chains against the +supplied pinning policy. If report URIs have been configured, the App will also +send reports to the specified URIs whenever a pin validation failure occurred. +For more information, see the [Getting Started][getting-started] guide. -Deploying TrustKit Through Dynamic Linking ------------------------------------------- - -For Apps targeting iOS 8+, TrustKit can be dynamically linked, which allows enabling public key pinning without having to modify the App's source code. To embed TrustKit in your App: - -* Drag and drop the TrustKit.xcodeproj file into your App's workspace in Xcode. Make sure TrustKit isn't already opened in Xcode: - -![](http://datatheorem.github.io/TrustKit/images/dynamic1.png) - -* In the App's "General" settings code, add TrustKit.framework in the list of "Embedded Binaries": - -![](http://datatheorem.github.io/TrustKit/images/dynamic2.png) - -* Lastly, add the public key hashes TrustKit will use to check certificate chains. In the App's Info.plist file ("Info" tab in Xcode): - * Add a new Dictionary key called `TSKConfiguration`. - * Within this dictionary add a Dictionary value and use the server's domain (such as www.google.com) as the entry's key. - * Within dictionary you can add a few specific keys in order to configure how TrustKit handles pinning with this domain: - * `TSKPublicKeyHashes`: Each element of this Array should be the base64-encoded SHA 256 of a subject public key info that needs to be in the server's certificate chain. - * `TSKPublicKeyAlgorithms`: The algorithms TrustKit needs to support when generating public key hashes. Should be an array containing one or multiple entries from `TSKAlgorithmRsa2048`, `TSKAlgorithmRsa4096`, `TSKAlgorithmEcDsaSecp256r1`. Supporting multiple algorithms has a performance impact. - * `TSKIncludeSubdomains` (optional): Pin all the subdomains of the specific domain. - * `TSKReportUris` (optional): No effect at the moment. - * `TSKEnforcePinning` (optional): If set to NO, a pinning failure will not cause the connection to fail; default value is YES. This is meant to be used with `TSKReportUris` in order to report pin violations while still allowing connections to go through. - -Your App's Info.plist file should look like this: - -![](http://datatheorem.github.io/TrustKit/images/dynamic3.png) - -Then, all SSL connections relying on Apple's SecureTransport (NSURLSession, NSURLConnection, UIWebView, etc.) will be checking the server's certificate chain using the public key pins specified in the Info.plist. +Credits +------- +TrustKit is a joint-effort between the security teams at Data Theorem and Yahoo. +See AUTHORS for details. -Cordova +License ------- -TBD. +TrustKit is released under the MIT license. See LICENSE for details. + +[getting-started]: https://datatheorem.github.io/TrustKit/getting-started/ +[bh2015-pdf]: #tbd +[bh2015-conf]: https://www.blackhat.com/us-15/briefings.html#trustkit-code-injection-on-ios-8-for-the-greater-good +[api-doc]: https://datatheorem.github.io/TrustKit/documentation diff --git a/TrustKit.podspec b/TrustKit.podspec new file mode 100644 index 00000000..93c089f1 --- /dev/null +++ b/TrustKit.podspec @@ -0,0 +1,17 @@ +Pod::Spec.new do |s| + s.name = "TrustKit" + s.version = "1.0.0" + s.summary = 'TrustKit is an open source framework that makes it easy to deploy SSL pinning in any iOS or OS X App.' + s.homepage = "https://datatheorem.github.io/TrustKit" + s.documentation_url = 'https://datatheorem.github.io/TrustKit/documentation/' + 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.source_files = 'TrustKit', 'TrustKit/**/*.{h,m}', 'TrustKit/Dependencies/fishhook/*.{h,c}' + s.public_header_files = 'TrustKit/TrustKit.h', 'TrustKit/Pinning/TSKPinVerifier.h' + s.frameworks = 'Foundation', 'Security' + s.vendored_libraries = 'TrustKit/Dependencies/domain_registry/*.a' + s.requires_arc = true +end diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index f1f2b3dd..f86dac06 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -7,17 +7,45 @@ objects = { /* Begin PBXBuildFile section */ - 070868B51ADFFADF00E5AFDC /* ca.cert.der in Resources */ = {isa = PBXBuildFile; fileRef = 070868B41ADFF6B000E5AFDC /* ca.cert.der */; }; - 070868B61ADFFADF00E5AFDC /* ca-chain.cert.der in Resources */ = {isa = PBXBuildFile; fileRef = 070868B01ADFF67200E5AFDC /* ca-chain.cert.der */; }; - 070868B71ADFFADF00E5AFDC /* intermediate.cert.der in Resources */ = {isa = PBXBuildFile; fileRef = 070868B11ADFF67200E5AFDC /* intermediate.cert.der */; }; - 070868B81ADFFADF00E5AFDC /* www.good.com.cert.der in Resources */ = {isa = PBXBuildFile; fileRef = 070868B21ADFF67200E5AFDC /* www.good.com.cert.der */; }; - 070868BB1AE1672D00E5AFDC /* self.cert.der in Resources */ = {isa = PBXBuildFile; fileRef = 070868BA1AE1672D00E5AFDC /* self.cert.der */; }; - 070868BC1AE1673200E5AFDC /* ca-chain-plus-self.cert.der in Resources */ = {isa = PBXBuildFile; fileRef = 070868B91AE1658200E5AFDC /* ca-chain-plus-self.cert.der */; }; - 0710306C1AA88A510028092C /* TrustKitPinValidationOnlineTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0710306B1AA88A510028092C /* TrustKitPinValidationOnlineTests.m */; }; - 075AA1091AC985FD00178223 /* TrustKitPinValidationOfflineTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2FA2868CAFECA46ADE0B6E3E /* TrustKitPinValidationOfflineTests.m */; }; + 070868B51ADFFADF00E5AFDC /* GoodRootCA.der in Resources */ = {isa = PBXBuildFile; fileRef = 070868B41ADFF6B000E5AFDC /* GoodRootCA.der */; }; + 070868B71ADFFADF00E5AFDC /* GoodIntermediateCA.der in Resources */ = {isa = PBXBuildFile; fileRef = 070868B11ADFF67200E5AFDC /* GoodIntermediateCA.der */; }; + 070868B81ADFFADF00E5AFDC /* www.good.com.der in Resources */ = {isa = PBXBuildFile; fileRef = 070868B21ADFF67200E5AFDC /* www.good.com.der */; }; + 070868BB1AE1672D00E5AFDC /* www.good.com.selfsigned.der in Resources */ = {isa = PBXBuildFile; fileRef = 070868BA1AE1672D00E5AFDC /* www.good.com.selfsigned.der */; }; + 0710306C1AA88A510028092C /* TSKPinValidationOnlineTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0710306B1AA88A510028092C /* TSKPinValidationOnlineTests.m */; }; + 075AA1091AC985FD00178223 /* TSKPinValidationOfflineTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2FA2868CAFECA46ADE0B6E3E /* TSKPinValidationOfflineTests.m */; }; + 6B032D3A1AF1794D00EAFA69 /* TSKSimpleReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B032D391AF1794D00EAFA69 /* TSKSimpleReporter.m */; }; + 6B032D3E1AF1A4AC00EAFA69 /* TSKSimpleReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B032D3D1AF1A4AC00EAFA69 /* TSKSimpleReporter.h */; }; + 6B032D401AF1AEC200EAFA69 /* TSKReporterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B032D3F1AF1AEB600EAFA69 /* TSKReporterTests.m */; }; + 6B2B06AD1B05154A00FC749E /* TSKSimpleBackgroundReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B2B06AC1B05154A00FC749E /* TSKSimpleBackgroundReporter.h */; }; + 6B2B06AF1B05157400FC749E /* TSKSimpleBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B2B06AE1B05157400FC749E /* TSKSimpleBackgroundReporter.m */; }; + 8C15F9931B132F9200F06C0E /* TSKPinVerifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C15F9911B132F9200F06C0E /* TSKPinVerifier.h */; }; + 8C15F9941B132F9200F06C0E /* TSKPinVerifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C15F9921B132F9200F06C0E /* TSKPinVerifier.m */; }; + 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 */; }; 8C84804D1A896EE30017C155 /* TrustKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C84804C1A896EE30017C155 /* TrustKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8C8480531A896EE30017C155 /* TrustKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C8480471A896EE30017C155 /* TrustKit.framework */; }; 8C84806D1A896F660017C155 /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C84806C1A896F660017C155 /* TrustKit.m */; }; + 8C8716AD1B23A9D200267E1D /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE919311AEA0F8B002B29AE /* fishhook.c */; }; + 8C8716AE1B23A9DD00267E1D /* libassert_lib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CE9192A1AEA0F7E002B29AE /* libassert_lib.a */; }; + 8C8716AF1B23A9DD00267E1D /* libdomain_registry_lib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CE9192B1AEA0F7E002B29AE /* libdomain_registry_lib.a */; }; + 8C8716B01B23A9DD00267E1D /* libinit_registry_tables_lib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CE9192C1AEA0F7E002B29AE /* libinit_registry_tables_lib.a */; }; + 8C8716B11B23A9F000267E1D /* TSKSimpleReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B032D391AF1794D00EAFA69 /* TSKSimpleReporter.m */; }; + 8C8716B21B23A9F400267E1D /* TSKSimpleBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B2B06AE1B05157400FC749E /* TSKSimpleBackgroundReporter.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 */; }; + 8C8716B71B23AA0B00267E1D /* TSKPinVerifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C15F9921B132F9200F06C0E /* TSKPinVerifier.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 */; }; + 8CC78B201B1B586F00523A25 /* TSKCertificateUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CC78B1F1B1B586F00523A25 /* TSKCertificateUtils.m */; }; + 8CC78B251B1B616500523A25 /* TSKPublicKeyAlgorithmTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CC78B241B1B616500523A25 /* TSKPublicKeyAlgorithmTests.m */; }; + 8CC78B261B1B696500523A25 /* ThawteSSLCA.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CC78B211B1B604A00523A25 /* ThawteSSLCA.der */; }; + 8CC78B271B1B696800523A25 /* www.datatheorem.com.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CC78B221B1B604A00523A25 /* www.datatheorem.com.der */; }; + 8CC78B281B1B696D00523A25 /* sni41871.cloudflaressl.com.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CC78B1D1B1B55EC00523A25 /* sni41871.cloudflaressl.com.der */; }; + 8CC78B291B1B697000523A25 /* COMODOECCDomainValidationSecureServerCA2.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CC78B1C1B1B55EC00523A25 /* COMODOECCDomainValidationSecureServerCA2.der */; }; + 8CCBD15B1B186D1100CB88AF /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCBD15A1B186D1100CB88AF /* reporting_utils.m */; }; 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 */; }; @@ -41,22 +69,52 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 8C8716941B23A91D00267E1D /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ - 070868B01ADFF67200E5AFDC /* ca-chain.cert.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ca-chain.cert.der"; sourceTree = ""; }; - 070868B11ADFF67200E5AFDC /* intermediate.cert.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = intermediate.cert.der; sourceTree = ""; }; - 070868B21ADFF67200E5AFDC /* www.good.com.cert.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = www.good.com.cert.der; sourceTree = ""; }; - 070868B41ADFF6B000E5AFDC /* ca.cert.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = ca.cert.der; sourceTree = ""; }; - 070868B91AE1658200E5AFDC /* ca-chain-plus-self.cert.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ca-chain-plus-self.cert.der"; sourceTree = ""; }; - 070868BA1AE1672D00E5AFDC /* self.cert.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = self.cert.der; sourceTree = ""; }; - 0710306B1AA88A510028092C /* TrustKitPinValidationOnlineTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TrustKitPinValidationOnlineTests.m; sourceTree = ""; }; + 070868B11ADFF67200E5AFDC /* GoodIntermediateCA.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = GoodIntermediateCA.der; sourceTree = ""; }; + 070868B21ADFF67200E5AFDC /* www.good.com.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = www.good.com.der; sourceTree = ""; }; + 070868B41ADFF6B000E5AFDC /* GoodRootCA.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = GoodRootCA.der; sourceTree = ""; }; + 070868BA1AE1672D00E5AFDC /* www.good.com.selfsigned.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = www.good.com.selfsigned.der; sourceTree = ""; }; + 0710306B1AA88A510028092C /* TSKPinValidationOnlineTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinValidationOnlineTests.m; sourceTree = ""; }; 2FA286123F801C437F35D240 /* TrustKit+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TrustKit+Private.h"; sourceTree = ""; }; - 2FA2868CAFECA46ADE0B6E3E /* TrustKitPinValidationOfflineTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TrustKitPinValidationOfflineTests.m; sourceTree = ""; }; + 2FA2868CAFECA46ADE0B6E3E /* TSKPinValidationOfflineTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPinValidationOfflineTests.m; sourceTree = ""; }; + 6B032D391AF1794D00EAFA69 /* TSKSimpleReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKSimpleReporter.m; path = Reporting/TSKSimpleReporter.m; sourceTree = ""; }; + 6B032D3D1AF1A4AC00EAFA69 /* TSKSimpleReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKSimpleReporter.h; path = Reporting/TSKSimpleReporter.h; sourceTree = ""; }; + 6B032D3F1AF1AEB600EAFA69 /* TSKReporterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TSKReporterTests.m; sourceTree = ""; }; + 6B2B06AC1B05154A00FC749E /* TSKSimpleBackgroundReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKSimpleBackgroundReporter.h; path = Reporting/TSKSimpleBackgroundReporter.h; sourceTree = ""; }; + 6B2B06AE1B05157400FC749E /* TSKSimpleBackgroundReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKSimpleBackgroundReporter.m; path = Reporting/TSKSimpleBackgroundReporter.m; sourceTree = ""; }; + 8C15F9911B132F9200F06C0E /* TSKPinVerifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinVerifier.h; path = Pinning/TSKPinVerifier.h; sourceTree = ""; }; + 8C15F9921B132F9200F06C0E /* TSKPinVerifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKPinVerifier.m; path = Pinning/TSKPinVerifier.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 = ""; }; 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 = ""; }; 8C84806C1A896F660017C155 /* TrustKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TrustKit.m; sourceTree = ""; }; + 8C8716961B23A91D00267E1D /* libTrustKit_Static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTrustKit_Static.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 8C9492F51B2379A100F5DF38 /* reporting_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = reporting_utils.h; path = Reporting/reporting_utils.h; sourceTree = ""; }; + 8CC78B1C1B1B55EC00523A25 /* COMODOECCDomainValidationSecureServerCA2.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = COMODOECCDomainValidationSecureServerCA2.der; sourceTree = ""; }; + 8CC78B1D1B1B55EC00523A25 /* sni41871.cloudflaressl.com.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = sni41871.cloudflaressl.com.der; sourceTree = ""; }; + 8CC78B1E1B1B586F00523A25 /* TSKCertificateUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKCertificateUtils.h; sourceTree = ""; }; + 8CC78B1F1B1B586F00523A25 /* TSKCertificateUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKCertificateUtils.m; sourceTree = ""; }; + 8CC78B211B1B604A00523A25 /* ThawteSSLCA.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = ThawteSSLCA.der; sourceTree = ""; }; + 8CC78B221B1B604A00523A25 /* www.datatheorem.com.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = www.datatheorem.com.der; sourceTree = ""; }; + 8CC78B241B1B616500523A25 /* TSKPublicKeyAlgorithmTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKPublicKeyAlgorithmTests.m; sourceTree = ""; }; + 8CCBD15A1B186D1100CB88AF /* reporting_utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = reporting_utils.m; path = Reporting/reporting_utils.m; 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 = ""; }; @@ -89,18 +147,25 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 8C8716931B23A91D00267E1D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8C8716AF1B23A9DD00267E1D /* libdomain_registry_lib.a in Frameworks */, + 8C8716AE1B23A9DD00267E1D /* libassert_lib.a in Frameworks */, + 8C8716B01B23A9DD00267E1D /* libinit_registry_tables_lib.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 070868B31ADFF68200E5AFDC /* Certificates */ = { isa = PBXGroup; children = ( - 070868BA1AE1672D00E5AFDC /* self.cert.der */, - 070868B91AE1658200E5AFDC /* ca-chain-plus-self.cert.der */, - 070868B41ADFF6B000E5AFDC /* ca.cert.der */, - 070868B01ADFF67200E5AFDC /* ca-chain.cert.der */, - 070868B11ADFF67200E5AFDC /* intermediate.cert.der */, - 070868B21ADFF67200E5AFDC /* www.good.com.cert.der */, + 8CC78B1B1B1B552100523A25 /* RSA 4096 */, + 8CC78B1A1B1B551400523A25 /* RSA 2048 */, + 8CC78B191B1B54F700523A25 /* ECDSA sec256r1 */, ); name = Certificates; sourceTree = ""; @@ -130,6 +195,7 @@ children = ( 8C8480471A896EE30017C155 /* TrustKit.framework */, 8C8480521A896EE30017C155 /* TrustKitTests.xctest */, + 8C8716961B23A91D00267E1D /* libTrustKit_Static.a */, ); name = Products; sourceTree = ""; @@ -160,9 +226,14 @@ isa = PBXGroup; children = ( 070868B31ADFF68200E5AFDC /* Certificates */, - 0710306B1AA88A510028092C /* TrustKitPinValidationOnlineTests.m */, - 2FA2868CAFECA46ADE0B6E3E /* TrustKitPinValidationOfflineTests.m */, + 0710306B1AA88A510028092C /* TSKPinValidationOnlineTests.m */, + 2FA2868CAFECA46ADE0B6E3E /* TSKPinValidationOfflineTests.m */, 8C8480571A896EE30017C155 /* Supporting Files */, + 6B032D3F1AF1AEB600EAFA69 /* TSKReporterTests.m */, + 8C15F9A31B17564400F06C0E /* TSKPinConfigurationTests.m */, + 8CC78B1E1B1B586F00523A25 /* TSKCertificateUtils.h */, + 8CC78B1F1B1B586F00523A25 /* TSKCertificateUtils.m */, + 8CC78B241B1B616500523A25 /* TSKPublicKeyAlgorithmTests.m */, ); path = TrustKitTests; sourceTree = ""; @@ -184,6 +255,35 @@ name = fishhook; sourceTree = ""; }; + 8CC78B191B1B54F700523A25 /* ECDSA sec256r1 */ = { + isa = PBXGroup; + children = ( + 8CC78B1C1B1B55EC00523A25 /* COMODOECCDomainValidationSecureServerCA2.der */, + 8CC78B1D1B1B55EC00523A25 /* sni41871.cloudflaressl.com.der */, + ); + name = "ECDSA sec256r1"; + sourceTree = ""; + }; + 8CC78B1A1B1B551400523A25 /* RSA 2048 */ = { + isa = PBXGroup; + children = ( + 8CC78B211B1B604A00523A25 /* ThawteSSLCA.der */, + 8CC78B221B1B604A00523A25 /* www.datatheorem.com.der */, + ); + name = "RSA 2048"; + sourceTree = ""; + }; + 8CC78B1B1B1B552100523A25 /* RSA 4096 */ = { + isa = PBXGroup; + children = ( + 070868BA1AE1672D00E5AFDC /* www.good.com.selfsigned.der */, + 070868B41ADFF6B000E5AFDC /* GoodRootCA.der */, + 070868B11ADFF67200E5AFDC /* GoodIntermediateCA.der */, + 070868B21ADFF67200E5AFDC /* www.good.com.der */, + ); + name = "RSA 4096"; + sourceTree = ""; + }; 8CE9191B1AEA072A002B29AE /* Pinning */ = { isa = PBXGroup; children = ( @@ -191,6 +291,8 @@ 8CE9191D1AEA073C002B29AE /* public_key_utils.m */, 8CE919241AEA07C5002B29AE /* ssl_pin_verifier.h */, 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */, + 8C15F9911B132F9200F06C0E /* TSKPinVerifier.h */, + 8C15F9921B132F9200F06C0E /* TSKPinVerifier.m */, ); name = Pinning; sourceTree = ""; @@ -199,6 +301,14 @@ isa = PBXGroup; children = ( 8CE919261AEA0991002B29AE /* TSKReporterDelegate.h */, + 6B032D3D1AF1A4AC00EAFA69 /* TSKSimpleReporter.h */, + 6B032D391AF1794D00EAFA69 /* TSKSimpleReporter.m */, + 6B2B06AC1B05154A00FC749E /* TSKSimpleBackgroundReporter.h */, + 6B2B06AE1B05157400FC749E /* TSKSimpleBackgroundReporter.m */, + 8C15F99E1B16094D00F06C0E /* TSKPinFailureReport.h */, + 8C15F99F1B16094D00F06C0E /* TSKPinFailureReport.m */, + 8C9492F51B2379A100F5DF38 /* reporting_utils.h */, + 8CCBD15A1B186D1100CB88AF /* reporting_utils.m */, ); name = Reporting; sourceTree = ""; @@ -221,7 +331,12 @@ files = ( 8CE9192D1AEA0F7E002B29AE /* domain_registry.h in Headers */, 8CE9191E1AEA073C002B29AE /* public_key_utils.h in Headers */, + 6B2B06AD1B05154A00FC749E /* TSKSimpleBackgroundReporter.h in Headers */, + 6B032D3E1AF1A4AC00EAFA69 /* TSKSimpleReporter.h in Headers */, + 8C15F9931B132F9200F06C0E /* TSKPinVerifier.h in Headers */, + 8C9492F61B2379A100F5DF38 /* reporting_utils.h in Headers */, 8C84804D1A896EE30017C155 /* TrustKit.h in Headers */, + 8C15F9A01B16094D00F06C0E /* TSKPinFailureReport.h in Headers */, 8CE919271AEA0991002B29AE /* TSKReporterDelegate.h in Headers */, 8CE919251AEA07C5002B29AE /* ssl_pin_verifier.h in Headers */, 8CE919361AEA0F8B002B29AE /* fishhook.h in Headers */, @@ -267,14 +382,32 @@ productReference = 8C8480521A896EE30017C155 /* TrustKitTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 8C8716951B23A91D00267E1D /* TrustKit_Static */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8C8716AB1B23A91D00267E1D /* Build configuration list for PBXNativeTarget "TrustKit_Static" */; + buildPhases = ( + 8C8716921B23A91D00267E1D /* Sources */, + 8C8716931B23A91D00267E1D /* Frameworks */, + 8C8716941B23A91D00267E1D /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TrustKit_Static; + productName = TrustKit_Static; + productReference = 8C8716961B23A91D00267E1D /* libTrustKit_Static.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 8C84803E1A896EE30017C155 /* Project object */ = { isa = PBXProject; attributes = { + CLASSPREFIX = TSK; LastUpgradeCheck = 0610; - ORGANIZATIONNAME = "Data Theorem"; + ORGANIZATIONNAME = TrustKit; TargetAttributes = { 8C8480461A896EE30017C155 = { CreatedOnToolsVersion = 6.1.1; @@ -283,6 +416,9 @@ 8C8480511A896EE30017C155 = { CreatedOnToolsVersion = 6.1.1; }; + 8C8716951B23A91D00267E1D = { + CreatedOnToolsVersion = 6.3.2; + }; }; }; buildConfigurationList = 8C8480411A896EE30017C155 /* Build configuration list for PBXProject "TrustKit" */; @@ -298,6 +434,7 @@ projectRoot = ""; targets = ( 8C8480461A896EE30017C155 /* TrustKit */, + 8C8716951B23A91D00267E1D /* TrustKit_Static */, 8C8480511A896EE30017C155 /* TrustKitTests */, ); }; @@ -315,12 +452,14 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 070868B51ADFFADF00E5AFDC /* ca.cert.der in Resources */, - 070868BB1AE1672D00E5AFDC /* self.cert.der in Resources */, - 070868B61ADFFADF00E5AFDC /* ca-chain.cert.der in Resources */, - 070868B71ADFFADF00E5AFDC /* intermediate.cert.der in Resources */, - 070868BC1AE1673200E5AFDC /* ca-chain-plus-self.cert.der in Resources */, - 070868B81ADFFADF00E5AFDC /* www.good.com.cert.der in Resources */, + 8CC78B291B1B697000523A25 /* COMODOECCDomainValidationSecureServerCA2.der in Resources */, + 8CC78B261B1B696500523A25 /* ThawteSSLCA.der in Resources */, + 070868B51ADFFADF00E5AFDC /* GoodRootCA.der in Resources */, + 070868BB1AE1672D00E5AFDC /* www.good.com.selfsigned.der in Resources */, + 070868B71ADFFADF00E5AFDC /* GoodIntermediateCA.der in Resources */, + 8CC78B271B1B696800523A25 /* www.datatheorem.com.der in Resources */, + 8CC78B281B1B696D00523A25 /* sni41871.cloudflaressl.com.der in Resources */, + 070868B81ADFFADF00E5AFDC /* www.good.com.der in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -332,9 +471,14 @@ buildActionMask = 2147483647; files = ( 8CE919221AEA077F002B29AE /* ssl_pin_verifier.m in Sources */, + 6B2B06AF1B05157400FC749E /* TSKSimpleBackgroundReporter.m in Sources */, + 6B032D3A1AF1794D00EAFA69 /* TSKSimpleReporter.m in Sources */, 8CE9191F1AEA073C002B29AE /* public_key_utils.m in Sources */, 8CE919351AEA0F8B002B29AE /* fishhook.c in Sources */, + 8C15F9A11B16094E00F06C0E /* TSKPinFailureReport.m in Sources */, + 8C15F9941B132F9200F06C0E /* TSKPinVerifier.m in Sources */, 8C84806D1A896F660017C155 /* TrustKit.m in Sources */, + 8CCBD15B1B186D1100CB88AF /* reporting_utils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -342,8 +486,28 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 0710306C1AA88A510028092C /* TrustKitPinValidationOnlineTests.m in Sources */, - 075AA1091AC985FD00178223 /* TrustKitPinValidationOfflineTests.m in Sources */, + 0710306C1AA88A510028092C /* TSKPinValidationOnlineTests.m in Sources */, + 8C15F9A41B17564400F06C0E /* TSKPinConfigurationTests.m in Sources */, + 075AA1091AC985FD00178223 /* TSKPinValidationOfflineTests.m in Sources */, + 8CC78B251B1B616500523A25 /* TSKPublicKeyAlgorithmTests.m in Sources */, + 6B032D401AF1AEC200EAFA69 /* TSKReporterTests.m in Sources */, + 8CC78B201B1B586F00523A25 /* TSKCertificateUtils.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8C8716921B23A91D00267E1D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8C8716B71B23AA0B00267E1D /* TSKPinVerifier.m in Sources */, + 8C8716B51B23AA0600267E1D /* public_key_utils.m in Sources */, + 8C8716AD1B23A9D200267E1D /* fishhook.c in Sources */, + 8C8716B31B23A9F700267E1D /* TSKPinFailureReport.m in Sources */, + 8C8716B41B23A9FA00267E1D /* reporting_utils.m in Sources */, + 8C8716B81B23AA0D00267E1D /* TrustKit.m in Sources */, + 8C8716B21B23A9F400267E1D /* TSKSimpleBackgroundReporter.m in Sources */, + 8C8716B11B23A9F000267E1D /* TSKSimpleReporter.m in Sources */, + 8C8716B61B23AA0800267E1D /* ssl_pin_verifier.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -362,6 +526,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -393,11 +558,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = "arm64 armv7 armv7s x86_64"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -407,6 +573,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -432,11 +599,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; + VALID_ARCHS = "arm64 armv7 armv7s x86_64"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -445,7 +613,7 @@ 8C84805E1A896EE30017C155 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; + APPLICATION_EXTENSION_API_ONLY = "$(inherited)"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEFINES_MODULE = YES; @@ -462,8 +630,9 @@ ); PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; - SDKROOT = iphoneos; + SDKROOT = "$(inherited)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "$(inherited)"; VALID_ARCHS = "arm64 armv7 armv7s x86_64"; }; name = Debug; @@ -471,7 +640,7 @@ 8C84805F1A896EE30017C155 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; + APPLICATION_EXTENSION_API_ONLY = "$(inherited)"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEFINES_MODULE = YES; @@ -488,8 +657,9 @@ ); PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; - SDKROOT = iphoneos; + SDKROOT = "$(inherited)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "$(inherited)"; VALID_ARCHS = "arm64 armv7 armv7s x86_64"; }; name = Release; @@ -498,17 +668,19 @@ isa = XCBuildConfiguration; buildSettings = { FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", + "$(SDKROOT)", "$(inherited)", ); GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); + GCC_TREAT_WARNINGS_AS_ERRORS = YES; INFOPLIST_FILE = TrustKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @loader_path/../Frameworks @executable_path/../Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; + SDKROOT = "$(inherited)"; + SUPPORTED_PLATFORMS = "$(inherited)"; VALID_ARCHS = "arm64 armv7 armv7s x86_64"; }; name = Debug; @@ -517,17 +689,62 @@ isa = XCBuildConfiguration; buildSettings = { FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", + "$(SDKROOT)", "$(inherited)", ); + GCC_TREAT_WARNINGS_AS_ERRORS = YES; INFOPLIST_FILE = TrustKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @loader_path/../Frameworks @executable_path/../Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; + SDKROOT = "$(inherited)"; + SUPPORTED_PLATFORMS = "$(inherited)"; VALID_ARCHS = "arm64 armv7 armv7s x86_64"; }; name = Release; }; + 8C8716A71B23A91D00267E1D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = "$(inherited)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/TrustKit/Dependencies/domain_registry", + ); + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = "$(inherited)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "$(inherited)"; + }; + name = Debug; + }; + 8C8716A81B23A91D00267E1D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = "$(inherited)"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_NO_COMMON_BLOCKS = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/TrustKit/Dependencies/domain_registry", + ); + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = "$(inherited)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "$(inherited)"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -558,6 +775,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 8C8716AB1B23A91D00267E1D /* Build configuration list for PBXNativeTarget "TrustKit_Static" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8C8716A71B23A91D00267E1D /* Debug */, + 8C8716A81B23A91D00267E1D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 8C84803E1A896EE30017C155 /* Project object */; diff --git a/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit.xcscheme b/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit.xcscheme new file mode 100644 index 00000000..345645f6 --- /dev/null +++ b/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit.xcscheme @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit_Static.xcscheme b/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit_Static.xcscheme new file mode 100644 index 00000000..e427b632 --- /dev/null +++ b/TrustKit.xcodeproj/xcshareddata/xcschemes/TrustKit_Static.xcscheme @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TrustKit/Dependencies/fishhook b/TrustKit/Dependencies/fishhook deleted file mode 160000 index ae2bc097..00000000 --- a/TrustKit/Dependencies/fishhook +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ae2bc09796828f3b26b509b3faa5c166cf042db5 diff --git a/TrustKit/Dependencies/fishhook/LICENSE b/TrustKit/Dependencies/fishhook/LICENSE new file mode 100755 index 00000000..c45bb7c0 --- /dev/null +++ b/TrustKit/Dependencies/fishhook/LICENSE @@ -0,0 +1,22 @@ +// Copyright (c) 2013, Facebook, Inc. +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name Facebook nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific +// prior written permission. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/TrustKit/Dependencies/fishhook/README.md b/TrustKit/Dependencies/fishhook/README.md new file mode 100755 index 00000000..6f0a7a22 --- /dev/null +++ b/TrustKit/Dependencies/fishhook/README.md @@ -0,0 +1,81 @@ +# fishhook + +__fishhook__ is a very simple library that enables dynamically rebinding symbols in Mach-O binaries running on iOS in the simulator and on device. This provides functionality that is similar to using [`DYLD_INTERPOSE`][interpose] on OS X. At Facebook, we've found it useful as a way to hook calls in libSystem for debugging/tracing purposes (for example, auditing for double-close issues with file descriptors). + +[interpose]: http://opensource.apple.com/source/dyld/dyld-210.2.3/include/mach-o/dyld-interposing.h "" + +## Usage + +Once you add `fishhook.h`/`fishhook.c` to your project, you can rebind symbols as follows: +```Objective-C +#import + +#import + +#import "AppDelegate.h" +#import "fishhook.h" + +static int (*orig_close)(int); +static int (*orig_open)(const char *, int, ...); + +void save_original_symbols() { + orig_close = dlsym(RTLD_DEFAULT, "close"); + orig_open = dlsym(RTLD_DEFAULT, "open"); +} + +int my_close(int fd) { + printf("Calling real close(%d)\n", fd); + return orig_close(fd); +} + +int my_open(const char *path, int oflag, ...) { + va_list ap = {0}; + mode_t mode = 0; + + if ((oflag & O_CREAT) != 0) { + // mode only applies to O_CREAT + va_start(ap, oflag); + mode = va_arg(ap, int); + va_end(ap); + printf("Calling real open('%s', %d, %d)\n", path, oflag, mode); + return orig_open(path, oflag, mode); + } else { + printf("Calling real open('%s', %d)\n", path, oflag); + return orig_open(path, oflag, mode); + } +} + +int main(int argc, char * argv[]) +{ + @autoreleasepool { + save_original_symbols(); + rebind_symbols((struct rebinding[2]){{"close", my_close}, {"open", my_open}}, 2); + + // Open our own binary and print out first 4 bytes (which is the same + // for all Mach-O binaries on a given architecture) + int fd = open(argv[0], O_RDONLY); + uint32_t magic_number = 0; + read(fd, &magic_number, 4); + printf("Mach-O Magic Number: %x \n", magic_number); + close(fd); + + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} +``` +### Sample output +``` +Calling real open('/var/mobile/Applications/161DA598-5B83-41F5-8A44-675491AF6A2C/Test.app/Test', 0) +Mach-O Magic Number: feedface +Calling real close(3) +... +``` + +## How it works + +`dyld` binds lazy and non-lazy symbols by updating pointers in particular sections of the `__DATA` segment of a Mach-O binary. __fishhook__ re-binds these symbols by determining the locations to update for each of the symbol names passed to `rebind_symbols` and then writing out the corresponding replacements. + +For a given image, the `__DATA` segment may contain two sections that are relevant for dynamic symbol bindings: `__nl_symbol_ptr` and `__la_symbol_ptr`. `__nl_symbol_ptr` is an array of pointers to non-lazily bound data (these are bound at the time a library is loaded) and `__la_symbol_ptr` is an array of pointers to imported functions that is generally filled by a routine called `dyld_stub_binder` during the first call to that symbol (it's also possible to tell `dyld` to bind these at launch). In order to find the name of the symbol that corresponds to a particular location in one of these sections, we have to jump through several layers of indirection. For the two relevant sections, the section headers (`struct section`s from ``) provide an offset (in the `reserved1` field) into what is known as the indirect symbol table. The indirect symbol table, which is located in the `__LINKEDIT` segment of the binary, is just an array of indexes into the symbol table (also in `__LINKEDIT`) whose order is identical to that of the pointers in the non-lazy and lazy symbol sections. So, given `struct section nl_symbol_ptr`, the corresponding index in the symbol table of the first address in that section is `indirect_symbol_table[nl_symbol_ptr->reserved1]`. The symbol table itself is an array of `struct nlist`s (see ``), and each `nlist` contains an index into the string table in `__LINKEDIT` which where the actual symbol names are stored. So, for each pointer `__nl_symbol_ptr` and `__la_symbol_ptr`, we are able to find the corresponding symbol and then the corresponding string to compare against the requested symbol names, and if there is a match, we replace the pointer in the section with the replacement. + +The process of looking up the name of a given entry in the lazy or non-lazy pointer tables looks like this: +![Visual explanation](http://i.imgur.com/HVXqHCz.png) \ No newline at end of file diff --git a/TrustKit/Dependencies/fishhook/fishhook.c b/TrustKit/Dependencies/fishhook/fishhook.c new file mode 100755 index 00000000..4f3c853c --- /dev/null +++ b/TrustKit/Dependencies/fishhook/fishhook.c @@ -0,0 +1,199 @@ +// Copyright (c) 2013, Facebook, Inc. +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name Facebook nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific +// prior written permission. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import "fishhook.h" + +#import +#import +#import +#import +#import +#import +#import + +#ifdef __LP64__ +typedef struct mach_header_64 mach_header_t; +typedef struct segment_command_64 segment_command_t; +typedef struct section_64 section_t; +typedef struct nlist_64 nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 +#else +typedef struct mach_header mach_header_t; +typedef struct segment_command segment_command_t; +typedef struct section section_t; +typedef struct nlist nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT +#endif + +struct rebindings_entry { + struct rebinding *rebindings; + size_t rebindings_nel; + struct rebindings_entry *next; +}; + +static struct rebindings_entry *_rebindings_head; + +static int prepend_rebindings(struct rebindings_entry **rebindings_head, + struct rebinding rebindings[], + size_t nel) { + struct rebindings_entry *new_entry = malloc(sizeof(struct rebindings_entry)); + if (!new_entry) { + return -1; + } + new_entry->rebindings = malloc(sizeof(struct rebinding) * nel); + if (!new_entry->rebindings) { + free(new_entry); + return -1; + } + memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel); + new_entry->rebindings_nel = nel; + new_entry->next = *rebindings_head; + *rebindings_head = new_entry; + return 0; +} + +static void perform_rebinding_with_section(struct rebindings_entry *rebindings, + section_t *section, + intptr_t slide, + nlist_t *symtab, + char *strtab, + uint32_t *indirect_symtab) { + uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; + void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); + for (uint i = 0; i < section->size / sizeof(void *); i++) { + uint32_t symtab_index = indirect_symbol_indices[i]; + if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || + symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { + continue; + } + uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; + char *symbol_name = strtab + strtab_offset; + struct rebindings_entry *cur = rebindings; + while (cur) { + for (uint j = 0; j < cur->rebindings_nel; j++) { + if (strlen(symbol_name) > 1 && + strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { + indirect_symbol_bindings[i] = cur->rebindings[j].replacement; + goto symbol_loop; + } + } + cur = cur->next; + } + symbol_loop:; + } +} + +static void rebind_symbols_for_image(struct rebindings_entry *rebindings, + const struct mach_header *header, + intptr_t slide) { + Dl_info info; + if (dladdr(header, &info) == 0) { + return; + } + + segment_command_t *cur_seg_cmd; + segment_command_t *linkedit_segment = NULL; + struct symtab_command* symtab_cmd = NULL; + struct dysymtab_command* dysymtab_cmd = NULL; + + uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); + for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { + cur_seg_cmd = (segment_command_t *)cur; + if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { + linkedit_segment = cur_seg_cmd; + } + } else if (cur_seg_cmd->cmd == LC_SYMTAB) { + symtab_cmd = (struct symtab_command*)cur_seg_cmd; + } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { + dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; + } + } + + if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || + !dysymtab_cmd->nindirectsyms) { + return; + } + + // Find base symbol/string table addresses + uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; + nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); + char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); + + // Get indirect symbol table (array of uint32_t indices into symbol table) + uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); + + cur = (uintptr_t)header + sizeof(mach_header_t); + for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { + cur_seg_cmd = (segment_command_t *)cur; + if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0) { + continue; + } + for (uint j = 0; j < cur_seg_cmd->nsects; j++) { + section_t *sect = + (section_t *)(cur + sizeof(segment_command_t)) + j; + if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { + perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); + } + if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { + perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); + } + } + } + } +} + +static void _rebind_symbols_for_image(const struct mach_header *header, + intptr_t slide) { + rebind_symbols_for_image(_rebindings_head, header, slide); +} + +int rebind_symbols_image(void *header, + intptr_t slide, + struct rebinding rebindings[], + size_t rebindings_nel) { + struct rebindings_entry *rebindings_head = NULL; + int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel); + rebind_symbols_for_image(rebindings_head, header, slide); + free(rebindings_head); + return retval; +} + +int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) { + int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel); + if (retval < 0) { + return retval; + } + // If this was the first call, register callback for image additions (which is also invoked for + // existing images, otherwise, just run on existing images + if (!_rebindings_head->next) { + _dyld_register_func_for_add_image(_rebind_symbols_for_image); + } else { + uint32_t c = _dyld_image_count(); + for (uint32_t i = 0; i < c; i++) { + _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); + } + } + return retval; +} diff --git a/TrustKit/Dependencies/fishhook/fishhook.h b/TrustKit/Dependencies/fishhook/fishhook.h new file mode 100755 index 00000000..6a7af766 --- /dev/null +++ b/TrustKit/Dependencies/fishhook/fishhook.h @@ -0,0 +1,67 @@ +// Copyright (c) 2013, Facebook, Inc. +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name Facebook nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific +// prior written permission. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef fishhook_h +#define fishhook_h + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +/* + * A structure representing a particular intended rebinding from a symbol + * name to its replacement + */ +struct rebinding { + char *name; + void *replacement; +}; + +/* + * For each rebinding in rebindings, rebinds references to external, indirect + * symbols with the specified name to instead point at replacement for each + * image in the calling process as well as for all future images that are loaded + * by the process. If rebind_functions is called more than once, the symbols to + * rebind are added to the existing list of rebindings, and if a given symbol + * is rebound more than once, the later rebinding will take precedence. + */ +int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel); + +/* + * Rebinds as above, but only in the specified image. The header should point + * to the mach-o header, the slide should be the slide offset. Others as above. + */ +int rebind_symbols_image(void *header, + intptr_t slide, + struct rebinding rebindings[], + size_t rebindings_nel); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif //fishhook_h + diff --git a/TrustKit/Pinning/TSKPinVerifier.h b/TrustKit/Pinning/TSKPinVerifier.h new file mode 100644 index 00000000..1d7dcf6b --- /dev/null +++ b/TrustKit/Pinning/TSKPinVerifier.h @@ -0,0 +1,79 @@ +/* + + TSKPinVerifier.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 + +/** + Possible return values when verifying a server's identity against the global SSL pinning policy using `TSKPinVerifier`. + + */ +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 supplied hostname does not have a pinning policy configured; no validation was performed. + */ + TSKPinValidationResultDomainNotPinned, +}; + + +/** + `TSKPinVerifier` is a class for manually verifying a server's identity against the global SSL pinning policy. + + In a few specific scenarios, 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 `TSKPinVerifier` for validation. + + The following scenarios require manual pin validation: + + * Connections initiated from an external process, where TrustKit does not get loaded: + * `WKWebView` connections: the server's trust object can be retrieved within the `webView:didReceiveAuthenticationChallenge:completionHandler:` method. + * `NSURLSession` connections using the background transfer service: the server's trust object can be retrieved within the `application:handleEventsForBackgroundURLSession:completionHandler:` method. + * Connections initiated using a third-party SSL library such as OpenSSL, instead of Apple's SecureTransport. The server's trust object needs to be built using its certificate chain. + + */ +@interface TSKPinVerifier : NSObject + +///-------------------------------- +/// @name Manual SSL Pin Validation +///-------------------------------- + +/** + Verify the validity of the supplied server trust against the global SSL pinning policy previously configured. + + @param serverTrust The trust object representing the server's certificate chain. The trust's validation policy is always overriden to ensure all the proper SSL policies (expiration, hostname validation, etc.) are enabled. + + @param serverHostname The hostname of the server whose identity is being validated. + + @return The result of validation. See `TSKPinValidationResult` for possible values. + + @warning If no SSL pinning policy was configured for the supplied _serverHostname_, this method has no effect and will return `TSKPinValidationResultDomainNotPinned` without validating the supplied _serverTrust_ at all. + */ ++ (TSKPinValidationResult) verifyPinForTrust:(SecTrustRef)serverTrust andHostname:(NSString *)serverHostname; + +@end diff --git a/TrustKit/Pinning/TSKPinVerifier.m b/TrustKit/Pinning/TSKPinVerifier.m new file mode 100644 index 00000000..e82e78f6 --- /dev/null +++ b/TrustKit/Pinning/TSKPinVerifier.m @@ -0,0 +1,40 @@ +/* + + TSKPinVerifier.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 "TSKPinVerifier.h" +#import "ssl_pin_verifier.h" +#import "TrustKit+Private.h" + + +@implementation TSKPinVerifier + ++ (TSKPinValidationResult) verifyPinForTrust:(SecTrustRef)serverTrust andHostname:(NSString *)serverHostname +{ + TSKPinValidationResult result = TSKPinValidationResultFailed; + NSDictionary *trustKitConfig = [TrustKit configuration]; + + // Retrieve the pinning configuration for this specific domain, if there is one + NSString *domainConfigKey = getPinningConfigurationKeyForDomain(serverHostname, trustKitConfig); + if (domainConfigKey != nil) + { + // This domain is pinned: look for one the configured public key pins in the server's evaluated certificate chain + NSDictionary *domainConfig = trustKitConfig[domainConfigKey]; + result = verifyPublicKeyPin(serverTrust, serverHostname, domainConfig[kTSKPublicKeyAlgorithms], domainConfig[kTSKPublicKeyHashes]); + } + else + { + // The domain is not pinned: nothing to validate + result = TSKPinValidationResultDomainNotPinned; + } + return result; +} + +@end \ No newline at end of file diff --git a/TrustKit/Pinning/public_key_utils.h b/TrustKit/Pinning/public_key_utils.h index 09d7301a..5783bdff 100644 --- a/TrustKit/Pinning/public_key_utils.h +++ b/TrustKit/Pinning/public_key_utils.h @@ -1,10 +1,13 @@ -// -// public_key_utils.h -// TrustKit -// -// Created by Alban Diquet on 4/7/15. -// Copyright (c) 2015 Data Theorem. All rights reserved. -// +/* + + 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 diff --git a/TrustKit/Pinning/public_key_utils.m b/TrustKit/Pinning/public_key_utils.m index 4c1123b0..4a8d8cf0 100644 --- a/TrustKit/Pinning/public_key_utils.m +++ b/TrustKit/Pinning/public_key_utils.m @@ -1,10 +1,14 @@ -// -// public_key_utils.h -// TrustKit -// -// Created by Alban Diquet on 4/7/15. -// Copyright (c) 2015 Data Theorem. All rights reserved. -// +/* + + public_key_utils.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 "public_key_utils.h" #include @@ -24,24 +28,25 @@ #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 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 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[] = { 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, - 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00 }; // Careful with the order... must match how TSKPublicKeyAlgorithm is defined -static unsigned char *asn1HeaderBytes[3] = { Rsa2048Asn1Header, Rsa4096Asn1Header, ecDsaSecp256r1Asn1Header }; -static unsigned int asn1HeaderSizes[3] = { sizeof(Rsa2048Asn1Header), sizeof(Rsa4096Asn1Header), sizeof(ecDsaSecp256r1Asn1Header) }; +static unsigned char *asn1HeaderBytes[3] = { rsa2048Asn1Header, rsa4096Asn1Header, ecDsaSecp256r1Asn1Header }; +static unsigned int asn1HeaderSizes[3] = { sizeof(rsa2048Asn1Header), sizeof(rsa4096Asn1Header), sizeof(ecDsaSecp256r1Asn1Header) }; @@ -49,7 +54,7 @@ #pragma mark Public Key Converter - iOS -static const NSString *TrustKitPublicKeyTag = @"TSKPublicKeyTag"; // Used to add and find the public key in the Keychain +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 @@ -67,13 +72,15 @@ SecTrustCreateWithCertificates(certificate, policy, &tempTrust); SecTrustEvaluate(tempTrust, NULL); publicKey = SecTrustCopyPublicKey(tempTrust); + CFRelease(policy); + CFRelease(tempTrust); // Extract the actual bytes from the key reference using the Keychain // Prepare the dictionnary to add the key NSMutableDictionary *peerPublicKeyAdd = [[NSMutableDictionary alloc] init]; [peerPublicKeyAdd setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; - [peerPublicKeyAdd setObject:TrustKitPublicKeyTag forKey:(__bridge id)kSecAttrApplicationTag]; + [peerPublicKeyAdd setObject:kTSKKeychainPublicKeyTag forKey:(__bridge id)kSecAttrApplicationTag]; [peerPublicKeyAdd setObject:(__bridge id)(publicKey) forKey:(__bridge id)kSecValueRef]; // Request the key's data to be returned [peerPublicKeyAdd setObject:(__bridge id)(kCFBooleanTrue) forKey:(__bridge id)kSecReturnData]; @@ -81,7 +88,7 @@ // Prepare the dictionnary to retrieve the key NSMutableDictionary * publicKeyGet = [[NSMutableDictionary alloc] init]; [publicKeyGet setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; - [publicKeyGet setObject:(TrustKitPublicKeyTag) forKey:(__bridge id)kSecAttrApplicationTag]; + [publicKeyGet setObject:(kTSKKeychainPublicKeyTag) forKey:(__bridge id)kSecAttrApplicationTag]; [publicKeyGet setObject:(__bridge id)(kCFBooleanTrue) forKey:(__bridge id)kSecReturnData]; @@ -111,25 +118,29 @@ static NSData *getPublicKeyDataFromCertificate(SecCertificateRef certificate) { NSData *publicKeyData = nil; - NSDictionary *certificateValues = nil; - CFErrorRef *error = NULL; + CFErrorRef error = NULL; // SecCertificateCopyValues() is OS X only - certificateValues = (__bridge NSDictionary *)(SecCertificateCopyValues(certificate, NULL, error)); + NSArray *oids = [NSArray arrayWithObject:(__bridge id)(kSecOIDX509V1SubjectPublicKey)]; + CFDictionaryRef certificateValues = SecCertificateCopyValues(certificate, (__bridge CFArrayRef)(oids), &error); if (certificateValues == NULL) { - TSKLog(@"SecCertificateCopyValues() error"); + CFStringRef errorDescription = CFErrorCopyDescription(error); + TSKLog(@"SecCertificateCopyValues() error: %@", errorDescription); + CFRelease(errorDescription); + CFRelease(error); return nil; } - for (NSString* fieldName in certificateValues) + for (NSString* fieldName in (__bridge NSDictionary *)certificateValues) { - NSDictionary *fieldDict = certificateValues[fieldName]; + NSDictionary *fieldDict = CFDictionaryGetValue(certificateValues, (__bridge const void *)(fieldName)); if ([fieldDict[(__bridge __strong id)(kSecPropertyKeyLabel)] isEqualToString:@"Public Key Data"]) { publicKeyData = fieldDict[(__bridge __strong id)(kSecPropertyKeyValue)]; } } + CFRelease(certificateValues); return publicKeyData; } @@ -208,7 +219,7 @@ void initializeSubjectPublicKeyInfoCache(void) // 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:(TrustKitPublicKeyTag) forKey:(__bridge id)kSecAttrApplicationTag]; + [publicKeyGet setObject:(kTSKKeychainPublicKeyTag) forKey:(__bridge id)kSecAttrApplicationTag]; [publicKeyGet setObject:(__bridge id)(kCFBooleanTrue) forKey:(__bridge id)kSecReturnData]; pthread_mutex_lock(&_keychainLock); { diff --git a/TrustKit/Pinning/ssl_pin_verifier.h b/TrustKit/Pinning/ssl_pin_verifier.h index 985c717a..0cc32b69 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.h +++ b/TrustKit/Pinning/ssl_pin_verifier.h @@ -1,27 +1,27 @@ -// -// ssl_pin_verifier.h -// TrustKit -// -// Created by Alban Diquet on 4/23/15. -// Copyright (c) 2015 Data Theorem. All rights reserved. -// +/* + + ssl_pin_verifier.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_ssl_pin_verifier_h #define TrustKit_ssl_pin_verifier_h #import +#import "TSKPinVerifier.h" -typedef NS_ENUM(NSInteger, TSKPinValidationResult) { - TSKPinValidationResultSuccess, - TSKPinValidationResultFailed, - TSKPinValidationResultDomainNotPinned, - TSKPinValidationResultInvalidParameters, - TSKPinValidationResultPinningNotEnforced, - TSKPinValidationResultInvalidCertificateChain, -}; +// 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); -TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *serverName, 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); #endif diff --git a/TrustKit/Pinning/ssl_pin_verifier.m b/TrustKit/Pinning/ssl_pin_verifier.m index 70187bc3..cdd30bdb 100644 --- a/TrustKit/Pinning/ssl_pin_verifier.m +++ b/TrustKit/Pinning/ssl_pin_verifier.m @@ -1,11 +1,13 @@ -// -// ssl_pin_verifier.m -// TrustKit -// -// Created by Alban Diquet on 4/23/15. -// Copyright (c) 2015 Data Theorem. All rights reserved. -// - +/* + + ssl_pin_verifier.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 "ssl_pin_verifier.h" #import "domain_registry.h" @@ -13,7 +15,8 @@ #import "TrustKit+Private.h" -#pragma mark SSL Pin Validator + +#pragma mark Utility functions static BOOL isSubdomain(NSString *domain, NSString *subdomain) { @@ -39,65 +42,79 @@ static BOOL isSubdomain(NSString *domain, NSString *subdomain) } -TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *serverName, NSDictionary *TrustKitConfiguration) +NSString *getPinningConfigurationKeyForDomain(NSString *hostname, NSDictionary *trustKitConfiguration) { - if ((serverTrust == NULL) || (serverName == NULL)) - { - return TSKPinValidationResultInvalidParameters; - } - - // First let's figure out if this domain is pinned - // Do we have this specific domain explicitely pinned ? - NSDictionary *serverPinningConfiguration = TrustKitConfiguration[serverName]; - + NSString *configHostname = nil; - // No pins explicitly configured for this domain - if (serverPinningConfiguration == nil) + if (trustKitConfiguration[hostname] == nil) { + // No pins explicitly configured for this domain // Look for an includeSubdomain pin that applies - for (NSString *pinnedServerName in TrustKitConfiguration) + for (NSString *pinnedServerName in trustKitConfiguration) { // Check each domain configured with the includeSubdomain flag - if ([TrustKitConfiguration[pinnedServerName][kTSKIncludeSubdomains] boolValue]) + if ([trustKitConfiguration[pinnedServerName][kTSKIncludeSubdomains] boolValue]) { // Is the server a subdomain of this pinned server? TSKLog(@"Checking includeSubdomains configuration for %@", pinnedServerName); - if (isSubdomain(pinnedServerName, serverName)) + if (isSubdomain(pinnedServerName, hostname)) { - // Yes; let's use the parent domain's pins - TSKLog(@"Applying includeSubdomains configuration from %@ to %@", pinnedServerName, serverName); - serverPinningConfiguration = TrustKitConfiguration[pinnedServerName]; + // Yes; let's use the parent domain's pinning configuration + TSKLog(@"Applying includeSubdomains configuration from %@ to %@", pinnedServerName, hostname); + configHostname = pinnedServerName; break; } } } } + else + { + // This hostname has a pinnning configuration + configHostname = hostname; + } - // If this domain isn't pinned the validation always succeeds - if (serverPinningConfiguration == nil) + if (configHostname == nil) { - TSKLog(@"Domain %@ is not pinned", serverName); - return TSKPinValidationResultDomainNotPinned; + TSKLog(@"Domain %@ is not pinned", hostname); + } + return configHostname; +} + + +#pragma mark SSL Pin Verifier + +TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *serverHostname, NSArray *supportedAlgorithms, NSSet *knownPins) +{ + if ((serverTrust == NULL) || (supportedAlgorithms == nil) || (knownPins == nil)) + { + return TSKPinValidationResultErrorInvalidParameters; } - // Domain is pinned // First re-check the certificate chain using the default SSL validation in case it was disabled // This gives us revocation (only for EV certs I think?) and also ensures the certificate chain is sane // And also gives us the exact path that successfully validated the chain - NSSet *serverPins = serverPinningConfiguration[kTSKPublicKeyHashes]; + + // Create and use a sane SSL policy to force hostname validation, even if the supplied trust has a bad + // policy configured (such as one from SecPolicyCreateBasicX509()) + SecPolicyRef SslPolicy = SecPolicyCreateSSL(YES, (__bridge CFStringRef)(serverHostname)); + SecTrustSetPolicies(serverTrust, SslPolicy); + CFRelease(SslPolicy); SecTrustResultType trustResult = 0; if (SecTrustEvaluate(serverTrust, &trustResult) != errSecSuccess) { TSKLog(@"SecTrustEvaluate error"); - return TSKPinValidationResultInvalidParameters; + return TSKPinValidationResultErrorInvalidParameters; } if ((trustResult != kSecTrustResultUnspecified) && (trustResult != kSecTrustResultProceed)) { // Default SSL validation failed - TSKLog(@"Error: default SSL validation failed"); - return TSKPinValidationResultInvalidCertificateChain; + + CFDictionaryRef evaluationDetails = SecTrustCopyResult(serverTrust); + TSKLog(@"Error: default SSL validation failed: %@", evaluationDetails); + CFRelease(evaluationDetails); + return TSKPinValidationResultFailedCertificateChainNotTrusted; } // Check each certificate in the server's certificate chain (the trust object) @@ -109,14 +126,14 @@ TSKPinValidationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *ser // For each public key algorithm flagged as supported in the config, generate the subject public key info hash - for (id savedAlgorithm in serverPinningConfiguration[kTSKPublicKeyAlgorithms]) + for (id savedAlgorithm in supportedAlgorithms) { TSKPublicKeyAlgorithm algorithm = [savedAlgorithm integerValue]; NSData *subjectPublicKeyInfoHash = hashSubjectPublicKeyInfoFromCertificate(certificate, algorithm); // Is the generated hash in our set of pinned hashes ? TSKLog(@"Testing SSL Pin %@", subjectPublicKeyInfoHash); - if ([serverPins containsObject:subjectPublicKeyInfoHash]) + if ([knownPins containsObject:subjectPublicKeyInfoHash]) { TSKLog(@"SSL Pin found"); return TSKPinValidationResultSuccess; @@ -124,15 +141,7 @@ 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"); - if ([serverPinningConfiguration[kTSKEnforcePinning] boolValue] == YES) - { - // TrustKit was configured to enforce pinning; force an error - return TSKPinValidationResultFailed; - } - - // TrustKit was configured to not enforce pinning for this domain; don't return an error - return TSKPinValidationResultPinningNotEnforced; + return TSKPinValidationResultFailed; } diff --git a/TrustKit/Reporting/TSKPinFailureReport.h b/TrustKit/Reporting/TSKPinFailureReport.h new file mode 100644 index 00000000..d88984fa --- /dev/null +++ b/TrustKit/Reporting/TSKPinFailureReport.h @@ -0,0 +1,45 @@ +/* + + TSKPinFailureReport.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 + +@interface TSKPinFailureReport : NSObject + +@property (readonly, nonatomic) NSString *appBundleId; // Not part of the HPKP spec +@property (readonly, nonatomic) NSString *appVersion; // Not part of the HPKP spec +@property (readonly, nonatomic) NSString *notedHostname; +@property (readonly, nonatomic) NSString *hostname; +@property (readonly, nonatomic) NSNumber *port; +@property (readonly, nonatomic) NSDate *dateTime; +@property (readonly, nonatomic) BOOL includeSubdomains; +@property (readonly, nonatomic) NSArray *validatedCertificateChain; +@property (readonly, nonatomic) NSArray *knownPins; + + +// Init with default bundle ID and current time as the date-time +- (instancetype) initWithAppBundleId:(NSString *) appBundleId + appVersion:(NSString *)appVersion + notedHostname:(NSString *)notedHostname + hostname:(NSString *)serverHostname + port:(NSNumber *)serverPort + dateTime:(NSDate *)dateTime + includeSubdomains:(BOOL) includeSubdomains + validatedCertificateChain:(NSArray *)validatedCertificateChain + knownPins:(NSArray *)knownPins; + +// Return the report in JSON format for POSTing it +- (NSData *)json; + +// Return a request ready to be sent with the report in JSON format in the response's body +- (NSMutableURLRequest *)requestToUri:(NSURL *)reportUri; + + +@end diff --git a/TrustKit/Reporting/TSKPinFailureReport.m b/TrustKit/Reporting/TSKPinFailureReport.m new file mode 100644 index 00000000..195b7f7e --- /dev/null +++ b/TrustKit/Reporting/TSKPinFailureReport.m @@ -0,0 +1,84 @@ +/* + + TSKPinFailureReport.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 "TSKPinFailureReport.h" + +@implementation TSKPinFailureReport + + +- (instancetype) initWithAppBundleId:(NSString *) appBundleId + appVersion:(NSString *)appVersion + notedHostname:(NSString *)notedHostname + hostname:(NSString *)serverHostname + port:(NSNumber *)serverPort + dateTime:(NSDate *)dateTime + includeSubdomains:(BOOL) includeSubdomains + validatedCertificateChain:(NSArray *)validatedCertificateChain + knownPins:(NSArray *)knownPins +{ + self = [super init]; + if (self) + { + _appBundleId = appBundleId; + _appVersion = appVersion; + _notedHostname = notedHostname; + _hostname = serverHostname; + _port = serverPort; + _dateTime = dateTime; + _includeSubdomains = includeSubdomains; + _validatedCertificateChain = validatedCertificateChain; + _knownPins = knownPins; + } + return self; +} + + +- (NSData *)json; +{ + // Convert the date to a string + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"]; + [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; + NSString *currentTimeStr = [dateFormatter stringFromDate: self.dateTime]; + + // TODO: Convert knownPins and validatedCertificateChain + + // Create the dictionary + NSDictionary *requestData = @ { + @"app-bundle-id" : self.appBundleId, + @"app-version" : self.appVersion, + @"date-time" : currentTimeStr, + @"hostname" : self.hostname, + @"port" : self.port, + @"include-subdomains" : [NSNumber numberWithBool:self.includeSubdomains], + @"noted-hostname" : self.notedHostname, + @"validated-certificate-chain" : self.validatedCertificateChain, + @"known-pins" : self.knownPins + }; + + // TODO: Check error + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:requestData options:0 error:&error]; + return jsonData; +} + + +- (NSMutableURLRequest *)requestToUri:(NSURL *)reportUri +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:reportUri]; + [request setHTTPMethod:@"POST"]; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + [request setHTTPBody:[self json]]; + return request; +} + + +@end diff --git a/TrustKit/Reporting/TSKReporterDelegate.h b/TrustKit/Reporting/TSKReporterDelegate.h index 0039819d..0e05a205 100644 --- a/TrustKit/Reporting/TSKReporterDelegate.h +++ b/TrustKit/Reporting/TSKReporterDelegate.h @@ -1,36 +1,43 @@ -// -// TSKReporterDelegate.h -// TrustKit -// -// Created by Angela Chow on 4/23/15. -// Copyright (c) 2015 Yahoo! Inc. All rights reserved. -// +/* + + TSKReporterDelegate.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 @protocol TSKReporterDelegate -/* - * Initialize the reporter with the app's bundle id, app version, and the URL to send the reports to - */ -- (instancetype)initWithAppBundleId:(NSString *) appBundleId - appVersion:(NSString *) appVersion - reportingURL:(NSString *) reportingURL; -@optional /* - * Pin validation failed for a connection to a pinned domain + * Pin validation failed for a connection to a pinned domain. Each argument is described section 3. of RFC 7469. */ -- (void) pinValidationFailed:(NSString *) pinnedDomain - serverHostname:(NSString *) hostname - serverPort:(NSString *) port - includeSubdomains:(Boolean) includeSubdomains - certificateChain:(NSArray *) validatedCertificateChain - expectedPins:(NSArray *) knownPins; +- (void) pinValidationFailedForHostname:(NSString *) serverHostname + port:(NSNumber *) serverPort + trust:(SecTrustRef) serverTrust + notedHostname:(NSString *) notedHostname + reportURIs:(NSArray *) reportURIs + includeSubdomains:(BOOL) includeSubdomains + knownPins:(NSArray *) knownPins; + +@optional /* - * Pin validation succeeded for a connection to a pinned domain + * Pin validation succeeded for a connection to a pinned domain. + * + * This is an optional function, as usually, reports are only sent upon pin failure. However, a + * smart reporter would realize that sending reports while connections are being MITM is not the + * best way of ensuring reports are being successfully received. Therefore, those reporters + * may delay the sending of those reports and preferably send them when the connections + * are no longer in the MITM state. In order to know that connections are no longer MITM, + * the pinValidationSucceeded function will provide a way to know that connections are fine now. */ -- (void) pinValidationSucceeded:(NSString*) pinnedDomain; - +- (void) pinValidationSucceededForHostname:(NSString *) serverHostname + port:(NSNumber *) serverPort + notedHostname:(NSString *) notedHostname; @end diff --git a/TrustKit/Reporting/TSKSimpleBackgroundReporter.h b/TrustKit/Reporting/TSKSimpleBackgroundReporter.h new file mode 100644 index 00000000..193e4eda --- /dev/null +++ b/TrustKit/Reporting/TSKSimpleBackgroundReporter.h @@ -0,0 +1,29 @@ +/* + + TSKSimpleBackgroundReporter.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 "TSKReporterDelegate.h" + +/* + * This is a very simple implementation of a reporter delegate using a background task in sending out the + * report each time it receives pinValidationFailed. It does not implement pinValidationSucceeded + * as it does not care about successful validation. It also does not try to optimize/throttle the reports sent. + */ +@interface TSKSimpleBackgroundReporter : NSObject + +/* + * Initialize the reporter with the app's bundle id, and app version + */ +- (instancetype)initWithAppBundleId:(NSString *)appBundleId + appVersion:(NSString *)appVersion; + +@end + diff --git a/TrustKit/Reporting/TSKSimpleBackgroundReporter.m b/TrustKit/Reporting/TSKSimpleBackgroundReporter.m new file mode 100644 index 00000000..cabaa3ec --- /dev/null +++ b/TrustKit/Reporting/TSKSimpleBackgroundReporter.m @@ -0,0 +1,173 @@ +/* + + TSKSimpleBackgroundReporter.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 "TSKSimpleBackgroundReporter.h" +#import "TrustKit+Private.h" +#import "TSKPinFailureReport.h" +#import "reporting_utils.h" + +// Session identifier for background uploads: .TSKSimpleReporter +static NSString* kTSKBackgroundSessionIdentifierFormat = @"%@.TSKSimpleReporter"; +static dispatch_once_t dispatchOnceBackgroundSession; + + +@interface TSKSimpleBackgroundReporter() + +@property (nonatomic, strong) NSString * appBundleId; +@property (nonatomic, strong) NSString * appVersion; +@property (nonatomic) NSURLSession *session; +@end + + +@implementation TSKSimpleBackgroundReporter + + +- (instancetype)initWithAppBundleId:(NSString *)appBundleId + appVersion:(NSString *)appVersion +{ + self = [super init]; + if (self) + { + if ((appBundleId == nil) || ([appBundleId length] == 0)) + { + self.appBundleId = @"N/A"; + } + else + { + self.appBundleId = appBundleId; + } + + if ((appVersion == nil) || ([appVersion length] == 0)) + { + self.appVersion = @"N/A"; + } + else + { + self.appVersion = appVersion; + } + self.session = [self backgroundSession]; + } + return self; +} + +- (NSURLSession *)backgroundSession +{ + /* + Using disptach_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. + */ + __block NSURLSession *session = nil; + dispatch_once(&dispatchOnceBackgroundSession, ^{ + + NSURLSessionConfiguration *backgroundConfiguration; + + // The API for creating background sessions changed between iOS 7 and iOS 8 and OS X 10.9 and 10.10 + if ([NSURLSessionConfiguration respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]) + { + // iOS 8+ or OS X 10.10+ + backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: [NSString stringWithFormat:kTSKBackgroundSessionIdentifierFormat, self.appBundleId]]; + } else + { + // iOS 7 or OS X 10.9 + backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:[NSString stringWithFormat:kTSKBackgroundSessionIdentifierFormat, self.appBundleId]]; + } + +#if TARGET_OS_IPHONE + // iOS-only settings + // Do not wake up the App after completing the upload + backgroundConfiguration.sessionSendsLaunchEvents = NO; +#endif + + backgroundConfiguration.discretionary = YES; + session = [NSURLSession sessionWithConfiguration:backgroundConfiguration delegate:self delegateQueue:nil]; + }); + return session; +} + +/* + Pin validation failed for a connection to a pinned domain + In this implementation for a simple background reporter, we're just going to send out the report upon each failure + in a background task + */ + +- (void) pinValidationFailedForHostname:(NSString *) serverHostname + port:(NSNumber *) serverPort + trust:(SecTrustRef) serverTrust + notedHostname:(NSString *) notedHostname + reportURIs:(NSArray *) reportURIs + includeSubdomains:(BOOL) includeSubdomains + knownPins:(NSArray *) knownPins; +{ + // Default port to 443 if not specified + if (serverPort == nil) + { + serverPort = [NSNumber numberWithInt:443]; + } + + if (reportURIs == nil) + { + [NSException raise:@"TrustKit Simple Background Reporter configuration invalid" + format:@"Reporter was given an invalid value for reportURIs: %@ for domain %@", + reportURIs, notedHostname]; + } + + // Create the pin validation failure report + NSArray *certificateChain = convertTrustToPemArray(serverTrust); + NSArray *formattedPins = convertPinsToHpkpPins(knownPins); + TSKPinFailureReport *report = [[TSKPinFailureReport alloc]initWithAppBundleId:self.appBundleId + appVersion:self.appVersion + notedHostname:notedHostname + hostname:serverHostname + port:serverPort + dateTime:[NSDate date] // Use the current time + includeSubdomains:includeSubdomains + validatedCertificateChain:certificateChain + knownPins:formattedPins]; + + // 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"]; + TSKLog(@"Report created at: %@", [tmpFileURL path]); + + // Write the JSON report data to the temporary file + if (!([[report json] writeToFile:[tmpFileURL path] atomically:YES])) { + [NSException raise:@"TrustKit Simple Reporter runtime error" + format:@"Report cannot be saved to file"]; + } + + + // Create the HTTP request for all the configured report URIs and send it + for (NSURL *reportUri in reportURIs) + { + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:reportUri]; + [request setHTTPMethod:@"POST"]; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + + // Pass the URL and the temporary file to the background upload task and start uploading + NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:tmpFileURL]; + [uploadTask resume]; + } +} + + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error +{ + if (error == nil) + { + TSKLog(@"Background upload - task %@ completed successfully", task); + } + else + { + TSKLog(@"Background upload - task %@ completed with error: %@ (code %ld)", task, [error localizedDescription], (long)error.code); + } +} + +@end + diff --git a/TrustKit/Reporting/TSKSimpleReporter.h b/TrustKit/Reporting/TSKSimpleReporter.h new file mode 100644 index 00000000..f15475a4 --- /dev/null +++ b/TrustKit/Reporting/TSKSimpleReporter.h @@ -0,0 +1,29 @@ +/* + + TSKSimpleReporter.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 "TSKReporterDelegate.h" + +/* + * This is a very simple implementation of a reporter delegate showing how it just sends out a POST request + * with the report each time it receives pinValidationFailed. It does not implement pinValidationSucceeded + * as it does not care about successful validation. It also does not try to optimize/throttle the reports sent. + */ +@interface TSKSimpleReporter : NSObject + +/* + * Initialize the reporter with the app's bundle id, and app version + */ +- (instancetype)initWithAppBundleId:(NSString *) appBundleId + appVersion:(NSString *) appVersion; + +@end + diff --git a/TrustKit/Reporting/TSKSimpleReporter.m b/TrustKit/Reporting/TSKSimpleReporter.m new file mode 100644 index 00000000..61ed63b1 --- /dev/null +++ b/TrustKit/Reporting/TSKSimpleReporter.m @@ -0,0 +1,110 @@ +/* + + TSKSimpleReporter.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 "TSKSimpleReporter.h" +#import "TSKPinFailureReport.h" +#import "reporting_utils.h" + +@interface TSKSimpleReporter() +@property (nonatomic, strong) NSString * appBundleId; +@property (nonatomic, strong) NSString * appVersion; +@end + + +@implementation TSKSimpleReporter + + +/* + * Initialize the reporter with the app's bundle id and app version + */ +- (instancetype)initWithAppBundleId:(NSString *) appBundleId + appVersion:(NSString *) appVersion +{ + self = [super init]; + if (self) + { + if ((appBundleId == nil) || ([appBundleId length] == 0)) + { + [NSException raise:@"TrustKit Simple Reporter configuration invalid" + format:@"Reporter was given empty appBundleId"]; + } + self.appBundleId = appBundleId; + + if ((appVersion == nil) || ([appVersion length] == 0)) + { + [NSException raise:@"TrustKit Simple Reporter configuration invalid" + format:@"Reporter was given empty appVersion"]; + } + self.appVersion = appVersion; + } + return self; +} + +/* + * Pin validation failed for a connection to a pinned domain + * In this implementation for a simple reporter, we're just going to send out the report upon each failure + */ +- (void) pinValidationFailedForHostname:(NSString *) serverHostname + port:(NSNumber *) serverPort + trust:(SecTrustRef) serverTrust + notedHostname:(NSString *) notedHostname + reportURIs:(NSArray *) reportURIs + includeSubdomains:(BOOL) includeSubdomains + knownPins:(NSArray *) knownPins; +{ + // Default port to 443 if not specified + if (serverPort == nil) + { + serverPort = [NSNumber numberWithInt:443]; + } + + if (reportURIs == nil) + { + [NSException raise:@"TrustKit Simple Reporter configuration invalid" + format:@"Reporter was given an invalid value for reportURIs: %@ for domain %@", + reportURIs, notedHostname]; + } + + // Create the pin validation failure report + NSArray *certificateChain = convertTrustToPemArray(serverTrust); + NSArray *formattedPins = convertPinsToHpkpPins(knownPins); + TSKPinFailureReport *report = [[TSKPinFailureReport alloc]initWithAppBundleId:self.appBundleId + appVersion:self.appVersion + notedHostname:notedHostname + hostname:serverHostname + port:serverPort + dateTime:[NSDate date] // Use the current time + includeSubdomains:includeSubdomains + validatedCertificateChain:certificateChain + knownPins:formattedPins]; + + // Create the session for sending the report + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration + delegate:self + delegateQueue:nil]; + + // POST the report to all the configured report URIs + for (NSURL *reportUri in reportURIs) + { + NSURLRequest *request = [report requestToUri:reportUri]; + NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + // We don't do anything here as reports are meant to be sent + // on a best-effort basis: even if we got an error, there's + // nothing to do anyway. + }]; + [postDataTask resume]; + } +} + + +@end diff --git a/TrustKit/Reporting/reporting_utils.h b/TrustKit/Reporting/reporting_utils.h new file mode 100644 index 00000000..ffd96077 --- /dev/null +++ b/TrustKit/Reporting/reporting_utils.h @@ -0,0 +1,19 @@ +/* + + reporting_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_reporting_utils_h +#define TrustKit_reporting_utils_h + + +NSArray *convertTrustToPemArray(SecTrustRef serverTrust); +NSArray *convertPinsToHpkpPins(NSArray *knownPins); + +#endif diff --git a/TrustKit/Reporting/reporting_utils.m b/TrustKit/Reporting/reporting_utils.m new file mode 100644 index 00000000..758e87c6 --- /dev/null +++ b/TrustKit/Reporting/reporting_utils.m @@ -0,0 +1,42 @@ +/* + + reporting_utils.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 + + +NSArray *convertTrustToPemArray(SecTrustRef serverTrust) +{ + // Convert the trust object into an array of PEM certificates + NSMutableArray *certificateChain = [NSMutableArray array]; + CFIndex chainLen = SecTrustGetCertificateCount(serverTrust); + for (CFIndex i=0;i @@ -15,21 +18,102 @@ FOUNDATION_EXPORT double TrustKitVersionNumber; FOUNDATION_EXPORT const unsigned char TrustKitVersionString[]; -// Keys for each domain within the config dictionnary +#pragma mark TrustKit Configuration Keys extern NSString * const kTSKPublicKeyHashes; extern NSString * const kTSKEnforcePinning; extern NSString * const kTSKIncludeSubdomains; extern NSString * const kTSKPublicKeyAlgorithms; extern NSString * const kTSKReportUris; +extern NSString * const kTSKDisableDefaultReportUri; -// Public key algorithms supported by TrustKit +#pragma mark Supported Public Key Algorithm Keys extern NSString * const kTSKAlgorithmRsa2048; extern NSString * const kTSKAlgorithmRsa4096; extern NSString * const kTSKAlgorithmEcDsaSecp256r1; +/** + `TrustKit` is a class for configuring the global SSL pinning policy in an App that statically links TrustKit. + + Initializing TrustKit requires supplying a dictionary containing domain names as keys and dictionaries as values. Each domain dictionary should specify some configuration keys, which will specify the pinning policy for this domain. For example: + + NSDictionary *trustKitConfig; + trustKitConfig = @{ + @"www.datatheorem.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + kTSKPublicKeyHashes : @[ + @"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=", + @"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8=" + ], + kTSKEnforcePinning : @NO, + kTSKReportUris : @[@"http://report.datatheorem.com/log_hpkp_report"], + }, + @"yahoo.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[ + @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", + @"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=", + ], + kTSKIncludeSubdomains : @YES + } + }; + + [TrustKit initializeWithConfiguration:trustKitConfig]; + + When dynamically linked, TrustKit is automatically initialized by reading configuration keys from the App's _Info.plist_, and no initialization method needs to be called. + + + ### Required Configuration Keys + + #### `kTSKPublicKeyHashes` + An array of SSL pins; 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. + + #### `kTSKPublicKeyAlgorithms` + An array of `kTSKAlgorithm` 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 there are no APIs to directly extract the key's algorithm from an SSL certificate. To minimize the performance impact of Trustkit, only one algorithm should be enabled. + + + ### Optional Configuration Keys + + #### `kTSKIncludeSubdomains` + If set to `YES`, also pin all the subdomains of the specified domain; default value is `NO`. + + #### `kTSKEnforcePinning` + If set to `NO`, a pinning failure will not cause the SSL connection to fail; default value is `YES`. + + #### `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. 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 the HPKP specification. + + #### `kTSKDisableDefaultReportUri` + If set to `YES`, the default report URL for sending pin failure reports will be disabled. 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. Only reports are sent, which contain the App's bundle ID and the server's hostname and certificate chain that failed validation. This behavior can be disabled by setting this key to `YES`. + + + ### Public Key Algorithms Keys + + Public key algorithms supported by TrustKit for computing SSL pins. + + #### `kTSKAlgorithmRsa2048` + + #### `kTSKAlgorithmRsa4096` + + #### `kTSKAlgorithmEcDsaSecp256r1` + + */ @interface TrustKit : NSObject -+ (void) initializeWithConfiguration:(NSDictionary *)TrustKitConfig; +///--------------------- +/// @name Initialization +///--------------------- + +/** + Initializes 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 HTTPS connections are validated by TrustKit. + + @param trustKitConfig A dictionnary 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; @end + diff --git a/TrustKit/TrustKit.m b/TrustKit/TrustKit.m index 40bd34c1..43c13af2 100644 --- a/TrustKit/TrustKit.m +++ b/TrustKit/TrustKit.m @@ -1,11 +1,13 @@ -// -// TrustKit.m -// TrustKit -// -// Created by Alban Diquet on 2/9/15. -// Copyright (c) 2015 Data Theorem. All rights reserved. -// - +/* + + TrustKit.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 "TrustKit+Private.h" #include @@ -14,19 +16,24 @@ #import "public_key_utils.h" #import "domain_registry.h" #import "ssl_pin_verifier.h" +#import "TSKSimpleBackgroundReporter.h" + +#pragma mark Configuration Constants // Info.plist key we read the public key hashes from static NSString * const kTSKConfiguration = @"TSKConfiguration"; + // Keys for each domain within the config dictionnary NSString * const kTSKPublicKeyHashes = @"TSKPublicKeyHashes"; NSString * const kTSKEnforcePinning = @"TSKEnforcePinning"; NSString * const kTSKIncludeSubdomains = @"TSKIncludeSubdomains"; NSString * const kTSKPublicKeyAlgorithms = @"TSKPublicKeyAlgorithms"; NSString * const kTSKReportUris = @"TSKReportUris"; +NSString * const kTSKDisableDefaultReportUri = @"TSKDisableDefaultReportUri"; -// Public key algorithms supported by TrustKit +#pragma mark Public key Algorithms Constants NSString * const kTSKAlgorithmRsa2048 = @"TSKAlgorithmRsa2048"; NSString * const kTSKAlgorithmRsa4096 = @"TSKAlgorithmRsa4096"; NSString * const kTSKAlgorithmEcDsaSecp256r1 = @"TSKAlgorithmEcDsaSecp256r1"; @@ -38,11 +45,21 @@ // Global preventing multiple initializations (double function interposition, etc.) static BOOL _isTrustKitInitialized = NO; +static dispatch_once_t dispatchOnceTrustKitInit; + +// Reporter delegate for sending pin violation reports +static TSKSimpleBackgroundReporter *_pinFailureReporter = nil; +static char kTSKPinFailureReporterQueueLabel[] = "com.datatheorem.trustkit.reporterqueue"; +static dispatch_queue_t _pinFailureReporterQueue = NULL; // For tests static BOOL _wasTrustKitCalled = NO; +// Default report URI - can be disabled with TSKDisableDefaultReportUri +static NSString * const kTSKDefaultReportUri = @"https://trustkit-reports-server.appspot.com/log_report"; + + #pragma mark Logging Function void TSKLog(NSString *format, ...) @@ -58,7 +75,6 @@ void TSKLog(NSString *format, ...) } - #pragma mark SSLHandshake Hook static OSStatus (*original_SSLHandshake)(SSLContextRef context) = NULL; @@ -81,19 +97,55 @@ static OSStatus replaced_SSLHandshake(SSLContextRef context) NSString *serverNameStr = [NSString stringWithUTF8String:serverName]; free(serverName); - // Verify the server's certificate if it is pinned SecTrustRef serverTrust; SSLCopyPeerTrust(context, &serverTrust); - TSKPinValidationResult validationResult = verifyPublicKeyPin(serverTrust, serverNameStr, _trustKitGlobalConfiguration); - - if (! - ((validationResult == TSKPinValidationResultSuccess) - || (validationResult == TSKPinValidationResultDomainNotPinned) - || (validationResult == TSKPinValidationResultPinningNotEnforced))) + // Retrieve the pinning configuration for this specific domain, if there is one + NSString *domainConfigKey = getPinningConfigurationKeyForDomain(serverNameStr, _trustKitGlobalConfiguration); + if (domainConfigKey != nil) { - // Validation failed - result = errSSLXCertChainInvalid; + // This domain is pinned: look for one the configured public key pins in the server's evaluated certificate chain + TSKPinValidationResult validationResult = TSKPinValidationResultFailed; + NSDictionary *domainConfig = _trustKitGlobalConfiguration[domainConfigKey]; + + validationResult = verifyPublicKeyPin(serverTrust, serverNameStr, domainConfig[kTSKPublicKeyAlgorithms], domainConfig[kTSKPublicKeyHashes]); + + if (validationResult != TSKPinValidationResultSuccess) + { + // Pin validation failed: notify the reporter delegate if a report URI was configured + NSMutableArray *reportUris = [NSMutableArray arrayWithArray:domainConfig[kTSKReportUris]]; + +#if !DEBUG + // For prod also enable the default URL + if ([domainConfig[kTSKDisableDefaultReportUri] boolValue] == NO) + { + [reportUris addObject:[NSURL URLWithString:kTSKDefaultReportUri]]; + } +#endif + + if ((reportUris != nil) && ([reportUris count] > 0)) + { + dispatch_async(_pinFailureReporterQueue, ^(void) + { + [_pinFailureReporter pinValidationFailedForHostname:serverNameStr + port:nil + trust:serverTrust + notedHostname:domainConfigKey + reportURIs:reportUris + includeSubdomains:[domainConfig[kTSKIncludeSubdomains] boolValue] + knownPins:domainConfig[kTSKPublicKeyHashes]]; + CFRelease(serverTrust); + }); + } + + if (([domainConfig[kTSKEnforcePinning] boolValue] == YES) + || (validationResult == TSKPinValidationResultFailedCertificateChainNotTrusted) + || (validationResult == TSKPinValidationResultErrorInvalidParameters)) + { + // TrustKit was configured to enforce pinning or the certificate chain was not trusted: make the connection fail + result = errSSLXCertChainInvalid; + } + } } } return result; @@ -154,6 +206,19 @@ static OSStatus replaced_SSLHandshake(SSLContextRef context) } + // Extract the optional disableDefaultReportUri setting + NSNumber *shouldDisableDefaultReportUri = domainTrustKitArguments[kTSKDisableDefaultReportUri]; + if (shouldDisableDefaultReportUri) + { + domainFinalConfiguration[kTSKDisableDefaultReportUri] = shouldDisableDefaultReportUri; + } + else + { + // Default setting is NO + domainFinalConfiguration[kTSKDisableDefaultReportUri] = [NSNumber numberWithBool:NO]; + } + + // Extract the list of public key algorithms to support and convert them from string to the TSKPublicKeyAlgorithm type NSArray *publicKeyAlgsStr = domainTrustKitArguments[kTSKPublicKeyAlgorithms]; if (publicKeyAlgsStr == nil) @@ -230,9 +295,9 @@ static OSStatus replaced_SSLHandshake(SSLContextRef context) } -static void initializeTrustKit(NSDictionary *TrustKitConfig) +static void initializeTrustKit(NSDictionary *trustKitConfig) { - if (TrustKitConfig == nil) + if (trustKitConfig == nil) { return; } @@ -243,49 +308,72 @@ static void initializeTrustKit(NSDictionary *TrustKitConfig) [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:parseTrustKitArguments(TrustKitConfig)]; - - // Hook SSLHandshake() - if (original_SSLHandshake == NULL) + dispatch_once(&dispatchOnceTrustKitInit, ^{ + if ([trustKitConfig count] > 0) { - int rebindResult = -1; - char functionToHook[] = "SSLHandshake"; - original_SSLHandshake = dlsym(RTLD_DEFAULT, functionToHook); - rebindResult = rebind_symbols((struct rebinding[1]){{(char *)functionToHook, (void *)replaced_SSLHandshake}}, 1); - if ((rebindResult < 0) || (original_SSLHandshake == NULL)) + initializeSubjectPublicKeyInfoCache(); + + // Convert and store the SSL pins in our global variable + _trustKitGlobalConfiguration = [[NSDictionary alloc]initWithDictionary:parseTrustKitArguments(trustKitConfig)]; + + // Hook SSLHandshake() + if (original_SSLHandshake == NULL) { - [NSException raise:@"TrustKit initialization error" format:@"Fishook returned an error: %d", rebindResult]; + int rebindResult = -1; + char functionToHook[] = "SSLHandshake"; + original_SSLHandshake = dlsym(RTLD_DEFAULT, functionToHook); + rebindResult = rebind_symbols((struct rebinding[1]){{(char *)functionToHook, (void *)replaced_SSLHandshake}}, 1); + if ((rebindResult < 0) || (original_SSLHandshake == NULL)) + { + [NSException raise:@"TrustKit initialization error" format:@"Fishook returned an error: %d", rebindResult]; + } } + + // Create our reporter for sending pin validation failures + CFBundleRef appBundle = CFBundleGetMainBundle(); + NSString *appBundleId = (NSString *)CFBundleGetIdentifier(appBundle); + NSString *appVersion = CFBundleGetValueForInfoDictionaryKey(appBundle, kCFBundleVersionKey); + _pinFailureReporter = [[TSKSimpleBackgroundReporter alloc]initWithAppBundleId:appBundleId appVersion:appVersion]; + + // 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)); + + // All done + _isTrustKitInitialized = YES; + TSKLog(@"TrustKit initialized with configuration %@", _trustKitGlobalConfiguration); } - - _isTrustKitInitialized = YES; - TSKLog(@"TrustKit initialized with configuration %@", _trustKitGlobalConfiguration); - } + }); } -#pragma mark Framework Initialization When Statically Linked - @implementation TrustKit -+ (void) initializeWithConfiguration:(NSDictionary *)TrustKitConfig +#pragma mark Framework Initialization When Statically Linked + ++ (void) initializeWithConfiguration:(NSDictionary *)trustKitConfig { - TSKLog(@"TrustKit started statically in App %@", CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), (__bridge CFStringRef)@"CFBundleIdentifier")); - initializeTrustKit(TrustKitConfig); + TSKLog(@"TrustKit started statically in App %@", (NSString *)CFBundleGetIdentifier(CFBundleGetMainBundle())); + initializeTrustKit(trustKitConfig); } + +# pragma mark Private / Test Methods + (BOOL) wasTrustKitCalled { return _wasTrustKitCalled; } ++ (NSDictionary *) configuration +{ + return _trustKitGlobalConfiguration; +} + + + (void) resetConfiguration { // This is only used for tests @@ -293,6 +381,9 @@ + (void) resetConfiguration _trustKitGlobalConfiguration = nil; _isTrustKitInitialized = NO; _wasTrustKitCalled = NO; + _pinFailureReporter = nil; + _pinFailureReporterQueue= NULL; + dispatchOnceTrustKitInit = 0; } @end @@ -304,7 +395,7 @@ + (void) resetConfiguration { // TrustKit just got injected in the App CFBundleRef appBundle = CFBundleGetMainBundle(); - TSKLog(@"TrustKit started dynamically in App %@", CFBundleGetValueForInfoDictionaryKey(appBundle, (__bridge CFStringRef)@"CFBundleIdentifier")); + TSKLog(@"TrustKit started dynamically in App %@", (NSString *)CFBundleGetIdentifier(CFBundleGetMainBundle())); // Retrieve the configuration from the App's Info.plist file NSDictionary *trustKitConfigFromInfoPlist = CFBundleGetValueForInfoDictionaryKey(appBundle, (__bridge CFStringRef)kTSKConfiguration); diff --git a/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj b/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj new file mode 100644 index 00000000..d269dce8 --- /dev/null +++ b/TrustKitDemo/TrustKitDemo.xcodeproj/project.pbxproj @@ -0,0 +1,550 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 6B6B475A1B1EECB4007757EE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B6B47591B1EECB4007757EE /* main.m */; }; + 6B6B475D1B1EECB4007757EE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B6B475C1B1EECB4007757EE /* AppDelegate.m */; }; + 6B6B47601B1EECB4007757EE /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B6B475F1B1EECB4007757EE /* ViewController.m */; }; + 6B6B47631B1EECB4007757EE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6B6B47611B1EECB4007757EE /* Main.storyboard */; }; + 6B6B47651B1EECB4007757EE /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6B6B47641B1EECB4007757EE /* Images.xcassets */; }; + 6B6B47681B1EECB4007757EE /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6B6B47661B1EECB4007757EE /* LaunchScreen.xib */; }; + 6B6B47741B1EECB4007757EE /* TrustKitDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B6B47731B1EECB4007757EE /* TrustKitDemoTests.m */; }; + 8C8717291B23D13C00267E1D /* TrustKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B6B47831B1F0BBD007757EE /* TrustKit.framework */; }; + 8C87172A1B23D13C00267E1D /* TrustKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6B6B47831B1F0BBD007757EE /* TrustKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 6B6B476E1B1EECB4007757EE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6B6B474C1B1EECB3007757EE /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6B6B47531B1EECB3007757EE; + remoteInfo = TrustKitDemo; + }; + 6B6B47821B1F0BBD007757EE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6B6B477D1B1F0BBD007757EE /* TrustKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8C8480471A896EE30017C155; + remoteInfo = TrustKit; + }; + 6B6B47841B1F0BBD007757EE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6B6B477D1B1F0BBD007757EE /* TrustKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8C8480521A896EE30017C155; + remoteInfo = TrustKitTests; + }; + 6B6B47861B1F0BF3007757EE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6B6B477D1B1F0BBD007757EE /* TrustKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8C8480461A896EE30017C155; + remoteInfo = TrustKit; + }; + 8C8717271B23D12300267E1D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6B6B477D1B1F0BBD007757EE /* TrustKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8C8716961B23A91D00267E1D; + remoteInfo = TrustKit_Static; + }; + 8C87172B1B23D13D00267E1D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6B6B477D1B1F0BBD007757EE /* TrustKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8C8480461A896EE30017C155; + remoteInfo = TrustKit; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 8C87172D1B23D13D00267E1D /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 8C87172A1B23D13C00267E1D /* TrustKit.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 6B6B47541B1EECB4007757EE /* TrustKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TrustKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6B6B47581B1EECB4007757EE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6B6B47591B1EECB4007757EE /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 6B6B475B1B1EECB4007757EE /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 6B6B475C1B1EECB4007757EE /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 6B6B475E1B1EECB4007757EE /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 6B6B475F1B1EECB4007757EE /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 6B6B47621B1EECB4007757EE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 6B6B47641B1EECB4007757EE /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 6B6B47671B1EECB4007757EE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; + 6B6B476D1B1EECB4007757EE /* TrustKitDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TrustKitDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 6B6B47721B1EECB4007757EE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6B6B47731B1EECB4007757EE /* TrustKitDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TrustKitDemoTests.m; sourceTree = ""; }; + 6B6B477D1B1F0BBD007757EE /* TrustKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TrustKit.xcodeproj; path = ../../TrustKit/TrustKit.xcodeproj; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6B6B47511B1EECB3007757EE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8C8717291B23D13C00267E1D /* TrustKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6B6B476A1B1EECB4007757EE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6B6B474B1B1EECB3007757EE = { + isa = PBXGroup; + children = ( + 6B6B477D1B1F0BBD007757EE /* TrustKit.xcodeproj */, + 6B6B47561B1EECB4007757EE /* TrustKitDemo */, + 6B6B47701B1EECB4007757EE /* TrustKitDemoTests */, + 6B6B47551B1EECB4007757EE /* Products */, + ); + sourceTree = ""; + }; + 6B6B47551B1EECB4007757EE /* Products */ = { + isa = PBXGroup; + children = ( + 6B6B47541B1EECB4007757EE /* TrustKitDemo.app */, + 6B6B476D1B1EECB4007757EE /* TrustKitDemoTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 6B6B47561B1EECB4007757EE /* TrustKitDemo */ = { + isa = PBXGroup; + children = ( + 6B6B475B1B1EECB4007757EE /* AppDelegate.h */, + 6B6B475C1B1EECB4007757EE /* AppDelegate.m */, + 6B6B475E1B1EECB4007757EE /* ViewController.h */, + 6B6B475F1B1EECB4007757EE /* ViewController.m */, + 6B6B47611B1EECB4007757EE /* Main.storyboard */, + 6B6B47641B1EECB4007757EE /* Images.xcassets */, + 6B6B47661B1EECB4007757EE /* LaunchScreen.xib */, + 6B6B47571B1EECB4007757EE /* Supporting Files */, + ); + path = TrustKitDemo; + sourceTree = ""; + }; + 6B6B47571B1EECB4007757EE /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 6B6B47581B1EECB4007757EE /* Info.plist */, + 6B6B47591B1EECB4007757EE /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 6B6B47701B1EECB4007757EE /* TrustKitDemoTests */ = { + isa = PBXGroup; + children = ( + 6B6B47731B1EECB4007757EE /* TrustKitDemoTests.m */, + 6B6B47711B1EECB4007757EE /* Supporting Files */, + ); + path = TrustKitDemoTests; + sourceTree = ""; + }; + 6B6B47711B1EECB4007757EE /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 6B6B47721B1EECB4007757EE /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 6B6B477E1B1F0BBD007757EE /* Products */ = { + isa = PBXGroup; + children = ( + 6B6B47831B1F0BBD007757EE /* TrustKit.framework */, + 8C8717281B23D12300267E1D /* libTrustKit_Static.a */, + 6B6B47851B1F0BBD007757EE /* TrustKitTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6B6B47531B1EECB3007757EE /* TrustKitDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6B6B47771B1EECB4007757EE /* Build configuration list for PBXNativeTarget "TrustKitDemo" */; + buildPhases = ( + 6B6B47501B1EECB3007757EE /* Sources */, + 6B6B47511B1EECB3007757EE /* Frameworks */, + 6B6B47521B1EECB3007757EE /* Resources */, + 8C87172D1B23D13D00267E1D /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 6B6B47871B1F0BF3007757EE /* PBXTargetDependency */, + 8C87172C1B23D13D00267E1D /* PBXTargetDependency */, + ); + name = TrustKitDemo; + productName = TrustKitDemo; + productReference = 6B6B47541B1EECB4007757EE /* TrustKitDemo.app */; + productType = "com.apple.product-type.application"; + }; + 6B6B476C1B1EECB4007757EE /* TrustKitDemoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6B6B477A1B1EECB4007757EE /* Build configuration list for PBXNativeTarget "TrustKitDemoTests" */; + buildPhases = ( + 6B6B47691B1EECB4007757EE /* Sources */, + 6B6B476A1B1EECB4007757EE /* Frameworks */, + 6B6B476B1B1EECB4007757EE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 6B6B476F1B1EECB4007757EE /* PBXTargetDependency */, + ); + name = TrustKitDemoTests; + productName = TrustKitDemoTests; + productReference = 6B6B476D1B1EECB4007757EE /* TrustKitDemoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6B6B474C1B1EECB3007757EE /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0630; + ORGANIZATIONNAME = "Data Theorem"; + TargetAttributes = { + 6B6B47531B1EECB3007757EE = { + CreatedOnToolsVersion = 6.3.1; + }; + 6B6B476C1B1EECB4007757EE = { + CreatedOnToolsVersion = 6.3.1; + TestTargetID = 6B6B47531B1EECB3007757EE; + }; + }; + }; + buildConfigurationList = 6B6B474F1B1EECB3007757EE /* Build configuration list for PBXProject "TrustKitDemo" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6B6B474B1B1EECB3007757EE; + productRefGroup = 6B6B47551B1EECB4007757EE /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 6B6B477E1B1F0BBD007757EE /* Products */; + ProjectRef = 6B6B477D1B1F0BBD007757EE /* TrustKit.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 6B6B47531B1EECB3007757EE /* TrustKitDemo */, + 6B6B476C1B1EECB4007757EE /* TrustKitDemoTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 6B6B47831B1F0BBD007757EE /* TrustKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = TrustKit.framework; + remoteRef = 6B6B47821B1F0BBD007757EE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 6B6B47851B1F0BBD007757EE /* TrustKitTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = TrustKitTests.xctest; + remoteRef = 6B6B47841B1F0BBD007757EE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8C8717281B23D12300267E1D /* libTrustKit_Static.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libTrustKit_Static.a; + remoteRef = 8C8717271B23D12300267E1D /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 6B6B47521B1EECB3007757EE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6B6B47631B1EECB4007757EE /* Main.storyboard in Resources */, + 6B6B47681B1EECB4007757EE /* LaunchScreen.xib in Resources */, + 6B6B47651B1EECB4007757EE /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6B6B476B1B1EECB4007757EE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6B6B47501B1EECB3007757EE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6B6B47601B1EECB4007757EE /* ViewController.m in Sources */, + 6B6B475D1B1EECB4007757EE /* AppDelegate.m in Sources */, + 6B6B475A1B1EECB4007757EE /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6B6B47691B1EECB4007757EE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6B6B47741B1EECB4007757EE /* TrustKitDemoTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 6B6B476F1B1EECB4007757EE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6B6B47531B1EECB3007757EE /* TrustKitDemo */; + targetProxy = 6B6B476E1B1EECB4007757EE /* PBXContainerItemProxy */; + }; + 6B6B47871B1F0BF3007757EE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = TrustKit; + targetProxy = 6B6B47861B1F0BF3007757EE /* PBXContainerItemProxy */; + }; + 8C87172C1B23D13D00267E1D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = TrustKit; + targetProxy = 8C87172B1B23D13D00267E1D /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 6B6B47611B1EECB4007757EE /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 6B6B47621B1EECB4007757EE /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 6B6B47661B1EECB4007757EE /* LaunchScreen.xib */ = { + isa = PBXVariantGroup; + children = ( + 6B6B47671B1EECB4007757EE /* Base */, + ); + name = LaunchScreen.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 6B6B47751B1EECB4007757EE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "../TrustKit/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 6B6B47761B1EECB4007757EE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "../TrustKit/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 6B6B47781B1EECB4007757EE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = TrustKitDemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 6B6B47791B1EECB4007757EE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = TrustKitDemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 6B6B477B1B1EECB4007757EE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = TrustKitDemoTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TrustKitDemo.app/TrustKitDemo"; + }; + name = Debug; + }; + 6B6B477C1B1EECB4007757EE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = TrustKitDemoTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TrustKitDemo.app/TrustKitDemo"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6B6B474F1B1EECB3007757EE /* Build configuration list for PBXProject "TrustKitDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6B6B47751B1EECB4007757EE /* Debug */, + 6B6B47761B1EECB4007757EE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6B6B47771B1EECB4007757EE /* Build configuration list for PBXNativeTarget "TrustKitDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6B6B47781B1EECB4007757EE /* Debug */, + 6B6B47791B1EECB4007757EE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6B6B477A1B1EECB4007757EE /* Build configuration list for PBXNativeTarget "TrustKitDemoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6B6B477B1B1EECB4007757EE /* Debug */, + 6B6B477C1B1EECB4007757EE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6B6B474C1B1EECB3007757EE /* Project object */; +} diff --git a/TrustKitDemo/TrustKitDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TrustKitDemo/TrustKitDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..e094f0ce --- /dev/null +++ b/TrustKitDemo/TrustKitDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TrustKitDemo/TrustKitDemo/AppDelegate.h b/TrustKitDemo/TrustKitDemo/AppDelegate.h new file mode 100644 index 00000000..a6b7acbd --- /dev/null +++ b/TrustKitDemo/TrustKitDemo/AppDelegate.h @@ -0,0 +1,20 @@ +/* + + AppDelegate.h + TrustKitDemo + + 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 + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + + +@end + diff --git a/TrustKitDemo/TrustKitDemo/AppDelegate.m b/TrustKitDemo/TrustKitDemo/AppDelegate.m new file mode 100644 index 00000000..8f6a2985 --- /dev/null +++ b/TrustKitDemo/TrustKitDemo/AppDelegate.m @@ -0,0 +1,64 @@ +/* + + AppDelegate.m + TrustKitDemo + + 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 "AppDelegate.h" +#import "TrustKit.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + + // Initialize TrustKit here so that from this point on, pin check is turned on + NSDictionary *trustKitConfig = + @{ + @"www.datatheorem.com" : @{ + kTSKEnforcePinning:@YES, + kTSKIncludeSubdomains:@YES, + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + kTSKPublicKeyHashes : @[@"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTX=" + ], //wrong CA public key for datatheorem to demonstrate the case of fail pinning + kTSKReportUris: @[@"http://127.0.0.1:8080/log_csp_report"] //failure report to be sent here + }}; + + [TrustKit initializeWithConfiguration:trustKitConfig]; + + 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/Base.lproj/LaunchScreen.xib b/TrustKitDemo/TrustKitDemo/Base.lproj/LaunchScreen.xib new file mode 100644 index 00000000..724f670e --- /dev/null +++ b/TrustKitDemo/TrustKitDemo/Base.lproj/LaunchScreen.xib @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard b/TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard new file mode 100644 index 00000000..e9819efe --- /dev/null +++ b/TrustKitDemo/TrustKitDemo/Base.lproj/Main.storyboard @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TrustKitDemo/TrustKitDemo/Images.xcassets/AppIcon.appiconset/Contents.json b/TrustKitDemo/TrustKitDemo/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..118c98f7 --- /dev/null +++ b/TrustKitDemo/TrustKitDemo/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/TrustKitDemo/TrustKitDemo/Info.plist b/TrustKitDemo/TrustKitDemo/Info.plist new file mode 100644 index 00000000..5c8fced7 --- /dev/null +++ b/TrustKitDemo/TrustKitDemo/Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.datatheorem.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + Main + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/TrustKitDemo/TrustKitDemo/ViewController.h b/TrustKitDemo/TrustKitDemo/ViewController.h new file mode 100644 index 00000000..f7d18de3 --- /dev/null +++ b/TrustKitDemo/TrustKitDemo/ViewController.h @@ -0,0 +1,18 @@ +/* + + ViewController.h + TrustKitDemo + + 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 + +@interface ViewController : UIViewController + + +@end + diff --git a/TrustKitDemo/TrustKitDemo/ViewController.m b/TrustKitDemo/TrustKitDemo/ViewController.m new file mode 100644 index 00000000..88f4c38d --- /dev/null +++ b/TrustKitDemo/TrustKitDemo/ViewController.m @@ -0,0 +1,60 @@ +/* + + ViewController.m + TrustKitDemo + + 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 "ViewController.h" + +@interface ViewController () + +@property (weak, nonatomic) IBOutlet UITextField *connectionTextfield; +@property (weak, nonatomic) IBOutlet UIWebView *destinationWebView; + + +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + self.destinationWebView.delegate = self; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +// connect to a website +- (IBAction)connectButton:(UIButton *)sender { + if (self.connectionTextfield.hasText) { + NSLog(@"connection field: %@", self.connectionTextfield.text); + NSString *urlString = self.connectionTextfield.text; + NSURL *url = [NSURL URLWithString:urlString]; + NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; + [self.destinationWebView loadRequest:urlRequest]; + } else { + NSLog(@"connection field is empty"); + + } +} + +// show user an error dialog when webview cannot load +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { + NSLog(@"%s webview fail load error=%@", __FUNCTION__, error); + UIAlertView *infoMessage; + infoMessage = [[UIAlertView alloc] + initWithTitle:@"webview load failed" message:[error localizedDescription] + delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:nil]; + infoMessage.alertViewStyle = UIAlertViewStyleDefault; + [infoMessage show]; +} + + +@end diff --git a/TrustKitDemo/TrustKitDemo/main.m b/TrustKitDemo/TrustKitDemo/main.m new file mode 100644 index 00000000..488711d1 --- /dev/null +++ b/TrustKitDemo/TrustKitDemo/main.m @@ -0,0 +1,26 @@ +/* + + main.m + TrustKitDemo + + 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. + + */ + +/* + This is a demo app whereby we demonstrate how to configure the TrustKit with a pin to + www.datatheorem.com, but with the wrong pin hash value, so that we intentionally would fail https connection + to www.datatheorem.com if the user were to input "https://www.datatheorem.com" in the textfield + Connections to all other websites should succeed. + */ + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/TrustKitDemo/TrustKitDemoTests/Info.plist b/TrustKitDemo/TrustKitDemoTests/Info.plist new file mode 100644 index 00000000..eeb14bac --- /dev/null +++ b/TrustKitDemo/TrustKitDemoTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.datatheorem.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/TrustKitDemo/TrustKitDemoTests/TrustKitDemoTests.m b/TrustKitDemo/TrustKitDemoTests/TrustKitDemoTests.m new file mode 100644 index 00000000..54fecee0 --- /dev/null +++ b/TrustKitDemo/TrustKitDemoTests/TrustKitDemoTests.m @@ -0,0 +1,43 @@ +/* + + TrustKitDemoTests.m + TrustKitDemoTests + + 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 + +@interface TrustKitDemoTests : XCTestCase + +@end + +@implementation TrustKitDemoTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // This is an example of a functional test case. + XCTAssert(YES, @"Pass"); +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/TrustKitServer/README.md b/TrustKitServer/README.md deleted file mode 100644 index 73ef9dfd..00000000 --- a/TrustKitServer/README.md +++ /dev/null @@ -1,33 +0,0 @@ -TrustKitServer -============== - - -Why do you need a server? -------------------------- - -If you plan to enable the reporting functionality of TrustKit, then you would need to setup a server that can receive the reports. As the reports are sent via HTTPs in the form of a POST request with JSON data, you'll need to setup a server that can receive such requests and be able to parse the JSON data and preferably store them in a database for analysis later. The subject of a server setup and database setup is actually beyond the scope of TrustKit. However, we do present here a very simple sample web server setup based on express to help speed up the construction of a server for testing purposes. It basically just print out whatever JSON data it receives from the POST request. Real web server setup would need more planning and more detail configuration (so don’t use the following). - -Steps to create a server ------------------------- - -Assuming that you've already installed Express, do the following (if you don't have express, please go to http://expressjs.com and follow their instructions on how to get started and install express and express-generator) - - $ express myserver - $ cd myserver - $ npm install - $ npm i multer (if you get error about cannot find module 'multer') - copy the myapp.js from this git repository to your current dir - $ node myapp.js (that will start your server running on port 3000 of your localhost) - - -On the client side (open a new terminal), you can also use the following curl command to see if you can connect to your server properly before trying out a client app running TrustKit: - - $ curl -H "Content-Type: application/json" -X POST -d '{"username":"xyz","password":"abc"}' http://localhost:3000 - -On your server terminal, you should see the following after the client is connected to it successfully: - - $ node myapp.js - Example app listening at http://:::3000 - { username: 'xyz', password: 'abc' } - -Note that this server is setup to receive http requests rather than https. So, in production system, you'd probably need to change the myapp.js to respond to https requests or use proxies to proxy requests to your nodejs app after they've terminated the SSL endpoint. diff --git a/TrustKitServer/myapp.js b/TrustKitServer/myapp.js deleted file mode 100644 index 4c931838..00000000 --- a/TrustKitServer/myapp.js +++ /dev/null @@ -1,22 +0,0 @@ -var app = require('express')(); -var bodyParser = require('body-parser'); -var multer = require('multer'); - -app.use(bodyParser.json()); // for parsing application/json -app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded -app.use(multer()); //for parsing multipart/form-data - -app.post('/', function (req, res) { - console.log(req.body); - res.send('got a POST request'); -}) - -var server = app.listen(3000, function () { - - var host = server.address().address; - var port = server.address().port; - - console.log('Example app listening at http://%s:%s', host, port); - -}); - diff --git a/TrustKitTests/COMODOECCDomainValidationSecureServerCA2.der b/TrustKitTests/COMODOECCDomainValidationSecureServerCA2.der new file mode 100644 index 00000000..a11d5d5c Binary files /dev/null and b/TrustKitTests/COMODOECCDomainValidationSecureServerCA2.der differ diff --git a/TrustKitTests/ca-chain-plus-self.cert.der b/TrustKitTests/GoodIntermediateCA.der similarity index 100% rename from TrustKitTests/ca-chain-plus-self.cert.der rename to TrustKitTests/GoodIntermediateCA.der diff --git a/TrustKitTests/ca.cert.der b/TrustKitTests/GoodRootCA.der similarity index 100% rename from TrustKitTests/ca.cert.der rename to TrustKitTests/GoodRootCA.der diff --git a/TrustKitTests/TSKCertificateUtils.h b/TrustKitTests/TSKCertificateUtils.h new file mode 100644 index 00000000..a519c32c --- /dev/null +++ b/TrustKitTests/TSKCertificateUtils.h @@ -0,0 +1,23 @@ +/* + + TSKCertificateUtils.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 + +@interface TSKCertificateUtils : NSObject + ++ (SecCertificateRef)createCertificateFromDer:(NSString *)derCertiticatePath; + ++ (SecTrustRef)createTrustWithCertificates:(const void **)certArray + arrayLength:(NSInteger)certArrayLength + anchorCertificates:(const void **)anchorCertificates + arrayLength:(NSInteger)anchorArrayLength; + +@end diff --git a/TrustKitTests/TSKCertificateUtils.m b/TrustKitTests/TSKCertificateUtils.m new file mode 100644 index 00000000..6a4e70f4 --- /dev/null +++ b/TrustKitTests/TSKCertificateUtils.m @@ -0,0 +1,65 @@ +/* + + TSKCertificateUtils.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 "TSKCertificateUtils.h" + +@implementation TSKCertificateUtils + ++ (SecCertificateRef)createCertificateFromDer:(NSString *)derCertiticatePath +{ + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + CFDataRef certData = (__bridge_retained CFDataRef)[NSData dataWithContentsOfFile:[bundle pathForResource:derCertiticatePath ofType:@"der"]]; + if (!certData) + { + [NSException raise:@"Test error" format:@"Could not open certificate at path %@", derCertiticatePath]; + } + SecCertificateRef certificate = SecCertificateCreateWithData(kCFAllocatorDefault, certData); + CFRelease(certData); + return certificate; +} + + ++ (SecTrustRef)createTrustWithCertificates:(const void **)certArray + arrayLength:(NSInteger)certArrayLength + anchorCertificates:(const void **)anchorCertificates + arrayLength:(NSInteger)anchorArrayLength +{ + CFArrayRef certificateChain = CFArrayCreate(NULL, (const void **)certArray, certArrayLength, NULL); + SecTrustRef trust; + + SecPolicyRef policy = SecPolicyCreateSSL(true, NULL); + + if (SecTrustCreateWithCertificates(certificateChain, policy, &trust) != errSecSuccess) + { + CFRelease(certificateChain); + CFRelease(policy); + [NSException raise:@"Test error" format:@"SecTrustCreateWithCertificates did not return errSecSuccess"]; + } + CFRelease(policy); + + if (anchorCertificates) + { + CFArrayRef trustStore = CFArrayCreate(NULL, (const void **)anchorCertificates, anchorArrayLength, NULL); + + if (SecTrustSetAnchorCertificates(trust, trustStore) != errSecSuccess) + { + CFRelease(certificateChain); + CFRelease(trust); + [NSException raise:@"Test error" format:@"SecTrustCreateWithCertificates did not return errSecSuccess"]; + } + CFRelease(trustStore); + } + + CFRelease(certificateChain); + return trust; +} + +@end diff --git a/TrustKitTests/TSKPinConfigurationTests.m b/TrustKitTests/TSKPinConfigurationTests.m new file mode 100644 index 00000000..d321fc10 --- /dev/null +++ b/TrustKitTests/TSKPinConfigurationTests.m @@ -0,0 +1,106 @@ +/* + + TSKPinConfigurationTests.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 +#import "TrustKit+Private.h" +#import "ssl_pin_verifier.h" +#import "public_key_utils.h" + + + +@interface TSKPinConfigurationTests : XCTestCase +{ + +} +@end + +@implementation TSKPinConfigurationTests + +- (void)setUp +{ + [super setUp]; +} + +- (void)tearDown +{ + [super tearDown]; +} + + +- (void)testGetConfigurationPinningEnabled +{ + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY="]}}); + + NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.good.com", trustKitConfig); + XCTAssert([serverConfigKey isEqualToString:@"www.good.com"], @"Did not receive a configuration for a pinned domain"); +} + + +- (void)testGetConfigurationPinningDisabled +{ + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"good.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=" + ]}}); + + // Ensure www.datatheorem.com gets no configuration + NSString *serverConfigKey = getPinningConfigurationKeyForDomain(@"www.datatheorem.com", trustKitConfig); + XCTAssert(serverConfigKey == nil, @"Received a configuration a non-pinned domain"); +} + + +- (void)testIncludeSubdomainsEnabled +{ + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"good.com" : @{ + kTSKIncludeSubdomains : @YES, + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=" + ]}}); + + // 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"); +} + + +- (void)testIncludeSubdomainsDisabled +{ + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"good.com" : @{ + kTSKIncludeSubdomains : @NO, + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=" + ]}}); + + // 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"); +} + +- (void)testIncludeSubdomainsEnabledAndSpecificConfiguration +{ + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"good.com" : @{ + kTSKIncludeSubdomains : @YES, + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=" + ]}, + @"www.good.com": @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=" + ]}}); + + // 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"); +} + +@end \ No newline at end of file diff --git a/TrustKitTests/TSKPinValidationOfflineTests.m b/TrustKitTests/TSKPinValidationOfflineTests.m new file mode 100644 index 00000000..74de7a42 --- /dev/null +++ b/TrustKitTests/TSKPinValidationOfflineTests.m @@ -0,0 +1,260 @@ +/* + + TSKPinValidationOfflineTests.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 +#import "TrustKit+Private.h" +#import "ssl_pin_verifier.h" +#import "public_key_utils.h" +#import "TSKCertificateUtils.h" + + +@interface TSKPinValidationOfflineTests : XCTestCase +{ + +} +@end + +@implementation TSKPinValidationOfflineTests +{ + SecCertificateRef _rootCertificate; + SecCertificateRef _intermediateCertificate; + SecCertificateRef _selfSignedCertificate; + SecCertificateRef _leafCertificate; +} + + +- (void)setUp +{ + [super setUp]; + // Create our certificate objects + _rootCertificate = [TSKCertificateUtils createCertificateFromDer:@"GoodRootCA"]; + _intermediateCertificate = [TSKCertificateUtils createCertificateFromDer:@"GoodIntermediateCA"]; + _leafCertificate = [TSKCertificateUtils createCertificateFromDer:@"www.good.com"]; + _selfSignedCertificate = [TSKCertificateUtils createCertificateFromDer:@"www.good.com.selfsigned"]; +} + + +- (void)tearDown +{ + CFRelease(_rootCertificate); + CFRelease(_intermediateCertificate); + CFRelease(_leafCertificate); + [super tearDown]; +} + + +// Pin to any of CA, Intermediate CA and Leaf certificates public keys (all valid) and ensure it succeeds +- (void)testVerifyAgainstAnyPublicKey +{ + // Create a valid server trust + SecCertificateRef certChainArray[2] = {_leafCertificate, _intermediateCertificate}; + SecCertificateRef trustStoreArray[1] = {_rootCertificate}; + SecTrustRef trust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) + anchorCertificates:(const void **)trustStoreArray + arrayLength:sizeof(trustStoreArray)/sizeof(trustStoreArray[0])]; + + // Create a configuration and parse it so we get the right format + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", // Server key + @"khKI6ae4micEvX74MB/BZ4u15WCWGXPD6Gjg6iIRVeE=", // Intermediate key + @"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=" // CA key + ]}}); + + + TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig[@"www.good.com"][kTSKPublicKeyAlgorithms], trustKitConfig[@"www.good.com"][kTSKPublicKeyHashes]); + CFRelease(trust); + + XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); +} + + +// Pin only to the Intermediate CA certificate public key and ensure it succeeds +- (void)testVerifyAgainstIntermediateCAPublicKey +{ + // Create a valid server trust + SecCertificateRef certChainArray[2] = {_leafCertificate, _intermediateCertificate}; + SecCertificateRef trustStoreArray[1] = {_rootCertificate}; + SecTrustRef trust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) + anchorCertificates:(const void **)trustStoreArray + arrayLength:sizeof(trustStoreArray)/sizeof(trustStoreArray[0])]; + + // Create a configuration and parse it so we get the right format + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"khKI6ae4micEvX74MB/BZ4u15WCWGXPD6Gjg6iIRVeE=" // Intermediate key only + ]}}); + + + TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig[@"www.good.com"][kTSKPublicKeyAlgorithms], trustKitConfig[@"www.good.com"][kTSKPublicKeyHashes]); + CFRelease(trust); + + XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); +} + + +// Pin only to the CA certificate public key and ensure it succeeds +- (void)testVerifyAgainstCAPublicKey +{ + // Create a valid server trust + SecCertificateRef certChainArray[2] = {_leafCertificate, _intermediateCertificate}; + SecCertificateRef trustStoreArray[1] = {_rootCertificate}; + SecTrustRef trust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) + anchorCertificates:(const void **)trustStoreArray + arrayLength:sizeof(trustStoreArray)/sizeof(trustStoreArray[0])]; + + // Create a configuration and parse it so we get the right format + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=" // CA key only + ]}}); + + TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig[@"www.good.com"][kTSKPublicKeyAlgorithms], trustKitConfig[@"www.good.com"][kTSKPublicKeyHashes]); + CFRelease(trust); + + XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); +} + + +// Pin only to the leaf certificate public key and ensure it succeeds +- (void)testVerifyAgainstLeafPublicKey +{ + // Create a valid server trust + SecCertificateRef certChainArray[2] = {_leafCertificate, _intermediateCertificate}; + SecCertificateRef trustStoreArray[1] = {_rootCertificate}; + SecTrustRef trust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) + anchorCertificates:(const void **)trustStoreArray + arrayLength:sizeof(trustStoreArray)/sizeof(trustStoreArray[0])]; + + // Create a configuration and parse it so we get the right format + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=" // Leaf key only + ]}}); + + TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig[@"www.good.com"][kTSKPublicKeyAlgorithms], trustKitConfig[@"www.good.com"][kTSKPublicKeyHashes]); + CFRelease(trust); + + XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); +} + + +// Pin a bad key and ensure validation fails +- (void)testVerifyAgainstBadPublicKey +{ + // Create a valid server trust + SecCertificateRef certChainArray[2] = {_leafCertificate, _intermediateCertificate}; + SecCertificateRef trustStoreArray[1] = {_rootCertificate}; + SecTrustRef trust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) + anchorCertificates:(const void **)trustStoreArray + arrayLength:sizeof(trustStoreArray)/sizeof(trustStoreArray[0])]; + + + // Create a configuration and parse it so we get the right format + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Bad key + ]}}); + + TSKPinValidationResult verificationResult = TSKPinValidationResultSuccess; + verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig[@"www.good.com"][kTSKPublicKeyAlgorithms], trustKitConfig[@"www.good.com"][kTSKPublicKeyHashes]); + CFRelease(trust); + + XCTAssert(verificationResult == TSKPinValidationResultFailed, @"Validation must NOT pass against invalid public key pins"); +} + + +// Pin a bad key and a good key and ensure validation succeeds +- (void)testVerifyAgainstLeafPublicKeyAndBadPublicKey +{ + // Create a valid server trust + SecCertificateRef certChainArray[2] = {_leafCertificate, _intermediateCertificate}; + SecCertificateRef trustStoreArray[1] = {_rootCertificate}; + SecTrustRef trust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) + anchorCertificates:(const void **)trustStoreArray + arrayLength:sizeof(trustStoreArray)/sizeof(trustStoreArray[0])]; + + // Create a configuration and parse it so we get the right format + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Bad key + @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=" // Leaf key + ]}}); + + TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig[@"www.good.com"][kTSKPublicKeyAlgorithms], trustKitConfig[@"www.good.com"][kTSKPublicKeyHashes]); + CFRelease(trust); + + XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against a good and an invalid public key pins"); +} + + +// Pin the valid CA key with an invalid certificate chain and ensure validation fails +- (void)testVerifyAgainstCaPublicKeyAndBadCertificateChain +{ + // The leaf certificate is self-signed + SecCertificateRef certChainArray[2] = {_selfSignedCertificate, _intermediateCertificate}; + SecCertificateRef trustStoreArray[1] = {_rootCertificate}; + SecTrustRef trust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) + anchorCertificates:(const void **)trustStoreArray + arrayLength:sizeof(trustStoreArray)/sizeof(trustStoreArray[0])]; + + // Create a configuration and parse it so we get the right format + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=" // CA key + ]}}); + + TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig[@"www.good.com"][kTSKPublicKeyAlgorithms], trustKitConfig[@"www.good.com"][kTSKPublicKeyHashes]); + CFRelease(trust); + + XCTAssert(verificationResult == TSKPinValidationResultFailedCertificateChainNotTrusted, @"Validation must fail against an invalid certificate chain"); +} + + +// Pin the valid CA key with an valid certificate chain but a wrong hostname and ensure validation fails +- (void)testVerifyAgainstCaPublicKeyAndBadHostname +{ + // The certificate chain is valid for www.good.com but we are connecting to www.bad.com + SecCertificateRef certChainArray[2] = {_selfSignedCertificate, _intermediateCertificate}; + SecCertificateRef trustStoreArray[1] = {_rootCertificate}; + SecTrustRef trust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) + anchorCertificates:(const void **)trustStoreArray + arrayLength:sizeof(trustStoreArray)/sizeof(trustStoreArray[0])]; + + // Create a configuration and parse it so we get the right format + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.bad.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=" // CA key + ]}}); + + TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + verificationResult = verifyPublicKeyPin(trust, @"www.bad.com", trustKitConfig[@"www.bad.com"][kTSKPublicKeyAlgorithms], trustKitConfig[@"www.bad.com"][kTSKPublicKeyHashes]); + CFRelease(trust); + + XCTAssert(verificationResult == TSKPinValidationResultFailedCertificateChainNotTrusted, @"Validation must fail against an invalid hostname"); +} + + +@end diff --git a/TrustKitTests/TrustKitPinValidationOnlineTests.m b/TrustKitTests/TSKPinValidationOnlineTests.m similarity index 88% rename from TrustKitTests/TrustKitPinValidationOnlineTests.m rename to TrustKitTests/TSKPinValidationOnlineTests.m index 99079841..03422ede 100644 --- a/TrustKitTests/TrustKitPinValidationOnlineTests.m +++ b/TrustKitTests/TSKPinValidationOnlineTests.m @@ -1,21 +1,24 @@ -// -// TrustKitPinValidationOnlineTests.m -// TrustKit -// -// Created by Eric on 05/03/15. -// Copyright (c) 2015 Data Theorem. All rights reserved. -// +/* + + TSKPinValidationOnlineTests.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 #import "TrustKit+Private.h" #import "public_key_utils.h" -@interface TrustKitPinValidationOnlineTests : XCTestCase +@interface TSKPinValidationOnlineTests : XCTestCase @end -@implementation TrustKitPinValidationOnlineTests +@implementation TSKPinValidationOnlineTests /* WARNING: For the online tests, we need to use a different domain for every test otherwise the tests will be disrupted by SSL session resumption. Specifically, connecting to the same host more than once will potentially allow the first session to be resumed, thereby skipping all SSL validation including TrustKit's. This is not a security issue but will make the tests report unexpected results. */ @@ -38,11 +41,11 @@ - (void)tearDown { - (void)testConnectionValidatingCAPublicKey { NSDictionary *trustKitConfig = - @{ - @"www.datatheorem.com" : @{ - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], - kTSKPublicKeyHashes : @[@"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=" // CA key - ]}}; + @{ + @"www.datatheorem.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + kTSKPublicKeyHashes : @[@"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=" // CA key + ]}}; [TrustKit initializeWithConfiguration:trustKitConfig]; @@ -57,7 +60,6 @@ - (void)testConnectionValidatingCAPublicKey } - // Tests a secure connection to https://www.yahoo.com and forces validation to fail by providing a fake hash - (void)testConnectionUsingFakeHashInvalidatingAllCertificates { diff --git a/TrustKitTests/TSKPublicKeyAlgorithmTests.m b/TrustKitTests/TSKPublicKeyAlgorithmTests.m new file mode 100644 index 00000000..bcdef60b --- /dev/null +++ b/TrustKitTests/TSKPublicKeyAlgorithmTests.m @@ -0,0 +1,125 @@ +/* + + TSKPublicKeyAlgorithmTests.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 +#import "TrustKit+Private.h" +#import "ssl_pin_verifier.h" +#import "public_key_utils.h" +#import "TSKCertificateUtils.h" + + +@interface TSKPublicKeyAlgorithmTests : XCTestCase +{ + +} +@end + +@implementation TSKPublicKeyAlgorithmTests + +- (void)setUp +{ + [super setUp]; +} + +- (void)tearDown +{ + [super tearDown]; +} + + +- (void)testVerifyRsa2048 +{ + // Create a valid server trust + SecCertificateRef leafCertificate = [TSKCertificateUtils createCertificateFromDer:@"www.datatheorem.com"]; + SecCertificateRef intermediateCertificate = [TSKCertificateUtils createCertificateFromDer:@"ThawteSSLCA"]; + SecCertificateRef certChainArray[2] = {leafCertificate, intermediateCertificate}; + + SecTrustRef trust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) + anchorCertificates:NULL + arrayLength:0]; + + // Create a configuration and parse it so we get the right format + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.datatheorem.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + kTSKPublicKeyHashes : @[@"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8=", // Leaf key + ]}}); + + TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + verificationResult = verifyPublicKeyPin(trust, @"www.datatheorem.com", trustKitConfig[@"www.datatheorem.com"][kTSKPublicKeyAlgorithms], trustKitConfig[@"www.datatheorem.com"][kTSKPublicKeyHashes]); + CFRelease(trust); + CFRelease(leafCertificate); + CFRelease(intermediateCertificate); + + XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins for RSA 2048"); +} + + +- (void)testVerifyRsa4096 +{ + // Create a valid server trust + 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}; + SecTrustRef trust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) + anchorCertificates:(const void **)trustStoreArray + arrayLength:sizeof(trustStoreArray)/sizeof(trustStoreArray[0])]; + + // Create a configuration and parse it so we get the right format + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", // Server key + ]}}); + + TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig[@"www.good.com"][kTSKPublicKeyAlgorithms], trustKitConfig[@"www.good.com"][kTSKPublicKeyHashes]); + CFRelease(trust); + CFRelease(leafCertificate); + CFRelease(intermediateCertificate); + CFRelease(rootCertificate); + + XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins for RSA 4096"); +} + + +- (void)testVerifyEcDsaSecp256r1 +{ + // Create a valid server trust + SecCertificateRef leafCertificate = [TSKCertificateUtils createCertificateFromDer:@"sni41871.cloudflaressl.com"]; + SecCertificateRef intermediateCertificate = [TSKCertificateUtils createCertificateFromDer:@"COMODOECCDomainValidationSecureServerCA2"]; + SecCertificateRef certChainArray[2] = {leafCertificate, intermediateCertificate}; + + SecTrustRef trust = [TSKCertificateUtils createTrustWithCertificates:(const void **)certChainArray + arrayLength:sizeof(certChainArray)/sizeof(certChainArray[0]) + anchorCertificates:NULL + arrayLength:0]; + + // Create a configuration and parse it so we get the right format + NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"istlsfastyet.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmEcDsaSecp256r1], + kTSKPublicKeyHashes : @[@"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=", // Server key + ]}}); + + TSKPinValidationResult verificationResult = TSKPinValidationResultFailed; + verificationResult = verifyPublicKeyPin(trust, @"istlsfastyet.com", trustKitConfig[@"istlsfastyet.com"][kTSKPublicKeyAlgorithms], trustKitConfig[@"istlsfastyet.com"][kTSKPublicKeyHashes]); + CFRelease(trust); + CFRelease(leafCertificate); + CFRelease(intermediateCertificate); + + XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins for ECDSA secp256r1"); +} + + +@end diff --git a/TrustKitTests/TSKReporterTests.m b/TrustKitTests/TSKReporterTests.m new file mode 100644 index 00000000..56ab56d7 --- /dev/null +++ b/TrustKitTests/TSKReporterTests.m @@ -0,0 +1,104 @@ +/* + + TSKReporterTests.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 +#import "TSKSimpleReporter.h" +#import "TSKSimpleBackgroundReporter.h" +#import "TSKCertificateUtils.h" + + +@interface TSKReporterTests : XCTestCase + +@end + +@implementation TSKReporterTests +{ + SecTrustRef _testTrust; + SecCertificateRef _rootCertificate; + SecCertificateRef _intermediateCertificate; + SecCertificateRef _leafCertificate; +} + + +- (void)setUp { + [super setUp]; + + _rootCertificate = [TSKCertificateUtils createCertificateFromDer:@"GoodRootCA"]; + _intermediateCertificate = [TSKCertificateUtils createCertificateFromDer:@"GoodIntermediateCA"]; + _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])]; +} + +- (void)tearDown +{ + CFRelease(_rootCertificate); + CFRelease(_intermediateCertificate); + CFRelease(_leafCertificate); + CFRelease(_testTrust); + + [super tearDown]; +} + +- (void)testSimpleReporter { + + //just try a simple valid case to see if we can post this to the server + TSKSimpleReporter *reporter = [[TSKSimpleReporter alloc] initWithAppBundleId:@"com.example.ABC" appVersion:@"1.0"]; + + [reporter pinValidationFailedForHostname:@"mail.example.com" + port:[NSNumber numberWithInt:443] + trust:_testTrust + notedHostname:@"example.com" + reportURIs:@[[NSURL URLWithString:@"http://127.0.0.1:8080/log_csp_report"]] + includeSubdomains:YES + knownPins:@[[[NSData alloc]initWithBase64EncodedString:@"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=" options:0], + [[NSData alloc]initWithBase64EncodedString:@"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" options:0], + ]]; + + + [NSThread sleepForTimeInterval:5.0]; + XCTAssert(YES, @"Pass"); +} + +- (void)testSimpleBackgroundReporter { + + //just try a simple valid case to see if we can post this to the server + TSKSimpleBackgroundReporter *reporter = [[TSKSimpleBackgroundReporter alloc] initWithAppBundleId:@"com.example.ABC" appVersion:@"1.0"]; + + [reporter pinValidationFailedForHostname:@"mail.example.com" + port:[NSNumber numberWithInt:443] + trust:_testTrust + notedHostname:@"example.com" + reportURIs:@[[NSURL URLWithString:@"http://127.0.0.1:8080/log_csp_report"]] + includeSubdomains:YES + knownPins:@[[[NSData alloc]initWithBase64EncodedString:@"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=" options:0], + [[NSData alloc]initWithBase64EncodedString:@"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" options:0], + ]]; + + [NSThread sleepForTimeInterval:5.0]; + XCTAssert(YES, @"Pass"); +} + + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/TrustKitTests/ThawteSSLCA.der b/TrustKitTests/ThawteSSLCA.der new file mode 100644 index 00000000..0fae5d09 Binary files /dev/null and b/TrustKitTests/ThawteSSLCA.der differ diff --git a/TrustKitTests/TrustKitPinValidationOfflineTests.m b/TrustKitTests/TrustKitPinValidationOfflineTests.m deleted file mode 100644 index 267686f0..00000000 --- a/TrustKitTests/TrustKitPinValidationOfflineTests.m +++ /dev/null @@ -1,372 +0,0 @@ -// -// TrustKitPinValidationOfflineTests.m -// TrustKit -// -// Created by Eric on 30/03/15. -// Copyright (c) 2015 Data Theorem. All rights reserved. -// - -#import -#import "TrustKit+Private.h" -#import "ssl_pin_verifier.h" -#import "public_key_utils.h" - -#define HEXDUMP_COLS 16 - -@interface TrustKitPinValidationOnlineTests : XCTestCase -@end - - -@interface TrustKitPinValidationOfflineTests : XCTestCase -{ - -} -@end - -@implementation TrustKitPinValidationOfflineTests { - SecCertificateRef _rootCertificate; - SecCertificateRef _chainCertificate; - SecCertificateRef _chainPlusSelfCertificate; - SecCertificateRef _selfCertificate; - SecCertificateRef _leafCertificate; - SecPolicyRef _policy; -} - -- (void)setUp { - [super setUp]; - - CFDataRef rootData = (__bridge_retained CFDataRef)[NSData dataWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:@"ca.cert" ofType:@"der"]]; - _rootCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, rootData); - CFRelease(rootData); - - CFDataRef chainData = (__bridge_retained CFDataRef)[NSData dataWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:@"ca-chain.cert" ofType:@"der"]]; - _chainCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, chainData); - CFRelease(chainData); - - CFDataRef chainPlusSelfData = (__bridge_retained CFDataRef)[NSData dataWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:@"ca-chain-plus-self.cert" ofType:@"der"]]; - _chainPlusSelfCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, chainPlusSelfData); - CFRelease(chainPlusSelfData); - - CFDataRef selfData = (__bridge_retained CFDataRef)[NSData dataWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:@"self.cert" ofType:@"der"]]; - _selfCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, selfData); - CFRelease(selfData); - - CFDataRef leafData = (__bridge_retained CFDataRef)[NSData dataWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:@"www.good.com.cert" ofType:@"der"]]; - _leafCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, leafData); - CFRelease(leafData); - - CFStringRef hostname = CFSTR("www.good.com"); - _policy = SecPolicyCreateSSL(true, hostname); - -} - -- (void)tearDown { - - CFRelease(_rootCertificate); - CFRelease(_chainCertificate); - CFRelease(_leafCertificate); - CFRelease(_policy); - - [super tearDown]; -} - - -// Pin to any of CA, Intermediate CA and Leaf certificates public keys (all valid) and ensure it succeeds -- (void)testWwwGoodComCertificateAgainstAnyPublicKey -{ - NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ - kTSKIncludeSubdomains : @NO, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], - kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", // Server key - @"khKI6ae4micEvX74MB/BZ4u15WCWGXPD6Gjg6iIRVeE=", // Intermediate key - @"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=" // CA key - ]}}); - - SecCertificateRef trustCertArray[2] = {_leafCertificate, _chainCertificate}; - SecCertificateRef caRootArray[1] = {_rootCertificate}; - - SecTrustRef trust = [self _createTrustWithCertificates:(const void **)trustCertArray arrayLength:sizeof(trustCertArray)/sizeof(trustCertArray[0]) - anchorCertificates:(const void **)caRootArray arrayLength:sizeof(caRootArray)/sizeof(caRootArray[0])]; - - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig); - - CFRelease(trust); - - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); -} - - -// Pin only to the Intermediate CA certificate public key and ensure it succeeds -- (void)testWwwGoodComCertificateAgainstGoodIntermediateCAPublicKey -{ - NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ - kTSKIncludeSubdomains : @NO, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], - kTSKPublicKeyHashes : @[@"khKI6ae4micEvX74MB/BZ4u15WCWGXPD6Gjg6iIRVeE=" // Intermediate key only - ]}}); - - SecCertificateRef trustCertArray[2] = {_leafCertificate, _chainCertificate}; - SecCertificateRef caRootArray[1] = {_rootCertificate}; - - SecTrustRef trust = [self _createTrustWithCertificates:(const void **)trustCertArray arrayLength:sizeof(trustCertArray)/sizeof(trustCertArray[0]) - anchorCertificates:(const void **)caRootArray arrayLength:sizeof(caRootArray)/sizeof(caRootArray[0])]; - - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig); - - CFRelease(trust); - - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); -} - - -// Pin only to the CA certificate public key and ensure it succeeds -- (void)testWwwGoodComCertificateAgainstGoodCAPublicKey -{ - NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ - kTSKIncludeSubdomains : @NO, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], - kTSKPublicKeyHashes : @[@"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=" // CA key - ]}}); - - SecCertificateRef trustCertArray[2] = {_leafCertificate, _chainCertificate}; - SecCertificateRef caRootArray[1] = {_rootCertificate}; - - SecTrustRef trust = [self _createTrustWithCertificates:(const void **)trustCertArray arrayLength:sizeof(trustCertArray)/sizeof(trustCertArray[0]) - anchorCertificates:(const void **)caRootArray arrayLength:sizeof(caRootArray)/sizeof(caRootArray[0])]; - - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig); - - CFRelease(trust); - - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); -} - - -// Pin only to the leaf certificate public key and ensure it succeeds -- (void)testWwwGoodComCertificateAgainstGoodLeafPublicKey -{ - NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ - kTSKIncludeSubdomains : @NO, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], - kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=" // Server key only - ]}}); - - SecCertificateRef trustCertArray[2] = {_leafCertificate, _chainCertificate}; - SecCertificateRef caRootArray[1] = {_rootCertificate}; - - SecTrustRef trust = [self _createTrustWithCertificates:(const void **)trustCertArray arrayLength:sizeof(trustCertArray)/sizeof(trustCertArray[0]) - anchorCertificates:(const void **)caRootArray arrayLength:sizeof(caRootArray)/sizeof(caRootArray[0])]; - - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig); - - CFRelease(trust); - - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); -} - -// Pin a bad key for www.good.com and ensure it fails - -- (void)testWwwGoodComCertificateAgainstBadKeyPinning -{ - NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ - kTSKIncludeSubdomains : @NO, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], - kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key - ]}}); - - SecCertificateRef trustCertArray[2] = {_leafCertificate, _chainCertificate}; - SecCertificateRef caRootArray[1] = {_rootCertificate}; - - SecTrustRef trust = [self _createTrustWithCertificates:(const void **)trustCertArray arrayLength:sizeof(trustCertArray)/sizeof(trustCertArray[0]) - anchorCertificates:(const void **)caRootArray arrayLength:sizeof(caRootArray)/sizeof(caRootArray[0])]; - - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig); - - CFRelease(trust); - - XCTAssert(verificationResult == TSKPinValidationResultFailed, @"Validation must NOT pass against invalid public key pins"); -} - - -// Validation to domain names with no pins must never fail -- (void)testWwwGoodComCertificateWithNoPins -{ - SecCertificateRef trustCertArray[2] = {_leafCertificate, _chainCertificate}; - SecCertificateRef caRootArray[1] = {_rootCertificate}; - - SecTrustRef trust = [self _createTrustWithCertificates:(const void **)trustCertArray arrayLength:sizeof(trustCertArray)/sizeof(trustCertArray[0]) - anchorCertificates:(const void **)caRootArray arrayLength:sizeof(caRootArray)/sizeof(caRootArray[0])]; - - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, @"www.good.com", nil); - - CFRelease(trust); - - XCTAssert(verificationResult == TSKPinValidationResultDomainNotPinned, @"Validation must pass if no public key pins are set."); -} - - -// Pin a valid key for www.good.com and ensure it succeeds both with a trusted CA and a Self-Signed cert. -- (void)testWwwGoodComCertificateAgainstCAWithSelfSignedCAAsWell -{ - NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"www.good.com" : @{ - kTSKIncludeSubdomains : @NO, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], - kTSKPublicKeyHashes : @[//@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", // Server key - //@"khKI6ae4micEvX74MB/BZ4u15WCWGXPD6Gjg6iIRVeE=", // Intermediate key - @"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0="//, // CA key - //@"naw8JswG9YvBkitP4iGuyEgbFxssEMM/v4m7MglIzEw=" // Self-signed Key - ]}}); - - SecCertificateRef trustCertArray[2] = {_leafCertificate, _chainCertificate}; - SecCertificateRef caRootArray[2] = {_rootCertificate, _selfCertificate }; - - SecTrustRef trust = [self _createTrustWithCertificates:(const void **)trustCertArray arrayLength:sizeof(trustCertArray)/sizeof(trustCertArray[0]) - anchorCertificates:(const void **)caRootArray arrayLength:sizeof(caRootArray)/sizeof(caRootArray[0])]; - - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig); - - CFRelease(trust); - - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); -} - - -// Pin a valid key for good.com with includeSubdomains and ensure the validation for www.good.com succeeds -- (void)testSubdomainWithGoodComCertificateAgainstGoodLeafPublicKey -{ - NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"good.com" : @{ - kTSKIncludeSubdomains : @YES, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], - kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=" // Server key - ]}}); - - SecCertificateRef trustCertArray[2] = {_leafCertificate, _chainCertificate}; - SecCertificateRef caRootArray[1] = {_rootCertificate}; - - SecTrustRef trust = [self _createTrustWithCertificates:(const void **)trustCertArray arrayLength:sizeof(trustCertArray)/sizeof(trustCertArray[0]) - anchorCertificates:(const void **)caRootArray arrayLength:sizeof(caRootArray)/sizeof(caRootArray[0])]; - - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig); - - CFRelease(trust); - - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid public key pins"); -} - - -// Pin a bad key for good.com with includeSubdomains and ensure the validation for www.good.com fails -- (void)testSubdomainWithGoodComCertificateAgainstBadKeyPinning -{ - NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"good.com" : @{ - kTSKIncludeSubdomains : @YES, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], - kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key - ]}}); - - SecCertificateRef trustCertArray[2] = {_leafCertificate, _chainCertificate}; - SecCertificateRef caRootArray[1] = {_rootCertificate}; - - SecTrustRef trust = [self _createTrustWithCertificates:(const void **)trustCertArray arrayLength:sizeof(trustCertArray)/sizeof(trustCertArray[0]) - anchorCertificates:(const void **)caRootArray arrayLength:sizeof(caRootArray)/sizeof(caRootArray[0])]; - - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig); - - CFRelease(trust); - - XCTAssert(verificationResult == TSKPinValidationResultFailed, @"Validation must NOT pass against invalid public key pins"); -} - - -// Pin a bad key for good.com with includeSubdomains and a good key for www.good.com and ensure the validation for www.good.com succeeds -// (ie. the more specific pin should take precedence over the general good.com pin) -- (void)testSubdomainWithGoodComCertificateAgainstGoodLeafPublicKeyAndBadKeyAsWell -{ - NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"good.com" : @{ - kTSKIncludeSubdomains : @YES, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], - kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", // Server key - @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key - ]}}); - - SecCertificateRef trustCertArray[2] = {_leafCertificate, _chainCertificate}; - SecCertificateRef caRootArray[1] = {_rootCertificate}; - - SecTrustRef trust = [self _createTrustWithCertificates:(const void **)trustCertArray arrayLength:sizeof(trustCertArray)/sizeof(trustCertArray[0]) - anchorCertificates:(const void **)caRootArray arrayLength:sizeof(caRootArray)/sizeof(caRootArray[0])]; - - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig); - - CFRelease(trust); - - XCTAssert(verificationResult == TSKPinValidationResultSuccess, @"Validation must pass against valid existing public key pins, regardless of an invalid key being present"); -} - - -// Pin a valid key for good.com with includeSubdomains set to NO, and ensure the validation for www.good.com fails -- (void)testSubdomainWithGoodComCertificateAgainstAnyPublicKeyNotIncludingSubdomains -{ - NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"good.com" : @{ - kTSKIncludeSubdomains : @NO, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], - kTSKPublicKeyHashes : @[@"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", // Server key - @"khKI6ae4micEvX74MB/BZ4u15WCWGXPD6Gjg6iIRVeE=", // Intermediate key - @"iQMk4onrJJz/nwW1wCUR0Ycsh3omhbM+PqMEwNof/K0=" // CA key - ]}}); - - SecCertificateRef trustCertArray[2] = {_leafCertificate, _chainCertificate}; - SecCertificateRef caRootArray[1] = {_rootCertificate}; - - SecTrustRef trust = [self _createTrustWithCertificates:(const void **)trustCertArray arrayLength:sizeof(trustCertArray)/sizeof(trustCertArray[0]) - anchorCertificates:(const void **)caRootArray arrayLength:sizeof(caRootArray)/sizeof(caRootArray[0])]; - - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig); - - CFRelease(trust); - - XCTAssert(verificationResult == TSKPinValidationResultDomainNotPinned, @"Validation must NOT pass because includeSubdomain is not enabled so wwww.good.com is not actually pinned"); -} - - -// Tricky case: pin a bad key for good.co.uk with includeSubdomains and ensure the validation for www.good.com succeeds. -// Basically we want to make sure that TrustKit doesn’t confused with weird top-level domains (like .co.uk). -- (void)testWwwGoodComCertificateAgainstDifferentTLDPublicKeyPinning -{ - NSDictionary *trustKitConfig = parseTrustKitArguments(@{@"good.co.uk" : @{ - kTSKIncludeSubdomains : @YES, - kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], - kTSKPublicKeyHashes : @[@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // Fake key - ]}}); - - SecCertificateRef trustCertArray[2] = {_leafCertificate, _chainCertificate}; - SecCertificateRef caRootArray[1] = {_rootCertificate}; - - SecTrustRef trust = [self _createTrustWithCertificates:(const void **)trustCertArray arrayLength:sizeof(trustCertArray)/sizeof(trustCertArray[0]) - anchorCertificates:(const void **)caRootArray arrayLength:sizeof(caRootArray)/sizeof(caRootArray[0])]; - - TSKPinValidationResult verificationResult = verifyPublicKeyPin(trust, @"www.good.com", trustKitConfig); - - CFRelease(trust); - - XCTAssert(verificationResult == TSKPinValidationResultDomainNotPinned, @"Validation must pass as www.good.com domain shouldn't be pinned, when config is for a different TLD (co.uk)"); -} - - -// Helper methods for cleaner testing code -- (SecTrustRef)_createTrustWithCertificates:(const void **)certArray arrayLength:(NSInteger)certArrayLength anchorCertificates:(const void **)anchorCertificates arrayLength:(NSInteger)anchorArrayLength -{ - CFArrayRef certs = CFArrayCreate(NULL, (const void **)certArray, certArrayLength, NULL); - SecTrustRef trust; - - XCTAssert(SecTrustCreateWithCertificates(certs, _policy, &trust) == errSecSuccess, @"SecTrustCreateWithCertificates did not return errSecSuccess"); - - CFArrayRef caRootCertificates = CFArrayCreate(NULL, (const void **)anchorCertificates, anchorArrayLength, NULL); - - XCTAssert(SecTrustSetAnchorCertificates(trust, caRootCertificates) == errSecSuccess, @"SecTrustSetAnchorCertificates did not return errSecSuccess"); - - CFRelease(caRootCertificates); - CFRelease(certs); - - return trust; -} - -@end diff --git a/TrustKitTests/ca-chain.cert.der b/TrustKitTests/ca-chain.cert.der deleted file mode 100644 index a4e3106d..00000000 Binary files a/TrustKitTests/ca-chain.cert.der and /dev/null differ diff --git a/TrustKitTests/intermediate.cert.der b/TrustKitTests/intermediate.cert.der deleted file mode 100644 index a4e3106d..00000000 Binary files a/TrustKitTests/intermediate.cert.der and /dev/null differ diff --git a/TrustKitTests/sni41871.cloudflaressl.com.der b/TrustKitTests/sni41871.cloudflaressl.com.der new file mode 100644 index 00000000..ebaf53be Binary files /dev/null and b/TrustKitTests/sni41871.cloudflaressl.com.der differ diff --git a/TrustKitTests/www.datatheorem.com.der b/TrustKitTests/www.datatheorem.com.der new file mode 100644 index 00000000..c1b8ec25 Binary files /dev/null and b/TrustKitTests/www.datatheorem.com.der differ diff --git a/TrustKitTests/www.good.com.cert.der b/TrustKitTests/www.good.com.der similarity index 100% rename from TrustKitTests/www.good.com.cert.der rename to TrustKitTests/www.good.com.der diff --git a/TrustKitTests/self.cert.der b/TrustKitTests/www.good.com.selfsigned.der similarity index 100% rename from TrustKitTests/self.cert.der rename to TrustKitTests/www.good.com.selfsigned.der diff --git a/docs/AppledocSettings.plist b/docs/AppledocSettings.plist new file mode 100755 index 00000000..d50390ed --- /dev/null +++ b/docs/AppledocSettings.plist @@ -0,0 +1,44 @@ + + + + + --repeat-first-par + + --company-id + com.datatheorem + --create-docset + + --create-html + + --ignore + + TrustKitTests + Dependencies + Reporting + public_key_utils.h + docs + + --index-desc + docs/api-doc-index.md + --install-docset + + --keep-undocumented-objects + + --logformat + xcode + --project-company + TrustKit + --project-name + TrustKit + --verbose + 2 + --warn-empty-description + + --warn-undocumented-member + + --warn-undocumented-object + + --warn-unknown-directive + + + diff --git a/docs/api-doc-index.md b/docs/api-doc-index.md new file mode 100755 index 00000000..f0bcf894 --- /dev/null +++ b/docs/api-doc-index.md @@ -0,0 +1,14 @@ +TrustKit is an open source framework that makes it easy to deploy SSL public key +pinning in any iOS or OS X 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 . + +TrustKit requires iOS 7+ or OS X 10.9+ as the minimum deployment target and +provides two classes: + +* TrustKit, for configuring the global SSL pinning policy within an App. +* TSKPinVerifier, for manually validating a certificate chain against the +configured pinning policy. This class only needs to be used for connections +that are not automatically intercepted by TrustKit. diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 00000000..92e6bf48 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,192 @@ +Getting Started +=============== + +Adding TrustKit to an App can be achieved through the following steps: + +1. Generating SSL pins for the App's server endpoints and choosing a pinning +policy. +2. Adding TrustKit as a dependency to the App's Xcode project. +3. Initializing TrustKit with the pinning policy. + + +Warning +------- + +Public key pinning can be dangerous and requires more than just code-level +changes in your App. If you make a mistake, you might cause your App to pin a +set of keys that validates today but which stops validating a week or a year +from now, if something changes. In that case, your App will no longer be able to +connect to its servers and will most likely stop working, until it gets updated +with a new set of pins. + +Unless you are confident that you understand the Web PKI that you can manage +the App servers' cryptographic identity very well, you should not use key +pinning. + + +Generating SSL Pins +------------------- + +Before deploying SSL pinning within your App, you first need to investigate and +choose which domains and public keys need to be pinned. This is **very +important** as enabling the wrong pinning policy may prevent your App from being +able to connect to its servers, when the servers' keys are rotated. + +The following blog post provides some information on which keys to pin and what +the trade-offs are: +[https://noncombatant.org/2015/05/01/about-http-public-key-pinning/](https://noncombatant.org/2015/05/01/about-http-public-key-pinning/). + +In the context of TrustKit, an SSL pin is the base64-encoded SHA-256 of a +certificate's Subject Public Key Info; this is the same as what is described in +the [HTTP Public Key Pinning +specification](https://developer.mozilla.org/en-US/docs/Web/Security/Public_Key_Pinning). + +To generate such values, three bash scripts are available. The first two scripts +can be used to generate the pin configuration from a PEM or DER certificate: + + $ ./get_pin_from_pem_certificate.sh ca.pem + $ ./get_pin_from_der_certificate.sh ca.der + +The second script can be used to generate the pin configuration for the highest +certificate within the certificate chain returned by a given server: + + $ ./get_pin_from_server.sh www.google.com + + +Deploying TrustKit +------------------ + +Enabling TrustKit within an App requires generating a pinning policy and then +initializing TrustKit with this policy. This can be done using one of the +following mechanisms: + +* By statically linking TrustKit and supplying the pinning policy +programmatically, using TrustKit's `initializeWithConfig:` method. +* By dynamically linking TrustKit and storing the pinning policy in the App's +Info.plist. This approach allows deploying TrustKit without modifying the App's +source code but is only available on iOS8+ and OS X. + +After initialization, TrustKit will intercept the App's outgoing SSL +connections, in order to perform additional validation against the server's +certificate chain based on the configured SSL pinning policy. + + +### Choosing a Pinning Policy + +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: + + NSDictionary *trustKitConfig; + trustKitConfig = @{ + @"www.datatheorem.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + kTSKPublicKeyHashes : @[ + @"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=", + @"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8=" + ] + }, + @"yahoo.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096], + kTSKPublicKeyHashes : @[ + @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=", + @"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=", + ] + } + }; + +To avoid locking out too many users from your App when deploying SSL pinning +for the first time, a more elaborate policy can be enabled using the following +configuration keys: + +* Setting `kTSKEnforcePinning` to `NO`, so that SSL connections will succeed +regardless of pin validation. +* Adding a report URL using the `kTSKReportUris` setting to receive pin +validation failure reports. + +This will allow the App to work regardless of pin validation failures, but the +reports will give you an idea of how many users would be blocked, if pin +validation was to be enforced. + +The list of all the configuration keys is available in the +[documentation](https://datatheorem.github.io/TrustKit/documentation/Classes/TrustKit.html). + + +### Static Linking + +For Apps targeting iOS 7, TrustKit must 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) + +3. Lastly, call the `initializeWithConfiguration:` method with your pinning +policy: + + #import "TrustKit.h" + + [...] + + NSDictionary *trustKitConfig = + @{ + @"www.datatheorem.com" : @{ + kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048], + kTSKPublicKeyHashes : @[@"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=", + @"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8=" + ]}}; + + [TrustKit initializeWithConfiguration:trustKitConfig]; + + +### CocoaPods + +TrustKit will be made available through CocoaPods when it is open-sourced +at Black Hat 2015 in August. Until then, TrustKit can still be added as a local +pod by adding the following dependency to your project's Podfile: + + pod 'TrustKit', :path => '/path/to/TrustKit' + + +### Dynamic Linking + +For Apps targeting iOS 8+ or OS X, TrustKit can be dynamically linked, which +allows deploying SSL pinning without having to modify the App's source code. + +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) + +3. Lastly, specify your App's pinning policy 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) + + +Manual Pin Validation +--------------------- + +In a few specific scenarios, TrustKit cannot intercept outgoing SSL connections +and automatically validate the server's identity against the pinning policy. +This includes for example connections initiated by external processes (such as +the `NSURLSession`'s background transfer service) or through third-party SSL +libraries (such as OpenSSL). + +For these connections, the pin validation must be +triggered manually; see the documentation for the [TSKPinVerifier +class](https://datatheorem.github.io/TrustKit/documentation/Classes/TSKPinVerifier.html) +for more details. diff --git a/get_pin_from_der_certificate.sh b/get_pin_from_der_certificate.sh index db4ce214..e61a0cae 100755 --- a/get_pin_from_der_certificate.sh +++ b/get_pin_from_der_certificate.sh @@ -19,7 +19,7 @@ echo '--------------------------' # Generate the Subject Public Key Info hash pin=$(openssl x509 -pubkey -noout -inform DER -in $CERT | openssl rsa -outform DER -pubin -in /dev/stdin 2>/dev/null | openssl dgst -sha256 -binary | openssl enc -base64) -echo "kTSKPublicKeyHashes: @[\"$pin\"]" +echo "kTSKPublicKeyHashes: @[@\"$pin\"]" # Generate the public key algorithm publickey=$(openssl x509 -in $CERT -inform DER -text -noout | grep 'Public Key') diff --git a/get_pin_from_pem_certificate.sh b/get_pin_from_pem_certificate.sh index 5c24ef23..f24866a6 100755 --- a/get_pin_from_pem_certificate.sh +++ b/get_pin_from_pem_certificate.sh @@ -20,7 +20,7 @@ echo '--------------------------' # Generate the Subject Public Key Info hash pin=$(openssl x509 -pubkey -noout -in $CERT | openssl rsa -outform DER -pubin -in /dev/stdin 2>/dev/null | openssl dgst -sha256 -binary | openssl enc -base64) -echo "kTSKPublicKeyHashes: @[\"$pin\"]" +echo "kTSKPublicKeyHashes: @[@\"$pin\"]" # Generate the public key algorithm publickey=$(openssl x509 -in $CERT -text -noout | grep 'Public Key') diff --git a/get_pin_from_server.sh b/get_pin_from_server.sh index 6c0d3937..673c8407 100755 --- a/get_pin_from_server.sh +++ b/get_pin_from_server.sh @@ -16,7 +16,7 @@ echo 'TrustKit Pin Configuration' echo '--------------------------' # Generate the Subject Public Key Info hash pin=$(echo "$CACERT" | openssl x509 -pubkey -noout -in /dev/stdin | openssl rsa -outform DER -pubin -in /dev/stdin 2>/dev/null | openssl dgst -sha256 -binary | openssl enc -base64) -echo "kTSKPublicKeyHashes: @[\"$pin\"]" +echo "kTSKPublicKeyHashes: @[@\"$pin\"]" # Generate the public key algorithm publickey=$(echo "$CACERT" | openssl x509 -in /dev/stdin -text -noout | grep 'Public Key')