@novasamatech/statement-store 0.8.7-0 → 0.8.7-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.
@@ -4,4 +4,7 @@ export type StatementProver = {
4
4
  generateMessageProof(statement: Statement): ResultAsync<SignedStatement, Error>;
5
5
  verifyMessageProof(statement: Statement): ResultAsync<boolean, Error>;
6
6
  };
7
+ /** Prover for scure-HDKD / device statement account secrets. */
7
8
  export declare function createSr25519Prover(secret: Uint8Array): StatementProver;
9
+ /** Prover for a mobile slot-account secret (`privateKey || nonce`). Call `ensureSubstrateSlotSr25519Ready()` first. */
10
+ export declare function createSlotAccountProver(secret: Uint8Array): StatementProver;
@@ -2,11 +2,11 @@ import { getStatementSigner, statementCodec } from '@novasamatech/sdk-statement'
2
2
  import { compact } from '@polkadot-api/substrate-bindings';
3
3
  import { errAsync, fromPromise, fromThrowable, okAsync } from 'neverthrow';
4
4
  import { fromHex } from 'polkadot-api/utils';
5
- import { deriveSr25519PublicKey, signWithSr25519Secret, verifySr25519Signature } from '../crypto.js';
5
+ import { deriveSlotAccountPublicKey, deriveSr25519PublicKey, signSlotAccountSecret, signWithSr25519Secret, verifySlotAccountSignature, verifySr25519Signature, } from '../crypto.js';
6
6
  import { toError } from '../helpers.js';
7
- export function createSr25519Prover(secret) {
8
- const signer = getStatementSigner(deriveSr25519PublicKey(secret), 'sr25519', data => signWithSr25519Secret(secret, data));
9
- const verify = fromThrowable(verifySr25519Signature, toError);
7
+ function createSr25519SchemeProver(secret, scheme) {
8
+ const signer = getStatementSigner(scheme.derivePublicKey(secret), 'sr25519', data => scheme.sign(secret, data));
9
+ const verify = fromThrowable(scheme.verify, toError);
10
10
  return {
11
11
  generateMessageProof(statement) {
12
12
  return fromPromise(signer.sign(statement), toError);
@@ -27,3 +27,19 @@ export function createSr25519Prover(secret) {
27
27
  },
28
28
  };
29
29
  }
30
+ /** Prover for scure-HDKD / device statement account secrets. */
31
+ export function createSr25519Prover(secret) {
32
+ return createSr25519SchemeProver(secret, {
33
+ derivePublicKey: deriveSr25519PublicKey,
34
+ sign: signWithSr25519Secret,
35
+ verify: verifySr25519Signature,
36
+ });
37
+ }
38
+ /** Prover for a mobile slot-account secret (`privateKey || nonce`). Call `ensureSubstrateSlotSr25519Ready()` first. */
39
+ export function createSlotAccountProver(secret) {
40
+ return createSr25519SchemeProver(secret, {
41
+ derivePublicKey: deriveSlotAccountPublicKey,
42
+ sign: signSlotAccountSecret,
43
+ verify: verifySlotAccountSignature,
44
+ });
45
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,71 @@
1
+ import { createExpiryFromDuration } from '@novasamatech/sdk-statement';
2
+ import { ensureSubstrateSlotSr25519Ready, substrateSlotSecretFromSeedBytes, } from '@novasamatech/substrate-slot-sr25519-wasm';
3
+ import { mnemonicToEntropy, mnemonicToMiniSecret } from '@polkadot-labs/hdkd-helpers';
4
+ import { beforeAll, describe, expect, it } from 'vitest';
5
+ import { createSr25519Secret, deriveSlotAccountPublicKey, deriveSr25519PublicKey } from '../crypto.js';
6
+ import { createSlotAccountProver, createSr25519Prover } from './statementProver.js';
7
+ const DEV_MNEMONIC = 'bottom drive obey lake curtain smoke basket hold race lonely fit walk';
8
+ const toHex = (bytes) => `0x${[...bytes].map(b => b.toString(16).padStart(2, '0')).join('')}`;
9
+ function makeStatement(data) {
10
+ return {
11
+ expiry: createExpiryFromDuration(7 * 24 * 60 * 60),
12
+ data,
13
+ topics: [],
14
+ channel: `0x${'00'.repeat(32)}`,
15
+ };
16
+ }
17
+ function proofSigner(signed) {
18
+ if (signed.proof.type !== 'sr25519') {
19
+ throw new Error(`unexpected proof type ${signed.proof.type}`);
20
+ }
21
+ return signed.proof.value.signer;
22
+ }
23
+ describe('statementProver', () => {
24
+ beforeAll(async () => {
25
+ // Only the slot scheme needs explicit init; the scure path initializes lazily.
26
+ await ensureSubstrateSlotSr25519Ready();
27
+ });
28
+ describe('createSr25519Prover (scure-HDKD secrets)', () => {
29
+ it('signs a statement under the scure-derived public key and verifies it', async () => {
30
+ const secret = createSr25519Secret(mnemonicToEntropy(DEV_MNEMONIC));
31
+ const prover = createSr25519Prover(secret);
32
+ const signed = (await prover.generateMessageProof(makeStatement(new Uint8Array([1, 2, 3]))))._unsafeUnwrap();
33
+ expect(proofSigner(signed)).toBe(toHex(deriveSr25519PublicKey(secret)));
34
+ const verified = await prover.verifyMessageProof(signed);
35
+ expect(verified._unsafeUnwrap()).toBe(true);
36
+ });
37
+ });
38
+ describe('createSlotAccountProver (mobile slot secrets)', () => {
39
+ // Derived inside each test: the wasm is only ready after beforeAll runs.
40
+ const makeSlotSecret = () => substrateSlotSecretFromSeedBytes(mnemonicToMiniSecret(DEV_MNEMONIC));
41
+ it('signs a statement under the slot-derived public key and verifies it', async () => {
42
+ const slotSecret = makeSlotSecret();
43
+ const prover = createSlotAccountProver(slotSecret);
44
+ const signed = (await prover.generateMessageProof(makeStatement(new Uint8Array([4, 5, 6]))))._unsafeUnwrap();
45
+ expect(proofSigner(signed)).toBe(toHex(deriveSlotAccountPublicKey(slotSecret)));
46
+ const verified = await prover.verifyMessageProof(signed);
47
+ expect(verified._unsafeUnwrap()).toBe(true);
48
+ });
49
+ it('signs under a different public key than the scure prover would for the same secret', () => {
50
+ // Regression guard: a slot secret pushed through the scure scheme derives the WRONG
51
+ // signer, which is the bug createSlotAccountProver fixes for getStatementStoreProver.
52
+ const slotSecret = makeSlotSecret();
53
+ expect(toHex(deriveSlotAccountPublicKey(slotSecret))).not.toBe(toHex(deriveSr25519PublicKey(slotSecret)));
54
+ });
55
+ it('rejects a proof whose statement data was tampered with', async () => {
56
+ const slotSecret = makeSlotSecret();
57
+ const prover = createSlotAccountProver(slotSecret);
58
+ const signed = (await prover.generateMessageProof(makeStatement(new Uint8Array([7, 8, 9]))))._unsafeUnwrap();
59
+ const tampered = { ...signed, data: new Uint8Array([9, 9, 9]) };
60
+ const verified = await prover.verifyMessageProof(tampered);
61
+ expect(verified._unsafeUnwrap()).toBe(false);
62
+ });
63
+ });
64
+ describe('verifyMessageProof', () => {
65
+ it('errors when the statement carries no proof', async () => {
66
+ const prover = createSr25519Prover(createSr25519Secret(mnemonicToEntropy(DEV_MNEMONIC)));
67
+ const verified = await prover.verifyMessageProof(makeStatement(new Uint8Array([1])));
68
+ expect(verified.isErr()).toBe(true);
69
+ });
70
+ });
71
+ });
@@ -31,6 +31,18 @@ export type Session = {
31
31
  requestId: string;
32
32
  }, Error>;
33
33
  submitResponseMessage(requestId: string, responseCode: ResponseStatus): ResultAsync<void, Error>;
34
+ /**
35
+ * Subscribe to incoming peer requests and answer each one automatically.
36
+ * The handler returns the transport-level {@link ResponseStatus} (or a
37
+ * `ResultAsync` resolving to one) that the session submits as the response on
38
+ * the peer's behalf; a handler that errors is answered with `'unknown'`.
39
+ *
40
+ * This is the can't-forget counterpart to {@link submitResponseMessage}: the
41
+ * ACK is driven by the handler's return value, so a consumer cannot receive a
42
+ * request and silently fail to respond. Response-type statements are ignored
43
+ * (only requests are answered).
44
+ */
45
+ respondToRequests<T>(codec: Codec<T>, handler: (request: RequestMessage<T>) => ResponseStatus | ResultAsync<ResponseStatus, Error>): VoidFunction;
34
46
  /**
35
47
  * Replace the in-flight outgoing request batch with an empty one on the same
36
48
  * request channel at the session's current expiry (the statement store keeps
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@novasamatech/statement-store",
3
3
  "type": "module",
4
- "version": "0.8.7-0",
4
+ "version": "0.8.7-1",
5
5
  "description": "Statement store integration",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -25,9 +25,9 @@
25
25
  "README.md"
26
26
  ],
27
27
  "dependencies": {
28
- "@novasamatech/scale": "0.8.7-0",
28
+ "@novasamatech/scale": "0.8.7-1",
29
29
  "@novasamatech/sdk-statement": "^0.6.0",
30
- "@novasamatech/substrate-slot-sr25519-wasm": "0.8.7-0",
30
+ "@novasamatech/substrate-slot-sr25519-wasm": "0.8.7-1",
31
31
  "@polkadot-api/substrate-bindings": "^0.20.3",
32
32
  "@polkadot-api/substrate-client": "^0.7.0",
33
33
  "@polkadot-labs/hdkd-helpers": "^0.0.30",