From 81eb2f0a99b718735b8e1344d427958b0c296f98 Mon Sep 17 00:00:00 2001 From: Jan Kobersky Date: Tue, 26 Nov 2024 17:10:45 +0100 Subject: [PATCH] Removed unnecesarry MachO signature parsing --- .../project.pbxproj | 92 +---- .../ProvisioningUtils/WMTMachOReader.swift} | 84 +++-- .../WMTProvisioningUtils.swift | 0 .../WMTSignatureAPNSEnvironmentDetector.swift | 46 +++ .../Push/MachO/BinaryReader.swift | 47 --- .../Push/MachO/CertParser/ASN1Decoder.swift | 268 -------------- .../CertParser/ASN1DistinguishedNames.swift | 91 ----- .../Push/MachO/CertParser/ASN1Encoder.swift | 58 --- .../MachO/CertParser/ASN1Identifier.swift | 100 ----- .../Push/MachO/CertParser/ASN1Object.swift | 105 ------ .../Push/MachO/CertParser/OID.swift | 101 ----- .../Push/MachO/CertParser/PKCS7.swift | 99 ----- .../MachO/CertParser/X509Certificate.swift | 344 ------------------ .../Push/MachO/CertParser/X509Extension.swift | 69 ---- .../CertParser/X509ExtensionAltName.swift | 82 ----- .../CertParser/X509ExtensionClasses.swift | 181 --------- .../Push/MachO/CertParser/X509PublicKey.swift | 75 ---- .../Push/MachO/Entitlements.swift | 68 ---- .../Push/Model/WMTPushRegistrationData.swift | 8 +- .../Push/Service/WMTPushImpl.swift | 4 +- .../WMTSignatureAPNSEnvironmentDetector.swift | 88 ----- 21 files changed, 116 insertions(+), 1894 deletions(-) rename WultraMobileTokenSDK/{Push/MachO/MachOReader.swift => Common/ProvisioningUtils/WMTMachOReader.swift} (67%) rename WultraMobileTokenSDK/Common/{ => ProvisioningUtils}/WMTProvisioningUtils.swift (100%) create mode 100644 WultraMobileTokenSDK/Common/ProvisioningUtils/WMTSignatureAPNSEnvironmentDetector.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/BinaryReader.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Decoder.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/CertParser/ASN1DistinguishedNames.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Encoder.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Identifier.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Object.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/CertParser/OID.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/CertParser/PKCS7.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/CertParser/X509Certificate.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/CertParser/X509Extension.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/CertParser/X509ExtensionAltName.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/CertParser/X509ExtensionClasses.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/CertParser/X509PublicKey.swift delete mode 100644 WultraMobileTokenSDK/Push/MachO/Entitlements.swift delete mode 100644 WultraMobileTokenSDK/Push/WMTSignatureAPNSEnvironmentDetector.swift diff --git a/WultraMobileTokenSDK.xcodeproj/project.pbxproj b/WultraMobileTokenSDK.xcodeproj/project.pbxproj index 844b60a..e8b2f11 100644 --- a/WultraMobileTokenSDK.xcodeproj/project.pbxproj +++ b/WultraMobileTokenSDK.xcodeproj/project.pbxproj @@ -67,21 +67,7 @@ DCE660D124CEBECA00870E53 /* IntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE660D024CEBECA00870E53 /* IntegrationTests.swift */; }; DCE660D324CEF56400870E53 /* IntegrationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE660D224CEF56400870E53 /* IntegrationProxy.swift */; }; DCE6D5742CF5F46000865D6E /* WMTSignatureAPNSEnvironmentDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D5732CF5F46000865D6E /* WMTSignatureAPNSEnvironmentDetector.swift */; }; - DCE6D5772CF5F5D500865D6E /* MachOReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D5762CF5F5D500865D6E /* MachOReader.swift */; }; - DCE6D5792CF5F5E400865D6E /* Entitlements.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D5782CF5F5E400865D6E /* Entitlements.swift */; }; - DCE6D5872CF5F63100865D6E /* X509PublicKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D5852CF5F63100865D6E /* X509PublicKey.swift */; }; - DCE6D5882CF5F63100865D6E /* X509Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D5822CF5F63100865D6E /* X509Extension.swift */; }; - DCE6D5892CF5F63100865D6E /* ASN1DistinguishedNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D57B2CF5F63100865D6E /* ASN1DistinguishedNames.swift */; }; - DCE6D58A2CF5F63100865D6E /* X509ExtensionClasses.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D5842CF5F63100865D6E /* X509ExtensionClasses.swift */; }; - DCE6D58B2CF5F63100865D6E /* ASN1Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D57A2CF5F63100865D6E /* ASN1Decoder.swift */; }; - DCE6D58C2CF5F63100865D6E /* ASN1Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D57E2CF5F63100865D6E /* ASN1Object.swift */; }; - DCE6D58D2CF5F63100865D6E /* PKCS7.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D5802CF5F63100865D6E /* PKCS7.swift */; }; - DCE6D58E2CF5F63100865D6E /* OID.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D57F2CF5F63100865D6E /* OID.swift */; }; - DCE6D58F2CF5F63100865D6E /* X509Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D5812CF5F63100865D6E /* X509Certificate.swift */; }; - DCE6D5902CF5F63100865D6E /* ASN1Encoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D57C2CF5F63100865D6E /* ASN1Encoder.swift */; }; - DCE6D5912CF5F63100865D6E /* ASN1Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D57D2CF5F63100865D6E /* ASN1Identifier.swift */; }; - DCE6D5922CF5F63100865D6E /* X509ExtensionAltName.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D5832CF5F63100865D6E /* X509ExtensionAltName.swift */; }; - DCE6D5942CF5F65200865D6E /* BinaryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D5932CF5F65200865D6E /* BinaryReader.swift */; }; + DCE6D5772CF5F5D500865D6E /* WMTMachOReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6D5762CF5F5D500865D6E /* WMTMachOReader.swift */; }; EA294F3D29F6A07A00A0494E /* WMTOperationUIData.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA294F3C29F6A07A00A0494E /* WMTOperationUIData.swift */; }; EA44366A29F9294600DDEC1C /* WMTPostApprovaScreenReview.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA44366929F9294600DDEC1C /* WMTPostApprovaScreenReview.swift */; }; EA44366C29F9297100DDEC1C /* WMTPostApprovaScreenRedirect.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA44366B29F9297100DDEC1C /* WMTPostApprovaScreenRedirect.swift */; }; @@ -173,21 +159,7 @@ DCE660D024CEBECA00870E53 /* IntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationTests.swift; sourceTree = ""; }; DCE660D224CEF56400870E53 /* IntegrationProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationProxy.swift; sourceTree = ""; }; DCE6D5732CF5F46000865D6E /* WMTSignatureAPNSEnvironmentDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTSignatureAPNSEnvironmentDetector.swift; sourceTree = ""; }; - DCE6D5762CF5F5D500865D6E /* MachOReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MachOReader.swift; sourceTree = ""; }; - DCE6D5782CF5F5E400865D6E /* Entitlements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Entitlements.swift; sourceTree = ""; }; - DCE6D57A2CF5F63100865D6E /* ASN1Decoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1Decoder.swift; sourceTree = ""; }; - DCE6D57B2CF5F63100865D6E /* ASN1DistinguishedNames.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1DistinguishedNames.swift; sourceTree = ""; }; - DCE6D57C2CF5F63100865D6E /* ASN1Encoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1Encoder.swift; sourceTree = ""; }; - DCE6D57D2CF5F63100865D6E /* ASN1Identifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1Identifier.swift; sourceTree = ""; }; - DCE6D57E2CF5F63100865D6E /* ASN1Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1Object.swift; sourceTree = ""; }; - DCE6D57F2CF5F63100865D6E /* OID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OID.swift; sourceTree = ""; }; - DCE6D5802CF5F63100865D6E /* PKCS7.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PKCS7.swift; sourceTree = ""; }; - DCE6D5812CF5F63100865D6E /* X509Certificate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = X509Certificate.swift; sourceTree = ""; }; - DCE6D5822CF5F63100865D6E /* X509Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = X509Extension.swift; sourceTree = ""; }; - DCE6D5832CF5F63100865D6E /* X509ExtensionAltName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = X509ExtensionAltName.swift; sourceTree = ""; }; - DCE6D5842CF5F63100865D6E /* X509ExtensionClasses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = X509ExtensionClasses.swift; sourceTree = ""; }; - DCE6D5852CF5F63100865D6E /* X509PublicKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = X509PublicKey.swift; sourceTree = ""; }; - DCE6D5932CF5F65200865D6E /* BinaryReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryReader.swift; sourceTree = ""; }; + DCE6D5762CF5F5D500865D6E /* WMTMachOReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTMachOReader.swift; sourceTree = ""; }; EA294F3C29F6A07A00A0494E /* WMTOperationUIData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTOperationUIData.swift; sourceTree = ""; }; EA44366929F9294600DDEC1C /* WMTPostApprovaScreenReview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTPostApprovaScreenReview.swift; sourceTree = ""; }; EA44366B29F9297100DDEC1C /* WMTPostApprovaScreenRedirect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTPostApprovaScreenRedirect.swift; sourceTree = ""; }; @@ -274,6 +246,16 @@ path = UserOperation; sourceTree = ""; }; + DC3E529E2CF62891002621C1 /* ProvisioningUtils */ = { + isa = PBXGroup; + children = ( + DCAC559B2CE773E90070644A /* WMTProvisioningUtils.swift */, + DCE6D5732CF5F46000865D6E /* WMTSignatureAPNSEnvironmentDetector.swift */, + DCE6D5762CF5F5D500865D6E /* WMTMachOReader.swift */, + ); + path = ProvisioningUtils; + sourceTree = ""; + }; DC488034292282FF00DB844B /* Inbox */ = { isa = PBXGroup; children = ( @@ -380,8 +362,6 @@ DCC3420324E3DB310045D27D /* WMTPushParser.swift */, DC76D01B24531413009F2DFC /* Service */, DC81D1C7244F382800F80CD6 /* Model */, - DCE6D5732CF5F46000865D6E /* WMTSignatureAPNSEnvironmentDetector.swift */, - DCE6D5752CF5F5C900865D6E /* MachO */, ); path = Push; sourceTree = ""; @@ -397,12 +377,12 @@ DC81D1CE24502E0300F80CD6 /* Common */ = { isa = PBXGroup; children = ( + DC3E529E2CF62891002621C1 /* ProvisioningUtils */, BFEEB20A2937AD700047941D /* WMTCancellable.swift */, DC488030292282C900DB844B /* WMTService.swift */, DCC5CCCD244DB0AD004679AC /* WMTLogger.swift */, DC06D01E25AC74E400F2EA69 /* WMTLock.swift */, DC9511F826EA02C100FF40AD /* WPNIntegration.swift */, - DCAC559B2CE773E90070644A /* WMTProvisioningUtils.swift */, DCAC55BB2CEC954C0070644A /* WMTUtils.swift */, ); path = Common; @@ -483,36 +463,6 @@ path = Requests; sourceTree = ""; }; - DCE6D5752CF5F5C900865D6E /* MachO */ = { - isa = PBXGroup; - children = ( - DCE6D5862CF5F63100865D6E /* CertParser */, - DCE6D5932CF5F65200865D6E /* BinaryReader.swift */, - DCE6D5782CF5F5E400865D6E /* Entitlements.swift */, - DCE6D5762CF5F5D500865D6E /* MachOReader.swift */, - ); - path = MachO; - sourceTree = ""; - }; - DCE6D5862CF5F63100865D6E /* CertParser */ = { - isa = PBXGroup; - children = ( - DCE6D57A2CF5F63100865D6E /* ASN1Decoder.swift */, - DCE6D57B2CF5F63100865D6E /* ASN1DistinguishedNames.swift */, - DCE6D57C2CF5F63100865D6E /* ASN1Encoder.swift */, - DCE6D57D2CF5F63100865D6E /* ASN1Identifier.swift */, - DCE6D57E2CF5F63100865D6E /* ASN1Object.swift */, - DCE6D57F2CF5F63100865D6E /* OID.swift */, - DCE6D5802CF5F63100865D6E /* PKCS7.swift */, - DCE6D5812CF5F63100865D6E /* X509Certificate.swift */, - DCE6D5822CF5F63100865D6E /* X509Extension.swift */, - DCE6D5832CF5F63100865D6E /* X509ExtensionAltName.swift */, - DCE6D5842CF5F63100865D6E /* X509ExtensionClasses.swift */, - DCE6D5852CF5F63100865D6E /* X509PublicKey.swift */, - ); - path = CertParser; - sourceTree = ""; - }; EA6DDF0D29F8031F0011E234 /* Screens */ = { isa = PBXGroup; children = ( @@ -680,12 +630,10 @@ DC8CB202244DCBE2009DDAA3 /* WMTOperations.swift in Sources */, DC48803E292282FF00DB844B /* WMTInboxMessage.swift in Sources */, DCC5CCB52449F8E9004679AC /* WMTOperationAttributeAmount.swift in Sources */, - DCE6D5942CF5F65200865D6E /* BinaryReader.swift in Sources */, DCC5CCD6244DBB7F004679AC /* WMTPushRegistrationData.swift in Sources */, DC3D0B392480F886000DC4D9 /* WMTLocalOperation.swift in Sources */, DCD8B336246C1BAF00385F02 /* WMTRejectionReason.swift in Sources */, DCC5CCD8244DBBBD004679AC /* WMTAuthorizationData.swift in Sources */, - DCE6D5792CF5F5E400865D6E /* Entitlements.swift in Sources */, DCAC55BC2CEC954C0070644A /* WMTUtils.swift in Sources */, DC488040292282FF00DB844B /* WMTInboxCount.swift in Sources */, DCA43C6B29927C960059A163 /* WMTOperationAttributeAmountConversion.swift in Sources */, @@ -716,7 +664,7 @@ DCC5CCCE244DB0AD004679AC /* WMTLogger.swift in Sources */, DCC5CCAE2449F7AC004679AC /* WMTUserOperation.swift in Sources */, DC9511F926EA02C100FF40AD /* WPNIntegration.swift in Sources */, - DCE6D5772CF5F5D500865D6E /* MachOReader.swift in Sources */, + DCE6D5772CF5F5D500865D6E /* WMTMachOReader.swift in Sources */, DCC5CCBD2449F965004679AC /* WMTOperationAttributeHeading.swift in Sources */, DCAC559C2CE773E90070644A /* WMTProvisioningUtils.swift in Sources */, DC8CB206244DD007009DDAA3 /* WMTAllowedOperationSignature.swift in Sources */, @@ -724,18 +672,6 @@ BFEEB20529379C700047941D /* WMTInboxGetMessageDetail.swift in Sources */, EACAF7B02A126B7D0021CA54 /* WMTJsonValue.swift in Sources */, DCAB7BCA24580BAC0006989D /* WMTQROperation.swift in Sources */, - DCE6D5872CF5F63100865D6E /* X509PublicKey.swift in Sources */, - DCE6D5882CF5F63100865D6E /* X509Extension.swift in Sources */, - DCE6D5892CF5F63100865D6E /* ASN1DistinguishedNames.swift in Sources */, - DCE6D58A2CF5F63100865D6E /* X509ExtensionClasses.swift in Sources */, - DCE6D58B2CF5F63100865D6E /* ASN1Decoder.swift in Sources */, - DCE6D58C2CF5F63100865D6E /* ASN1Object.swift in Sources */, - DCE6D58D2CF5F63100865D6E /* PKCS7.swift in Sources */, - DCE6D58E2CF5F63100865D6E /* OID.swift in Sources */, - DCE6D58F2CF5F63100865D6E /* X509Certificate.swift in Sources */, - DCE6D5902CF5F63100865D6E /* ASN1Encoder.swift in Sources */, - DCE6D5912CF5F63100865D6E /* ASN1Identifier.swift in Sources */, - DCE6D5922CF5F63100865D6E /* X509ExtensionAltName.swift in Sources */, DCE6D5742CF5F46000865D6E /* WMTSignatureAPNSEnvironmentDetector.swift in Sources */, DCC5CCBF2449F981004679AC /* WMTOperationAttributePartyInfo.swift in Sources */, DC81D1CB244F451E00F80CD6 /* WMTPushImpl.swift in Sources */, diff --git a/WultraMobileTokenSDK/Push/MachO/MachOReader.swift b/WultraMobileTokenSDK/Common/ProvisioningUtils/WMTMachOReader.swift similarity index 67% rename from WultraMobileTokenSDK/Push/MachO/MachOReader.swift rename to WultraMobileTokenSDK/Common/ProvisioningUtils/WMTMachOReader.swift index d513c26..35f92ef 100644 --- a/WultraMobileTokenSDK/Push/MachO/MachOReader.swift +++ b/WultraMobileTokenSDK/Common/ProvisioningUtils/WMTMachOReader.swift @@ -18,12 +18,7 @@ import Foundation import MachO import CommonCrypto -internal struct MachOSignatureBlob { - let pkcs: PKCS7? - let entitlemens: Entitlements? -} - -internal class MachOReader { +class WMTMachOReader { private struct CSSuperBlob { var magic: UInt32 @@ -39,8 +34,6 @@ internal class MachOReader { private struct CSMagic { static let embeddedSignature: UInt32 = 0xfade0cc0 static let embeddedEntitlements: UInt32 = 0xfade7171 - static let blobWrapper: UInt32 = 0xfade0b01 - static let codeDirectory: UInt32 = 0xfade0c02 } private enum BinaryType { @@ -55,10 +48,10 @@ internal class MachOReader { case fat(header: FatHeaderData) } - private var blobs: [MachOSignatureBlob]! + private var entitlements = [WMTProvision.Entitlements]() - static func readSignatures(_ binaryPath: String) -> [MachOSignatureBlob]? { - MachOReader(binaryPath)?.blobs + static func readEntitlements(_ binaryPath: String) -> [WMTProvision.Entitlements]? { + WMTMachOReader(binaryPath)?.entitlements } private init?(_ binaryPath: String) { @@ -70,9 +63,11 @@ internal class MachOReader { case .singleArch(let headerInfo): let headerSize = headerInfo.headerSize let commandCount = headerInfo.commandCount - blobs = [readSignatureFromBinarySlice(binary: binary, headerOffset: headerSize, dataOffset: 0, cmdCount: commandCount)] + if let data = readEntitlementsFromBinarySlice(binary: binary, headerOffset: headerSize, dataOffset: 0, cmdCount: commandCount) { + entitlements.append(data) + } case .fat(let header): - blobs = readSignaturesFromFatBinary(binary: binary, architectureCount: header.archCount, startingAt: MemoryLayout.size) + entitlements.append(contentsOf: readEntitlementsFromFatBinary(binary: binary, architectureCount: header.archCount, startingAt: MemoryLayout.size)) default: return nil } @@ -101,8 +96,8 @@ internal class MachOReader { } } - private func readSignaturesFromFatBinary(binary: BinaryReader, architectureCount: Int, startingAt: Int) -> [MachOSignatureBlob] { - var blobs = [MachOSignatureBlob]() + private func readEntitlementsFromFatBinary(binary: BinaryReader, architectureCount: Int, startingAt: Int) -> [WMTProvision.Entitlements] { + var entitlements = [WMTProvision.Entitlements]() for i in 0...size) binary.seek(to: UInt64(offset)) @@ -112,32 +107,30 @@ internal class MachOReader { switch arch { case .singleArch(let headerInfo): let headerOffset = Int(fatArchOffset) + headerInfo.headerSize - blobs.append(readSignatureFromBinarySlice(binary: binary, headerOffset: headerOffset, dataOffset: fatArchOffset, cmdCount: headerInfo.commandCount)) + if let parsed = readEntitlementsFromBinarySlice(binary: binary, headerOffset: headerOffset, dataOffset: fatArchOffset, cmdCount: headerInfo.commandCount) { + entitlements.append(parsed) + } default: - blobs.append(MachOSignatureBlob(pkcs: nil, entitlemens: nil)) + break } } - return blobs + return entitlements } - private func readSignatureFromBinarySlice(binary: BinaryReader, headerOffset: Int, dataOffset: UInt32, cmdCount: Int) -> MachOSignatureBlob { + private func readEntitlementsFromBinarySlice(binary: BinaryReader, headerOffset: Int, dataOffset: UInt32, cmdCount: Int) -> WMTProvision.Entitlements? { binary.seek(to: UInt64(headerOffset)) - var blob: MachOSignatureBlob? for _ in 0...size))) } - return blob ?? MachOSignatureBlob(pkcs: nil, entitlemens: nil) + return nil } - private func readSignatureData(binary: BinaryReader, startingAt offset: UInt32) -> MachOSignatureBlob { - var pkcs: PKCS7? - var entitlements: Entitlements? + private func readEntitlementsData(binary: BinaryReader, startingAt offset: UInt32) -> WMTProvision.Entitlements? { binary.seek(to: UInt64(offset)) let metaBlob: CSSuperBlob = binary.read() if CFSwapInt32(metaBlob.magic) == CSMagic.embeddedSignature { @@ -153,15 +146,40 @@ internal class MachOReader { if blobMagic == CSMagic.embeddedEntitlements { let signatureLength = CFSwapInt32(binary.read()) let signatureData = binary.readData(ofLength: Int(signatureLength) - 8) - entitlements = Entitlements(signatureData) - } else if blobMagic == CSMagic.blobWrapper { - let blobLength = CFSwapInt32(binary.read()) - let blobData: Data = binary.readData(ofLength: Int(blobLength) - 8) - pkcs = try? PKCS7(data: blobData) + return try? PropertyListDecoder().decode(WMTProvision.Entitlements.self, from: signatureData) } } - } - return MachOSignatureBlob(pkcs: pkcs, entitlemens: entitlements) + return nil + } +} + +private class BinaryReader { + + private let handle: FileHandle + + init?(_ path: String) { + guard let binaryHandle = FileHandle(forReadingAtPath: path) else { + return nil + } + handle = binaryHandle + } + + var currentOffset: UInt64 { handle.offsetInFile } + + func seek(to offset: UInt64) { + handle.seek(toFileOffset: offset) + } + + func read() -> T { + handle.readData(ofLength: MemoryLayout.size).withUnsafeBytes({ $0.load(as: T.self) }) + } + + func readData(ofLength length: Int) -> Data { + handle.readData(ofLength: length) + } + + deinit { + handle.closeFile() } } diff --git a/WultraMobileTokenSDK/Common/WMTProvisioningUtils.swift b/WultraMobileTokenSDK/Common/ProvisioningUtils/WMTProvisioningUtils.swift similarity index 100% rename from WultraMobileTokenSDK/Common/WMTProvisioningUtils.swift rename to WultraMobileTokenSDK/Common/ProvisioningUtils/WMTProvisioningUtils.swift diff --git a/WultraMobileTokenSDK/Common/ProvisioningUtils/WMTSignatureAPNSEnvironmentDetector.swift b/WultraMobileTokenSDK/Common/ProvisioningUtils/WMTSignatureAPNSEnvironmentDetector.swift new file mode 100644 index 0000000..a788324 --- /dev/null +++ b/WultraMobileTokenSDK/Common/ProvisioningUtils/WMTSignatureAPNSEnvironmentDetector.swift @@ -0,0 +1,46 @@ +// +// Copyright 2024 Wultra s.r.o. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions +// and limitations under the License. +// + +import Foundation + +class WMTSignatureAPNSEnvironmentDetector { + + static func detectAPNSEnvironment() -> WMTProvision.Entitlements? { + + D.debug("Parsing main bundle signature to get APNS Environment.") + + guard let executableName = Bundle.main.infoDictionary?[kCFBundleExecutableKey as String] as? String else { + D.error("Could not read executable name from Info.plist") + return nil + } + guard let executablePath = Bundle.main.path(forResource: executableName, ofType: nil) else { + D.error("Could not find executable \(executableName)") + return nil + } + + guard let entitlements = WMTMachOReader.readEntitlements(executablePath) else { + D.error("Could not read entitlements from \(executablePath)") + return nil + } + + guard entitlements.isEmpty == false else { + D.info("Not entitlements found") + return nil + } + + return entitlements.first(where: { $0.apsEnvironment != nil }) + } +} diff --git a/WultraMobileTokenSDK/Push/MachO/BinaryReader.swift b/WultraMobileTokenSDK/Push/MachO/BinaryReader.swift deleted file mode 100644 index ef76547..0000000 --- a/WultraMobileTokenSDK/Push/MachO/BinaryReader.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright 2024 Wultra s.r.o. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions -// and limitations under the License. -// - -import Foundation - -internal class BinaryReader { - - private let handle: FileHandle - - init?(_ path: String) { - guard let binaryHandle = FileHandle(forReadingAtPath: path) else { - return nil - } - handle = binaryHandle - } - - var currentOffset: UInt64 { handle.offsetInFile } - - func seek(to offset: UInt64) { - handle.seek(toFileOffset: offset) - } - - func read() -> T { - handle.readData(ofLength: MemoryLayout.size).withUnsafeBytes({ $0.load(as: T.self) }) - } - - func readData(ofLength length: Int) -> Data { - handle.readData(ofLength: length) - } - - deinit { - handle.closeFile() - } -} diff --git a/WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Decoder.swift b/WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Decoder.swift deleted file mode 100644 index 83a38ab..0000000 --- a/WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Decoder.swift +++ /dev/null @@ -1,268 +0,0 @@ -// -// ASN1DERDecoder.swift -// -// Copyright © 2017 Filippo Maguolo. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation - -internal class ASN1DERDecoder { - - static func decode(data: Data) throws -> [ASN1Object] { - var iterator = data.makeIterator() - return try parse(iterator: &iterator) - } - - private static func parse(iterator: inout Data.Iterator) throws -> [ASN1Object] { - - var result: [ASN1Object] = [] - - while let nextValue = iterator.next() { - - let asn1obj = ASN1Object() - asn1obj.identifier = ASN1Identifier(rawValue: nextValue) - - if asn1obj.identifier!.isConstructed() { - - let contentData = try loadSubContent(iterator: &iterator) - - if contentData.isEmpty { - asn1obj.sub = try parse(iterator: &iterator) - } else { - var subIterator = contentData.makeIterator() - asn1obj.sub = try parse(iterator: &subIterator) - } - - asn1obj.value = nil - - asn1obj.rawValue = Data(contentData) - - for item in asn1obj.sub! { - item.parent = asn1obj - } - } else { - - if asn1obj.identifier!.typeClass() == .universal { - - var contentData = try loadSubContent(iterator: &iterator) - - asn1obj.rawValue = Data(contentData) - - // decode the content data with come more convenient format - - switch asn1obj.identifier!.tagNumber() { - - case .endOfContent: - return result - - case .boolean: - if let value = contentData.first { - asn1obj.value = value > 0 ? true : false - - } - - case .integer: - while contentData.first == 0 { - contentData.remove(at: 0) // remove not significant digit - } - asn1obj.value = contentData - - case .null: - asn1obj.value = nil - - case .objectIdentifier: - asn1obj.value = decodeOid(contentData: &contentData) - - case .utf8String, - .printableString, - .numericString, - .generalString, - .universalString, - .characterString, - .t61String: - - asn1obj.value = String(data: contentData, encoding: .utf8) - - case .bmpString: - asn1obj.value = String(data: contentData, encoding: .unicode) - - case .visibleString, - .ia5String: - - asn1obj.value = String(data: contentData, encoding: .ascii) - - case .utcTime: - asn1obj.value = dateFormatter(contentData: &contentData, - formats: ["yyMMddHHmmssZ", "yyMMddHHmmZ"]) - - case .generalizedTime: - asn1obj.value = dateFormatter(contentData: &contentData, - formats: ["yyyyMMddHHmmssZ"]) - - case .bitString: - if contentData.count > 0 { - _ = contentData.remove(at: 0) // unused bits - } - asn1obj.value = contentData - - case .octetString: - do { - var subIterator = contentData.makeIterator() - asn1obj.sub = try parse(iterator: &subIterator) - } catch { - if let str = String(data: contentData, encoding: .utf8) { - asn1obj.value = str - } else { - asn1obj.value = contentData - } - } - - default: - asn1obj.value = contentData - } - } else { - // custom/private tag - - let contentData = try loadSubContent(iterator: &iterator) - asn1obj.rawValue = Data(contentData) - - if let str = String(data: contentData, encoding: .utf8) { - asn1obj.value = str - } else { - asn1obj.value = contentData - } - } - } - result.append(asn1obj) - } - return result - } - - // Decode DER OID bytes to String with dot notation - static func decodeOid(contentData: inout Data) -> String? { - if contentData.isEmpty { - return nil - } - - var oid: String = "" - - let first = Int(contentData.remove(at: 0)) - oid.append("\(first / 40).\(first % 40)") - - var t = 0 - while contentData.count > 0 { - let n = Int(contentData.remove(at: 0)) - t = (t << 7) | (n & 0x7F) - if (n & 0x80) == 0 { - oid.append(".\(t)") - t = 0 - } - } - return oid - } - - private static func dateFormatter(contentData: inout Data, formats: [String]) -> Date? { - guard let str = String(data: contentData, encoding: .utf8) else { return nil } - for format in formats { - let fmt = DateFormatter() - fmt.locale = Locale(identifier: "en_US_POSIX") - fmt.dateFormat = format - if let dt = fmt.date(from: str) { - return dt - } - } - return nil - } -} - -internal enum ASN1Error: Error { - case parseError - case outOfBuffer -} - -internal extension Data { - func toIntValue() -> UInt64? { - if self.count > 8 { // check if suitable for UInt64 - return nil - } - - var value: UInt64 = 0 - for (index, byte) in self.enumerated() { - value += UInt64(byte) << UInt64(8*(count-index-1)) - } - return value - } -} - -internal extension Data { - var sequenceContent: Data { - var iterator = self.makeIterator() - _ = iterator.next() - do { - return try loadSubContent(iterator: &iterator) - } catch { - return self - } - } -} - -// Decode the number of bytes of the content -private func getContentLength(iterator: inout Data.Iterator) -> UInt64 { - let first = iterator.next() - - guard first != nil else { - return 0 - } - - if (first! & 0x80) != 0 { // long - let octetsToRead = first! - 0x80 - var data = Data() - for _ in 0.. Data { - - let len = getContentLength(iterator: &iterator) - - guard len < Int.max else { - return Data() - } - - var byteArray: [UInt8] = [] - - for _ in 0.. String { - var result = "" - let oidNames: [ASN1DistinguishedNames] = [ - .commonName, - .dnQualifier, - .serialNumber, - .givenName, - .surname, - .organizationalUnitName, - .organizationName, - .streetAddress, - .localityName, - .stateOrProvinceName, - .countryName, - .email - ] - for oidName in oidNames { - guard let oidBlock = block.findOid(oidName.oid) else { - continue - } - if !result.isEmpty { - // RFC allow "," or ";" and an optional additional space before and after - result.append(", ") - } - result.append(oidName.representation) - result.append("=") - if let value = oidBlock.parent?.sub?.last?.value as? String { - result.append(quote(string: value)) - } - } - return result - } - - class func quote(string: String) -> String { - let specialChar = ",+=\n<>#;\\" - if string.contains(where: { specialChar.contains($0) }) { - return "\"" + string + "\"" - } else { - return string - } - } -} diff --git a/WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Encoder.swift b/WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Encoder.swift deleted file mode 100644 index 174d4b9..0000000 --- a/WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Encoder.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// ASN1Encoder.swift -// ASN1Decoder -// -// Copyright © 2020 Filippo Maguolo. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation - -internal class ASN1DEREncoder { - - static func encodeSequence(content: Data) -> Data { - var encoded = Data() - encoded.append(ASN1Identifier.constructedTag | ASN1Identifier.TagNumber.sequence.rawValue) - encoded.append(contentLength(of: content.count)) - encoded.append(content) - return encoded - } - - private static func contentLength(of size: Int) -> Data { - if size >= 128 { - var lenBytes = byteArray(from: size) - while lenBytes.first == 0 { lenBytes.removeFirst() } - let len: UInt8 = 0x80 | UInt8(lenBytes.count) - return Data([len] + lenBytes) - } else { - return Data([UInt8(size)]) - } - } - - private static func byteArray(from value: T) -> [UInt8] where T: FixedWidthInteger { - return withUnsafeBytes(of: value.bigEndian, Array.init) - } - -} - -internal extension Data { - var derEncodedSequence: Data { - return ASN1DEREncoder.encodeSequence(content: self) - } -} diff --git a/WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Identifier.swift b/WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Identifier.swift deleted file mode 100644 index 269c466..0000000 --- a/WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Identifier.swift +++ /dev/null @@ -1,100 +0,0 @@ -// -// ASN1Identifier.swift -// -// Copyright © 2017 Filippo Maguolo. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation - -internal class ASN1Identifier: CustomStringConvertible { - - enum Class: UInt8 { - case universal = 0x00 - case application = 0x40 - case contextSpecific = 0x80 - case `private` = 0xC0 - } - - enum TagNumber: UInt8 { - case endOfContent = 0x00 - case boolean = 0x01 - case integer = 0x02 - case bitString = 0x03 - case octetString = 0x04 - case null = 0x05 - case objectIdentifier = 0x06 - case objectDescriptor = 0x07 - case external = 0x08 - case read = 0x09 - case enumerated = 0x0A - case embeddedPdv = 0x0B - case utf8String = 0x0C - case relativeOid = 0x0D - case sequence = 0x10 - case set = 0x11 - case numericString = 0x12 - case printableString = 0x13 - case t61String = 0x14 - case videotexString = 0x15 - case ia5String = 0x16 - case utcTime = 0x17 - case generalizedTime = 0x18 - case graphicString = 0x19 - case visibleString = 0x1A - case generalString = 0x1B - case universalString = 0x1C - case characterString = 0x1D - case bmpString = 0x1E - } - - static let constructedTag: UInt8 = 0x20 - - var rawValue: UInt8 - - init(rawValue: UInt8) { - self.rawValue = rawValue - } - - func typeClass() -> Class { - for tc in [Class.application, Class.contextSpecific, Class.private] where (rawValue & tc.rawValue) == tc.rawValue { - return tc - } - return .universal - } - - func isPrimitive() -> Bool { - return (rawValue & ASN1Identifier.constructedTag) == 0 - } - func isConstructed() -> Bool { - return (rawValue & ASN1Identifier.constructedTag) != 0 - } - - func tagNumber() -> TagNumber { - return TagNumber(rawValue: rawValue & 0x1F) ?? .endOfContent - } - - var description: String { - if typeClass() == .universal { - return String(describing: tagNumber()) - } else { - return "\(typeClass())(\(tagNumber().rawValue))" - } - } -} diff --git a/WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Object.swift b/WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Object.swift deleted file mode 100644 index 3f0b735..0000000 --- a/WultraMobileTokenSDK/Push/MachO/CertParser/ASN1Object.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// ASN1Object.swift -// -// Copyright © 2017 Filippo Maguolo. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation - -internal class ASN1Object: CustomStringConvertible { - - /// This property contains the DER encoded object - var rawValue: Data? - - /// This property contains the decoded Swift object whenever is possible - var value: Any? - - var identifier: ASN1Identifier? - - var sub: [ASN1Object]? - - weak var parent: ASN1Object? - - func sub(_ index: Int) -> ASN1Object? { - if let sub = self.sub, index >= 0, index < sub.count { - return sub[index] - } - return nil - } - - func subCount() -> Int { - return sub?.count ?? 0 - } - - func findOid(_ oid: OID) -> ASN1Object? { - return findOid(oid.rawValue) - } - - func findOid(_ oid: String) -> ASN1Object? { - for child in sub ?? [] { - if child.identifier?.tagNumber() == .objectIdentifier { - if child.value as? String == oid { - return child - } - } else { - if let result = child.findOid(oid) { - return result - } - } - } - return nil - } - - var description: String { - return printAsn1() - } - - var asString: String? { - if let string = value as? String { - return string - } - - for item in sub ?? [] { - if let string = item.asString { - return string - } - } - - return nil - } - - fileprivate func printAsn1(insets: String = "") -> String { - var output = insets - output.append(identifier?.description.uppercased() ?? "") - output.append(value != nil ? ": \(value!)": "") - if identifier?.typeClass() == .universal, identifier?.tagNumber() == .objectIdentifier { - if let oidName = OID.description(of: value as? String ?? "") { - output.append(" (\(oidName))") - } - } - output.append(sub != nil && sub!.count > 0 ? " {": "") - output.append("\n") - for item in sub ?? [] { - output.append(item.printAsn1(insets: insets + " ")) - } - output.append(sub != nil && sub!.count > 0 ? insets + "}\n": "") - return output - } -} diff --git a/WultraMobileTokenSDK/Push/MachO/CertParser/OID.swift b/WultraMobileTokenSDK/Push/MachO/CertParser/OID.swift deleted file mode 100644 index 6592e94..0000000 --- a/WultraMobileTokenSDK/Push/MachO/CertParser/OID.swift +++ /dev/null @@ -1,101 +0,0 @@ -// -// Copyright © 2017 Filippo Maguolo. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation - -internal enum OID: String { - case etsiQcsCompliance = "0.4.0.1862.1.1" - case etsiQcsRetentionPeriod = "0.4.0.1862.1.3" - case etsiQcsQcSSCD = "0.4.0.1862.1.4" - case dsa = "1.2.840.10040.4.1" - case ecPublicKey = "1.2.840.10045.2.1" - case prime256v1 = "1.2.840.10045.3.1.7" - case ecdsaWithSHA256 = "1.2.840.10045.4.3.2" - case ecdsaWithSHA512 = "1.2.840.10045.4.3.4" - case rsaEncryption = "1.2.840.113549.1.1.1" - case sha256WithRSAEncryption = "1.2.840.113549.1.1.11" - case md5WithRSAEncryption = "1.2.840.113549.1.1.4" - case sha1WithRSAEncryption = "1.2.840.113549.1.1.5" - - // Digest algorithms - case sha1 = "1.3.14.3.2.26" - case pkcsSha256 = "1.3.6.1.4.1.22554.1.2.1" - case sha2Family = "1.3.6.1.4.1.22554.1.2" - case sha3_244 = "2.16.840.1.101.3.4.2.7" - case sha3_256 = "2.16.840.1.101.3.4.2.8" - case sha3_384 = "2.16.840.1.101.3.4.2.9" - case md5 = "0.2.262.1.10.1.3.2" - - case pkcs7data = "1.2.840.113549.1.7.1" - case pkcs7signedData = "1.2.840.113549.1.7.2" - case pkcs7envelopedData = "1.2.840.113549.1.7.3" - case emailAddress = "1.2.840.113549.1.9.1" - case signingCertificateV2 = "1.2.840.113549.1.9.16.2.47" - case contentType = "1.2.840.113549.1.9.3" - case messageDigest = "1.2.840.113549.1.9.4" - case signingTime = "1.2.840.113549.1.9.5" - case certificateExtension = "1.3.6.1.4.1.11129.2.4.2" - case jurisdictionOfIncorporationSP = "1.3.6.1.4.1.311.60.2.1.2" - case jurisdictionOfIncorporationC = "1.3.6.1.4.1.311.60.2.1.3" - case authorityInfoAccess = "1.3.6.1.5.5.7.1.1" - case qcStatements = "1.3.6.1.5.5.7.1.3" - case cps = "1.3.6.1.5.5.7.2.1" - case unotice = "1.3.6.1.5.5.7.2.2" - case serverAuth = "1.3.6.1.5.5.7.3.1" - case clientAuth = "1.3.6.1.5.5.7.3.2" - case ocsp = "1.3.6.1.5.5.7.48.1" - case caIssuers = "1.3.6.1.5.5.7.48.2" - case dateOfBirth = "1.3.6.1.5.5.7.9.1" - case sha256 = "2.16.840.1.101.3.4.2.1" - case VeriSignEVpolicy = "2.16.840.1.113733.1.7.23.6" - case extendedValidation = "2.23.140.1.1" - case organizationValidated = "2.23.140.1.2.2" - case subjectKeyIdentifier = "2.5.29.14" - case keyUsage = "2.5.29.15" - case subjectAltName = "2.5.29.17" - case issuerAltName = "2.5.29.18" - case basicConstraints = "2.5.29.19" - case cRLDistributionPoints = "2.5.29.31" - case certificatePolicies = "2.5.29.32" - case authorityKeyIdentifier = "2.5.29.35" - case extKeyUsage = "2.5.29.37" - case subjectDirectoryAttributes = "2.5.29.9" - case organizationName = "2.5.4.10" - case organizationalUnitName = "2.5.4.11" - case businessCategory = "2.5.4.15" - case postalCode = "2.5.4.17" - case commonName = "2.5.4.3" - case surname = "2.5.4.4" - case givenName = "2.5.4.42" - case dnQualifier = "2.5.4.46" - case serialNumber = "2.5.4.5" - case countryName = "2.5.4.6" - case localityName = "2.5.4.7" - case stateOrProvinceName = "2.5.4.8" - case streetAddress = "2.5.4.9" - - static func description(of value: String) -> String? { - guard let oid = OID(rawValue: value) else { - return nil - } - return "\(oid)" - } -} diff --git a/WultraMobileTokenSDK/Push/MachO/CertParser/PKCS7.swift b/WultraMobileTokenSDK/Push/MachO/CertParser/PKCS7.swift deleted file mode 100644 index e46fbbe..0000000 --- a/WultraMobileTokenSDK/Push/MachO/CertParser/PKCS7.swift +++ /dev/null @@ -1,99 +0,0 @@ -// -// PKCS7.swift -// -// Copyright © 2017 Filippo Maguolo. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation - -internal class PKCS7 { - let derData: Data - let asn1: [ASN1Object] - let mainBlock: ASN1Object - - init(data: Data) throws { - derData = data - asn1 = try ASN1DERDecoder.decode(data: derData) - - guard let firstBlock = asn1.first, - let mainBlock = firstBlock.sub(1)?.sub(0) else { - throw PKCS7Error.parseError - } - - self.mainBlock = mainBlock - - guard firstBlock.sub(0)?.value as? String == OID.pkcs7signedData.rawValue else { - throw PKCS7Error.notSupported - } - } - - var digestAlgorithm: String? { - if let block = mainBlock.sub(1) { - return firstLeafValue(block: block) as? String - } - return nil - } - - var digestAlgorithmName: String? { - return OID.description(of: digestAlgorithm ?? "") ?? digestAlgorithm - } - - var certificate: X509Certificate? { - return mainBlock.sub(3)?.sub?.first.map { try? X509Certificate(asn1: $0) } ?? nil - } - - var certificates: [X509Certificate] { - return mainBlock.sub(3)?.sub?.compactMap { try? X509Certificate(asn1: $0) } ?? [] - } - - var data: Data? { - if let block = mainBlock.findOid(.pkcs7data) { - if let dataBlock = block.parent?.sub?.last { - var out = Data() - if let value = dataBlock.value as? Data { - out.append(value) - } else if dataBlock.value is String, let rawValue = dataBlock.rawValue { - out.append(rawValue) - } else { - for sub in dataBlock.sub ?? [] { - if let value = sub.value as? Data { - out.append(value) - } else if sub.value is String, let rawValue = sub.rawValue { - out.append(rawValue) - } else { - for sub2 in sub.sub ?? [] { - if let value = sub2.rawValue { - out.append(value) - } - } - } - } - } - return out.count > 0 ? out : nil - } - } - return nil - } -} - -internal enum PKCS7Error: Error { - case notSupported - case parseError -} diff --git a/WultraMobileTokenSDK/Push/MachO/CertParser/X509Certificate.swift b/WultraMobileTokenSDK/Push/MachO/CertParser/X509Certificate.swift deleted file mode 100644 index e6fbd3b..0000000 --- a/WultraMobileTokenSDK/Push/MachO/CertParser/X509Certificate.swift +++ /dev/null @@ -1,344 +0,0 @@ -// -// X509Certificate.swift -// -// Copyright © 2017 Filippo Maguolo. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation - -internal class X509Certificate: CustomStringConvertible { - - private let asn1: [ASN1Object] - private let block1: ASN1Object - let derData: Data? - - private static let beginPemBlock = "-----BEGIN CERTIFICATE-----" - private static let endPemBlock = "-----END CERTIFICATE-----" - - enum X509BlockPosition: Int { - case version = 0 - case serialNumber = 1 - case signatureAlg = 2 - case issuer = 3 - case dateValidity = 4 - case subject = 5 - case publicKey = 6 - case extensions = 7 - } - - convenience init(data: Data) throws { - if String(data: data, encoding: .utf8)?.contains(X509Certificate.beginPemBlock) ?? false { - try self.init(pem: data) - } else { - try self.init(der: data) - } - } - - init(der: Data) throws { - asn1 = try ASN1DERDecoder.decode(data: der) - guard asn1.count > 0, - let block1 = asn1.first?.sub(0) else { - throw ASN1Error.parseError - } - - self.block1 = block1 - self.derData = der - } - - convenience init(pem: Data) throws { - guard let derData = X509Certificate.decodeToDER(pem: pem) else { - throw ASN1Error.parseError - } - - try self.init(der: derData) - } - - init(asn1: ASN1Object) throws { - guard let block1 = asn1.sub(0) else { throw ASN1Error.parseError } - - self.asn1 = [asn1] - self.block1 = block1 - if let rawValue = asn1.rawValue { - self.derData = ASN1DEREncoder.encodeSequence(content: rawValue) - } else { - self.derData = nil - } - - } - - var description: String { - return asn1.reduce("") { $0 + "\($1.description)\n" } - } - - /// Checks that the given date is within the certificate's validity period. - func checkValidity(_ date: Date = Date()) -> Bool { - if let notBefore = notBefore, let notAfter = notAfter { - return date > notBefore && date < notAfter - } - return false - } - - /// Gets the version (version number) value from the certificate. - var version: Int? { - if let v = firstLeafValue(block: block1) as? Data, let index = v.toIntValue() { - return Int(index) + 1 - } - return nil - } - - /// Gets the serialNumber value from the certificate. - var serialNumber: Data? { - return block1[X509BlockPosition.serialNumber]?.value as? Data - } - - /// Returns the issuer (issuer distinguished name) value from the certificate as a String. - var issuerDistinguishedName: String? { - if let issuerBlock = block1[X509BlockPosition.issuer] { - return ASN1DistinguishedNames.string(from: issuerBlock) - } - return nil - } - - var issuerOIDs: [String] { - var result: [String] = [] - if let subjectBlock = block1[X509BlockPosition.issuer] { - for sub in subjectBlock.sub ?? [] { - if let value = firstLeafValue(block: sub) as? String { - result.append(value) - } - } - } - return result - } - - func issuer(oid: String) -> String? { - if let subjectBlock = block1[X509BlockPosition.issuer] { - if let oidBlock = subjectBlock.findOid(oid) { - return oidBlock.parent?.sub?.last?.value as? String - } - } - return nil - } - - func issuer(dn: ASN1DistinguishedNames) -> String? { - return issuer(oid: dn.oid) - } - - /// Returns the subject (subject distinguished name) value from the certificate as a String. - var subjectDistinguishedName: String? { - if let subjectBlock = block1[X509BlockPosition.subject] { - return ASN1DistinguishedNames.string(from: subjectBlock) - } - return nil - } - - var subjectOIDs: [String] { - var result: [String] = [] - if let subjectBlock = block1[X509BlockPosition.subject] { - for sub in subjectBlock.sub ?? [] { - if let value = firstLeafValue(block: sub) as? String { - result.append(value) - } - } - } - return result - } - - func subject(oid: String) -> String? { - if let subjectBlock = block1[X509BlockPosition.subject] { - if let oidBlock = subjectBlock.findOid(oid) { - return oidBlock.parent?.sub?.last?.value as? String - } - } - return nil - } - - func subject(dn: ASN1DistinguishedNames) -> String? { - return subject(oid: dn.oid) - } - - /// Gets the notBefore date from the validity period of the certificate. - var notBefore: Date? { - return block1[X509BlockPosition.dateValidity]?.sub(0)?.value as? Date - } - - /// Gets the notAfter date from the validity period of the certificate. - var notAfter: Date? { - return block1[X509BlockPosition.dateValidity]?.sub(1)?.value as? Date - } - - /// Gets the signature value (the raw signature bits) from the certificate. - var signature: Data? { - return asn1[0].sub(2)?.value as? Data - } - - /// Gets the signature algorithm name for the certificate signature algorithm. - var sigAlgName: String? { - return OID.description(of: sigAlgOID ?? "") - } - - /// Gets the signature algorithm OID string from the certificate. - var sigAlgOID: String? { - return block1.sub(2)?.sub(0)?.value as? String - } - - /// Gets the DER-encoded signature algorithm parameters from this certificate's signature algorithm. - var sigAlgParams: Data? { - return nil - } - - /** - Gets a boolean array representing bits of the KeyUsage extension, (OID = 2.5.29.15). - ``` - KeyUsage ::= BIT STRING { - digitalSignature (0), - nonRepudiation (1), - keyEncipherment (2), - dataEncipherment (3), - keyAgreement (4), - keyCertSign (5), - cRLSign (6), - encipherOnly (7), - decipherOnly (8) - } - ``` - */ - var keyUsage: [Bool] { - var result: [Bool] = [] - if let oidBlock = block1.findOid(OID.keyUsage) { - let data = oidBlock.parent?.sub?.last?.sub(0)?.value as? Data - let bits: UInt8 = data?.first ?? 0 - for index in 0...7 { - let value = bits & UInt8(1 << index) != 0 - result.insert(value, at: 0) - } - } - return result - } - - /// Gets a list of Strings representing the OBJECT IDENTIFIERs of the ExtKeyUsageSyntax field of - /// the extended key usage extension, (OID = 2.5.29.37). - var extendedKeyUsage: [String] { - return extensionObject(oid: OID.extKeyUsage)?.valueAsStrings ?? [] - } - - /// Gets a collection of subject alternative names from the SubjectAltName extension, (OID = 2.5.29.17). - var subjectAlternativeNames: [String] { - return extensionObject(oid: OID.subjectAltName)?.alternativeNameAsStrings ?? [] - } - - /// Gets a collection of issuer alternative names from the IssuerAltName extension, (OID = 2.5.29.18). - var issuerAlternativeNames: [String] { - return extensionObject(oid: OID.issuerAltName)?.alternativeNameAsStrings ?? [] - } - - /// Gets the informations of the key from this certificate. - var publicKey: X509PublicKey? { - return block1[X509BlockPosition.publicKey].map(X509PublicKey.init) - } - - /// Get a list of critical extension OID codes - var criticalExtensionOIDs: [String] { - guard let extensionBlocks = extensionBlocks else { return [] } - return extensionBlocks - .map { X509Extension(block: $0) } - .filter { $0.isCritical } - .compactMap { $0.oid } - } - - /// Get a list of non critical extension OID codes - var nonCriticalExtensionOIDs: [String] { - guard let extensionBlocks = extensionBlocks else { return [] } - return extensionBlocks - .map { X509Extension(block: $0) } - .filter { !$0.isCritical } - .compactMap { $0.oid } - } - - private var extensionBlocks: [ASN1Object]? { - return block1[X509BlockPosition.extensions]?.sub(0)?.sub - } - - /// Gets the extension information of the given OID enum. - func extensionObject(oid: OID) -> X509Extension? { - return extensionObject(oid: oid.rawValue) - } - - /// Gets the extension information of the given OID code. - func extensionObject(oid: String) -> X509Extension? { - return block1[X509BlockPosition.extensions]? - .findOid(oid)? - .parent - .map { oidExtensionMap[oid]?.init(block: $0) ?? X509Extension(block: $0) } - } - - // Association of Class decoding helper and OID - private let oidExtensionMap: [String: X509Extension.Type] = [ - OID.basicConstraints.rawValue: BasicConstraintExtension.self, - OID.subjectKeyIdentifier.rawValue: SubjectKeyIdentifierExtension.self, - OID.authorityInfoAccess.rawValue: AuthorityInfoAccessExtension.self, - OID.authorityKeyIdentifier.rawValue: AuthorityKeyIdentifierExtension.self, - OID.certificatePolicies.rawValue: CertificatePoliciesExtension.self, - OID.cRLDistributionPoints.rawValue: CRLDistributionPointsExtension.self - ] - - // read possibile PEM encoding - private static func decodeToDER(pem pemData: Data) -> Data? { - if - let pem = String(data: pemData, encoding: .ascii), - pem.contains(beginPemBlock) { - - let lines = pem.components(separatedBy: .newlines) - var base64buffer = "" - var certLine = false - for line in lines { - if line == endPemBlock { - certLine = false - } - if certLine { - base64buffer.append(line) - } - if line == beginPemBlock { - certLine = true - } - } - if let derDataDecoded = Data(base64Encoded: base64buffer) { - return derDataDecoded - } - } - - return nil - } -} - -internal func firstLeafValue(block: ASN1Object) -> Any? { - if let sub = block.sub?.first { - return firstLeafValue(block: sub) - } - return block.value -} - -internal extension ASN1Object { - subscript(index: X509Certificate.X509BlockPosition) -> ASN1Object? { - guard let sub = sub, - sub.indices.contains(index.rawValue) else { return nil } - return sub[index.rawValue] - } -} diff --git a/WultraMobileTokenSDK/Push/MachO/CertParser/X509Extension.swift b/WultraMobileTokenSDK/Push/MachO/CertParser/X509Extension.swift deleted file mode 100644 index 7ce6d7a..0000000 --- a/WultraMobileTokenSDK/Push/MachO/CertParser/X509Extension.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// X509Extension.swift -// -// Copyright © 2019 Filippo Maguolo. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation - -internal class X509Extension { - - let block: ASN1Object - - required init(block: ASN1Object) { - self.block = block - } - - var oid: String? { - return block.sub(0)?.value as? String - } - - var name: String? { - return OID.description(of: oid ?? "") - } - - var isCritical: Bool { - if block.sub?.count ?? 0 > 2 { - return block.sub(1)?.value as? Bool ?? false - } - return false - } - - var value: Any? { - if let valueBlock = block.sub?.last { - return firstLeafValue(block: valueBlock) - } - return nil - } - - var valueAsBlock: ASN1Object? { - return block.sub?.last - } - - var valueAsStrings: [String] { - var result: [String] = [] - for item in block.sub?.last?.sub?.last?.sub ?? [] { - if let name = item.value as? String { - result.append(name) - } - } - return result - } -} diff --git a/WultraMobileTokenSDK/Push/MachO/CertParser/X509ExtensionAltName.swift b/WultraMobileTokenSDK/Push/MachO/CertParser/X509ExtensionAltName.swift deleted file mode 100644 index 442f708..0000000 --- a/WultraMobileTokenSDK/Push/MachO/CertParser/X509ExtensionAltName.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// X509ExtensionAltName.swift -// -// Copyright © 2020 Filippo Maguolo. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation - -internal extension X509Extension { - - // Used for SubjectAltName and IssuerAltName - // Every name can be one of these subtype: - // - otherName [0] INSTANCE OF OTHER-NAME, - // - rfc822Name [1] IA5String, - // - dNSName [2] IA5String, - // - x400Address [3] ORAddress, - // - directoryName [4] Name, - // - ediPartyName [5] EDIPartyName, - // - uniformResourceIdentifier [6] IA5String, - // - IPAddress [7] OCTET STRING, - // - registeredID [8] OBJECT IDENTIFIER - // - // Result does not support: x400Address and ediPartyName - // - var alternativeNameAsStrings: [String] { - var result: [String] = [] - for item in block.sub?.last?.sub?.last?.sub ?? [] { - guard let name = generalName(of: item) else { - continue - } - result.append(name) - } - return result - } - - func generalName(of item: ASN1Object) -> String? { - guard let nameType = item.identifier?.tagNumber().rawValue else { - return nil - } - switch nameType { - case 0: - if let name = item.sub?.last?.sub?.last?.value as? String { - return name - } - case 1, 2, 6: - if let name = item.value as? String { - return name - } - case 4: - return ASN1DistinguishedNames.string(from: item) - case 7: - if let ip = item.value as? Data { - return ip.map({ "\($0)" }).joined(separator: ".") - } - case 8: - if let value = item.value as? String, var data = value.data(using: .utf8) { - let oid = ASN1DERDecoder.decodeOid(contentData: &data) - return oid - } - default: - return nil - } - return nil - } -} diff --git a/WultraMobileTokenSDK/Push/MachO/CertParser/X509ExtensionClasses.swift b/WultraMobileTokenSDK/Push/MachO/CertParser/X509ExtensionClasses.swift deleted file mode 100644 index f2482c1..0000000 --- a/WultraMobileTokenSDK/Push/MachO/CertParser/X509ExtensionClasses.swift +++ /dev/null @@ -1,181 +0,0 @@ -// -// X509ExtensionClasses.swift -// -// Copyright © 2020 Filippo Maguolo. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation - -internal extension X509Certificate { - - /// Recognition for Basic Constraint Extension (2.5.29.19) - class BasicConstraintExtension: X509Extension { - - var isCA: Bool { - return (valueAsBlock?.sub(0)?.sub(0)?.value as? Bool) ?? false - } - - var pathLenConstraint: UInt64? { - guard let data = valueAsBlock?.sub(0)?.sub(1)?.value as? Data else { - return nil - } - return data.toIntValue() - } - } - - /// Recognition for Subject Key Identifier Extension (2.5.29.14) - class SubjectKeyIdentifierExtension: X509Extension { - - override var value: Any? { - guard let rawValue = valueAsBlock?.rawValue else { - return nil - } - return rawValue.sequenceContent - } - } - - // MARK: - Authority Extensions - - struct AuthorityInfoAccess { - let method: String - let location: String - } - - /// Recognition for Authority Info Access Extension (1.3.6.1.5.5.7.1.1) - class AuthorityInfoAccessExtension: X509Extension { - - var infoAccess: [AuthorityInfoAccess]? { - guard let valueAsBlock = valueAsBlock else { - return nil - } - let subs = valueAsBlock.sub(0)?.sub ?? [] - - return subs.compactMap { sub in - guard var oidData = sub.sub(0)?.rawValue, - let nameBlock = sub.sub(1) else { - return nil - } - if - let oid = ASN1DERDecoder.decodeOid(contentData: &oidData), - let location = generalName(of: nameBlock) { - return AuthorityInfoAccess(method: oid, location: location) - } else { - return nil - } - } - } - } - - /// Recognition for Authority Key Identifier Extension (2.5.29.35) - class AuthorityKeyIdentifierExtension: X509Extension { - - /* - AuthorityKeyIdentifier ::= SEQUENCE { - keyIdentifier [0] KeyIdentifier OPTIONAL, - authorityCertIssuer [1] GeneralNames OPTIONAL, - authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } - */ - - var keyIdentifier: Data? { - guard let sequence = valueAsBlock?.sub(0)?.sub else { - return nil - } - if let sub = sequence.first(where: { $0.identifier?.tagNumber().rawValue == 0 }) { - return sub.rawValue - } - return nil - } - - var certificateIssuer: [String]? { - guard let sequence = valueAsBlock?.sub(0)?.sub else { - return nil - } - if let sub = sequence.first(where: { $0.identifier?.tagNumber().rawValue == 1 }) { - return sub.sub?.compactMap { generalName(of: $0) } - } - return nil - } - - var serialNumber: Data? { - guard let sequence = valueAsBlock?.sub(0)?.sub else { - return nil - } - if let sub = sequence.first(where: { $0.identifier?.tagNumber().rawValue == 2 }) { - return sub.rawValue - } - return nil - } - } - - // MARK: - Certificate Policies Extension - - struct CertificatePolicyQualifier { - let oid: String - let value: String? - } - struct CertificatePolicy { - let oid: String - let qualifiers: [CertificatePolicyQualifier]? - } - - /// Recognition for Certificate Policies Extension (2.5.29.32) - class CertificatePoliciesExtension: X509Extension { - - var policies: [CertificatePolicy]? { - guard let valueAsBlock = valueAsBlock else { - return nil - } - let subs = valueAsBlock.sub(0)?.sub ?? [] - - return subs.compactMap { sub in - guard - var data = sub.sub(0)?.rawValue, - let oid = ASN1DERDecoder.decodeOid(contentData: &data) else { - return nil - } - var qualifiers: [CertificatePolicyQualifier]? - if let subQualifiers = sub.sub(1) { - qualifiers = subQualifiers.sub?.compactMap { sub in - if var rawValue = sub.sub(0)?.rawValue, let oid = ASN1DERDecoder.decodeOid(contentData: &rawValue) { - let value = sub.sub(1)?.asString - return CertificatePolicyQualifier(oid: oid, value: value) - } else { - return nil - } - } - } - return CertificatePolicy(oid: oid, qualifiers: qualifiers) - } - } - } - - // MARK: - CRL Distribution Points - - class CRLDistributionPointsExtension: X509Extension { - - var crls: [String]? { - guard let valueAsBlock = valueAsBlock else { - return nil - } - let subs = valueAsBlock.sub(0)?.sub ?? [] - return subs.compactMap { $0.asString } - } - } -} diff --git a/WultraMobileTokenSDK/Push/MachO/CertParser/X509PublicKey.swift b/WultraMobileTokenSDK/Push/MachO/CertParser/X509PublicKey.swift deleted file mode 100644 index 48a1fa2..0000000 --- a/WultraMobileTokenSDK/Push/MachO/CertParser/X509PublicKey.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// X509PublicKey.swift -// -// Copyright © 2019 Filippo Maguolo. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation - -internal class X509PublicKey { - - let pkBlock: ASN1Object - - init(pkBlock: ASN1Object) { - self.pkBlock = pkBlock - } - - var algOid: String? { - return pkBlock.sub(0)?.sub(0)?.value as? String - } - - var algName: String? { - return OID.description(of: algOid ?? "") - } - - var algParams: String? { - return pkBlock.sub(0)?.sub(1)?.value as? String - } - - var derEncodedKey: Data? { - return pkBlock.rawValue?.derEncodedSequence - } - - var key: Data? { - guard - let algOid = algOid, - let oid = OID(rawValue: algOid), - let keyData = pkBlock.sub(1)?.value as? Data else { - return nil - } - - switch oid { - case .ecPublicKey: - return keyData - - case .rsaEncryption: - guard let publicKeyAsn1Objects = (try? ASN1DERDecoder.decode(data: keyData)) else { - return nil - } - guard let publicKeyModulus = publicKeyAsn1Objects.first?.sub(0)?.value as? Data else { - return nil - } - return publicKeyModulus - - default: - return nil - } - } -} diff --git a/WultraMobileTokenSDK/Push/MachO/Entitlements.swift b/WultraMobileTokenSDK/Push/MachO/Entitlements.swift deleted file mode 100644 index 5c63e2d..0000000 --- a/WultraMobileTokenSDK/Push/MachO/Entitlements.swift +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright 2024 Wultra s.r.o. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions -// and limitations under the License. -// - -import Foundation - -internal class Entitlements { - - enum Key: String { - case autofillCredentialProvider = "com.apple.developer.authentication-services.autofill-credential-provider" - case signWithApple = "com.apple.developer.applesignin" - case contacts = "com.apple.developer.contacts.notes" - case classKit = "com.apple.developer.ClassKit-environment" - case automaticAssesmentConfiguration = "com.apple.developer.automatic-assessment-configuration" - case gameCenter = "com.apple.developer.game-center" - case healthKit = "com.apple.developer.healthkit" - case healthKitCapabilities = "com.apple.developer.healthkit.access" - case homeKit = "com.apple.developer.homekit" - case iCloudDevelopmentContainersIdentifiers = "com.apple.developer.icloud-container-development-container-identifiers" - case iCloudContainersEnvironment = "com.apple.developer.icloud-container-environment" - case iCloudContainerIdentifiers = "com.apple.developer.icloud-container-identifiers" - case iCloudServices = "com.apple.developer.icloud-services" - case iCloudKeyValueStore = "com.apple.developer.ubiquity-kvstore-identifier" - case interAppAudio = "inter-app-audio" - case networkExtensions = "com.apple.developer.networking.networkextension" - case personalVPN = "com.apple.developer.networking.vpn.api" - case apsEnvironment = "aps-environment" - case appGroups = "com.apple.security.application-groups" - case keychainAccessGroups = "keychain-access-groups" - case dataProtection = "com.apple.developer.default-data-protection" - case siri = "com.apple.developer.siri" - case passTypeIDs = "com.apple.developer.pass-type-identifiers" - case merchantIDs = "com.apple.developer.in-app-payments" - case wifiInfo = "com.apple.developer.networking.wifi-info" - case externalAccessoryConfiguration = "com.apple.external-accessory.wireless-configuration" - case multipath = "com.apple.developer.networking.multipath" - case hotspotConfiguration = "com.apple.developer.networking.HotspotConfiguration" - case nfcTagReaderSessionFormats = "com.apple.developer.nfc.readersession.formats" - case associatedDomains = "com.apple.developer.associated-domains" - case maps = "com.apple.developer.maps" - case driverKit = "com.apple.developer.driverkit.transport.pci" - } - - private let values: [String: Any] - - init?(_ data: Data) { - guard let rawValues = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [String: Any] else { - return nil - } - self.values = rawValues - } - - func value(forKey key: Entitlements.Key) -> Any? { - values[key.rawValue] - } -} diff --git a/WultraMobileTokenSDK/Push/Model/WMTPushRegistrationData.swift b/WultraMobileTokenSDK/Push/Model/WMTPushRegistrationData.swift index 1dd088f..cc18379 100644 --- a/WultraMobileTokenSDK/Push/Model/WMTPushRegistrationData.swift +++ b/WultraMobileTokenSDK/Push/Model/WMTPushRegistrationData.swift @@ -41,15 +41,13 @@ enum WMTPushRegistrationEnvironment: String, Codable { case development } -extension WMTProvision { - var apnsEnvironment: WMTPushRegistrationEnvironment? { - switch entitlements.apsEnvironment { +extension WMTProvision.Entitlements.Environment { + var serverObject: WMTPushRegistrationEnvironment { + switch self { case .development: return .development case .production: return .production - case nil: - return nil } } } diff --git a/WultraMobileTokenSDK/Push/Service/WMTPushImpl.swift b/WultraMobileTokenSDK/Push/Service/WMTPushImpl.swift index 4a6eba4..a460d05 100644 --- a/WultraMobileTokenSDK/Push/Service/WMTPushImpl.swift +++ b/WultraMobileTokenSDK/Push/Service/WMTPushImpl.swift @@ -131,13 +131,13 @@ class WMTPushImpl: WMTPush, WMTService { D.info("Using APNS production environment for push notifications.") return .production case .automatic: - let env = WMTProvisioningUtils.getMainProvisioningProfile()?.apnsEnvironment ?? WMTSignatureAPNSEnvironmentDetector.detectAPNSEnvironment() + let env = WMTProvisioningUtils.getMainProvisioningProfile()?.entitlements.apsEnvironment ?? WMTSignatureAPNSEnvironmentDetector.detectAPNSEnvironment()?.apsEnvironment if let env { D.info("Using \(env) environment for push notifications (automatic resolution).") } else { D.warning("No APNS environment found in provisioning profile. Server configuration will be used.") } - return env + return env?.serverObject } } } diff --git a/WultraMobileTokenSDK/Push/WMTSignatureAPNSEnvironmentDetector.swift b/WultraMobileTokenSDK/Push/WMTSignatureAPNSEnvironmentDetector.swift deleted file mode 100644 index 80aaddd..0000000 --- a/WultraMobileTokenSDK/Push/WMTSignatureAPNSEnvironmentDetector.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// Copyright 2024 Wultra s.r.o. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions -// and limitations under the License. -// - -import Foundation - -class WMTSignatureAPNSEnvironmentDetector { - - static func detectAPNSEnvironment() -> WMTPushRegistrationEnvironment? { - - D.debug("Parsing main bundle signature to get APNS Environment.") - - guard let executableName = Bundle.main.infoDictionary?[kCFBundleExecutableKey as String] as? String else { - D.error("Could not read executable name from Info.plist") - return nil - } - guard let executablePath = Bundle.main.path(forResource: executableName, ofType: nil) else { - D.error("Could not find executable \(executableName)") - return nil - } - - guard let cmsList = MachOReader.readSignatures(executablePath) else { - D.error("Could not read signatures from \(executablePath)") - return nil - } - - guard cmsList.isEmpty == false else { - D.info("Executable is not signed.") - return nil - } - - for cms in cmsList { - - if let apnsEnvironment = cms.entitlemens?.value(forKey: .apsEnvironment) as? String { - switch apnsEnvironment { - case "development": - D.debug("Development entitlement found in the app signature") - return .development - case "production": - D.debug("Production entitlement found in the app signature") - return .production - default: - D.debug("Unknown entitlement found in the app signature: \(apnsEnvironment)") - } - } - - guard let pkcs = cms.pkcs else { - D.error("Could not read CMS signature") - return nil - } - - guard pkcs.certificates.count == 3 else { - D.error("Invalid CMS signature count") - return nil - } - - if pkcs.certificates.contains(where: { $0.subjectDistinguishedName == KnownCertificates.appleICAname }) && pkcs.certificates.contains(where: { $0.subjectDistinguishedName == KnownCertificates.appleOASname }) { - D.debug("App is signed with Apple's iOS Development Certificate - assuming production APNS environment") - return .production - } - - if pkcs.certificates.contains(where: { $0.subjectDistinguishedName == KnownCertificates.appleTFBname }) { - D.debug("App is signed with Apple's iOS TestFlight Beta Certificate - assuming production APNS environment") - return .production - } - } - - return nil - } -} - -public struct KnownCertificates { - static let appleICAname = "CN=Apple iPhone Certification Authority, OU=Certification Authority, O=Apple Inc., C=US" - static let appleOASname = "CN=Apple iPhone OS Application Signing, OU=iPhone, O=Apple Inc., C=US" - static let appleTFBname = "CN=TestFlight Beta Distribution, OU=TESTFLIGHT, O=Apple Inc., C=US" -}