@oobit/react-native-sdk 1.0.5 → 1.0.6
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/dist/biometricUtils.d.ts +8 -8
- package/dist/biometricUtils.d.ts.map +1 -1
- package/dist/biometricUtils.js +42 -38
- package/dist/biometricUtils.js.map +1 -1
- package/dist/cryptoUtils.d.ts +3 -6
- package/dist/cryptoUtils.d.ts.map +1 -1
- package/dist/cryptoUtils.js +16 -36
- package/dist/cryptoUtils.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +8 -6
- package/src/biometricUtils.ts +47 -53
- package/src/cryptoUtils.ts +15 -44
- package/src/index.ts +1 -1
package/dist/biometricUtils.d.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Biometric Authentication Utilities
|
|
3
3
|
*
|
|
4
4
|
* Provides cross-platform biometric authentication for card details access.
|
|
5
|
-
*
|
|
5
|
+
* Uses expo-local-authentication which works in Expo Go without native modules.
|
|
6
6
|
*
|
|
7
|
-
* @requires
|
|
7
|
+
* @requires expo-local-authentication - included in Expo SDK
|
|
8
8
|
*/
|
|
9
|
-
import
|
|
9
|
+
import * as LocalAuthentication from 'expo-local-authentication';
|
|
10
10
|
/**
|
|
11
11
|
* Result of a biometric authentication attempt
|
|
12
12
|
*/
|
|
@@ -25,13 +25,13 @@ export interface BiometricResult {
|
|
|
25
25
|
export interface BiometricAvailability {
|
|
26
26
|
/** Whether any biometric authentication is available */
|
|
27
27
|
available: boolean;
|
|
28
|
-
/** The
|
|
29
|
-
|
|
28
|
+
/** The types of biometry available */
|
|
29
|
+
biometryTypes: LocalAuthentication.AuthenticationType[];
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* Checks if biometric authentication is available on the device
|
|
33
33
|
*
|
|
34
|
-
* @returns Object containing availability status and biometry
|
|
34
|
+
* @returns Object containing availability status and biometry types
|
|
35
35
|
*/
|
|
36
36
|
export declare function isBiometricAvailable(): Promise<BiometricAvailability>;
|
|
37
37
|
/**
|
|
@@ -61,6 +61,6 @@ export declare function authenticateWithBiometrics(promptMessage?: string): Prom
|
|
|
61
61
|
* @param biometryType - The biometry type from the device
|
|
62
62
|
* @returns Human-readable string for the biometry type
|
|
63
63
|
*/
|
|
64
|
-
export declare function getBiometryTypeLabel(biometryType:
|
|
65
|
-
export {
|
|
64
|
+
export declare function getBiometryTypeLabel(biometryType: LocalAuthentication.AuthenticationType): string;
|
|
65
|
+
export { AuthenticationType } from 'expo-local-authentication';
|
|
66
66
|
//# sourceMappingURL=biometricUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"biometricUtils.d.ts","sourceRoot":"","sources":["../src/biometricUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,
|
|
1
|
+
{"version":3,"file":"biometricUtils.d.ts","sourceRoot":"","sources":["../src/biometricUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,mBAAmB,MAAM,2BAA2B,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,6CAA6C;IAC7C,KAAK,CAAC,EAAE;QACN,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,eAAe,GAAG,cAAc,CAAC;QAClE,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,SAAS,EAAE,OAAO,CAAC;IACnB,sCAAsC;IACtC,aAAa,EAAE,mBAAmB,CAAC,kBAAkB,EAAE,CAAC;CACzD;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAiB3E;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,0BAA0B,CAC9C,aAAa,GAAE,MAA4C,GAC1D,OAAO,CAAC,eAAe,CAAC,CA8E1B;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,mBAAmB,CAAC,kBAAkB,GACnD,MAAM,CAWR;AAGD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}
|
package/dist/biometricUtils.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Biometric Authentication Utilities
|
|
4
4
|
*
|
|
5
5
|
* Provides cross-platform biometric authentication for card details access.
|
|
6
|
-
*
|
|
6
|
+
* Uses expo-local-authentication which works in Expo Go without native modules.
|
|
7
7
|
*
|
|
8
|
-
* @requires
|
|
8
|
+
* @requires expo-local-authentication - included in Expo SDK
|
|
9
9
|
*/
|
|
10
10
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
11
|
if (k2 === undefined) k2 = k;
|
|
@@ -41,34 +41,31 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
41
41
|
};
|
|
42
42
|
})();
|
|
43
43
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
-
exports.
|
|
44
|
+
exports.AuthenticationType = void 0;
|
|
45
45
|
exports.isBiometricAvailable = isBiometricAvailable;
|
|
46
46
|
exports.authenticateWithBiometrics = authenticateWithBiometrics;
|
|
47
47
|
exports.getBiometryTypeLabel = getBiometryTypeLabel;
|
|
48
|
-
const
|
|
49
|
-
Object.defineProperty(exports, "BiometryTypes", { enumerable: true, get: function () { return react_native_biometrics_1.BiometryTypes; } });
|
|
50
|
-
// Singleton instance of ReactNativeBiometrics
|
|
51
|
-
const rnBiometrics = new react_native_biometrics_1.default({
|
|
52
|
-
allowDeviceCredentials: false, // Only allow biometric, not PIN/password fallback
|
|
53
|
-
});
|
|
48
|
+
const LocalAuthentication = __importStar(require("expo-local-authentication"));
|
|
54
49
|
/**
|
|
55
50
|
* Checks if biometric authentication is available on the device
|
|
56
51
|
*
|
|
57
|
-
* @returns Object containing availability status and biometry
|
|
52
|
+
* @returns Object containing availability status and biometry types
|
|
58
53
|
*/
|
|
59
54
|
async function isBiometricAvailable() {
|
|
60
55
|
try {
|
|
61
|
-
const
|
|
56
|
+
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
57
|
+
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
58
|
+
const supportedTypes = await LocalAuthentication.supportedAuthenticationTypesAsync();
|
|
62
59
|
return {
|
|
63
|
-
available,
|
|
64
|
-
|
|
60
|
+
available: hasHardware && isEnrolled,
|
|
61
|
+
biometryTypes: supportedTypes,
|
|
65
62
|
};
|
|
66
63
|
}
|
|
67
64
|
catch (error) {
|
|
68
65
|
console.error('[BiometricUtils] Error checking biometric availability:', error);
|
|
69
66
|
return {
|
|
70
67
|
available: false,
|
|
71
|
-
|
|
68
|
+
biometryTypes: [],
|
|
72
69
|
};
|
|
73
70
|
}
|
|
74
71
|
}
|
|
@@ -95,8 +92,8 @@ async function isBiometricAvailable() {
|
|
|
95
92
|
async function authenticateWithBiometrics(promptMessage = 'Authenticate to view card details') {
|
|
96
93
|
try {
|
|
97
94
|
// First check if biometrics are available
|
|
98
|
-
const
|
|
99
|
-
if (!
|
|
95
|
+
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
96
|
+
if (!hasHardware) {
|
|
100
97
|
console.log('[BiometricUtils] Biometrics not available on device');
|
|
101
98
|
return {
|
|
102
99
|
success: false,
|
|
@@ -107,42 +104,46 @@ async function authenticateWithBiometrics(promptMessage = 'Authenticate to view
|
|
|
107
104
|
};
|
|
108
105
|
}
|
|
109
106
|
// Check if biometrics are enrolled
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
108
|
+
if (!isEnrolled) {
|
|
109
|
+
console.log('[BiometricUtils] No biometrics enrolled');
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
error: {
|
|
113
|
+
reason: 'not_enrolled',
|
|
114
|
+
message: 'No biometric authentication is set up on this device',
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
console.log('[BiometricUtils] Attempting biometric authentication...');
|
|
114
119
|
// Perform the biometric authentication
|
|
115
|
-
const
|
|
120
|
+
const result = await LocalAuthentication.authenticateAsync({
|
|
116
121
|
promptMessage,
|
|
117
|
-
|
|
122
|
+
cancelLabel: 'Cancel',
|
|
123
|
+
disableDeviceFallback: false, // Allow PIN/password fallback
|
|
124
|
+
fallbackLabel: 'Use Passcode',
|
|
118
125
|
});
|
|
119
|
-
if (success) {
|
|
126
|
+
if (result.success) {
|
|
120
127
|
console.log('[BiometricUtils] Biometric authentication successful');
|
|
121
128
|
return { success: true };
|
|
122
129
|
}
|
|
123
130
|
// Handle various failure reasons
|
|
124
|
-
console.log('[BiometricUtils] Biometric authentication failed:', error);
|
|
125
|
-
// Parse the error message to determine the reason
|
|
126
|
-
const errorMessage = error || 'Authentication failed';
|
|
131
|
+
console.log('[BiometricUtils] Biometric authentication failed:', result.error);
|
|
127
132
|
let reason = 'failed';
|
|
128
|
-
if (
|
|
129
|
-
errorMessage.toLowerCase().includes('user cancel')) {
|
|
133
|
+
if (result.error === 'user_cancel' || result.error === 'system_cancel') {
|
|
130
134
|
reason = 'cancelled';
|
|
131
135
|
}
|
|
132
|
-
else if (
|
|
133
|
-
errorMessage.toLowerCase().includes('no fingerprint') ||
|
|
134
|
-
errorMessage.toLowerCase().includes('no face')) {
|
|
136
|
+
else if (result.error === 'not_enrolled') {
|
|
135
137
|
reason = 'not_enrolled';
|
|
136
138
|
}
|
|
137
|
-
else if (
|
|
138
|
-
errorMessage.toLowerCase().includes('not supported')) {
|
|
139
|
+
else if (result.error === 'not_available') {
|
|
139
140
|
reason = 'not_available';
|
|
140
141
|
}
|
|
141
142
|
return {
|
|
142
143
|
success: false,
|
|
143
144
|
error: {
|
|
144
145
|
reason,
|
|
145
|
-
message:
|
|
146
|
+
message: result.warning || 'Authentication failed',
|
|
146
147
|
},
|
|
147
148
|
};
|
|
148
149
|
}
|
|
@@ -166,14 +167,17 @@ async function authenticateWithBiometrics(promptMessage = 'Authenticate to view
|
|
|
166
167
|
*/
|
|
167
168
|
function getBiometryTypeLabel(biometryType) {
|
|
168
169
|
switch (biometryType) {
|
|
169
|
-
case
|
|
170
|
+
case LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION:
|
|
170
171
|
return 'Face ID';
|
|
171
|
-
case
|
|
172
|
+
case LocalAuthentication.AuthenticationType.FINGERPRINT:
|
|
172
173
|
return 'Touch ID';
|
|
173
|
-
case
|
|
174
|
-
return '
|
|
174
|
+
case LocalAuthentication.AuthenticationType.IRIS:
|
|
175
|
+
return 'Iris';
|
|
175
176
|
default:
|
|
176
177
|
return 'Biometric Authentication';
|
|
177
178
|
}
|
|
178
179
|
}
|
|
180
|
+
// Re-export AuthenticationType for convenience
|
|
181
|
+
var expo_local_authentication_1 = require("expo-local-authentication");
|
|
182
|
+
Object.defineProperty(exports, "AuthenticationType", { enumerable: true, get: function () { return expo_local_authentication_1.AuthenticationType; } });
|
|
179
183
|
//# sourceMappingURL=biometricUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"biometricUtils.js","sourceRoot":"","sources":["../src/biometricUtils.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"biometricUtils.js","sourceRoot":"","sources":["../src/biometricUtils.ts"],"names":[],"mappings":"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"}
|
package/dist/cryptoUtils.d.ts
CHANGED
|
@@ -7,10 +7,8 @@
|
|
|
7
7
|
* - Input to RSA: secretKey as Base64 string
|
|
8
8
|
* - Output: encrypted sessionId as Base64 string
|
|
9
9
|
*
|
|
10
|
-
* Uses
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* @requires react-native-quick-crypto - npm install react-native-quick-crypto
|
|
10
|
+
* Uses node-forge for pure JavaScript RSA-OAEP encryption.
|
|
11
|
+
* This works in Expo Go without native modules.
|
|
14
12
|
*/
|
|
15
13
|
/**
|
|
16
14
|
* Generates a random hex key matching Android's generateRandomHexKey()
|
|
@@ -30,10 +28,9 @@ export declare function generateRandomHexKey(): string;
|
|
|
30
28
|
*/
|
|
31
29
|
export declare function hexToBase64(hexString: string): string;
|
|
32
30
|
/**
|
|
33
|
-
* Encrypts data using RSA with OAEP-SHA1 padding
|
|
31
|
+
* Encrypts data using RSA with OAEP-SHA1 padding
|
|
34
32
|
*
|
|
35
33
|
* MUST match Android's RSA/ECB/OAEPWithSHA-1AndMGF1Padding algorithm
|
|
36
|
-
* Uses native crypto (OpenSSL on Android, CommonCrypto on iOS)
|
|
37
34
|
*
|
|
38
35
|
* @param data - Plain text data to encrypt (Base64 string of the secret key)
|
|
39
36
|
* @param publicKeyPem - RSA public key in PEM format
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cryptoUtils.d.ts","sourceRoot":"","sources":["../src/cryptoUtils.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cryptoUtils.d.ts","sourceRoot":"","sources":["../src/cryptoUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAU7C;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAUrD;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAmBzE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,CAqBnF;AAGD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
package/dist/cryptoUtils.js
CHANGED
|
@@ -8,10 +8,8 @@
|
|
|
8
8
|
* - Input to RSA: secretKey as Base64 string
|
|
9
9
|
* - Output: encrypted sessionId as Base64 string
|
|
10
10
|
*
|
|
11
|
-
* Uses
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @requires react-native-quick-crypto - npm install react-native-quick-crypto
|
|
11
|
+
* Uses node-forge for pure JavaScript RSA-OAEP encryption.
|
|
12
|
+
* This works in Expo Go without native modules.
|
|
15
13
|
*/
|
|
16
14
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
15
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -21,7 +19,7 @@ exports.generateRandomHexKey = generateRandomHexKey;
|
|
|
21
19
|
exports.hexToBase64 = hexToBase64;
|
|
22
20
|
exports.encryptWithRSA = encryptWithRSA;
|
|
23
21
|
exports.generateSessionCredentials = generateSessionCredentials;
|
|
24
|
-
const
|
|
22
|
+
const node_forge_1 = __importDefault(require("node-forge"));
|
|
25
23
|
/**
|
|
26
24
|
* Generates a random hex key matching Android's generateRandomHexKey()
|
|
27
25
|
*
|
|
@@ -55,32 +53,13 @@ function hexToBase64(hexString) {
|
|
|
55
53
|
const byte = parseInt(hexString.substring(i, i + 2), 16);
|
|
56
54
|
binaryString += String.fromCharCode(byte);
|
|
57
55
|
}
|
|
58
|
-
// Use
|
|
59
|
-
|
|
60
|
-
return btoa(binaryString);
|
|
61
|
-
}
|
|
62
|
-
// Fallback: manual Base64 encoding
|
|
63
|
-
const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
64
|
-
let result = '';
|
|
65
|
-
let i = 0;
|
|
66
|
-
while (i < binaryString.length) {
|
|
67
|
-
const a = binaryString.charCodeAt(i++);
|
|
68
|
-
const b = binaryString.charCodeAt(i++);
|
|
69
|
-
const c = binaryString.charCodeAt(i++);
|
|
70
|
-
const triplet = (a << 16) | ((b || 0) << 8) | (c || 0);
|
|
71
|
-
result +=
|
|
72
|
-
base64Chars[(triplet >> 18) & 0x3f] +
|
|
73
|
-
base64Chars[(triplet >> 12) & 0x3f] +
|
|
74
|
-
(isNaN(b) ? '=' : base64Chars[(triplet >> 6) & 0x3f]) +
|
|
75
|
-
(isNaN(c) ? '=' : base64Chars[triplet & 0x3f]);
|
|
76
|
-
}
|
|
77
|
-
return result;
|
|
56
|
+
// Use forge's utility for Base64 encoding
|
|
57
|
+
return node_forge_1.default.util.encode64(binaryString);
|
|
78
58
|
}
|
|
79
59
|
/**
|
|
80
|
-
* Encrypts data using RSA with OAEP-SHA1 padding
|
|
60
|
+
* Encrypts data using RSA with OAEP-SHA1 padding
|
|
81
61
|
*
|
|
82
62
|
* MUST match Android's RSA/ECB/OAEPWithSHA-1AndMGF1Padding algorithm
|
|
83
|
-
* Uses native crypto (OpenSSL on Android, CommonCrypto on iOS)
|
|
84
63
|
*
|
|
85
64
|
* @param data - Plain text data to encrypt (Base64 string of the secret key)
|
|
86
65
|
* @param publicKeyPem - RSA public key in PEM format
|
|
@@ -89,16 +68,17 @@ function hexToBase64(hexString) {
|
|
|
89
68
|
*/
|
|
90
69
|
function encryptWithRSA(data, publicKeyPem) {
|
|
91
70
|
try {
|
|
92
|
-
//
|
|
93
|
-
const
|
|
71
|
+
// Parse the PEM-formatted public key
|
|
72
|
+
const publicKey = node_forge_1.default.pki.publicKeyFromPem(publicKeyPem);
|
|
94
73
|
// Encrypt using RSA-OAEP with SHA-1 (matches Android's OAEPWithSHA-1AndMGF1Padding)
|
|
95
|
-
const encrypted =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
74
|
+
const encrypted = publicKey.encrypt(data, 'RSA-OAEP', {
|
|
75
|
+
md: node_forge_1.default.md.sha1.create(),
|
|
76
|
+
mgf1: {
|
|
77
|
+
md: node_forge_1.default.md.sha1.create(),
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
// Encode to Base64
|
|
81
|
+
return node_forge_1.default.util.encode64(encrypted);
|
|
102
82
|
}
|
|
103
83
|
catch (error) {
|
|
104
84
|
console.error('[CryptoUtils] RSA encryption failed:', error);
|
package/dist/cryptoUtils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cryptoUtils.js","sourceRoot":"","sources":["../src/cryptoUtils.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"cryptoUtils.js","sourceRoot":"","sources":["../src/cryptoUtils.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;AAYH,oDAUC;AASD,kCAUC;AAYD,wCAmBC;AAoBD,gEAqBC;AA/GD,4DAA+B;AAE/B;;;;;;;GAOG;AACH,SAAgB,oBAAoB;IAClC,6CAA6C;IAC7C,iFAAiF;IACjF,OAAO,sCAAsC;SAC1C,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;QACtB,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;QAC1C,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACxB,CAAC,CAAC;SACD,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AACvB,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,WAAW,CAAC,SAAiB;IAC3C,sCAAsC;IACtC,IAAI,YAAY,GAAG,EAAE,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACzD,YAAY,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,0CAA0C;IAC1C,OAAO,oBAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,cAAc,CAAC,IAAY,EAAE,YAAoB;IAC/D,IAAI,CAAC;QACH,qCAAqC;QACrC,MAAM,SAAS,GAAG,oBAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;QAE3D,oFAAoF;QACpF,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE;YACpD,EAAE,EAAE,oBAAK,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE;YAC1B,IAAI,EAAE;gBACJ,EAAE,EAAE,oBAAK,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE;aAC3B;SACF,CAAC,CAAC;QAEH,mBAAmB;QACnB,OAAO,oBAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,0BAA0B,CAAC,YAAoB;IAC7D,sBAAsB;IACtB,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IAED,8EAA8E;IAC9E,MAAM,YAAY,GAAG,oBAAoB,EAAE,CAAC;IAE5C,4DAA4D;IAC5D,MAAM,eAAe,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;IAElD,+EAA+E;IAC/E,MAAM,SAAS,GAAG,cAAc,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;IAEhE,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;IAExE,OAAO;QACL,YAAY;QACZ,SAAS;KACV,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export type { WidgetSDKConfig, WidgetEnvironment, DepositToken, WidgetMessageTyp
|
|
|
8
8
|
export { WALLET_URLS, MessageTypes } from './types';
|
|
9
9
|
export { WIDGET_URLS, getWidgetUrl } from './config';
|
|
10
10
|
export { openNativeWallet, isWalletAvailable } from './walletUtils';
|
|
11
|
-
export { authenticateWithBiometrics, isBiometricAvailable, getBiometryTypeLabel,
|
|
11
|
+
export { authenticateWithBiometrics, isBiometricAvailable, getBiometryTypeLabel, AuthenticationType, } from './biometricUtils';
|
|
12
12
|
export type { BiometricResult, BiometricAvailability } from './biometricUtils';
|
|
13
13
|
export { generateSessionCredentials, generateRandomHexKey, hexToBase64, } from './cryptoUtils';
|
|
14
14
|
export type { SessionCredentials } from './cryptoUtils';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,YAAY,EAEV,eAAe,EACf,iBAAiB,EACjB,YAAY,EAGZ,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,2BAA2B,EAC3B,gCAAgC,EAGhC,iBAAiB,EACjB,aAAa,EACb,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGrD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGpE,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,EACpB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,YAAY,EAEV,eAAe,EACf,iBAAiB,EACjB,YAAY,EAGZ,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,2BAA2B,EAC3B,gCAAgC,EAGhC,iBAAiB,EACjB,aAAa,EACb,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGrD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGpE,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAG/E,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Export all public components, types, and utilities
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.hexToBase64 = exports.generateRandomHexKey = exports.generateSessionCredentials = exports.
|
|
7
|
+
exports.hexToBase64 = exports.generateRandomHexKey = exports.generateSessionCredentials = exports.AuthenticationType = exports.getBiometryTypeLabel = exports.isBiometricAvailable = exports.authenticateWithBiometrics = exports.isWalletAvailable = exports.openNativeWallet = exports.getWidgetUrl = exports.WIDGET_URLS = exports.MessageTypes = exports.WALLET_URLS = exports.WidgetSDK = void 0;
|
|
8
8
|
var WidgetSDK_1 = require("./WidgetSDK");
|
|
9
9
|
Object.defineProperty(exports, "WidgetSDK", { enumerable: true, get: function () { return WidgetSDK_1.WidgetSDK; } });
|
|
10
10
|
// Export constants
|
|
@@ -23,7 +23,7 @@ var biometricUtils_1 = require("./biometricUtils");
|
|
|
23
23
|
Object.defineProperty(exports, "authenticateWithBiometrics", { enumerable: true, get: function () { return biometricUtils_1.authenticateWithBiometrics; } });
|
|
24
24
|
Object.defineProperty(exports, "isBiometricAvailable", { enumerable: true, get: function () { return biometricUtils_1.isBiometricAvailable; } });
|
|
25
25
|
Object.defineProperty(exports, "getBiometryTypeLabel", { enumerable: true, get: function () { return biometricUtils_1.getBiometryTypeLabel; } });
|
|
26
|
-
Object.defineProperty(exports, "
|
|
26
|
+
Object.defineProperty(exports, "AuthenticationType", { enumerable: true, get: function () { return biometricUtils_1.AuthenticationType; } });
|
|
27
27
|
// Export crypto utilities (for advanced usage)
|
|
28
28
|
var cryptoUtils_1 = require("./cryptoUtils");
|
|
29
29
|
Object.defineProperty(exports, "generateSessionCredentials", { enumerable: true, get: function () { return cryptoUtils_1.generateSessionCredentials; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,yCAAwC;AAA/B,sGAAA,SAAS,OAAA;AAiClB,mBAAmB;AACnB,iCAAoD;AAA3C,oGAAA,WAAW,OAAA;AAAE,qGAAA,YAAY,OAAA;AAClC,mCAAqD;AAA5C,qGAAA,WAAW,OAAA;AAAE,sGAAA,YAAY,OAAA;AAElC,0BAA0B;AAC1B,6CAAoE;AAA3D,+GAAA,gBAAgB,OAAA;AAAE,gHAAA,iBAAiB,OAAA;AAE5C,6BAA6B;AAC7B,mDAK0B;AAJxB,4HAAA,0BAA0B,OAAA;AAC1B,sHAAA,oBAAoB,OAAA;AACpB,sHAAA,oBAAoB,OAAA;AACpB
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,yCAAwC;AAA/B,sGAAA,SAAS,OAAA;AAiClB,mBAAmB;AACnB,iCAAoD;AAA3C,oGAAA,WAAW,OAAA;AAAE,qGAAA,YAAY,OAAA;AAClC,mCAAqD;AAA5C,qGAAA,WAAW,OAAA;AAAE,sGAAA,YAAY,OAAA;AAElC,0BAA0B;AAC1B,6CAAoE;AAA3D,+GAAA,gBAAgB,OAAA;AAAE,gHAAA,iBAAiB,OAAA;AAE5C,6BAA6B;AAC7B,mDAK0B;AAJxB,4HAAA,0BAA0B,OAAA;AAC1B,sHAAA,oBAAoB,OAAA;AACpB,sHAAA,oBAAoB,OAAA;AACpB,oHAAA,kBAAkB,OAAA;AAIpB,+CAA+C;AAC/C,6CAIuB;AAHrB,yHAAA,0BAA0B,OAAA;AAC1B,mHAAA,oBAAoB,OAAA;AACpB,0GAAA,WAAW,OAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oobit/react-native-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "React Native SDK for integrating Oobit crypto payments into wallet apps",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -41,23 +41,25 @@
|
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"expo-intent-launcher": ">=6.0.0",
|
|
43
43
|
"expo-linking": ">=6.0.0",
|
|
44
|
+
"expo-local-authentication": ">=14.0.0",
|
|
44
45
|
"react": ">=18.0.0",
|
|
45
46
|
"react-native": ">=0.70.0",
|
|
46
|
-
"react-native-webview": ">=13.0.0"
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
"react-native-webview": ">=13.0.0"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"node-forge": "^1.3.1"
|
|
49
51
|
},
|
|
50
52
|
"devDependencies": {
|
|
53
|
+
"@types/node-forge": "^1.3.11",
|
|
51
54
|
"@types/react": "~19.1.0",
|
|
52
55
|
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
|
53
56
|
"@typescript-eslint/parser": "^8.47.0",
|
|
54
57
|
"eslint": "^9.39.1",
|
|
55
58
|
"expo-intent-launcher": "~13.0.7",
|
|
56
59
|
"expo-linking": "~8.0.8",
|
|
60
|
+
"expo-local-authentication": "~15.0.2",
|
|
57
61
|
"react": "19.1.0",
|
|
58
62
|
"react-native": "0.81.5",
|
|
59
|
-
"react-native-biometrics": "^3.0.1",
|
|
60
|
-
"react-native-quick-crypto": "^0.7.9",
|
|
61
63
|
"react-native-webview": "13.15.0",
|
|
62
64
|
"typescript": "~5.9.2"
|
|
63
65
|
},
|
package/src/biometricUtils.ts
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Biometric Authentication Utilities
|
|
3
3
|
*
|
|
4
4
|
* Provides cross-platform biometric authentication for card details access.
|
|
5
|
-
*
|
|
5
|
+
* Uses expo-local-authentication which works in Expo Go without native modules.
|
|
6
6
|
*
|
|
7
|
-
* @requires
|
|
7
|
+
* @requires expo-local-authentication - included in Expo SDK
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
import * as LocalAuthentication from 'expo-local-authentication';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Result of a biometric authentication attempt
|
|
@@ -28,33 +28,30 @@ export interface BiometricResult {
|
|
|
28
28
|
export interface BiometricAvailability {
|
|
29
29
|
/** Whether any biometric authentication is available */
|
|
30
30
|
available: boolean;
|
|
31
|
-
/** The
|
|
32
|
-
|
|
31
|
+
/** The types of biometry available */
|
|
32
|
+
biometryTypes: LocalAuthentication.AuthenticationType[];
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
// Singleton instance of ReactNativeBiometrics
|
|
36
|
-
const rnBiometrics = new ReactNativeBiometrics({
|
|
37
|
-
allowDeviceCredentials: false, // Only allow biometric, not PIN/password fallback
|
|
38
|
-
});
|
|
39
|
-
|
|
40
35
|
/**
|
|
41
36
|
* Checks if biometric authentication is available on the device
|
|
42
37
|
*
|
|
43
|
-
* @returns Object containing availability status and biometry
|
|
38
|
+
* @returns Object containing availability status and biometry types
|
|
44
39
|
*/
|
|
45
40
|
export async function isBiometricAvailable(): Promise<BiometricAvailability> {
|
|
46
41
|
try {
|
|
47
|
-
const
|
|
42
|
+
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
43
|
+
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
44
|
+
const supportedTypes = await LocalAuthentication.supportedAuthenticationTypesAsync();
|
|
48
45
|
|
|
49
46
|
return {
|
|
50
|
-
available,
|
|
51
|
-
|
|
47
|
+
available: hasHardware && isEnrolled,
|
|
48
|
+
biometryTypes: supportedTypes,
|
|
52
49
|
};
|
|
53
50
|
} catch (error) {
|
|
54
51
|
console.error('[BiometricUtils] Error checking biometric availability:', error);
|
|
55
52
|
return {
|
|
56
53
|
available: false,
|
|
57
|
-
|
|
54
|
+
biometryTypes: [],
|
|
58
55
|
};
|
|
59
56
|
}
|
|
60
57
|
}
|
|
@@ -84,9 +81,9 @@ export async function authenticateWithBiometrics(
|
|
|
84
81
|
): Promise<BiometricResult> {
|
|
85
82
|
try {
|
|
86
83
|
// First check if biometrics are available
|
|
87
|
-
const
|
|
84
|
+
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
88
85
|
|
|
89
|
-
if (!
|
|
86
|
+
if (!hasHardware) {
|
|
90
87
|
console.log('[BiometricUtils] Biometrics not available on device');
|
|
91
88
|
return {
|
|
92
89
|
success: false,
|
|
@@ -98,48 +95,44 @@ export async function authenticateWithBiometrics(
|
|
|
98
95
|
}
|
|
99
96
|
|
|
100
97
|
// Check if biometrics are enrolled
|
|
101
|
-
const
|
|
98
|
+
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
102
99
|
|
|
103
|
-
|
|
104
|
-
|
|
100
|
+
if (!isEnrolled) {
|
|
101
|
+
console.log('[BiometricUtils] No biometrics enrolled');
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
error: {
|
|
105
|
+
reason: 'not_enrolled',
|
|
106
|
+
message: 'No biometric authentication is set up on this device',
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
105
110
|
|
|
106
|
-
console.log(
|
|
107
|
-
`[BiometricUtils] Attempting biometric auth. Type: ${biometryType}, Keys exist: ${keysExist}`
|
|
108
|
-
);
|
|
111
|
+
console.log('[BiometricUtils] Attempting biometric authentication...');
|
|
109
112
|
|
|
110
113
|
// Perform the biometric authentication
|
|
111
|
-
const
|
|
114
|
+
const result = await LocalAuthentication.authenticateAsync({
|
|
112
115
|
promptMessage,
|
|
113
|
-
|
|
116
|
+
cancelLabel: 'Cancel',
|
|
117
|
+
disableDeviceFallback: false, // Allow PIN/password fallback
|
|
118
|
+
fallbackLabel: 'Use Passcode',
|
|
114
119
|
});
|
|
115
120
|
|
|
116
|
-
if (success) {
|
|
121
|
+
if (result.success) {
|
|
117
122
|
console.log('[BiometricUtils] Biometric authentication successful');
|
|
118
123
|
return { success: true };
|
|
119
124
|
}
|
|
120
125
|
|
|
121
126
|
// Handle various failure reasons
|
|
122
|
-
console.log('[BiometricUtils] Biometric authentication failed:', error);
|
|
127
|
+
console.log('[BiometricUtils] Biometric authentication failed:', result.error);
|
|
123
128
|
|
|
124
|
-
|
|
125
|
-
const errorMessage = error || 'Authentication failed';
|
|
126
|
-
let reason: NonNullable<BiometricResult['error']>['reason'] = 'failed';
|
|
129
|
+
let reason: 'cancelled' | 'failed' | 'not_available' | 'not_enrolled' = 'failed';
|
|
127
130
|
|
|
128
|
-
if (
|
|
129
|
-
errorMessage.toLowerCase().includes('cancel') ||
|
|
130
|
-
errorMessage.toLowerCase().includes('user cancel')
|
|
131
|
-
) {
|
|
131
|
+
if (result.error === 'user_cancel' || result.error === 'system_cancel') {
|
|
132
132
|
reason = 'cancelled';
|
|
133
|
-
} else if (
|
|
134
|
-
errorMessage.toLowerCase().includes('not enrolled') ||
|
|
135
|
-
errorMessage.toLowerCase().includes('no fingerprint') ||
|
|
136
|
-
errorMessage.toLowerCase().includes('no face')
|
|
137
|
-
) {
|
|
133
|
+
} else if (result.error === 'not_enrolled') {
|
|
138
134
|
reason = 'not_enrolled';
|
|
139
|
-
} else if (
|
|
140
|
-
errorMessage.toLowerCase().includes('not available') ||
|
|
141
|
-
errorMessage.toLowerCase().includes('not supported')
|
|
142
|
-
) {
|
|
135
|
+
} else if (result.error === 'not_available') {
|
|
143
136
|
reason = 'not_available';
|
|
144
137
|
}
|
|
145
138
|
|
|
@@ -147,14 +140,13 @@ export async function authenticateWithBiometrics(
|
|
|
147
140
|
success: false,
|
|
148
141
|
error: {
|
|
149
142
|
reason,
|
|
150
|
-
message:
|
|
143
|
+
message: result.warning || 'Authentication failed',
|
|
151
144
|
},
|
|
152
145
|
};
|
|
153
146
|
} catch (error) {
|
|
154
147
|
console.error('[BiometricUtils] Biometric authentication error:', error);
|
|
155
148
|
|
|
156
|
-
const errorMessage =
|
|
157
|
-
error instanceof Error ? error.message : 'Unknown error occurred';
|
|
149
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
158
150
|
|
|
159
151
|
return {
|
|
160
152
|
success: false,
|
|
@@ -172,18 +164,20 @@ export async function authenticateWithBiometrics(
|
|
|
172
164
|
* @param biometryType - The biometry type from the device
|
|
173
165
|
* @returns Human-readable string for the biometry type
|
|
174
166
|
*/
|
|
175
|
-
export function getBiometryTypeLabel(
|
|
167
|
+
export function getBiometryTypeLabel(
|
|
168
|
+
biometryType: LocalAuthentication.AuthenticationType
|
|
169
|
+
): string {
|
|
176
170
|
switch (biometryType) {
|
|
177
|
-
case
|
|
171
|
+
case LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION:
|
|
178
172
|
return 'Face ID';
|
|
179
|
-
case
|
|
173
|
+
case LocalAuthentication.AuthenticationType.FINGERPRINT:
|
|
180
174
|
return 'Touch ID';
|
|
181
|
-
case
|
|
182
|
-
return '
|
|
175
|
+
case LocalAuthentication.AuthenticationType.IRIS:
|
|
176
|
+
return 'Iris';
|
|
183
177
|
default:
|
|
184
178
|
return 'Biometric Authentication';
|
|
185
179
|
}
|
|
186
180
|
}
|
|
187
181
|
|
|
188
|
-
// Re-export
|
|
189
|
-
export {
|
|
182
|
+
// Re-export AuthenticationType for convenience
|
|
183
|
+
export { AuthenticationType } from 'expo-local-authentication';
|
package/src/cryptoUtils.ts
CHANGED
|
@@ -7,13 +7,11 @@
|
|
|
7
7
|
* - Input to RSA: secretKey as Base64 string
|
|
8
8
|
* - Output: encrypted sessionId as Base64 string
|
|
9
9
|
*
|
|
10
|
-
* Uses
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* @requires react-native-quick-crypto - npm install react-native-quick-crypto
|
|
10
|
+
* Uses node-forge for pure JavaScript RSA-OAEP encryption.
|
|
11
|
+
* This works in Expo Go without native modules.
|
|
14
12
|
*/
|
|
15
13
|
|
|
16
|
-
import
|
|
14
|
+
import forge from 'node-forge';
|
|
17
15
|
|
|
18
16
|
/**
|
|
19
17
|
* Generates a random hex key matching Android's generateRandomHexKey()
|
|
@@ -50,39 +48,14 @@ export function hexToBase64(hexString: string): string {
|
|
|
50
48
|
binaryString += String.fromCharCode(byte);
|
|
51
49
|
}
|
|
52
50
|
|
|
53
|
-
// Use
|
|
54
|
-
|
|
55
|
-
return btoa(binaryString);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Fallback: manual Base64 encoding
|
|
59
|
-
const base64Chars =
|
|
60
|
-
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
61
|
-
let result = '';
|
|
62
|
-
let i = 0;
|
|
63
|
-
|
|
64
|
-
while (i < binaryString.length) {
|
|
65
|
-
const a = binaryString.charCodeAt(i++);
|
|
66
|
-
const b = binaryString.charCodeAt(i++);
|
|
67
|
-
const c = binaryString.charCodeAt(i++);
|
|
68
|
-
|
|
69
|
-
const triplet = (a << 16) | ((b || 0) << 8) | (c || 0);
|
|
70
|
-
|
|
71
|
-
result +=
|
|
72
|
-
base64Chars[(triplet >> 18) & 0x3f] +
|
|
73
|
-
base64Chars[(triplet >> 12) & 0x3f] +
|
|
74
|
-
(isNaN(b) ? '=' : base64Chars[(triplet >> 6) & 0x3f]) +
|
|
75
|
-
(isNaN(c) ? '=' : base64Chars[triplet & 0x3f]);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return result;
|
|
51
|
+
// Use forge's utility for Base64 encoding
|
|
52
|
+
return forge.util.encode64(binaryString);
|
|
79
53
|
}
|
|
80
54
|
|
|
81
55
|
/**
|
|
82
|
-
* Encrypts data using RSA with OAEP-SHA1 padding
|
|
56
|
+
* Encrypts data using RSA with OAEP-SHA1 padding
|
|
83
57
|
*
|
|
84
58
|
* MUST match Android's RSA/ECB/OAEPWithSHA-1AndMGF1Padding algorithm
|
|
85
|
-
* Uses native crypto (OpenSSL on Android, CommonCrypto on iOS)
|
|
86
59
|
*
|
|
87
60
|
* @param data - Plain text data to encrypt (Base64 string of the secret key)
|
|
88
61
|
* @param publicKeyPem - RSA public key in PEM format
|
|
@@ -91,21 +64,19 @@ export function hexToBase64(hexString: string): string {
|
|
|
91
64
|
*/
|
|
92
65
|
export function encryptWithRSA(data: string, publicKeyPem: string): string {
|
|
93
66
|
try {
|
|
94
|
-
//
|
|
95
|
-
const
|
|
67
|
+
// Parse the PEM-formatted public key
|
|
68
|
+
const publicKey = forge.pki.publicKeyFromPem(publicKeyPem);
|
|
96
69
|
|
|
97
70
|
// Encrypt using RSA-OAEP with SHA-1 (matches Android's OAEPWithSHA-1AndMGF1Padding)
|
|
98
|
-
const encrypted =
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
oaepHash: 'sha1',
|
|
71
|
+
const encrypted = publicKey.encrypt(data, 'RSA-OAEP', {
|
|
72
|
+
md: forge.md.sha1.create(),
|
|
73
|
+
mgf1: {
|
|
74
|
+
md: forge.md.sha1.create(),
|
|
103
75
|
},
|
|
104
|
-
|
|
105
|
-
);
|
|
76
|
+
});
|
|
106
77
|
|
|
107
|
-
//
|
|
108
|
-
return
|
|
78
|
+
// Encode to Base64
|
|
79
|
+
return forge.util.encode64(encrypted);
|
|
109
80
|
} catch (error) {
|
|
110
81
|
console.error('[CryptoUtils] RSA encryption failed:', error);
|
|
111
82
|
throw new Error('Failed to encrypt session data');
|
package/src/index.ts
CHANGED