@majikah/majik-signature 0.2.9 → 0.3.0
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/README.md +37 -36
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -103,17 +103,18 @@ Verification requires **both** to pass. This means:
|
|
|
103
103
|
Both signatures cover a **domain-separated canonical payload**:
|
|
104
104
|
|
|
105
105
|
```
|
|
106
|
-
"majik-signature-v1:" + JSON({ v, id, ts, ct, hash[, alh] })
|
|
106
|
+
"majik-signature-v1:" + JSON({ v, id, ts, ct, hash[, alh][, vu] })
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
| Field | Description
|
|
110
|
-
| ------ |
|
|
111
|
-
| `v` | Envelope version
|
|
112
|
-
| `id` | Signer fingerprint (MajikKey identity)
|
|
113
|
-
| `ts` | ISO 8601 timestamp
|
|
114
|
-
| `ct` | Content type (advisory, or `null`)
|
|
115
|
-
| `hash` | SHA-256 of the original content, base64
|
|
116
|
-
| `alh` | SHA-256 of the canonical allowlist, base64 — **present only** when this signer is establishing an allowlist
|
|
109
|
+
| Field | Description |
|
|
110
|
+
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
111
|
+
| `v` | Envelope version |
|
|
112
|
+
| `id` | Signer fingerprint (MajikKey identity) |
|
|
113
|
+
| `ts` | ISO 8601 timestamp |
|
|
114
|
+
| `ct` | Content type (advisory, or `null`) |
|
|
115
|
+
| `hash` | SHA-256 of the original content, base64 |
|
|
116
|
+
| `alh` | SHA-256 of the canonical allowlist, base64 — **present only** when this signer is establishing an allowlist |
|
|
117
|
+
| `vu` | Optional ISO 8601 expiry. When present, verify() treats the signature as invalid once the current time is past this value. Absent = never expires (matches all pre-existing signatures — fully backward compatible). Covered by the canonical signing payload when present, so it cannot be stripped or extended post-hoc without breaking both signatures. |
|
|
117
118
|
|
|
118
119
|
`alh` is *omitted entirely* (not set to `null`) on every signature that isn't establishing an allowlist. This is a deliberate backward-compatibility guarantee: every signature produced before multi-sig support existed still verifies today, because its payload bytes are unchanged.
|
|
119
120
|
|
|
@@ -951,18 +952,18 @@ const restoredFromB64 = MajikSignature.deserialize(b64);
|
|
|
951
952
|
|
|
952
953
|
Handlers are tried in order; the first one whose `canHandle()` matches wins. If nothing matches, the **universal trailer fallback** always applies — meaning *every* file type is signable, even ones with no dedicated handler.
|
|
953
954
|
|
|
954
|
-
| Format(s)
|
|
955
|
-
|
|
|
956
|
-
| PDF
|
|
957
|
-
| PNG
|
|
958
|
-
| WAV
|
|
959
|
-
| MP3
|
|
960
|
-
| MP4, MOV, M4A, M4V
|
|
961
|
-
| DOCX, XLSX, PPTX, ODT, ODS, ODP
|
|
962
|
-
| MKV, WebM
|
|
963
|
-
| JPEG, FLAC
|
|
964
|
-
| HTML, Markdown, JSON, plain text, source code | Text
|
|
965
|
-
| Anything else
|
|
955
|
+
| Format(s) | Handler | Embedding Mechanism |
|
|
956
|
+
| --------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------- |
|
|
957
|
+
| PDF | PDF | Binary trailer appended after the last `%%EOF` marker (PDF 1.7 §7.5.6 compliant) |
|
|
958
|
+
| PNG | PNG | `iTXt` metadata chunk |
|
|
959
|
+
| WAV | WAV | RIFF `LIST INFO` chunk, custom `ISIG` sub-chunk |
|
|
960
|
+
| MP3 | MP3 | ID3v2 `TXXX` frame |
|
|
961
|
+
| MP4, MOV, M4A, M4V | MP4/MOV | `moov/udta` box, custom `majk` box type |
|
|
962
|
+
| DOCX, XLSX, PPTX, ODT, ODS, ODP | Office | Dedicated `majik-signature.json` entry inside the ZIP container |
|
|
963
|
+
| MKV, WebM | MKV | Custom Matroska metadata tag |
|
|
964
|
+
| JPEG, FLAC | JPEG / FLAC | Native format metadata (non-destructive; same round-trip guarantees as other handlers) |
|
|
965
|
+
| HTML, Markdown, JSON, plain text, source code | Text | Appended, format-appropriate metadata block |
|
|
966
|
+
| Anything else | Fallback | Universal binary trailer: `[original][signature JSON][8-byte length][8-byte magic "MAJIKSIG"]` |
|
|
966
967
|
|
|
967
968
|
All handlers guarantee: files remain fully usable after signing (PDFs still open, videos still play, Office files stay editable), signing is idempotent (safe to re-sign), and `strip()` always reproduces exactly the bytes that were originally hashed — including deterministic ZIP re-canonicalization for Office formats (the ZIP is always rebuilt via a full unzip/rezip pass on `strip()`, even when no signature entry exists yet), so that identical content always strips to identical bytes regardless of when it was last re-zipped.
|
|
968
969
|
|
|
@@ -1030,9 +1031,9 @@ A batch manifest (`MjksMapJSON`, backing `MajikSignatureMap`) is a flat list of
|
|
|
1030
1031
|
**Approximate serialized sizes (per signer):**
|
|
1031
1032
|
|
|
1032
1033
|
| Format | Size |
|
|
1033
|
-
|
|
|
1034
|
-
| JSON (minified)
|
|
1035
|
-
| Base64 serialized
|
|
1034
|
+
| ----------------- | ------ |
|
|
1035
|
+
| JSON (minified) | ~10 KB |
|
|
1036
|
+
| Base64 serialized | ~14 KB |
|
|
1036
1037
|
|
|
1037
1038
|
The dominant contributor is `mlDsaSignature` (~6 KB base64) and `signerMlDsaPublicKey` (~3.5 KB base64) — the inherent cost of post-quantum signatures, negligible relative to any real content being signed.
|
|
1038
1039
|
|
|
@@ -1044,10 +1045,10 @@ Two dedicated, versioned, self-identifying binary containers exist for out-of-ba
|
|
|
1044
1045
|
|
|
1045
1046
|
Both share the same header layout: `[magic bytes][1-byte version][1-byte reserved][4-byte big-endian payload length][payload JSON]`.
|
|
1046
1047
|
|
|
1047
|
-
| Format
|
|
1048
|
-
|
|
|
1049
|
-
| `.mjksig` — a single detached envelope | `MJKSIG`
|
|
1050
|
-
| `.mjksmap` — a batch manifest
|
|
1048
|
+
| Format | Magic | Magic Length | Header Length | Media Type | Extension |
|
|
1049
|
+
| -------------------------------------- | --------- | ------------ | ------------- | --------------------------------- | ---------- |
|
|
1050
|
+
| `.mjksig` — a single detached envelope | `MJKSIG` | 6 bytes | 12 bytes | `application/vnd.majikah.mjksig` | `.mjksig` |
|
|
1051
|
+
| `.mjksmap` — a batch manifest | `MJKSMAP` | 7 bytes | 13 bytes | `application/vnd.majikah.mjksmap` | `.mjksmap` |
|
|
1051
1052
|
|
|
1052
1053
|
Both formats validate magic bytes, supported version, and declared payload length **before** attempting to parse the JSON payload — a truncated or corrupted buffer fails fast with a clear `MajikSignatureSerializationError` rather than an obscure `JSON.parse` error.
|
|
1053
1054
|
|
|
@@ -1074,14 +1075,14 @@ const isMjksmap = await MajikSignatureMap.isMJKSMAP(mapBlob);
|
|
|
1074
1075
|
|
|
1075
1076
|
Majik Signature throws a typed error hierarchy rather than generic `Error` objects, so you can catch precisely what you need:
|
|
1076
1077
|
|
|
1077
|
-
| Error Class
|
|
1078
|
-
|
|
|
1079
|
-
| `MajikSignatureError`
|
|
1080
|
-
| `MajikSignatureKeyError`
|
|
1081
|
-
| `MajikSignatureVerificationError`
|
|
1082
|
-
| `MajikSignatureSerializationError`
|
|
1083
|
-
| `MajikSignatureAllowlistError`
|
|
1084
|
-
| `MajikSignatureValidationError`
|
|
1078
|
+
| Error Class | Thrown when... |
|
|
1079
|
+
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1080
|
+
| `MajikSignatureError` | Base class; also thrown for general/unexpected failures (e.g. signing a sealed envelope, missing envelope on seal/anchor) |
|
|
1081
|
+
| `MajikSignatureKeyError` | The key is locked, lacks signing keys, or isn't the required issuer |
|
|
1082
|
+
| `MajikSignatureVerificationError` | Verification fails unexpectedly (not the same as `valid: false`), including a failed TSA signature check |
|
|
1083
|
+
| `MajikSignatureSerializationError` | JSON/base64/MJKSIG/MJKSMAP parsing or encoding fails, including malformed binary headers |
|
|
1084
|
+
| `MajikSignatureAllowlistError` | A non-listed signer attempts to sign a restricted file |
|
|
1085
|
+
| `MajikSignatureValidationError` | A structural shape check fails — malformed envelope, malformed batch manifest entry, empty/duplicate batch paths, empty allowlist, mismatched seal fields, invalid `expectedOrder` input, etc. |
|
|
1085
1086
|
|
|
1086
1087
|
Note the distinction: `verify()`/`verifyFile()` return `{ valid: false, reason }` for a signature that *fails cryptographic verification* — that's an expected, handled outcome, not an exception. Exceptions are reserved for misuse (locked keys, malformed envelopes, disallowed signers, sealed files, malformed batch input, etc.).
|
|
1087
1088
|
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@majikah/majik-signature",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"description": "Majik Signature is a hybrid post-quantum content signing and verification library for the Majikah ecosystem. Built on top of Majik Key, 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": "0.
|
|
5
|
+
"version": "0.3.0",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"author": "Zelijah",
|
|
8
8
|
"main": "./dist/index.js",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"test:watch": "vitest"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@majikah/majik-key": "^0.
|
|
54
|
+
"@majikah/majik-key": "^0.4.0",
|
|
55
55
|
"@noble/post-quantum": "^0.7.0",
|
|
56
56
|
"@stablelib/ed25519": "^2.1.0",
|
|
57
57
|
"@stablelib/sha256": "^2.0.1",
|