From b3482819abd94d24568a089bbc34b61589d283cd Mon Sep 17 00:00:00 2001 From: Dimitri Bouniol Date: Fri, 9 May 2025 01:02:54 -0700 Subject: [PATCH 1/2] Added missing Hashable and Sendable conformances --- .../AppStoreServerLibrary/AppStoreServerAPIClient.swift | 8 +++++--- Sources/AppStoreServerLibrary/ChainVerifier.swift | 4 ++++ Sources/AppStoreServerLibrary/SignedDataVerifier.swift | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Sources/AppStoreServerLibrary/AppStoreServerAPIClient.swift b/Sources/AppStoreServerLibrary/AppStoreServerAPIClient.swift index 579264d..0d237aa 100644 --- a/Sources/AppStoreServerLibrary/AppStoreServerAPIClient.swift +++ b/Sources/AppStoreServerLibrary/AppStoreServerAPIClient.swift @@ -9,7 +9,7 @@ import NIOFoundationCompat public class AppStoreServerAPIClient { - public enum ConfigurationError: Error { + public enum ConfigurationError: Error, Hashable, Sendable { /// Xcode is not a supported environment for an AppStoreServerAPIClient case invalidEnvironment } @@ -345,7 +345,9 @@ public enum APIResult { case failure(statusCode: Int?, rawApiError: Int64?, apiError: APIError?, errorMessage: String?, causedBy: Error?) } -public enum APIError: Int64 { +extension APIResult: Sendable where T: Sendable {} + +public enum APIError: Int64, Hashable, Sendable { ///An error that indicates an invalid request. /// ///[GeneralBadRequestError](https://developer.apple.com/documentation/appstoreserverapi/generalbadrequesterror) @@ -633,7 +635,7 @@ public enum APIError: Int64 { case generalInternalRetryable = 5000001 } -public enum GetTransactionHistoryVersion: String { +public enum GetTransactionHistoryVersion: String, Hashable, Sendable { @available(*, deprecated) case v1 = "v1" case v2 = "v2" diff --git a/Sources/AppStoreServerLibrary/ChainVerifier.swift b/Sources/AppStoreServerLibrary/ChainVerifier.swift index 3d3ae67..fd85930 100644 --- a/Sources/AppStoreServerLibrary/ChainVerifier.swift +++ b/Sources/AppStoreServerLibrary/ChainVerifier.swift @@ -226,6 +226,10 @@ public enum VerificationResult { case invalid(VerificationError) } +extension VerificationResult: Equatable where T: Equatable {} +extension VerificationResult: Hashable where T: Hashable {} +extension VerificationResult: Sendable where T: Sendable {} + public enum VerificationError: Hashable, Sendable { case INVALID_JWT_FORMAT case INVALID_CERTIFICATE diff --git a/Sources/AppStoreServerLibrary/SignedDataVerifier.swift b/Sources/AppStoreServerLibrary/SignedDataVerifier.swift index 2df7b3a..1f1b376 100644 --- a/Sources/AppStoreServerLibrary/SignedDataVerifier.swift +++ b/Sources/AppStoreServerLibrary/SignedDataVerifier.swift @@ -5,7 +5,7 @@ import Foundation ///A verifier and decoder class designed to decode signed data from the App Store. public struct SignedDataVerifier { - public enum ConfigurationError: Error { + public enum ConfigurationError: Error, Hashable, Sendable { case INVALID_APP_APPLE_ID } From a6b2ea43e186cb1740cf5b2bc460f3a0a6922068 Mon Sep 17 00:00:00 2001 From: Dimitri Bouniol Date: Fri, 9 May 2025 01:39:26 -0700 Subject: [PATCH 2/2] Updated SignedDataVerifier and ChainVerifier to both be sendable and concurrency-safe --- Sources/AppStoreServerLibrary/ChainVerifier.swift | 9 ++++----- Sources/AppStoreServerLibrary/SignedDataVerifier.swift | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Sources/AppStoreServerLibrary/ChainVerifier.swift b/Sources/AppStoreServerLibrary/ChainVerifier.swift index fd85930..8872699 100644 --- a/Sources/AppStoreServerLibrary/ChainVerifier.swift +++ b/Sources/AppStoreServerLibrary/ChainVerifier.swift @@ -8,8 +8,7 @@ import Crypto import AsyncHTTPClient import NIOFoundationCompat -class ChainVerifier { - +actor ChainVerifier { private static let EXPECTED_CHAIN_LENGTH = 3 private static let EXPECTED_JWT_SEGMENTS = 3 private static let EXPECTED_ALGORITHM = "ES256" @@ -28,7 +27,7 @@ class ChainVerifier { self.verifiedPublicKeyCache = [:] } - func verify(signedData: String, type: T.Type, onlineVerification: Bool, environment: AppStoreEnvironment) async -> VerificationResult where T: Decodable { + func verify(signedData: String, type: T.Type, onlineVerification: Bool, environment: AppStoreEnvironment) async -> VerificationResult where T: Decodable & Sendable { let header: JWTHeader; let decodedBody: T; do { @@ -120,7 +119,7 @@ class ChainVerifier { return verificationResult } - func verifyChainWithoutCaching(leaf: Certificate, intermediate: Certificate, online: Bool, validationTime: Date) async -> X509.VerificationResult { + nonisolated func verifyChainWithoutCaching(leaf: Certificate, intermediate: Certificate, online: Bool, validationTime: Date) async -> X509.VerificationResult { var verifier = Verifier(rootCertificates: self.store) { RFC5280Policy(validationTime: validationTime) AppStoreOIDPolicy() @@ -132,7 +131,7 @@ class ChainVerifier { return await verifier.validate(leafCertificate: leaf, intermediates: intermediateStore) } - func getDate() -> Date { + nonisolated func getDate() -> Date { return Date() } } diff --git a/Sources/AppStoreServerLibrary/SignedDataVerifier.swift b/Sources/AppStoreServerLibrary/SignedDataVerifier.swift index 1f1b376..df008c5 100644 --- a/Sources/AppStoreServerLibrary/SignedDataVerifier.swift +++ b/Sources/AppStoreServerLibrary/SignedDataVerifier.swift @@ -3,7 +3,7 @@ import Foundation ///A verifier and decoder class designed to decode signed data from the App Store. -public struct SignedDataVerifier { +public struct SignedDataVerifier: Sendable { public enum ConfigurationError: Error, Hashable, Sendable { case INVALID_APP_APPLE_ID @@ -148,7 +148,7 @@ public struct SignedDataVerifier { return appTransactionResult } - private func decodeSignedData(signedData: String, type: T.Type) async -> VerificationResult where T : Decodable { + private func decodeSignedData(signedData: String, type: T.Type) async -> VerificationResult where T : Decodable & Sendable { return await chainVerifier.verify(signedData: signedData, type: type, onlineVerification: self.enableOnlineChecks, environment: self.environment) } }