react-native-stallion 2.2.0 → 2.3.0-alpha.2
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/android/src/main/java/com/stallion/events/StallionEventConstants.java +2 -1
- package/android/src/main/java/com/stallion/events/StallionEventManager.java +7 -0
- package/android/src/main/java/com/stallion/networkmanager/StallionSyncHandler.java +36 -4
- package/android/src/main/java/com/stallion/storage/StallionConfig.java +27 -0
- package/android/src/main/java/com/stallion/storage/StallionConfigConstants.java +2 -0
- package/android/src/main/java/com/stallion/utils/StallionSignatureVerification.java +129 -0
- package/ios/main/Stallion.swift +1 -0
- package/ios/main/StallionConfig.h +4 -1
- package/ios/main/StallionConfig.m +9 -2
- package/ios/main/StallionConfigConstants.h +5 -1
- package/ios/main/StallionConfigConstants.m +5 -1
- package/ios/main/StallionConstants.swift +1 -0
- package/ios/main/StallionEventHandler.h +1 -1
- package/ios/main/StallionEventHandler.m +9 -3
- package/ios/main/StallionExceptionHandler.h +1 -1
- package/ios/main/StallionExceptionHandler.m +104 -14
- package/ios/main/StallionFileManager.h +1 -1
- package/ios/main/StallionFileManager.m +1 -1
- package/ios/main/StallionMeta.h +1 -1
- package/ios/main/StallionMeta.m +1 -1
- package/ios/main/StallionMetaConstants.h +1 -1
- package/ios/main/StallionMetaConstants.m +1 -1
- package/ios/main/StallionSignatureVerification.swift +135 -0
- package/ios/main/StallionSlotManager.h +1 -1
- package/ios/main/StallionSlotManager.m +1 -1
- package/ios/main/StallionStageManager.swift +1 -1
- package/ios/main/StallionStateManager.h +1 -1
- package/ios/main/StallionStateManager.m +1 -1
- package/ios/main/StallionSyncHandler.swift +52 -20
- package/package.json +1 -1
- package/src/main/state/useStallionEvents.js +2 -2
- package/src/main/state/useStallionEvents.js.map +1 -1
- package/src/main/utils/StallionNativeUtils.js +4 -1
- package/src/main/utils/StallionNativeUtils.js.map +1 -1
- package/types/main/utils/StallionNativeUtils.d.ts +1 -1
- package/types/main/utils/StallionNativeUtils.d.ts.map +1 -1
|
@@ -18,6 +18,7 @@ public class StallionEventManager {
|
|
|
18
18
|
public static final String STALLION_NATIVE_EVENT_NAME = "STALLION_NATIVE_EVENT";
|
|
19
19
|
private static final String EVENTS_KEY = "stored_events";
|
|
20
20
|
private static final int MAX_BATCH_COUNT_SIZE = 9;
|
|
21
|
+
private static final int MAX_EVENT_STORAGE_LIMIT = 20;
|
|
21
22
|
|
|
22
23
|
private static StallionEventManager instance;
|
|
23
24
|
private final StallionStateManager stallionStateManager;
|
|
@@ -105,6 +106,12 @@ public class StallionEventManager {
|
|
|
105
106
|
try {
|
|
106
107
|
String eventsString = stallionStateManager.getString(EVENTS_KEY, "{}");
|
|
107
108
|
JSONObject eventsObject = new JSONObject(eventsString);
|
|
109
|
+
|
|
110
|
+
// Flush all if limit reached
|
|
111
|
+
if (eventsObject.length() >= MAX_EVENT_STORAGE_LIMIT) {
|
|
112
|
+
eventsObject = new JSONObject(); // reset
|
|
113
|
+
}
|
|
114
|
+
|
|
108
115
|
eventsObject.put(uniqueId, eventPayload.toString());
|
|
109
116
|
stallionStateManager.setString(EVENTS_KEY, eventsObject.toString());
|
|
110
117
|
} catch (JSONException e) {
|
|
@@ -5,10 +5,14 @@ import com.stallion.storage.StallionConfigConstants;
|
|
|
5
5
|
import com.stallion.storage.StallionMetaConstants;
|
|
6
6
|
import com.stallion.storage.StallionStateManager;
|
|
7
7
|
import com.stallion.storage.StallionConfig;
|
|
8
|
+
import com.stallion.utils.StallionFileManager;
|
|
9
|
+
import com.stallion.utils.StallionSignatureVerification;
|
|
8
10
|
import com.stallion.utils.StallionSlotManager;
|
|
9
11
|
import com.stallion.events.StallionEventConstants.NativeProdEventTypes;
|
|
10
12
|
|
|
11
13
|
import org.json.JSONObject;
|
|
14
|
+
|
|
15
|
+
import java.io.File;
|
|
12
16
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
13
17
|
|
|
14
18
|
public class StallionSyncHandler {
|
|
@@ -85,11 +89,13 @@ public class StallionSyncHandler {
|
|
|
85
89
|
|
|
86
90
|
StallionStateManager stateManager = StallionStateManager.getInstance();
|
|
87
91
|
String lastRolledBackHash = stateManager.stallionMeta.getLastRolledBackHash();
|
|
92
|
+
String lastUnverifiedHash = stateManager.getStallionConfig().getLastUnverifiedHash();
|
|
88
93
|
|
|
89
94
|
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
!newReleaseHash.isEmpty()
|
|
96
|
+
&& !newReleaseUrl.isEmpty()
|
|
97
|
+
&& !newReleaseHash.equals(lastRolledBackHash)
|
|
98
|
+
&& !newReleaseHash.equals(lastUnverifiedHash)
|
|
93
99
|
) {
|
|
94
100
|
if(stateManager.getIsMounted()) {
|
|
95
101
|
downloadNewRelease(newReleaseHash, newReleaseUrl);
|
|
@@ -112,6 +118,7 @@ public class StallionSyncHandler {
|
|
|
112
118
|
+ StallionConfigConstants.TEMP_FOLDER_SLOT;
|
|
113
119
|
String projectId = config.getProjectId();
|
|
114
120
|
String downloadUrl = newReleaseUrl + "?projectId=" + projectId;
|
|
121
|
+
String publicSigningKey = config.getPublicSigningKey();
|
|
115
122
|
|
|
116
123
|
long alreadyDownloaded = StallionDownloadCacheManager.getDownloadCache(config, downloadUrl, downloadPath);
|
|
117
124
|
|
|
@@ -131,6 +138,22 @@ public class StallionSyncHandler {
|
|
|
131
138
|
@Override
|
|
132
139
|
public void onSuccess(String successPayload) {
|
|
133
140
|
isDownloadInProgress.set(false);
|
|
141
|
+
StallionDownloadCacheManager.deleteDownloadCache(downloadPath);
|
|
142
|
+
|
|
143
|
+
if(publicSigningKey != null && !publicSigningKey.isEmpty()) {
|
|
144
|
+
if(
|
|
145
|
+
!StallionSignatureVerification.verifyReleaseSignature(
|
|
146
|
+
downloadPath + StallionConfigConstants.UNZIP_FOLDER_NAME,
|
|
147
|
+
publicSigningKey)
|
|
148
|
+
) {
|
|
149
|
+
// discard the downloaded release
|
|
150
|
+
config.setLastUnverifiedHash(newReleaseHash);
|
|
151
|
+
emitSignatureVerificationFailed(newReleaseHash);
|
|
152
|
+
StallionFileManager.deleteFileOrFolderSilently(new File(downloadPath));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
134
157
|
stateManager.stallionMeta.setCurrentProdSlot(StallionMetaConstants.SlotStates.NEW_SLOT);
|
|
135
158
|
stateManager.stallionMeta.setProdTempHash(newReleaseHash);
|
|
136
159
|
String currentProdNewHash = stateManager.stallionMeta.getProdNewHash();
|
|
@@ -138,7 +161,6 @@ public class StallionSyncHandler {
|
|
|
138
161
|
StallionSlotManager.stabilizeProd();
|
|
139
162
|
}
|
|
140
163
|
stateManager.syncStallionMeta();
|
|
141
|
-
StallionDownloadCacheManager.deleteDownloadCache(downloadPath);
|
|
142
164
|
emitDownloadSuccess(newReleaseHash);
|
|
143
165
|
}
|
|
144
166
|
|
|
@@ -198,4 +220,14 @@ public class StallionSyncHandler {
|
|
|
198
220
|
successPayload
|
|
199
221
|
);
|
|
200
222
|
}
|
|
223
|
+
private static void emitSignatureVerificationFailed(String releaseHash) {
|
|
224
|
+
JSONObject successPayload = new JSONObject();
|
|
225
|
+
try {
|
|
226
|
+
successPayload.put("releaseHash", releaseHash);
|
|
227
|
+
} catch (Exception ignored) { }
|
|
228
|
+
StallionEventManager.getInstance().sendEvent(
|
|
229
|
+
NativeProdEventTypes.SIGNATURE_VERIFICATION_FAILED.toString(),
|
|
230
|
+
successPayload
|
|
231
|
+
);
|
|
232
|
+
}
|
|
201
233
|
}
|
|
@@ -20,6 +20,9 @@ public class StallionConfig {
|
|
|
20
20
|
private final SharedPreferences sharedPreferences;
|
|
21
21
|
private final String filesDirectory;
|
|
22
22
|
private String lastDownloadingUrl;
|
|
23
|
+
private String lastUnverifiedHash;
|
|
24
|
+
private final String publicSigningKey;
|
|
25
|
+
|
|
23
26
|
|
|
24
27
|
public StallionConfig(Context context, SharedPreferences sharedPreferences) {
|
|
25
28
|
this.sharedPreferences = sharedPreferences;
|
|
@@ -32,6 +35,7 @@ public class StallionConfig {
|
|
|
32
35
|
parentPackageName
|
|
33
36
|
);
|
|
34
37
|
this.projectId = stallionProjectIdRes != 0 ?context.getString(stallionProjectIdRes) : "";
|
|
38
|
+
|
|
35
39
|
int stallionAppTokenRes = res.getIdentifier(
|
|
36
40
|
StallionConfigConstants.STALLION_APP_TOKEN_IDENTIFIER,
|
|
37
41
|
"string",
|
|
@@ -39,6 +43,13 @@ public class StallionConfig {
|
|
|
39
43
|
);
|
|
40
44
|
this.appToken = stallionAppTokenRes != 0 ? context.getString(stallionAppTokenRes) : "";
|
|
41
45
|
|
|
46
|
+
int stallionPublicKeyRes = res.getIdentifier(
|
|
47
|
+
StallionConfigConstants.STALLION_PUBLIC_SIGNING_KEY_IDENTIFIER,
|
|
48
|
+
"string",
|
|
49
|
+
parentPackageName
|
|
50
|
+
);
|
|
51
|
+
this.publicSigningKey = stallionPublicKeyRes != 0 ? context.getString(stallionPublicKeyRes) : "";
|
|
52
|
+
|
|
42
53
|
// get or generate UID
|
|
43
54
|
String cachedUniqueId = sharedPreferences.getString(
|
|
44
55
|
StallionConfigConstants.UNIQUE_ID_IDENTIFIER,
|
|
@@ -65,6 +76,7 @@ public class StallionConfig {
|
|
|
65
76
|
this.appVersion = fetchAppVersion(context);
|
|
66
77
|
this.filesDirectory = context.getFilesDir().getAbsolutePath();
|
|
67
78
|
this.lastDownloadingUrl = sharedPreferences.getString(StallionConfigConstants.LAST_DOWNLOADING_URL_IDENTIFIER, "");
|
|
79
|
+
this.lastUnverifiedHash = sharedPreferences.getString(StallionConfigConstants.LAST_UNVERIFIED_HASH, "");
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
public String getLastDownloadingUrl() {
|
|
@@ -78,6 +90,17 @@ public class StallionConfig {
|
|
|
78
90
|
editor.apply();
|
|
79
91
|
}
|
|
80
92
|
|
|
93
|
+
public String getLastUnverifiedHash() {
|
|
94
|
+
return this.lastUnverifiedHash;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public void setLastUnverifiedHash(String newUnverifiedHash) {
|
|
98
|
+
this.lastUnverifiedHash = newUnverifiedHash;
|
|
99
|
+
SharedPreferences.Editor editor = sharedPreferences.edit();
|
|
100
|
+
editor.putString(StallionConfigConstants.LAST_UNVERIFIED_HASH, newUnverifiedHash);
|
|
101
|
+
editor.apply();
|
|
102
|
+
}
|
|
103
|
+
|
|
81
104
|
private String fetchAppVersion(Context context) {
|
|
82
105
|
try {
|
|
83
106
|
PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
|
@@ -118,6 +141,10 @@ public class StallionConfig {
|
|
|
118
141
|
|
|
119
142
|
public String getFilesDirectory() { return this.filesDirectory; }
|
|
120
143
|
|
|
144
|
+
public String getPublicSigningKey() {
|
|
145
|
+
return this.publicSigningKey;
|
|
146
|
+
}
|
|
147
|
+
|
|
121
148
|
public JSONObject toJSON() {
|
|
122
149
|
JSONObject configJson = new JSONObject();
|
|
123
150
|
try {
|
|
@@ -7,9 +7,11 @@ public class StallionConfigConstants {
|
|
|
7
7
|
|
|
8
8
|
public static final String STALLION_PROJECT_ID_IDENTIFIER = "StallionProjectId";
|
|
9
9
|
public static final String STALLION_APP_TOKEN_IDENTIFIER = "StallionAppToken";
|
|
10
|
+
public static final String STALLION_PUBLIC_SIGNING_KEY_IDENTIFIER = "StallionPublicSigningKey";
|
|
10
11
|
public static final String UNIQUE_ID_IDENTIFIER = "stallionDeviceId";
|
|
11
12
|
public static final String API_KEY_IDENTIFIER = "x-sdk-access-token";
|
|
12
13
|
public static final String LAST_DOWNLOADING_URL_IDENTIFIER = "StallionLastDownloadingUrl";
|
|
14
|
+
public static final String LAST_UNVERIFIED_HASH = "LastUnverifiedHash";
|
|
13
15
|
|
|
14
16
|
public static final String PROD_DIRECTORY = "/StallionProd";
|
|
15
17
|
public static final String STAGE_DIRECTORY = "/StallionStage";
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
package com.stallion.utils;
|
|
2
|
+
|
|
3
|
+
import java.io.ByteArrayInputStream;
|
|
4
|
+
import java.io.File;
|
|
5
|
+
import java.io.FileInputStream;
|
|
6
|
+
import java.io.IOException;
|
|
7
|
+
import java.io.InputStream;
|
|
8
|
+
import java.nio.charset.StandardCharsets;
|
|
9
|
+
import java.security.DigestInputStream;
|
|
10
|
+
import java.security.KeyFactory;
|
|
11
|
+
import java.security.MessageDigest;
|
|
12
|
+
import java.security.PublicKey;
|
|
13
|
+
import java.security.Signature;
|
|
14
|
+
import java.security.spec.X509EncodedKeySpec;
|
|
15
|
+
import java.util.ArrayList;
|
|
16
|
+
import java.util.Collections;
|
|
17
|
+
import java.util.Scanner;
|
|
18
|
+
|
|
19
|
+
import org.json.JSONArray;
|
|
20
|
+
import org.json.JSONObject;
|
|
21
|
+
|
|
22
|
+
public class StallionSignatureVerification {
|
|
23
|
+
|
|
24
|
+
public static final String SIGNATURE_FILE_NAME = ".stallionsigned";
|
|
25
|
+
|
|
26
|
+
public static boolean verifyReleaseSignature(String downloadedBundlePath, String publicKeyPem) {
|
|
27
|
+
try {
|
|
28
|
+
File folderPath = new File(downloadedBundlePath);
|
|
29
|
+
File signatureFile = new File(folderPath, SIGNATURE_FILE_NAME);
|
|
30
|
+
if (!signatureFile.exists()) return false;
|
|
31
|
+
|
|
32
|
+
String jwt = readFileLegacy(signatureFile);
|
|
33
|
+
String[] jwtParts = jwt.split("\\.");
|
|
34
|
+
if (jwtParts.length != 3) return false;
|
|
35
|
+
|
|
36
|
+
String header = jwtParts[0];
|
|
37
|
+
String payload = jwtParts[1];
|
|
38
|
+
String signature = jwtParts[2];
|
|
39
|
+
|
|
40
|
+
byte[] signatureBytes = base64UrlDecode(signature);
|
|
41
|
+
byte[] signedContent = (header + "." + payload).getBytes(StandardCharsets.UTF_8);
|
|
42
|
+
|
|
43
|
+
String cleanedPem = publicKeyPem
|
|
44
|
+
.replace("-----BEGIN PUBLIC KEY-----", "")
|
|
45
|
+
.replace("-----END PUBLIC KEY-----", "")
|
|
46
|
+
.replaceAll("\\s", "");
|
|
47
|
+
|
|
48
|
+
byte[] decodedKey = android.util.Base64.decode(cleanedPem, android.util.Base64.DEFAULT);
|
|
49
|
+
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
|
|
50
|
+
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
|
51
|
+
PublicKey pubKey = keyFactory.generatePublic(keySpec);
|
|
52
|
+
|
|
53
|
+
Signature sig = Signature.getInstance("SHA256withRSA");
|
|
54
|
+
sig.initVerify(pubKey);
|
|
55
|
+
sig.update(signedContent);
|
|
56
|
+
if (!sig.verify(signatureBytes)) return false;
|
|
57
|
+
|
|
58
|
+
String jsonPayload = new String(base64UrlDecode(payload), StandardCharsets.UTF_8);
|
|
59
|
+
JSONObject payloadObj = new JSONObject(jsonPayload);
|
|
60
|
+
String expectedHash = payloadObj.optString("packageHash", "");
|
|
61
|
+
|
|
62
|
+
String actualHash = computeFolderHash(folderPath);
|
|
63
|
+
return expectedHash.equals(actualHash);
|
|
64
|
+
|
|
65
|
+
} catch (Exception e) {
|
|
66
|
+
e.printStackTrace();
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private static String computeFolderHash(File folder) throws Exception {
|
|
72
|
+
ArrayList<String> manifest = new ArrayList<>();
|
|
73
|
+
addFolderContentsToManifest(folder, "", manifest);
|
|
74
|
+
Collections.sort(manifest);
|
|
75
|
+
JSONArray jsonArray = new JSONArray();
|
|
76
|
+
for (String entry : manifest) {
|
|
77
|
+
jsonArray.put(entry);
|
|
78
|
+
}
|
|
79
|
+
String jsonString = jsonArray.toString().replace("\\/", "/");
|
|
80
|
+
return computeHash(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private static void addFolderContentsToManifest(File folder, String pathPrefix, ArrayList<String> manifest) throws Exception {
|
|
84
|
+
File[] files = folder.listFiles();
|
|
85
|
+
for (File file : files) {
|
|
86
|
+
String fileName = file.getName();
|
|
87
|
+
String relativePath = pathPrefix.isEmpty() ? fileName : pathPrefix + "/" + fileName;
|
|
88
|
+
if (isIgnored(relativePath)) continue;
|
|
89
|
+
if (file.isDirectory()) {
|
|
90
|
+
addFolderContentsToManifest(file, relativePath, manifest);
|
|
91
|
+
} else {
|
|
92
|
+
manifest.add(relativePath + ":" + computeHash(new FileInputStream(file)));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private static boolean isIgnored(String path) {
|
|
98
|
+
return path.equals(".DS_Store") || path.equals(SIGNATURE_FILE_NAME) || path.startsWith("__MACOSX/");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private static String computeHash(InputStream stream) throws Exception {
|
|
102
|
+
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
|
103
|
+
DigestInputStream dis = new DigestInputStream(stream, digest);
|
|
104
|
+
byte[] buffer = new byte[8192];
|
|
105
|
+
while (dis.read(buffer) != -1);
|
|
106
|
+
dis.close();
|
|
107
|
+
byte[] hash = digest.digest();
|
|
108
|
+
return String.format("%064x", new java.math.BigInteger(1, hash));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private static String readFileLegacy(File file) throws IOException {
|
|
112
|
+
Scanner scanner = new Scanner(file, "UTF-8");
|
|
113
|
+
StringBuilder sb = new StringBuilder();
|
|
114
|
+
while (scanner.hasNextLine()) {
|
|
115
|
+
sb.append(scanner.nextLine());
|
|
116
|
+
}
|
|
117
|
+
scanner.close();
|
|
118
|
+
return sb.toString();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private static byte[] base64UrlDecode(String input) {
|
|
122
|
+
String converted = input.replace('-', '+').replace('_', '/');
|
|
123
|
+
switch (converted.length() % 4) {
|
|
124
|
+
case 2: converted += "=="; break;
|
|
125
|
+
case 3: converted += "="; break;
|
|
126
|
+
}
|
|
127
|
+
return android.util.Base64.decode(converted, android.util.Base64.NO_WRAP);
|
|
128
|
+
}
|
|
129
|
+
}
|
package/ios/main/Stallion.swift
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// StallionConfig.h
|
|
3
3
|
// DoubleConversion
|
|
4
4
|
//
|
|
5
|
-
// Created by
|
|
5
|
+
// Created by Thor963 on 28/01/25.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
#import <Foundation/Foundation.h>
|
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
@property (nonatomic, copy) NSString *sdkToken;
|
|
16
16
|
@property (nonatomic, copy, readonly) NSString *appVersion;
|
|
17
17
|
@property (nonatomic, copy, readonly) NSString *filesDirectory;
|
|
18
|
+
@property (nonatomic, copy, readonly) NSString *publicSigningKey;
|
|
19
|
+
@property (nonatomic, copy) NSString *lastUnverifiedHash;
|
|
18
20
|
|
|
19
21
|
- (instancetype)initWithDefaults:(NSUserDefaults *)defaults;
|
|
20
22
|
|
|
21
23
|
- (void)updateSdkToken:(NSString *)newSdkToken;
|
|
24
|
+
- (void)updateLastUnverifiedHash:(NSString *)newUnverifiedHash;
|
|
22
25
|
- (NSDictionary *)toDictionary;
|
|
23
26
|
|
|
24
27
|
@end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// StallionConfig.m
|
|
3
3
|
// DoubleConversion
|
|
4
4
|
//
|
|
5
|
-
// Created by
|
|
5
|
+
// Created by Thor963 on 28/01/25.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
#import "StallionConfig.h"
|
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
_appToken = [[NSBundle mainBundle] objectForInfoDictionaryKey:STALLION_APP_TOKEN_IDENTIFIER] ?: @"";
|
|
18
18
|
_sdkToken = [defaults stringForKey:API_KEY_IDENTIFIER] ?: @"";
|
|
19
19
|
_appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:STALLION_APP_VERSION_IDENTIFIER] ?: @"";
|
|
20
|
-
|
|
21
20
|
_filesDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject ?: @"";
|
|
21
|
+
_publicSigningKey = [[NSBundle mainBundle] objectForInfoDictionaryKey:STALLION_PUBLIC_SIGNING_KEY_IDENTIFIER] ?: @"";
|
|
22
|
+
_lastUnverifiedHash = [defaults stringForKey:LAST_UNVERIFIED_KEY_IDENTIFIER] ?: @"";
|
|
22
23
|
|
|
23
24
|
NSString *cachedUid = [defaults stringForKey:UNIQUE_ID_IDENTIFIER];
|
|
24
25
|
if (cachedUid && ![cachedUid isEqualToString:@""]) {
|
|
@@ -44,6 +45,12 @@
|
|
|
44
45
|
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
- (void)updateLastUnverifiedHash:(NSString *)newUnverifiedHash {
|
|
49
|
+
_lastUnverifiedHash = newUnverifiedHash ?: @"";
|
|
50
|
+
[[NSUserDefaults standardUserDefaults] setObject:_lastUnverifiedHash forKey:LAST_UNVERIFIED_KEY_IDENTIFIER];
|
|
51
|
+
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
52
|
+
}
|
|
53
|
+
|
|
47
54
|
- (NSDictionary *)toDictionary {
|
|
48
55
|
@try {
|
|
49
56
|
return @{
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// StallionConfigConstants.h
|
|
3
3
|
// DoubleConversion
|
|
4
4
|
//
|
|
5
|
-
// Created by
|
|
5
|
+
// Created by Thor963 on 28/01/25.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
#import <Foundation/Foundation.h>
|
|
@@ -29,4 +29,8 @@ extern NSString *const UNZIP_FOLDER_NAME;
|
|
|
29
29
|
|
|
30
30
|
extern NSString *const STALLION_META_IDENTIFIER;
|
|
31
31
|
|
|
32
|
+
extern NSString *const STALLION_PUBLIC_SIGNING_KEY_IDENTIFIER;
|
|
33
|
+
|
|
34
|
+
extern NSString *const LAST_UNVERIFIED_KEY_IDENTIFIER;
|
|
35
|
+
|
|
32
36
|
@end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// StallionConfigConstants.m
|
|
3
3
|
// DoubleConversion
|
|
4
4
|
//
|
|
5
|
-
// Created by
|
|
5
|
+
// Created by Thor963 on 28/01/25.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
#import "StallionConfigConstants.h"
|
|
@@ -29,4 +29,8 @@ NSString *const ANDROID_BUNDLE_FILE_NAME = @"/index.android.bundle";
|
|
|
29
29
|
NSString *const DEFAULT_JS_BUNDLE_LOCATION_BASE = @"assets:/";
|
|
30
30
|
NSString *const UNZIP_FOLDER_NAME = @"/build";
|
|
31
31
|
|
|
32
|
+
NSString *const STALLION_PUBLIC_SIGNING_KEY_IDENTIFIER = @"StallionPublicSigningKey";
|
|
33
|
+
|
|
34
|
+
NSString *const LAST_UNVERIFIED_KEY_IDENTIFIER = @"LastUnverifiedHash";
|
|
35
|
+
|
|
32
36
|
@end
|
|
@@ -78,6 +78,7 @@ class StallionConstants {
|
|
|
78
78
|
static let INSTALLED_PROD = "INSTALLED_PROD"
|
|
79
79
|
static let STABILIZED_PROD = "STABILIZED_PROD"
|
|
80
80
|
static let EXCEPTION_PROD = "EXCEPTION_PROD"
|
|
81
|
+
static let SIGNATURE_VERIFICATION_FAILED = "SIGNATURE_VERIFICATION_FAILED"
|
|
81
82
|
}
|
|
82
83
|
public struct NativeEventTypesStage {
|
|
83
84
|
static let DOWNLOAD_STARTED_STAGE = "DOWNLOAD_STARTED_STAGE"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// StallionEventHandler.m
|
|
3
3
|
// DoubleConversion
|
|
4
4
|
//
|
|
5
|
-
// Created by
|
|
5
|
+
// Created by Thor963 on 28/01/25.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
#import "StallionEventHandler.h"
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
static NSString *const STALLION_NATIVE_EVENT_NAME = @"STALLION_NATIVE_EVENT";
|
|
16
16
|
static NSString *const EVENTS_KEY = @"stored_events";
|
|
17
17
|
static NSInteger const MAX_BATCH_COUNT_SIZE = 9;
|
|
18
|
+
static NSInteger const MAX_EVENT_STORAGE_LIMIT = 20;
|
|
18
19
|
|
|
19
20
|
@implementation StallionEventHandler
|
|
20
21
|
|
|
@@ -30,11 +31,11 @@ static NSInteger const MAX_BATCH_COUNT_SIZE = 9;
|
|
|
30
31
|
// Emit event to React Native and store locally
|
|
31
32
|
- (void)cacheEvent:(NSString *)eventName eventPayload:(NSDictionary *)eventPayload {
|
|
32
33
|
StallionStateManager *stallionStateManager = [StallionStateManager sharedInstance];
|
|
33
|
-
|
|
34
|
+
|
|
34
35
|
NSString *projectId = stallionStateManager.stallionConfig.projectId ?: @"";
|
|
35
36
|
NSString *appVersion = stallionStateManager.stallionConfig.appVersion ?: @"";
|
|
36
37
|
NSString *uid = stallionStateManager.stallionConfig.uid ?: @"";
|
|
37
|
-
|
|
38
|
+
|
|
38
39
|
NSMutableDictionary *mutablePayload = [eventPayload mutableCopy];
|
|
39
40
|
NSString *uniqueId = [[NSUUID UUID] UUIDString];
|
|
40
41
|
mutablePayload[@"eventId"] = uniqueId;
|
|
@@ -62,6 +63,11 @@ static NSInteger const MAX_BATCH_COUNT_SIZE = 9;
|
|
|
62
63
|
eventsObject = [NSMutableDictionary dictionary];
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
// Enforce max size
|
|
67
|
+
if (eventsObject.count >= MAX_EVENT_STORAGE_LIMIT) {
|
|
68
|
+
eventsObject = [NSMutableDictionary dictionary];
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
eventsObject[uniqueId] = eventPayload;
|
|
66
72
|
|
|
67
73
|
NSData *updatedData = [NSJSONSerialization dataWithJSONObject:eventsObject options:0 error:nil];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// StallionExceptionHandler.m
|
|
3
3
|
// react-native-stallion
|
|
4
4
|
//
|
|
5
|
-
// Created by
|
|
5
|
+
// Created by Thor963 on 29/01/25.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
#import "StallionExceptionHandler.h"
|
|
@@ -12,29 +12,42 @@
|
|
|
12
12
|
#import "StallionMeta.h"
|
|
13
13
|
#import "StallionMetaConstants.h"
|
|
14
14
|
#import "StallionObjConstants.h"
|
|
15
|
+
#import <signal.h>
|
|
16
|
+
#import <execinfo.h>
|
|
17
|
+
|
|
18
|
+
// Forward declarations
|
|
19
|
+
void handleException(NSException *exception);
|
|
20
|
+
void handleSignal(int signal);
|
|
15
21
|
|
|
16
22
|
@implementation StallionExceptionHandler
|
|
17
23
|
|
|
18
24
|
NSUncaughtExceptionHandler *_defaultExceptionHandler;
|
|
25
|
+
BOOL exceptionAlertDismissed = FALSE;
|
|
19
26
|
|
|
20
27
|
+ (void)initExceptionHandler {
|
|
21
28
|
if (!_defaultExceptionHandler) {
|
|
22
29
|
_defaultExceptionHandler = NSGetUncaughtExceptionHandler();
|
|
23
30
|
}
|
|
24
31
|
NSSetUncaughtExceptionHandler(&handleException);
|
|
32
|
+
|
|
33
|
+
signal(SIGABRT, handleSignal);
|
|
34
|
+
signal(SIGILL, handleSignal);
|
|
35
|
+
signal(SIGSEGV, handleSignal);
|
|
36
|
+
signal(SIGFPE, handleSignal);
|
|
37
|
+
signal(SIGBUS, handleSignal);
|
|
25
38
|
}
|
|
26
39
|
|
|
27
|
-
|
|
40
|
+
@end
|
|
28
41
|
|
|
29
|
-
|
|
42
|
+
#pragma mark - Shared Rollback Logic
|
|
43
|
+
|
|
44
|
+
static void performStallionRollback(NSString *errorString) {
|
|
30
45
|
StallionStateManager *stateManager = [StallionStateManager sharedInstance];
|
|
31
46
|
StallionMeta *meta = stateManager.stallionMeta;
|
|
32
47
|
BOOL isAutoRollback = !stateManager.isMounted;
|
|
33
|
-
|
|
34
|
-
NSString *readableError = [exception reason];
|
|
35
48
|
|
|
36
|
-
if (
|
|
37
|
-
|
|
49
|
+
if (errorString.length > 900) {
|
|
50
|
+
errorString = [errorString substringToIndex:900];
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
if (meta.switchState == SwitchStateProd) {
|
|
@@ -42,28 +55,32 @@ void handleException(NSException *exception) {
|
|
|
42
55
|
|
|
43
56
|
[[StallionEventHandler sharedInstance] cacheEvent:StallionObjConstants.exception_prod_event
|
|
44
57
|
eventPayload:@{
|
|
45
|
-
@"meta":
|
|
58
|
+
@"meta": errorString,
|
|
46
59
|
StallionObjConstants.release_hash_key: currentHash,
|
|
47
60
|
StallionObjConstants.is_auto_rollback_key: isAutoRollback ? @"true" : @"false"
|
|
48
61
|
}];
|
|
49
62
|
if (isAutoRollback) {
|
|
50
|
-
[StallionSlotManager rollbackProdWithAutoRollback:YES errorString:
|
|
63
|
+
[StallionSlotManager rollbackProdWithAutoRollback:YES errorString:errorString];
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
} else if (meta.switchState == SwitchStateStage) {
|
|
54
|
-
|
|
67
|
+
|
|
68
|
+
if(isAutoRollback) {
|
|
69
|
+
[StallionSlotManager rollbackStage];
|
|
70
|
+
}
|
|
55
71
|
|
|
56
72
|
[[StallionEventHandler sharedInstance] cacheEvent:StallionObjConstants.exception_stage_event
|
|
57
73
|
eventPayload:@{
|
|
58
|
-
@"meta":
|
|
74
|
+
@"meta": errorString,
|
|
59
75
|
StallionObjConstants.release_hash_key: meta.stageNewHash,
|
|
60
76
|
StallionObjConstants.is_auto_rollback_key: isAutoRollback ? @"true" : @"false"
|
|
61
77
|
}];
|
|
62
78
|
|
|
79
|
+
if(!exceptionAlertDismissed) {
|
|
63
80
|
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Stallion Exception Handler"
|
|
64
81
|
message:[NSString stringWithFormat:@"%@\n%@",
|
|
65
82
|
@"A crash occurred in the app. Build was rolled back. Check crash report below. Continue crash to invoke other exception handlers. \n \n",
|
|
66
|
-
|
|
83
|
+
errorString]
|
|
67
84
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
68
85
|
|
|
69
86
|
[alert addAction:[UIAlertAction actionWithTitle:@"Continue Crash"
|
|
@@ -82,13 +99,86 @@ void handleException(NSException *exception) {
|
|
|
82
99
|
while (exceptionAlertDismissed == FALSE) {
|
|
83
100
|
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
|
84
101
|
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (meta.switchState == SwitchStateProd) {
|
|
106
|
+
NSString *currentHash = [meta getActiveReleaseHash] ?: @"";
|
|
107
|
+
|
|
108
|
+
[[StallionEventHandler sharedInstance] cacheEvent:StallionObjConstants.exception_prod_event
|
|
109
|
+
eventPayload:@{
|
|
110
|
+
@"meta": errorString,
|
|
111
|
+
StallionObjConstants.release_hash_key: currentHash,
|
|
112
|
+
StallionObjConstants.is_auto_rollback_key: isAutoRollback ? @"true" : @"false"
|
|
113
|
+
}];
|
|
114
|
+
if (isAutoRollback) {
|
|
115
|
+
[StallionSlotManager rollbackProdWithAutoRollback:YES errorString:errorString];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
} else if (meta.switchState == SwitchStateStage) {
|
|
119
|
+
|
|
120
|
+
if(isAutoRollback) {
|
|
121
|
+
[StallionSlotManager rollbackStage];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
[[StallionEventHandler sharedInstance] cacheEvent:StallionObjConstants.exception_stage_event
|
|
125
|
+
eventPayload:@{
|
|
126
|
+
@"meta": errorString,
|
|
127
|
+
StallionObjConstants.release_hash_key: meta.stageNewHash,
|
|
128
|
+
StallionObjConstants.is_auto_rollback_key: isAutoRollback ? @"true" : @"false"
|
|
129
|
+
}];
|
|
130
|
+
|
|
131
|
+
if(!exceptionAlertDismissed) {
|
|
132
|
+
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Stallion Exception Handler"
|
|
133
|
+
message:[NSString stringWithFormat:@"%@\n%@",
|
|
134
|
+
@"A crash occurred in the app. Build was rolled back. Check crash report below. Continue crash to invoke other exception handlers.\n\n",
|
|
135
|
+
errorString]
|
|
136
|
+
preferredStyle:UIAlertControllerStyleAlert];
|
|
137
|
+
|
|
138
|
+
[alert addAction:[UIAlertAction actionWithTitle:@"Continue Crash"
|
|
139
|
+
style:UIAlertActionStyleDefault
|
|
140
|
+
handler:^(UIAlertAction *action) {
|
|
141
|
+
exceptionAlertDismissed = TRUE;
|
|
142
|
+
}]];
|
|
143
|
+
|
|
144
|
+
UIApplication *app = [UIApplication sharedApplication];
|
|
145
|
+
UIViewController *rootViewController = app.delegate.window.rootViewController;
|
|
146
|
+
|
|
147
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
148
|
+
[rootViewController presentViewController:alert animated:YES completion:nil];
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
while (exceptionAlertDismissed == FALSE) {
|
|
152
|
+
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
85
155
|
}
|
|
156
|
+
}
|
|
86
157
|
|
|
87
|
-
|
|
158
|
+
void handleException(NSException *exception) {
|
|
159
|
+
NSString *readableError = [exception reason] ?: @"Unknown exception";
|
|
160
|
+
performStallionRollback(readableError);
|
|
161
|
+
|
|
162
|
+
// Call default exception handler if available
|
|
88
163
|
if (_defaultExceptionHandler) {
|
|
89
164
|
_defaultExceptionHandler(exception);
|
|
90
165
|
}
|
|
91
166
|
}
|
|
92
167
|
|
|
93
|
-
|
|
168
|
+
void handleSignal(int signalVal) {
|
|
169
|
+
void *callstack[128];
|
|
170
|
+
int frames = backtrace(callstack, 128);
|
|
171
|
+
char **symbols = backtrace_symbols(callstack, frames);
|
|
94
172
|
|
|
173
|
+
NSMutableString *stackTrace = [NSMutableString stringWithFormat:@"Signal %d was raised.\n", signalVal];
|
|
174
|
+
for (int i = 0; i < frames; ++i) {
|
|
175
|
+
[stackTrace appendFormat:@"%s\n", symbols[i]];
|
|
176
|
+
}
|
|
177
|
+
free(symbols);
|
|
178
|
+
|
|
179
|
+
performStallionRollback(stackTrace);
|
|
180
|
+
|
|
181
|
+
// Restore default and raise to proceed with crash
|
|
182
|
+
signal(signalVal, SIG_DFL);
|
|
183
|
+
raise(signalVal);
|
|
184
|
+
}
|
package/ios/main/StallionMeta.h
CHANGED
package/ios/main/StallionMeta.m
CHANGED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CommonCrypto
|
|
3
|
+
import Security
|
|
4
|
+
|
|
5
|
+
class StallionSignatureVerification {
|
|
6
|
+
|
|
7
|
+
static let signatureFileName = ".stallionsigned"
|
|
8
|
+
|
|
9
|
+
static func verifyReleaseSignature(downloadedBundlePath: String, publicKeyPem: String) -> Bool {
|
|
10
|
+
do {
|
|
11
|
+
let signatureFilePath = (downloadedBundlePath as NSString).appendingPathComponent(signatureFileName)
|
|
12
|
+
guard FileManager.default.fileExists(atPath: signatureFilePath) else { return false }
|
|
13
|
+
|
|
14
|
+
let jwt = try String(contentsOfFile: signatureFilePath, encoding: .utf8)
|
|
15
|
+
let parts = jwt.components(separatedBy: ".")
|
|
16
|
+
guard parts.count == 3 else { return false }
|
|
17
|
+
let header = parts[0]
|
|
18
|
+
let payload = parts[1]
|
|
19
|
+
let signatureB64 = parts[2]
|
|
20
|
+
|
|
21
|
+
guard let signatureData = base64UrlDecode(signatureB64),
|
|
22
|
+
let signedData = (header + "." + payload).data(using: .utf8) else { return false }
|
|
23
|
+
|
|
24
|
+
guard let pubKey = try? convertPemToPublicKey(pemString: publicKeyPem) else { return false }
|
|
25
|
+
|
|
26
|
+
var error: Unmanaged<CFError>?
|
|
27
|
+
let verified = SecKeyVerifySignature(
|
|
28
|
+
pubKey,
|
|
29
|
+
.rsaSignatureMessagePKCS1v15SHA256,
|
|
30
|
+
signedData as CFData,
|
|
31
|
+
signatureData as CFData,
|
|
32
|
+
&error
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
guard verified else { return false }
|
|
36
|
+
|
|
37
|
+
guard let payloadData = base64UrlDecode(payload),
|
|
38
|
+
let payloadJson = try JSONSerialization.jsonObject(with: payloadData) as? [String: Any],
|
|
39
|
+
let expectedHash = payloadJson["packageHash"] as? String else { return false }
|
|
40
|
+
|
|
41
|
+
let actualHash = try computeCodePushStyleHash(folderPath: downloadedBundlePath)
|
|
42
|
+
return expectedHash == actualHash
|
|
43
|
+
|
|
44
|
+
} catch {
|
|
45
|
+
print("Signature verification error: \(error)")
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private static func computeCodePushStyleHash(folderPath: String) throws -> String {
|
|
51
|
+
var manifest: [String] = []
|
|
52
|
+
try addContentsOfFolder(to: &manifest, folderPath: folderPath, pathPrefix: "")
|
|
53
|
+
let sortedManifest = manifest.sorted()
|
|
54
|
+
let jsonData = try JSONSerialization.data(withJSONObject: sortedManifest, options: [])
|
|
55
|
+
var jsonString = String(data: jsonData, encoding: .utf8) ?? ""
|
|
56
|
+
jsonString = jsonString.replacingOccurrences(of: "\\/", with: "/")
|
|
57
|
+
return sha256Hex(jsonString)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private static func addContentsOfFolder(to manifest: inout [String], folderPath: String, pathPrefix: String) throws {
|
|
61
|
+
let items = try FileManager.default.contentsOfDirectory(atPath: folderPath)
|
|
62
|
+
for item in items {
|
|
63
|
+
let fullPath = (folderPath as NSString).appendingPathComponent(item)
|
|
64
|
+
let relativePath = pathPrefix.isEmpty ? item : (pathPrefix as NSString).appendingPathComponent(item)
|
|
65
|
+
|
|
66
|
+
if isIgnored(relativePath) { continue }
|
|
67
|
+
|
|
68
|
+
var isDir: ObjCBool = false
|
|
69
|
+
FileManager.default.fileExists(atPath: fullPath, isDirectory: &isDir)
|
|
70
|
+
|
|
71
|
+
if isDir.boolValue {
|
|
72
|
+
try addContentsOfFolder(to: &manifest, folderPath: fullPath, pathPrefix: relativePath)
|
|
73
|
+
} else {
|
|
74
|
+
let data = try Data(contentsOf: URL(fileURLWithPath: fullPath))
|
|
75
|
+
let hash = sha256Hex(data)
|
|
76
|
+
manifest.append("\(relativePath):\(hash)")
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private static func isIgnored(_ path: String) -> Bool {
|
|
82
|
+
return path.hasPrefix("__MACOSX/") ||
|
|
83
|
+
path == ".DS_Store" ||
|
|
84
|
+
path.hasSuffix("/.DS_Store") ||
|
|
85
|
+
path == signatureFileName ||
|
|
86
|
+
path.hasSuffix("/\(signatureFileName)")
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private static func sha256Hex(_ input: String) -> String {
|
|
90
|
+
guard let data = input.data(using: .utf8) else { return "" }
|
|
91
|
+
return sha256Hex(data)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private static func sha256Hex(_ data: Data) -> String {
|
|
95
|
+
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
|
|
96
|
+
data.withUnsafeBytes {
|
|
97
|
+
_ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &digest)
|
|
98
|
+
}
|
|
99
|
+
return digest.map { String(format: "%02x", $0) }.joined()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private static func base64UrlDecode(_ input: String) -> Data? {
|
|
103
|
+
var base64 = input.replacingOccurrences(of: "-", with: "+")
|
|
104
|
+
.replacingOccurrences(of: "_", with: "/")
|
|
105
|
+
let padding = base64.count % 4
|
|
106
|
+
if padding > 0 {
|
|
107
|
+
base64 += String(repeating: "=", count: 4 - padding)
|
|
108
|
+
}
|
|
109
|
+
return Data(base64Encoded: base64)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private static func convertPemToPublicKey(pemString: String) throws -> SecKey {
|
|
113
|
+
let cleaned = pemString
|
|
114
|
+
.replacingOccurrences(of: "-----BEGIN PUBLIC KEY-----\n", with: "")
|
|
115
|
+
.replacingOccurrences(of: "-----END PUBLIC KEY-----", with: "")
|
|
116
|
+
.replacingOccurrences(of: "\n", with: "")
|
|
117
|
+
.replacingOccurrences(of: "\r", with: "")
|
|
118
|
+
|
|
119
|
+
guard let keyData = Data(base64Encoded: cleaned, options: .ignoreUnknownCharacters) else {
|
|
120
|
+
throw NSError(domain: "Stallion", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid base64 key"])
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let keyDict: [String: Any] = [
|
|
124
|
+
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
|
|
125
|
+
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
|
|
126
|
+
kSecAttrKeySizeInBits as String: 2048
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
var error: Unmanaged<CFError>?
|
|
130
|
+
guard let secKey = SecKeyCreateWithData(keyData as CFData, keyDict as CFDictionary, &error) else {
|
|
131
|
+
throw error!.takeRetainedValue() as Error
|
|
132
|
+
}
|
|
133
|
+
return secKey
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// StallionSyncHandler.swift
|
|
3
3
|
// react-native-stallion
|
|
4
4
|
//
|
|
5
|
-
// Created by
|
|
5
|
+
// Created by Thor963 on 29/01/25.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
import Foundation
|
|
@@ -152,8 +152,9 @@ class StallionSyncHandler {
|
|
|
152
152
|
|
|
153
153
|
let stateManager = StallionStateManager.sharedInstance()
|
|
154
154
|
let lastRolledBackHash = stateManager?.stallionMeta?.lastRolledBackHash ?? ""
|
|
155
|
+
let lastUnverifiedHash = stateManager?.stallionConfig?.lastUnverifiedHash ?? ""
|
|
155
156
|
|
|
156
|
-
if newReleaseHash != lastRolledBackHash {
|
|
157
|
+
if newReleaseHash != lastRolledBackHash && newReleaseHash != lastUnverifiedHash {
|
|
157
158
|
if stateManager?.isMounted == true {
|
|
158
159
|
downloadNewRelease(newReleaseHash: newReleaseHash, newReleaseUrl: newReleaseUrl)
|
|
159
160
|
} else {
|
|
@@ -179,29 +180,52 @@ class StallionSyncHandler {
|
|
|
179
180
|
let downloadPath = config.filesDirectory + "/" + StallionConstants.PROD_DIRECTORY + "/" + StallionConstants.TEMP_FOLDER_SLOT
|
|
180
181
|
let projectId = config.projectId ?? ""
|
|
181
182
|
|
|
183
|
+
let publicSigningKey = config.publicSigningKey ?? ""
|
|
184
|
+
|
|
182
185
|
guard let fromUrl = URL(string: newReleaseUrl + "?projectId=" + projectId) else { return }
|
|
183
186
|
|
|
184
187
|
emitDownloadStarted(releaseHash: newReleaseHash)
|
|
185
188
|
|
|
186
|
-
StallionFileDownloader().downloadBundle(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
189
|
+
StallionFileDownloader().downloadBundle(
|
|
190
|
+
url: fromUrl,
|
|
191
|
+
downloadDirectory: downloadPath,
|
|
192
|
+
onProgress: { progress in
|
|
193
|
+
// Handle progress updates if necessary
|
|
194
|
+
},
|
|
195
|
+
resolve: { _ in
|
|
196
|
+
completeDownload()
|
|
197
|
+
|
|
198
|
+
if(publicSigningKey != nil && !publicSigningKey.isEmpty) {
|
|
199
|
+
if(
|
|
200
|
+
!StallionSignatureVerification.verifyReleaseSignature(
|
|
201
|
+
downloadedBundlePath: downloadPath + "/" + StallionConstants.FilePaths.ZipFolderName,
|
|
202
|
+
publicKeyPem: publicSigningKey
|
|
203
|
+
)
|
|
204
|
+
) {
|
|
205
|
+
// discard downloaded release
|
|
206
|
+
config.updateLastUnverifiedHash(newReleaseHash)
|
|
207
|
+
emitSignatureVerificationFailed(releaseHash: newReleaseHash)
|
|
208
|
+
StallionFileManager.deleteFileOrFolderSilently(downloadPath)
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
stateManager.stallionMeta?.currentProdSlot = SlotStates.newSlot
|
|
213
|
+
stateManager.stallionMeta?.prodTempHash = newReleaseHash
|
|
214
|
+
if let currentProdNewHash = stateManager.stallionMeta?.prodNewHash,
|
|
215
|
+
!currentProdNewHash.isEmpty {
|
|
216
|
+
StallionSlotManager.stabilizeProd()
|
|
217
|
+
}
|
|
218
|
+
stateManager.syncStallionMeta()
|
|
219
|
+
emitDownloadSuccess(releaseHash: newReleaseHash)
|
|
220
|
+
},
|
|
221
|
+
reject: { code, prefix, error in
|
|
222
|
+
completeDownload()
|
|
223
|
+
emitDownloadError(
|
|
224
|
+
releaseHash: newReleaseHash,
|
|
225
|
+
error: "\(String(describing: prefix))\(String(describing: error))"
|
|
226
|
+
)
|
|
227
|
+
}
|
|
203
228
|
)
|
|
204
|
-
})
|
|
205
229
|
}
|
|
206
230
|
|
|
207
231
|
private static func completeDownload() {
|
|
@@ -246,5 +270,13 @@ class StallionSyncHandler {
|
|
|
246
270
|
shouldCache: true
|
|
247
271
|
)
|
|
248
272
|
}
|
|
273
|
+
|
|
274
|
+
private static func emitSignatureVerificationFailed(releaseHash: String) {
|
|
275
|
+
let verificationFailurePayload: NSDictionary = ["releaseHash": releaseHash]
|
|
276
|
+
Stallion.sendEventToRn(eventName: StallionConstants.NativeEventTypesProd.SIGNATURE_VERIFICATION_FAILED,
|
|
277
|
+
eventBody: verificationFailurePayload,
|
|
278
|
+
shouldCache: true
|
|
279
|
+
)
|
|
280
|
+
}
|
|
249
281
|
}
|
|
250
282
|
|
package/package.json
CHANGED
|
@@ -94,9 +94,9 @@ export const useStallionEvents = (refreshMeta, setProgress, configState) => {
|
|
|
94
94
|
};
|
|
95
95
|
}, [refreshMeta, setProgress, popEventsDebounced]);
|
|
96
96
|
useEffect(() => {
|
|
97
|
-
|
|
97
|
+
setTimeout(() => {
|
|
98
98
|
onLaunchNative('Success');
|
|
99
|
-
});
|
|
99
|
+
}, 100);
|
|
100
100
|
}, []);
|
|
101
101
|
useEffect(() => {
|
|
102
102
|
popEventsDebounced();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useEffect","useCallback","useMemo","NativeEventEmitter","NativeEventTypesProd","STALLION_NATIVE_EVENT","NativeEventTypesStage","stallionEventEmitter","acknowledgeEventsNative","onLaunchNative","popEventsNative","StallionNativeModule","useApiClient","API_PATHS","debounce","REFRESH_META_EVENTS","DOWNLOAD_COMPLETE_PROD","ROLLED_BACK_PROD","AUTO_ROLLED_BACK_PROD","STABILIZED_PROD","DOWNLOAD_COMPLETE_STAGE","DOWNLOAD_ERROR_STAGE","INSTALLED_STAGE","STALLION_EVENT_DEBOUNCE_INTERVAL","processStallionEvent","eventString","JSON","parse","_","useStallionEvents","refreshMeta","setProgress","configState","getData","syncStallionEvents","stallionEvents","projectId","LOG_EVENTS","eventData","then","res","success","eventIds","map","event","eventId","eventIdString","stringify","popEvents","eventsString","eventsArr","length","requestAnimationFrame","popEventsDebounced","eventEmitter","addListener","nativeEventString","eventType","type","DOWNLOAD_STARTED_PROD","DOWNLOAD_ERROR_PROD","INSTALLED_PROD","SYNC_ERROR_PROD","EXCEPTION_PROD","emit","DOWNLOAD_PROGRESS_STAGE","progress","Number","removeAllListeners"],"sourceRoot":"../../../../src","sources":["main/state/useStallionEvents.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"names":["useEffect","useCallback","useMemo","NativeEventEmitter","NativeEventTypesProd","STALLION_NATIVE_EVENT","NativeEventTypesStage","stallionEventEmitter","acknowledgeEventsNative","onLaunchNative","popEventsNative","StallionNativeModule","useApiClient","API_PATHS","debounce","REFRESH_META_EVENTS","DOWNLOAD_COMPLETE_PROD","ROLLED_BACK_PROD","AUTO_ROLLED_BACK_PROD","STABILIZED_PROD","DOWNLOAD_COMPLETE_STAGE","DOWNLOAD_ERROR_STAGE","INSTALLED_STAGE","STALLION_EVENT_DEBOUNCE_INTERVAL","processStallionEvent","eventString","JSON","parse","_","useStallionEvents","refreshMeta","setProgress","configState","getData","syncStallionEvents","stallionEvents","projectId","LOG_EVENTS","eventData","then","res","success","eventIds","map","event","eventId","eventIdString","stringify","popEvents","eventsString","eventsArr","length","requestAnimationFrame","popEventsDebounced","eventEmitter","addListener","nativeEventString","eventType","type","DOWNLOAD_STARTED_PROD","DOWNLOAD_ERROR_PROD","INSTALLED_PROD","SYNC_ERROR_PROD","EXCEPTION_PROD","emit","DOWNLOAD_PROGRESS_STAGE","progress","Number","removeAllListeners","setTimeout"],"sourceRoot":"../../../../src","sources":["main/state/useStallionEvents.ts"],"mappings":"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"}
|
|
@@ -32,5 +32,8 @@ export const onLaunchNative = StallionNativeModule === null || StallionNativeMod
|
|
|
32
32
|
export const sync = StallionNativeModule === null || StallionNativeModule === void 0 ? void 0 : StallionNativeModule.sync;
|
|
33
33
|
export const popEventsNative = StallionNativeModule === null || StallionNativeModule === void 0 ? void 0 : StallionNativeModule.popEvents;
|
|
34
34
|
export const acknowledgeEventsNative = StallionNativeModule === null || StallionNativeModule === void 0 ? void 0 : StallionNativeModule.acknowledgeEvents;
|
|
35
|
-
export const restart =
|
|
35
|
+
export const restart = () => {
|
|
36
|
+
var _StallionNativeModule;
|
|
37
|
+
StallionNativeModule === null || StallionNativeModule === void 0 ? void 0 : (_StallionNativeModule = StallionNativeModule.restart) === null || _StallionNativeModule === void 0 ? void 0 : _StallionNativeModule.call(StallionNativeModule);
|
|
38
|
+
};
|
|
36
39
|
//# sourceMappingURL=StallionNativeUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["StallionNativeModule","setSdkTokenNative","updateSdkToken","getStallionMetaNative","Promise","resolve","reject","getStallionMeta","then","metaString","JSON","parse","_","catch","getStallionConfigNative","getStallionConfig","configString","toggleStallionSwitchNative","toggleStallionSwitch","downloadBundleNative","downloadStageBundle","onLaunchNative","onLaunch","sync","popEventsNative","popEvents","acknowledgeEventsNative","acknowledgeEvents","restart"],"sourceRoot":"../../../../src","sources":["main/utils/StallionNativeUtils.ts"],"mappings":"AAAA,OAAOA,oBAAoB,MAAM,4BAA4B;AAW7D,OAAO,MAAMC,iBAAqC,GAChDD,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEE,cAAc;AAEtC,OAAO,MAAMC,qBAA6C,GAAGA,CAAA,KAAM;EACjE,OAAO,IAAIC,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;IACtCN,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEO,eAAe,CAAC,CAAC,CACpCC,IAAI,CAAEC,UAAkB,IAAK;MAC5B,IAAI;QACFJ,OAAO,CAACK,IAAI,CAACC,KAAK,CAACF,UAAU,CAAC,CAAC;MACjC,CAAC,CAAC,OAAOG,CAAC,EAAE;QACVN,MAAM,CAAC,qBAAqB,CAAC;MAC/B;IACF,CAAC,CAAC,CACDO,KAAK,CAAC,MAAM;MACXP,MAAM,CAAC,6BAA6B,CAAC;IACvC,CAAC,CAAC;EACN,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,MAAMQ,uBAAiD,GAAGA,CAAA,KAAM;EACrE,OAAO,IAAIV,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;IACtCN,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEe,iBAAiB,CAAC,CAAC,CACtCP,IAAI,CAAEQ,YAAoB,IAAK;MAC9B,IAAI;QACFX,OAAO,CAACK,IAAI,CAACC,KAAK,CAACK,YAAY,CAAC,CAAC;MACnC,CAAC,CAAC,OAAOJ,CAAC,EAAE;QACVN,MAAM,CAAC,uBAAuB,CAAC;MACjC;IACF,CAAC,CAAC,CACDO,KAAK,CAAC,MAAM;MACXP,MAAM,CAAC,+BAA+B,CAAC;IACzC,CAAC,CAAC;EACN,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,MAAMW,0BAAuD,GAClEjB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEkB,oBAAoB;AAE5C,OAAO,MAAMC,oBAA2C,GACtDnB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEoB,mBAAmB;AAE3C,OAAO,MAAMC,cAAqC,GAChDrB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEsB,QAAQ;AAEhC,OAAO,MAAMC,IAAgB,GAAGvB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEuB,IAAI;AAE1D,OAAO,MAAMC,eAAsC,GACjDxB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEyB,SAAS;AAEjC,OAAO,MAAMC,uBAA8D,GACzE1B,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAE2B,iBAAiB;AAEzC,OAAO,MAAMC,
|
|
1
|
+
{"version":3,"names":["StallionNativeModule","setSdkTokenNative","updateSdkToken","getStallionMetaNative","Promise","resolve","reject","getStallionMeta","then","metaString","JSON","parse","_","catch","getStallionConfigNative","getStallionConfig","configString","toggleStallionSwitchNative","toggleStallionSwitch","downloadBundleNative","downloadStageBundle","onLaunchNative","onLaunch","sync","popEventsNative","popEvents","acknowledgeEventsNative","acknowledgeEvents","restart","_StallionNativeModule","call"],"sourceRoot":"../../../../src","sources":["main/utils/StallionNativeUtils.ts"],"mappings":"AAAA,OAAOA,oBAAoB,MAAM,4BAA4B;AAW7D,OAAO,MAAMC,iBAAqC,GAChDD,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEE,cAAc;AAEtC,OAAO,MAAMC,qBAA6C,GAAGA,CAAA,KAAM;EACjE,OAAO,IAAIC,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;IACtCN,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEO,eAAe,CAAC,CAAC,CACpCC,IAAI,CAAEC,UAAkB,IAAK;MAC5B,IAAI;QACFJ,OAAO,CAACK,IAAI,CAACC,KAAK,CAACF,UAAU,CAAC,CAAC;MACjC,CAAC,CAAC,OAAOG,CAAC,EAAE;QACVN,MAAM,CAAC,qBAAqB,CAAC;MAC/B;IACF,CAAC,CAAC,CACDO,KAAK,CAAC,MAAM;MACXP,MAAM,CAAC,6BAA6B,CAAC;IACvC,CAAC,CAAC;EACN,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,MAAMQ,uBAAiD,GAAGA,CAAA,KAAM;EACrE,OAAO,IAAIV,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;IACtCN,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEe,iBAAiB,CAAC,CAAC,CACtCP,IAAI,CAAEQ,YAAoB,IAAK;MAC9B,IAAI;QACFX,OAAO,CAACK,IAAI,CAACC,KAAK,CAACK,YAAY,CAAC,CAAC;MACnC,CAAC,CAAC,OAAOJ,CAAC,EAAE;QACVN,MAAM,CAAC,uBAAuB,CAAC;MACjC;IACF,CAAC,CAAC,CACDO,KAAK,CAAC,MAAM;MACXP,MAAM,CAAC,+BAA+B,CAAC;IACzC,CAAC,CAAC;EACN,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,MAAMW,0BAAuD,GAClEjB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEkB,oBAAoB;AAE5C,OAAO,MAAMC,oBAA2C,GACtDnB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEoB,mBAAmB;AAE3C,OAAO,MAAMC,cAAqC,GAChDrB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEsB,QAAQ;AAEhC,OAAO,MAAMC,IAAgB,GAAGvB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEuB,IAAI;AAE1D,OAAO,MAAMC,eAAsC,GACjDxB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEyB,SAAS;AAEjC,OAAO,MAAMC,uBAA8D,GACzE1B,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAE2B,iBAAiB;AAEzC,OAAO,MAAMC,OAAO,GAAGA,CAAA,KAAM;EAAA,IAAAC,qBAAA;EAC3B7B,oBAAoB,aAApBA,oBAAoB,wBAAA6B,qBAAA,GAApB7B,oBAAoB,CAAE4B,OAAO,cAAAC,qBAAA,uBAA7BA,qBAAA,CAAAC,IAAA,CAAA9B,oBAAgC,CAAC;AACnC,CAAC"}
|
|
@@ -8,5 +8,5 @@ export declare const onLaunchNative: TOnLaunchBundleNative;
|
|
|
8
8
|
export declare const sync: () => void;
|
|
9
9
|
export declare const popEventsNative: () => Promise<string>;
|
|
10
10
|
export declare const acknowledgeEventsNative: (eventIds: string) => Promise<string>;
|
|
11
|
-
export declare const restart: () =>
|
|
11
|
+
export declare const restart: () => void;
|
|
12
12
|
//# sourceMappingURL=StallionNativeUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StallionNativeUtils.d.ts","sourceRoot":"","sources":["../../../../src/main/utils/StallionNativeUtils.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,uBAAuB,CAAC;AAE/B,eAAO,MAAM,iBAAiB,EAAE,kBACM,CAAC;AAEvC,eAAO,MAAM,qBAAqB,EAAE,sBAcnC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,wBAcrC,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,2BACG,CAAC;AAE7C,eAAO,MAAM,oBAAoB,EAAE,qBACQ,CAAC;AAE5C,eAAO,MAAM,cAAc,EAAE,qBACG,CAAC;AAEjC,eAAO,MAAM,IAAI,EAAE,MAAM,IAAiC,CAAC;AAE3D,eAAO,MAAM,eAAe,EAAE,MAAM,OAAO,CAAC,MAAM,CACjB,CAAC;AAElC,eAAO,MAAM,uBAAuB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CACjC,CAAC;AAE1C,eAAO,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"StallionNativeUtils.d.ts","sourceRoot":"","sources":["../../../../src/main/utils/StallionNativeUtils.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,uBAAuB,CAAC;AAE/B,eAAO,MAAM,iBAAiB,EAAE,kBACM,CAAC;AAEvC,eAAO,MAAM,qBAAqB,EAAE,sBAcnC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,wBAcrC,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,2BACG,CAAC;AAE7C,eAAO,MAAM,oBAAoB,EAAE,qBACQ,CAAC;AAE5C,eAAO,MAAM,cAAc,EAAE,qBACG,CAAC;AAEjC,eAAO,MAAM,IAAI,EAAE,MAAM,IAAiC,CAAC;AAE3D,eAAO,MAAM,eAAe,EAAE,MAAM,OAAO,CAAC,MAAM,CACjB,CAAC;AAElC,eAAO,MAAM,uBAAuB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CACjC,CAAC;AAE1C,eAAO,MAAM,OAAO,YAEnB,CAAC"}
|