Skip to content

Commit

Permalink
Better visibility into verify errors (#160)
Browse files Browse the repository at this point in the history
* remove verify from createSignedData() and capture all security framework errors as one error with the code bubbled up
  • Loading branch information
gadphly authored Sep 8, 2023
1 parent 596f358 commit b1c85cc
Show file tree
Hide file tree
Showing 6 changed files with 18 additions and 38 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ let certs = try response.map({ try TrifleCertificate.deserialize(data: $0) })
// certs is an array of certificates where [0] will be device certificate
// and the rest of the elements will be intermediate chain.

// For the following, verify API can throw a number of errors as defined by TrifleError

// Check if app has the root cert of Certificate Authority (CA).

// Validate cert matches the certificate request (so generated key)
Expand Down
8 changes: 4 additions & 4 deletions ios/Example/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
PODS:
- Trifle (0.2.2):
- Wire (~> 4.5.1)
- Trifle (0.2.3):
- Wire (~> 4)
- Wire (4.5.1)
- WireCompiler (4.5.1)

Expand All @@ -18,10 +18,10 @@ EXTERNAL SOURCES:
:path: "../../"

SPEC CHECKSUMS:
Trifle: 1907231873b9d3ebd037197d5bcfb0cb183f5fe3
Trifle: 1bc03d4dadabef03a928fc05924bbd7df1a53dd0
Wire: b07a2ff1c4cd4b71f5ae26771cdd13fc7868c9df
WireCompiler: 417c2ac583c01de328010738658758556ea92a92

PODFILE CHECKSUM: 4b8d6c2fc4c9668821977302cb1ff5c5891d9920

COCOAPODS: 1.12.0
COCOAPODS: 1.12.1
6 changes: 4 additions & 2 deletions ios/Example/Tests/CertificateTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ final class CertificateTests: XCTestCase {

XCTAssertTrue(try validCertificate.verify(intermediateChain: Array<Certificate>()))

XCTAssertThrowsError(try expiredCertificate.verify(intermediateChain: Array<Certificate>()),
"Certificate is expired.")
XCTAssertThrowsError(try expiredCertificate.verify(intermediateChain: Array<Certificate>())) { error in
XCTAssertTrue(( ((error as? TrifleError)?.errorDescription?.contains(
"Security Framework error ( -67818 )") == true ) ))
}
}

}
13 changes: 0 additions & 13 deletions ios/Example/Tests/TrifleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,17 +167,4 @@ final class TrifleTests: XCTestCase {
certificates: []),
"Data or Certificate should not be empty.")
}

func testSignInvalidCertChain_fail() throws {
let trifle = try Trifle(reverseDomain: TestFixtures.reverseDomain)
let keyHandle = try trifle.generateKeyHandle()

let deviceCertificate = try TrifleCertificate.deserialize(data: TestFixtures.deviceTrifleCertEncoded!)
let otherRootCertificate = try TrifleCertificate.deserialize(data: TestFixtures.otherRootTrifleCertEncoded!)

XCTAssertThrowsError(try trifle.createSignedData(data: TestFixtures.data,
keyHandle: keyHandle,
certificates: [deviceCertificate]+[otherRootCertificate]),
"Invalid certificate chain.")
}
}
21 changes: 7 additions & 14 deletions ios/Trifle/Sources/Trifle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,19 +117,14 @@ public class Trifle {
certificates: Array<TrifleCertificate>
) throws -> TrifleSignedData {

guard let leafCert = certificates.first, !data.isEmpty else {
guard !certificates.isEmpty, !data.isEmpty else {
throw TrifleError.invalidInput("Data or Certificate should not be empty.")
}

// TODO: (gelareh) check key handle domain matches the one in trifle

// TODO: (gelareh) check leaf cert matches the public key that will be used for signing

// check cert chain validates
guard try leafCert.verify(intermediateTrifleChain: Array(certificates.dropFirst(1))) else {
throw TrifleError.invalidCertificateChain
}


let signingDataAlgorithm: SignedData.Algorithm
switch contentSigner.signingAlgorithm {
case .ecdsaSha256:
Expand Down Expand Up @@ -160,20 +155,18 @@ public enum TrifleError: LocalizedError {
case invalidInput(String)
case unhandled(Error)
case invalidCertificateChain
case expiredCertificate
case invalidCertificate
case securityFramework(Int)

public var errorDescription: String? {
switch self {
case .invalidCertificateChain:
return "Invalid certificate chain."
case .expiredCertificate:
return "Certificate is expired."
case .invalidCertificate:
return "Certificate is invalid."
case let .securityFramework(errorCode):
// https://developer.apple.com/documentation/security/1542001-security_framework_result_codes
// check error code https://www.osstatus.com/
return "Security Framework error ( \(errorCode) )."
case let .invalidInput(error):
return error

case let .unhandled(error):
return error.localizedDescription
}
Expand Down
6 changes: 1 addition & 5 deletions ios/Trifle/Sources/TrifleCertificate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,7 @@ extension Certificate {
let result = try X509TrustManager.evaluate(chain)
return result
} catch let error as NSError {
if error.code == errSecCertificateExpired {
throw TrifleError.expiredCertificate
} else {
throw TrifleError.invalidCertificate
}
throw TrifleError.securityFramework(error.code)
}
}
}

0 comments on commit b1c85cc

Please sign in to comment.