@majikah/majik-signature 0.2.2 → 0.2.4
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/core/constants.d.ts +12 -0
- package/dist/core/constants.js +19 -0
- package/dist/core/embed/majik-embed.d.ts +40 -105
- package/dist/core/embed/majik-embed.js +167 -575
- package/dist/core/envelope.d.ts +228 -0
- package/dist/core/envelope.js +766 -0
- package/dist/core/types.d.ts +3 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/majik-signature.d.ts +4 -3
- package/package.json +3 -3
- package/dist/core/multi-sig.d.ts +0 -107
- package/dist/core/multi-sig.js +0 -240
package/dist/core/constants.d.ts
CHANGED
|
@@ -59,3 +59,15 @@ export declare const MAJIK_TIMESTAMP_VERSION: 1;
|
|
|
59
59
|
export declare const MAJIK_TSA_DOMAIN: "majikah-tsa-v-1:";
|
|
60
60
|
export declare const MAJIK_NOTARY_VERSION: 1;
|
|
61
61
|
export declare const MAJIK_NOTARY_MEMO_DOMAIN: "majik-notary-v-1:";
|
|
62
|
+
export declare const MJKSIG_MAGIC: number[];
|
|
63
|
+
export declare const MJKSIG_MAGIC_LEN: number;
|
|
64
|
+
export declare const MJKSIG_VERSION = 1;
|
|
65
|
+
export declare const MJKSIG_SUPPORTED_VERSIONS: readonly [1];
|
|
66
|
+
export declare const MJKSIG_HEADER_LEN: number;
|
|
67
|
+
/**
|
|
68
|
+
* Proposed IANA media type / conventional file extension for this format.
|
|
69
|
+
* Referenced here so both live in one place ahead of registration —
|
|
70
|
+
* update if/when the registration settles on different values.
|
|
71
|
+
*/
|
|
72
|
+
export declare const MJKSIG_MEDIA_TYPE: "application/vnd.majikah.mjksig";
|
|
73
|
+
export declare const MJKSIG_FILE_EXTENSION: ".mjksig";
|
package/dist/core/constants.js
CHANGED
|
@@ -58,3 +58,22 @@ export const MAJIK_TIMESTAMP_VERSION = 1;
|
|
|
58
58
|
export const MAJIK_TSA_DOMAIN = `majikah-tsa-v-${MAJIK_TIMESTAMP_VERSION}:`;
|
|
59
59
|
export const MAJIK_NOTARY_VERSION = 1;
|
|
60
60
|
export const MAJIK_NOTARY_MEMO_DOMAIN = `majik-notary-v-${MAJIK_NOTARY_VERSION}:`;
|
|
61
|
+
// ─── MJKSIG binary format ───────────────────────────────────────────────────
|
|
62
|
+
//
|
|
63
|
+
// Layout: [magic(6)][version(1)][reserved(1)][payloadLen(4, BE u32)][payload JSON]
|
|
64
|
+
// Header length is fixed at 12 bytes regardless of version — only the
|
|
65
|
+
// payload shape may change between versions, never the header layout.
|
|
66
|
+
// This lets fromMJKSIG() always locate and validate the header before it
|
|
67
|
+
// needs to know anything about the payload's internal shape.
|
|
68
|
+
export const MJKSIG_MAGIC = [0x4d, 0x4a, 0x4b, 0x53, 0x49, 0x47]; // "MJKSIG"
|
|
69
|
+
export const MJKSIG_MAGIC_LEN = MJKSIG_MAGIC.length;
|
|
70
|
+
export const MJKSIG_VERSION = 0x01;
|
|
71
|
+
export const MJKSIG_SUPPORTED_VERSIONS = [MJKSIG_VERSION];
|
|
72
|
+
export const MJKSIG_HEADER_LEN = MJKSIG_MAGIC_LEN + 1 + 1 + 4; // 12
|
|
73
|
+
/**
|
|
74
|
+
* Proposed IANA media type / conventional file extension for this format.
|
|
75
|
+
* Referenced here so both live in one place ahead of registration —
|
|
76
|
+
* update if/when the registration settles on different values.
|
|
77
|
+
*/
|
|
78
|
+
export const MJKSIG_MEDIA_TYPE = "application/vnd.majikah.mjksig";
|
|
79
|
+
export const MJKSIG_FILE_EXTENSION = ".mjksig";
|
|
@@ -10,17 +10,19 @@
|
|
|
10
10
|
*
|
|
11
11
|
* majik-signature → majik-embed → majik-signature ✗
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
* MajikSignatureStaticAdapter interface — no circular import needed.
|
|
13
|
+
* Operations that need MajikSignature (signing, verifying) receive it via
|
|
14
|
+
* the MajikSignatureStaticAdapter interface — no circular import needed.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
16
|
+
* MajikSignatureEnvelope, by contrast, is pure/structural (no crypto), so it
|
|
17
|
+
* IS imported directly here — no adapter required for it. All parsing,
|
|
18
|
+
* validation, allowlist enforcement, seal computation, and signatory/issuer
|
|
19
|
+
* resolution now live on that class (core/envelope.ts). This file is
|
|
20
|
+
* reduced to file-format orchestration: read bytes → resolve handler →
|
|
21
|
+
* extract/strip → delegate to the envelope class → re-embed.
|
|
21
22
|
*/
|
|
22
23
|
import type { MajikKey } from "@majikah/majik-key";
|
|
23
|
-
import type { EmbedOptions, EmbedResult, EnvelopeInfo, ExpectedSigner, ExtractOptions, ExtractResult,
|
|
24
|
+
import type { EmbedOptions, EmbedResult, EnvelopeInfo, ExpectedSigner, ExtractOptions, ExtractResult, MajikSignatureEnvelopeJSON, MajikSignatureJSON, MajikSignerPublicKeys, SealInfo, SealVerificationResult, SignatoriesFilter, SignatoriesResult, SignOptions, VerificationResult } from "../../core/types";
|
|
25
|
+
import { MajikSignatureEnvelope } from "../../core/envelope";
|
|
24
26
|
import { FormatHandlerRegistry } from "./registry";
|
|
25
27
|
import { MajikChainAnchor } from "../../anchor/types";
|
|
26
28
|
export interface MajikSignatureAdapter {
|
|
@@ -38,7 +40,7 @@ export declare class MajikSignatureEmbed {
|
|
|
38
40
|
/**
|
|
39
41
|
* Embed a pre-computed signature into a file Blob.
|
|
40
42
|
* Reads any existing envelope, upserts the new signature by signerId,
|
|
41
|
-
* and writes the updated
|
|
43
|
+
* and writes the updated envelope back.
|
|
42
44
|
* Does NOT sign — call signAndEmbed() for sign + embed together.
|
|
43
45
|
*/
|
|
44
46
|
static embed(file: Blob, signature: MajikSignatureAdapter | MajikSignatureJSON, options?: EmbedOptions): Promise<EmbedResult>;
|
|
@@ -46,15 +48,15 @@ export declare class MajikSignatureEmbed {
|
|
|
46
48
|
* Sign a file and embed the signature in one call.
|
|
47
49
|
*
|
|
48
50
|
* Flow:
|
|
49
|
-
* 1.
|
|
50
|
-
* 2.
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* 6.
|
|
56
|
-
*
|
|
57
|
-
*
|
|
51
|
+
* 1. Read existing envelope (or start fresh)
|
|
52
|
+
* 2. assertCanSign() — rejects sealed envelopes and non-allowlisted signers
|
|
53
|
+
* before any cryptographic operation (issuer always bypasses)
|
|
54
|
+
* 3. Strip existing envelope to get clean original bytes
|
|
55
|
+
* 4. Resolve allowlistHash for this signer (establishing / re-signing / none)
|
|
56
|
+
* 5. Sign the clean bytes
|
|
57
|
+
* 6. If establishing an allowlist, attach it BEFORE upserting the signature
|
|
58
|
+
* (withAllowlist() requires zero existing signatures — see note below)
|
|
59
|
+
* 7. Upsert signature into envelope
|
|
58
60
|
* 8. Embed updated envelope back into the file
|
|
59
61
|
*/
|
|
60
62
|
static signAndEmbed<T extends MajikSignatureAdapter>(file: Blob, key: MajikKey, MajikSig: MajikSignatureStaticAdapter, options?: EmbedOptions & {
|
|
@@ -67,15 +69,13 @@ export declare class MajikSignatureEmbed {
|
|
|
67
69
|
}>;
|
|
68
70
|
/**
|
|
69
71
|
* Sign a file and return the envelope detached.
|
|
70
|
-
*
|
|
71
|
-
* signs the stripped bytes, and returns the updated MultiSigEnvelope.
|
|
72
|
-
* Does NOT embed the envelope back into the file.
|
|
72
|
+
* Same flow as signAndEmbed(), minus the final embed step.
|
|
73
73
|
*/
|
|
74
74
|
static signDetached(file: Blob, key: MajikKey, MajikSig: MajikSignatureStaticAdapter, options?: EmbedOptions & {
|
|
75
75
|
contentType?: string;
|
|
76
76
|
timestamp?: string;
|
|
77
77
|
expectedSigners?: ExpectedSigner[];
|
|
78
|
-
existingEnvelope?: MajikSignatureEnvelope;
|
|
78
|
+
existingEnvelope?: MajikSignatureEnvelope | MajikSignatureEnvelopeJSON | Uint8Array | Blob;
|
|
79
79
|
}, debug?: boolean): Promise<{
|
|
80
80
|
blob: Blob;
|
|
81
81
|
envelope: MajikSignatureEnvelope;
|
|
@@ -83,15 +83,13 @@ export declare class MajikSignatureEmbed {
|
|
|
83
83
|
mimeType: string;
|
|
84
84
|
}>;
|
|
85
85
|
/**
|
|
86
|
-
* Extract the
|
|
86
|
+
* Extract the envelope from a file as a MajikSignatureEnvelope instance.
|
|
87
87
|
* Returns null if no signature is found.
|
|
88
|
-
* Old single-sig files are promoted to MultiSigEnvelope transparently.
|
|
89
88
|
*/
|
|
90
89
|
static extract(file: Blob, options?: ExtractOptions): Promise<ExtractResult | null>;
|
|
91
90
|
/**
|
|
92
91
|
* Verify a file's embedded signatures against public keys.
|
|
93
92
|
* Returns one VerificationResult per signature in the envelope.
|
|
94
|
-
* Old single-sig files return a single-item array.
|
|
95
93
|
*/
|
|
96
94
|
static verify(file: Blob, publicKeys: MajikSignerPublicKeys, MajikSig: MajikSignatureStaticAdapter, options?: ExtractOptions & {
|
|
97
95
|
expectedSignerId?: string;
|
|
@@ -100,29 +98,19 @@ export declare class MajikSignatureEmbed {
|
|
|
100
98
|
expectedSignerId?: string;
|
|
101
99
|
}, debug?: boolean): Promise<VerificationResult[]>;
|
|
102
100
|
/**
|
|
103
|
-
* Verify a file against a provided, detached
|
|
104
|
-
*
|
|
101
|
+
* Verify a file against a provided, detached envelope (instance, blob or JSON).
|
|
102
|
+
* Still strips the file in case it also contains an embedded envelope,
|
|
105
103
|
* ensuring verification runs against the clean original bytes.
|
|
106
104
|
*/
|
|
107
|
-
static verifyDetached(file: Blob,
|
|
105
|
+
static verifyDetached(file: Blob, envelopeInput: MajikSignatureEnvelope | MajikSignatureEnvelopeJSON | Uint8Array | Blob, publicKeys: MajikSignerPublicKeys, MajikSig: MajikSignatureStaticAdapter, options?: ExtractOptions & {
|
|
108
106
|
expectedSignerId?: string;
|
|
109
107
|
}, debug?: boolean): Promise<VerificationResult[]>;
|
|
110
|
-
static verifyDetachedWithKey(file: Blob,
|
|
108
|
+
static verifyDetachedWithKey(file: Blob, envelopeInput: MajikSignatureEnvelope | MajikSignatureEnvelopeJSON | Uint8Array | Blob, key: MajikKey, MajikSig: MajikSignatureStaticAdapter, options?: ExtractOptions & {
|
|
111
109
|
expectedSignerId?: string;
|
|
112
110
|
}, debug?: boolean): Promise<VerificationResult[]>;
|
|
113
111
|
/**
|
|
114
112
|
* Seal a multi-sig envelope, preventing any further signatures.
|
|
115
|
-
*
|
|
116
|
-
* Rules:
|
|
117
|
-
* - Only the issuer (allowlistSignerId) may seal
|
|
118
|
-
* - The envelope must have an allowlist (must be a restricted multi-sig file)
|
|
119
|
-
* - The envelope must not already be sealed
|
|
120
|
-
* - The key must be unlocked
|
|
121
|
-
*
|
|
122
|
-
* The seal hash is SHA3-512 of all current signatories + sealTimestamp,
|
|
123
|
-
* prefixed with MAJIK_SEAL_DOMAIN. It is stored in the envelope alongside
|
|
124
|
-
* the sealTimestamp and sealedBy fields. No new cryptographic signature
|
|
125
|
-
* is produced — the seal is a hash-based integrity lock.
|
|
113
|
+
* Issuer-only / already-sealed checks are enforced by envelope.withSeal().
|
|
126
114
|
*/
|
|
127
115
|
static seal(file: Blob, key: MajikKey, options?: ExtractOptions & {
|
|
128
116
|
timestamp?: string;
|
|
@@ -132,94 +120,41 @@ export declare class MajikSignatureEmbed {
|
|
|
132
120
|
handler: string;
|
|
133
121
|
mimeType: string;
|
|
134
122
|
}>;
|
|
135
|
-
/**
|
|
136
|
-
* Verify the seal hash against the current signatories and sealTimestamp.
|
|
137
|
-
* Returns invalid if the envelope is not sealed.
|
|
138
|
-
* Does NOT verify the individual cryptographic signatures — call verify() for that.
|
|
139
|
-
*/
|
|
140
123
|
static verifySeal(file: Blob, options?: ExtractOptions): Promise<SealVerificationResult>;
|
|
141
|
-
/**
|
|
142
|
-
* Return seal metadata without verifying.
|
|
143
|
-
* Returns null if the envelope is not sealed or has no envelope.
|
|
144
|
-
*/
|
|
145
124
|
static getSealInfo(file: Blob, options?: ExtractOptions): Promise<SealInfo | null>;
|
|
146
|
-
/**
|
|
147
|
-
* Returns true if the file has a sealed envelope (structural check only).
|
|
148
|
-
* Does not verify the seal hash.
|
|
149
|
-
*/
|
|
150
125
|
static isSealed(file: Blob, options?: ExtractOptions): Promise<boolean>;
|
|
151
|
-
/**
|
|
152
|
-
* Returns true when the file has a restricted multi-sig envelope
|
|
153
|
-
* (allowlist present with more than one expected signer).
|
|
154
|
-
* Returns false for unsigned files, open-signing files, or single-signer files.
|
|
155
|
-
*/
|
|
156
126
|
static isMultiSig(file: Blob, options?: ExtractOptions): Promise<boolean>;
|
|
157
|
-
/**
|
|
158
|
-
* Check whether a MajikKey is permitted to add a signature to this file.
|
|
159
|
-
*
|
|
160
|
-
* Returns false (with a reason) when:
|
|
161
|
-
* - The file is sealed
|
|
162
|
-
* - The file has an allowlist and the key is not on it (all three fields checked)
|
|
163
|
-
*
|
|
164
|
-
* Returns true when:
|
|
165
|
-
* - The file has no envelope (unsigned — anyone may sign)
|
|
166
|
-
* - The file has no allowlist (open signing — anyone may sign)
|
|
167
|
-
* - The key is on the allowlist
|
|
168
|
-
*
|
|
169
|
-
* Always requires a full MajikKey — fingerprint-only checks are not supported
|
|
170
|
-
* because they cannot verify the public key fields required by the allowlist.
|
|
171
|
-
*/
|
|
172
127
|
static canSign(file: Blob, key: MajikKey, options?: ExtractOptions): Promise<{
|
|
173
128
|
permitted: boolean;
|
|
174
129
|
reason?: string;
|
|
175
130
|
}>;
|
|
176
|
-
/**
|
|
177
|
-
* Core signatories method. Returns all, signed, and pending arrays.
|
|
178
|
-
* Pass filter to narrow the return — the filtered array is still returned
|
|
179
|
-
* inside the full SignatoriesResult so callers always have the complete picture.
|
|
180
|
-
*
|
|
181
|
-
* Returns null if the file has no envelope.
|
|
182
|
-
*/
|
|
183
131
|
static getSignatories(file: Blob, options?: ExtractOptions, filter?: SignatoriesFilter): Promise<SignatoriesResult | null>;
|
|
184
|
-
/**
|
|
185
|
-
* Return the issuer (the signer who established the allowlist and controls sealing).
|
|
186
|
-
* Returns null for open-signing files or unsigned files.
|
|
187
|
-
*/
|
|
188
132
|
static getIssuer(file: Blob, options?: ExtractOptions): Promise<import("../../core/types").SignatoryInfo | null>;
|
|
189
|
-
/**
|
|
190
|
-
* Return a full summary of the envelope state in a single file read.
|
|
191
|
-
* Useful for rendering UI state (badge, status, signatories list) without
|
|
192
|
-
* making multiple separate calls.
|
|
193
|
-
* Returns null if the file has no envelope.
|
|
194
|
-
*/
|
|
195
133
|
static getEnvelopeInfo(file: Blob, options?: ExtractOptions): Promise<EnvelopeInfo | null>;
|
|
196
134
|
static strip(file: Blob, options?: ExtractOptions): Promise<Blob>;
|
|
197
135
|
static hasSignature(file: Blob, options?: ExtractOptions): Promise<boolean>;
|
|
198
136
|
static getAllowlist(file: Blob, options?: ExtractOptions): Promise<ExpectedSigner[] | null>;
|
|
199
137
|
static readonly registry: FormatHandlerRegistry;
|
|
200
138
|
static listHandlers(): string[];
|
|
201
|
-
/**
|
|
202
|
-
* Check whether a file is eligible for chain anchoring.
|
|
203
|
-
* Anchoring requires a sealed envelope — permitted = isSealed(file).
|
|
204
|
-
* Chain-agnostic: does not know or care which chain the caller intends to use.
|
|
205
|
-
*/
|
|
206
139
|
static canAnchor(file: Blob, options?: ExtractOptions): Promise<{
|
|
207
140
|
permitted: boolean;
|
|
208
141
|
reason?: string;
|
|
209
142
|
}>;
|
|
210
143
|
/**
|
|
211
144
|
* Embed an already-confirmed chain anchor into the envelope.
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* Defensive checks (beyond the master plan's minimum):
|
|
215
|
-
* - File must be sealed
|
|
216
|
-
* - anchor.payload.digest.value must match the envelope's current sealHash —
|
|
217
|
-
* catches a caller accidentally embedding an anchor computed against a
|
|
218
|
-
* stale seal (e.g. file was re-sealed between notarize() and this call)
|
|
219
|
-
* - Upserts by anchor.id rather than blind-pushing, so a duplicate call
|
|
220
|
-
* with the same anchor (e.g. retried after a network blip) doesn't
|
|
221
|
-
* produce two entries for the same on-chain transaction
|
|
145
|
+
* Sealed check, digest match, and upsert-by-id dedup are all enforced by
|
|
146
|
+
* envelope.withChainAnchor().
|
|
222
147
|
*/
|
|
223
148
|
static registerChainAnchor(file: Blob, anchor: MajikChainAnchor, options?: ExtractOptions): Promise<EmbedResult>;
|
|
224
149
|
static getChainAnchors(file: Blob, options?: ExtractOptions): Promise<MajikChainAnchor[]>;
|
|
150
|
+
private static _prepare;
|
|
151
|
+
/** Extract + parse, or a fresh empty envelope when none exists. */
|
|
152
|
+
private static _readEnvelope;
|
|
153
|
+
private static _noSignatureResult;
|
|
154
|
+
/**
|
|
155
|
+
* Shared by verify() and verifyDetached(): filter by expectedSignerId,
|
|
156
|
+
* verify each remaining signature, and stamp the handler name onto each
|
|
157
|
+
* result. Previously duplicated near-verbatim in both methods.
|
|
158
|
+
*/
|
|
159
|
+
private static _verifySignatures;
|
|
225
160
|
}
|