@majikah/majik-signature-client 1.0.0 → 1.0.2

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.
@@ -207,6 +207,7 @@ export declare class MajikSignatureClient extends MajikKeyClient<MajikContact, M
207
207
  private _history;
208
208
  /** User activity log manager used for non-blocking activity records. */
209
209
  private _activity;
210
+ private _signingSessionDepth;
210
211
  /**
211
212
  * Creates a Majik Signature client with the supplied managers and storage adapters.
212
213
  *
@@ -967,6 +968,7 @@ export declare class MajikSignatureClient extends MajikKeyClient<MajikContact, M
967
968
  priorSignedFile?: Blob;
968
969
  /** Optional note attached to this specific revision. */
969
970
  message?: string;
971
+ saveHistory?: boolean;
970
972
  }, source?: HistorySource): Promise<Awaited<ReturnType<typeof MajikSignature.signFile>>>;
971
973
  /**
972
974
  * Sign a file with a detached signature envelope.
@@ -1006,8 +1008,15 @@ export declare class MajikSignatureClient extends MajikKeyClient<MajikContact, M
1006
1008
  timestamp?: string;
1007
1009
  mimeType?: string;
1008
1010
  validUntil?: string;
1011
+ /** Pre-stamp original, when the current file's embedded envelope was
1012
+ * destroyed by a wholesale re-encode (PDF flatten, image re-render,
1013
+ * audio re-mux). Lets the prior signature chain be recovered. */
1014
+ priorSignedFile?: Blob;
1009
1015
  }>, options?: {
1010
1016
  accountId?: string;
1017
+ expectedSigners?: ExpectedSigner[];
1018
+ /** Optional note attached to this specific revision. */
1019
+ message?: string;
1011
1020
  }, source?: HistorySource): Promise<Array<{
1012
1021
  blob: Blob | null;
1013
1022
  signature: MajikSignature | null;
@@ -1377,6 +1386,7 @@ export declare class MajikSignatureClient extends MajikKeyClient<MajikContact, M
1377
1386
  mimeType?: string;
1378
1387
  timestamp?: string;
1379
1388
  accountId?: string;
1389
+ saveHistory?: boolean;
1380
1390
  }, source?: HistorySource): Promise<Awaited<ReturnType<typeof MajikSignature.seal>>>;
1381
1391
  /**
1382
1392
  * Verify the seal hash against the current signatories and seal timestamp.
@@ -1695,6 +1705,13 @@ export declare class MajikSignatureClient extends MajikKeyClient<MajikContact, M
1695
1705
  * @throws {Error} When validation fails, required local data is unavailable, or the underlying operation cannot be completed.
1696
1706
  */
1697
1707
  private _withSigningKey;
1708
+ /**
1709
+ * Unlock once, run `fn` (which may call signFile/seal/etc. any number of
1710
+ * times), then re-lock once at the end if one-time unlock is disabled.
1711
+ */
1712
+ withSigningSession<T>(accountId: string | undefined, fn: () => Promise<T>): Promise<T>;
1713
+ get isSigningSessionActive(): boolean;
1714
+ forceEndSigningSession(): void;
1698
1715
  /**
1699
1716
  * Computes a SHA-256 digest and encodes it as base64.
1700
1717
  * @param bytes - Value used by the _sha256 base64 operation.
@@ -70,6 +70,7 @@ export class MajikSignatureClient extends MajikKeyClient {
70
70
  _history;
71
71
  /** User activity log manager used for non-blocking activity records. */
72
72
  _activity;
73
+ _signingSessionDepth = 0;
73
74
  /**
74
75
  * Creates a Majik Signature client with the supplied managers and storage adapters.
75
76
  *
@@ -1439,6 +1440,7 @@ export class MajikSignatureClient extends MajikKeyClient {
1439
1440
  message: options?.message,
1440
1441
  });
1441
1442
  const signedBytes = new Uint8Array(await signedResponse.blob.arrayBuffer());
1443
+ const recordHistory = options?.saveHistory ?? true;
1442
1444
  this._recordHistory(key.fingerprint, {
1443
1445
  reference_id: signedResponse.signature.contentHash,
1444
1446
  historyType: HistoryTypes.SIGN,
@@ -1450,8 +1452,8 @@ export class MajikSignatureClient extends MajikKeyClient {
1450
1452
  sealed: false,
1451
1453
  tsa: false,
1452
1454
  },
1453
- signerCount: 1,
1454
- data: signedBytes,
1455
+ signerCount: signedResponse.envelope.signatures.length,
1456
+ data: recordHistory ? signedBytes : undefined,
1455
1457
  identity: this._identityFromKey(key),
1456
1458
  }).catch((err) => console.warn(err));
1457
1459
  return signedResponse;
@@ -1510,13 +1512,16 @@ export class MajikSignatureClient extends MajikKeyClient {
1510
1512
  * @returns The result of the batch sign files operation (`Promise< Array<{ blob: Blob | null; signature: MajikSignature | null; serialized: string | null; handler: string | null; mimeType: string | null; error: Error | null; }> >`).
1511
1513
  */
1512
1514
  async batchSignFiles(files, options, source = HistorySources.SYSTEM) {
1513
- return this._withSigningKey(options?.accountId, "batchSignFiles", (key) => Promise.all(files.map(async ({ file, contentType, timestamp, mimeType, validUntil }) => {
1515
+ return this._withSigningKey(options?.accountId, "batchSignFiles", (key) => Promise.all(files.map(async ({ file, contentType, timestamp, mimeType, validUntil, priorSignedFile, }) => {
1514
1516
  try {
1515
1517
  const result = await MajikSignature.signFile(file, key, {
1516
1518
  contentType,
1517
1519
  timestamp,
1518
1520
  mimeType,
1519
1521
  validUntil,
1522
+ priorSignedFile,
1523
+ expectedSigners: options?.expectedSigners,
1524
+ message: options?.message,
1520
1525
  });
1521
1526
  this._recordHistory(key.fingerprint, {
1522
1527
  reference_id: result.signature.contentHash,
@@ -2317,6 +2322,7 @@ export class MajikSignatureClient extends MajikKeyClient {
2317
2322
  timestamp: options?.timestamp,
2318
2323
  });
2319
2324
  const sealedBytes = new Uint8Array(await sealResult.blob.arrayBuffer());
2325
+ const recordHistory = options?.saveHistory ?? true;
2320
2326
  this._recordHistory(key.fingerprint, {
2321
2327
  reference_id: sealResult.sealInfo.sealHash,
2322
2328
  historyType: HistoryTypes.SEAL,
@@ -2328,7 +2334,7 @@ export class MajikSignatureClient extends MajikKeyClient {
2328
2334
  sealed: true,
2329
2335
  tsa: false,
2330
2336
  },
2331
- data: sealedBytes,
2337
+ data: recordHistory ? sealedBytes : undefined,
2332
2338
  identity: this._identityFromKey(key),
2333
2339
  }).catch((err) => console.warn(err));
2334
2340
  return sealResult;
@@ -3065,7 +3071,9 @@ export class MajikSignatureClient extends MajikKeyClient {
3065
3071
  throw new Error(`Account "${id}" has no signing keys. ` +
3066
3072
  `Re-import via importAccountFromMnemonicBackup() to enable signing.`);
3067
3073
  }
3068
- shouldRelock = !(await this.isOnetimeUnlockEnabled());
3074
+ shouldRelock =
3075
+ this._signingSessionDepth === 0 &&
3076
+ !(await this.isOnetimeUnlockEnabled());
3069
3077
  return await fn(key);
3070
3078
  }
3071
3079
  catch (err) {
@@ -3077,6 +3085,42 @@ export class MajikSignatureClient extends MajikKeyClient {
3077
3085
  key?.lock();
3078
3086
  }
3079
3087
  }
3088
+ /**
3089
+ * Unlock once, run `fn` (which may call signFile/seal/etc. any number of
3090
+ * times), then re-lock once at the end if one-time unlock is disabled.
3091
+ */
3092
+ async withSigningSession(accountId, fn) {
3093
+ const id = accountId ?? this.getActiveAccount()?.id;
3094
+ if (!id)
3095
+ throw new Error("No active account — call setActiveAccount() first");
3096
+ await this._keys.ensureUnlocked(id);
3097
+ this._signingSessionDepth++;
3098
+ try {
3099
+ return await fn();
3100
+ }
3101
+ finally {
3102
+ this._signingSessionDepth = Math.max(0, this._signingSessionDepth - 1);
3103
+ if (this._signingSessionDepth === 0) {
3104
+ try {
3105
+ if (!(await this.isOnetimeUnlockEnabled())) {
3106
+ this._keys.get(id)?.lock();
3107
+ }
3108
+ }
3109
+ catch (err) {
3110
+ // Fail closed: if we can't read the preference, lock anyway.
3111
+ this._keys.get(id)?.lock();
3112
+ console.warn("withSigningSession: relock check failed", err);
3113
+ }
3114
+ }
3115
+ }
3116
+ }
3117
+ get isSigningSessionActive() {
3118
+ return this._signingSessionDepth > 0;
3119
+ }
3120
+ forceEndSigningSession() {
3121
+ this._signingSessionDepth = 0;
3122
+ return;
3123
+ }
3080
3124
  /**
3081
3125
  * Computes a SHA-256 digest and encodes it as base64.
3082
3126
  * @param bytes - Value used by the _sha256 base64 operation.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@majikah/majik-signature-client",
3
3
  "type": "module",
4
4
  "description": "Majik Signature Client is a hybrid post-quantum content signing and verification library for the Majikah ecosystem. Built on top of Majik Key Client, it provides tamper-proof, forgery-resistant digital signatures for any content format — using a dual-algorithm architecture that combines classical Ed25519 with post-quantum ML-DSA-87 (FIPS-204).",
5
- "version": "1.0.0",
5
+ "version": "1.0.2",
6
6
  "license": "Apache-2.0",
7
7
  "author": "Zelijah",
8
8
  "main": "./dist/index.js",