@oxy.so/core 7.13.1 → 7.14.0

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 (36) hide show
  1. package/dist/cjs/crypto/aead.js +3 -3
  2. package/dist/cjs/crypto/contentHash.js +4 -4
  3. package/dist/cjs/crypto/identityDerivations.js +5 -5
  4. package/dist/cjs/crypto/identityLink.js +3 -3
  5. package/dist/cjs/crypto/identityProof.js +3 -3
  6. package/dist/cjs/crypto/index.js +2 -2
  7. package/dist/cjs/crypto/kdf.js +3 -3
  8. package/dist/cjs/crypto/polyfill.js +13 -13
  9. package/dist/cjs/crypto/recoveryPhrase.js +12 -12
  10. package/dist/cjs/crypto/registrationPow.js +4 -4
  11. package/dist/cjs/index.js +2 -2
  12. package/dist/esm/crypto/aead.js +1 -1
  13. package/dist/esm/crypto/contentHash.js +2 -2
  14. package/dist/esm/crypto/identityDerivations.js +2 -2
  15. package/dist/esm/crypto/identityLink.js +2 -2
  16. package/dist/esm/crypto/identityProof.js +2 -2
  17. package/dist/esm/crypto/index.js +2 -2
  18. package/dist/esm/crypto/kdf.js +2 -2
  19. package/dist/esm/crypto/polyfill.js +13 -13
  20. package/dist/esm/crypto/recoveryPhrase.js +1 -1
  21. package/dist/esm/crypto/registrationPow.js +3 -3
  22. package/dist/esm/index.js +2 -2
  23. package/dist/types/crypto/polyfill.d.ts +13 -13
  24. package/dist/types/crypto/registrationPow.d.ts +1 -1
  25. package/package.json +8 -8
  26. package/src/crypto/aead.ts +1 -1
  27. package/src/crypto/contentHash.ts +2 -2
  28. package/src/crypto/identityDerivations.ts +2 -2
  29. package/src/crypto/identityLink.ts +2 -2
  30. package/src/crypto/identityProof.ts +2 -2
  31. package/src/crypto/index.ts +2 -2
  32. package/src/crypto/kdf.ts +2 -2
  33. package/src/crypto/polyfill.ts +13 -13
  34. package/src/crypto/recoveryPhrase.ts +1 -1
  35. package/src/crypto/registrationPow.ts +3 -3
  36. package/src/index.ts +2 -2
@@ -27,7 +27,7 @@ exports.decryptAead = decryptAead;
27
27
  // Loading the polyfill guarantees `globalThis.crypto.getRandomValues` exists on
28
28
  // every platform (native crypto on web/Node; expo-crypto-backed shim on RN).
29
29
  require("./polyfill");
30
- const chacha_1 = require("@noble/ciphers/chacha");
30
+ const chacha_js_1 = require("@noble/ciphers/chacha.js");
31
31
  /** Key length for XChaCha20-Poly1305, in bytes (256-bit). */
32
32
  exports.AEAD_KEY_LENGTH = 32;
33
33
  /** Nonce length for XChaCha20-Poly1305, in bytes (192-bit). */
@@ -55,7 +55,7 @@ function randomNonce() {
55
55
  function encryptAead(key, plaintext, aad) {
56
56
  assertKey(key);
57
57
  const nonce = randomNonce();
58
- const ciphertext = (0, chacha_1.xchacha20poly1305)(key, nonce, aad).encrypt(plaintext);
58
+ const ciphertext = (0, chacha_js_1.xchacha20poly1305)(key, nonce, aad).encrypt(plaintext);
59
59
  return { nonce, ciphertext };
60
60
  }
61
61
  /**
@@ -75,5 +75,5 @@ function decryptAead(key, nonce, ciphertext, aad) {
75
75
  if (nonce.length !== exports.AEAD_NONCE_LENGTH) {
76
76
  throw new Error(`AEAD nonce must be ${exports.AEAD_NONCE_LENGTH} bytes, got ${nonce.length}`);
77
77
  }
78
- return (0, chacha_1.xchacha20poly1305)(key, nonce, aad).decrypt(ciphertext);
78
+ return (0, chacha_js_1.xchacha20poly1305)(key, nonce, aad).decrypt(ciphertext);
79
79
  }
@@ -9,14 +9,14 @@ exports.createContentHasher = createContentHasher;
9
9
  * directly: `@noble/hashes` must load after this entry's polyfill, and through
10
10
  * the ONE lazy root, so it never lands in an app's startup JavaScript.
11
11
  */
12
- const sha256_1 = require("@noble/hashes/sha256");
13
- const utils_1 = require("@noble/hashes/utils");
12
+ const sha2_js_1 = require("@noble/hashes/sha2.js");
13
+ const utils_js_1 = require("@noble/hashes/utils.js");
14
14
  function createContentHasher() {
15
- const hash = sha256_1.sha256.create();
15
+ const hash = sha2_js_1.sha256.create();
16
16
  return {
17
17
  update: (chunk) => {
18
18
  hash.update(chunk);
19
19
  },
20
- digestHex: () => (0, utils_1.bytesToHex)(hash.digest()),
20
+ digestHex: () => (0, utils_js_1.bytesToHex)(hash.digest()),
21
21
  };
22
22
  }
@@ -17,8 +17,8 @@ exports.deriveScopedSeedFromKey = deriveScopedSeedFromKey;
17
17
  exports.deriveSocialReceiveKey = deriveSocialReceiveKey;
18
18
  exports.signSocialReceiveDigest = signSocialReceiveDigest;
19
19
  exports.authChallengeDigest = authChallengeDigest;
20
- const hmac_1 = require("@noble/hashes/hmac");
21
- const sha2_1 = require("@noble/hashes/sha2");
20
+ const hmac_js_1 = require("@noble/hashes/hmac.js");
21
+ const sha2_js_1 = require("@noble/hashes/sha2.js");
22
22
  const secp256k1_1 = require("@oxy.so/protocol/secp256k1");
23
23
  const kdf_1 = require("./kdf");
24
24
  /**
@@ -73,11 +73,11 @@ function deriveSocialReceiveKey(privateKeyHex, index) {
73
73
  }
74
74
  const privateKey = (0, secp256k1_1.normalizeSecp256k1PrivateKey)(privateKeyHex);
75
75
  const parentPublic = hexToBytes((0, secp256k1_1.deriveSecp256k1PublicKey)(privateKey, true));
76
- const chainCode = (0, hmac_1.hmac)(sha2_1.sha256, utf8(exports.SOCIAL_RECEIVE_CHAIN_CODE_KEY), parentPublic);
76
+ const chainCode = (0, hmac_js_1.hmac)(sha2_js_1.sha256, utf8(exports.SOCIAL_RECEIVE_CHAIN_CODE_KEY), parentPublic);
77
77
  const data = new Uint8Array(37);
78
78
  data.set(parentPublic, 0);
79
79
  new DataView(data.buffer).setUint32(33, index, false);
80
- const i = (0, hmac_1.hmac)(sha2_1.sha512, chainCode, data);
80
+ const i = (0, hmac_js_1.hmac)(sha2_js_1.sha512, chainCode, data);
81
81
  const childPrivate = (0, secp256k1_1.tweakAddSecp256k1PrivateKey)(privateKey, bytesToHex(i.slice(0, 32)));
82
82
  return { privateKey: childPrivate, publicKey: (0, secp256k1_1.deriveSecp256k1PublicKey)(childPrivate, true) };
83
83
  }
@@ -101,5 +101,5 @@ function signSocialReceiveDigest(privateKeyHex, index, digestHex) {
101
101
  * format is the one `POST /auth/verify` checks, unchanged.
102
102
  */
103
103
  function authChallengeDigest(publicKey, challenge, timestamp) {
104
- return bytesToHex((0, sha2_1.sha256)(utf8(`auth:${publicKey}:${challenge}:${timestamp}`)));
104
+ return bytesToHex((0, sha2_js_1.sha256)(utf8(`auth:${publicKey}:${challenge}:${timestamp}`)));
105
105
  }
@@ -8,11 +8,11 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.deriveIdentityLinkCode = deriveIdentityLinkCode;
11
- const sha256_1 = require("@noble/hashes/sha256");
12
- const utils_1 = require("@noble/hashes/utils");
11
+ const sha2_js_1 = require("@noble/hashes/sha2.js");
12
+ const utils_js_1 = require("@noble/hashes/utils.js");
13
13
  const DOMAIN = 'oxy-identity-link-code/v1';
14
14
  function deriveIdentityLinkCode(linkId, publicKey) {
15
- const digest = (0, sha256_1.sha256)((0, utils_1.utf8ToBytes)(`${DOMAIN}|${linkId}|${publicKey.trim().toLowerCase()}`));
15
+ const digest = (0, sha2_js_1.sha256)((0, utils_js_1.utf8ToBytes)(`${DOMAIN}|${linkId}|${publicKey.trim().toLowerCase()}`));
16
16
  const value = ((digest[0] << 24) | (digest[1] << 16) | (digest[2] << 8) | digest[3]) >>> 0;
17
17
  return String(value % 1000000).padStart(6, '0');
18
18
  }
@@ -10,13 +10,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.digestIdentityPayload = digestIdentityPayload;
11
11
  exports.signIdentityProof = signIdentityProof;
12
12
  require("./polyfill");
13
- const sha256_1 = require("@noble/hashes/sha256");
14
- const utils_1 = require("@noble/hashes/utils");
13
+ const sha2_js_1 = require("@noble/hashes/sha2.js");
14
+ const utils_js_1 = require("@noble/hashes/utils.js");
15
15
  const sign_1 = require("@oxy.so/protocol/sign");
16
16
  const client_1 = require("@oxy.so/contracts/client");
17
17
  /** SHA-256 hex of the canonical JSON of `payload` — the `payloadDigest` claim. */
18
18
  function digestIdentityPayload(payload) {
19
- return (0, utils_1.bytesToHex)((0, sha256_1.sha256)((0, utils_1.utf8ToBytes)((0, client_1.canonicalJson)(payload))));
19
+ return (0, utils_js_1.bytesToHex)((0, sha2_js_1.sha256)((0, utils_js_1.utf8ToBytes)((0, client_1.canonicalJson)(payload))));
20
20
  }
21
21
  /** Sign `claims` with the root, producing the proof as it travels. */
22
22
  async function signIdentityProof(identity, claims) {
@@ -13,8 +13,8 @@
13
13
  */
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.verifySignature = exports.signMessage = exports.loadCommonsIdentityBridge = exports.canonicalize = exports.createContentHasher = exports.solveRegistrationPow = exports.deriveIdentityLinkCode = exports.signIdentityProof = exports.digestIdentityPayload = exports.authChallengeDigest = exports.signSocialReceiveDigest = exports.deriveSocialReceiveKey = exports.deriveScopedSeedFromKey = exports.MAX_SOCIAL_RECEIVE_INDEX = exports.SOCIAL_RECEIVE_CHAIN_CODE_KEY = exports.SCOPED_SEED_KDF_SALT = exports.deriveSharedSecret = exports.AEAD_NONCE_LENGTH = exports.AEAD_KEY_LENGTH = exports.decryptAead = exports.encryptAead = exports.hkdfSha256 = exports.BACKUP_KDF_ENCRYPTION_INFO = exports.RecoveryPhraseService = exports.SignatureService = exports.updateIdentityMarker = exports.readIdentityMarker = exports.IdentityUnavailableError = exports.IdentityPersistError = exports.IdentityAlreadyExistsError = exports.KeyManager = void 0;
16
- // MUST stay the first import: `@noble/hashes` captures `globalThis.crypto` when
17
- // it is evaluated, so the shim has to exist before anything below reaches it.
16
+ // MUST stay the first import: the shim has to exist before any module below
17
+ // evaluates or draws randomness (see `./polyfill`).
18
18
  require("./polyfill");
19
19
  var keyManager_1 = require("./keyManager");
20
20
  Object.defineProperty(exports, "KeyManager", { enumerable: true, get: function () { return keyManager_1.KeyManager; } });
@@ -12,8 +12,8 @@
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.hkdfSha256 = hkdfSha256;
15
- const hkdf_1 = require("@noble/hashes/hkdf");
16
- const sha256_1 = require("@noble/hashes/sha256");
15
+ const hkdf_js_1 = require("@noble/hashes/hkdf.js");
16
+ const sha2_js_1 = require("@noble/hashes/sha2.js");
17
17
  /**
18
18
  * Derive `length` bytes of keying material from `ikm` using HKDF-SHA256
19
19
  * (RFC 5869 — extract-then-expand).
@@ -35,5 +35,5 @@ function hkdfSha256(ikm, salt, info, length) {
35
35
  if (length > 255 * 32) {
36
36
  throw new Error('hkdfSha256: length must not exceed 8160 bytes (255 * 32)');
37
37
  }
38
- return (0, hkdf_1.hkdf)(sha256_1.sha256, ikm, salt, info, length);
38
+ return (0, hkdf_js_1.hkdf)(sha2_js_1.sha256, ikm, salt, info, length);
39
39
  }
@@ -18,20 +18,20 @@
18
18
  * global WebCrypto, and a `require('node:crypto')` here made web bundlers ship
19
19
  * Node's crypto, stream and buffer polyfills (~600 KB) to every browser.
20
20
  *
21
- * # Evaluation order — this module must import NOTHING that can capture the global
21
+ * # Evaluation order — this module must import NOTHING that reaches a crypto library
22
22
  *
23
- * `@noble/hashes` 1.x reads `globalThis.crypto` exactly once, when its
24
- * `crypto.js` is evaluated, and `randomBytes` uses that captured binding for
25
- * the lifetime of the app. ES imports are evaluated before the importing
26
- * module's body, so every module this file imports runs BEFORE the shim below
27
- * is installed. This file used to import `@oxy.so/protocol`'s root entry,
28
- * which reaches `@noble/curves` → `@noble/hashes` through the envelope signer:
29
- * on Hermes (no `globalThis.crypto` at startup) noble captured `undefined`
30
- * before the shim existed, and every identity creation on Android failed with
31
- * `crypto.getRandomValues must be defined`. Its imports are therefore limited
32
- * to `buffer` and `@oxy.so/protocol/random`, both free of crypto libraries,
33
- * and `index.ts` imports this file first. `__tests__/polyfillOrder.test.ts`
34
- * guards both halves.
23
+ * ES imports are evaluated before the importing module's body, so every module
24
+ * this file imports runs BEFORE the shim below is installed. This file used to
25
+ * import `@oxy.so/protocol`'s root entry, which reaches `@noble/curves` →
26
+ * `@noble/hashes` through the envelope signer, and `@noble/hashes` 1.x read
27
+ * `globalThis.crypto` once, at evaluation: on Hermes (no `globalThis.crypto` at
28
+ * startup) it captured `undefined` before the shim existed, and every identity
29
+ * creation on Android failed with `crypto.getRandomValues must be defined`.
30
+ * Noble 2.x reads the global on every call, so that capture is gone, but the
31
+ * shim still has to exist before any module body that reads it runs. Its
32
+ * imports are therefore limited to `buffer` and `@oxy.so/protocol/random`,
33
+ * both free of crypto libraries, and `index.ts` imports this file first.
34
+ * `__tests__/polyfillOrder.test.ts` guards both halves.
35
35
  *
36
36
  */
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -16,7 +16,7 @@
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.RecoveryPhraseService = exports.BACKUP_MATERIAL_LENGTH = exports.BACKUP_KDF_LOOKUP_INFO = exports.BACKUP_KDF_ENCRYPTION_INFO = exports.BACKUP_KDF_SALT = void 0;
18
18
  const bip39_1 = require("@scure/bip39");
19
- const english_1 = require("@scure/bip39/wordlists/english");
19
+ const english_js_1 = require("@scure/bip39/wordlists/english.js");
20
20
  const keyManager_1 = require("./keyManager");
21
21
  const kdf_1 = require("./kdf");
22
22
  /**
@@ -59,7 +59,7 @@ class RecoveryPhraseService {
59
59
  */
60
60
  static async generateIdentityWithRecovery(options) {
61
61
  // Generate 128-bit entropy for 12-word mnemonic
62
- const mnemonic = (0, bip39_1.generateMnemonic)(english_1.wordlist, 128);
62
+ const mnemonic = (0, bip39_1.generateMnemonic)(english_js_1.wordlist, 128);
63
63
  // Derive private key from mnemonic
64
64
  // Using the seed directly as the private key (simplified approach)
65
65
  const seed = await (0, bip39_1.mnemonicToSeed)(mnemonic);
@@ -83,7 +83,7 @@ class RecoveryPhraseService {
83
83
  */
84
84
  static async generateIdentityWithRecovery24(options) {
85
85
  // Generate 256-bit entropy for 24-word mnemonic
86
- const mnemonic = (0, bip39_1.generateMnemonic)(english_1.wordlist, 256);
86
+ const mnemonic = (0, bip39_1.generateMnemonic)(english_js_1.wordlist, 256);
87
87
  const seed = await (0, bip39_1.mnemonicToSeed)(mnemonic);
88
88
  const privateKeyHex = toHex(seed.subarray(0, 32));
89
89
  const publicKey = await keyManager_1.KeyManager.importKeyPair(privateKeyHex, {
@@ -109,7 +109,7 @@ class RecoveryPhraseService {
109
109
  * committed anywhere — if it is lost the account becomes unrecoverable.
110
110
  */
111
111
  static async derivePendingIdentity() {
112
- const mnemonic = (0, bip39_1.generateMnemonic)(english_1.wordlist, 128);
112
+ const mnemonic = (0, bip39_1.generateMnemonic)(english_js_1.wordlist, 128);
113
113
  const seed = await (0, bip39_1.mnemonicToSeed)(mnemonic);
114
114
  const privateKey = toHex(seed.subarray(0, 32));
115
115
  const publicKey = keyManager_1.KeyManager.derivePublicKey(privateKey);
@@ -130,7 +130,7 @@ class RecoveryPhraseService {
130
130
  */
131
131
  static async derivePrivateKeyFromPhrase(phrase) {
132
132
  const normalizedPhrase = phrase.trim().toLowerCase();
133
- if (!(0, bip39_1.validateMnemonic)(normalizedPhrase, english_1.wordlist)) {
133
+ if (!(0, bip39_1.validateMnemonic)(normalizedPhrase, english_js_1.wordlist)) {
134
134
  throw new Error('Invalid recovery phrase');
135
135
  }
136
136
  const seed = await (0, bip39_1.mnemonicToSeed)(normalizedPhrase);
@@ -155,7 +155,7 @@ class RecoveryPhraseService {
155
155
  */
156
156
  static async deriveBackupMaterial(phrase) {
157
157
  const normalizedPhrase = phrase.trim().toLowerCase();
158
- if (!(0, bip39_1.validateMnemonic)(normalizedPhrase, english_1.wordlist)) {
158
+ if (!(0, bip39_1.validateMnemonic)(normalizedPhrase, english_js_1.wordlist)) {
159
159
  throw new Error('Invalid recovery phrase. Please check the words and try again.');
160
160
  }
161
161
  const seed = await (0, bip39_1.mnemonicToSeed)(normalizedPhrase);
@@ -175,7 +175,7 @@ class RecoveryPhraseService {
175
175
  static async restoreFromPhrase(phrase, options) {
176
176
  // Normalize and validate the phrase
177
177
  const normalizedPhrase = phrase.trim().toLowerCase();
178
- if (!(0, bip39_1.validateMnemonic)(normalizedPhrase, english_1.wordlist)) {
178
+ if (!(0, bip39_1.validateMnemonic)(normalizedPhrase, english_js_1.wordlist)) {
179
179
  throw new Error('Invalid recovery phrase. Please check the words and try again.');
180
180
  }
181
181
  // Derive the same private key from the mnemonic
@@ -192,26 +192,26 @@ class RecoveryPhraseService {
192
192
  */
193
193
  static validatePhrase(phrase) {
194
194
  const normalizedPhrase = phrase.trim().toLowerCase();
195
- return (0, bip39_1.validateMnemonic)(normalizedPhrase, english_1.wordlist);
195
+ return (0, bip39_1.validateMnemonic)(normalizedPhrase, english_js_1.wordlist);
196
196
  }
197
197
  /**
198
198
  * Get the word list for autocomplete/validation
199
199
  */
200
200
  static getWordList() {
201
- return english_1.wordlist;
201
+ return english_js_1.wordlist;
202
202
  }
203
203
  /**
204
204
  * Check if a word is valid in the BIP39 word list
205
205
  */
206
206
  static isValidWord(word) {
207
- return english_1.wordlist.includes(word.toLowerCase());
207
+ return english_js_1.wordlist.includes(word.toLowerCase());
208
208
  }
209
209
  /**
210
210
  * Get suggestions for a partial word
211
211
  */
212
212
  static getSuggestions(partial, limit = 5) {
213
213
  const lowerPartial = partial.toLowerCase();
214
- return english_1.wordlist.filter((word) => word.startsWith(lowerPartial)).slice(0, limit);
214
+ return english_js_1.wordlist.filter((word) => word.startsWith(lowerPartial)).slice(0, limit);
215
215
  }
216
216
  /**
217
217
  * Derive the public key from a phrase without storing
@@ -219,7 +219,7 @@ class RecoveryPhraseService {
219
219
  */
220
220
  static async derivePublicKeyFromPhrase(phrase) {
221
221
  const normalizedPhrase = phrase.trim().toLowerCase();
222
- if (!(0, bip39_1.validateMnemonic)(normalizedPhrase, english_1.wordlist)) {
222
+ if (!(0, bip39_1.validateMnemonic)(normalizedPhrase, english_js_1.wordlist)) {
223
223
  throw new Error('Invalid recovery phrase');
224
224
  }
225
225
  const seed = await (0, bip39_1.mnemonicToSeed)(normalizedPhrase);
@@ -10,8 +10,8 @@
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.solveRegistrationPow = solveRegistrationPow;
12
12
  require("./polyfill");
13
- const sha256_1 = require("@noble/hashes/sha256");
14
- const utils_1 = require("@noble/hashes/utils");
13
+ const sha2_js_1 = require("@noble/hashes/sha2.js");
14
+ const utils_js_1 = require("@noble/hashes/utils.js");
15
15
  const protocol_1 = require("@oxy.so/protocol");
16
16
  /**
17
17
  * How many attempts the grind loop runs before yielding to the event loop.
@@ -31,7 +31,7 @@ function yieldToEventLoop() {
31
31
  * Grind a nonce for `publicKey`/`timestamp` that clears
32
32
  * {@link REGISTRATION_POW_DIFFICULTY_BITS}, returned as a decimal counter.
33
33
  *
34
- * Hashes with `@noble/hashes/sha256` — pure JS, synchronous, identical output
34
+ * Hashes with `@noble/hashes/sha2.js`'s `sha256` — pure JS, synchronous, identical output
35
35
  * on web/Node/React Native with no native module and no per-attempt bridge
36
36
  * crossing (see the difficulty constant's own doc for why that distinction is
37
37
  * the whole point). Nonces are sequential (0, 1, 2, …) rather than random: the
@@ -42,7 +42,7 @@ function yieldToEventLoop() {
42
42
  async function solveRegistrationPow(publicKey, timestamp) {
43
43
  for (let nonce = 0;; nonce += 1) {
44
44
  const candidate = String(nonce);
45
- const digest = (0, utils_1.bytesToHex)((0, sha256_1.sha256)((0, utils_1.utf8ToBytes)((0, protocol_1.registrationPowMessage)(publicKey, timestamp, candidate))));
45
+ const digest = (0, utils_js_1.bytesToHex)((0, sha2_js_1.sha256)((0, utils_js_1.utf8ToBytes)((0, protocol_1.registrationPowMessage)(publicKey, timestamp, candidate))));
46
46
  if ((0, protocol_1.meetsRegistrationPowDifficulty)(digest, protocol_1.REGISTRATION_POW_DIFFICULTY_BITS)) {
47
47
  return candidate;
48
48
  }
package/dist/cjs/index.js CHANGED
@@ -28,8 +28,8 @@ exports.packageInfo = exports.consumeOAuthReturnPath = exports.persistOAuthRetur
28
28
  // The crypto shim MUST stay the first import. The root itself ships no crypto
29
29
  // library (`__tests__/rootEntryGraph.test.ts`), but `@oxy.so/protocol`'s root
30
30
  // entry (reached by the OAuth/PKCE and device helpers) does, and Metro does not
31
- // tree-shake: on Hermes, `@noble/hashes` would capture an absent
32
- // `globalThis.crypto` at evaluation. See the note in `./crypto/polyfill`.
31
+ // tree-shake: on Hermes, the shim has to exist before any of it evaluates. See
32
+ // the note in `./crypto/polyfill`.
33
33
  require("./crypto/polyfill");
34
34
  // ---------------------------------------------------------------------------
35
35
  // API client
@@ -22,7 +22,7 @@
22
22
  // Loading the polyfill guarantees `globalThis.crypto.getRandomValues` exists on
23
23
  // every platform (native crypto on web/Node; expo-crypto-backed shim on RN).
24
24
  import './polyfill.js';
25
- import { xchacha20poly1305 } from '@noble/ciphers/chacha';
25
+ import { xchacha20poly1305 } from '@noble/ciphers/chacha.js';
26
26
  /** Key length for XChaCha20-Poly1305, in bytes (256-bit). */
27
27
  export const AEAD_KEY_LENGTH = 32;
28
28
  /** Nonce length for XChaCha20-Poly1305, in bytes (192-bit). */
@@ -6,8 +6,8 @@
6
6
  * directly: `@noble/hashes` must load after this entry's polyfill, and through
7
7
  * the ONE lazy root, so it never lands in an app's startup JavaScript.
8
8
  */
9
- import { sha256 } from '@noble/hashes/sha256';
10
- import { bytesToHex } from '@noble/hashes/utils';
9
+ import { sha256 } from '@noble/hashes/sha2.js';
10
+ import { bytesToHex } from '@noble/hashes/utils.js';
11
11
  export function createContentHasher() {
12
12
  const hash = sha256.create();
13
13
  return {
@@ -10,8 +10,8 @@
10
10
  *
11
11
  * ESM/CJS safe: static `import` only, no `require()`.
12
12
  */
13
- import { hmac } from '@noble/hashes/hmac';
14
- import { sha256, sha512 } from '@noble/hashes/sha2';
13
+ import { hmac } from '@noble/hashes/hmac.js';
14
+ import { sha256, sha512 } from '@noble/hashes/sha2.js';
15
15
  import { deriveSecp256k1PublicKey, normalizeSecp256k1PrivateKey, signSecp256k1Digest, tweakAddSecp256k1PrivateKey, } from '@oxy.so/protocol/secp256k1';
16
16
  import { hkdfSha256 } from './kdf.js';
17
17
  /**
@@ -5,8 +5,8 @@
5
5
  * Commons from its own; the person checks they match before the link is
6
6
  * confirmed, so a photographed QR cannot slip another key in.
7
7
  */
8
- import { sha256 } from '@noble/hashes/sha256';
9
- import { utf8ToBytes } from '@noble/hashes/utils';
8
+ import { sha256 } from '@noble/hashes/sha2.js';
9
+ import { utf8ToBytes } from '@noble/hashes/utils.js';
10
10
  const DOMAIN = 'oxy-identity-link-code/v1';
11
11
  export function deriveIdentityLinkCode(linkId, publicKey) {
12
12
  const digest = sha256(utf8ToBytes(`${DOMAIN}|${linkId}|${publicKey.trim().toLowerCase()}`));
@@ -6,8 +6,8 @@
6
6
  * its own JSON.
7
7
  */
8
8
  import './polyfill.js';
9
- import { sha256 } from '@noble/hashes/sha256';
10
- import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils';
9
+ import { sha256 } from '@noble/hashes/sha2.js';
10
+ import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils.js';
11
11
  import { signMessage } from '@oxy.so/protocol/sign';
12
12
  import { buildIdentityProofMessage, canonicalJson, IDENTITY_PROOF_VERSION, } from '@oxy.so/contracts/client';
13
13
  /** SHA-256 hex of the canonical JSON of `payload` — the `payloadDigest` claim. */
@@ -10,8 +10,8 @@
10
10
  * internal barrel) made Metro hoist the shared stack into the always-loaded
11
11
  * common chunk, which is the cost the lazy loading exists to avoid.
12
12
  */
13
- // MUST stay the first import: `@noble/hashes` captures `globalThis.crypto` when
14
- // it is evaluated, so the shim has to exist before anything below reaches it.
13
+ // MUST stay the first import: the shim has to exist before any module below
14
+ // evaluates or draws randomness (see `./polyfill`).
15
15
  import './polyfill.js';
16
16
  export { KeyManager, IdentityAlreadyExistsError, IdentityPersistError, IdentityUnavailableError, } from './keyManager.js';
17
17
  export { readIdentityMarker, updateIdentityMarker } from './identityMarker.js';
@@ -9,8 +9,8 @@
9
9
  *
10
10
  * ESM/CJS safe: static `import` only, no `require()`.
11
11
  */
12
- import { hkdf } from '@noble/hashes/hkdf';
13
- import { sha256 } from '@noble/hashes/sha256';
12
+ import { hkdf } from '@noble/hashes/hkdf.js';
13
+ import { sha256 } from '@noble/hashes/sha2.js';
14
14
  /**
15
15
  * Derive `length` bytes of keying material from `ikm` using HKDF-SHA256
16
16
  * (RFC 5869 — extract-then-expand).
@@ -17,20 +17,20 @@
17
17
  * global WebCrypto, and a `require('node:crypto')` here made web bundlers ship
18
18
  * Node's crypto, stream and buffer polyfills (~600 KB) to every browser.
19
19
  *
20
- * # Evaluation order — this module must import NOTHING that can capture the global
20
+ * # Evaluation order — this module must import NOTHING that reaches a crypto library
21
21
  *
22
- * `@noble/hashes` 1.x reads `globalThis.crypto` exactly once, when its
23
- * `crypto.js` is evaluated, and `randomBytes` uses that captured binding for
24
- * the lifetime of the app. ES imports are evaluated before the importing
25
- * module's body, so every module this file imports runs BEFORE the shim below
26
- * is installed. This file used to import `@oxy.so/protocol`'s root entry,
27
- * which reaches `@noble/curves` → `@noble/hashes` through the envelope signer:
28
- * on Hermes (no `globalThis.crypto` at startup) noble captured `undefined`
29
- * before the shim existed, and every identity creation on Android failed with
30
- * `crypto.getRandomValues must be defined`. Its imports are therefore limited
31
- * to `buffer` and `@oxy.so/protocol/random`, both free of crypto libraries,
32
- * and `index.ts` imports this file first. `__tests__/polyfillOrder.test.ts`
33
- * guards both halves.
22
+ * ES imports are evaluated before the importing module's body, so every module
23
+ * this file imports runs BEFORE the shim below is installed. This file used to
24
+ * import `@oxy.so/protocol`'s root entry, which reaches `@noble/curves` →
25
+ * `@noble/hashes` through the envelope signer, and `@noble/hashes` 1.x read
26
+ * `globalThis.crypto` once, at evaluation: on Hermes (no `globalThis.crypto` at
27
+ * startup) it captured `undefined` before the shim existed, and every identity
28
+ * creation on Android failed with `crypto.getRandomValues must be defined`.
29
+ * Noble 2.x reads the global on every call, so that capture is gone, but the
30
+ * shim still has to exist before any module body that reads it runs. Its
31
+ * imports are therefore limited to `buffer` and `@oxy.so/protocol/random`,
32
+ * both free of crypto libraries, and `index.ts` imports this file first.
33
+ * `__tests__/polyfillOrder.test.ts` guards both halves.
34
34
  *
35
35
  */
36
36
  import _cjs_buffer from 'buffer';
@@ -13,7 +13,7 @@
13
13
  * app. Adding another language means one more subpath import, ideally lazy.
14
14
  */
15
15
  import { generateMnemonic, mnemonicToSeed, validateMnemonic } from '@scure/bip39';
16
- import { wordlist } from '@scure/bip39/wordlists/english';
16
+ import { wordlist } from '@scure/bip39/wordlists/english.js';
17
17
  import { KeyManager } from './keyManager.js';
18
18
  import { hkdfSha256 } from './kdf.js';
19
19
  /**
@@ -7,8 +7,8 @@
7
7
  * `auth/registrationPow.ts` — this file is only the solve loop.
8
8
  */
9
9
  import './polyfill.js';
10
- import { sha256 } from '@noble/hashes/sha256';
11
- import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils';
10
+ import { sha256 } from '@noble/hashes/sha2.js';
11
+ import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils.js';
12
12
  import { meetsRegistrationPowDifficulty, registrationPowMessage, REGISTRATION_POW_DIFFICULTY_BITS, } from '@oxy.so/protocol';
13
13
  /**
14
14
  * How many attempts the grind loop runs before yielding to the event loop.
@@ -28,7 +28,7 @@ function yieldToEventLoop() {
28
28
  * Grind a nonce for `publicKey`/`timestamp` that clears
29
29
  * {@link REGISTRATION_POW_DIFFICULTY_BITS}, returned as a decimal counter.
30
30
  *
31
- * Hashes with `@noble/hashes/sha256` — pure JS, synchronous, identical output
31
+ * Hashes with `@noble/hashes/sha2.js`'s `sha256` — pure JS, synchronous, identical output
32
32
  * on web/Node/React Native with no native module and no per-attempt bridge
33
33
  * crossing (see the difficulty constant's own doc for why that distinction is
34
34
  * the whole point). Nonces are sequential (0, 1, 2, …) rather than random: the
package/dist/esm/index.js CHANGED
@@ -22,8 +22,8 @@
22
22
  // The crypto shim MUST stay the first import. The root itself ships no crypto
23
23
  // library (`__tests__/rootEntryGraph.test.ts`), but `@oxy.so/protocol`'s root
24
24
  // entry (reached by the OAuth/PKCE and device helpers) does, and Metro does not
25
- // tree-shake: on Hermes, `@noble/hashes` would capture an absent
26
- // `globalThis.crypto` at evaluation. See the note in `./crypto/polyfill`.
25
+ // tree-shake: on Hermes, the shim has to exist before any of it evaluates. See
26
+ // the note in `./crypto/polyfill`.
27
27
  import './crypto/polyfill.js';
28
28
  // ---------------------------------------------------------------------------
29
29
  // API client
@@ -17,20 +17,20 @@
17
17
  * global WebCrypto, and a `require('node:crypto')` here made web bundlers ship
18
18
  * Node's crypto, stream and buffer polyfills (~600 KB) to every browser.
19
19
  *
20
- * # Evaluation order — this module must import NOTHING that can capture the global
20
+ * # Evaluation order — this module must import NOTHING that reaches a crypto library
21
21
  *
22
- * `@noble/hashes` 1.x reads `globalThis.crypto` exactly once, when its
23
- * `crypto.js` is evaluated, and `randomBytes` uses that captured binding for
24
- * the lifetime of the app. ES imports are evaluated before the importing
25
- * module's body, so every module this file imports runs BEFORE the shim below
26
- * is installed. This file used to import `@oxy.so/protocol`'s root entry,
27
- * which reaches `@noble/curves` → `@noble/hashes` through the envelope signer:
28
- * on Hermes (no `globalThis.crypto` at startup) noble captured `undefined`
29
- * before the shim existed, and every identity creation on Android failed with
30
- * `crypto.getRandomValues must be defined`. Its imports are therefore limited
31
- * to `buffer` and `@oxy.so/protocol/random`, both free of crypto libraries,
32
- * and `index.ts` imports this file first. `__tests__/polyfillOrder.test.ts`
33
- * guards both halves.
22
+ * ES imports are evaluated before the importing module's body, so every module
23
+ * this file imports runs BEFORE the shim below is installed. This file used to
24
+ * import `@oxy.so/protocol`'s root entry, which reaches `@noble/curves` →
25
+ * `@noble/hashes` through the envelope signer, and `@noble/hashes` 1.x read
26
+ * `globalThis.crypto` once, at evaluation: on Hermes (no `globalThis.crypto` at
27
+ * startup) it captured `undefined` before the shim existed, and every identity
28
+ * creation on Android failed with `crypto.getRandomValues must be defined`.
29
+ * Noble 2.x reads the global on every call, so that capture is gone, but the
30
+ * shim still has to exist before any module body that reads it runs. Its
31
+ * imports are therefore limited to `buffer` and `@oxy.so/protocol/random`,
32
+ * both free of crypto libraries, and `index.ts` imports this file first.
33
+ * `__tests__/polyfillOrder.test.ts` guards both halves.
34
34
  *
35
35
  */
36
36
  import { Buffer } from 'buffer';
@@ -11,7 +11,7 @@ import './polyfill';
11
11
  * Grind a nonce for `publicKey`/`timestamp` that clears
12
12
  * {@link REGISTRATION_POW_DIFFICULTY_BITS}, returned as a decimal counter.
13
13
  *
14
- * Hashes with `@noble/hashes/sha256` — pure JS, synchronous, identical output
14
+ * Hashes with `@noble/hashes/sha2.js`'s `sha256` — pure JS, synchronous, identical output
15
15
  * on web/Node/React Native with no native module and no per-attempt bridge
16
16
  * crossing (see the difficulty constant's own doc for why that distinction is
17
17
  * the whole point). Nonces are sequential (0, 1, 2, …) rather than random: the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxy.so/core",
3
- "version": "7.13.1",
3
+ "version": "7.14.0",
4
4
  "description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -160,7 +160,7 @@
160
160
  "license": "SEE LICENSE IN LICENSE",
161
161
  "homepage": "https://oxy.so",
162
162
  "engines": {
163
- "node": ">=20.0.0"
163
+ "node": "^20.19.0 || >=22.12.0"
164
164
  },
165
165
  "scripts": {
166
166
  "build": "bun run check:compact-locales && node scripts/build-workspace-deps.mjs @oxy.so/telemetry && bun run build:cjs && bun run build:esm && bun run build:types && node scripts/mark-module-formats.mjs && node -e \"const p='../app-preset/metro/source-stamp.js';require('node:fs').existsSync(p)&&require(p).writeStamp(process.cwd())\"",
@@ -179,12 +179,12 @@
179
179
  "test:bun": "cd bun-tests && bun test"
180
180
  },
181
181
  "dependencies": {
182
- "@noble/ciphers": "^1.3.0",
183
- "@noble/hashes": "^1.8.0",
182
+ "@noble/ciphers": "^2.4.0",
183
+ "@noble/hashes": "^2.4.0",
184
184
  "@oxy.so/contracts": "6.5.0",
185
- "@oxy.so/protocol": "^1.5.1",
185
+ "@oxy.so/protocol": "^1.6.0",
186
186
  "@oxy.so/telemetry": "^2.1.0",
187
- "@scure/bip39": "^1.6.0",
187
+ "@scure/bip39": "^2.4.0",
188
188
  "buffer": "^6.0.3",
189
189
  "express-rate-limit": "^8.6.0",
190
190
  "helmet": "^8.0.0",
@@ -195,7 +195,7 @@
195
195
  "zod": "^4.6.7"
196
196
  },
197
197
  "peerDependencies": {
198
- "@react-native-async-storage/async-storage": "<3.0.0",
198
+ "@react-native-async-storage/async-storage": "<4.0.0",
199
199
  "expo-crypto": "<59.0.0",
200
200
  "expo-secure-store": "<59.0.0",
201
201
  "express": "^4.0.0",
@@ -223,7 +223,7 @@
223
223
  }
224
224
  },
225
225
  "devDependencies": {
226
- "@react-native-async-storage/async-storage": "^2.2.0",
226
+ "@react-native-async-storage/async-storage": "^3.1.1",
227
227
  "@types/express": "^4.17.25",
228
228
  "@types/invariant": "^2.2.34",
229
229
  "@types/node": "^20.19.43",
@@ -23,7 +23,7 @@
23
23
  // Loading the polyfill guarantees `globalThis.crypto.getRandomValues` exists on
24
24
  // every platform (native crypto on web/Node; expo-crypto-backed shim on RN).
25
25
  import './polyfill';
26
- import { xchacha20poly1305 } from '@noble/ciphers/chacha';
26
+ import { xchacha20poly1305 } from '@noble/ciphers/chacha.js';
27
27
 
28
28
  /** Key length for XChaCha20-Poly1305, in bytes (256-bit). */
29
29
  export const AEAD_KEY_LENGTH = 32;
@@ -6,8 +6,8 @@
6
6
  * directly: `@noble/hashes` must load after this entry's polyfill, and through
7
7
  * the ONE lazy root, so it never lands in an app's startup JavaScript.
8
8
  */
9
- import { sha256 } from '@noble/hashes/sha256';
10
- import { bytesToHex } from '@noble/hashes/utils';
9
+ import { sha256 } from '@noble/hashes/sha2.js';
10
+ import { bytesToHex } from '@noble/hashes/utils.js';
11
11
 
12
12
  export interface ContentHasher {
13
13
  update(chunk: Uint8Array): void;
@@ -11,8 +11,8 @@
11
11
  * ESM/CJS safe: static `import` only, no `require()`.
12
12
  */
13
13
 
14
- import { hmac } from '@noble/hashes/hmac';
15
- import { sha256, sha512 } from '@noble/hashes/sha2';
14
+ import { hmac } from '@noble/hashes/hmac.js';
15
+ import { sha256, sha512 } from '@noble/hashes/sha2.js';
16
16
  import {
17
17
  deriveSecp256k1PublicKey,
18
18
  normalizeSecp256k1PrivateKey,
@@ -6,8 +6,8 @@
6
6
  * confirmed, so a photographed QR cannot slip another key in.
7
7
  */
8
8
 
9
- import { sha256 } from '@noble/hashes/sha256';
10
- import { utf8ToBytes } from '@noble/hashes/utils';
9
+ import { sha256 } from '@noble/hashes/sha2.js';
10
+ import { utf8ToBytes } from '@noble/hashes/utils.js';
11
11
 
12
12
  const DOMAIN = 'oxy-identity-link-code/v1';
13
13
 
@@ -7,8 +7,8 @@
7
7
  */
8
8
 
9
9
  import './polyfill';
10
- import { sha256 } from '@noble/hashes/sha256';
11
- import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils';
10
+ import { sha256 } from '@noble/hashes/sha2.js';
11
+ import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils.js';
12
12
  import { signMessage } from '@oxy.so/protocol/sign';
13
13
  import type { IdentityProof, IdentityProofClaims } from '@oxy.so/contracts';
14
14
  import {
@@ -11,8 +11,8 @@
11
11
  * common chunk, which is the cost the lazy loading exists to avoid.
12
12
  */
13
13
 
14
- // MUST stay the first import: `@noble/hashes` captures `globalThis.crypto` when
15
- // it is evaluated, so the shim has to exist before anything below reaches it.
14
+ // MUST stay the first import: the shim has to exist before any module below
15
+ // evaluates or draws randomness (see `./polyfill`).
16
16
  import './polyfill';
17
17
 
18
18
  export {
package/src/crypto/kdf.ts CHANGED
@@ -10,8 +10,8 @@
10
10
  * ESM/CJS safe: static `import` only, no `require()`.
11
11
  */
12
12
 
13
- import { hkdf } from '@noble/hashes/hkdf';
14
- import { sha256 } from '@noble/hashes/sha256';
13
+ import { hkdf } from '@noble/hashes/hkdf.js';
14
+ import { sha256 } from '@noble/hashes/sha2.js';
15
15
 
16
16
  /**
17
17
  * Derive `length` bytes of keying material from `ikm` using HKDF-SHA256
@@ -17,20 +17,20 @@
17
17
  * global WebCrypto, and a `require('node:crypto')` here made web bundlers ship
18
18
  * Node's crypto, stream and buffer polyfills (~600 KB) to every browser.
19
19
  *
20
- * # Evaluation order — this module must import NOTHING that can capture the global
20
+ * # Evaluation order — this module must import NOTHING that reaches a crypto library
21
21
  *
22
- * `@noble/hashes` 1.x reads `globalThis.crypto` exactly once, when its
23
- * `crypto.js` is evaluated, and `randomBytes` uses that captured binding for
24
- * the lifetime of the app. ES imports are evaluated before the importing
25
- * module's body, so every module this file imports runs BEFORE the shim below
26
- * is installed. This file used to import `@oxy.so/protocol`'s root entry,
27
- * which reaches `@noble/curves` → `@noble/hashes` through the envelope signer:
28
- * on Hermes (no `globalThis.crypto` at startup) noble captured `undefined`
29
- * before the shim existed, and every identity creation on Android failed with
30
- * `crypto.getRandomValues must be defined`. Its imports are therefore limited
31
- * to `buffer` and `@oxy.so/protocol/random`, both free of crypto libraries,
32
- * and `index.ts` imports this file first. `__tests__/polyfillOrder.test.ts`
33
- * guards both halves.
22
+ * ES imports are evaluated before the importing module's body, so every module
23
+ * this file imports runs BEFORE the shim below is installed. This file used to
24
+ * import `@oxy.so/protocol`'s root entry, which reaches `@noble/curves` →
25
+ * `@noble/hashes` through the envelope signer, and `@noble/hashes` 1.x read
26
+ * `globalThis.crypto` once, at evaluation: on Hermes (no `globalThis.crypto` at
27
+ * startup) it captured `undefined` before the shim existed, and every identity
28
+ * creation on Android failed with `crypto.getRandomValues must be defined`.
29
+ * Noble 2.x reads the global on every call, so that capture is gone, but the
30
+ * shim still has to exist before any module body that reads it runs. Its
31
+ * imports are therefore limited to `buffer` and `@oxy.so/protocol/random`,
32
+ * both free of crypto libraries, and `index.ts` imports this file first.
33
+ * `__tests__/polyfillOrder.test.ts` guards both halves.
34
34
  *
35
35
  */
36
36
 
@@ -14,7 +14,7 @@
14
14
  */
15
15
 
16
16
  import { generateMnemonic, mnemonicToSeed, validateMnemonic } from '@scure/bip39';
17
- import { wordlist } from '@scure/bip39/wordlists/english';
17
+ import { wordlist } from '@scure/bip39/wordlists/english.js';
18
18
  import { KeyManager } from './keyManager';
19
19
  import { hkdfSha256 } from './kdf';
20
20
 
@@ -8,8 +8,8 @@
8
8
  */
9
9
 
10
10
  import './polyfill';
11
- import { sha256 } from '@noble/hashes/sha256';
12
- import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils';
11
+ import { sha256 } from '@noble/hashes/sha2.js';
12
+ import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils.js';
13
13
  import {
14
14
  meetsRegistrationPowDifficulty,
15
15
  registrationPowMessage,
@@ -36,7 +36,7 @@ function yieldToEventLoop(): Promise<void> {
36
36
  * Grind a nonce for `publicKey`/`timestamp` that clears
37
37
  * {@link REGISTRATION_POW_DIFFICULTY_BITS}, returned as a decimal counter.
38
38
  *
39
- * Hashes with `@noble/hashes/sha256` — pure JS, synchronous, identical output
39
+ * Hashes with `@noble/hashes/sha2.js`'s `sha256` — pure JS, synchronous, identical output
40
40
  * on web/Node/React Native with no native module and no per-attempt bridge
41
41
  * crossing (see the difficulty constant's own doc for why that distinction is
42
42
  * the whole point). Nonces are sequential (0, 1, 2, …) rather than random: the
package/src/index.ts CHANGED
@@ -23,8 +23,8 @@
23
23
  // The crypto shim MUST stay the first import. The root itself ships no crypto
24
24
  // library (`__tests__/rootEntryGraph.test.ts`), but `@oxy.so/protocol`'s root
25
25
  // entry (reached by the OAuth/PKCE and device helpers) does, and Metro does not
26
- // tree-shake: on Hermes, `@noble/hashes` would capture an absent
27
- // `globalThis.crypto` at evaluation. See the note in `./crypto/polyfill`.
26
+ // tree-shake: on Hermes, the shim has to exist before any of it evaluates. See
27
+ // the note in `./crypto/polyfill`.
28
28
  import './crypto/polyfill';
29
29
 
30
30
  // ---------------------------------------------------------------------------