@oxy.so/protocol 1.1.3 → 1.2.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.
package/dist/cjs/index.js CHANGED
@@ -11,7 +11,7 @@
11
11
  * subpath so they never enter React Native / web bundles.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.loadSharedIdentityBridge = exports.getRandomBytesRN = exports.loadAsyncStorage = exports.loadSecureStore = exports.loadExpoCrypto = exports.loadNodeCrypto = exports.isNodeJS = exports.isReactNative = exports.isAuthorizedKey = exports.REGISTRATION_POW_DIFFICULTY_BITS = exports.meetsRegistrationPowDifficulty = exports.countLeadingZeroBits = exports.registrationPowMessage = exports.verifyCheckpointSignature = exports.signCheckpoint = exports.checkpointHash = exports.checkpointSigningInput = exports.verifyInclusionProof = exports.inclusionProof = exports.buildTransparencyTreeFromHeads = exports.buildTransparencyTree = exports.transparencyLeafHash = exports.EMPTY_TRANSPARENCY_ROOT = exports.verifyAndAppend = exports.DEFAULT_CLOCK_SKEW_MS = exports.verifyEnvelope = exports.checkContinuity = exports.UNCHAINED_SEQ = exports.verifyEnvelopeSignature = exports.signEnvelope = exports.verifySignature = exports.signMessage = exports.computeRecordId = exports.sha256 = exports.signedRecordSigningInput = exports.canonicalize = void 0;
14
+ exports.loadCommonsIdentityBridge = exports.getRandomBytesRN = exports.loadAsyncStorage = exports.loadSecureStore = exports.loadExpoCrypto = exports.loadNodeCrypto = exports.isNodeJS = exports.isReactNative = exports.isAuthorizedKey = exports.REGISTRATION_POW_DIFFICULTY_BITS = exports.meetsRegistrationPowDifficulty = exports.countLeadingZeroBits = exports.registrationPowMessage = exports.verifyCheckpointSignature = exports.signCheckpoint = exports.checkpointHash = exports.checkpointSigningInput = exports.verifyInclusionProof = exports.inclusionProof = exports.buildTransparencyTreeFromHeads = exports.buildTransparencyTree = exports.transparencyLeafHash = exports.EMPTY_TRANSPARENCY_ROOT = exports.verifyAndAppend = exports.DEFAULT_CLOCK_SKEW_MS = exports.verifyEnvelope = exports.checkContinuity = exports.UNCHAINED_SEQ = exports.verifyEnvelopeSignature = exports.signEnvelope = exports.verifySignature = exports.signMessage = exports.computeRecordId = exports.sha256 = exports.signedRecordSigningInput = exports.canonicalize = void 0;
15
15
  // ---------------------------------------------------------------------------
16
16
  // Envelope — canonical JSON, signing input, content address, signing/verify
17
17
  // ---------------------------------------------------------------------------
@@ -76,4 +76,4 @@ Object.defineProperty(exports, "loadExpoCrypto", { enumerable: true, get: functi
76
76
  Object.defineProperty(exports, "loadSecureStore", { enumerable: true, get: function () { return crypto_1.loadSecureStore; } });
77
77
  Object.defineProperty(exports, "loadAsyncStorage", { enumerable: true, get: function () { return crypto_1.loadAsyncStorage; } });
78
78
  Object.defineProperty(exports, "getRandomBytesRN", { enumerable: true, get: function () { return crypto_1.getRandomBytesRN; } });
79
- Object.defineProperty(exports, "loadSharedIdentityBridge", { enumerable: true, get: function () { return crypto_1.loadSharedIdentityBridge; } });
79
+ Object.defineProperty(exports, "loadCommonsIdentityBridge", { enumerable: true, get: function () { return crypto_1.loadCommonsIdentityBridge; } });
@@ -111,7 +111,7 @@ exports.loadNodeCrypto = loadNodeCrypto;
111
111
  exports.loadExpoCrypto = loadExpoCrypto;
112
112
  exports.loadSecureStore = loadSecureStore;
113
113
  exports.loadAsyncStorage = loadAsyncStorage;
114
- exports.loadSharedIdentityBridge = loadSharedIdentityBridge;
114
+ exports.loadCommonsIdentityBridge = loadCommonsIdentityBridge;
115
115
  const platform_1 = require("./platform");
116
116
  // ---------------------------------------------------------------------------
117
117
  // Node `crypto` — Node built-in
@@ -170,13 +170,10 @@ async function loadAsyncStorage() {
170
170
  var random_1 = require("./random");
171
171
  Object.defineProperty(exports, "getRandomBytesRN", { enumerable: true, get: function () { return random_1.getRandomBytesRN; } });
172
172
  // ---------------------------------------------------------------------------
173
- // Shared identity bridge — `@oxy.so/expo-oxy-identity` (native-only).
173
+ // Commons identity bridge — the `OxyIdentity` native module (native-only).
174
174
  //
175
- // The default (web / Node) variant has no cross-app identity channel, so this
176
- // always resolves to `null`. `@oxy.so/core`'s `KeyManager` treats `null` as "no
177
- // bridge" and falls back to its package-private store — which is correct on web
178
- // (there is no shared identity there).
175
+ // Web and Node have no Commons to ask, so this always resolves to `null`.
179
176
  // ---------------------------------------------------------------------------
180
- function loadSharedIdentityBridge() {
177
+ function loadCommonsIdentityBridge() {
181
178
  return Promise.resolve(null);
182
179
  }
@@ -58,7 +58,7 @@ exports.loadNodeCrypto = loadNodeCrypto;
58
58
  exports.loadExpoCrypto = loadExpoCrypto;
59
59
  exports.loadSecureStore = loadSecureStore;
60
60
  exports.loadAsyncStorage = loadAsyncStorage;
61
- exports.loadSharedIdentityBridge = loadSharedIdentityBridge;
61
+ exports.loadCommonsIdentityBridge = loadCommonsIdentityBridge;
62
62
  const expo_modules_core_1 = require("expo-modules-core");
63
63
  const optionalPeer_1 = require("./optionalPeer");
64
64
  const random_native_1 = require("./random.native");
@@ -132,41 +132,71 @@ async function loadAsyncStorage() {
132
132
  // Metro pointed at the same file.
133
133
  var random_native_2 = require("./random.native");
134
134
  Object.defineProperty(exports, "getRandomBytesRN", { enumerable: true, get: function () { return random_native_2.getRandomBytesRN; } });
135
- // ---------------------------------------------------------------------------
136
- // Shared identity bridge — `@oxy.so/expo-oxy-identity` (native-only, OPTIONAL).
137
- //
138
- // `@oxy.so/expo-oxy-identity` is the in-repo Expo module autolinked into the
139
- // identity apps (Commons + the reader RPs). We resolve its NATIVE module
140
- // directly via expo-modules-core's `requireOptionalNativeModule('OxyIdentity')`
141
- // — a static import Metro always resolves — instead of dynamically importing the
142
- // module's JS wrapper. A runtime-computed `import(moduleName)` compiled to a
143
- // `require(variable)` in the CJS build, which Metro cannot resolve in a consuming
144
- // repo (the bridge silently resolved `null` there — the cross-app SSO bug). Since
145
- // the native module is what actually holds the shared identity, going through the
146
- // native registry is both correct and Metro-safe. `requireOptionalNativeModule`
147
- // returns `null` (never throws) when the module is not autolinked (web, or apps
148
- // that don't ship it), so `@oxy.so/core`'s `KeyManager` cleanly falls back to its
149
- // package-private store.
150
- // ---------------------------------------------------------------------------
151
- let sharedIdentityBridgePromise = null;
152
- function loadSharedIdentityBridge() {
153
- if (!sharedIdentityBridgePromise) {
154
- sharedIdentityBridgePromise = Promise.resolve().then(() => {
135
+ const LOWER_HEX = /^[0-9a-f]+$/;
136
+ function record(value) {
137
+ return typeof value === 'object' && value !== null ? value : null;
138
+ }
139
+ function hexField(value, length) {
140
+ if (typeof value !== 'string' || !LOWER_HEX.test(value))
141
+ return null;
142
+ if (length !== undefined && value.length !== length)
143
+ return null;
144
+ return value;
145
+ }
146
+ function narrowDescription(value) {
147
+ const r = record(value);
148
+ const publicKey = hexField(r?.publicKey, 130);
149
+ const v = typeof r?.v === 'number' ? r.v : Number(r?.v);
150
+ if (!publicKey || !Number.isInteger(v))
151
+ return null;
152
+ return { v, publicKey };
153
+ }
154
+ function narrowProof(value) {
155
+ const r = record(value);
156
+ const publicKey = hexField(r?.publicKey, 130);
157
+ const signature = hexField(r?.signature);
158
+ const timestamp = typeof r?.timestamp === 'number' ? r.timestamp : Number(r?.timestamp);
159
+ if (!publicKey || !signature || !Number.isSafeInteger(timestamp) || timestamp <= 0)
160
+ return null;
161
+ return { publicKey, signature, timestamp };
162
+ }
163
+ function narrowSocialReceive(value) {
164
+ const r = record(value);
165
+ const signature = hexField(r?.signature);
166
+ const publicKey = hexField(r?.publicKey, 66);
167
+ if (!signature || !publicKey)
168
+ return null;
169
+ return { signature, publicKey };
170
+ }
171
+ /** Run one native call; a rejection or an unexpected shape is `null`. */
172
+ async function ask(call, narrow) {
173
+ try {
174
+ return narrow(await call());
175
+ }
176
+ catch {
177
+ return null;
178
+ }
179
+ }
180
+ let commonsIdentityBridgePromise = null;
181
+ function loadCommonsIdentityBridge() {
182
+ if (!commonsIdentityBridgePromise) {
183
+ commonsIdentityBridgePromise = Promise.resolve().then(() => {
155
184
  const native = (0, expo_modules_core_1.requireOptionalNativeModule)('OxyIdentity');
156
- if (native &&
157
- typeof native.getShared === 'function' &&
158
- typeof native.putShared === 'function' &&
159
- typeof native.hasShared === 'function' &&
160
- typeof native.clearShared === 'function') {
161
- return {
162
- getShared: native.getShared.bind(native),
163
- putShared: native.putShared.bind(native),
164
- hasShared: native.hasShared.bind(native),
165
- clearShared: native.clearShared.bind(native),
166
- };
185
+ if (!native ||
186
+ typeof native.describe !== 'function' ||
187
+ typeof native.proveIdentity !== 'function' ||
188
+ typeof native.deriveScopedSeed !== 'function' ||
189
+ typeof native.signSocialReceive !== 'function') {
190
+ return null;
167
191
  }
168
- return null;
192
+ const m = native;
193
+ return {
194
+ describe: () => ask(() => m.describe(), narrowDescription),
195
+ proveIdentity: (challenge) => ask(() => m.proveIdentity(challenge), narrowProof),
196
+ deriveScopedSeed: (info) => ask(() => m.deriveScopedSeed(info), (v) => hexField(v, 64)),
197
+ signSocialReceive: (index, digest) => ask(() => m.signSocialReceive(index, digest), narrowSocialReceive),
198
+ };
169
199
  });
170
200
  }
171
- return sharedIdentityBridgePromise;
201
+ return commonsIdentityBridgePromise;
172
202
  }
@@ -16,6 +16,7 @@ exports.deriveSecp256k1PublicKey = deriveSecp256k1PublicKey;
16
16
  exports.normalizeSecp256k1PublicKey = normalizeSecp256k1PublicKey;
17
17
  exports.signSecp256k1Digest = signSecp256k1Digest;
18
18
  exports.verifySecp256k1Digest = verifySecp256k1Digest;
19
+ exports.tweakAddSecp256k1PrivateKey = tweakAddSecp256k1PrivateKey;
19
20
  exports.deriveSecp256k1SharedSecret = deriveSecp256k1SharedSecret;
20
21
  const secp256k1_js_1 = require("@noble/curves/secp256k1.js");
21
22
  const HEX = /^[0-9a-fA-F]+$/;
@@ -138,6 +139,29 @@ function verifySecp256k1Digest(publicKeyHex, digestHex, signatureDerHex) {
138
139
  lowS: false,
139
140
  });
140
141
  }
142
+ /**
143
+ * `(privateKey + tweak) mod n`, the scalar step of BIP32 non-hardened child
144
+ * derivation (`CKDpriv`: the tweak is `IL`, the left half of the HMAC-SHA512).
145
+ *
146
+ * Throws, as BIP32 requires, when the tweak is not below the curve order or
147
+ * the sum is zero: that child is invalid and the caller must not use it.
148
+ */
149
+ function tweakAddSecp256k1PrivateKey(privateKeyHex, tweakHex) {
150
+ const n = secp256k1_js_1.secp256k1.Point.Fn.ORDER;
151
+ const key = BigInt(`0x${normalizeSecp256k1PrivateKey(privateKeyHex)}`);
152
+ if (typeof tweakHex !== "string" || tweakHex.length !== 64 || !HEX.test(tweakHex)) {
153
+ throw new Error("secp256k1 tweak must be exactly 32 bytes of hexadecimal data");
154
+ }
155
+ const tweak = BigInt(`0x${tweakHex}`);
156
+ if (tweak >= n) {
157
+ throw new Error("secp256k1 tweak is not below the curve order");
158
+ }
159
+ const child = (key + tweak) % n;
160
+ if (child === 0n) {
161
+ throw new Error("secp256k1 tweaked private key is zero");
162
+ }
163
+ return child.toString(16).padStart(64, "0");
164
+ }
141
165
  /** Derive the fixed-width 32-byte ECDH x-coordinate shared secret. */
142
166
  function deriveSecp256k1SharedSecret(privateKeyHex, publicKeyHex) {
143
167
  const encodedPoint = secp256k1_js_1.secp256k1.getSharedSecret(parsePrivateKey(privateKeyHex), parsePublicKey(publicKeyHex), true);