@majikah/majik-signature 0.2.1 → 0.2.3
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/embed/handlers/mp4.js +47 -43
- package/dist/core/embed/majik-embed.d.ts +61 -97
- package/dist/core/embed/majik-embed.js +211 -467
- package/dist/core/envelope.d.ts +181 -0
- package/dist/core/envelope.js +628 -0
- package/dist/core/types.d.ts +3 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/majik-signature.d.ts +33 -7
- package/dist/majik-signature.js +28 -1
- package/package.json +3 -3
- package/dist/core/multi-sig.d.ts +0 -107
- package/dist/core/multi-sig.js +0 -240
|
@@ -42,58 +42,58 @@ export class Mp4Handler {
|
|
|
42
42
|
async embed(bytes, signatureJson) {
|
|
43
43
|
const clean = await this.strip(bytes);
|
|
44
44
|
const majkBox = this._buildBox(MP4_BOX_TYPE, textEncode(signatureJson));
|
|
45
|
-
const boxes = this._parseBoxes(clean, 0, clean.length);
|
|
45
|
+
const { boxes, trailing: topTrailing } = this._parseBoxes(clean, 0, clean.length);
|
|
46
46
|
const moovIdx = boxes.findIndex((b) => b.type === "moov");
|
|
47
47
|
if (moovIdx < 0) {
|
|
48
|
-
// No moov box — use trailer fallback
|
|
49
48
|
const { appendTrailer } = await import("../utils");
|
|
50
49
|
return appendTrailer(clean, signatureJson);
|
|
51
50
|
}
|
|
52
51
|
const moovBox = boxes[moovIdx];
|
|
53
|
-
const moovChildren = this._parseBoxes(moovBox.data, 0, moovBox.data.length);
|
|
52
|
+
const { boxes: moovChildren, trailing: moovTrailing } = this._parseBoxes(moovBox.data, 0, moovBox.data.length);
|
|
54
53
|
const udtaIdx = moovChildren.findIndex((b) => b.type === "udta");
|
|
55
|
-
|
|
54
|
+
const { boxes: existingUdtaChildren, trailing: udtaTrailing } = udtaIdx >= 0
|
|
55
|
+
? this._parseBoxes(moovChildren[udtaIdx].data, 0, moovChildren[udtaIdx].data.length)
|
|
56
|
+
: { boxes: [], trailing: new Uint8Array(0) };
|
|
57
|
+
const newUdta = this._buildBox("udta", concatBytes(...existingUdtaChildren.map((b) => b.raw), majkBox, // ← box-aligned, right after real children
|
|
58
|
+
udtaTrailing));
|
|
59
|
+
const newMoovChildren = [];
|
|
56
60
|
if (udtaIdx >= 0) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
// Retain the exact original index of the udta box to avoid byte shifting
|
|
62
|
+
for (let i = 0; i < moovChildren.length; i++) {
|
|
63
|
+
newMoovChildren.push(i === udtaIdx ? newUdta : moovChildren[i].raw);
|
|
64
|
+
}
|
|
61
65
|
}
|
|
62
66
|
else {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (i === udtaIdx)
|
|
69
|
-
continue; // skip old udta (we'll add new one)
|
|
70
|
-
newMoovChildren.push(moovChildren[i].raw);
|
|
67
|
+
// If it didn't exist previously, it's safe to append it to the end
|
|
68
|
+
for (let i = 0; i < moovChildren.length; i++) {
|
|
69
|
+
newMoovChildren.push(moovChildren[i].raw);
|
|
70
|
+
}
|
|
71
|
+
newMoovChildren.push(newUdta);
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
-
const newMoov = this._buildBox("moov", concatBytes(...newMoovChildren));
|
|
74
|
-
// Rebuild file
|
|
73
|
+
const newMoov = this._buildBox("moov", concatBytes(...newMoovChildren, moovTrailing));
|
|
75
74
|
const parts = [];
|
|
76
75
|
for (let i = 0; i < boxes.length; i++) {
|
|
77
|
-
|
|
78
|
-
parts.push(newMoov);
|
|
79
|
-
else
|
|
80
|
-
parts.push(boxes[i].raw);
|
|
76
|
+
parts.push(i === moovIdx ? newMoov : boxes[i].raw);
|
|
81
77
|
}
|
|
82
|
-
return concatBytes(...parts);
|
|
78
|
+
return concatBytes(...parts, topTrailing);
|
|
83
79
|
}
|
|
84
80
|
async extract(bytes) {
|
|
85
81
|
if (!this.canHandle(bytes))
|
|
86
82
|
return null;
|
|
87
83
|
try {
|
|
88
|
-
const boxes = this._parseBoxes(bytes, 0, bytes.length);
|
|
84
|
+
const { boxes } = this._parseBoxes(bytes, 0, bytes.length);
|
|
89
85
|
const moov = boxes.find((b) => b.type === "moov");
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
// Fallback: If no moov box exists, check for a Tier-2 trailer
|
|
87
|
+
if (!moov) {
|
|
88
|
+
const { extractTrailer } = await import("../utils");
|
|
89
|
+
const trailer = extractTrailer(bytes);
|
|
90
|
+
return trailer ? trailer.signatureJson : null;
|
|
91
|
+
}
|
|
92
|
+
const { boxes: moovChildren } = this._parseBoxes(moov.data, 0, moov.data.length);
|
|
93
93
|
const udta = moovChildren.find((b) => b.type === "udta");
|
|
94
94
|
if (!udta)
|
|
95
95
|
return null;
|
|
96
|
-
const udtaChildren = this._parseBoxes(udta.data, 0, udta.data.length);
|
|
96
|
+
const { boxes: udtaChildren } = this._parseBoxes(udta.data, 0, udta.data.length);
|
|
97
97
|
const majk = udtaChildren.find((b) => b.type === MP4_BOX_TYPE);
|
|
98
98
|
if (!majk)
|
|
99
99
|
return null;
|
|
@@ -107,32 +107,36 @@ export class Mp4Handler {
|
|
|
107
107
|
if (!this.canHandle(bytes))
|
|
108
108
|
return bytes;
|
|
109
109
|
try {
|
|
110
|
-
const boxes = this._parseBoxes(bytes, 0, bytes.length);
|
|
110
|
+
const { boxes, trailing: topTrailing } = this._parseBoxes(bytes, 0, bytes.length);
|
|
111
111
|
const moovIdx = boxes.findIndex((b) => b.type === "moov");
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
// Fallback: If no moov box exists, strip the Tier-2 trailer if present
|
|
113
|
+
if (moovIdx < 0) {
|
|
114
|
+
const { extractTrailer } = await import("../utils");
|
|
115
|
+
const trailer = extractTrailer(bytes);
|
|
116
|
+
return trailer ? trailer.original : bytes;
|
|
117
|
+
}
|
|
114
118
|
const moovBox = boxes[moovIdx];
|
|
115
|
-
const moovChildren = this._parseBoxes(moovBox.data, 0, moovBox.data.length);
|
|
119
|
+
const { boxes: moovChildren, trailing: moovTrailing } = this._parseBoxes(moovBox.data, 0, moovBox.data.length);
|
|
116
120
|
const udtaIdx = moovChildren.findIndex((b) => b.type === "udta");
|
|
117
121
|
if (udtaIdx < 0)
|
|
118
122
|
return bytes;
|
|
119
123
|
const udtaBox = moovChildren[udtaIdx];
|
|
120
|
-
const udtaChildren = this._parseBoxes(udtaBox.data, 0, udtaBox.data.length);
|
|
124
|
+
const { boxes: udtaChildren, trailing: udtaTrailing } = this._parseBoxes(udtaBox.data, 0, udtaBox.data.length);
|
|
121
125
|
const filteredUdta = udtaChildren.filter((b) => b.type !== MP4_BOX_TYPE);
|
|
122
126
|
let newMoovChildren;
|
|
123
|
-
if
|
|
124
|
-
|
|
127
|
+
// Only remove the udta box entirely if it is completely barren of data and trailing bytes
|
|
128
|
+
if (filteredUdta.length === 0 && udtaTrailing.length === 0) {
|
|
125
129
|
newMoovChildren = moovChildren
|
|
126
130
|
.filter((_, i) => i !== udtaIdx)
|
|
127
131
|
.map((b) => b.raw);
|
|
128
132
|
}
|
|
129
133
|
else {
|
|
130
|
-
const newUdta = this._buildBox("udta", concatBytes(...filteredUdta.map((b) => b.raw)));
|
|
134
|
+
const newUdta = this._buildBox("udta", concatBytes(...filteredUdta.map((b) => b.raw), udtaTrailing));
|
|
131
135
|
newMoovChildren = moovChildren.map((b, i) => i === udtaIdx ? newUdta : b.raw);
|
|
132
136
|
}
|
|
133
|
-
const newMoov = this._buildBox("moov", concatBytes(...newMoovChildren));
|
|
137
|
+
const newMoov = this._buildBox("moov", concatBytes(...newMoovChildren, moovTrailing));
|
|
134
138
|
const parts = boxes.map((b, i) => (i === moovIdx ? newMoov : b.raw));
|
|
135
|
-
return concatBytes(...parts);
|
|
139
|
+
return concatBytes(...parts, topTrailing);
|
|
136
140
|
}
|
|
137
141
|
catch {
|
|
138
142
|
return bytes;
|
|
@@ -146,14 +150,11 @@ export class Mp4Handler {
|
|
|
146
150
|
let size = readUint32BE(bytes, offset);
|
|
147
151
|
const type = textDecode(bytes.slice(offset + 4, offset + 8));
|
|
148
152
|
if (size === 1) {
|
|
149
|
-
// 64-bit extended size (large box)
|
|
150
|
-
// Read as two 32-bit values
|
|
151
153
|
const hi = readUint32BE(bytes, offset + 8);
|
|
152
154
|
const lo = readUint32BE(bytes, offset + 12);
|
|
153
155
|
size = hi * 0x100000000 + lo;
|
|
154
156
|
}
|
|
155
157
|
else if (size === 0) {
|
|
156
|
-
// Box extends to end of file
|
|
157
158
|
size = end - offset;
|
|
158
159
|
}
|
|
159
160
|
if (size < 8 || offset + size > end)
|
|
@@ -164,7 +165,10 @@ export class Mp4Handler {
|
|
|
164
165
|
boxes.push({ type, data, raw });
|
|
165
166
|
offset += size;
|
|
166
167
|
}
|
|
167
|
-
|
|
168
|
+
// Anything left over (padding, malformed box, unparseable trailing bytes)
|
|
169
|
+
// — preserve it verbatim instead of silently dropping it.
|
|
170
|
+
const trailing = bytes.slice(offset, end);
|
|
171
|
+
return { boxes, trailing };
|
|
168
172
|
}
|
|
169
173
|
_buildBox(type, data) {
|
|
170
174
|
const size = 8 + data.length;
|
|
@@ -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, MajikSignatureJSON, MajikSignerPublicKeys, SealInfo, SealVerificationResult, SignatoriesFilter, SignatoriesResult, SignOptions, VerificationResult } from "../../core/types";
|
|
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 & {
|
|
@@ -63,17 +65,31 @@ export declare class MajikSignatureEmbed {
|
|
|
63
65
|
expectedSigners?: ExpectedSigner[];
|
|
64
66
|
}, debug?: boolean): Promise<EmbedResult & {
|
|
65
67
|
signature: T;
|
|
68
|
+
envelope: MajikSignatureEnvelope;
|
|
66
69
|
}>;
|
|
67
70
|
/**
|
|
68
|
-
*
|
|
71
|
+
* Sign a file and return the envelope detached.
|
|
72
|
+
* Same flow as signAndEmbed(), minus the final embed step.
|
|
73
|
+
*/
|
|
74
|
+
static signDetached(file: Blob, key: MajikKey, MajikSig: MajikSignatureStaticAdapter, options?: EmbedOptions & {
|
|
75
|
+
contentType?: string;
|
|
76
|
+
timestamp?: string;
|
|
77
|
+
expectedSigners?: ExpectedSigner[];
|
|
78
|
+
existingEnvelope?: MajikSignatureEnvelope | MajikSignatureEnvelopeJSON;
|
|
79
|
+
}, debug?: boolean): Promise<{
|
|
80
|
+
blob: Blob;
|
|
81
|
+
envelope: MajikSignatureEnvelope;
|
|
82
|
+
handler: string;
|
|
83
|
+
mimeType: string;
|
|
84
|
+
}>;
|
|
85
|
+
/**
|
|
86
|
+
* Extract the envelope from a file as a MajikSignatureEnvelope instance.
|
|
69
87
|
* Returns null if no signature is found.
|
|
70
|
-
* Old single-sig files are promoted to MultiSigEnvelope transparently.
|
|
71
88
|
*/
|
|
72
89
|
static extract(file: Blob, options?: ExtractOptions): Promise<ExtractResult | null>;
|
|
73
90
|
/**
|
|
74
91
|
* Verify a file's embedded signatures against public keys.
|
|
75
92
|
* Returns one VerificationResult per signature in the envelope.
|
|
76
|
-
* Old single-sig files return a single-item array.
|
|
77
93
|
*/
|
|
78
94
|
static verify(file: Blob, publicKeys: MajikSignerPublicKeys, MajikSig: MajikSignatureStaticAdapter, options?: ExtractOptions & {
|
|
79
95
|
expectedSignerId?: string;
|
|
@@ -81,19 +97,20 @@ export declare class MajikSignatureEmbed {
|
|
|
81
97
|
static verifyWithKey(file: Blob, key: MajikKey, MajikSig: MajikSignatureStaticAdapter, options?: ExtractOptions & {
|
|
82
98
|
expectedSignerId?: string;
|
|
83
99
|
}, debug?: boolean): Promise<VerificationResult[]>;
|
|
100
|
+
/**
|
|
101
|
+
* Verify a file against a provided, detached envelope (instance or JSON).
|
|
102
|
+
* Still strips the file in case it also contains an embedded envelope,
|
|
103
|
+
* ensuring verification runs against the clean original bytes.
|
|
104
|
+
*/
|
|
105
|
+
static verifyDetached(file: Blob, envelopeInput: MajikSignatureEnvelope | MajikSignatureEnvelopeJSON, publicKeys: MajikSignerPublicKeys, MajikSig: MajikSignatureStaticAdapter, options?: ExtractOptions & {
|
|
106
|
+
expectedSignerId?: string;
|
|
107
|
+
}, debug?: boolean): Promise<VerificationResult[]>;
|
|
108
|
+
static verifyDetachedWithKey(file: Blob, envelopeInput: MajikSignatureEnvelope | MajikSignatureEnvelopeJSON, key: MajikKey, MajikSig: MajikSignatureStaticAdapter, options?: ExtractOptions & {
|
|
109
|
+
expectedSignerId?: string;
|
|
110
|
+
}, debug?: boolean): Promise<VerificationResult[]>;
|
|
84
111
|
/**
|
|
85
112
|
* Seal a multi-sig envelope, preventing any further signatures.
|
|
86
|
-
*
|
|
87
|
-
* Rules:
|
|
88
|
-
* - Only the issuer (allowlistSignerId) may seal
|
|
89
|
-
* - The envelope must have an allowlist (must be a restricted multi-sig file)
|
|
90
|
-
* - The envelope must not already be sealed
|
|
91
|
-
* - The key must be unlocked
|
|
92
|
-
*
|
|
93
|
-
* The seal hash is SHA3-512 of all current signatories + sealTimestamp,
|
|
94
|
-
* prefixed with MAJIK_SEAL_DOMAIN. It is stored in the envelope alongside
|
|
95
|
-
* the sealTimestamp and sealedBy fields. No new cryptographic signature
|
|
96
|
-
* is produced — the seal is a hash-based integrity lock.
|
|
113
|
+
* Issuer-only / already-sealed checks are enforced by envelope.withSeal().
|
|
97
114
|
*/
|
|
98
115
|
static seal(file: Blob, key: MajikKey, options?: ExtractOptions & {
|
|
99
116
|
timestamp?: string;
|
|
@@ -103,94 +120,41 @@ export declare class MajikSignatureEmbed {
|
|
|
103
120
|
handler: string;
|
|
104
121
|
mimeType: string;
|
|
105
122
|
}>;
|
|
106
|
-
/**
|
|
107
|
-
* Verify the seal hash against the current signatories and sealTimestamp.
|
|
108
|
-
* Returns invalid if the envelope is not sealed.
|
|
109
|
-
* Does NOT verify the individual cryptographic signatures — call verify() for that.
|
|
110
|
-
*/
|
|
111
123
|
static verifySeal(file: Blob, options?: ExtractOptions): Promise<SealVerificationResult>;
|
|
112
|
-
/**
|
|
113
|
-
* Return seal metadata without verifying.
|
|
114
|
-
* Returns null if the envelope is not sealed or has no envelope.
|
|
115
|
-
*/
|
|
116
124
|
static getSealInfo(file: Blob, options?: ExtractOptions): Promise<SealInfo | null>;
|
|
117
|
-
/**
|
|
118
|
-
* Returns true if the file has a sealed envelope (structural check only).
|
|
119
|
-
* Does not verify the seal hash.
|
|
120
|
-
*/
|
|
121
125
|
static isSealed(file: Blob, options?: ExtractOptions): Promise<boolean>;
|
|
122
|
-
/**
|
|
123
|
-
* Returns true when the file has a restricted multi-sig envelope
|
|
124
|
-
* (allowlist present with more than one expected signer).
|
|
125
|
-
* Returns false for unsigned files, open-signing files, or single-signer files.
|
|
126
|
-
*/
|
|
127
126
|
static isMultiSig(file: Blob, options?: ExtractOptions): Promise<boolean>;
|
|
128
|
-
/**
|
|
129
|
-
* Check whether a MajikKey is permitted to add a signature to this file.
|
|
130
|
-
*
|
|
131
|
-
* Returns false (with a reason) when:
|
|
132
|
-
* - The file is sealed
|
|
133
|
-
* - The file has an allowlist and the key is not on it (all three fields checked)
|
|
134
|
-
*
|
|
135
|
-
* Returns true when:
|
|
136
|
-
* - The file has no envelope (unsigned — anyone may sign)
|
|
137
|
-
* - The file has no allowlist (open signing — anyone may sign)
|
|
138
|
-
* - The key is on the allowlist
|
|
139
|
-
*
|
|
140
|
-
* Always requires a full MajikKey — fingerprint-only checks are not supported
|
|
141
|
-
* because they cannot verify the public key fields required by the allowlist.
|
|
142
|
-
*/
|
|
143
127
|
static canSign(file: Blob, key: MajikKey, options?: ExtractOptions): Promise<{
|
|
144
128
|
permitted: boolean;
|
|
145
129
|
reason?: string;
|
|
146
130
|
}>;
|
|
147
|
-
/**
|
|
148
|
-
* Core signatories method. Returns all, signed, and pending arrays.
|
|
149
|
-
* Pass filter to narrow the return — the filtered array is still returned
|
|
150
|
-
* inside the full SignatoriesResult so callers always have the complete picture.
|
|
151
|
-
*
|
|
152
|
-
* Returns null if the file has no envelope.
|
|
153
|
-
*/
|
|
154
131
|
static getSignatories(file: Blob, options?: ExtractOptions, filter?: SignatoriesFilter): Promise<SignatoriesResult | null>;
|
|
155
|
-
/**
|
|
156
|
-
* Return the issuer (the signer who established the allowlist and controls sealing).
|
|
157
|
-
* Returns null for open-signing files or unsigned files.
|
|
158
|
-
*/
|
|
159
132
|
static getIssuer(file: Blob, options?: ExtractOptions): Promise<import("../../core/types").SignatoryInfo | null>;
|
|
160
|
-
/**
|
|
161
|
-
* Return a full summary of the envelope state in a single file read.
|
|
162
|
-
* Useful for rendering UI state (badge, status, signatories list) without
|
|
163
|
-
* making multiple separate calls.
|
|
164
|
-
* Returns null if the file has no envelope.
|
|
165
|
-
*/
|
|
166
133
|
static getEnvelopeInfo(file: Blob, options?: ExtractOptions): Promise<EnvelopeInfo | null>;
|
|
167
134
|
static strip(file: Blob, options?: ExtractOptions): Promise<Blob>;
|
|
168
135
|
static hasSignature(file: Blob, options?: ExtractOptions): Promise<boolean>;
|
|
169
136
|
static getAllowlist(file: Blob, options?: ExtractOptions): Promise<ExpectedSigner[] | null>;
|
|
170
137
|
static readonly registry: FormatHandlerRegistry;
|
|
171
138
|
static listHandlers(): string[];
|
|
172
|
-
/**
|
|
173
|
-
* Check whether a file is eligible for chain anchoring.
|
|
174
|
-
* Anchoring requires a sealed envelope — permitted = isSealed(file).
|
|
175
|
-
* Chain-agnostic: does not know or care which chain the caller intends to use.
|
|
176
|
-
*/
|
|
177
139
|
static canAnchor(file: Blob, options?: ExtractOptions): Promise<{
|
|
178
140
|
permitted: boolean;
|
|
179
141
|
reason?: string;
|
|
180
142
|
}>;
|
|
181
143
|
/**
|
|
182
144
|
* Embed an already-confirmed chain anchor into the envelope.
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
* Defensive checks (beyond the master plan's minimum):
|
|
186
|
-
* - File must be sealed
|
|
187
|
-
* - anchor.payload.digest.value must match the envelope's current sealHash —
|
|
188
|
-
* catches a caller accidentally embedding an anchor computed against a
|
|
189
|
-
* stale seal (e.g. file was re-sealed between notarize() and this call)
|
|
190
|
-
* - Upserts by anchor.id rather than blind-pushing, so a duplicate call
|
|
191
|
-
* with the same anchor (e.g. retried after a network blip) doesn't
|
|
192
|
-
* 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().
|
|
193
147
|
*/
|
|
194
148
|
static registerChainAnchor(file: Blob, anchor: MajikChainAnchor, options?: ExtractOptions): Promise<EmbedResult>;
|
|
195
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;
|
|
196
160
|
}
|