@capgo/capacitor-updater 7.0.36 → 7.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapgoCapacitorUpdater.podspec +1 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1 -1
- package/ios/Plugin/AES.swift +66 -0
- package/ios/Plugin/BigInt.swift +55 -0
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +1 -1
- package/ios/Plugin/CryptoCipherV2.swift +97 -225
- package/ios/Plugin/RSA.swift +273 -0
- package/package.json +1 -1
|
@@ -57,7 +57,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
57
57
|
private static final String statsUrlDefault = "https://plugin.capgo.app/stats";
|
|
58
58
|
private static final String channelUrlDefault = "https://plugin.capgo.app/channel_self";
|
|
59
59
|
|
|
60
|
-
private final String PLUGIN_VERSION = "7.0.
|
|
60
|
+
private final String PLUGIN_VERSION = "7.0.37";
|
|
61
61
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
62
62
|
|
|
63
63
|
private SharedPreferences.Editor editor;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import BigInt
|
|
2
|
+
import CommonCrypto
|
|
3
|
+
import CryptoKit
|
|
4
|
+
|
|
5
|
+
///
|
|
6
|
+
/// Constants
|
|
7
|
+
///
|
|
8
|
+
private enum AESConstants {
|
|
9
|
+
static let aesAlgorithm: CCAlgorithm = CCAlgorithm(kCCAlgorithmAES)
|
|
10
|
+
static let aesOptions: CCOptions = CCOptions(kCCOptionPKCS7Padding)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// We do all this stuff because ios is shit and open source libraries allow to do decryption with public key
|
|
14
|
+
// So we have to do it manually, while in nodejs or Java it's ok and done at language level.
|
|
15
|
+
|
|
16
|
+
///
|
|
17
|
+
/// The AES key. Contains both the initialization vector and secret key.
|
|
18
|
+
///
|
|
19
|
+
public struct AES128Key {
|
|
20
|
+
/// Initialization vector
|
|
21
|
+
private let iv: Data
|
|
22
|
+
private let aes128Key: Data
|
|
23
|
+
#if DEBUG
|
|
24
|
+
public var __debug_iv: Data { iv }
|
|
25
|
+
public var __debug_aes128Key: Data { aes128Key }
|
|
26
|
+
#endif
|
|
27
|
+
init(iv: Data, aes128Key: Data) {
|
|
28
|
+
self.iv = iv
|
|
29
|
+
self.aes128Key = aes128Key
|
|
30
|
+
}
|
|
31
|
+
///
|
|
32
|
+
/// Takes the data and uses the private key to decrypt it. Will call `CCCrypt` in CommonCrypto
|
|
33
|
+
/// and provide it `ivData` for the initialization vector. Will use cipher block chaining (CBC) as
|
|
34
|
+
/// the mode of operation.
|
|
35
|
+
///
|
|
36
|
+
/// Returns the decrypted data.
|
|
37
|
+
///
|
|
38
|
+
public func decrypt(data: Data) -> Data? {
|
|
39
|
+
let encryptedData: UnsafePointer<UInt8> = (data as NSData).bytes.bindMemory(to: UInt8.self, capacity: data.count)
|
|
40
|
+
let encryptedDataLength: Int = data.count
|
|
41
|
+
|
|
42
|
+
if let result: NSMutableData = NSMutableData(length: encryptedDataLength) {
|
|
43
|
+
let keyData: UnsafePointer<UInt8> = (self.aes128Key as NSData).bytes.bindMemory(to: UInt8.self, capacity: self.aes128Key.count)
|
|
44
|
+
let keyLength: size_t = size_t(self.aes128Key.count)
|
|
45
|
+
let ivData: UnsafePointer<UInt8> = (iv as NSData).bytes.bindMemory(to: UInt8.self, capacity: self.iv.count)
|
|
46
|
+
|
|
47
|
+
let decryptedData: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>(result.mutableBytes.assumingMemoryBound(to: UInt8.self))
|
|
48
|
+
let decryptedDataLength: size_t = size_t(result.length)
|
|
49
|
+
|
|
50
|
+
var decryptedLength: size_t = 0
|
|
51
|
+
|
|
52
|
+
let status: CCCryptorStatus = CCCrypt(CCOperation(kCCDecrypt), AESConstants.aesAlgorithm, AESConstants.aesOptions, keyData, keyLength, ivData, encryptedData, encryptedDataLength, decryptedData, decryptedDataLength, &decryptedLength)
|
|
53
|
+
|
|
54
|
+
if Int32(status) == Int32(kCCSuccess) {
|
|
55
|
+
result.length = Int(decryptedLength)
|
|
56
|
+
return result as Data
|
|
57
|
+
} else {
|
|
58
|
+
print("\(CapacitorUpdater.TAG) AES decryption failed with status: \(status)")
|
|
59
|
+
return nil
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
print("\(CapacitorUpdater.TAG) Failed to allocate memory for AES decryption")
|
|
63
|
+
return nil
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import BigInt
|
|
2
|
+
|
|
3
|
+
// Extension to serialize BigInt to bytes array
|
|
4
|
+
extension BigInt {
|
|
5
|
+
func serializeToBytes() -> [UInt8] {
|
|
6
|
+
let byteCount = (self.bitWidth + 7) / 8
|
|
7
|
+
var bytes = [UInt8](repeating: 0, count: byteCount)
|
|
8
|
+
|
|
9
|
+
var value = self
|
|
10
|
+
for i in 0..<byteCount {
|
|
11
|
+
bytes[byteCount - i - 1] = UInt8(truncatingIfNeeded: value & 0xFF)
|
|
12
|
+
value >>= 8
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return bytes
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Add this custom power function to ensure safer handling of power operations
|
|
20
|
+
|
|
21
|
+
// Manual exponentiation using the square-and-multiply algorithm
|
|
22
|
+
// which is more efficient and avoids using the built-in functions that might handle BigInt differently
|
|
23
|
+
extension BigInt {
|
|
24
|
+
func manualPower(_ exponent: BigInt, modulus: BigInt) -> BigInt {
|
|
25
|
+
// Quick checks
|
|
26
|
+
if modulus == 0 {
|
|
27
|
+
return 0
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if exponent == 0 {
|
|
31
|
+
return 1
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
guard let base = self.magnitude as? BigUInt,
|
|
35
|
+
let exp = exponent.magnitude as? BigUInt,
|
|
36
|
+
let mod = modulus.magnitude as? BigUInt else {
|
|
37
|
+
return 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Square and multiply algorithm for modular exponentiation
|
|
41
|
+
var result = BigUInt(1)
|
|
42
|
+
var x = base % mod
|
|
43
|
+
var e = exp
|
|
44
|
+
|
|
45
|
+
while e > 0 {
|
|
46
|
+
if e & 1 == 1 {
|
|
47
|
+
result = (result * x) % mod
|
|
48
|
+
}
|
|
49
|
+
x = (x * x) % mod
|
|
50
|
+
e >>= 1
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return BigInt(result)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -45,7 +45,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
45
45
|
CAPPluginMethod(name: "getNextBundle", returnType: CAPPluginReturnPromise)
|
|
46
46
|
]
|
|
47
47
|
public var implementation = CapacitorUpdater()
|
|
48
|
-
private let PLUGIN_VERSION: String = "7.0.
|
|
48
|
+
private let PLUGIN_VERSION: String = "7.0.37"
|
|
49
49
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
50
50
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
51
51
|
static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
|
|
@@ -5,235 +5,40 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import Foundation
|
|
8
|
-
import CommonCrypto
|
|
9
8
|
import CryptoKit
|
|
9
|
+
import BigInt
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
/// Constants
|
|
13
|
-
///
|
|
14
|
-
private enum CryptoCipherConstants {
|
|
15
|
-
static let rsaKeySizeInBits: NSNumber = 2048
|
|
16
|
-
static let aesAlgorithm: CCAlgorithm = CCAlgorithm(kCCAlgorithmAES)
|
|
17
|
-
static let aesOptions: CCOptions = CCOptions(kCCOptionPKCS7Padding)
|
|
18
|
-
static let rsaAlgorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA256
|
|
19
|
-
}
|
|
20
|
-
///
|
|
21
|
-
/// The AES key. Contains both the initialization vector and secret key.
|
|
22
|
-
///
|
|
23
|
-
public struct AES128Key {
|
|
24
|
-
/// Initialization vector
|
|
25
|
-
private let iv: Data
|
|
26
|
-
private let aes128Key: Data
|
|
27
|
-
#if DEBUG
|
|
28
|
-
public var __debug_iv: Data { iv }
|
|
29
|
-
public var __debug_aes128Key: Data { aes128Key }
|
|
30
|
-
#endif
|
|
31
|
-
init(iv: Data, aes128Key: Data) {
|
|
32
|
-
self.iv = iv
|
|
33
|
-
self.aes128Key = aes128Key
|
|
34
|
-
}
|
|
35
|
-
///
|
|
36
|
-
/// Takes the data and uses the private key to decrypt it. Will call `CCCrypt` in CommonCrypto
|
|
37
|
-
/// and provide it `ivData` for the initialization vector. Will use cipher block chaining (CBC) as
|
|
38
|
-
/// the mode of operation.
|
|
39
|
-
///
|
|
40
|
-
/// Returns the decrypted data.
|
|
41
|
-
///
|
|
42
|
-
public func decrypt(data: Data) -> Data? {
|
|
43
|
-
let encryptedData: UnsafePointer<UInt8> = (data as NSData).bytes.bindMemory(to: UInt8.self, capacity: data.count)
|
|
44
|
-
let encryptedDataLength: Int = data.count
|
|
45
|
-
|
|
46
|
-
if let result: NSMutableData = NSMutableData(length: encryptedDataLength) {
|
|
47
|
-
let keyData: UnsafePointer<UInt8> = (self.aes128Key as NSData).bytes.bindMemory(to: UInt8.self, capacity: self.aes128Key.count)
|
|
48
|
-
let keyLength: size_t = size_t(self.aes128Key.count)
|
|
49
|
-
let ivData: UnsafePointer<UInt8> = (iv as NSData).bytes.bindMemory(to: UInt8.self, capacity: self.iv.count)
|
|
50
|
-
|
|
51
|
-
let decryptedData: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>(result.mutableBytes.assumingMemoryBound(to: UInt8.self))
|
|
52
|
-
let decryptedDataLength: size_t = size_t(result.length)
|
|
53
|
-
|
|
54
|
-
var decryptedLength: size_t = 0
|
|
55
|
-
|
|
56
|
-
let status: CCCryptorStatus = CCCrypt(CCOperation(kCCDecrypt), CryptoCipherConstants.aesAlgorithm, CryptoCipherConstants.aesOptions, keyData, keyLength, ivData, encryptedData, encryptedDataLength, decryptedData, decryptedDataLength, &decryptedLength)
|
|
57
|
-
|
|
58
|
-
if Int32(status) == Int32(kCCSuccess) {
|
|
59
|
-
result.length = Int(decryptedLength)
|
|
60
|
-
return result as Data
|
|
61
|
-
} else {
|
|
62
|
-
return nil
|
|
63
|
-
}
|
|
64
|
-
} else {
|
|
65
|
-
return nil
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
///
|
|
71
|
-
/// The RSA public key.
|
|
72
|
-
///
|
|
73
|
-
public struct RSAPublicKey {
|
|
74
|
-
private let publicKey: SecKey
|
|
75
|
-
|
|
76
|
-
#if DEBUG
|
|
77
|
-
public var __debug_publicKey: SecKey { self.publicKey }
|
|
78
|
-
#endif
|
|
79
|
-
|
|
80
|
-
fileprivate init(publicKey: SecKey) {
|
|
81
|
-
self.publicKey = publicKey
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
///
|
|
85
|
-
/// Takes the data and uses the public key to decrypt it.
|
|
86
|
-
/// Returns the decrypted data.
|
|
87
|
-
///
|
|
88
|
-
public func decrypt(data: Data) -> Data? {
|
|
89
|
-
do {
|
|
90
|
-
guard let decryptedData = RSAPublicKey.decryptWithRSAKey(data, rsaKeyRef: self.publicKey, padding: SecPadding()) else {
|
|
91
|
-
throw CustomError.cannotDecryptSessionKey
|
|
92
|
-
}
|
|
11
|
+
public struct CryptoCipherV2 {
|
|
93
12
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
print("
|
|
97
|
-
return
|
|
13
|
+
public static func decryptChecksum(checksum: String, publicKey: String, version: String) throws -> String {
|
|
14
|
+
if publicKey.isEmpty {
|
|
15
|
+
print("\(CapacitorUpdater.TAG) The public key is empty")
|
|
16
|
+
return checksum
|
|
98
17
|
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
///
|
|
102
|
-
/// Allows you to load an RSA public key (i.e. one downloaded from the net).
|
|
103
|
-
///
|
|
104
|
-
public static func load(rsaPublicKey: String) -> RSAPublicKey? {
|
|
105
|
-
var pubKey: String = rsaPublicKey
|
|
106
|
-
pubKey = pubKey.replacingOccurrences(of: "-----BEGIN RSA PUBLIC KEY-----", with: "")
|
|
107
|
-
pubKey = pubKey.replacingOccurrences(of: "-----END RSA PUBLIC KEY-----", with: "")
|
|
108
|
-
pubKey = pubKey.replacingOccurrences(of: "\\n+", with: "", options: .regularExpression)
|
|
109
|
-
pubKey = pubKey.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
110
18
|
do {
|
|
111
|
-
guard let
|
|
19
|
+
guard let checksumBytes = Data(base64Encoded: checksum) else {
|
|
20
|
+
print("\(CapacitorUpdater.TAG) Cannot decode checksum as base64: \(checksum)")
|
|
112
21
|
throw CustomError.cannotDecode
|
|
113
22
|
}
|
|
114
23
|
|
|
115
|
-
|
|
24
|
+
if checksumBytes.isEmpty {
|
|
25
|
+
print("\(CapacitorUpdater.TAG) Decoded checksum is empty")
|
|
116
26
|
throw CustomError.cannotDecode
|
|
117
27
|
}
|
|
118
28
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
print("Error load RSA: \(error)")
|
|
122
|
-
return nil
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// code is copied from here: https://github.com/btnguyen2k/swiftutils/blob/88494f4c635b6c6d42ef0fb30a7d666acd38c4fa/SwiftUtils/RSAUtils.swift#L393
|
|
127
|
-
public static func decryptWithRSAKey(_ encryptedData: Data, rsaKeyRef: SecKey, padding: SecPadding) -> Data? {
|
|
128
|
-
let blockSize = SecKeyGetBlockSize(rsaKeyRef)
|
|
129
|
-
let dataSize = encryptedData.count / MemoryLayout<UInt8>.size
|
|
130
|
-
|
|
131
|
-
var encryptedDataAsArray = [UInt8](repeating: 0, count: dataSize)
|
|
132
|
-
(encryptedData as NSData).getBytes(&encryptedDataAsArray, length: dataSize)
|
|
133
|
-
|
|
134
|
-
var decryptedData = [UInt8](repeating: 0, count: 0)
|
|
135
|
-
var idx = 0
|
|
136
|
-
while idx < encryptedDataAsArray.count {
|
|
137
|
-
var idxEnd = idx + blockSize
|
|
138
|
-
if idxEnd > encryptedDataAsArray.count {
|
|
139
|
-
idxEnd = encryptedDataAsArray.count
|
|
140
|
-
}
|
|
141
|
-
var chunkData = [UInt8](repeating: 0, count: blockSize)
|
|
142
|
-
for i in idx..<idxEnd {
|
|
143
|
-
chunkData[i-idx] = encryptedDataAsArray[i]
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
var decryptedDataBuffer = [UInt8](repeating: 0, count: blockSize)
|
|
147
|
-
var decryptedDataLength = blockSize
|
|
148
|
-
|
|
149
|
-
let status = SecKeyDecrypt(rsaKeyRef, padding, chunkData, idxEnd-idx, &decryptedDataBuffer, &decryptedDataLength)
|
|
150
|
-
if status != noErr {
|
|
151
|
-
return nil
|
|
152
|
-
}
|
|
153
|
-
let finalData = removePadding(decryptedDataBuffer)
|
|
154
|
-
decryptedData += finalData
|
|
155
|
-
|
|
156
|
-
idx += blockSize
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return Data(decryptedData)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// code is copied from here: https://github.com/btnguyen2k/swiftutils/blob/88494f4c635b6c6d42ef0fb30a7d666acd38c4fa/SwiftUtils/RSAUtils.swift#L429
|
|
163
|
-
private static func removePadding(_ data: [UInt8]) -> [UInt8] {
|
|
164
|
-
var idxFirstZero = -1
|
|
165
|
-
var idxNextZero = data.count
|
|
166
|
-
for i in 0..<data.count {
|
|
167
|
-
if data[i] == 0 {
|
|
168
|
-
if idxFirstZero < 0 {
|
|
169
|
-
idxFirstZero = i
|
|
170
|
-
} else {
|
|
171
|
-
idxNextZero = i
|
|
172
|
-
break
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
if idxNextZero-idxFirstZero-1 == 0 {
|
|
177
|
-
idxNextZero = idxFirstZero
|
|
178
|
-
idxFirstZero = -1
|
|
179
|
-
}
|
|
180
|
-
var newData = [UInt8](repeating: 0, count: idxNextZero-idxFirstZero-1)
|
|
181
|
-
for i in idxFirstZero+1..<idxNextZero {
|
|
182
|
-
newData[i-idxFirstZero-1] = data[i]
|
|
183
|
-
}
|
|
184
|
-
return newData
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
fileprivate extension SecKey {
|
|
189
|
-
func exportToData() -> Data? {
|
|
190
|
-
var error: Unmanaged<CFError>?
|
|
191
|
-
if let cfData: CFData = SecKeyCopyExternalRepresentation(self, &error) {
|
|
192
|
-
if error != nil {
|
|
193
|
-
return nil
|
|
194
|
-
} else {
|
|
195
|
-
return cfData as Data
|
|
196
|
-
}
|
|
197
|
-
} else {
|
|
198
|
-
return nil
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
static func loadPublicFromData(_ data: Data) -> SecKey? {
|
|
202
|
-
let keyDict: [NSObject: NSObject] = [
|
|
203
|
-
kSecAttrKeyType: kSecAttrKeyTypeRSA,
|
|
204
|
-
kSecAttrKeyClass: kSecAttrKeyClassPublic,
|
|
205
|
-
kSecAttrKeySizeInBits: CryptoCipherConstants.rsaKeySizeInBits
|
|
206
|
-
]
|
|
207
|
-
return SecKeyCreateWithData(data as NSData, keyDict as CFDictionary, nil)
|
|
208
|
-
}
|
|
209
|
-
static func loadPrivateFromData(_ data: Data) -> SecKey? {
|
|
210
|
-
let keyDict: [NSObject: NSObject] = [
|
|
211
|
-
kSecAttrKeyType: kSecAttrKeyTypeRSA,
|
|
212
|
-
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
|
213
|
-
kSecAttrKeySizeInBits: CryptoCipherConstants.rsaKeySizeInBits
|
|
214
|
-
]
|
|
215
|
-
return SecKeyCreateWithData(data as CFData, keyDict as CFDictionary, nil)
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
public struct CryptoCipherV2 {
|
|
220
|
-
|
|
221
|
-
public static func decryptChecksum(checksum: String, publicKey: String, version: String) throws -> String {
|
|
222
|
-
if publicKey.isEmpty {
|
|
223
|
-
return checksum
|
|
224
|
-
}
|
|
225
|
-
do {
|
|
226
|
-
let checksumBytes: Data = Data(base64Encoded: checksum)!
|
|
227
|
-
guard let rsaPublicKey: RSAPublicKey = .load(rsaPublicKey: publicKey) else {
|
|
228
|
-
print("cannot decode publicKey", publicKey)
|
|
29
|
+
guard let rsaPublicKey = RSAPublicKey.load(rsaPublicKey: publicKey) else {
|
|
30
|
+
print("\(CapacitorUpdater.TAG) The public key is not a valid RSA Public key")
|
|
229
31
|
throw CustomError.cannotDecode
|
|
230
32
|
}
|
|
33
|
+
|
|
231
34
|
guard let decryptedChecksum = rsaPublicKey.decrypt(data: checksumBytes) else {
|
|
35
|
+
print("\(CapacitorUpdater.TAG) decryptChecksum fail")
|
|
232
36
|
throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
|
|
233
37
|
}
|
|
38
|
+
|
|
234
39
|
return decryptedChecksum.base64EncodedString()
|
|
235
40
|
} catch {
|
|
236
|
-
print("\(CapacitorUpdater.TAG)
|
|
41
|
+
print("\(CapacitorUpdater.TAG) decryptChecksum fail: \(error.localizedDescription)")
|
|
237
42
|
throw CustomError.cannotDecode
|
|
238
43
|
}
|
|
239
44
|
}
|
|
@@ -242,13 +47,35 @@ public struct CryptoCipherV2 {
|
|
|
242
47
|
var sha256 = SHA256()
|
|
243
48
|
|
|
244
49
|
do {
|
|
245
|
-
let fileHandle
|
|
50
|
+
let fileHandle: FileHandle
|
|
51
|
+
do {
|
|
52
|
+
fileHandle = try FileHandle(forReadingFrom: filePath)
|
|
53
|
+
} catch {
|
|
54
|
+
print("\(CapacitorUpdater.TAG) Cannot open file for checksum: \(filePath.path)", error)
|
|
55
|
+
return ""
|
|
56
|
+
}
|
|
57
|
+
|
|
246
58
|
defer {
|
|
247
|
-
|
|
59
|
+
do {
|
|
60
|
+
try fileHandle.close()
|
|
61
|
+
} catch {
|
|
62
|
+
print("\(CapacitorUpdater.TAG) Error closing file: \(error)")
|
|
63
|
+
}
|
|
248
64
|
}
|
|
249
65
|
|
|
250
66
|
while autoreleasepool(invoking: {
|
|
251
|
-
let fileData
|
|
67
|
+
let fileData: Data
|
|
68
|
+
do {
|
|
69
|
+
if #available(iOS 13.4, *) {
|
|
70
|
+
fileData = try fileHandle.read(upToCount: bufferSize) ?? Data()
|
|
71
|
+
} else {
|
|
72
|
+
fileData = fileHandle.readData(ofLength: bufferSize)
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
print("\(CapacitorUpdater.TAG) Error reading file: \(error)")
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
|
|
252
79
|
if fileData.count > 0 {
|
|
253
80
|
sha256.update(data: fileData)
|
|
254
81
|
return true // Continue
|
|
@@ -266,44 +93,89 @@ public struct CryptoCipherV2 {
|
|
|
266
93
|
}
|
|
267
94
|
|
|
268
95
|
public static func decryptFile(filePath: URL, publicKey: String, sessionKey: String, version: String) throws {
|
|
269
|
-
if publicKey.isEmpty || sessionKey.isEmpty
|
|
270
|
-
print("\(CapacitorUpdater.TAG) Cannot
|
|
96
|
+
if publicKey.isEmpty || sessionKey.isEmpty || sessionKey.components(separatedBy: ":").count != 2 {
|
|
97
|
+
print("\(CapacitorUpdater.TAG) Cannot found public key or sessionKey")
|
|
271
98
|
return
|
|
272
99
|
}
|
|
100
|
+
|
|
101
|
+
if !publicKey.hasPrefix("-----BEGIN RSA PUBLIC KEY-----") {
|
|
102
|
+
print("\(CapacitorUpdater.TAG) The public key is not a valid RSA Public key")
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
273
106
|
do {
|
|
274
|
-
guard let rsaPublicKey
|
|
275
|
-
print("
|
|
107
|
+
guard let rsaPublicKey = RSAPublicKey.load(rsaPublicKey: publicKey) else {
|
|
108
|
+
print("\(CapacitorUpdater.TAG) The public key is not a valid RSA Public key")
|
|
276
109
|
throw CustomError.cannotDecode
|
|
277
110
|
}
|
|
278
111
|
|
|
279
|
-
let
|
|
280
|
-
|
|
281
|
-
|
|
112
|
+
let sessionKeyComponents = sessionKey.components(separatedBy: ":")
|
|
113
|
+
let ivBase64 = sessionKeyComponents[0]
|
|
114
|
+
let encryptedKeyBase64 = sessionKeyComponents[1]
|
|
115
|
+
|
|
116
|
+
guard let ivData = Data(base64Encoded: ivBase64) else {
|
|
117
|
+
print("\(CapacitorUpdater.TAG) Cannot decode sessionKey IV", ivBase64)
|
|
282
118
|
throw CustomError.cannotDecode
|
|
283
119
|
}
|
|
284
120
|
|
|
285
|
-
|
|
121
|
+
if ivData.count != 16 {
|
|
122
|
+
print("\(CapacitorUpdater.TAG) IV data has invalid length: \(ivData.count), expected 16")
|
|
123
|
+
throw CustomError.cannotDecode
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
guard let sessionKeyDataEncrypted = Data(base64Encoded: encryptedKeyBase64) else {
|
|
127
|
+
print("\(CapacitorUpdater.TAG) Cannot decode sessionKey data", encryptedKeyBase64)
|
|
286
128
|
throw NSError(domain: "Invalid session key data", code: 1, userInfo: nil)
|
|
287
129
|
}
|
|
288
130
|
|
|
289
131
|
guard let sessionKeyDataDecrypted = rsaPublicKey.decrypt(data: sessionKeyDataEncrypted) else {
|
|
132
|
+
print("\(CapacitorUpdater.TAG) Failed to decrypt session key data")
|
|
290
133
|
throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
|
|
291
134
|
}
|
|
292
135
|
|
|
136
|
+
if sessionKeyDataDecrypted.count != 16 {
|
|
137
|
+
print("\(CapacitorUpdater.TAG) Decrypted session key has invalid length: \(sessionKeyDataDecrypted.count), expected 16")
|
|
138
|
+
throw NSError(domain: "Invalid decrypted session key", code: 5, userInfo: nil)
|
|
139
|
+
}
|
|
140
|
+
|
|
293
141
|
let aesPrivateKey = AES128Key(iv: ivData, aes128Key: sessionKeyDataDecrypted)
|
|
294
142
|
|
|
295
|
-
|
|
143
|
+
let encryptedData: Data
|
|
144
|
+
do {
|
|
145
|
+
encryptedData = try Data(contentsOf: filePath)
|
|
146
|
+
} catch {
|
|
147
|
+
print("\(CapacitorUpdater.TAG) Failed to read encrypted data: \(error)")
|
|
296
148
|
throw NSError(domain: "Failed to read encrypted data", code: 3, userInfo: nil)
|
|
297
149
|
}
|
|
298
150
|
|
|
151
|
+
if encryptedData.isEmpty {
|
|
152
|
+
print("\(CapacitorUpdater.TAG) Encrypted file data is empty")
|
|
153
|
+
throw NSError(domain: "Empty encrypted data", code: 6, userInfo: nil)
|
|
154
|
+
}
|
|
155
|
+
|
|
299
156
|
guard let decryptedData = aesPrivateKey.decrypt(data: encryptedData) else {
|
|
157
|
+
print("\(CapacitorUpdater.TAG) Failed to decrypt data")
|
|
300
158
|
throw NSError(domain: "Failed to decrypt data", code: 4, userInfo: nil)
|
|
301
159
|
}
|
|
302
160
|
|
|
303
|
-
|
|
161
|
+
if decryptedData.isEmpty {
|
|
162
|
+
print("\(CapacitorUpdater.TAG) Decrypted data is empty")
|
|
163
|
+
throw NSError(domain: "Empty decrypted data", code: 7, userInfo: nil)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
do {
|
|
167
|
+
try decryptedData.write(to: filePath, options: .atomic)
|
|
168
|
+
if !FileManager.default.fileExists(atPath: filePath.path) {
|
|
169
|
+
print("\(CapacitorUpdater.TAG) File was not created after write")
|
|
170
|
+
throw NSError(domain: "File write failed", code: 8, userInfo: nil)
|
|
171
|
+
}
|
|
172
|
+
} catch {
|
|
173
|
+
print("\(CapacitorUpdater.TAG) Error writing decrypted file: \(error)")
|
|
174
|
+
throw error
|
|
175
|
+
}
|
|
304
176
|
|
|
305
177
|
} catch {
|
|
306
|
-
print("\(CapacitorUpdater.TAG)
|
|
178
|
+
print("\(CapacitorUpdater.TAG) decryptFile fail")
|
|
307
179
|
throw CustomError.cannotDecode
|
|
308
180
|
}
|
|
309
181
|
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import BigInt
|
|
2
|
+
import CommonCrypto
|
|
3
|
+
import CryptoKit
|
|
4
|
+
|
|
5
|
+
///
|
|
6
|
+
/// Constants
|
|
7
|
+
///
|
|
8
|
+
private enum RSAConstants {
|
|
9
|
+
static let rsaKeySizeInBits: NSNumber = 2048
|
|
10
|
+
static let rsaAlgorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA256
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// We do all this stuff because ios is shit and open source libraries allow to do decryption with public key
|
|
14
|
+
// So we have to do it manually, while in nodejs or Java it's ok and done at language level.
|
|
15
|
+
|
|
16
|
+
///
|
|
17
|
+
/// The RSA public key.
|
|
18
|
+
///
|
|
19
|
+
public struct RSAPublicKey {
|
|
20
|
+
private let manualKey: ManualRSAPublicKey
|
|
21
|
+
|
|
22
|
+
fileprivate init(manualKey: ManualRSAPublicKey) {
|
|
23
|
+
self.manualKey = manualKey
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
///
|
|
27
|
+
/// Takes the data and uses the public key to decrypt it.
|
|
28
|
+
/// Returns the decrypted data.
|
|
29
|
+
///
|
|
30
|
+
public func decrypt(data: Data) -> Data? {
|
|
31
|
+
return manualKey.decrypt(data)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
///
|
|
35
|
+
/// Allows you to load an RSA public key (i.e. one downloaded from the net).
|
|
36
|
+
///
|
|
37
|
+
public static func load(rsaPublicKey: String) -> RSAPublicKey? {
|
|
38
|
+
// Clean up the key string
|
|
39
|
+
var pubKey: String = rsaPublicKey
|
|
40
|
+
pubKey = pubKey.replacingOccurrences(of: "-----BEGIN RSA PUBLIC KEY-----", with: "")
|
|
41
|
+
pubKey = pubKey.replacingOccurrences(of: "-----END RSA PUBLIC KEY-----", with: "")
|
|
42
|
+
pubKey = pubKey.replacingOccurrences(of: "-----BEGIN PUBLIC KEY-----", with: "")
|
|
43
|
+
pubKey = pubKey.replacingOccurrences(of: "-----END PUBLIC KEY-----", with: "")
|
|
44
|
+
pubKey = pubKey.replacingOccurrences(of: "\\n+", with: "", options: .regularExpression)
|
|
45
|
+
pubKey = pubKey.replacingOccurrences(of: "\n", with: "")
|
|
46
|
+
pubKey = pubKey.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
47
|
+
|
|
48
|
+
do {
|
|
49
|
+
guard let rsaPublicKeyData: Data = Data(base64Encoded: String(pubKey)) else {
|
|
50
|
+
throw CustomError.cannotDecode
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Try parsing as PKCS#1
|
|
54
|
+
if let manualKey = ManualRSAPublicKey.fromPKCS1(rsaPublicKeyData) {
|
|
55
|
+
return RSAPublicKey(manualKey: manualKey)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Most common public exponent is 65537 (0x010001)
|
|
59
|
+
let commonExponent = Data([0x01, 0x00, 0x01]) // 65537 in big-endian
|
|
60
|
+
|
|
61
|
+
// Assume the entire key data is the modulus
|
|
62
|
+
let lastResortKey = ManualRSAPublicKey(modulus: rsaPublicKeyData, exponent: commonExponent)
|
|
63
|
+
return RSAPublicKey(manualKey: lastResortKey)
|
|
64
|
+
} catch {
|
|
65
|
+
return nil
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Manual RSA Public Key Implementation using the BigInt library
|
|
71
|
+
struct ManualRSAPublicKey {
|
|
72
|
+
let modulus: BigInt
|
|
73
|
+
let exponent: BigInt
|
|
74
|
+
|
|
75
|
+
init(modulus: Data, exponent: Data) {
|
|
76
|
+
// Create positive BigInts from Data
|
|
77
|
+
let modulusBytes = [UInt8](modulus)
|
|
78
|
+
var modulusValue = BigUInt(0)
|
|
79
|
+
for byte in modulusBytes {
|
|
80
|
+
modulusValue = (modulusValue << 8) | BigUInt(byte)
|
|
81
|
+
}
|
|
82
|
+
self.modulus = BigInt(modulusValue)
|
|
83
|
+
let exponentBytes = [UInt8](exponent)
|
|
84
|
+
var exponentValue = BigUInt(0)
|
|
85
|
+
for byte in exponentBytes {
|
|
86
|
+
exponentValue = (exponentValue << 8) | BigUInt(byte)
|
|
87
|
+
}
|
|
88
|
+
self.exponent = BigInt(exponentValue)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Parse PKCS#1 format public key
|
|
92
|
+
static func fromPKCS1(_ publicKeyData: Data) -> ManualRSAPublicKey? {
|
|
93
|
+
// Parse ASN.1 DER encoded RSA public key
|
|
94
|
+
// Format: RSAPublicKey ::= SEQUENCE { modulus INTEGER, publicExponent INTEGER }
|
|
95
|
+
|
|
96
|
+
guard publicKeyData.count > 0 else {
|
|
97
|
+
return nil
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let bytes = [UInt8](publicKeyData)
|
|
101
|
+
|
|
102
|
+
// Check for sequence tag (0x30)
|
|
103
|
+
guard bytes[0] == 0x30 else {
|
|
104
|
+
// Try direct modulus/exponent approach as fallback
|
|
105
|
+
if publicKeyData.count >= 3 {
|
|
106
|
+
// Assume this is a raw RSA public key with modulus + exponent
|
|
107
|
+
// Most common: modulus is 256 bytes (2048 bits), exponent is 3 bytes (0x010001 = 65537)
|
|
108
|
+
let modulusSize = publicKeyData.count - 3
|
|
109
|
+
if modulusSize > 0 {
|
|
110
|
+
let modulusData = publicKeyData.prefix(modulusSize)
|
|
111
|
+
let exponentData = publicKeyData.suffix(3)
|
|
112
|
+
return ManualRSAPublicKey(modulus: modulusData, exponent: exponentData)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return nil
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
var index = 1
|
|
120
|
+
|
|
121
|
+
// Skip length
|
|
122
|
+
if bytes[index] & 0x80 != 0 {
|
|
123
|
+
let lenBytes = Int(bytes[index] & 0x7F)
|
|
124
|
+
if (index + 1 + lenBytes) >= bytes.count {
|
|
125
|
+
return nil
|
|
126
|
+
}
|
|
127
|
+
index += 1 + lenBytes
|
|
128
|
+
} else {
|
|
129
|
+
index += 1
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check for INTEGER tag for modulus (0x02)
|
|
133
|
+
if index >= bytes.count {
|
|
134
|
+
return nil
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
guard bytes[index] == 0x02 else {
|
|
138
|
+
return nil
|
|
139
|
+
}
|
|
140
|
+
index += 1
|
|
141
|
+
|
|
142
|
+
// Get modulus length
|
|
143
|
+
if index >= bytes.count {
|
|
144
|
+
return nil
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
var modulusLength = 0
|
|
148
|
+
if bytes[index] & 0x80 != 0 {
|
|
149
|
+
let lenBytes = Int(bytes[index] & 0x7F)
|
|
150
|
+
if (index + 1 + lenBytes) >= bytes.count {
|
|
151
|
+
return nil
|
|
152
|
+
}
|
|
153
|
+
index += 1
|
|
154
|
+
for i in 0..<lenBytes {
|
|
155
|
+
modulusLength = (modulusLength << 8) | Int(bytes[index + i])
|
|
156
|
+
}
|
|
157
|
+
index += lenBytes
|
|
158
|
+
} else {
|
|
159
|
+
modulusLength = Int(bytes[index])
|
|
160
|
+
index += 1
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Skip any leading zero in modulus (for unsigned integer)
|
|
164
|
+
if index < bytes.count && bytes[index] == 0x00 {
|
|
165
|
+
index += 1
|
|
166
|
+
modulusLength -= 1
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Extract modulus
|
|
170
|
+
if (index + modulusLength) > bytes.count {
|
|
171
|
+
return nil
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let modulusData = Data(bytes[index..<(index + modulusLength)])
|
|
175
|
+
index += modulusLength
|
|
176
|
+
|
|
177
|
+
// Check for INTEGER tag for exponent
|
|
178
|
+
if index >= bytes.count {
|
|
179
|
+
return nil
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
guard bytes[index] == 0x02 else {
|
|
183
|
+
return nil
|
|
184
|
+
}
|
|
185
|
+
index += 1
|
|
186
|
+
|
|
187
|
+
// Get exponent length
|
|
188
|
+
if index >= bytes.count {
|
|
189
|
+
return nil
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
var exponentLength = 0
|
|
193
|
+
if bytes[index] & 0x80 != 0 {
|
|
194
|
+
let lenBytes = Int(bytes[index] & 0x7F)
|
|
195
|
+
if (index + 1 + lenBytes) >= bytes.count {
|
|
196
|
+
return nil
|
|
197
|
+
}
|
|
198
|
+
index += 1
|
|
199
|
+
for i in 0..<lenBytes {
|
|
200
|
+
exponentLength = (exponentLength << 8) | Int(bytes[index + i])
|
|
201
|
+
}
|
|
202
|
+
index += lenBytes
|
|
203
|
+
} else {
|
|
204
|
+
exponentLength = Int(bytes[index])
|
|
205
|
+
index += 1
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Extract exponent
|
|
209
|
+
if (index + exponentLength) > bytes.count {
|
|
210
|
+
return nil
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
let exponentData = Data(bytes[index..<(index + exponentLength)])
|
|
214
|
+
return ManualRSAPublicKey(modulus: modulusData, exponent: exponentData)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Decrypt data using raw RSA operation (c^d mod n)
|
|
218
|
+
func decrypt(_ encryptedData: Data) -> Data? {
|
|
219
|
+
// Create positive BigInt from encrypted data
|
|
220
|
+
let encryptedBytes = [UInt8](encryptedData)
|
|
221
|
+
var encryptedValue = BigUInt(0)
|
|
222
|
+
for byte in encryptedBytes {
|
|
223
|
+
encryptedValue = (encryptedValue << 8) | BigUInt(byte)
|
|
224
|
+
}
|
|
225
|
+
let encrypted = BigInt(encryptedValue)
|
|
226
|
+
|
|
227
|
+
// In Node.js:
|
|
228
|
+
// privateEncrypt uses the private key (d) to encrypt
|
|
229
|
+
// publicDecrypt uses the public key (e) to decrypt
|
|
230
|
+
// The operation we want is: ciphertext^e mod n
|
|
231
|
+
|
|
232
|
+
// RSA operation: c^e mod n
|
|
233
|
+
let decrypted = encrypted.manualPower(exponent, modulus: modulus)
|
|
234
|
+
|
|
235
|
+
// Convert to bytes with proper padding
|
|
236
|
+
guard let bigUIntValue = decrypted.magnitude as? BigUInt else {
|
|
237
|
+
return nil
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Convert BigUInt to bytes with padding
|
|
241
|
+
var resultBytes = [UInt8]()
|
|
242
|
+
var tempValue = bigUIntValue
|
|
243
|
+
while tempValue > 0 {
|
|
244
|
+
let byte = UInt8(tempValue & 0xFF)
|
|
245
|
+
resultBytes.insert(byte, at: 0) // Prepend to get big-endian
|
|
246
|
+
tempValue >>= 8
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Ensure we have at least 256 bytes (2048 bits) with leading zeros
|
|
250
|
+
let paddedBytes = [UInt8](repeating: 0, count: max(0, 256 - resultBytes.count)) + resultBytes
|
|
251
|
+
|
|
252
|
+
// For PKCS1 padding from Node.js privateEncrypt, the format is:
|
|
253
|
+
// 0x00 || 0x01 || PS || 0x00 || actual data
|
|
254
|
+
// where PS is a string of 0xFF bytes
|
|
255
|
+
|
|
256
|
+
// Check for privateEncrypt padding format (0x00 || 0x01 || PS || 0x00)
|
|
257
|
+
var startIndex = 0
|
|
258
|
+
if paddedBytes.count > 2 && paddedBytes[0] == 0x00 && paddedBytes[1] == 0x01 {
|
|
259
|
+
for i in 2..<paddedBytes.count {
|
|
260
|
+
if paddedBytes[i] == 0x00 {
|
|
261
|
+
startIndex = i + 1
|
|
262
|
+
break
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if startIndex < paddedBytes.count {
|
|
267
|
+
let result = Data(paddedBytes[startIndex...])
|
|
268
|
+
return result
|
|
269
|
+
} else {
|
|
270
|
+
return Data(paddedBytes)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|