@oxyhq/core 1.11.12 → 1.11.13
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/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/CrossDomainAuth.js +3 -1
- package/dist/cjs/HttpService.js +214 -33
- package/dist/cjs/OxyServices.base.js +9 -0
- package/dist/cjs/OxyServices.js +8 -3
- package/dist/cjs/crypto/index.js +3 -1
- package/dist/cjs/crypto/keyManager.js +476 -172
- package/dist/cjs/crypto/polyfill.js +14 -65
- package/dist/cjs/crypto/recoveryPhrase.js +30 -11
- package/dist/cjs/crypto/signatureService.js +25 -60
- package/dist/cjs/i18n/locales/en-US.json +46 -1
- package/dist/cjs/i18n/locales/es-ES.json +46 -1
- package/dist/cjs/i18n/locales/locales/en-US.json +46 -1
- package/dist/cjs/i18n/locales/locales/es-ES.json +46 -1
- package/dist/cjs/index.js +7 -2
- package/dist/cjs/mixins/OxyServices.assets.js +9 -4
- package/dist/cjs/mixins/OxyServices.auth.js +27 -0
- package/dist/cjs/mixins/OxyServices.contacts.js +50 -0
- package/dist/cjs/mixins/OxyServices.features.js +0 -11
- package/dist/cjs/mixins/OxyServices.fedcm.js +4 -3
- package/dist/cjs/mixins/OxyServices.language.js +5 -36
- package/dist/cjs/mixins/OxyServices.redirect.js +6 -2
- package/dist/cjs/mixins/OxyServices.security.js +13 -2
- package/dist/cjs/mixins/OxyServices.user.js +59 -38
- package/dist/cjs/mixins/OxyServices.utility.js +19 -43
- package/dist/cjs/mixins/index.js +11 -3
- package/dist/cjs/utils/accountUtils.js +71 -2
- package/dist/cjs/utils/deviceManager.js +5 -36
- package/dist/cjs/utils/platformCrypto.js +165 -0
- package/dist/cjs/utils/platformCrypto.native.js +123 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/CrossDomainAuth.js +3 -1
- package/dist/esm/HttpService.js +215 -34
- package/dist/esm/OxyServices.base.js +9 -0
- package/dist/esm/OxyServices.js +8 -3
- package/dist/esm/crypto/index.js +1 -1
- package/dist/esm/crypto/keyManager.js +473 -138
- package/dist/esm/crypto/polyfill.js +14 -32
- package/dist/esm/crypto/recoveryPhrase.js +30 -11
- package/dist/esm/crypto/signatureService.js +25 -27
- package/dist/esm/i18n/locales/en-US.json +46 -1
- package/dist/esm/i18n/locales/es-ES.json +46 -1
- package/dist/esm/i18n/locales/locales/en-US.json +46 -1
- package/dist/esm/i18n/locales/locales/es-ES.json +46 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/mixins/OxyServices.assets.js +9 -4
- package/dist/esm/mixins/OxyServices.auth.js +27 -0
- package/dist/esm/mixins/OxyServices.contacts.js +47 -0
- package/dist/esm/mixins/OxyServices.features.js +0 -11
- package/dist/esm/mixins/OxyServices.fedcm.js +4 -3
- package/dist/esm/mixins/OxyServices.language.js +5 -3
- package/dist/esm/mixins/OxyServices.redirect.js +6 -2
- package/dist/esm/mixins/OxyServices.security.js +13 -2
- package/dist/esm/mixins/OxyServices.user.js +59 -38
- package/dist/esm/mixins/OxyServices.utility.js +19 -10
- package/dist/esm/mixins/index.js +11 -3
- package/dist/esm/utils/accountUtils.js +67 -1
- package/dist/esm/utils/deviceManager.js +5 -3
- package/dist/esm/utils/platformCrypto.js +125 -0
- package/dist/esm/utils/platformCrypto.native.js +80 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/HttpService.d.ts +47 -3
- package/dist/types/OxyServices.base.d.ts +7 -0
- package/dist/types/OxyServices.d.ts +36 -3
- package/dist/types/crypto/index.d.ts +1 -1
- package/dist/types/crypto/keyManager.d.ts +110 -9
- package/dist/types/crypto/polyfill.d.ts +3 -1
- package/dist/types/crypto/recoveryPhrase.d.ts +31 -7
- package/dist/types/crypto/signatureService.d.ts +4 -0
- package/dist/types/index.d.ts +4 -3
- package/dist/types/mixins/OxyServices.analytics.d.ts +1 -0
- package/dist/types/mixins/OxyServices.assets.d.ts +6 -10
- package/dist/types/mixins/OxyServices.auth.d.ts +16 -0
- package/dist/types/mixins/OxyServices.contacts.d.ts +99 -0
- package/dist/types/mixins/OxyServices.developer.d.ts +1 -0
- package/dist/types/mixins/OxyServices.devices.d.ts +1 -0
- package/dist/types/mixins/OxyServices.features.d.ts +2 -7
- package/dist/types/mixins/OxyServices.fedcm.d.ts +1 -0
- package/dist/types/mixins/OxyServices.karma.d.ts +1 -0
- package/dist/types/mixins/OxyServices.language.d.ts +1 -0
- package/dist/types/mixins/OxyServices.location.d.ts +1 -0
- package/dist/types/mixins/OxyServices.managedAccounts.d.ts +1 -0
- package/dist/types/mixins/OxyServices.payment.d.ts +1 -0
- package/dist/types/mixins/OxyServices.popup.d.ts +1 -0
- package/dist/types/mixins/OxyServices.privacy.d.ts +1 -0
- package/dist/types/mixins/OxyServices.redirect.d.ts +1 -0
- package/dist/types/mixins/OxyServices.security.d.ts +1 -0
- package/dist/types/mixins/OxyServices.topics.d.ts +1 -0
- package/dist/types/mixins/OxyServices.user.d.ts +28 -11
- package/dist/types/mixins/OxyServices.utility.d.ts +1 -0
- package/dist/types/mixins/index.d.ts +52 -4
- package/dist/types/models/interfaces.d.ts +62 -3
- package/dist/types/utils/accountUtils.d.ts +41 -1
- package/dist/types/utils/platformCrypto.d.ts +87 -0
- package/dist/types/utils/platformCrypto.native.d.ts +54 -0
- package/package.json +28 -1
- package/src/CrossDomainAuth.ts +12 -10
- package/src/HttpService.ts +251 -40
- package/src/OxyServices.base.ts +10 -0
- package/src/OxyServices.ts +9 -4
- package/src/crypto/__tests__/keyManager.test.ts +336 -0
- package/src/crypto/index.ts +6 -1
- package/src/crypto/keyManager.ts +529 -151
- package/src/crypto/polyfill.ts +14 -34
- package/src/crypto/recoveryPhrase.ts +56 -17
- package/src/crypto/signatureService.ts +25 -30
- package/src/i18n/locales/en-US.json +46 -1
- package/src/i18n/locales/es-ES.json +46 -1
- package/src/index.ts +16 -3
- package/src/mixins/OxyServices.assets.ts +15 -11
- package/src/mixins/OxyServices.auth.ts +28 -0
- package/src/mixins/OxyServices.contacts.ts +73 -0
- package/src/mixins/OxyServices.features.ts +2 -12
- package/src/mixins/OxyServices.fedcm.ts +4 -3
- package/src/mixins/OxyServices.language.ts +6 -4
- package/src/mixins/OxyServices.redirect.ts +6 -2
- package/src/mixins/OxyServices.security.ts +18 -8
- package/src/mixins/OxyServices.user.ts +72 -49
- package/src/mixins/OxyServices.utility.ts +19 -10
- package/src/mixins/index.ts +58 -7
- package/src/models/interfaces.ts +65 -3
- package/src/utils/accountUtils.ts +82 -2
- package/src/utils/deviceManager.ts +7 -4
- package/src/utils/platformCrypto.native.ts +101 -0
- package/src/utils/platformCrypto.ts +145 -0
|
@@ -6,45 +6,15 @@
|
|
|
6
6
|
* across all platforms (Node.js, Browser, React Native).
|
|
7
7
|
*
|
|
8
8
|
* - Browser/Node.js: Uses native crypto
|
|
9
|
-
* - React Native:
|
|
9
|
+
* - React Native: Uses expo-crypto (statically imported via the
|
|
10
|
+
* per-platform `platformCrypto` module — see that file's doc-comment for
|
|
11
|
+
* how platform routing works).
|
|
10
12
|
*/
|
|
11
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
-
if (k2 === undefined) k2 = k;
|
|
13
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
-
}
|
|
17
|
-
Object.defineProperty(o, k2, desc);
|
|
18
|
-
}) : (function(o, m, k, k2) {
|
|
19
|
-
if (k2 === undefined) k2 = k;
|
|
20
|
-
o[k2] = m[k];
|
|
21
|
-
}));
|
|
22
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
-
}) : function(o, v) {
|
|
25
|
-
o["default"] = v;
|
|
26
|
-
});
|
|
27
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
-
var ownKeys = function(o) {
|
|
29
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
-
var ar = [];
|
|
31
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
-
return ar;
|
|
33
|
-
};
|
|
34
|
-
return ownKeys(o);
|
|
35
|
-
};
|
|
36
|
-
return function (mod) {
|
|
37
|
-
if (mod && mod.__esModule) return mod;
|
|
38
|
-
var result = {};
|
|
39
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
-
__setModuleDefault(result, mod);
|
|
41
|
-
return result;
|
|
42
|
-
};
|
|
43
|
-
})();
|
|
44
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
14
|
exports.Buffer = void 0;
|
|
46
15
|
const buffer_1 = require("buffer");
|
|
47
16
|
Object.defineProperty(exports, "Buffer", { enumerable: true, get: function () { return buffer_1.Buffer; } });
|
|
17
|
+
const platformCrypto_1 = require("../utils/platformCrypto");
|
|
48
18
|
const getGlobalObject = () => {
|
|
49
19
|
if (typeof globalThis !== 'undefined')
|
|
50
20
|
return globalThis;
|
|
@@ -61,37 +31,19 @@ const globalObject = getGlobalObject();
|
|
|
61
31
|
if (!globalObject.Buffer) {
|
|
62
32
|
globalObject.Buffer = buffer_1.Buffer;
|
|
63
33
|
}
|
|
64
|
-
// Cache for expo-crypto module (lazy loaded only in React Native)
|
|
65
|
-
let expoCryptoModule = null;
|
|
66
|
-
let expoCryptoLoadPromise = null;
|
|
67
34
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
35
|
+
* Synchronous random-bytes shim. On RN, this delegates to
|
|
36
|
+
* `expo-crypto.getRandomBytes` (statically imported by the RN variant of
|
|
37
|
+
* `platformCrypto`, so available without any async warm-up). On Node /
|
|
38
|
+
* browser, this throws — but is never called there because both platforms
|
|
39
|
+
* already provide `globalThis.crypto.getRandomValues` natively.
|
|
72
40
|
*/
|
|
73
|
-
function startExpoCryptoLoad() {
|
|
74
|
-
if (expoCryptoLoadPromise)
|
|
75
|
-
return;
|
|
76
|
-
expoCryptoLoadPromise = (async () => {
|
|
77
|
-
try {
|
|
78
|
-
const moduleName = 'expo-crypto';
|
|
79
|
-
expoCryptoModule = await Promise.resolve(`${moduleName}`).then(s => __importStar(require(s)));
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
// expo-crypto not available — expected in non-RN environments
|
|
83
|
-
}
|
|
84
|
-
})();
|
|
85
|
-
}
|
|
86
41
|
function getRandomBytesSync(byteCount) {
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
throw new Error('No crypto.getRandomValues implementation available. ' +
|
|
93
|
-
'In React Native, install expo-crypto. ' +
|
|
94
|
-
'If expo-crypto is installed, ensure the polyfill module is imported early enough for the async load to complete.');
|
|
42
|
+
// `getRandomBytesRN` throws on non-RN platforms. That's fine: this
|
|
43
|
+
// function is only ever called as a fallback when the native
|
|
44
|
+
// `globalThis.crypto.getRandomValues` is missing, which on a normal
|
|
45
|
+
// Node/browser host never happens.
|
|
46
|
+
return (0, platformCrypto_1.getRandomBytesRN)(byteCount);
|
|
95
47
|
}
|
|
96
48
|
const cryptoPolyfill = {
|
|
97
49
|
getRandomValues(array) {
|
|
@@ -103,11 +55,8 @@ const cryptoPolyfill = {
|
|
|
103
55
|
};
|
|
104
56
|
// Only polyfill if crypto or crypto.getRandomValues is not available
|
|
105
57
|
if (typeof globalObject.crypto === 'undefined') {
|
|
106
|
-
// Start loading expo-crypto eagerly so it is ready by the time getRandomValues is called
|
|
107
|
-
startExpoCryptoLoad();
|
|
108
58
|
globalObject.crypto = cryptoPolyfill;
|
|
109
59
|
}
|
|
110
60
|
else if (typeof globalObject.crypto.getRandomValues !== 'function') {
|
|
111
|
-
startExpoCryptoLoad();
|
|
112
61
|
globalObject.crypto.getRandomValues = cryptoPolyfill.getRandomValues;
|
|
113
62
|
}
|
|
@@ -57,10 +57,15 @@ function toHex(data) {
|
|
|
57
57
|
}
|
|
58
58
|
class RecoveryPhraseService {
|
|
59
59
|
/**
|
|
60
|
-
* Generate a new identity with a recovery phrase
|
|
61
|
-
*
|
|
60
|
+
* Generate a new identity with a recovery phrase.
|
|
61
|
+
* The mnemonic phrase MUST be shown to the user exactly once after this
|
|
62
|
+
* call resolves — if it is lost, the account becomes unrecoverable.
|
|
63
|
+
*
|
|
64
|
+
* Refuses to overwrite an existing identity unless `options.overwrite === true`.
|
|
65
|
+
*
|
|
66
|
+
* @throws IdentityAlreadyExistsError if an identity already exists and overwrite is not set
|
|
62
67
|
*/
|
|
63
|
-
static async generateIdentityWithRecovery() {
|
|
68
|
+
static async generateIdentityWithRecovery(options) {
|
|
64
69
|
// Generate 128-bit entropy for 12-word mnemonic
|
|
65
70
|
const mnemonic = bip39.generateMnemonic(128);
|
|
66
71
|
// Derive private key from mnemonic
|
|
@@ -69,8 +74,11 @@ class RecoveryPhraseService {
|
|
|
69
74
|
// Use first 32 bytes of seed as private key
|
|
70
75
|
const seedSlice = seed.subarray ? seed.subarray(0, 32) : seed.slice(0, 32);
|
|
71
76
|
const privateKeyHex = toHex(seedSlice);
|
|
72
|
-
// Import the derived key pair
|
|
73
|
-
|
|
77
|
+
// Import the derived key pair. KeyManager.importKeyPair will refuse to
|
|
78
|
+
// clobber an existing identity unless overwrite is explicitly requested.
|
|
79
|
+
const publicKey = await keyManager_1.KeyManager.importKeyPair(privateKeyHex, {
|
|
80
|
+
overwrite: options?.overwrite === true,
|
|
81
|
+
});
|
|
74
82
|
return {
|
|
75
83
|
phrase: mnemonic,
|
|
76
84
|
words: mnemonic.split(' '),
|
|
@@ -78,15 +86,19 @@ class RecoveryPhraseService {
|
|
|
78
86
|
};
|
|
79
87
|
}
|
|
80
88
|
/**
|
|
81
|
-
* Generate a 24-word recovery phrase for higher security
|
|
89
|
+
* Generate a 24-word recovery phrase for higher security.
|
|
90
|
+
*
|
|
91
|
+
* Same overwrite-protection semantics as `generateIdentityWithRecovery`.
|
|
82
92
|
*/
|
|
83
|
-
static async generateIdentityWithRecovery24() {
|
|
93
|
+
static async generateIdentityWithRecovery24(options) {
|
|
84
94
|
// Generate 256-bit entropy for 24-word mnemonic
|
|
85
95
|
const mnemonic = bip39.generateMnemonic(256);
|
|
86
96
|
const seed = await bip39.mnemonicToSeed(mnemonic);
|
|
87
97
|
const seedSlice = seed.subarray ? seed.subarray(0, 32) : seed.slice(0, 32);
|
|
88
98
|
const privateKeyHex = toHex(seedSlice);
|
|
89
|
-
const publicKey = await keyManager_1.KeyManager.importKeyPair(privateKeyHex
|
|
99
|
+
const publicKey = await keyManager_1.KeyManager.importKeyPair(privateKeyHex, {
|
|
100
|
+
overwrite: options?.overwrite === true,
|
|
101
|
+
});
|
|
90
102
|
return {
|
|
91
103
|
phrase: mnemonic,
|
|
92
104
|
words: mnemonic.split(' '),
|
|
@@ -94,9 +106,14 @@ class RecoveryPhraseService {
|
|
|
94
106
|
};
|
|
95
107
|
}
|
|
96
108
|
/**
|
|
97
|
-
* Restore an identity from a recovery phrase
|
|
109
|
+
* Restore an identity from a recovery phrase.
|
|
110
|
+
*
|
|
111
|
+
* Refuses to overwrite a DIFFERENT existing identity unless
|
|
112
|
+
* `options.overwrite === true`. Re-importing the same phrase that
|
|
113
|
+
* matches the current identity is always allowed (it's a no-op refresh
|
|
114
|
+
* of the backup record).
|
|
98
115
|
*/
|
|
99
|
-
static async restoreFromPhrase(phrase) {
|
|
116
|
+
static async restoreFromPhrase(phrase, options) {
|
|
100
117
|
// Normalize and validate the phrase
|
|
101
118
|
const normalizedPhrase = phrase.trim().toLowerCase();
|
|
102
119
|
if (!bip39.validateMnemonic(normalizedPhrase)) {
|
|
@@ -107,7 +124,9 @@ class RecoveryPhraseService {
|
|
|
107
124
|
const seedSlice = seed.subarray ? seed.subarray(0, 32) : seed.slice(0, 32);
|
|
108
125
|
const privateKeyHex = toHex(seedSlice);
|
|
109
126
|
// Import and store the key pair
|
|
110
|
-
const publicKey = await keyManager_1.KeyManager.importKeyPair(privateKeyHex
|
|
127
|
+
const publicKey = await keyManager_1.KeyManager.importKeyPair(privateKeyHex, {
|
|
128
|
+
overwrite: options?.overwrite === true,
|
|
129
|
+
});
|
|
111
130
|
return publicKey;
|
|
112
131
|
}
|
|
113
132
|
/**
|
|
@@ -5,78 +5,32 @@
|
|
|
5
5
|
* Handles signing and verification of messages using ECDSA secp256k1.
|
|
6
6
|
* Used for authenticating requests and proving identity ownership.
|
|
7
7
|
*/
|
|
8
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
-
if (k2 === undefined) k2 = k;
|
|
10
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
-
}
|
|
14
|
-
Object.defineProperty(o, k2, desc);
|
|
15
|
-
}) : (function(o, m, k, k2) {
|
|
16
|
-
if (k2 === undefined) k2 = k;
|
|
17
|
-
o[k2] = m[k];
|
|
18
|
-
}));
|
|
19
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
-
}) : function(o, v) {
|
|
22
|
-
o["default"] = v;
|
|
23
|
-
});
|
|
24
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
-
var ownKeys = function(o) {
|
|
26
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
-
var ar = [];
|
|
28
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
-
return ar;
|
|
30
|
-
};
|
|
31
|
-
return ownKeys(o);
|
|
32
|
-
};
|
|
33
|
-
return function (mod) {
|
|
34
|
-
if (mod && mod.__esModule) return mod;
|
|
35
|
-
var result = {};
|
|
36
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
-
__setModuleDefault(result, mod);
|
|
38
|
-
return result;
|
|
39
|
-
};
|
|
40
|
-
})();
|
|
41
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
9
|
exports.SignatureService = void 0;
|
|
43
10
|
const elliptic_1 = require("elliptic");
|
|
44
11
|
const keyManager_1 = require("./keyManager");
|
|
45
12
|
const platform_1 = require("../utils/platform");
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
13
|
+
const platformCrypto_1 = require("../utils/platformCrypto");
|
|
14
|
+
const loggerUtils_1 = require("../utils/loggerUtils");
|
|
15
|
+
const debugUtils_1 = require("../shared/utils/debugUtils");
|
|
49
16
|
const ec = new elliptic_1.ec('secp256k1');
|
|
50
|
-
async function initExpoCrypto() {
|
|
51
|
-
if (!ExpoCrypto) {
|
|
52
|
-
const moduleName = 'expo-crypto';
|
|
53
|
-
ExpoCrypto = await Promise.resolve(`${moduleName}`).then(s => __importStar(require(s)));
|
|
54
|
-
}
|
|
55
|
-
return ExpoCrypto;
|
|
56
|
-
}
|
|
57
|
-
async function initNodeCrypto() {
|
|
58
|
-
if (!NodeCrypto) {
|
|
59
|
-
const moduleName = 'crypto';
|
|
60
|
-
NodeCrypto = await Promise.resolve(`${moduleName}`).then(s => __importStar(require(s)));
|
|
61
|
-
}
|
|
62
|
-
return NodeCrypto;
|
|
63
|
-
}
|
|
64
17
|
/**
|
|
65
18
|
* Compute SHA-256 hash of a string
|
|
66
19
|
*/
|
|
67
20
|
async function sha256(message) {
|
|
68
21
|
// In React Native, use expo-crypto
|
|
69
22
|
if ((0, platform_1.isReactNative)()) {
|
|
70
|
-
const Crypto = await
|
|
23
|
+
const Crypto = await (0, platformCrypto_1.loadExpoCrypto)();
|
|
71
24
|
return Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256, message);
|
|
72
25
|
}
|
|
73
26
|
if ((0, platform_1.isNodeJS)()) {
|
|
74
27
|
try {
|
|
75
|
-
const nodeCrypto = await
|
|
28
|
+
const nodeCrypto = await (0, platformCrypto_1.loadNodeCrypto)();
|
|
76
29
|
return nodeCrypto.createHash('sha256').update(message).digest('hex');
|
|
77
30
|
}
|
|
78
|
-
catch {
|
|
79
|
-
//
|
|
31
|
+
catch (error) {
|
|
32
|
+
// Node crypto failed to load — log and fall through to Web Crypto API
|
|
33
|
+
loggerUtils_1.logger.warn('[oxy.crypto] Node crypto unavailable, falling back to Web Crypto', { component: 'SignatureService' }, error);
|
|
80
34
|
}
|
|
81
35
|
}
|
|
82
36
|
// Browser: use Web Crypto API
|
|
@@ -94,7 +48,7 @@ class SignatureService {
|
|
|
94
48
|
static async generateChallenge() {
|
|
95
49
|
// In React Native, use expo-crypto
|
|
96
50
|
if ((0, platform_1.isReactNative)()) {
|
|
97
|
-
const Crypto = await
|
|
51
|
+
const Crypto = await (0, platformCrypto_1.loadExpoCrypto)();
|
|
98
52
|
const randomBytes = await Crypto.getRandomBytesAsync(32);
|
|
99
53
|
return Array.from(new Uint8Array(randomBytes))
|
|
100
54
|
.map((b) => b.toString(16).padStart(2, '0'))
|
|
@@ -102,11 +56,12 @@ class SignatureService {
|
|
|
102
56
|
}
|
|
103
57
|
if ((0, platform_1.isNodeJS)()) {
|
|
104
58
|
try {
|
|
105
|
-
const nodeCrypto = await
|
|
59
|
+
const nodeCrypto = await (0, platformCrypto_1.loadNodeCrypto)();
|
|
106
60
|
return nodeCrypto.randomBytes(32).toString('hex');
|
|
107
61
|
}
|
|
108
|
-
catch {
|
|
109
|
-
//
|
|
62
|
+
catch (error) {
|
|
63
|
+
// Node crypto failed to load — log and fall through to Web Crypto API
|
|
64
|
+
loggerUtils_1.logger.warn('[oxy.crypto] Node crypto unavailable, falling back to Web Crypto', { component: 'SignatureService' }, error);
|
|
110
65
|
}
|
|
111
66
|
}
|
|
112
67
|
// Browser: use Web Crypto API
|
|
@@ -147,6 +102,10 @@ class SignatureService {
|
|
|
147
102
|
}
|
|
148
103
|
/**
|
|
149
104
|
* Verify a signature against a message and public key
|
|
105
|
+
*
|
|
106
|
+
* Returns false on any error (invalid signature, malformed input, etc.).
|
|
107
|
+
* Errors are logged at debug level so they're available when troubleshooting
|
|
108
|
+
* signature mismatches but don't surface to the caller.
|
|
150
109
|
*/
|
|
151
110
|
static async verify(message, signature, publicKey) {
|
|
152
111
|
try {
|
|
@@ -154,7 +113,10 @@ class SignatureService {
|
|
|
154
113
|
const messageHash = await sha256(message);
|
|
155
114
|
return key.verify(messageHash, signature);
|
|
156
115
|
}
|
|
157
|
-
catch {
|
|
116
|
+
catch (error) {
|
|
117
|
+
if ((0, debugUtils_1.isDev)()) {
|
|
118
|
+
loggerUtils_1.logger.debug('[oxy.crypto] verify() returned false', { component: 'SignatureService' }, error);
|
|
119
|
+
}
|
|
158
120
|
return false;
|
|
159
121
|
}
|
|
160
122
|
}
|
|
@@ -180,7 +142,10 @@ class SignatureService {
|
|
|
180
142
|
const messageHash = crypto.createHash('sha256').update(message).digest('hex');
|
|
181
143
|
return key.verify(messageHash, signature);
|
|
182
144
|
}
|
|
183
|
-
catch {
|
|
145
|
+
catch (error) {
|
|
146
|
+
if ((0, debugUtils_1.isDev)()) {
|
|
147
|
+
loggerUtils_1.logger.debug('[oxy.crypto] verifySync() returned false', { component: 'SignatureService' }, error);
|
|
148
|
+
}
|
|
184
149
|
return false;
|
|
185
150
|
}
|
|
186
151
|
}
|
|
@@ -655,6 +655,21 @@
|
|
|
655
655
|
"confirms": {
|
|
656
656
|
"removeAvatar": "Remove your profile picture?"
|
|
657
657
|
},
|
|
658
|
+
"crop": {
|
|
659
|
+
"title": "Crop avatar",
|
|
660
|
+
"subtitle": "Pinch to zoom, drag to position",
|
|
661
|
+
"noImage": "No image to crop",
|
|
662
|
+
"reset": "Reset",
|
|
663
|
+
"resetToCenter": "Reset to center",
|
|
664
|
+
"confirm": "Use photo",
|
|
665
|
+
"cancel": "Cancel",
|
|
666
|
+
"saving": "Saving…",
|
|
667
|
+
"helper": "The cropped circle is what will appear on your profile. Pinch to zoom, drag to position.",
|
|
668
|
+
"zoom": "{{value}}×",
|
|
669
|
+
"a11yImage": "Crop preview. Pinch to zoom and drag to reposition the image.",
|
|
670
|
+
"a11yReset": "Reset crop to default position",
|
|
671
|
+
"a11yResetAnnouncement": "Crop reset"
|
|
672
|
+
},
|
|
658
673
|
"toasts": {
|
|
659
674
|
"profileUpdated": "Profile updated successfully",
|
|
660
675
|
"updateFailed": "Failed to update profile",
|
|
@@ -664,7 +679,10 @@
|
|
|
664
679
|
"avatarSelected": "Avatar selected",
|
|
665
680
|
"avatarUpdated": "Avatar updated",
|
|
666
681
|
"updateAvatarFailed": "Failed to update avatar",
|
|
667
|
-
"noActiveSession": "No active session"
|
|
682
|
+
"noActiveSession": "No active session",
|
|
683
|
+
"cropMeasureFailed": "Could not measure the image",
|
|
684
|
+
"cropNotReady": "Image not ready yet",
|
|
685
|
+
"cropFailed": "Failed to crop image"
|
|
668
686
|
}
|
|
669
687
|
},
|
|
670
688
|
"accountOverview": {
|
|
@@ -1046,6 +1064,8 @@
|
|
|
1046
1064
|
"save": "Save",
|
|
1047
1065
|
"saved": "Saved successfully",
|
|
1048
1066
|
"saving": "Saving...",
|
|
1067
|
+
"unnamed": "Unnamed",
|
|
1068
|
+
"accountFallback": "Account {{handle}}",
|
|
1049
1069
|
"links": {
|
|
1050
1070
|
"recoverAccount": "Recover your account",
|
|
1051
1071
|
"signUp": "Sign Up"
|
|
@@ -1308,9 +1328,34 @@
|
|
|
1308
1328
|
"loadingMore": "Loading more...",
|
|
1309
1329
|
"loadingPhotoLayout": "Loading photo layout...",
|
|
1310
1330
|
"uploading": "Uploading",
|
|
1331
|
+
"upload": "Upload",
|
|
1332
|
+
"uploadPhoto": "Upload Photo",
|
|
1311
1333
|
"uploadPhotos": "Upload Photos",
|
|
1312
1334
|
"uploadFiles": "Upload Files",
|
|
1313
1335
|
"clearSearch": "Clear Search",
|
|
1336
|
+
"choosePhoto": "Choose Photo",
|
|
1337
|
+
"done": "Done",
|
|
1338
|
+
"doneWithCount": "Done ({{count}})",
|
|
1339
|
+
"photoPicker": {
|
|
1340
|
+
"emptyTitle": "No photos yet",
|
|
1341
|
+
"emptySubtitle": "Upload from your device to get started"
|
|
1342
|
+
},
|
|
1343
|
+
"a11y": {
|
|
1344
|
+
"viewAll": "Show all files",
|
|
1345
|
+
"viewPhotos": "Show photos only",
|
|
1346
|
+
"viewVideos": "Show videos only",
|
|
1347
|
+
"viewDocuments": "Show documents only",
|
|
1348
|
+
"viewAudio": "Show audio only",
|
|
1349
|
+
"sortBy": "Sort by {{field}}, {{order}}",
|
|
1350
|
+
"uploadFile": "Upload file from device",
|
|
1351
|
+
"uploadFromDevice": "Upload photo from device",
|
|
1352
|
+
"photoCellSelected": "Photo {{name}}, selected",
|
|
1353
|
+
"photoCellUnselected": "Photo {{name}}, not selected",
|
|
1354
|
+
"selectionCount": "{{count}} photo selected",
|
|
1355
|
+
"selectionCount_plural": "{{count}} photos selected",
|
|
1356
|
+
"cancelPicker": "Cancel photo selection",
|
|
1357
|
+
"confirmSelection": "Confirm selection"
|
|
1358
|
+
},
|
|
1314
1359
|
"emptyPhotos": {
|
|
1315
1360
|
"title": "No Photos Yet",
|
|
1316
1361
|
"ownDescription": "Upload photos to get started. You can select multiple photos at once.",
|
|
@@ -241,6 +241,21 @@
|
|
|
241
241
|
"confirms": {
|
|
242
242
|
"removeAvatar": "¿Eliminar tu foto de perfil?"
|
|
243
243
|
},
|
|
244
|
+
"crop": {
|
|
245
|
+
"title": "Recortar avatar",
|
|
246
|
+
"subtitle": "Pellizca para acercar, arrastra para colocar",
|
|
247
|
+
"noImage": "No hay imagen para recortar",
|
|
248
|
+
"reset": "Restablecer",
|
|
249
|
+
"resetToCenter": "Restablecer al centro",
|
|
250
|
+
"confirm": "Usar foto",
|
|
251
|
+
"cancel": "Cancelar",
|
|
252
|
+
"saving": "Guardando…",
|
|
253
|
+
"helper": "El círculo recortado es lo que aparecerá en tu perfil. Pellizca para acercar, arrastra para colocar.",
|
|
254
|
+
"zoom": "{{value}}×",
|
|
255
|
+
"a11yImage": "Vista previa del recorte. Pellizca para acercar y arrastra para reposicionar la imagen.",
|
|
256
|
+
"a11yReset": "Restablecer el recorte a la posición predeterminada",
|
|
257
|
+
"a11yResetAnnouncement": "Recorte restablecido"
|
|
258
|
+
},
|
|
244
259
|
"toasts": {
|
|
245
260
|
"profileUpdated": "Perfil actualizado correctamente",
|
|
246
261
|
"updateFailed": "Error al actualizar el perfil",
|
|
@@ -250,7 +265,10 @@
|
|
|
250
265
|
"avatarSelected": "Avatar seleccionado",
|
|
251
266
|
"avatarUpdated": "Avatar actualizado",
|
|
252
267
|
"updateAvatarFailed": "Error al actualizar el avatar",
|
|
253
|
-
"noActiveSession": "No hay sesión activa"
|
|
268
|
+
"noActiveSession": "No hay sesión activa",
|
|
269
|
+
"cropMeasureFailed": "No se pudo medir la imagen",
|
|
270
|
+
"cropNotReady": "La imagen aún no está lista",
|
|
271
|
+
"cropFailed": "No se pudo recortar la imagen"
|
|
254
272
|
}
|
|
255
273
|
},
|
|
256
274
|
"common": {
|
|
@@ -270,6 +288,8 @@
|
|
|
270
288
|
"save": "Guardar",
|
|
271
289
|
"saved": "Guardado correctamente",
|
|
272
290
|
"saving": "Guardando...",
|
|
291
|
+
"unnamed": "Sin nombre",
|
|
292
|
+
"accountFallback": "Cuenta {{handle}}",
|
|
273
293
|
"links": {
|
|
274
294
|
"recoverAccount": "Recuperar tu cuenta",
|
|
275
295
|
"signUp": "Registrarse"
|
|
@@ -1296,9 +1316,34 @@
|
|
|
1296
1316
|
"loadingMore": "Cargando más...",
|
|
1297
1317
|
"loadingPhotoLayout": "Cargando diseño de fotos...",
|
|
1298
1318
|
"uploading": "Subiendo",
|
|
1319
|
+
"upload": "Subir",
|
|
1320
|
+
"uploadPhoto": "Subir foto",
|
|
1299
1321
|
"uploadPhotos": "Subir fotos",
|
|
1300
1322
|
"uploadFiles": "Subir archivos",
|
|
1301
1323
|
"clearSearch": "Limpiar búsqueda",
|
|
1324
|
+
"choosePhoto": "Elegir foto",
|
|
1325
|
+
"done": "Listo",
|
|
1326
|
+
"doneWithCount": "Listo ({{count}})",
|
|
1327
|
+
"photoPicker": {
|
|
1328
|
+
"emptyTitle": "Aún no hay fotos",
|
|
1329
|
+
"emptySubtitle": "Sube desde tu dispositivo para empezar"
|
|
1330
|
+
},
|
|
1331
|
+
"a11y": {
|
|
1332
|
+
"viewAll": "Mostrar todos los archivos",
|
|
1333
|
+
"viewPhotos": "Mostrar solo fotos",
|
|
1334
|
+
"viewVideos": "Mostrar solo vídeos",
|
|
1335
|
+
"viewDocuments": "Mostrar solo documentos",
|
|
1336
|
+
"viewAudio": "Mostrar solo audio",
|
|
1337
|
+
"sortBy": "Ordenar por {{field}}, {{order}}",
|
|
1338
|
+
"uploadFile": "Subir archivo desde el dispositivo",
|
|
1339
|
+
"uploadFromDevice": "Subir foto desde el dispositivo",
|
|
1340
|
+
"photoCellSelected": "Foto {{name}}, seleccionada",
|
|
1341
|
+
"photoCellUnselected": "Foto {{name}}, no seleccionada",
|
|
1342
|
+
"selectionCount": "{{count}} foto seleccionada",
|
|
1343
|
+
"selectionCount_plural": "{{count}} fotos seleccionadas",
|
|
1344
|
+
"cancelPicker": "Cancelar selección de foto",
|
|
1345
|
+
"confirmSelection": "Confirmar selección"
|
|
1346
|
+
},
|
|
1302
1347
|
"emptyPhotos": {
|
|
1303
1348
|
"title": "Aún no hay fotos",
|
|
1304
1349
|
"ownDescription": "Sube fotos para empezar. Puedes seleccionar varias fotos a la vez.",
|
|
@@ -655,6 +655,21 @@
|
|
|
655
655
|
"confirms": {
|
|
656
656
|
"removeAvatar": "Remove your profile picture?"
|
|
657
657
|
},
|
|
658
|
+
"crop": {
|
|
659
|
+
"title": "Crop avatar",
|
|
660
|
+
"subtitle": "Pinch to zoom, drag to position",
|
|
661
|
+
"noImage": "No image to crop",
|
|
662
|
+
"reset": "Reset",
|
|
663
|
+
"resetToCenter": "Reset to center",
|
|
664
|
+
"confirm": "Use photo",
|
|
665
|
+
"cancel": "Cancel",
|
|
666
|
+
"saving": "Saving…",
|
|
667
|
+
"helper": "The cropped circle is what will appear on your profile. Pinch to zoom, drag to position.",
|
|
668
|
+
"zoom": "{{value}}×",
|
|
669
|
+
"a11yImage": "Crop preview. Pinch to zoom and drag to reposition the image.",
|
|
670
|
+
"a11yReset": "Reset crop to default position",
|
|
671
|
+
"a11yResetAnnouncement": "Crop reset"
|
|
672
|
+
},
|
|
658
673
|
"toasts": {
|
|
659
674
|
"profileUpdated": "Profile updated successfully",
|
|
660
675
|
"updateFailed": "Failed to update profile",
|
|
@@ -664,7 +679,10 @@
|
|
|
664
679
|
"avatarSelected": "Avatar selected",
|
|
665
680
|
"avatarUpdated": "Avatar updated",
|
|
666
681
|
"updateAvatarFailed": "Failed to update avatar",
|
|
667
|
-
"noActiveSession": "No active session"
|
|
682
|
+
"noActiveSession": "No active session",
|
|
683
|
+
"cropMeasureFailed": "Could not measure the image",
|
|
684
|
+
"cropNotReady": "Image not ready yet",
|
|
685
|
+
"cropFailed": "Failed to crop image"
|
|
668
686
|
}
|
|
669
687
|
},
|
|
670
688
|
"accountOverview": {
|
|
@@ -1046,6 +1064,8 @@
|
|
|
1046
1064
|
"save": "Save",
|
|
1047
1065
|
"saved": "Saved successfully",
|
|
1048
1066
|
"saving": "Saving...",
|
|
1067
|
+
"unnamed": "Unnamed",
|
|
1068
|
+
"accountFallback": "Account {{handle}}",
|
|
1049
1069
|
"links": {
|
|
1050
1070
|
"recoverAccount": "Recover your account",
|
|
1051
1071
|
"signUp": "Sign Up"
|
|
@@ -1308,9 +1328,34 @@
|
|
|
1308
1328
|
"loadingMore": "Loading more...",
|
|
1309
1329
|
"loadingPhotoLayout": "Loading photo layout...",
|
|
1310
1330
|
"uploading": "Uploading",
|
|
1331
|
+
"upload": "Upload",
|
|
1332
|
+
"uploadPhoto": "Upload Photo",
|
|
1311
1333
|
"uploadPhotos": "Upload Photos",
|
|
1312
1334
|
"uploadFiles": "Upload Files",
|
|
1313
1335
|
"clearSearch": "Clear Search",
|
|
1336
|
+
"choosePhoto": "Choose Photo",
|
|
1337
|
+
"done": "Done",
|
|
1338
|
+
"doneWithCount": "Done ({{count}})",
|
|
1339
|
+
"photoPicker": {
|
|
1340
|
+
"emptyTitle": "No photos yet",
|
|
1341
|
+
"emptySubtitle": "Upload from your device to get started"
|
|
1342
|
+
},
|
|
1343
|
+
"a11y": {
|
|
1344
|
+
"viewAll": "Show all files",
|
|
1345
|
+
"viewPhotos": "Show photos only",
|
|
1346
|
+
"viewVideos": "Show videos only",
|
|
1347
|
+
"viewDocuments": "Show documents only",
|
|
1348
|
+
"viewAudio": "Show audio only",
|
|
1349
|
+
"sortBy": "Sort by {{field}}, {{order}}",
|
|
1350
|
+
"uploadFile": "Upload file from device",
|
|
1351
|
+
"uploadFromDevice": "Upload photo from device",
|
|
1352
|
+
"photoCellSelected": "Photo {{name}}, selected",
|
|
1353
|
+
"photoCellUnselected": "Photo {{name}}, not selected",
|
|
1354
|
+
"selectionCount": "{{count}} photo selected",
|
|
1355
|
+
"selectionCount_plural": "{{count}} photos selected",
|
|
1356
|
+
"cancelPicker": "Cancel photo selection",
|
|
1357
|
+
"confirmSelection": "Confirm selection"
|
|
1358
|
+
},
|
|
1314
1359
|
"emptyPhotos": {
|
|
1315
1360
|
"title": "No Photos Yet",
|
|
1316
1361
|
"ownDescription": "Upload photos to get started. You can select multiple photos at once.",
|
|
@@ -241,6 +241,21 @@
|
|
|
241
241
|
"confirms": {
|
|
242
242
|
"removeAvatar": "¿Eliminar tu foto de perfil?"
|
|
243
243
|
},
|
|
244
|
+
"crop": {
|
|
245
|
+
"title": "Recortar avatar",
|
|
246
|
+
"subtitle": "Pellizca para acercar, arrastra para colocar",
|
|
247
|
+
"noImage": "No hay imagen para recortar",
|
|
248
|
+
"reset": "Restablecer",
|
|
249
|
+
"resetToCenter": "Restablecer al centro",
|
|
250
|
+
"confirm": "Usar foto",
|
|
251
|
+
"cancel": "Cancelar",
|
|
252
|
+
"saving": "Guardando…",
|
|
253
|
+
"helper": "El círculo recortado es lo que aparecerá en tu perfil. Pellizca para acercar, arrastra para colocar.",
|
|
254
|
+
"zoom": "{{value}}×",
|
|
255
|
+
"a11yImage": "Vista previa del recorte. Pellizca para acercar y arrastra para reposicionar la imagen.",
|
|
256
|
+
"a11yReset": "Restablecer el recorte a la posición predeterminada",
|
|
257
|
+
"a11yResetAnnouncement": "Recorte restablecido"
|
|
258
|
+
},
|
|
244
259
|
"toasts": {
|
|
245
260
|
"profileUpdated": "Perfil actualizado correctamente",
|
|
246
261
|
"updateFailed": "Error al actualizar el perfil",
|
|
@@ -250,7 +265,10 @@
|
|
|
250
265
|
"avatarSelected": "Avatar seleccionado",
|
|
251
266
|
"avatarUpdated": "Avatar actualizado",
|
|
252
267
|
"updateAvatarFailed": "Error al actualizar el avatar",
|
|
253
|
-
"noActiveSession": "No hay sesión activa"
|
|
268
|
+
"noActiveSession": "No hay sesión activa",
|
|
269
|
+
"cropMeasureFailed": "No se pudo medir la imagen",
|
|
270
|
+
"cropNotReady": "La imagen aún no está lista",
|
|
271
|
+
"cropFailed": "No se pudo recortar la imagen"
|
|
254
272
|
}
|
|
255
273
|
},
|
|
256
274
|
"common": {
|
|
@@ -270,6 +288,8 @@
|
|
|
270
288
|
"save": "Guardar",
|
|
271
289
|
"saved": "Guardado correctamente",
|
|
272
290
|
"saving": "Guardando...",
|
|
291
|
+
"unnamed": "Sin nombre",
|
|
292
|
+
"accountFallback": "Cuenta {{handle}}",
|
|
273
293
|
"links": {
|
|
274
294
|
"recoverAccount": "Recuperar tu cuenta",
|
|
275
295
|
"signUp": "Registrarse"
|
|
@@ -1296,9 +1316,34 @@
|
|
|
1296
1316
|
"loadingMore": "Cargando más...",
|
|
1297
1317
|
"loadingPhotoLayout": "Cargando diseño de fotos...",
|
|
1298
1318
|
"uploading": "Subiendo",
|
|
1319
|
+
"upload": "Subir",
|
|
1320
|
+
"uploadPhoto": "Subir foto",
|
|
1299
1321
|
"uploadPhotos": "Subir fotos",
|
|
1300
1322
|
"uploadFiles": "Subir archivos",
|
|
1301
1323
|
"clearSearch": "Limpiar búsqueda",
|
|
1324
|
+
"choosePhoto": "Elegir foto",
|
|
1325
|
+
"done": "Listo",
|
|
1326
|
+
"doneWithCount": "Listo ({{count}})",
|
|
1327
|
+
"photoPicker": {
|
|
1328
|
+
"emptyTitle": "Aún no hay fotos",
|
|
1329
|
+
"emptySubtitle": "Sube desde tu dispositivo para empezar"
|
|
1330
|
+
},
|
|
1331
|
+
"a11y": {
|
|
1332
|
+
"viewAll": "Mostrar todos los archivos",
|
|
1333
|
+
"viewPhotos": "Mostrar solo fotos",
|
|
1334
|
+
"viewVideos": "Mostrar solo vídeos",
|
|
1335
|
+
"viewDocuments": "Mostrar solo documentos",
|
|
1336
|
+
"viewAudio": "Mostrar solo audio",
|
|
1337
|
+
"sortBy": "Ordenar por {{field}}, {{order}}",
|
|
1338
|
+
"uploadFile": "Subir archivo desde el dispositivo",
|
|
1339
|
+
"uploadFromDevice": "Subir foto desde el dispositivo",
|
|
1340
|
+
"photoCellSelected": "Foto {{name}}, seleccionada",
|
|
1341
|
+
"photoCellUnselected": "Foto {{name}}, no seleccionada",
|
|
1342
|
+
"selectionCount": "{{count}} foto seleccionada",
|
|
1343
|
+
"selectionCount_plural": "{{count}} fotos seleccionadas",
|
|
1344
|
+
"cancelPicker": "Cancelar selección de foto",
|
|
1345
|
+
"confirmSelection": "Confirmar selección"
|
|
1346
|
+
},
|
|
1302
1347
|
"emptyPhotos": {
|
|
1303
1348
|
"title": "Aún no hay fotos",
|
|
1304
1349
|
"ownDescription": "Sube fotos para empezar. Puedes seleccionar varias fotos a la vez.",
|