Skip to content

Commit 4963854

Browse files
committed
Allow clients to programmatically accept an identity change
This will allow clients to take the information from specific registry error cases and programmatically accept them as valid. This can be useful if a client wants to offer a way for users to accept an allowed identity change.
1 parent 57d829a commit 4963854

File tree

6 files changed

+71
-6
lines changed

6 files changed

+71
-6
lines changed

Sources/PackageRegistry/RegistryClient.swift

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,26 @@ public final class RegistryClient: Cancellable {
126126
try self.httpClient.cancel(deadline: deadline)
127127
}
128128

129+
public func changeSigningEntityFromVersion(
130+
package: PackageIdentity,
131+
version: Version,
132+
signingEntity: SigningEntity,
133+
origin: SigningEntity.Origin,
134+
observabilityScope: ObservabilityScope,
135+
callbackQueue: DispatchQueue,
136+
completion: @escaping (Result<Void, Error>) -> Void
137+
) {
138+
self.signingEntityStorage?.changeSigningEntityFromVersion(
139+
package: package,
140+
version: version,
141+
signingEntity: signingEntity,
142+
origin: origin,
143+
observabilityScope: observabilityScope,
144+
callbackQueue: callbackQueue,
145+
callback: completion
146+
)
147+
}
148+
129149
public func getPackageMetadata(
130150
package: PackageIdentity,
131151
timeout: DispatchTimeInterval? = .none,
@@ -1430,7 +1450,7 @@ public enum RegistryError: Error, CustomStringConvertible {
14301450
case unknownSignatureFormat(String)
14311451
case invalidSignature(reason: String)
14321452
case invalidSigningCertificate(reason: String)
1433-
case signerNotTrusted(SigningEntity)
1453+
case signerNotTrusted(PackageIdentity, SigningEntity)
14341454
case failedToValidateSignature(Error)
14351455
case signingEntityForReleaseChanged(
14361456
registry: Registry,
@@ -1536,7 +1556,7 @@ public enum RegistryError: Error, CustomStringConvertible {
15361556
return "signature is invalid: \(reason)"
15371557
case .invalidSigningCertificate(let reason):
15381558
return "the signing certificate is invalid: \(reason)"
1539-
case .signerNotTrusted(let signingEntity):
1559+
case .signerNotTrusted(_, let signingEntity):
15401560
return "the signer \(signingEntity) is not trusted"
15411561
case .failedToValidateSignature(let error):
15421562
return "failed to validate signature: \(error)"

Sources/PackageRegistry/SignatureValidation.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ struct SignatureValidation {
209209
Task {
210210
do {
211211
let signatureStatus = try await SignatureProvider.status(
212+
package: package,
212213
signature: Array(signature),
213214
content: Array(content),
214215
format: signatureFormat,
@@ -227,7 +228,7 @@ struct SignatureValidation {
227228
completion(.failure(RegistryError.invalidSignature(reason: reason)))
228229
case .certificateInvalid(let reason):
229230
completion(.failure(RegistryError.invalidSigningCertificate(reason: reason)))
230-
case .certificateNotTrusted(let signingEntity):
231+
case .certificateNotTrusted(let package, let signingEntity):
231232
observabilityScope
232233
.emit(
233234
info: "\(package) \(version) from \(registry) signing entity '\(signingEntity)' is untrusted"
@@ -239,7 +240,7 @@ struct SignatureValidation {
239240
))
240241
}
241242

242-
let signerNotTrustedError = RegistryError.signerNotTrusted(signingEntity)
243+
let signerNotTrustedError = RegistryError.signerNotTrusted(package.underlying, signingEntity)
243244

244245
switch onUntrusted {
245246
case .prompt:

Sources/PackageSigning/SignatureProvider.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import struct Foundation.Date
1818
#endif
1919

2020
import Basics
21+
import PackageModel
2122
@_implementationOnly import SwiftASN1
2223
@_implementationOnly @_spi(CMS) import X509
2324

@@ -41,6 +42,7 @@ public enum SignatureProvider {
4142
}
4243

4344
public static func status(
45+
package: PackageIdentity.RegistryIdentity,
4446
signature: [UInt8],
4547
content: [UInt8],
4648
format: SignatureFormat,
@@ -49,6 +51,7 @@ public enum SignatureProvider {
4951
) async throws -> SignatureStatus {
5052
let provider = format.provider
5153
return try await provider.status(
54+
package: package,
5255
signature: signature,
5356
content: content,
5457
verifierConfiguration: verifierConfiguration,
@@ -99,7 +102,7 @@ public enum SignatureStatus: Equatable {
99102
case valid(SigningEntity)
100103
case invalid(String)
101104
case certificateInvalid(String)
102-
case certificateNotTrusted(SigningEntity)
105+
case certificateNotTrusted(PackageIdentity.RegistryIdentity, SigningEntity)
103106
}
104107

105108
public enum SigningError: Error {
@@ -157,6 +160,7 @@ protocol SignatureProviderProtocol {
157160
) throws -> [UInt8]
158161

159162
func status(
163+
package: PackageIdentity.RegistryIdentity,
160164
signature: [UInt8],
161165
content: [UInt8],
162166
verifierConfiguration: VerifierConfiguration,
@@ -233,6 +237,7 @@ struct CMSSignatureProvider: SignatureProviderProtocol {
233237
}
234238

235239
func status(
240+
package: PackageIdentity.RegistryIdentity,
236241
signature: [UInt8],
237242
content: [UInt8],
238243
verifierConfiguration: VerifierConfiguration,
@@ -269,7 +274,7 @@ struct CMSSignatureProvider: SignatureProviderProtocol {
269274
case .failure(CMS.VerificationError.unableToValidateSigner(let failure)):
270275
if failure.validationFailures.isEmpty {
271276
let signingEntity = SigningEntity.from(certificate: failure.signer)
272-
return .certificateNotTrusted(signingEntity)
277+
return .certificateNotTrusted(package, signingEntity)
273278
} else {
274279
observabilityScope.emit(info: "cannot validate certificate chain. Validation failures: \(failure.validationFailures)")
275280
return .certificateInvalid("failures: \(failure.validationFailures.map { $0.policyFailureReason })")

Sources/Workspace/Workspace.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3471,6 +3471,26 @@ extension Workspace {
34713471
// remove the local copy
34723472
try registryDownloadsManager.remove(package: dependency.packageRef.identity)
34733473
}
3474+
3475+
public func acceptIdentityChange(
3476+
package: PackageIdentity,
3477+
version: Version,
3478+
signingEntity: SigningEntity,
3479+
origin: SigningEntity.Origin,
3480+
observabilityScope: ObservabilityScope,
3481+
callbackQueue: DispatchQueue,
3482+
completion: @escaping (Result<Void, Error>) -> Void
3483+
) {
3484+
self.registryClient.changeSigningEntityFromVersion(
3485+
package: package,
3486+
version: version,
3487+
signingEntity: signingEntity,
3488+
origin: origin,
3489+
observabilityScope: observabilityScope,
3490+
callbackQueue: callbackQueue,
3491+
completion: completion
3492+
)
3493+
}
34743494
}
34753495

34763496
// MARK: - Utility extensions

Tests/CommandsTests/PackageRegistryToolTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ final class PackageRegistryToolTests: CommandsTestCase {
470470
verifierConfiguration.trustedRoots = try tsc_await { self.testRoots(callback: $0) }
471471

472472
let signatureStatus = try await SignatureProvider.status(
473+
package: PackageIdentity.plain("mona.LinkedList").registry!,
473474
signature: signature,
474475
content: archive,
475476
format: .cms_1_0_0,

Tests/PackageSigningTests/SigningTests.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import XCTest
1616
import _CryptoExtras // for RSA
1717
import Basics
1818
import Crypto
19+
import PackageModel
1920
@testable import PackageSigning
2021
import SPMTestSupport
2122
import SwiftASN1
@@ -49,6 +50,7 @@ final class SigningTests: XCTestCase {
4950
)
5051

5152
let status = try await SignatureProvider.status(
53+
package: PackageIdentity.plain("mona.LinkedList").registry!,
5254
signature: signature,
5355
content: content,
5456
format: signatureFormat,
@@ -92,6 +94,7 @@ final class SigningTests: XCTestCase {
9294
)
9395

9496
let status = try await cmsProvider.status(
97+
package: PackageIdentity.plain("mona.LinkedList").registry!,
9598
signature: signature,
9699
content: content,
97100
verifierConfiguration: verifierConfiguration,
@@ -134,6 +137,7 @@ final class SigningTests: XCTestCase {
134137
)
135138

136139
let status = try await cmsProvider.status(
140+
package: PackageIdentity.plain("mona.LinkedList").registry!,
137141
signature: signature,
138142
content: content,
139143
verifierConfiguration: verifierConfiguration,
@@ -204,6 +208,7 @@ final class SigningTests: XCTestCase {
204208
)
205209

206210
let status = try await SignatureProvider.status(
211+
package: PackageIdentity.plain("mona.LinkedList").registry!,
207212
signature: signature,
208213
content: content,
209214
format: signatureFormat,
@@ -247,6 +252,7 @@ final class SigningTests: XCTestCase {
247252
)
248253

249254
let status = try await cmsProvider.status(
255+
package: PackageIdentity.plain("mona.LinkedList").registry!,
250256
signature: signature,
251257
content: content,
252258
verifierConfiguration: verifierConfiguration,
@@ -289,6 +295,7 @@ final class SigningTests: XCTestCase {
289295
)
290296

291297
let status = try await cmsProvider.status(
298+
package: PackageIdentity.plain("mona.LinkedList").registry!,
292299
signature: signature,
293300
content: content,
294301
verifierConfiguration: verifierConfiguration,
@@ -312,6 +319,7 @@ final class SigningTests: XCTestCase {
312319

313320
let cmsProvider = CMSSignatureProvider(signatureAlgorithm: .ecdsaP256)
314321
let status = try await cmsProvider.status(
322+
package: PackageIdentity.plain("mona.LinkedList").registry!,
315323
signature: signature,
316324
content: content,
317325
verifierConfiguration: .init(),
@@ -349,6 +357,7 @@ final class SigningTests: XCTestCase {
349357
)
350358

351359
let status = try await cmsProvider.status(
360+
package: PackageIdentity.plain("mona.LinkedList").registry!,
352361
signature: signature,
353362
content: otherContent,
354363
verifierConfiguration: verifierConfiguration,
@@ -385,6 +394,7 @@ final class SigningTests: XCTestCase {
385394
)
386395

387396
let status = try await cmsProvider.status(
397+
package: PackageIdentity.plain("mona.LinkedList").registry!,
388398
signature: signature,
389399
content: content,
390400
verifierConfiguration: verifierConfiguration,
@@ -425,6 +435,7 @@ final class SigningTests: XCTestCase {
425435
)
426436

427437
let status = try await cmsProvider.status(
438+
package: PackageIdentity.plain("mona.LinkedList").registry!,
428439
signature: signature,
429440
content: content,
430441
verifierConfiguration: verifierConfiguration,
@@ -449,6 +460,7 @@ final class SigningTests: XCTestCase {
449460
)
450461

451462
let status = try await cmsProvider.status(
463+
package: PackageIdentity.plain("mona.LinkedList").registry!,
452464
signature: signature,
453465
content: content,
454466
verifierConfiguration: verifierConfiguration,
@@ -514,6 +526,7 @@ final class SigningTests: XCTestCase {
514526
)
515527

516528
let status = try await cmsProvider.status(
529+
package: PackageIdentity.plain("mona.LinkedList").registry!,
517530
signature: signature,
518531
content: content,
519532
verifierConfiguration: verifierConfiguration,
@@ -535,6 +548,7 @@ final class SigningTests: XCTestCase {
535548
)
536549

537550
let status = try await cmsProvider.status(
551+
package: PackageIdentity.plain("mona.LinkedList").registry!,
538552
signature: signature,
539553
content: content,
540554
verifierConfiguration: verifierConfiguration,
@@ -576,6 +590,7 @@ final class SigningTests: XCTestCase {
576590
)
577591

578592
let status = try await cmsProvider.status(
593+
package: PackageIdentity.plain("mona.LinkedList").registry!,
579594
signature: signature,
580595
content: content,
581596
verifierConfiguration: verifierConfiguration,
@@ -644,6 +659,7 @@ final class SigningTests: XCTestCase {
644659
)
645660

646661
let status = try await SignatureProvider.status(
662+
package: PackageIdentity.plain("mona.LinkedList").registry!,
647663
signature: signature,
648664
content: content,
649665
format: signatureFormat,
@@ -701,6 +717,7 @@ final class SigningTests: XCTestCase {
701717
)
702718

703719
let status = try await cmsProvider.status(
720+
package: PackageIdentity.plain("mona.LinkedList").registry!,
704721
signature: signature,
705722
content: content,
706723
verifierConfiguration: verifierConfiguration,
@@ -757,6 +774,7 @@ final class SigningTests: XCTestCase {
757774
)
758775

759776
let status = try await cmsProvider.status(
777+
package: PackageIdentity.plain("mona.LinkedList").registry!,
760778
signature: signature,
761779
content: content,
762780
verifierConfiguration: verifierConfiguration,

0 commit comments

Comments
 (0)