react-native-bspatch 1.1.1 → 1.1.3

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.
Files changed (58) hide show
  1. package/README.md +120 -22
  2. package/android/src/main/java/vn/reactnativebspatch/BsPatchModule.kt +117 -17
  3. package/cpp/CMakeLists.txt +9 -1
  4. package/cpp/bsdiff/bspatch.c +14 -3
  5. package/cpp/bsdiff/bspatch_wrapper.c +124 -40
  6. package/ios/RNBsPatch.mm +142 -1
  7. package/lib/commonjs/NativeBsPatch.js.map +1 -1
  8. package/lib/commonjs/ota/OtaProvider.js +61 -12
  9. package/lib/commonjs/ota/OtaProvider.js.map +1 -1
  10. package/lib/commonjs/ota/otaCore.js +43 -20
  11. package/lib/commonjs/ota/otaCore.js.map +1 -1
  12. package/lib/commonjs/ota/otaMetadata.js +41 -5
  13. package/lib/commonjs/ota/otaMetadata.js.map +1 -1
  14. package/lib/commonjs/ota/storage/CustomStorage.js +23 -3
  15. package/lib/commonjs/ota/storage/CustomStorage.js.map +1 -1
  16. package/lib/commonjs/ota/storage/GitHubRawStorage.js +12 -2
  17. package/lib/commonjs/ota/storage/GitHubRawStorage.js.map +1 -1
  18. package/lib/commonjs/ota/storage/GitHubReleaseStorage.js +3 -0
  19. package/lib/commonjs/ota/storage/GitHubReleaseStorage.js.map +1 -1
  20. package/lib/module/NativeBsPatch.js.map +1 -1
  21. package/lib/module/ota/OtaProvider.js +61 -12
  22. package/lib/module/ota/OtaProvider.js.map +1 -1
  23. package/lib/module/ota/otaCore.js +43 -20
  24. package/lib/module/ota/otaCore.js.map +1 -1
  25. package/lib/module/ota/otaMetadata.js +38 -3
  26. package/lib/module/ota/otaMetadata.js.map +1 -1
  27. package/lib/module/ota/storage/CustomStorage.js +23 -3
  28. package/lib/module/ota/storage/CustomStorage.js.map +1 -1
  29. package/lib/module/ota/storage/GitHubRawStorage.js +12 -2
  30. package/lib/module/ota/storage/GitHubRawStorage.js.map +1 -1
  31. package/lib/module/ota/storage/GitHubReleaseStorage.js +3 -0
  32. package/lib/module/ota/storage/GitHubReleaseStorage.js.map +1 -1
  33. package/lib/typescript/NativeBsPatch.d.ts +11 -0
  34. package/lib/typescript/NativeBsPatch.d.ts.map +1 -1
  35. package/lib/typescript/ota/OtaProvider.d.ts.map +1 -1
  36. package/lib/typescript/ota/otaCore.d.ts +3 -1
  37. package/lib/typescript/ota/otaCore.d.ts.map +1 -1
  38. package/lib/typescript/ota/otaMetadata.d.ts +6 -0
  39. package/lib/typescript/ota/otaMetadata.d.ts.map +1 -1
  40. package/lib/typescript/ota/storage/CustomStorage.d.ts.map +1 -1
  41. package/lib/typescript/ota/storage/GitHubRawStorage.d.ts.map +1 -1
  42. package/lib/typescript/ota/storage/GitHubReleaseStorage.d.ts.map +1 -1
  43. package/lib/typescript/ota/types.d.ts +6 -2
  44. package/lib/typescript/ota/types.d.ts.map +1 -1
  45. package/package.json +3 -1
  46. package/react-native-bspatch.podspec +6 -1
  47. package/src/NativeBsPatch.ts +17 -0
  48. package/src/ota/OtaProvider.tsx +80 -23
  49. package/src/ota/otaCore.ts +54 -24
  50. package/src/ota/otaMetadata.ts +46 -4
  51. package/src/ota/storage/CustomStorage.ts +26 -3
  52. package/src/ota/storage/GitHubRawStorage.ts +15 -4
  53. package/src/ota/storage/GitHubReleaseStorage.ts +5 -0
  54. package/src/ota/types.ts +6 -2
  55. package/tools/build_ota.sh +103 -7
  56. package/tools/bump_version.js +133 -0
  57. package/tools/generate_keys.js +75 -0
  58. package/tools/update_registry.js +51 -1
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ // ─────────────────────────────────────────────────────────────────────────────
3
+ // generate_keys.js — Generate Asymmetric Keypair for OTA Patch Signing
4
+ //
5
+ // Usage:
6
+ // node tools/generate_keys.js [--type rsa|ec] [--out <dir>]
7
+ // e.g. node tools/generate_keys.js
8
+ // ─────────────────────────────────────────────────────────────────────────────
9
+
10
+ const crypto = require('crypto');
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ const args = process.argv.slice(2);
15
+ let keyType = 'rsa';
16
+ let outDir = process.cwd();
17
+
18
+ for (let i = 0; i < args.length; i++) {
19
+ if (args[i] === '--type' && args[i + 1]) {
20
+ keyType = args[i + 1].toLowerCase();
21
+ i++;
22
+ } else if (args[i] === '--out' && args[i + 1]) {
23
+ outDir = path.resolve(args[i + 1]);
24
+ i++;
25
+ }
26
+ }
27
+
28
+ console.log(`\n=== Generating ${keyType.toUpperCase()} Keypair for OTA Patch Security ===\n`);
29
+
30
+ let privateKey, publicKey;
31
+
32
+ if (keyType === 'ec') {
33
+ const keypair = crypto.generateKeyPairSync('ec', {
34
+ namedCurve: 'prime256v1',
35
+ publicKeyEncoding: { type: 'spki', format: 'pem' },
36
+ privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
37
+ });
38
+ privateKey = keypair.privateKey;
39
+ publicKey = keypair.publicKey;
40
+ } else {
41
+ // Default: RSA 2048
42
+ const keypair = crypto.generateKeyPairSync('rsa', {
43
+ modulusLength: 2048,
44
+ publicKeyEncoding: { type: 'spki', format: 'pem' },
45
+ privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
46
+ });
47
+ privateKey = keypair.privateKey;
48
+ publicKey = keypair.publicKey;
49
+ }
50
+
51
+ const privPath = path.join(outDir, 'ota_private_key.pem');
52
+ const pubPath = path.join(outDir, 'ota_public_key.pem');
53
+
54
+ fs.writeFileSync(privPath, privateKey, { mode: 0o600 });
55
+ fs.writeFileSync(pubPath, publicKey, { mode: 0o644 });
56
+
57
+ // Clean public key string for inline JS config
58
+ const pubKeyClean = publicKey
59
+ .replace(/-----BEGIN PUBLIC KEY-----/, '')
60
+ .replace(/-----END PUBLIC KEY-----/, '')
61
+ .replace(/\s+/g, '');
62
+
63
+ console.log(` ✓ Private Key saved to: ${privPath}`);
64
+ console.log(` ✓ Public Key saved to: ${pubPath}`);
65
+ console.log('\n────────────────────────────────────────────────────────');
66
+ console.log(' HOW TO USE:');
67
+ console.log(' 1. KEEP PRIVATE KEY SAFE on your build machine / CI server:');
68
+ console.log(' Add "ota_private_key.pem" to .gitignore!');
69
+ console.log('');
70
+ console.log(' 2. ADD PUBLIC KEY TO YOUR CLIENT APP CONFIG:');
71
+ console.log(' const otaConfig: OtaConfig = {');
72
+ console.log(` publicKey: '${pubKeyClean}',`);
73
+ console.log(' ...');
74
+ console.log(' };');
75
+ console.log('────────────────────────────────────────────────────────\n');
@@ -12,6 +12,7 @@
12
12
 
13
13
  const fs = require('fs');
14
14
  const path = require('path');
15
+ const crypto = require('crypto');
15
16
 
16
17
  const [
17
18
  projectRoot,
@@ -61,6 +62,24 @@ if (!registry.apps[appVersion].flavors[flavor]) {
61
62
  const flavorGroup = registry.apps[appVersion].flavors[flavor];
62
63
  flavorGroup.latestOtaVersion = toOtaVersion;
63
64
 
65
+ // ── Check for private key for asymmetric signing ───────────────────────────
66
+ let signingKey = process.env.OTA_PRIVATE_KEY || null;
67
+ if (!signingKey) {
68
+ const candidateKeyPaths = [
69
+ path.join(process.cwd(), 'ota_private_key.pem'),
70
+ path.join(path.dirname(registryPath), 'ota_private_key.pem'),
71
+ path.join(path.dirname(registryPath), '../ota_private_key.pem'),
72
+ ];
73
+ for (const kp of candidateKeyPaths) {
74
+ if (fs.existsSync(kp)) {
75
+ try {
76
+ signingKey = fs.readFileSync(kp, 'utf8');
77
+ break;
78
+ } catch (e) {}
79
+ }
80
+ }
81
+ }
82
+
64
83
  // ── Update patches ──────────────────────────────────────────────────────────
65
84
  if (newPatches.length > 0) {
66
85
  // We merge patches by platform to avoid overwriting between Android and iOS
@@ -71,7 +90,32 @@ if (newPatches.length > 0) {
71
90
  const fullBundleName = `bundle_${appVersion}_v${toOtaVersion}_${platform}_${flavor}.hbc`;
72
91
  const manifestName = path.basename(_assetManifestFile);
73
92
 
74
- return {
93
+ let signature = undefined;
94
+ if (signingKey) {
95
+ try {
96
+ const patchFileCandidates = [
97
+ path.join(path.dirname(registryPath), p.file),
98
+ path.join(process.cwd(), p.file),
99
+ ];
100
+ let patchData = null;
101
+ for (const pfc of patchFileCandidates) {
102
+ if (fs.existsSync(pfc)) {
103
+ patchData = fs.readFileSync(pfc);
104
+ break;
105
+ }
106
+ }
107
+ if (patchData) {
108
+ const signer = crypto.createSign('SHA256');
109
+ signer.update(patchData);
110
+ signature = signer.sign(signingKey, 'base64');
111
+ console.log(` ✓ Signed patch ${p.file} with asymmetric private key`);
112
+ }
113
+ } catch (signErr) {
114
+ console.warn(` ⚠ Could not sign patch ${p.file}: ${signErr.message}`);
115
+ }
116
+ }
117
+
118
+ const patchObj = {
75
119
  id: `v${p.from}-to-v${p.to}-${flavor}-${platform}`,
76
120
  label: `V${p.from} → V${p.to} (${flavor} ${platform})`,
77
121
  appVersion,
@@ -85,6 +129,12 @@ if (newPatches.length > 0) {
85
129
  assetManifestUrl: manifestName,
86
130
  flavor: flavor,
87
131
  };
132
+
133
+ if (signature) {
134
+ patchObj.signature = signature;
135
+ }
136
+
137
+ return patchObj;
88
138
  });
89
139
 
90
140
  flavorGroup.patches = [...otherPlatformPatches, ...mappedNewPatches];