Skip to content

Commit c1d1f21

Browse files
committed
Update to latest swift-certificates
- Adjust code - Re-enable `SigningTests.testCMSCheckCertificateRevocationStatus`
1 parent 970f792 commit c1d1f21

14 files changed

+248
-114
lines changed
Binary file not shown.
57 Bytes
Binary file not shown.
95 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
95 Bytes
Binary file not shown.
-1 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
750750
.package(url: "https://github.com/apple/swift-crypto.git", .upToNextMinor(from: "2.4.0")),
751751
.package(url: "https://github.com/apple/swift-system.git", .upToNextMinor(from: "1.1.1")),
752752
.package(url: "https://github.com/apple/swift-collections.git", .upToNextMinor(from: "1.0.1")),
753-
.package(url: "https://github.com/apple/swift-certificates.git", .upToNextMinor(from: "0.1.0")),
753+
.package(url: "https://github.com/apple/swift-certificates.git", branch: "main"),
754754
]
755755
} else {
756756
package.dependencies += [

Sources/PackageSigning/VerifierPolicies.swift

Lines changed: 91 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,21 @@ import struct Foundation.Date
1616
import struct Foundation.URL
1717

1818
import Basics
19+
@_implementationOnly import SwiftASN1
1920
@_implementationOnly import X509
2021

2122
extension SignatureProviderProtocol {
2223
func buildPolicySet(configuration: VerifierConfiguration, httpClient: HTTPClient) -> PolicySet {
23-
var policies = [VerifierPolicy]()
24-
25-
if case .enabled(let validationTime) = configuration.certificateExpiration {
26-
policies.append(RFC5280Policy(validationTime: validationTime ?? Date()))
24+
var policies: [VerifierPolicy] = [
25+
_CodeSigningPolicy(),
26+
_ADPCertificatePolicy(),
27+
]
28+
29+
switch configuration.certificateExpiration {
30+
case .enabled(let validationTime):
31+
policies.append(_RFC5280Policy(validationTime: validationTime, enableExpiryCheck: true))
32+
case .disabled:
33+
policies.append(_RFC5280Policy(validationTime: .none, enableExpiryCheck: false))
2734
}
2835

2936
switch configuration.certificateRevocation {
@@ -44,38 +51,99 @@ extension SignatureProviderProtocol {
4451
}
4552
}
4653

54+
/// Policy for code signing certificates.
55+
struct _CodeSigningPolicy: VerifierPolicy {
56+
let verifyingCriticalExtensions: [ASN1ObjectIdentifier] = [
57+
ASN1ObjectIdentifier.X509ExtensionID.keyUsage,
58+
ASN1ObjectIdentifier.X509ExtensionID.extendedKeyUsage,
59+
]
60+
61+
func chainMeetsPolicyRequirements(chain: UnverifiedCertificateChain) async -> PolicyEvaluationResult {
62+
let isCodeSigning = (
63+
try? chain.leaf.extensions.extendedKeyUsage?.contains(ExtendedKeyUsage.Usage.codeSigning)
64+
) ??
65+
false
66+
guard isCodeSigning else {
67+
return .failsToMeetPolicy(reason: "Certificate \(chain.leaf) does not have code signing extended key usage")
68+
}
69+
return .meetsPolicy
70+
}
71+
}
72+
73+
/// Policy for ADP certificates.
74+
struct _ADPCertificatePolicy: VerifierPolicy {
75+
/// Include custom marker extensions (which can be critical) so they would not
76+
/// be considered unhandled and cause certificate chain validation to fail.
77+
let verifyingCriticalExtensions: [ASN1ObjectIdentifier] = Self.swiftPackageMarkers
78+
+ Self.developmentMarkers
79+
80+
// Marker extensions for Swift Package certificate
81+
private static let swiftPackageMarkers: [ASN1ObjectIdentifier] = [
82+
// This is not a critical extension but including it just in case
83+
ASN1ObjectIdentifier.NameAttributes.adpSwiftPackageMarker,
84+
]
85+
86+
// Marker extensions for Development certificate (included for testing)
87+
private static let developmentMarkers: [ASN1ObjectIdentifier] = [
88+
[1, 2, 840, 113_635, 100, 6, 1, 2],
89+
[1, 2, 840, 113_635, 100, 6, 1, 12],
90+
]
91+
92+
func chainMeetsPolicyRequirements(chain: UnverifiedCertificateChain) async -> PolicyEvaluationResult {
93+
// Not policing anything here. This policy is mainly for
94+
// listing marker extensions to prevent chain validation
95+
// from failing prematurely.
96+
.meetsPolicy
97+
}
98+
}
99+
100+
struct _RFC5280Policy: VerifierPolicy {
101+
/// See RFC5280Policy
102+
public let verifyingCriticalExtensions: [ASN1ObjectIdentifier] = [
103+
.X509ExtensionID.basicConstraints,
104+
.X509ExtensionID.nameConstraints,
105+
.X509ExtensionID.keyUsage,
106+
]
107+
108+
let validationTime: Date?
109+
let enableExpiryCheck: Bool
110+
111+
func chainMeetsPolicyRequirements(chain: UnverifiedCertificateChain) async -> PolicyEvaluationResult {
112+
let policy: RFC5280Policy
113+
if self.enableExpiryCheck {
114+
policy = RFC5280Policy(validationTime: self.validationTime ?? Date())
115+
} else {
116+
// Use leaf.notValidBefore as validationTime to ensure the leaf
117+
// will pass expiry check. This should work for the chain if the
118+
// chain follows validity nesting, but if a parent expires before
119+
// a child does then the expiry check will fail.
120+
policy = RFC5280Policy(validationTime: chain.leaf.notValidBefore)
121+
}
122+
return policy.chainMeetsPolicyRequirements(chain: chain)
123+
}
124+
}
125+
47126
struct _OCSPVerifierPolicy: VerifierPolicy {
48127
private static let cacheTTL: DispatchTimeInterval = .seconds(5 * 60)
49128
private let cache = ThreadSafeKeyValueStore<
50129
UnverifiedCertificateChain,
51130
(result: PolicyEvaluationResult, expires: DispatchTime)
52131
>()
53132

54-
private let underlying: OCSPVerifierPolicy<_OCSPRequester>
133+
private var underlying: OCSPVerifierPolicy<_OCSPRequester>
55134
private let mode: Mode
56-
private let validationTime: Date
135+
136+
let verifyingCriticalExtensions: [ASN1ObjectIdentifier] = []
57137

58138
init(httpClient: HTTPClient, mode: Mode, validationTime: Date?) {
59-
self.underlying = OCSPVerifierPolicy(requester: _OCSPRequester(httpClient: httpClient))
139+
self.underlying = OCSPVerifierPolicy(
140+
requester: _OCSPRequester(httpClient: httpClient),
141+
validationTime: validationTime ?? Date()
142+
)
60143
self.mode = mode
61-
self.validationTime = validationTime ?? Date()
62144
}
63145

64-
func chainMeetsPolicyRequirements(chain: UnverifiedCertificateChain) async -> PolicyEvaluationResult {
65-
// Check for expiration of the leaf before revocation
66-
let leaf = chain.leaf
67-
if leaf.notValidBefore > leaf.notValidAfter {
68-
return .failsToMeetPolicy(
69-
reason: "OCSPVerifierPolicy: leaf certificate \(leaf) has invalid expiry, notValidAfter is earlier than notValidBefore"
70-
)
71-
}
72-
if self.validationTime < leaf.notValidBefore {
73-
return .failsToMeetPolicy(reason: "OCSPVerifierPolicy: leaf certificate \(leaf) is not yet valid")
74-
}
75-
if self.validationTime > leaf.notValidAfter {
76-
return .failsToMeetPolicy(reason: "OCSPVerifierPolicy: leaf certificate \(leaf) has expired")
77-
}
78-
146+
mutating func chainMeetsPolicyRequirements(chain: UnverifiedCertificateChain) async -> PolicyEvaluationResult {
79147
// Look for cached result
80148
if let cached = self.cache[chain], cached.expires < .now() {
81149
return cached.result

Tests/PackageSigningTests/SigningIdentityTests.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ final class SigningIdentityTests: XCTestCase {
3030
let certificate = try Certificate(certificateBytes)
3131

3232
let subject = certificate.subject
33-
XCTAssertEqual("Test (EC)", subject.commonName)
34-
XCTAssertEqual("Test (EC)", subject.organizationalUnitName)
35-
XCTAssertEqual("Test (EC)", subject.organizationName)
33+
XCTAssertEqual("Test (EC) leaf", subject.commonName)
34+
XCTAssertEqual("Test (EC) org unit", subject.organizationalUnitName)
35+
XCTAssertEqual("Test (EC) org", subject.organizationName)
3636

3737
let privateKeyBytes = try readFileContents(
3838
in: fixturePath,
@@ -63,9 +63,9 @@ final class SigningIdentityTests: XCTestCase {
6363
let certificate = try Certificate(certificateBytes)
6464

6565
let subject = certificate.subject
66-
XCTAssertEqual("Test (RSA)", subject.commonName)
67-
XCTAssertEqual("Test (RSA)", subject.organizationalUnitName)
68-
XCTAssertEqual("Test (RSA)", subject.organizationName)
66+
XCTAssertEqual("Test (RSA) leaf", subject.commonName)
67+
XCTAssertEqual("Test (RSA) org unit", subject.organizationalUnitName)
68+
XCTAssertEqual("Test (RSA) org", subject.organizationName)
6969

7070
let privateKeyBytes = try readFileContents(
7171
in: fixturePath,

0 commit comments

Comments
 (0)