Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added new server trust policy: Revocation #1822

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Source/ServerTrustPolicy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,18 @@ extension URLSession {
/// Applications are encouraged to always validate the host and require a valid certificate
/// chain in production environments.
///
/// - performRevokedEvaluation: Uses the default server trust evaluation while allowing you to control whether to
/// validate the host provided by the challenge. And this option also check status of revoked
/// server certificate.
///
/// - disableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid.
///
/// - customEvaluation: Uses the associated closure to evaluate the validity of the server trust.
public enum ServerTrustPolicy {
case performDefaultEvaluation(validateHost: Bool)
case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool)
case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool)
case performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags)
case disableEvaluation
case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool)

Expand Down Expand Up @@ -214,6 +219,12 @@ public enum ServerTrustPolicy {
}
}
}
case let .performRevokedEvaluation(validateHost, revocationFlags):
let policyDefault = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
let policyRevoked = SecPolicyCreateRevocation(revocationFlags)
SecTrustSetPolicies(serverTrust, [policyDefault, policyRevoked] as CFTypeRef)

serverTrustIsValid = trustIsValid(serverTrust)
case .disableEvaluation:
serverTrustIsValid = true
case let .customEvaluation(closure):
Expand Down
167 changes: 167 additions & 0 deletions Tests/ServerTrustPolicyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1325,6 +1325,173 @@ class ServerTrustPolicyPinPublicKeysTestCase: ServerTrustPolicyTestCase {
}
}

// MARK: - Server Revoke Policy Tests -

class ServerTrustPolicyPerformRevokedEvaluationTestCase: ServerTrustPolicyTestCase {

// MARK: Do NOT Validate Host

func testThatValidCertificateChainPassesEvaluationWithoutHostValidation() {
// Given
let host = "test.alamofire.org"
let serverTrust = TestTrusts.leafValidDNSName.trust
let serverTrustPolicy = ServerTrustPolicy.performRevokedEvaluation(validateHost: false, revocationFlags: kSecRevocationRequirePositiveResponse)

// When
setRootCertificateAsLoneAnchorCertificateForTrust(serverTrust)
let serverTrustIsValid = serverTrustPolicy.evaluate(serverTrust, forHost: host)

// Then
XCTAssertFalse(serverTrustIsValid, "server trust should not pass evaluation")
}

func testThatNonAnchoredRootCertificateChainFailsEvaluationWithoutHostValidation() {
// Given
let host = "test.alamofire.org"
let serverTrust = TestTrusts.trustWithCertificates([
TestCertificates.leafValidDNSName,
TestCertificates.intermediateCA2
])
let serverTrustPolicy = ServerTrustPolicy.performRevokedEvaluation(validateHost: false, revocationFlags: kSecRevocationRequirePositiveResponse)

// When
let serverTrustIsValid = serverTrustPolicy.evaluate(serverTrust, forHost: host)

// Then
XCTAssertFalse(serverTrustIsValid, "server trust should not pass evaluation")
}

func testThatMissingDNSNameLeafCertificatePassesEvaluationWithoutHostValidation() {
// Given
let host = "test.alamofire.org"
let serverTrust = TestTrusts.leafMissingDNSNameAndURI.trust
let serverTrustPolicy = ServerTrustPolicy.performRevokedEvaluation(validateHost: false, revocationFlags: kSecRevocationRequirePositiveResponse)

// When
setRootCertificateAsLoneAnchorCertificateForTrust(serverTrust)
let serverTrustIsValid = serverTrustPolicy.evaluate(serverTrust, forHost: host)

// Then
XCTAssertFalse(serverTrustIsValid, "server trust should not pass evaluation")
}

func testThatExpiredCertificateChainFailsEvaluationWithoutHostValidation() {
// Given
let host = "test.alamofire.org"
let serverTrust = TestTrusts.leafExpired.trust
let serverTrustPolicy = ServerTrustPolicy.performRevokedEvaluation(validateHost: false, revocationFlags: kSecRevocationRequirePositiveResponse)

// When
setRootCertificateAsLoneAnchorCertificateForTrust(serverTrust)
let serverTrustIsValid = serverTrustPolicy.evaluate(serverTrust, forHost: host)

// Then
XCTAssertFalse(serverTrustIsValid, "server trust should not pass evaluation")
}

func testThatMissingIntermediateCertificateInChainFailsEvaluationWithoutHostValidation() {
// Given
let host = "test.alamofire.org"
let serverTrust = TestTrusts.leafValidDNSNameMissingIntermediate.trust
let serverTrustPolicy = ServerTrustPolicy.performRevokedEvaluation(validateHost: false, revocationFlags: kSecRevocationRequirePositiveResponse)

// When
setRootCertificateAsLoneAnchorCertificateForTrust(serverTrust)
let serverTrustIsValid = serverTrustPolicy.evaluate(serverTrust, forHost: host)

// Then
XCTAssertFalse(serverTrustIsValid, "server trust should not pass evaluation")
}

// MARK: Validate Host

func testThatValidCertificateChainPassesEvaluationWithHostValidation() {
// Given
let host = "test.alamofire.org"
let serverTrust = TestTrusts.leafValidDNSName.trust
let serverTrustPolicy = ServerTrustPolicy.performRevokedEvaluation(validateHost: true, revocationFlags: kSecRevocationRequirePositiveResponse)

// When
setRootCertificateAsLoneAnchorCertificateForTrust(serverTrust)
let serverTrustIsValid = serverTrustPolicy.evaluate(serverTrust, forHost: host)

// Then
XCTAssertFalse(serverTrustIsValid, "server trust should not pass evaluation")
}

func testThatNonAnchoredRootCertificateChainFailsEvaluationWithHostValidation() {
// Given
let host = "test.alamofire.org"
let serverTrust = TestTrusts.trustWithCertificates([
TestCertificates.leafValidDNSName,
TestCertificates.intermediateCA2
])
let serverTrustPolicy = ServerTrustPolicy.performRevokedEvaluation(validateHost: true, revocationFlags: kSecRevocationRequirePositiveResponse)

// When
let serverTrustIsValid = serverTrustPolicy.evaluate(serverTrust, forHost: host)

// Then
XCTAssertFalse(serverTrustIsValid, "server trust should not pass evaluation")
}

func testThatMissingDNSNameLeafCertificateFailsEvaluationWithHostValidation() {
// Given
let host = "test.alamofire.org"
let serverTrust = TestTrusts.leafMissingDNSNameAndURI.trust
let serverTrustPolicy = ServerTrustPolicy.performRevokedEvaluation(validateHost: true, revocationFlags: kSecRevocationRequirePositiveResponse)

// When
setRootCertificateAsLoneAnchorCertificateForTrust(serverTrust)
let serverTrustIsValid = serverTrustPolicy.evaluate(serverTrust, forHost: host)

// Then
XCTAssertFalse(serverTrustIsValid, "server trust should not pass evaluation")
}

func testThatWildcardedLeafCertificateChainPassesEvaluationWithHostValidation() {
// Given
let host = "test.alamofire.org"
let serverTrust = TestTrusts.leafWildcard.trust
let serverTrustPolicy = ServerTrustPolicy.performRevokedEvaluation(validateHost: true, revocationFlags: kSecRevocationRequirePositiveResponse)

// When
setRootCertificateAsLoneAnchorCertificateForTrust(serverTrust)
let serverTrustIsValid = serverTrustPolicy.evaluate(serverTrust, forHost: host)

// Then
XCTAssertFalse(serverTrustIsValid, "server trust should pass evaluation")
}

func testThatExpiredCertificateChainFailsEvaluationWithHostValidation() {
// Given
let host = "test.alamofire.org"
let serverTrust = TestTrusts.leafExpired.trust
let serverTrustPolicy = ServerTrustPolicy.performRevokedEvaluation(validateHost: true, revocationFlags: kSecRevocationRequirePositiveResponse)

// When
setRootCertificateAsLoneAnchorCertificateForTrust(serverTrust)
let serverTrustIsValid = serverTrustPolicy.evaluate(serverTrust, forHost: host)

// Then
XCTAssertFalse(serverTrustIsValid, "server trust should not pass evaluation")
}

func testThatMissingIntermediateCertificateInChainFailsEvaluationWithHostValidation() {
// Given
let host = "test.alamofire.org"
let serverTrust = TestTrusts.leafValidDNSNameMissingIntermediate.trust
let serverTrustPolicy = ServerTrustPolicy.performRevokedEvaluation(validateHost: true, revocationFlags: kSecRevocationRequirePositiveResponse)

// When
setRootCertificateAsLoneAnchorCertificateForTrust(serverTrust)
let serverTrustIsValid = serverTrustPolicy.evaluate(serverTrust, forHost: host)

// Then
XCTAssertFalse(serverTrustIsValid, "server trust should not pass evaluation")
}
}

// MARK: -

class ServerTrustPolicyDisableEvaluationTestCase: ServerTrustPolicyTestCase {
Expand Down
Loading