Skip to content

Commit

Permalink
Push Notifications improvements (#178)
Browse files Browse the repository at this point in the history
  • Loading branch information
kober32 committed Nov 27, 2024
1 parent 5b0da64 commit 41bf25b
Show file tree
Hide file tree
Showing 17 changed files with 823 additions and 99 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ jobs:
OP_URL: ${{ secrets.TESTS_OP_URL }}
ER_URL: ${{ secrets.TESTS_ER_URL }}
IN_URL: ${{ secrets.TESTS_IN_URL }}
run: ./scripts/test.sh -destination "platform=iOS Simulator,OS=17.0.1,name=iPhone 15" -sdkconfig "$SDK_CONFIG" -er "$ER_URL" -op "$OP_URL" -in "$IN_URL" -cl "$CL_URL" -clu "$CL_LGN" -clp "$CL_PWD" -cla "$CL_AID"
PU_URL: ${{ secrets.TESTS_PU_URL }}
run: ./scripts/test.sh -destination "platform=iOS Simulator,OS=17.0.1,name=iPhone 15" -sdkconfig "$SDK_CONFIG" -er "$ER_URL" -op "$OP_URL" -in "$IN_URL" -cl "$CL_URL" -clu "$CL_LGN" -clp "$CL_PWD" -cla "$CL_AID" -pu "$PU_URL"
28 changes: 28 additions & 0 deletions WultraMobileTokenSDK.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
DCA43C6D2993F63E0059A163 /* WMTOperationAttributeImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA43C6C2993F63E0059A163 /* WMTOperationAttributeImage.swift */; };
DCAB7BC824580B4C0006989D /* WMTQROperationParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAB7BC724580B4C0006989D /* WMTQROperationParser.swift */; };
DCAB7BCA24580BAC0006989D /* WMTQROperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAB7BC924580BAC0006989D /* WMTQROperation.swift */; };
DCAC55992CE68C2A0070644A /* ProvisioningUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAC55982CE68C2A0070644A /* ProvisioningUtilsTests.swift */; };
DCAC559C2CE773E90070644A /* WMTProvisioningUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAC559B2CE773E90070644A /* WMTProvisioningUtils.swift */; };
DCAC55BC2CEC954C0070644A /* WMTUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAC55BB2CEC954C0070644A /* WMTUtils.swift */; };
DCC3420424E3DB310045D27D /* WMTPushParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC3420324E3DB310045D27D /* WMTPushParser.swift */; };
DCC5CC9F2449EE21004679AC /* MobileTokenSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = DCC5CC9D2449EE21004679AC /* MobileTokenSDK.h */; settings = {ATTRIBUTES = (Public, ); }; };
DCC5CCAC2449F765004679AC /* WMTOperationsImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC5CCAB2449F765004679AC /* WMTOperationsImpl.swift */; };
Expand All @@ -63,6 +66,8 @@
DCD8B336246C1BAF00385F02 /* WMTRejectionReason.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD8B335246C1BAF00385F02 /* WMTRejectionReason.swift */; };
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 /* 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 */; };
Expand Down Expand Up @@ -126,6 +131,9 @@
DCA43C6C2993F63E0059A163 /* WMTOperationAttributeImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTOperationAttributeImage.swift; sourceTree = "<group>"; };
DCAB7BC724580B4C0006989D /* WMTQROperationParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTQROperationParser.swift; sourceTree = "<group>"; };
DCAB7BC924580BAC0006989D /* WMTQROperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTQROperation.swift; sourceTree = "<group>"; };
DCAC55982CE68C2A0070644A /* ProvisioningUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProvisioningUtilsTests.swift; sourceTree = "<group>"; };
DCAC559B2CE773E90070644A /* WMTProvisioningUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTProvisioningUtils.swift; sourceTree = "<group>"; };
DCAC55BB2CEC954C0070644A /* WMTUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTUtils.swift; sourceTree = "<group>"; };
DCC3420324E3DB310045D27D /* WMTPushParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTPushParser.swift; sourceTree = "<group>"; };
DCC5CC9A2449EE21004679AC /* WultraMobileTokenSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WultraMobileTokenSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DCC5CC9D2449EE21004679AC /* MobileTokenSDK.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MobileTokenSDK.h; sourceTree = "<group>"; };
Expand All @@ -150,6 +158,8 @@
DCD8B335246C1BAF00385F02 /* WMTRejectionReason.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTRejectionReason.swift; sourceTree = "<group>"; };
DCE660D024CEBECA00870E53 /* IntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationTests.swift; sourceTree = "<group>"; };
DCE660D224CEF56400870E53 /* IntegrationProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationProxy.swift; sourceTree = "<group>"; };
DCE6D5732CF5F46000865D6E /* WMTSignatureAPNSEnvironmentDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTSignatureAPNSEnvironmentDetector.swift; sourceTree = "<group>"; };
DCE6D5762CF5F5D500865D6E /* WMTMachOReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTMachOReader.swift; sourceTree = "<group>"; };
EA294F3C29F6A07A00A0494E /* WMTOperationUIData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTOperationUIData.swift; sourceTree = "<group>"; };
EA44366929F9294600DDEC1C /* WMTPostApprovaScreenReview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTPostApprovaScreenReview.swift; sourceTree = "<group>"; };
EA44366B29F9297100DDEC1C /* WMTPostApprovaScreenRedirect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTPostApprovaScreenRedirect.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -236,6 +246,16 @@
path = UserOperation;
sourceTree = "<group>";
};
DC3E529E2CF62891002621C1 /* ProvisioningUtils */ = {
isa = PBXGroup;
children = (
DCAC559B2CE773E90070644A /* WMTProvisioningUtils.swift */,
DCE6D5732CF5F46000865D6E /* WMTSignatureAPNSEnvironmentDetector.swift */,
DCE6D5762CF5F5D500865D6E /* WMTMachOReader.swift */,
);
path = ProvisioningUtils;
sourceTree = "<group>";
};
DC488034292282FF00DB844B /* Inbox */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -277,6 +297,7 @@
DCE660D024CEBECA00870E53 /* IntegrationTests.swift */,
DCE660D224CEF56400870E53 /* IntegrationProxy.swift */,
DC395C0924E55B9B0007C36E /* PushParserTests.swift */,
DCAC55982CE68C2A0070644A /* ProvisioningUtilsTests.swift */,
DC6EDB7825A49ED900A229E4 /* OperationExpirationTests.swift */,
DC616235248508F8000DED17 /* QROperationParserTests.swift */,
EAB705492AF1161500756AC2 /* PACParserTests.swift */,
Expand Down Expand Up @@ -356,11 +377,13 @@
DC81D1CE24502E0300F80CD6 /* Common */ = {
isa = PBXGroup;
children = (
DC3E529E2CF62891002621C1 /* ProvisioningUtils */,
BFEEB20A2937AD700047941D /* WMTCancellable.swift */,
DC488030292282C900DB844B /* WMTService.swift */,
DCC5CCCD244DB0AD004679AC /* WMTLogger.swift */,
DC06D01E25AC74E400F2EA69 /* WMTLock.swift */,
DC9511F826EA02C100FF40AD /* WPNIntegration.swift */,
DCAC55BB2CEC954C0070644A /* WMTUtils.swift */,
);
path = Common;
sourceTree = "<group>";
Expand Down Expand Up @@ -588,6 +611,7 @@
files = (
DC61624224852B6D000DED17 /* NetworkingObjectsTests.swift in Sources */,
DC395C0A24E55B9B0007C36E /* PushParserTests.swift in Sources */,
DCAC55992CE68C2A0070644A /* ProvisioningUtilsTests.swift in Sources */,
DC6EDB7925A49ED900A229E4 /* OperationExpirationTests.swift in Sources */,
EAB7054A2AF1161500756AC2 /* PACParserTests.swift in Sources */,
DC616236248508F8000DED17 /* QROperationParserTests.swift in Sources */,
Expand All @@ -610,6 +634,7 @@
DC3D0B392480F886000DC4D9 /* WMTLocalOperation.swift in Sources */,
DCD8B336246C1BAF00385F02 /* WMTRejectionReason.swift in Sources */,
DCC5CCD8244DBBBD004679AC /* WMTAuthorizationData.swift in Sources */,
DCAC55BC2CEC954C0070644A /* WMTUtils.swift in Sources */,
DC488040292282FF00DB844B /* WMTInboxCount.swift in Sources */,
DCA43C6B29927C960059A163 /* WMTOperationAttributeAmountConversion.swift in Sources */,
EA74F7B32C2561BB004340B9 /* WMTResultTexts.swift in Sources */,
Expand Down Expand Up @@ -639,12 +664,15 @@
DCC5CCCE244DB0AD004679AC /* WMTLogger.swift in Sources */,
DCC5CCAE2449F7AC004679AC /* WMTUserOperation.swift in Sources */,
DC9511F926EA02C100FF40AD /* WPNIntegration.swift in Sources */,
DCE6D5772CF5F5D500865D6E /* WMTMachOReader.swift in Sources */,
DCC5CCBD2449F965004679AC /* WMTOperationAttributeHeading.swift in Sources */,
DCAC559C2CE773E90070644A /* WMTProvisioningUtils.swift in Sources */,
DC8CB206244DD007009DDAA3 /* WMTAllowedOperationSignature.swift in Sources */,
DCC3420424E3DB310045D27D /* WMTPushParser.swift in Sources */,
BFEEB20529379C700047941D /* WMTInboxGetMessageDetail.swift in Sources */,
EACAF7B02A126B7D0021CA54 /* WMTJsonValue.swift in Sources */,
DCAB7BCA24580BAC0006989D /* WMTQROperation.swift in Sources */,
DCE6D5742CF5F46000865D6E /* WMTSignatureAPNSEnvironmentDetector.swift in Sources */,
DCC5CCBF2449F981004679AC /* WMTOperationAttributePartyInfo.swift in Sources */,
DC81D1CB244F451E00F80CD6 /* WMTPushImpl.swift in Sources */,
EA9CE2BE2AEAA9FD00FE4E35 /* WMTProximityCheck.swift in Sources */,
Expand Down
185 changes: 185 additions & 0 deletions WultraMobileTokenSDK/Common/ProvisioningUtils/WMTMachOReader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
//
// 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
import MachO
import CommonCrypto

class WMTMachOReader {

private struct CSSuperBlob {
var magic: UInt32
var length: UInt32
var count: UInt32
}

private struct CSBlob {
var type: UInt32
var offset: UInt32
}

private struct CSMagic {
static let embeddedSignature: UInt32 = 0xfade0cc0
static let embeddedEntitlements: UInt32 = 0xfade7171
}

private enum BinaryType {
struct HeaderData {
let headerSize: Int
let commandCount: Int
}
struct FatHeaderData {
let archCount: Int
}
case singleArch(headerInfo: HeaderData)
case fat(header: FatHeaderData)
}

private var entitlements = [WMTProvision.Entitlements]()

static func readEntitlements(_ binaryPath: String) -> [WMTProvision.Entitlements]? {
WMTMachOReader(binaryPath)?.entitlements
}

private init?(_ binaryPath: String) {
guard let binary = BinaryReader(binaryPath) else {
return nil
}

switch getBinaryType(binary: binary) {
case .singleArch(let headerInfo):
let headerSize = headerInfo.headerSize
let commandCount = headerInfo.commandCount
if let data = readEntitlementsFromBinarySlice(binary: binary, headerOffset: headerSize, dataOffset: 0, cmdCount: commandCount) {
entitlements.append(data)
}
case .fat(let header):
entitlements.append(contentsOf: readEntitlementsFromFatBinary(binary: binary, architectureCount: header.archCount, startingAt: MemoryLayout<fat_header>.size))
default:
return nil
}
}

private func getBinaryType(binary: BinaryReader, fromSliceStartingAt offset: UInt64 = 0) -> BinaryType? {
binary.seek(to: offset)
let header: mach_header = binary.read()
let commandCount = Int(header.ncmds)
switch header.magic {
case MH_MAGIC:
let data = BinaryType.HeaderData(headerSize: MemoryLayout<mach_header>.size, commandCount: commandCount)
return .singleArch(headerInfo: data)
case MH_MAGIC_64:
let data = BinaryType.HeaderData(headerSize: MemoryLayout<mach_header_64>.size, commandCount: commandCount)
return .singleArch(headerInfo: data)
default:
binary.seek(to: 0)
let fatHeader: fat_header = binary.read()
if CFSwapInt32(fatHeader.magic) == FAT_MAGIC {
let archCount = Int(CFSwapInt32(fatHeader.nfat_arch))
return .fat(header: BinaryType.FatHeaderData(archCount: archCount))
} else {
return nil
}
}
}

private func readEntitlementsFromFatBinary(binary: BinaryReader, architectureCount: Int, startingAt: Int) -> [WMTProvision.Entitlements] {
var entitlements = [WMTProvision.Entitlements]()
for i in 0..<architectureCount {
let offset = startingAt + (i * MemoryLayout<fat_arch>.size)
binary.seek(to: UInt64(offset))
let fatArch: fat_arch = binary.read()
let fatArchOffset = CFSwapInt32(fatArch.offset)
let arch = getBinaryType(binary: binary, fromSliceStartingAt: UInt64(fatArchOffset))
switch arch {
case .singleArch(let headerInfo):
let headerOffset = Int(fatArchOffset) + headerInfo.headerSize
if let parsed = readEntitlementsFromBinarySlice(binary: binary, headerOffset: headerOffset, dataOffset: fatArchOffset, cmdCount: headerInfo.commandCount) {
entitlements.append(parsed)
}
default:
break
}
}
return entitlements
}

private func readEntitlementsFromBinarySlice(binary: BinaryReader, headerOffset: Int, dataOffset: UInt32, cmdCount: Int) -> WMTProvision.Entitlements? {
binary.seek(to: UInt64(headerOffset))
for _ in 0..<cmdCount {
let command: load_command = binary.read()
if command.cmd == LC_CODE_SIGNATURE {
let signatureOffset: UInt32 = binary.read()
return readEntitlementsData(binary: binary, startingAt: signatureOffset + dataOffset)
}
binary.seek(to: binary.currentOffset + UInt64(command.cmdsize - UInt32(MemoryLayout<load_command>.size)))
}
return nil
}

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 {
let metaBlobSize = UInt32(MemoryLayout<CSSuperBlob>.size)
let blobSize = UInt32(MemoryLayout<CSBlob>.size)
let itemCount = CFSwapInt32(metaBlob.count)
for index in 0..<itemCount {
let readOffset = UInt64(offset + metaBlobSize + index * blobSize)
binary.seek(to: readOffset)
let blob: CSBlob = binary.read()
binary.seek(to: UInt64(offset + CFSwapInt32(blob.offset)))
let blobMagic = CFSwapInt32(binary.read())
if blobMagic == CSMagic.embeddedEntitlements {
let signatureLength = CFSwapInt32(binary.read())
let signatureData = binary.readData(ofLength: Int(signatureLength) - 8)
return try? PropertyListDecoder().decode(WMTProvision.Entitlements.self, from: signatureData)
}
}
}
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>() -> T {
handle.readData(ofLength: MemoryLayout<T>.size).withUnsafeBytes({ $0.load(as: T.self) })
}

func readData(ofLength length: Int) -> Data {
handle.readData(ofLength: length)
}

deinit {
handle.closeFile()
}
}
Loading

0 comments on commit 41bf25b

Please sign in to comment.