@majikah/majik-signature 0.0.7 → 0.0.9
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/stamp/core/image-utils.d.ts +1 -11
- package/dist/core/stamp/core/image-utils.js +1 -44
- package/dist/core/stamp/core/reed-solomon.d.ts +2 -2
- package/dist/core/stamp/core/reed-solomon.js +3 -3
- package/dist/core/stamp/core/stub.d.ts +3 -3
- package/dist/core/stamp/core/stub.js +3 -3
- package/dist/core/stamp/core/types.d.ts +3 -2
- package/dist/core/stamp/image-signature.d.ts +2 -2
- package/dist/core/stamp/image-signature.js +2 -2
- package/dist/core/stamp/index.d.ts +1 -1
- package/dist/core/stamp/index.js +1 -1
- package/package.json +2 -3
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Minimum image dimensions we enforce: 600×600 px.
|
|
10
10
|
* Smaller images are padded with white before embedding.
|
|
11
11
|
*/
|
|
12
|
-
export declare const MIN_DIMENSION =
|
|
12
|
+
export declare const MIN_DIMENSION = 640;
|
|
13
13
|
export interface ImagePixels {
|
|
14
14
|
pixels: Uint8ClampedArray;
|
|
15
15
|
width: number;
|
|
@@ -45,13 +45,3 @@ export declare function padToMinimum(img: ImagePixels): ImagePixels;
|
|
|
45
45
|
* Check if the runtime environment is a browser with Canvas API.
|
|
46
46
|
*/
|
|
47
47
|
export declare function hasBrowserCanvas(): boolean;
|
|
48
|
-
/**
|
|
49
|
-
* Node.js version of decodeImage using sharp.
|
|
50
|
-
* Only available if 'sharp' is installed.
|
|
51
|
-
*/
|
|
52
|
-
export declare function decodeImageNode(buffer: Buffer | Uint8Array): Promise<ImagePixels>;
|
|
53
|
-
/**
|
|
54
|
-
* Node.js version of encodeImage using sharp.
|
|
55
|
-
* Only available if 'sharp' is installed.
|
|
56
|
-
*/
|
|
57
|
-
export declare function encodeImageNode(pixels: Uint8ClampedArray, width: number, height: number, options?: ImageEncodeOptions): Promise<Buffer>;
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
* Minimum image dimensions we enforce: 600×600 px.
|
|
10
10
|
* Smaller images are padded with white before embedding.
|
|
11
11
|
*/
|
|
12
|
-
|
|
13
|
-
export const MIN_DIMENSION = 600;
|
|
12
|
+
export const MIN_DIMENSION = 640;
|
|
14
13
|
// ─── Browser implementation ───────────────────────────────────────────────────
|
|
15
14
|
/**
|
|
16
15
|
* Decode an image Blob to RGBA pixel data using browser Canvas API.
|
|
@@ -105,45 +104,3 @@ export function hasBrowserCanvas() {
|
|
|
105
104
|
return (typeof createImageBitmap !== "undefined" &&
|
|
106
105
|
typeof OffscreenCanvas !== "undefined");
|
|
107
106
|
}
|
|
108
|
-
// ─── Node.js stubs ────────────────────────────────────────────────────────────
|
|
109
|
-
//
|
|
110
|
-
// These throw helpful errors if called in a Node.js environment without the
|
|
111
|
-
// optional `sharp` dependency. Install sharp for Node.js support:
|
|
112
|
-
// npm install sharp
|
|
113
|
-
//
|
|
114
|
-
// Or use the canvas package:
|
|
115
|
-
// npm install canvas
|
|
116
|
-
/**
|
|
117
|
-
* Node.js version of decodeImage using sharp.
|
|
118
|
-
* Only available if 'sharp' is installed.
|
|
119
|
-
*/
|
|
120
|
-
export async function decodeImageNode(buffer) {
|
|
121
|
-
const img = sharp(buffer);
|
|
122
|
-
const { width, height } = await img.metadata();
|
|
123
|
-
if (!width || !height)
|
|
124
|
-
throw new Error("Could not read image dimensions");
|
|
125
|
-
const rawBuffer = await img.ensureAlpha().raw().toBuffer();
|
|
126
|
-
return {
|
|
127
|
-
pixels: new Uint8ClampedArray(rawBuffer.buffer),
|
|
128
|
-
width,
|
|
129
|
-
height,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Node.js version of encodeImage using sharp.
|
|
134
|
-
* Only available if 'sharp' is installed.
|
|
135
|
-
*/
|
|
136
|
-
export async function encodeImageNode(pixels, width, height, options = {}) {
|
|
137
|
-
const mimeType = options.mimeType ?? "image/png";
|
|
138
|
-
const quality = Math.round((options.quality ?? 0.92) * 100);
|
|
139
|
-
const img = sharp(Buffer.from(pixels.buffer), {
|
|
140
|
-
raw: { width, height, channels: 4 },
|
|
141
|
-
});
|
|
142
|
-
if (mimeType === "image/png")
|
|
143
|
-
return img.png().toBuffer();
|
|
144
|
-
if (mimeType === "image/jpeg")
|
|
145
|
-
return img.jpeg({ quality }).toBuffer();
|
|
146
|
-
if (mimeType === "image/webp")
|
|
147
|
-
return img.webp({ quality }).toBuffer();
|
|
148
|
-
throw new Error(`Unsupported mimeType: ${mimeType}`);
|
|
149
|
-
}
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
* Uint8Array.from(iterable) always returns Uint8Array<ArrayBuffer>
|
|
30
30
|
* across all TypeScript and lib versions.
|
|
31
31
|
*/
|
|
32
|
-
export declare const RS_DATA_BYTES =
|
|
33
|
-
export declare const RS_ECC_BYTES =
|
|
32
|
+
export declare const RS_DATA_BYTES = 150;
|
|
33
|
+
export declare const RS_ECC_BYTES = 75;
|
|
34
34
|
export declare const RS_TOTAL_BYTES: number;
|
|
35
35
|
/**
|
|
36
36
|
* Encode data bytes with Reed-Solomon ECC.
|
|
@@ -102,9 +102,9 @@ function makeGenerator(nEccSymbols) {
|
|
|
102
102
|
return g;
|
|
103
103
|
}
|
|
104
104
|
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
105
|
-
export const RS_DATA_BYTES =
|
|
106
|
-
export const RS_ECC_BYTES =
|
|
107
|
-
export const RS_TOTAL_BYTES = RS_DATA_BYTES + RS_ECC_BYTES; //
|
|
105
|
+
export const RS_DATA_BYTES = 150; // must match STUB_SIZE in stub.ts (150 bytes)
|
|
106
|
+
export const RS_ECC_BYTES = 75; // ~50% overhead → corrects up to 37 byte errors
|
|
107
|
+
export const RS_TOTAL_BYTES = RS_DATA_BYTES + RS_ECC_BYTES; // 225 bytes total
|
|
108
108
|
/**
|
|
109
109
|
* Encode data bytes with Reed-Solomon ECC.
|
|
110
110
|
*
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
* ------ ---
|
|
54
54
|
* Total: 150 bytes
|
|
55
55
|
*
|
|
56
|
-
*
|
|
57
|
-
* At 0.5 bits/block at Q70: needs
|
|
58
|
-
* Our minimum is
|
|
56
|
+
* 150 bytes + 75 RS parity = 225 bytes → 1800 bits
|
|
57
|
+
* At 0.5 bits/block at Q70: needs ~3600 blocks → ~480x480px minimum
|
|
58
|
+
* Our minimum is 640x640 → comfortable (480 bytes capacity, 113% headroom).
|
|
59
59
|
*/
|
|
60
60
|
import type { ImageSignatureStub } from "./types";
|
|
61
61
|
declare const STUB_SIZE = 150;
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
* ------ ---
|
|
54
54
|
* Total: 150 bytes
|
|
55
55
|
*
|
|
56
|
-
*
|
|
57
|
-
* At 0.5 bits/block at Q70: needs
|
|
58
|
-
* Our minimum is
|
|
56
|
+
* 150 bytes + 75 RS parity = 225 bytes → 1800 bits
|
|
57
|
+
* At 0.5 bits/block at Q70: needs ~3600 blocks → ~480x480px minimum
|
|
58
|
+
* Our minimum is 640x640 → comfortable (480 bytes capacity, 113% headroom).
|
|
59
59
|
*/
|
|
60
60
|
const MAGIC = new Uint8Array([0x4d, 0x53, 0x49, 0x47]); // 'MSIG'
|
|
61
61
|
const VERSION = 1;
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* The compact proof embedded into the image via DCT steganography.
|
|
6
6
|
*
|
|
7
7
|
* Intentionally minimal — must survive Q70 JPEG recompression.
|
|
8
|
-
* Total raw size: 4 (magic) + 2 (
|
|
9
|
-
*
|
|
8
|
+
* Total raw size: 4 (magic) + 2 (version) + 8 (pHash) + 32 (edPubKey) +
|
|
9
|
+
* 8 (timestamp) + 32 (signerId) + 64 (edSig) = 150 bytes
|
|
10
|
+
* With Reed-Solomon ECC (50% overhead, 75 parity bytes): 225 bytes encoded on disk
|
|
10
11
|
*/
|
|
11
12
|
export interface ImageSignatureStub {
|
|
12
13
|
/** 64-bit perceptual hash of the image (dct-pHash, bigint as hex string) */
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* · Stripped by platforms that crop/resize (expected — Layer 2 covers this)
|
|
11
11
|
*
|
|
12
12
|
* Layer 2 — DCT steganography (invisible, within existing pixels)
|
|
13
|
-
* · Compact stub: Ed25519 only (150 bytes + Reed-Solomon ECC =
|
|
13
|
+
* · Compact stub: Ed25519 only (150 bytes + Reed-Solomon ECC = 225 bytes total)
|
|
14
14
|
* · Survives Q70 JPEG recompression, WebP conversion, platform uploads
|
|
15
15
|
* · Invisible — no dimension change, no visible artifact
|
|
16
16
|
* · Cannot carry ML-DSA-87 at Q70 (mathematically impossible at typical sizes)
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* ── Signing flow ──────────────────────────────────────────────────────────────
|
|
26
26
|
*
|
|
27
27
|
* 1. Decode input image → RGBA pixels
|
|
28
|
-
* 2. Pad to
|
|
28
|
+
* 2. Pad to 640×640 minimum
|
|
29
29
|
* 3. Strip any existing layers (idempotent re-signing)
|
|
30
30
|
* 4. Compute pHash of clean pixels
|
|
31
31
|
* 5. Sign with Ed25519 → build DCT stub
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* · Stripped by platforms that crop/resize (expected — Layer 2 covers this)
|
|
11
11
|
*
|
|
12
12
|
* Layer 2 — DCT steganography (invisible, within existing pixels)
|
|
13
|
-
* · Compact stub: Ed25519 only (150 bytes + Reed-Solomon ECC =
|
|
13
|
+
* · Compact stub: Ed25519 only (150 bytes + Reed-Solomon ECC = 225 bytes total)
|
|
14
14
|
* · Survives Q70 JPEG recompression, WebP conversion, platform uploads
|
|
15
15
|
* · Invisible — no dimension change, no visible artifact
|
|
16
16
|
* · Cannot carry ML-DSA-87 at Q70 (mathematically impossible at typical sizes)
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* ── Signing flow ──────────────────────────────────────────────────────────────
|
|
26
26
|
*
|
|
27
27
|
* 1. Decode input image → RGBA pixels
|
|
28
|
-
* 2. Pad to
|
|
28
|
+
* 2. Pad to 640×640 minimum
|
|
29
29
|
* 3. Strip any existing layers (idempotent re-signing)
|
|
30
30
|
* 4. Compute pHash of clean pixels
|
|
31
31
|
* 5. Sign with Ed25519 → build DCT stub
|
|
@@ -40,4 +40,4 @@ export { rsEncode, rsDecode, RS_DATA_BYTES, RS_ECC_BYTES, RS_TOTAL_BYTES, } from
|
|
|
40
40
|
export { serializeStub, deserializeStub, STUB_SIZE } from "./core/stub";
|
|
41
41
|
export { buildImageSigningPayload } from "./core/payload";
|
|
42
42
|
export { pixelRowEmbed, pixelRowExtract, pixelRowStrip, pixelRowHasSignature, } from "./core/pixel-row";
|
|
43
|
-
export { decodeImage, encodeImage,
|
|
43
|
+
export { decodeImage, encodeImage, padToMinimum, MIN_DIMENSION, } from "./core/image-utils";
|
package/dist/core/stamp/index.js
CHANGED
|
@@ -43,4 +43,4 @@ export { rsEncode, rsDecode, RS_DATA_BYTES, RS_ECC_BYTES, RS_TOTAL_BYTES, } from
|
|
|
43
43
|
export { serializeStub, deserializeStub, STUB_SIZE } from "./core/stub";
|
|
44
44
|
export { buildImageSigningPayload } from "./core/payload";
|
|
45
45
|
export { pixelRowEmbed, pixelRowExtract, pixelRowStrip, pixelRowHasSignature, } from "./core/pixel-row";
|
|
46
|
-
export { decodeImage, encodeImage,
|
|
46
|
+
export { decodeImage, encodeImage, padToMinimum, MIN_DIMENSION, } from "./core/image-utils";
|
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.0.
|
|
5
|
+
"version": "0.0.9",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"author": "Zelijah",
|
|
8
8
|
"main": "./dist/index.js",
|
|
@@ -53,8 +53,7 @@
|
|
|
53
53
|
"@stablelib/ed25519": "^2.0.2",
|
|
54
54
|
"@stablelib/sha256": "^2.0.1",
|
|
55
55
|
"fflate": "^0.8.2",
|
|
56
|
-
"pdf-lib": "^1.17.1"
|
|
57
|
-
"sharp": "^0.34.5"
|
|
56
|
+
"pdf-lib": "^1.17.1"
|
|
58
57
|
},
|
|
59
58
|
"devDependencies": {
|
|
60
59
|
"@types/node": "^25.5.0"
|