@coordination-network/identity 0.0.2 → 0.1.1

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/src/binding.ts CHANGED
@@ -3,10 +3,15 @@
3
3
  *
4
4
  * NORMATIVE layouts:
5
5
  * ```
6
- * deviceBindingMessage = nonce(32) || networkId(32) || holderKeyCommitment(32) // credential issuance
7
- * accountBindingMessage = nonce(32) || confirmationKeyHash(32) || networkId(32) // 96B, signed by A (secp256k1)
6
+ * deviceBindingMessage = nonce(32) || networkId(32) || holderKeyCommitment(32) || requestCommitment(32)
7
+ * accountBindingMessage = nonce(32) || confirmationKeyHash(32) || networkId(32)
8
8
  * ```
9
9
  *
10
+ * B (P-256) signs the 128-byte device message; A (secp256k1) signs the 96-byte account message.
11
+ *
12
+ * `requestCommitment` is opaque here: the credential layer defines it and
13
+ * binds the issuer and scope shown at consent into it.
14
+ *
10
15
  * NORMATIVE MUST: the device signs a locally-derived `networkId`, never one
11
16
  * supplied by the issuance request. Every function here except the two
12
17
  * low-level builders and {@link verifyDeviceBindingSignature} enforces this
@@ -31,6 +36,12 @@ function toBindingBytes(value: Uint8Array | Hex, label: string): Uint8Array {
31
36
  }
32
37
  }
33
38
 
39
+ function toBindingHash32(value: Uint8Array | Hex, label: string): Uint8Array {
40
+ const bytes = toBindingBytes(value, label)
41
+ if (bytes.length !== 32) throw new InvalidBindingInputError(`${label}: expected exactly 32 bytes`)
42
+ return bytes
43
+ }
44
+
34
45
  function decodeAndValidateNonceAndNetworkId(
35
46
  nonce: Uint8Array | Hex,
36
47
  networkId: Uint8Array | Hex
@@ -111,7 +122,8 @@ function parseDeviceSignature(signature: Uint8Array | Hex): ReturnType<typeof p2
111
122
  }
112
123
 
113
124
  /**
114
- * Builds `nonce(32) || networkId(32)`, optionally followed by a 32-byte holder commitment.
125
+ * Builds the 128-byte `deviceBindingMessage`:
126
+ * `nonce(32) || networkId(32) || holderKeyCommitment(32) || requestCommitment(32)`.
115
127
  *
116
128
  * Low-level, spec-literal builder — prefer {@link buildBindingMessages},
117
129
  * which enforces the locally-derived-`networkId` MUST structurally.
@@ -119,13 +131,13 @@ function parseDeviceSignature(signature: Uint8Array | Hex): ReturnType<typeof p2
119
131
  export function buildDeviceBindingMessage(
120
132
  nonce: Uint8Array | Hex,
121
133
  networkId: Uint8Array | Hex,
122
- holderKeyCommitment?: Uint8Array | Hex
134
+ holderKeyCommitment: Uint8Array | Hex,
135
+ requestCommitment: Uint8Array | Hex
123
136
  ): Uint8Array {
124
137
  const { nonceBytes, networkIdBytes } = decodeAndValidateNonceAndNetworkId(nonce, networkId)
125
- if (holderKeyCommitment === undefined) return concatBytes(nonceBytes, networkIdBytes)
126
- const holderBytes = toBindingBytes(holderKeyCommitment, "holderKeyCommitment")
127
- if (holderBytes.length !== 32) throw new InvalidBindingInputError("holderKeyCommitment: expected exactly 32 bytes")
128
- return concatBytes(nonceBytes, networkIdBytes, holderBytes)
138
+ const holderBytes = toBindingHash32(holderKeyCommitment, "holderKeyCommitment")
139
+ const requestBytes = toBindingHash32(requestCommitment, "requestCommitment")
140
+ return concatBytes(nonceBytes, networkIdBytes, holderBytes, requestBytes)
129
141
  }
130
142
 
131
143
  /**
@@ -155,7 +167,8 @@ export function buildBindingMessages(
155
167
  nonce: Uint8Array | Hex,
156
168
  accountUncompressedPublicKey: Uint8Array | Hex,
157
169
  confirmationKey: Uint8Array | Hex,
158
- holderKeyCommitment?: Uint8Array | Hex
170
+ holderKeyCommitment: Uint8Array | Hex,
171
+ requestCommitment: Uint8Array | Hex
159
172
  ): { networkId: Hex; deviceBindingMessage: Uint8Array; accountBindingMessage: Uint8Array } {
160
173
  let networkId: Hex
161
174
 
@@ -167,7 +180,7 @@ export function buildBindingMessages(
167
180
  )
168
181
  }
169
182
 
170
- const deviceBindingMessage = buildDeviceBindingMessage(nonce, networkId, holderKeyCommitment)
183
+ const deviceBindingMessage = buildDeviceBindingMessage(nonce, networkId, holderKeyCommitment, requestCommitment)
171
184
  const accountBindingMessage = buildAccountBindingMessage(nonce, confirmationKey, networkId)
172
185
 
173
186
  return { networkId, deviceBindingMessage, accountBindingMessage }
@@ -237,15 +250,17 @@ export function verifyDeviceBindingSignature({
237
250
  networkId,
238
251
  confirmationKey,
239
252
  holderKeyCommitment,
253
+ requestCommitment,
240
254
  signature
241
255
  }: {
242
256
  nonce: Uint8Array | Hex
243
257
  networkId: Uint8Array | Hex
244
258
  confirmationKey: Uint8Array | Hex
245
- holderKeyCommitment?: Uint8Array | Hex
259
+ holderKeyCommitment: Uint8Array | Hex
260
+ requestCommitment: Uint8Array | Hex
246
261
  signature: Uint8Array | Hex
247
262
  }): boolean {
248
- const message = buildDeviceBindingMessage(nonce, networkId, holderKeyCommitment)
263
+ const message = buildDeviceBindingMessage(nonce, networkId, holderKeyCommitment, requestCommitment)
249
264
  const publicKeyBytes = prependUncompressedPrefix(normalizeConfirmationKey(confirmationKey))
250
265
  const parsedSignature = parseDeviceSignature(signature)
251
266
 
@@ -266,13 +281,15 @@ export function verifyCredentialBinding({
266
281
  accountPublicKey,
267
282
  confirmationKey,
268
283
  holderKeyCommitment,
284
+ requestCommitment,
269
285
  accountSignature,
270
286
  deviceSignature
271
287
  }: {
272
288
  nonce: Uint8Array | Hex
273
289
  accountPublicKey: Uint8Array | Hex
274
290
  confirmationKey: Uint8Array | Hex
275
- holderKeyCommitment?: Uint8Array | Hex
291
+ holderKeyCommitment: Uint8Array | Hex
292
+ requestCommitment: Uint8Array | Hex
276
293
  accountSignature: Uint8Array | Hex
277
294
  deviceSignature: Uint8Array | Hex
278
295
  }): { valid: boolean; networkId: Hex } {
@@ -291,6 +308,7 @@ export function verifyCredentialBinding({
291
308
  networkId,
292
309
  confirmationKey,
293
310
  holderKeyCommitment,
311
+ requestCommitment,
294
312
  signature: deviceSignature
295
313
  })
296
314
 
package/src/constants.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Normative constants for the `v1` key derivation scheme. Changing any of
2
+ * Normative constants for the `v2` key derivation scheme. Changing any of
3
3
  * these changes every derived key and `networkId`, so they MUST NOT change
4
4
  * without a version bump (new `KEY_DERIVATION_VERSION` and domain strings).
5
5
  */
6
6
 
7
- export const KEY_DERIVATION_VERSION = "v1" as const
7
+ export const KEY_DERIVATION_VERSION = "v2" as const
8
8
 
9
9
  /** Fixed domain-separation message signed by {@link signDerivationMessage}. */
10
10
  export const FIXED_DOMAIN_MSG = "coordination-network/identity/v1" as const
@@ -18,12 +18,18 @@ export const DOMAIN_AUTH = "coordination-network/identity/v1/auth" as const
18
18
  /** Domain-separation tag for deriving the Baby Jubjub EdDSA seed (the "fifth key") from the hardware ECDH shared secret. */
19
19
  export const DOMAIN_EDDSA = "coordination-network/identity/v1/eddsa" as const
20
20
 
21
+ /** Distinct Baby Jubjub seed for public paymaster membership; never use the credential holder key as a group leaf. */
22
+ export const DOMAIN_PAYMASTER = "coordination-network/identity/v1/paymaster" as const
23
+
21
24
  /** Domain-separation message hashed to the fixed NUMS P-256 peer point `Q` for the fifth-key ECDH (`eddsa-peer.ts`). */
22
25
  export const DOMAIN_EDDSA_PEER = "coordination-network/identity/v1/eddsa/peer" as const
23
26
 
27
+ /** Domain-separation tag prefixed to `X || Y` when hashing a user `networkId`. */
28
+ export const DOMAIN_NETWORK_ID = "coordination-network/identity/v2/network-id" as const
29
+
24
30
  /**
25
31
  * Byte length of `nonce` and `networkId` in the binding messages — fixed at
26
- * 32 so the 64- vs 96-byte messages are injective.
32
+ * 32 so the 128-byte device and 96-byte account messages are injective.
27
33
  */
28
34
  export const BINDING_NONCE_LENGTH = 32 as const
29
35
 
package/src/derive.ts CHANGED
@@ -3,15 +3,16 @@ import { KEY_DERIVATION_VERSION } from "./constants"
3
3
  import { deriveAccountKeypair, signDerivationMessage } from "./account"
4
4
  import { deriveStealthKeys } from "./stealthAddresses/deriveStealthKeys"
5
5
  import { deriveAuthKeypair } from "./auth"
6
- import { computeNetworkId } from "./network-id"
6
+ import { computeMemberNetworkId, computeNetworkId } from "./network-id"
7
7
 
8
8
  /**
9
- * Derives the full `v1` device identity — `networkId` plus the four-key
10
- * all-secp256k1 model — from a single device secret.
9
+ * Derives the full `v2` device identity — `networkId`, `memberNetworkId` and the account,
10
+ * spending, viewing and auth secp256k1 keypairs — from a single device secret.
11
11
  *
12
12
  * Pipeline (NORMATIVE):
13
13
  * 1. `account = deriveAccountKeypair(deviceSecret)`
14
- * 2. `networkId = computeNetworkId(account.uncompressedPublicKey)`
14
+ * 2. `networkId = computeNetworkId(account.uncompressedPublicKey)` and
15
+ * `memberNetworkId = computeMemberNetworkId(account.uncompressedPublicKey)`
15
16
  * 3. `sig = signDerivationMessage(account.privateKey)` (EIP-191, deterministic)
16
17
  * 4. `{ spending, viewing } = deriveStealthKeys(sig)` (FluidKey semantics)
17
18
  * 5. `auth = deriveAuthKeypair(sig)`
@@ -19,6 +20,7 @@ import { computeNetworkId } from "./network-id"
19
20
  export function deriveKeys(deviceSecret: Uint8Array | Hex): DerivedKeys {
20
21
  const account = deriveAccountKeypair(deviceSecret)
21
22
  const networkId = computeNetworkId(account.uncompressedPublicKey)
23
+ const memberNetworkId = computeMemberNetworkId(account.uncompressedPublicKey)
22
24
  const signature = signDerivationMessage(account.privateKey)
23
25
  const { spending, viewing } = deriveStealthKeys(signature)
24
26
  const auth = deriveAuthKeypair(signature)
@@ -26,6 +28,7 @@ export function deriveKeys(deviceSecret: Uint8Array | Hex): DerivedKeys {
26
28
  return {
27
29
  version: KEY_DERIVATION_VERSION,
28
30
  networkId,
31
+ memberNetworkId,
29
32
  account,
30
33
  spending,
31
34
  viewing,
package/src/eddsa.ts CHANGED
@@ -4,9 +4,25 @@ import { keccak_256 } from "@noble/hashes/sha3"
4
4
  import { deriveSecretScalar, derivePublicKey } from "@zk-kit/eddsa-poseidon"
5
5
  import { poseidon2 } from "poseidon-lite/poseidon2"
6
6
  import type { EddsaKeypair, Hex } from "./types"
7
- import { DOMAIN_EDDSA } from "./constants"
7
+ import { DOMAIN_EDDSA, DOMAIN_PAYMASTER } from "./constants"
8
8
  import { bytesToHex, concatBytes, normalizeEcdhSharedSecret, utf8ToBytes } from "./bytes"
9
9
 
10
+ function deriveKeypair(ecdhSharedSecret: Uint8Array | Hex, domain: string): EddsaKeypair {
11
+ const secret = normalizeEcdhSharedSecret(ecdhSharedSecret)
12
+ const seed = keccak_256(concatBytes(utf8ToBytes(domain), secret))
13
+
14
+ const secretScalar = deriveSecretScalar(seed)
15
+ const publicKey = derivePublicKey(seed)
16
+ const commitment = poseidon2(publicKey)
17
+
18
+ return {
19
+ seed: bytesToHex(seed),
20
+ secretScalar,
21
+ publicKey,
22
+ commitment
23
+ }
24
+ }
25
+
10
26
  /**
11
27
  * Derives the Baby Jubjub EdDSA keypair and Semaphore-v4 identity commitment
12
28
  * (the "fifth key"), rooted in a hardware ECDH shared secret.
@@ -21,17 +37,10 @@ import { bytesToHex, concatBytes, normalizeEcdhSharedSecret, utf8ToBytes } from
21
37
  * @throws {InvalidEcdhSecretError} if `ecdhSharedSecret` is not exactly 32 bytes
22
38
  */
23
39
  export function deriveEddsaKeypair(ecdhSharedSecret: Uint8Array | Hex): EddsaKeypair {
24
- const secret = normalizeEcdhSharedSecret(ecdhSharedSecret)
25
- const seed = keccak_256(concatBytes(utf8ToBytes(DOMAIN_EDDSA), secret))
26
-
27
- const secretScalar = deriveSecretScalar(seed)
28
- const publicKey = derivePublicKey(seed)
29
- const commitment = poseidon2(publicKey)
40
+ return deriveKeypair(ecdhSharedSecret, DOMAIN_EDDSA)
41
+ }
30
42
 
31
- return {
32
- seed: bytesToHex(seed),
33
- secretScalar,
34
- publicKey,
35
- commitment
36
- }
43
+ /** Derives a separate Semaphore identity whose public commitment may join the paymaster group. */
44
+ export function derivePaymasterKeypair(ecdhSharedSecret: Uint8Array | Hex): EddsaKeypair {
45
+ return deriveKeypair(ecdhSharedSecret, DOMAIN_PAYMASTER)
37
46
  }
package/src/index.ts CHANGED
@@ -37,9 +37,9 @@ export { metaStealthAddressUri } from "./stealthAddresses/metaStealthAddressUri"
37
37
  export { decodeMetaStealthAddress } from "./stealthAddresses/decodeMetaStealthAddress"
38
38
  export { buildAnnouncement } from "./stealthAddresses/buildAnnouncement"
39
39
  export { deriveAuthKeypair } from "./auth"
40
- export { deriveEddsaKeypair } from "./eddsa"
40
+ export { deriveEddsaKeypair, derivePaymasterKeypair } from "./eddsa"
41
41
  export { EDDSA_PEER_POINT_HEX, eddsaPeerPointBytes, computeEddsaPeerPoint } from "./eddsa-peer"
42
- export { computeNetworkId } from "./network-id"
42
+ export { computeNetworkId, computeMemberNetworkId } from "./network-id"
43
43
  export { deriveKeys, deriveNetworkId } from "./derive"
44
44
  export { normalizeConfirmationKey, computeConfirmationKeyHash } from "./confirmation-key"
45
45
  export {
package/src/network-id.ts CHANGED
@@ -1,26 +1,43 @@
1
1
  import { keccak_256 } from "@noble/hashes/sha3"
2
2
  import type { Hex } from "./types"
3
- import { bytesToHex, toBytes } from "./bytes"
3
+ import { bytesToHex, concatBytes, toBytes, utf8ToBytes } from "./bytes"
4
+ import { DOMAIN_NETWORK_ID } from "./constants"
5
+
6
+ function publicKeyCoordinates(uncompressedPublicKey: Uint8Array | Hex, caller: string): Uint8Array {
7
+ const bytes = toBytes(uncompressedPublicKey)
8
+
9
+ if (bytes.length === 65 && bytes[0] === 0x04) {
10
+ return bytes.slice(1)
11
+ }
12
+
13
+ if (bytes.length === 64) {
14
+ return bytes
15
+ }
16
+
17
+ throw new TypeError(`${caller}: expected a 65-byte 0x04-prefixed or 64-byte raw uncompressed public key`)
18
+ }
4
19
 
5
20
  /**
6
- * Computes the bytes32 `networkId` from an uncompressed secp256k1 public key
7
- * (65-byte SEC1 `0x04`-prefixed or 64-byte raw `X || Y`).
21
+ * Computes a user's bytes32 `networkId` from an uncompressed secp256k1 public
22
+ * key (65-byte SEC1 `0x04`-prefixed or 64-byte raw `X || Y`).
8
23
  *
9
- * NORMATIVE: `networkId = keccak256(X || Y)` — the SEC1 prefix is stripped.
10
- * The last 20 bytes are the account's Ethereum address.
24
+ * NORMATIVE: `networkId = keccak256(utf8(DOMAIN_NETWORK_ID) || X || Y)` — the
25
+ * SEC1 prefix is stripped.
11
26
  */
12
27
  export function computeNetworkId(uncompressedPublicKey: Uint8Array | Hex): Hex {
13
- const bytes = toBytes(uncompressedPublicKey)
28
+ const coordinates = publicKeyCoordinates(uncompressedPublicKey, "computeNetworkId")
14
29
 
15
- let coordinates: Uint8Array
30
+ return bytesToHex(keccak_256(concatBytes(utf8ToBytes(DOMAIN_NETWORK_ID), coordinates)))
31
+ }
16
32
 
17
- if (bytes.length === 65 && bytes[0] === 0x04) {
18
- coordinates = bytes.slice(1)
19
- } else if (bytes.length === 64) {
20
- coordinates = bytes
21
- } else {
22
- throw new TypeError("computeNetworkId: expected a 65-byte 0x04-prefixed or 64-byte raw uncompressed public key")
23
- }
33
+ /**
34
+ * Computes a member's bytes32 id from the same public key forms.
35
+ *
36
+ * NORMATIVE: `memberNetworkId = keccak256(X || Y)`. The last 20 bytes are the
37
+ * member's auth address, which `NetworkMemberRegistry` reads.
38
+ */
39
+ export function computeMemberNetworkId(uncompressedPublicKey: Uint8Array | Hex): Hex {
40
+ const coordinates = publicKeyCoordinates(uncompressedPublicKey, "computeMemberNetworkId")
24
41
 
25
42
  return bytesToHex(keccak_256(coordinates))
26
43
  }
package/src/types.ts CHANGED
@@ -14,12 +14,14 @@ export interface Secp256k1Keypair {
14
14
  uncompressedPublicKey: Hex
15
15
  }
16
16
 
17
- /** The full key set derived from one device secret, plus the device's `networkId`. */
17
+ /** The full key set derived from one device secret, plus the device's `networkId` and `memberNetworkId`. */
18
18
  export interface DerivedKeys {
19
19
  /** Derivation scheme version. See {@link KEY_DERIVATION_VERSION}. */
20
- version: "v1"
21
- /** bytes32 device identifier: `keccak256(uncompressed account pubkey, sans 04 prefix)`. */
20
+ version: "v2"
21
+ /** bytes32 user identifier: `keccak256(utf8(DOMAIN_NETWORK_ID) || X || Y)` of the account pubkey. */
22
22
  networkId: Hex
23
+ /** bytes32 member identifier: `keccak256(X || Y)`; its low 20 bytes are the member's auth address. */
24
+ memberNetworkId: Hex
23
25
  /** Root secp256k1 keypair the other keys are derived from. */
24
26
  account: Secp256k1Keypair
25
27
  /** secp256k1 keypair used to authorize spending. */
package/types/index.d.ts CHANGED
@@ -11,12 +11,14 @@ interface Secp256k1Keypair {
11
11
  /** 65-byte SEC1 public key, `0x04`-prefixed (uncompressed form). */
12
12
  uncompressedPublicKey: Hex;
13
13
  }
14
- /** The full key set derived from one device secret, plus the device's `networkId`. */
14
+ /** The full key set derived from one device secret, plus the device's `networkId` and `memberNetworkId`. */
15
15
  interface DerivedKeys {
16
16
  /** Derivation scheme version. See {@link KEY_DERIVATION_VERSION}. */
17
- version: "v1";
18
- /** bytes32 device identifier: `keccak256(uncompressed account pubkey, sans 04 prefix)`. */
17
+ version: "v2";
18
+ /** bytes32 user identifier: `keccak256(utf8(DOMAIN_NETWORK_ID) || X || Y)` of the account pubkey. */
19
19
  networkId: Hex;
20
+ /** bytes32 member identifier: `keccak256(X || Y)`; its low 20 bytes are the member's auth address. */
21
+ memberNetworkId: Hex;
20
22
  /** Root secp256k1 keypair the other keys are derived from. */
21
23
  account: Secp256k1Keypair;
22
24
  /** secp256k1 keypair used to authorize spending. */
@@ -47,11 +49,11 @@ interface EddsaKeypair {
47
49
  }
48
50
 
49
51
  /**
50
- * Normative constants for the `v1` key derivation scheme. Changing any of
52
+ * Normative constants for the `v2` key derivation scheme. Changing any of
51
53
  * these changes every derived key and `networkId`, so they MUST NOT change
52
54
  * without a version bump (new `KEY_DERIVATION_VERSION` and domain strings).
53
55
  */
54
- declare const KEY_DERIVATION_VERSION: "v1";
56
+ declare const KEY_DERIVATION_VERSION: "v2";
55
57
  /** Fixed domain-separation message signed by {@link signDerivationMessage}. */
56
58
  declare const FIXED_DOMAIN_MSG: "coordination-network/identity/v1";
57
59
  /** Domain-separation tag for deriving the account keypair from a device secret. */
@@ -60,11 +62,15 @@ declare const DOMAIN_ACCOUNT: "coordination-network/identity/v1/account";
60
62
  declare const DOMAIN_AUTH: "coordination-network/identity/v1/auth";
61
63
  /** Domain-separation tag for deriving the Baby Jubjub EdDSA seed (the "fifth key") from the hardware ECDH shared secret. */
62
64
  declare const DOMAIN_EDDSA: "coordination-network/identity/v1/eddsa";
65
+ /** Distinct Baby Jubjub seed for public paymaster membership; never use the credential holder key as a group leaf. */
66
+ declare const DOMAIN_PAYMASTER: "coordination-network/identity/v1/paymaster";
63
67
  /** Domain-separation message hashed to the fixed NUMS P-256 peer point `Q` for the fifth-key ECDH (`eddsa-peer.ts`). */
64
68
  declare const DOMAIN_EDDSA_PEER: "coordination-network/identity/v1/eddsa/peer";
69
+ /** Domain-separation tag prefixed to `X || Y` when hashing a user `networkId`. */
70
+ declare const DOMAIN_NETWORK_ID: "coordination-network/identity/v2/network-id";
65
71
  /**
66
72
  * Byte length of `nonce` and `networkId` in the binding messages — fixed at
67
- * 32 so the 64- vs 96-byte messages are injective.
73
+ * 32 so the 128-byte device and 96-byte account messages are injective.
68
74
  */
69
75
  declare const BINDING_NONCE_LENGTH: 32;
70
76
  /** SEC1-compressed secp256k1 public key length. */
@@ -340,6 +346,8 @@ declare function deriveAuthKeypair(sig64: Uint8Array | Hex): Secp256k1Keypair;
340
346
  * @throws {InvalidEcdhSecretError} if `ecdhSharedSecret` is not exactly 32 bytes
341
347
  */
342
348
  declare function deriveEddsaKeypair(ecdhSharedSecret: Uint8Array | Hex): EddsaKeypair;
349
+ /** Derives a separate Semaphore identity whose public commitment may join the paymaster group. */
350
+ declare function derivePaymasterKeypair(ecdhSharedSecret: Uint8Array | Hex): EddsaKeypair;
343
351
 
344
352
  /** Fixed NUMS P-256 peer point `Q` for the fifth-key ECDH — see `DERIVATION.md` §12. */
345
353
  /**
@@ -361,21 +369,29 @@ declare function eddsaPeerPointBytes(): Uint8Array;
361
369
  declare function computeEddsaPeerPoint(): Uint8Array;
362
370
 
363
371
  /**
364
- * Computes the bytes32 `networkId` from an uncompressed secp256k1 public key
365
- * (65-byte SEC1 `0x04`-prefixed or 64-byte raw `X || Y`).
372
+ * Computes a user's bytes32 `networkId` from an uncompressed secp256k1 public
373
+ * key (65-byte SEC1 `0x04`-prefixed or 64-byte raw `X || Y`).
366
374
  *
367
- * NORMATIVE: `networkId = keccak256(X || Y)` — the SEC1 prefix is stripped.
368
- * The last 20 bytes are the account's Ethereum address.
375
+ * NORMATIVE: `networkId = keccak256(utf8(DOMAIN_NETWORK_ID) || X || Y)` — the
376
+ * SEC1 prefix is stripped.
369
377
  */
370
378
  declare function computeNetworkId(uncompressedPublicKey: Uint8Array | Hex): Hex;
379
+ /**
380
+ * Computes a member's bytes32 id from the same public key forms.
381
+ *
382
+ * NORMATIVE: `memberNetworkId = keccak256(X || Y)`. The last 20 bytes are the
383
+ * member's auth address, which `NetworkMemberRegistry` reads.
384
+ */
385
+ declare function computeMemberNetworkId(uncompressedPublicKey: Uint8Array | Hex): Hex;
371
386
 
372
387
  /**
373
- * Derives the full `v1` device identity — `networkId` plus the four-key
374
- * all-secp256k1 model — from a single device secret.
388
+ * Derives the full `v2` device identity — `networkId`, `memberNetworkId` and the account,
389
+ * spending, viewing and auth secp256k1 keypairs — from a single device secret.
375
390
  *
376
391
  * Pipeline (NORMATIVE):
377
392
  * 1. `account = deriveAccountKeypair(deviceSecret)`
378
- * 2. `networkId = computeNetworkId(account.uncompressedPublicKey)`
393
+ * 2. `networkId = computeNetworkId(account.uncompressedPublicKey)` and
394
+ * `memberNetworkId = computeMemberNetworkId(account.uncompressedPublicKey)`
379
395
  * 3. `sig = signDerivationMessage(account.privateKey)` (EIP-191, deterministic)
380
396
  * 4. `{ spending, viewing } = deriveStealthKeys(sig)` (FluidKey semantics)
381
397
  * 5. `auth = deriveAuthKeypair(sig)`
@@ -403,10 +419,15 @@ declare function computeConfirmationKeyHash(confirmationKey: Uint8Array | Hex):
403
419
  *
404
420
  * NORMATIVE layouts:
405
421
  * ```
406
- * deviceBindingMessage = nonce(32) || networkId(32) || holderKeyCommitment(32) // credential issuance
407
- * accountBindingMessage = nonce(32) || confirmationKeyHash(32) || networkId(32) // 96B, signed by A (secp256k1)
422
+ * deviceBindingMessage = nonce(32) || networkId(32) || holderKeyCommitment(32) || requestCommitment(32)
423
+ * accountBindingMessage = nonce(32) || confirmationKeyHash(32) || networkId(32)
408
424
  * ```
409
425
  *
426
+ * B (P-256) signs the 128-byte device message; A (secp256k1) signs the 96-byte account message.
427
+ *
428
+ * `requestCommitment` is opaque here: the credential layer defines it and
429
+ * binds the issuer and scope shown at consent into it.
430
+ *
410
431
  * NORMATIVE MUST: the device signs a locally-derived `networkId`, never one
411
432
  * supplied by the issuance request. Every function here except the two
412
433
  * low-level builders and {@link verifyDeviceBindingSignature} enforces this
@@ -414,12 +435,13 @@ declare function computeConfirmationKeyHash(confirmationKey: Uint8Array | Hex):
414
435
  */
415
436
 
416
437
  /**
417
- * Builds `nonce(32) || networkId(32)`, optionally followed by a 32-byte holder commitment.
438
+ * Builds the 128-byte `deviceBindingMessage`:
439
+ * `nonce(32) || networkId(32) || holderKeyCommitment(32) || requestCommitment(32)`.
418
440
  *
419
441
  * Low-level, spec-literal builder — prefer {@link buildBindingMessages},
420
442
  * which enforces the locally-derived-`networkId` MUST structurally.
421
443
  */
422
- declare function buildDeviceBindingMessage(nonce: Uint8Array | Hex, networkId: Uint8Array | Hex, holderKeyCommitment?: Uint8Array | Hex): Uint8Array;
444
+ declare function buildDeviceBindingMessage(nonce: Uint8Array | Hex, networkId: Uint8Array | Hex, holderKeyCommitment: Uint8Array | Hex, requestCommitment: Uint8Array | Hex): Uint8Array;
423
445
  /**
424
446
  * Builds the 96-byte `accountBindingMessage`:
425
447
  * `nonce(32) || confirmationKeyHash(32) || networkId(32)`.
@@ -433,7 +455,7 @@ declare function buildAccountBindingMessage(nonce: Uint8Array | Hex, confirmatio
433
455
  * derived internally from the account's own public key, so a caller cannot
434
456
  * supply a `networkId` from elsewhere even by mistake.
435
457
  */
436
- declare function buildBindingMessages(nonce: Uint8Array | Hex, accountUncompressedPublicKey: Uint8Array | Hex, confirmationKey: Uint8Array | Hex, holderKeyCommitment?: Uint8Array | Hex): {
458
+ declare function buildBindingMessages(nonce: Uint8Array | Hex, accountUncompressedPublicKey: Uint8Array | Hex, confirmationKey: Uint8Array | Hex, holderKeyCommitment: Uint8Array | Hex, requestCommitment: Uint8Array | Hex): {
437
459
  networkId: Hex;
438
460
  deviceBindingMessage: Uint8Array;
439
461
  accountBindingMessage: Uint8Array;
@@ -470,11 +492,12 @@ declare function verifyAccountBindingSignature({ nonce, confirmationKey, account
470
492
  * `prehash: true` (signers hash SHA-256 internally); `lowS: false` (hardware
471
493
  * signatures aren't s-normalized — `DERIVATION.md` §11.4).
472
494
  */
473
- declare function verifyDeviceBindingSignature({ nonce, networkId, confirmationKey, holderKeyCommitment, signature }: {
495
+ declare function verifyDeviceBindingSignature({ nonce, networkId, confirmationKey, holderKeyCommitment, requestCommitment, signature }: {
474
496
  nonce: Uint8Array | Hex;
475
497
  networkId: Uint8Array | Hex;
476
498
  confirmationKey: Uint8Array | Hex;
477
- holderKeyCommitment?: Uint8Array | Hex;
499
+ holderKeyCommitment: Uint8Array | Hex;
500
+ requestCommitment: Uint8Array | Hex;
478
501
  signature: Uint8Array | Hex;
479
502
  }): boolean;
480
503
  /**
@@ -483,11 +506,12 @@ declare function verifyDeviceBindingSignature({ nonce, networkId, confirmationKe
483
506
  * with an unrelated account signature in either direction. `networkId` is
484
507
  * returned even when `valid` is false, for issuer-side logging/rate-limiting.
485
508
  */
486
- declare function verifyCredentialBinding({ nonce, accountPublicKey, confirmationKey, holderKeyCommitment, accountSignature, deviceSignature }: {
509
+ declare function verifyCredentialBinding({ nonce, accountPublicKey, confirmationKey, holderKeyCommitment, requestCommitment, accountSignature, deviceSignature }: {
487
510
  nonce: Uint8Array | Hex;
488
511
  accountPublicKey: Uint8Array | Hex;
489
512
  confirmationKey: Uint8Array | Hex;
490
- holderKeyCommitment?: Uint8Array | Hex;
513
+ holderKeyCommitment: Uint8Array | Hex;
514
+ requestCommitment: Uint8Array | Hex;
491
515
  accountSignature: Uint8Array | Hex;
492
516
  deviceSignature: Uint8Array | Hex;
493
517
  }): {
@@ -495,5 +519,5 @@ declare function verifyCredentialBinding({ nonce, accountPublicKey, confirmation
495
519
  networkId: Hex;
496
520
  };
497
521
 
498
- export { BINDING_NONCE_LENGTH, COMPRESSED_PUBLIC_KEY_LENGTH, DOMAIN_ACCOUNT, DOMAIN_AUTH, DOMAIN_EDDSA, DOMAIN_EDDSA_PEER, DerivationFailureError, EDDSA_PEER_POINT_HEX, ERC_5564_SCHEME_ID, FIXED_DOMAIN_MSG, InvalidBindingInputError, InvalidDeviceSecretError, InvalidEcdhSecretError, InvalidMetaStealthAddressError, InvalidPublicKeyLengthError, KEY_DERIVATION_VERSION, KeygenError, META_STEALTH_ADDRESS_LENGTH, META_STEALTH_URI_PREFIX, base64urlToBytes, buildAccountBindingMessage, buildAnnouncement, buildBindingMessages, buildDeviceBindingMessage, bytesToBase64url, bytesToHex, checkStealthAddress, computeConfirmationKeyHash, computeEddsaPeerPoint, computeNetworkId, computeSharedSecret, decodeMetaStealthAddress, deriveAccountKeypair, deriveAuthKeypair, deriveEddsaKeypair, deriveKeys, deriveNetworkId, deriveStealthKeys, eddsaPeerPointBytes, encodeMetaStealthAddress, encodeMetaStealthAddressUri, generateDeviceSecret, generateEphemeralKey, generateStealthAddress, generateStealthPrivateKey, getStealthPublicKey, getViewTag, hashToScalar, hashToScalarWith, hexToBytes, isValidScalar, metaStealthAddressUri, normalizeConfirmationKey, normalizeDeviceSecret, normalizeEcdhSharedSecret, publicKeyToAddress, scalarToKeypair, signAccountBinding, signDerivationMessage, validateMetaStealthAddress, verifyAccountBindingSignature, verifyCredentialBinding, verifyDeviceBindingSignature };
522
+ export { BINDING_NONCE_LENGTH, COMPRESSED_PUBLIC_KEY_LENGTH, DOMAIN_ACCOUNT, DOMAIN_AUTH, DOMAIN_EDDSA, DOMAIN_EDDSA_PEER, DOMAIN_NETWORK_ID, DOMAIN_PAYMASTER, DerivationFailureError, EDDSA_PEER_POINT_HEX, ERC_5564_SCHEME_ID, FIXED_DOMAIN_MSG, InvalidBindingInputError, InvalidDeviceSecretError, InvalidEcdhSecretError, InvalidMetaStealthAddressError, InvalidPublicKeyLengthError, KEY_DERIVATION_VERSION, KeygenError, META_STEALTH_ADDRESS_LENGTH, META_STEALTH_URI_PREFIX, base64urlToBytes, buildAccountBindingMessage, buildAnnouncement, buildBindingMessages, buildDeviceBindingMessage, bytesToBase64url, bytesToHex, checkStealthAddress, computeConfirmationKeyHash, computeEddsaPeerPoint, computeMemberNetworkId, computeNetworkId, computeSharedSecret, decodeMetaStealthAddress, deriveAccountKeypair, deriveAuthKeypair, deriveEddsaKeypair, deriveKeys, deriveNetworkId, derivePaymasterKeypair, deriveStealthKeys, eddsaPeerPointBytes, encodeMetaStealthAddress, encodeMetaStealthAddressUri, generateDeviceSecret, generateEphemeralKey, generateStealthAddress, generateStealthPrivateKey, getStealthPublicKey, getViewTag, hashToScalar, hashToScalarWith, hexToBytes, isValidScalar, metaStealthAddressUri, normalizeConfirmationKey, normalizeDeviceSecret, normalizeEcdhSharedSecret, publicKeyToAddress, scalarToKeypair, signAccountBinding, signDerivationMessage, validateMetaStealthAddress, verifyAccountBindingSignature, verifyCredentialBinding, verifyDeviceBindingSignature };
499
523
  export type { Address, DerivedKeys, EddsaKeypair, HashFn, Hex, KeygenErrorCode, Secp256k1Keypair };