@ternent/seal 0.3.10

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 ADDED
@@ -0,0 +1,258 @@
1
+ # @ternent/seal-cli
2
+
3
+ Seal signs files and manifests. It verifies artifacts offline. It emits portable proof JSON for legacy flows and recipient-targeted sealed artifacts for encrypted flows.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add -D @ternent/seal-cli
9
+ ```
10
+
11
+ ## Environment
12
+
13
+ Create a signer identity with Seal:
14
+
15
+ ```bash
16
+ seal identity create --out identity.json
17
+ ```
18
+
19
+ Create a mnemonic-backed identity and save the recovery phrase separately:
20
+
21
+ ```bash
22
+ seal identity create --out identity.json --words 24 --mnemonic-out seal-seed-phrase.txt
23
+ ```
24
+
25
+ `@ternent/seal-cli` reads signer material from a v2 identity JSON payload:
26
+
27
+ ```bash
28
+ export SEAL_IDENTITY="$(cat identity.json)"
29
+ # or
30
+ export SEAL_IDENTITY_FILE="./identity.json"
31
+ ```
32
+
33
+ ## Commands
34
+
35
+ ```bash
36
+ seal identity create --out identity.json
37
+ seal identity create --out identity.json --words 24 --mnemonic-out seal-seed-phrase.txt
38
+ seal manifest create --input apps/seal/dist --out apps/seal/dist/dist-manifest.json
39
+ seal sign --input apps/seal/dist/dist-manifest.json --out apps/seal/dist/proof.json
40
+ seal sign --input artifact.tar.gz --recipient age1... --recipient age1... --out artifact.seal.json
41
+ seal verify --proof apps/seal/dist/proof.json --input apps/seal/dist/dist-manifest.json --json
42
+ seal verify --artifact artifact.seal.json --json
43
+ seal public-key --json
44
+ ```
45
+
46
+ When one or more `--recipient` flags are provided, Seal encrypts the input bytes with `@ternent/armour`, then signs the unsigned artifact container. Recipient values are never serialized into the emitted artifact.
47
+
48
+ ## JavaScript API
49
+
50
+ Create a Seal-compatible identity and persist it as the `SEAL_IDENTITY` payload:
51
+
52
+ ```ts
53
+ import {
54
+ createSealIdentity,
55
+ createSealMnemonicIdentity,
56
+ exportIdentityJson,
57
+ } from "@ternent/seal-cli";
58
+
59
+ const identity = await createSealIdentity();
60
+ const identityJson = exportIdentityJson(identity);
61
+
62
+ const { identity: mnemonicIdentity, mnemonic } = await createSealMnemonicIdentity({ words: 24 });
63
+ ```
64
+
65
+ Create and verify recipient-targeted artifacts:
66
+
67
+ ```ts
68
+ import {
69
+ createSealArtifact,
70
+ decryptSealArtifactPayload,
71
+ verifySealArtifact,
72
+ } from "@ternent/seal-cli/artifact";
73
+
74
+ const artifact = await createSealArtifact({
75
+ signer: { identity },
76
+ subjectPath: "artifact.tar.gz",
77
+ payload: artifactBytes,
78
+ recipients: ["age1..."],
79
+ });
80
+
81
+ const verification = await verifySealArtifact(artifact);
82
+ const plaintext = await decryptSealArtifactPayload({
83
+ artifact,
84
+ identity,
85
+ });
86
+ ```
87
+
88
+ ## GitHub Actions
89
+
90
+ Use the published GitHub Action:
91
+
92
+ ```yaml
93
+ - name: Generate Seal artifacts
94
+ uses: samternent/seal-action@v2
95
+ env:
96
+ SEAL_IDENTITY: ${{ secrets.SEAL_IDENTITY }}
97
+ with:
98
+ assets-directory: dist
99
+ package-name: @ternent/seal-cli
100
+ package-version: latest
101
+ ```
102
+
103
+ The action is intentionally narrow. Your workflow still needs to:
104
+
105
+ - check out the repo
106
+ - set up Node
107
+ - build the static directory you want to sign
108
+
109
+ Inputs:
110
+
111
+ - `assets-directory`: built static directory to sign
112
+ - `working-directory`: base directory for path resolution
113
+ - `manifest-name`: manifest output filename
114
+ - `proof-name`: proof output filename
115
+ - `public-key-name`: public key output filename
116
+ - `package-name`: npm package name to execute when `cli-command` is omitted
117
+ - `package-version`: npm version or dist-tag to execute when `cli-command` is omitted
118
+ - `cli-command`: command prefix used to invoke Seal
119
+
120
+ Outputs:
121
+
122
+ - `manifest-path`
123
+ - `proof-path`
124
+ - `public-key-path`
125
+
126
+ The default path is npm-backed: if `cli-command` is empty, the action runs `npm exec --yes --package=<package-name>@<package-version> seal`.
127
+
128
+ ## Schemas
129
+
130
+ Manifest:
131
+
132
+ ```json
133
+ {
134
+ "version": "1",
135
+ "type": "seal-manifest",
136
+ "root": "dist",
137
+ "files": {
138
+ "assets/index.js": "sha256:..."
139
+ }
140
+ }
141
+ ```
142
+
143
+ Proof:
144
+
145
+ ```json
146
+ {
147
+ "version": "2",
148
+ "type": "seal-proof",
149
+ "algorithm": "Ed25519",
150
+ "createdAt": "2026-03-13T00:00:00.000Z",
151
+ "subject": {
152
+ "kind": "manifest",
153
+ "path": "dist-manifest.json",
154
+ "hash": "sha256:..."
155
+ },
156
+ "signer": {
157
+ "publicKey": "BASE64URL-RAW-ED25519-PUBLIC-KEY",
158
+ "keyId": "..."
159
+ },
160
+ "signature": "..."
161
+ }
162
+ ```
163
+
164
+ Encrypted artifact:
165
+
166
+ ```json
167
+ {
168
+ "version": "1",
169
+ "type": "seal-artifact",
170
+ "manifest": {
171
+ "version": "1",
172
+ "payloadType": "encrypted",
173
+ "payloadScheme": "age",
174
+ "payloadMode": "recipients",
175
+ "payloadEncoding": "armor",
176
+ "payloadHash": "sha256:..."
177
+ },
178
+ "payload": {
179
+ "type": "encrypted",
180
+ "scheme": "age",
181
+ "mode": "recipients",
182
+ "encoding": "armor",
183
+ "data": "-----BEGIN AGE ENCRYPTED FILE-----\n...\n-----END AGE ENCRYPTED FILE-----\n"
184
+ },
185
+ "proof": {
186
+ "version": "2",
187
+ "type": "seal-proof",
188
+ "algorithm": "Ed25519",
189
+ "createdAt": "2026-03-17T00:00:00.000Z",
190
+ "subject": {
191
+ "kind": "artifact",
192
+ "path": "artifact.tar.gz",
193
+ "hash": "sha256:..."
194
+ },
195
+ "signer": {
196
+ "publicKey": "BASE64URL-RAW-ED25519-PUBLIC-KEY",
197
+ "keyId": "..."
198
+ },
199
+ "signature": "..."
200
+ }
201
+ }
202
+ ```
203
+
204
+ Public key:
205
+
206
+ ```json
207
+ {
208
+ "version": "2",
209
+ "type": "seal-public-key",
210
+ "algorithm": "Ed25519",
211
+ "publicKey": "BASE64URL-RAW-ED25519-PUBLIC-KEY",
212
+ "keyId": "..."
213
+ }
214
+ ```
215
+
216
+ Identity:
217
+
218
+ ```json
219
+ {
220
+ "format": "ternent-identity",
221
+ "version": "2",
222
+ "algorithm": "Ed25519",
223
+ "createdAt": "2026-03-17T00:00:00.000Z",
224
+ "publicKey": "BASE64URL-RAW-ED25519-PUBLIC-KEY",
225
+ "keyId": "...",
226
+ "material": {
227
+ "kind": "seed",
228
+ "seed": "BASE64URL-RAW-32-BYTE-SEED"
229
+ }
230
+ }
231
+ ```
232
+
233
+ ## Frontend Contract
234
+
235
+ `apps/seal` verifies published artifacts by fetching:
236
+
237
+ - `/dist-manifest.json`
238
+ - `/proof.json`
239
+ - `/public-key.json` (optional)
240
+
241
+ Browser verification reuses `@ternent/seal-cli/proof`, `@ternent/seal-cli/crypto`, `@ternent/identity`, and `ternent-utils`.
242
+
243
+ Validation rules:
244
+
245
+ - parse `seal-proof`
246
+ - recompute the fetched manifest hash from raw bytes
247
+ - verify the embedded signature
248
+ - verify `signer.keyId`
249
+ - if `/public-key.json` exists, require its `publicKey` and `keyId` to match the proof signer
250
+
251
+ ## Exit Codes
252
+
253
+ - `0` success
254
+ - `1` general failure
255
+ - `2` subject hash mismatch
256
+ - `3` signature invalid
257
+ - `4` invalid proof
258
+ - `5` key or config error
package/bin/seal ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "../dist/cli.js";
3
+
4
+ runCli(process.argv.slice(2)).then((exitCode) => {
5
+ process.exitCode = exitCode;
6
+ });
@@ -0,0 +1,256 @@
1
+ import { initArmour, encryptForRecipients, decryptWithIdentity } from "@ternent/armour";
2
+ import { c as canonicalStringify } from "./chunks/utils.es-ad8f1dc4.js";
3
+ import { createSealHash, createSealProof, validateSealProofShape, verifySealProofSignature } from "./proof.js";
4
+ import { toSealEncryptionError, unsupportedEncryptionModeError, toSealDecryptionError } from "./errors.js";
5
+ import "./crypto.js";
6
+ import "@ternent/identity";
7
+ const utf8Encoder = new TextEncoder();
8
+ const utf8Decoder = new TextDecoder();
9
+ const SEAL_ARTIFACT_VERSION = "1";
10
+ const SEAL_ARTIFACT_TYPE = "seal-artifact";
11
+ const SEAL_ARTIFACT_MANIFEST_VERSION = "1";
12
+ function isRecord(value) {
13
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
14
+ }
15
+ function hasOnlyKeys(value, allowed) {
16
+ return Object.keys(value).every((key) => allowed.includes(key));
17
+ }
18
+ function isSealHash(value) {
19
+ return typeof value === "string" && /^sha256:[0-9a-f]{64}$/.test(value);
20
+ }
21
+ function normalizeBytes(value) {
22
+ return value instanceof Uint8Array ? value : new Uint8Array(value);
23
+ }
24
+ function getSealArtifactUnsignedFields(artifact) {
25
+ return {
26
+ version: artifact.version,
27
+ type: artifact.type,
28
+ manifest: artifact.manifest,
29
+ payload: artifact.payload
30
+ };
31
+ }
32
+ function getUnsignedArtifactBytes(artifact) {
33
+ return utf8Encoder.encode(canonicalStringify(getSealArtifactUnsignedFields(artifact)));
34
+ }
35
+ async function createSealArtifact(input) {
36
+ const plaintext = normalizeBytes(input.payload);
37
+ try {
38
+ await initArmour();
39
+ const ciphertext = await encryptForRecipients({
40
+ recipients: input.recipients,
41
+ data: plaintext,
42
+ output: "armor"
43
+ });
44
+ const payloadData = utf8Decoder.decode(ciphertext);
45
+ const manifest = {
46
+ version: SEAL_ARTIFACT_MANIFEST_VERSION,
47
+ payloadType: "encrypted",
48
+ payloadScheme: "age",
49
+ payloadMode: "recipients",
50
+ payloadEncoding: "armor",
51
+ payloadHash: await createSealHash(ciphertext)
52
+ };
53
+ const payload = {
54
+ type: "encrypted",
55
+ scheme: "age",
56
+ mode: "recipients",
57
+ encoding: "armor",
58
+ data: payloadData
59
+ };
60
+ const unsignedArtifact = {
61
+ version: SEAL_ARTIFACT_VERSION,
62
+ type: SEAL_ARTIFACT_TYPE,
63
+ manifest,
64
+ payload
65
+ };
66
+ const proof = await createSealProof({
67
+ createdAt: input.createdAt,
68
+ signer: input.signer,
69
+ subject: {
70
+ kind: "artifact",
71
+ path: input.subjectPath,
72
+ hash: await createSealHash(getUnsignedArtifactBytes(unsignedArtifact))
73
+ }
74
+ });
75
+ return {
76
+ ...unsignedArtifact,
77
+ proof
78
+ };
79
+ } catch (error) {
80
+ throw toSealEncryptionError(error);
81
+ }
82
+ }
83
+ function validateSealArtifactShape(value) {
84
+ if (!isRecord(value)) {
85
+ return {
86
+ ok: false,
87
+ errors: ["Artifact must be a JSON object."],
88
+ artifact: null
89
+ };
90
+ }
91
+ const errors = [];
92
+ if (!hasOnlyKeys(value, ["version", "type", "manifest", "payload", "proof"])) {
93
+ errors.push("Artifact contains unsupported fields.");
94
+ }
95
+ if (value.version !== SEAL_ARTIFACT_VERSION) {
96
+ errors.push(`Artifact version must be ${SEAL_ARTIFACT_VERSION}.`);
97
+ }
98
+ if (value.type !== SEAL_ARTIFACT_TYPE) {
99
+ errors.push(`Artifact type must be ${SEAL_ARTIFACT_TYPE}.`);
100
+ }
101
+ if (!isRecord(value.manifest)) {
102
+ errors.push("Artifact manifest must be an object.");
103
+ }
104
+ if (!isRecord(value.payload)) {
105
+ errors.push("Artifact payload must be an object.");
106
+ }
107
+ if (!isRecord(value.proof)) {
108
+ errors.push("Artifact proof must be an object.");
109
+ }
110
+ if (errors.length > 0 || !isRecord(value.manifest) || !isRecord(value.payload) || !isRecord(value.proof)) {
111
+ return {
112
+ ok: false,
113
+ errors,
114
+ artifact: null
115
+ };
116
+ }
117
+ if (!hasOnlyKeys(value.manifest, [
118
+ "version",
119
+ "payloadType",
120
+ "payloadScheme",
121
+ "payloadMode",
122
+ "payloadEncoding",
123
+ "payloadHash"
124
+ ])) {
125
+ errors.push("Artifact manifest contains unsupported fields.");
126
+ }
127
+ if (!hasOnlyKeys(value.payload, ["type", "scheme", "mode", "encoding", "data"])) {
128
+ errors.push("Artifact payload contains unsupported fields.");
129
+ }
130
+ if (value.manifest.version !== SEAL_ARTIFACT_MANIFEST_VERSION) {
131
+ errors.push(`Artifact manifest version must be ${SEAL_ARTIFACT_MANIFEST_VERSION}.`);
132
+ }
133
+ if (value.manifest.payloadType !== "encrypted") {
134
+ errors.push("Artifact manifest payloadType must be encrypted.");
135
+ }
136
+ if (value.manifest.payloadScheme !== "age") {
137
+ errors.push("Artifact manifest payloadScheme must be age.");
138
+ }
139
+ if (value.manifest.payloadMode !== "recipients") {
140
+ errors.push("Artifact manifest payloadMode must be recipients.");
141
+ }
142
+ if (value.manifest.payloadEncoding !== "armor") {
143
+ errors.push("Artifact manifest payloadEncoding must be armor.");
144
+ }
145
+ if (!isSealHash(value.manifest.payloadHash)) {
146
+ errors.push("Artifact manifest payloadHash must be a sha256 hash.");
147
+ }
148
+ if (value.payload.type !== "encrypted") {
149
+ errors.push("Artifact payload type must be encrypted.");
150
+ }
151
+ if (value.payload.scheme !== "age") {
152
+ errors.push("Artifact payload scheme must be age.");
153
+ }
154
+ if (value.payload.mode !== "recipients") {
155
+ errors.push("Artifact payload mode must be recipients.");
156
+ }
157
+ if (value.payload.encoding !== "armor") {
158
+ errors.push("Artifact payload encoding must be armor.");
159
+ }
160
+ if (typeof value.payload.data !== "string" || value.payload.data.length === 0) {
161
+ errors.push("Artifact payload data must be a non-empty string.");
162
+ }
163
+ if (value.manifest.payloadType !== value.payload.type || value.manifest.payloadScheme !== value.payload.scheme || value.manifest.payloadMode !== value.payload.mode || value.manifest.payloadEncoding !== value.payload.encoding) {
164
+ errors.push("Artifact manifest and payload metadata must match.");
165
+ }
166
+ const proofValidation = validateSealProofShape(value.proof);
167
+ if (!proofValidation.ok || !proofValidation.proof) {
168
+ errors.push(...proofValidation.errors);
169
+ } else if (proofValidation.proof.subject.kind !== "artifact") {
170
+ errors.push("Artifact proof subject kind must be artifact.");
171
+ }
172
+ if (errors.length > 0) {
173
+ return {
174
+ ok: false,
175
+ errors,
176
+ artifact: null
177
+ };
178
+ }
179
+ return {
180
+ ok: true,
181
+ errors: [],
182
+ artifact: value
183
+ };
184
+ }
185
+ function parseSealArtifactJson(raw) {
186
+ try {
187
+ return validateSealArtifactShape(JSON.parse(raw));
188
+ } catch {
189
+ return {
190
+ ok: false,
191
+ errors: ["Artifact JSON is not valid JSON."],
192
+ artifact: null
193
+ };
194
+ }
195
+ }
196
+ async function verifySealArtifact(artifact) {
197
+ const validation = validateSealArtifactShape(artifact);
198
+ if (!validation.ok || !validation.artifact) {
199
+ return {
200
+ valid: false,
201
+ hashMatch: false,
202
+ signatureValid: false,
203
+ encrypted: true,
204
+ payloadScheme: "age",
205
+ payloadMode: "recipients",
206
+ keyId: "",
207
+ algorithm: "Ed25519",
208
+ subjectHash: "sha256:0000000000000000000000000000000000000000000000000000000000000000",
209
+ errors: validation.errors
210
+ };
211
+ }
212
+ const signatureCheck = await verifySealProofSignature(artifact.proof);
213
+ const subjectHash = await createSealHash(getUnsignedArtifactBytes(artifact));
214
+ const payloadHash = await createSealHash(utf8Encoder.encode(artifact.payload.data));
215
+ const artifactHashMatch = artifact.proof.subject.hash === subjectHash;
216
+ const payloadHashMatch = artifact.manifest.payloadHash === payloadHash;
217
+ const errors = [...signatureCheck.errors];
218
+ if (!artifactHashMatch) {
219
+ errors.push("Artifact hash does not match proof subject hash.");
220
+ }
221
+ if (!payloadHashMatch) {
222
+ errors.push("Encrypted payload hash does not match manifest payload hash.");
223
+ }
224
+ return {
225
+ valid: signatureCheck.ok && artifactHashMatch && payloadHashMatch,
226
+ hashMatch: artifactHashMatch && payloadHashMatch,
227
+ signatureValid: signatureCheck.ok,
228
+ encrypted: true,
229
+ payloadScheme: artifact.payload.scheme,
230
+ payloadMode: artifact.payload.mode,
231
+ keyId: artifact.proof.signer.keyId,
232
+ algorithm: artifact.proof.algorithm,
233
+ subjectHash,
234
+ errors
235
+ };
236
+ }
237
+ async function decryptSealArtifactPayload(input) {
238
+ const verification = await verifySealArtifact(input.artifact);
239
+ if (!verification.valid) {
240
+ throw new Error(verification.errors.join(" ") || "Artifact verification failed.");
241
+ }
242
+ if (input.artifact.payload.type !== "encrypted" || input.artifact.payload.scheme !== "age" || input.artifact.payload.mode !== "recipients" || input.artifact.payload.encoding !== "armor") {
243
+ throw unsupportedEncryptionModeError("Seal only supports age recipient-mode armored payloads.");
244
+ }
245
+ try {
246
+ await initArmour();
247
+ return await decryptWithIdentity({
248
+ identity: input.identity,
249
+ data: utf8Encoder.encode(input.artifact.payload.data)
250
+ });
251
+ } catch (error) {
252
+ throw toSealDecryptionError(error);
253
+ }
254
+ }
255
+ export { SEAL_ARTIFACT_MANIFEST_VERSION, SEAL_ARTIFACT_TYPE, SEAL_ARTIFACT_VERSION, createSealArtifact, decryptSealArtifactPayload, getSealArtifactUnsignedFields, parseSealArtifactJson, validateSealArtifactShape, verifySealArtifact };
256
+ //# sourceMappingURL=artifact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact.js","sources":["../src/artifact.ts"],"sourcesContent":["import {\n decryptWithIdentity,\n encryptForRecipients,\n initArmour,\n type ArmourIdentityInput,\n} from \"@ternent/armour\";\nimport { canonicalStringify } from \"ternent-utils\";\nimport type { SealHash } from \"./manifest\";\nimport {\n createSealHash,\n createSealProof,\n validateSealProofShape,\n verifySealProofSignature,\n type SealProofV1,\n} from \"./proof\";\nimport type { SealSignerInput } from \"./crypto\";\nimport {\n toSealDecryptionError,\n toSealEncryptionError,\n unsupportedEncryptionModeError,\n} from \"./errors\";\n\nconst utf8Encoder = new TextEncoder();\nconst utf8Decoder = new TextDecoder();\n\nexport const SEAL_ARTIFACT_VERSION = \"1\" as const;\nexport const SEAL_ARTIFACT_TYPE = \"seal-artifact\" as const;\nexport const SEAL_ARTIFACT_MANIFEST_VERSION = \"1\" as const;\n\nexport type SealArtifactManifestV1 = {\n version: typeof SEAL_ARTIFACT_MANIFEST_VERSION;\n payloadType: \"encrypted\";\n payloadScheme: \"age\";\n payloadMode: \"recipients\";\n payloadEncoding: \"armor\";\n payloadHash: SealHash;\n};\n\nexport type SealEncryptedPayloadV1 = {\n type: \"encrypted\";\n scheme: \"age\";\n mode: \"recipients\";\n encoding: \"armor\";\n data: string;\n};\n\nexport type SealArtifactUnsignedV1 = {\n version: typeof SEAL_ARTIFACT_VERSION;\n type: typeof SEAL_ARTIFACT_TYPE;\n manifest: SealArtifactManifestV1;\n payload: SealEncryptedPayloadV1;\n};\n\nexport type SealArtifactV1 = SealArtifactUnsignedV1 & {\n proof: SealProofV1;\n};\n\nexport type VerifySealArtifactResult = {\n valid: boolean;\n hashMatch: boolean;\n signatureValid: boolean;\n encrypted: boolean;\n payloadScheme: \"age\";\n payloadMode: \"recipients\";\n keyId: string;\n algorithm: SealProofV1[\"algorithm\"];\n subjectHash: SealHash;\n errors: string[];\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction hasOnlyKeys(value: Record<string, unknown>, allowed: string[]): boolean {\n return Object.keys(value).every((key) => allowed.includes(key));\n}\n\nfunction isSealHash(value: unknown): value is SealHash {\n return typeof value === \"string\" && /^sha256:[0-9a-f]{64}$/.test(value);\n}\n\nfunction normalizeBytes(value: Uint8Array | ArrayBuffer): Uint8Array {\n return value instanceof Uint8Array ? value : new Uint8Array(value);\n}\n\nexport function getSealArtifactUnsignedFields(\n artifact: SealArtifactV1 | SealArtifactUnsignedV1,\n): SealArtifactUnsignedV1 {\n return {\n version: artifact.version,\n type: artifact.type,\n manifest: artifact.manifest,\n payload: artifact.payload,\n };\n}\n\nfunction getUnsignedArtifactBytes(artifact: SealArtifactV1 | SealArtifactUnsignedV1): Uint8Array {\n return utf8Encoder.encode(canonicalStringify(getSealArtifactUnsignedFields(artifact)));\n}\n\nexport async function createSealArtifact(input: {\n createdAt?: string;\n signer: SealSignerInput;\n subjectPath: string;\n payload: Uint8Array | ArrayBuffer;\n recipients: string[];\n}): Promise<SealArtifactV1> {\n const plaintext = normalizeBytes(input.payload);\n\n try {\n await initArmour();\n\n const ciphertext = await encryptForRecipients({\n recipients: input.recipients,\n data: plaintext,\n output: \"armor\",\n });\n const payloadData = utf8Decoder.decode(ciphertext);\n const manifest: SealArtifactManifestV1 = {\n version: SEAL_ARTIFACT_MANIFEST_VERSION,\n payloadType: \"encrypted\",\n payloadScheme: \"age\",\n payloadMode: \"recipients\",\n payloadEncoding: \"armor\",\n payloadHash: await createSealHash(ciphertext),\n };\n const payload: SealEncryptedPayloadV1 = {\n type: \"encrypted\",\n scheme: \"age\",\n mode: \"recipients\",\n encoding: \"armor\",\n data: payloadData,\n };\n const unsignedArtifact: SealArtifactUnsignedV1 = {\n version: SEAL_ARTIFACT_VERSION,\n type: SEAL_ARTIFACT_TYPE,\n manifest,\n payload,\n };\n\n const proof = await createSealProof({\n createdAt: input.createdAt,\n signer: input.signer,\n subject: {\n kind: \"artifact\",\n path: input.subjectPath,\n hash: await createSealHash(getUnsignedArtifactBytes(unsignedArtifact)),\n },\n });\n\n return {\n ...unsignedArtifact,\n proof,\n };\n } catch (error) {\n throw toSealEncryptionError(error);\n }\n}\n\nexport function validateSealArtifactShape(value: unknown): {\n ok: boolean;\n errors: string[];\n artifact: SealArtifactV1 | null;\n} {\n if (!isRecord(value)) {\n return {\n ok: false,\n errors: [\"Artifact must be a JSON object.\"],\n artifact: null,\n };\n }\n\n const errors: string[] = [];\n\n if (!hasOnlyKeys(value, [\"version\", \"type\", \"manifest\", \"payload\", \"proof\"])) {\n errors.push(\"Artifact contains unsupported fields.\");\n }\n if (value.version !== SEAL_ARTIFACT_VERSION) {\n errors.push(`Artifact version must be ${SEAL_ARTIFACT_VERSION}.`);\n }\n if (value.type !== SEAL_ARTIFACT_TYPE) {\n errors.push(`Artifact type must be ${SEAL_ARTIFACT_TYPE}.`);\n }\n if (!isRecord(value.manifest)) {\n errors.push(\"Artifact manifest must be an object.\");\n }\n if (!isRecord(value.payload)) {\n errors.push(\"Artifact payload must be an object.\");\n }\n if (!isRecord(value.proof)) {\n errors.push(\"Artifact proof must be an object.\");\n }\n\n if (\n errors.length > 0 ||\n !isRecord(value.manifest) ||\n !isRecord(value.payload) ||\n !isRecord(value.proof)\n ) {\n return {\n ok: false,\n errors,\n artifact: null,\n };\n }\n\n if (\n !hasOnlyKeys(value.manifest, [\n \"version\",\n \"payloadType\",\n \"payloadScheme\",\n \"payloadMode\",\n \"payloadEncoding\",\n \"payloadHash\",\n ])\n ) {\n errors.push(\"Artifact manifest contains unsupported fields.\");\n }\n if (!hasOnlyKeys(value.payload, [\"type\", \"scheme\", \"mode\", \"encoding\", \"data\"])) {\n errors.push(\"Artifact payload contains unsupported fields.\");\n }\n\n if (value.manifest.version !== SEAL_ARTIFACT_MANIFEST_VERSION) {\n errors.push(`Artifact manifest version must be ${SEAL_ARTIFACT_MANIFEST_VERSION}.`);\n }\n if (value.manifest.payloadType !== \"encrypted\") {\n errors.push(\"Artifact manifest payloadType must be encrypted.\");\n }\n if (value.manifest.payloadScheme !== \"age\") {\n errors.push(\"Artifact manifest payloadScheme must be age.\");\n }\n if (value.manifest.payloadMode !== \"recipients\") {\n errors.push(\"Artifact manifest payloadMode must be recipients.\");\n }\n if (value.manifest.payloadEncoding !== \"armor\") {\n errors.push(\"Artifact manifest payloadEncoding must be armor.\");\n }\n if (!isSealHash(value.manifest.payloadHash)) {\n errors.push(\"Artifact manifest payloadHash must be a sha256 hash.\");\n }\n\n if (value.payload.type !== \"encrypted\") {\n errors.push(\"Artifact payload type must be encrypted.\");\n }\n if (value.payload.scheme !== \"age\") {\n errors.push(\"Artifact payload scheme must be age.\");\n }\n if (value.payload.mode !== \"recipients\") {\n errors.push(\"Artifact payload mode must be recipients.\");\n }\n if (value.payload.encoding !== \"armor\") {\n errors.push(\"Artifact payload encoding must be armor.\");\n }\n if (typeof value.payload.data !== \"string\" || value.payload.data.length === 0) {\n errors.push(\"Artifact payload data must be a non-empty string.\");\n }\n\n if (\n value.manifest.payloadType !== value.payload.type ||\n value.manifest.payloadScheme !== value.payload.scheme ||\n value.manifest.payloadMode !== value.payload.mode ||\n value.manifest.payloadEncoding !== value.payload.encoding\n ) {\n errors.push(\"Artifact manifest and payload metadata must match.\");\n }\n\n const proofValidation = validateSealProofShape(value.proof);\n if (!proofValidation.ok || !proofValidation.proof) {\n errors.push(...proofValidation.errors);\n } else if (proofValidation.proof.subject.kind !== \"artifact\") {\n errors.push(\"Artifact proof subject kind must be artifact.\");\n }\n\n if (errors.length > 0) {\n return {\n ok: false,\n errors,\n artifact: null,\n };\n }\n\n return {\n ok: true,\n errors: [],\n artifact: value as SealArtifactV1,\n };\n}\n\nexport function parseSealArtifactJson(raw: string): {\n ok: boolean;\n errors: string[];\n artifact: SealArtifactV1 | null;\n} {\n try {\n return validateSealArtifactShape(JSON.parse(raw));\n } catch {\n return {\n ok: false,\n errors: [\"Artifact JSON is not valid JSON.\"],\n artifact: null,\n };\n }\n}\n\nexport async function verifySealArtifact(\n artifact: SealArtifactV1,\n): Promise<VerifySealArtifactResult> {\n const validation = validateSealArtifactShape(artifact);\n if (!validation.ok || !validation.artifact) {\n return {\n valid: false,\n hashMatch: false,\n signatureValid: false,\n encrypted: true,\n payloadScheme: \"age\",\n payloadMode: \"recipients\",\n keyId: \"\",\n algorithm: \"Ed25519\",\n subjectHash: \"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\n errors: validation.errors,\n };\n }\n\n const signatureCheck = await verifySealProofSignature(artifact.proof);\n const subjectHash = await createSealHash(getUnsignedArtifactBytes(artifact));\n const payloadHash = await createSealHash(utf8Encoder.encode(artifact.payload.data));\n const artifactHashMatch = artifact.proof.subject.hash === subjectHash;\n const payloadHashMatch = artifact.manifest.payloadHash === payloadHash;\n const errors = [...signatureCheck.errors];\n\n if (!artifactHashMatch) {\n errors.push(\"Artifact hash does not match proof subject hash.\");\n }\n if (!payloadHashMatch) {\n errors.push(\"Encrypted payload hash does not match manifest payload hash.\");\n }\n\n return {\n valid: signatureCheck.ok && artifactHashMatch && payloadHashMatch,\n hashMatch: artifactHashMatch && payloadHashMatch,\n signatureValid: signatureCheck.ok,\n encrypted: true,\n payloadScheme: artifact.payload.scheme,\n payloadMode: artifact.payload.mode,\n keyId: artifact.proof.signer.keyId,\n algorithm: artifact.proof.algorithm,\n subjectHash,\n errors,\n };\n}\n\nexport async function decryptSealArtifactPayload(input: {\n artifact: SealArtifactV1;\n identity: ArmourIdentityInput;\n}): Promise<Uint8Array> {\n const verification = await verifySealArtifact(input.artifact);\n if (!verification.valid) {\n throw new Error(verification.errors.join(\" \") || \"Artifact verification failed.\");\n }\n\n if (\n input.artifact.payload.type !== \"encrypted\" ||\n input.artifact.payload.scheme !== \"age\" ||\n input.artifact.payload.mode !== \"recipients\" ||\n input.artifact.payload.encoding !== \"armor\"\n ) {\n throw unsupportedEncryptionModeError(\"Seal only supports age recipient-mode armored payloads.\");\n }\n\n try {\n await initArmour();\n return await decryptWithIdentity({\n identity: input.identity,\n data: utf8Encoder.encode(input.artifact.payload.data),\n });\n } catch (error) {\n throw toSealDecryptionError(error);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ function toCryptoBuffer(data) {
2
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
3
+ return Uint8Array.from(bytes).buffer;
4
+ }
5
+ function getHashArray(hash) {
6
+ return Array.from(new Uint8Array(hash));
7
+ }
8
+ function getHashHex(hash) {
9
+ return hash.map((buf) => buf.toString(16).padStart(2, "0")).join("");
10
+ }
11
+ function canonicalize(value, seen) {
12
+ if (value === void 0) {
13
+ throw new TypeError("Cannot hash undefined");
14
+ }
15
+ const valueType = typeof value;
16
+ if (valueType === "function" || valueType === "symbol") {
17
+ throw new TypeError(`Cannot hash ${valueType} values`);
18
+ }
19
+ if (value === null || valueType === "string" || valueType === "number" || valueType === "boolean") {
20
+ return value;
21
+ }
22
+ if (valueType === "bigint") {
23
+ throw new TypeError("Cannot hash bigint values");
24
+ }
25
+ if (typeof value.toJSON === "function") {
26
+ return canonicalize(value.toJSON(), seen);
27
+ }
28
+ if (Array.isArray(value)) {
29
+ return value.map((item) => canonicalize(item, seen));
30
+ }
31
+ if (typeof value === "object") {
32
+ if (seen.has(value)) {
33
+ throw new TypeError("Cannot hash circular references");
34
+ }
35
+ seen.add(value);
36
+ const entries = Object.keys(value).sort();
37
+ const result = {};
38
+ for (const key of entries) {
39
+ result[key] = canonicalize(value[key], seen);
40
+ }
41
+ seen.delete(value);
42
+ return result;
43
+ }
44
+ throw new TypeError(`Cannot hash unsupported value type: ${valueType}`);
45
+ }
46
+ function canonicalStringify(data) {
47
+ return JSON.stringify(canonicalize(data, /* @__PURE__ */ new WeakSet()));
48
+ }
49
+ async function hashBytes(input) {
50
+ const hashBuffer = await crypto.subtle.digest("SHA-256", toCryptoBuffer(input));
51
+ return getHashHex(getHashArray(hashBuffer));
52
+ }
53
+ export { canonicalStringify as c, hashBytes as h };
54
+ //# sourceMappingURL=utils.es-ad8f1dc4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.es-ad8f1dc4.js","sources":["../../../utils/dist/utils.es.js"],"sourcesContent":["function getBufferCtor() {\n const maybeBuffer = globalThis.Buffer;\n return maybeBuffer != null ? maybeBuffer : null;\n}\nfunction addNewLines(str) {\n let finalString = \"\";\n while (str.length > 0) {\n finalString += str.substring(0, 64) + \"\\n\";\n str = str.substring(64);\n }\n return finalString;\n}\nfunction removeLines(str) {\n return str.replaceAll(\"\\n\", \"\");\n}\nfunction stripIdentityKey(key) {\n return key.replace(\"-----BEGIN PUBLIC KEY-----\\n\", \"\").replace(\"\\n-----END PUBLIC KEY-----\", \"\");\n}\nfunction formatIdentityKey(key) {\n return `-----BEGIN PUBLIC KEY-----\n${key}\n-----END PUBLIC KEY-----`;\n}\nfunction stripEncryptionFile(file) {\n return file.replace(\"-----BEGIN AGE ENCRYPTED FILE-----\\n\", \"\").replace(\"\\n-----END AGE ENCRYPTED FILE-----\\n\", \"\");\n}\nfunction formatEncryptionFile(file) {\n return `-----BEGIN AGE ENCRYPTED FILE-----\n${file}\n-----END AGE ENCRYPTED FILE-----\n`;\n}\nfunction generateId() {\n const uint32 = crypto.getRandomValues(new Uint32Array(1))[0];\n return uint32.toString(16);\n}\nfunction arrayBufferToBase64(arrayBuffer) {\n const byteArray = new Uint8Array(arrayBuffer);\n const bufferCtor = getBufferCtor();\n if (bufferCtor) {\n return bufferCtor.from(byteArray).toString(\"base64\");\n }\n let byteString = \"\";\n for (let i = 0; i < byteArray.byteLength; i++) {\n byteString += String.fromCharCode(byteArray[i]);\n }\n return btoa(byteString);\n}\nfunction base64ToArrayBuffer(b64) {\n const bufferCtor = getBufferCtor();\n if (bufferCtor) {\n const bytes = Uint8Array.from(\n bufferCtor.from(b64, \"base64\").toString(\"binary\"),\n (char) => char.charCodeAt(0)\n );\n return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);\n }\n const byteString = atob(b64);\n const byteArray = new Uint8Array(byteString.length);\n for (let i = 0; i < byteString.length; i++) {\n byteArray[i] = byteString.charCodeAt(i);\n }\n return byteArray.buffer;\n}\nfunction b64encode(buf) {\n return arrayBufferToBase64(buf);\n}\nfunction b64decode(str) {\n return base64ToArrayBuffer(str);\n}\nfunction encode(data) {\n const payload = typeof data === \"string\" ? data : JSON.stringify(data);\n return new TextEncoder().encode(payload);\n}\nfunction decode(data) {\n return new TextDecoder(\"utf-8\").decode(new Uint8Array(data));\n}\nfunction toCryptoBuffer(data) {\n const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);\n return Uint8Array.from(bytes).buffer;\n}\nfunction getHashBuffer(data) {\n return crypto.subtle.digest(\"SHA-256\", toCryptoBuffer(encode(data)));\n}\nfunction getHashArray(hash) {\n return Array.from(new Uint8Array(hash));\n}\nfunction getHashHex(hash) {\n return hash.map((buf) => buf.toString(16).padStart(2, \"0\")).join(\"\");\n}\nfunction canonicalize(value, seen) {\n if (value === void 0) {\n throw new TypeError(\"Cannot hash undefined\");\n }\n const valueType = typeof value;\n if (valueType === \"function\" || valueType === \"symbol\") {\n throw new TypeError(`Cannot hash ${valueType} values`);\n }\n if (value === null || valueType === \"string\" || valueType === \"number\" || valueType === \"boolean\") {\n return value;\n }\n if (valueType === \"bigint\") {\n throw new TypeError(\"Cannot hash bigint values\");\n }\n if (typeof value.toJSON === \"function\") {\n return canonicalize(value.toJSON(), seen);\n }\n if (Array.isArray(value)) {\n return value.map((item) => canonicalize(item, seen));\n }\n if (typeof value === \"object\") {\n if (seen.has(value)) {\n throw new TypeError(\"Cannot hash circular references\");\n }\n seen.add(value);\n const entries = Object.keys(value).sort();\n const result = {};\n for (const key of entries) {\n result[key] = canonicalize(value[key], seen);\n }\n seen.delete(value);\n return result;\n }\n throw new TypeError(`Cannot hash unsupported value type: ${valueType}`);\n}\nfunction canonicalStringify(data) {\n return JSON.stringify(canonicalize(data, /* @__PURE__ */ new WeakSet()));\n}\nasync function hashData(data) {\n const hash_buffer = await getHashBuffer(canonicalStringify(data));\n const hash_array = getHashArray(hash_buffer);\n return getHashHex(hash_array);\n}\nasync function hashBytes(input) {\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", toCryptoBuffer(input));\n return getHashHex(getHashArray(hashBuffer));\n}\nfunction generateColorStops(primaryColor, secondaryColor, steps) {\n const colors = [];\n for (let i = 0; i <= steps; i++) {\n const ratio = i / steps;\n const color = lerpColor(primaryColor, secondaryColor, ratio);\n colors.push(color);\n }\n return colors;\n}\nfunction lerpColor(color1, color2, ratio) {\n const r1 = parseInt(color1.substring(1, 3), 16);\n const g1 = parseInt(color1.substring(3, 5), 16);\n const b1 = parseInt(color1.substring(5, 7), 16);\n const r2 = parseInt(color2.substring(1, 3), 16);\n const g2 = parseInt(color2.substring(3, 5), 16);\n const b2 = parseInt(color2.substring(5, 7), 16);\n const r = Math.round(r1 + (r2 - r1) * ratio);\n const g = Math.round(g1 + (g2 - g1) * ratio);\n const b = Math.round(b1 + (b2 - b1) * ratio);\n return `#${(1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1).toUpperCase()}`;\n}\nexport { addNewLines, arrayBufferToBase64, b64decode, b64encode, base64ToArrayBuffer, canonicalStringify, decode, encode, formatEncryptionFile, formatIdentityKey, generateColorStops, generateId, getHashArray, getHashBuffer, getHashHex, hashBytes, hashData, removeLines, stripEncryptionFile, stripIdentityKey };\n"],"names":[],"mappings":"AA6EA,SAAS,eAAe,MAAM;AAC5B,QAAM,QAAQ,gBAAgB,aAAa,OAAO,IAAI,WAAW,IAAI;AACrE,SAAO,WAAW,KAAK,KAAK,EAAE;AAChC;AAIA,SAAS,aAAa,MAAM;AAC1B,SAAO,MAAM,KAAK,IAAI,WAAW,IAAI,CAAC;AACxC;AACA,SAAS,WAAW,MAAM;AACxB,SAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AACrE;AACA,SAAS,aAAa,OAAO,MAAM;AACjC,MAAI,UAAU,QAAQ;AACpB,UAAM,IAAI,UAAU,uBAAuB;AAAA,EAC5C;AACD,QAAM,YAAY,OAAO;AACzB,MAAI,cAAc,cAAc,cAAc,UAAU;AACtD,UAAM,IAAI,UAAU,eAAe,kBAAkB;AAAA,EACtD;AACD,MAAI,UAAU,QAAQ,cAAc,YAAY,cAAc,YAAY,cAAc,WAAW;AACjG,WAAO;AAAA,EACR;AACD,MAAI,cAAc,UAAU;AAC1B,UAAM,IAAI,UAAU,2BAA2B;AAAA,EAChD;AACD,MAAI,OAAO,MAAM,WAAW,YAAY;AACtC,WAAO,aAAa,MAAM,OAAQ,GAAE,IAAI;AAAA,EACzC;AACD,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,SAAS,aAAa,MAAM,IAAI,CAAC;AAAA,EACpD;AACD,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,KAAK,IAAI,KAAK,GAAG;AACnB,YAAM,IAAI,UAAU,iCAAiC;AAAA,IACtD;AACD,SAAK,IAAI,KAAK;AACd,UAAM,UAAU,OAAO,KAAK,KAAK,EAAE,KAAI;AACvC,UAAM,SAAS,CAAA;AACf,eAAW,OAAO,SAAS;AACzB,aAAO,OAAO,aAAa,MAAM,MAAM,IAAI;AAAA,IAC5C;AACD,SAAK,OAAO,KAAK;AACjB,WAAO;AAAA,EACR;AACD,QAAM,IAAI,UAAU,uCAAuC,WAAW;AACxE;AACA,SAAS,mBAAmB,MAAM;AAChC,SAAO,KAAK,UAAU,aAAa,MAAsB,oBAAI,QAAS,CAAA,CAAC;AACzE;AAMA,eAAe,UAAU,OAAO;AAC9B,QAAM,aAAa,MAAM,OAAO,OAAO,OAAO,WAAW,eAAe,KAAK,CAAC;AAC9E,SAAO,WAAW,aAAa,UAAU,CAAC;AAC5C;;"}