@ternent/seal-cli 0.2.0 → 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/dist/proof.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"proof.js","sources":["../src/proof.ts"],"sourcesContent":["import { canonicalStringify, hashBytes } from \"ternent-utils\";\nimport {\n resolveSealSigner,\n signSealUtf8,\n verifyPublicKeyKeyId,\n verifySealUtf8,\n type SealSignerInput,\n} from \"./crypto\";\n\nexport const SEAL_PROOF_VERSION = \"2\" as const;\nexport const SEAL_PROOF_TYPE = \"seal-proof\" as const;\nexport const SEAL_PUBLIC_KEY_TYPE = \"seal-public-key\" as const;\nexport const SEAL_SIGNATURE_ALGORITHM = \"Ed25519\" as const;\n\nexport type SealSubjectKind = \"file\" | \"manifest\";\n\nexport type SealProofV1 = {\n version: typeof SEAL_PROOF_VERSION;\n type: typeof SEAL_PROOF_TYPE;\n algorithm: typeof SEAL_SIGNATURE_ALGORITHM;\n createdAt: string;\n subject: {\n kind: SealSubjectKind;\n path: string;\n hash: `sha256:${string}`;\n };\n signer: {\n publicKey: string;\n keyId: string;\n };\n signature: string;\n};\n\nexport type SealPublicKeyArtifact = {\n version: typeof SEAL_PROOF_VERSION;\n type: typeof SEAL_PUBLIC_KEY_TYPE;\n algorithm: typeof SEAL_SIGNATURE_ALGORITHM;\n publicKey: string;\n keyId: string;\n};\n\ntype SealProofSignableFields = Omit<SealProofV1, \"signature\">;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction hasOnlyKeys(\n value: Record<string, unknown>,\n allowed: string[]\n): boolean {\n return Object.keys(value).every((key) => allowed.includes(key));\n}\n\nfunction isSealHash(value: unknown): value is `sha256:${string}` {\n return typeof value === \"string\" && /^sha256:[0-9a-f]{64}$/.test(value);\n}\n\nfunction isIsoDate(value: string): boolean {\n return !Number.isNaN(Date.parse(value));\n}\n\nexport function getSealProofSignableFields(\n proof: SealProofV1 | SealProofSignableFields\n): SealProofSignableFields {\n return {\n version: proof.version,\n type: proof.type,\n algorithm: proof.algorithm,\n createdAt: proof.createdAt,\n subject: proof.subject,\n signer: proof.signer,\n };\n}\n\nexport function getSealProofSigningPayload(\n proof: SealProofV1 | SealProofSignableFields\n): string {\n return canonicalStringify(getSealProofSignableFields(proof));\n}\n\nexport async function createSealHash(\n bytes: Uint8Array | ArrayBuffer\n): Promise<`sha256:${string}`> {\n const hash = await hashBytes(bytes);\n return `sha256:${hash}`;\n}\n\nexport async function createSealProof(input: {\n createdAt?: string;\n signer: SealSignerInput;\n subject: SealProofV1[\"subject\"];\n}): Promise<SealProofV1> {\n const signer = await resolveSealSigner(input.signer);\n const fields: SealProofSignableFields = {\n version: SEAL_PROOF_VERSION,\n type: SEAL_PROOF_TYPE,\n algorithm: SEAL_SIGNATURE_ALGORITHM,\n createdAt: input.createdAt ?? new Date().toISOString(),\n subject: input.subject,\n signer: {\n publicKey: signer.publicKey,\n keyId: signer.keyId,\n },\n };\n\n const signature = await signSealUtf8(\n signer.identity,\n getSealProofSigningPayload(fields)\n );\n\n return {\n ...fields,\n signature,\n };\n}\n\nexport async function createSealPublicKeyArtifact(\n signer: SealSignerInput\n): Promise<SealPublicKeyArtifact> {\n const resolved = await resolveSealSigner(signer);\n return {\n version: SEAL_PROOF_VERSION,\n type: SEAL_PUBLIC_KEY_TYPE,\n algorithm: SEAL_SIGNATURE_ALGORITHM,\n publicKey: resolved.publicKey,\n keyId: resolved.keyId,\n };\n}\n\nexport function validateSealProofShape(value: unknown): {\n ok: boolean;\n errors: string[];\n proof: SealProofV1 | null;\n} {\n if (!isRecord(value)) {\n return { ok: false, errors: [\"Proof must be a JSON object.\"], proof: null };\n }\n\n const errors: string[] = [];\n\n if (\n !hasOnlyKeys(value, [\n \"version\",\n \"type\",\n \"algorithm\",\n \"createdAt\",\n \"subject\",\n \"signer\",\n \"signature\",\n ])\n ) {\n errors.push(\"Proof contains unsupported fields.\");\n }\n\n if (value.version !== SEAL_PROOF_VERSION) {\n errors.push(`Proof version must be ${SEAL_PROOF_VERSION}.`);\n }\n if (value.type !== SEAL_PROOF_TYPE) {\n errors.push(`Proof type must be ${SEAL_PROOF_TYPE}.`);\n }\n if (value.algorithm !== SEAL_SIGNATURE_ALGORITHM) {\n errors.push(`Proof algorithm must be ${SEAL_SIGNATURE_ALGORITHM}.`);\n }\n if (typeof value.createdAt !== \"string\" || !isIsoDate(value.createdAt)) {\n errors.push(\"Proof createdAt must be an ISO timestamp.\");\n }\n if (typeof value.signature !== \"string\" || value.signature.length === 0) {\n errors.push(\"Proof signature must be a non-empty base64url string.\");\n }\n if (!isRecord(value.subject)) {\n errors.push(\"Proof subject must be an object.\");\n }\n if (!isRecord(value.signer)) {\n errors.push(\"Proof signer must be an object.\");\n }\n\n if (errors.length > 0 || !isRecord(value.subject) || !isRecord(value.signer)) {\n return { ok: false, errors, proof: null };\n }\n\n if (!hasOnlyKeys(value.subject, [\"kind\", \"path\", \"hash\"])) {\n errors.push(\"Proof subject contains unsupported fields.\");\n }\n if (!hasOnlyKeys(value.signer, [\"publicKey\", \"keyId\"])) {\n errors.push(\"Proof signer contains unsupported fields.\");\n }\n if (value.subject.kind !== \"file\" && value.subject.kind !== \"manifest\") {\n errors.push(\"Proof subject kind must be file or manifest.\");\n }\n if (typeof value.subject.path !== \"string\" || value.subject.path.length === 0) {\n errors.push(\"Proof subject path must be a non-empty string.\");\n }\n if (!isSealHash(value.subject.hash)) {\n errors.push(\"Proof subject hash must be a sha256 hash.\");\n }\n if (\n typeof value.signer.publicKey !== \"string\" ||\n value.signer.publicKey.length === 0\n ) {\n errors.push(\"Proof signer publicKey must be a non-empty base64url string.\");\n }\n if (typeof value.signer.keyId !== \"string\" || value.signer.keyId.length === 0) {\n errors.push(\"Proof signer keyId must be a non-empty string.\");\n }\n\n if (errors.length > 0) {\n return { ok: false, errors, proof: null };\n }\n\n return {\n ok: true,\n errors: [],\n proof: value as SealProofV1,\n };\n}\n\nexport function parseSealProofJson(raw: string): {\n ok: boolean;\n errors: string[];\n proof: SealProofV1 | null;\n} {\n try {\n return validateSealProofShape(JSON.parse(raw));\n } catch {\n return {\n ok: false,\n errors: [\"Proof JSON is not valid JSON.\"],\n proof: null,\n };\n }\n}\n\nexport function validateSealPublicKeyShape(value: unknown): {\n ok: boolean;\n errors: string[];\n artifact: SealPublicKeyArtifact | null;\n} {\n if (!isRecord(value)) {\n return {\n ok: false,\n errors: [\"Public key artifact must be a JSON object.\"],\n artifact: null,\n };\n }\n\n const errors: string[] = [];\n\n if (!hasOnlyKeys(value, [\"version\", \"type\", \"algorithm\", \"publicKey\", \"keyId\"])) {\n errors.push(\"Public key artifact contains unsupported fields.\");\n }\n if (value.version !== SEAL_PROOF_VERSION) {\n errors.push(`Public key artifact version must be ${SEAL_PROOF_VERSION}.`);\n }\n if (value.type !== SEAL_PUBLIC_KEY_TYPE) {\n errors.push(`Public key artifact type must be ${SEAL_PUBLIC_KEY_TYPE}.`);\n }\n if (value.algorithm !== SEAL_SIGNATURE_ALGORITHM) {\n errors.push(`Public key artifact algorithm must be ${SEAL_SIGNATURE_ALGORITHM}.`);\n }\n if (typeof value.publicKey !== \"string\" || value.publicKey.length === 0) {\n errors.push(\"Public key artifact publicKey must be a non-empty base64url string.\");\n }\n if (typeof value.keyId !== \"string\" || value.keyId.length === 0) {\n errors.push(\"Public key artifact keyId must be a non-empty string.\");\n }\n\n if (errors.length > 0) {\n return { ok: false, errors, artifact: null };\n }\n\n return {\n ok: true,\n errors: [],\n artifact: value as SealPublicKeyArtifact,\n };\n}\n\nexport function parseSealPublicKeyJson(raw: string): {\n ok: boolean;\n errors: string[];\n artifact: SealPublicKeyArtifact | null;\n} {\n try {\n return validateSealPublicKeyShape(JSON.parse(raw));\n } catch {\n return {\n ok: false,\n errors: [\"Public key JSON is not valid JSON.\"],\n artifact: null,\n };\n }\n}\n\nexport async function verifySealProofSignature(proof: SealProofV1): Promise<{\n ok: boolean;\n errors: string[];\n}> {\n const validation = validateSealProofShape(proof);\n if (!validation.ok || !validation.proof) {\n return { ok: false, errors: validation.errors };\n }\n\n if (!(await verifyPublicKeyKeyId(proof.signer.publicKey, proof.signer.keyId))) {\n return {\n ok: false,\n errors: [\"Proof signer keyId does not match signer public key.\"],\n };\n }\n\n try {\n const valid = await verifySealUtf8(\n proof.signature,\n getSealProofSigningPayload(proof),\n proof.signer.publicKey\n );\n if (!valid) {\n return { ok: false, errors: [\"Invalid signature.\"] };\n }\n return { ok: true, errors: [] };\n } catch (caught) {\n const message = caught instanceof Error ? caught.message : String(caught);\n return {\n ok: false,\n errors: [`Failed to verify signature: ${message}`],\n };\n }\n}\n\nexport async function verifySealProofAgainstBytes(\n proof: SealProofV1,\n bytes: Uint8Array | ArrayBuffer\n): Promise<{\n valid: boolean;\n hashMatch: boolean;\n signatureValid: boolean;\n keyId: string;\n algorithm: typeof SEAL_SIGNATURE_ALGORITHM;\n subjectHash: `sha256:${string}`;\n}> {\n const subjectHash = await createSealHash(bytes);\n const signatureCheck = await verifySealProofSignature(proof);\n const hashMatch = subjectHash === proof.subject.hash;\n const signatureValid = signatureCheck.ok;\n\n return {\n valid: hashMatch && signatureValid,\n hashMatch,\n signatureValid,\n keyId: proof.signer.keyId,\n algorithm: proof.algorithm,\n subjectHash,\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"proof.js","sources":["../src/proof.ts"],"sourcesContent":["import { canonicalStringify, hashBytes } from \"ternent-utils\";\nimport {\n resolveSealSigner,\n signSealUtf8,\n verifyPublicKeyKeyId,\n verifySealUtf8,\n type SealSignerInput,\n} from \"./crypto\";\n\nexport const SEAL_PROOF_VERSION = \"2\" as const;\nexport const SEAL_PROOF_TYPE = \"seal-proof\" as const;\nexport const SEAL_PUBLIC_KEY_TYPE = \"seal-public-key\" as const;\nexport const SEAL_SIGNATURE_ALGORITHM = \"Ed25519\" as const;\n\nexport type SealSubjectKind = \"file\" | \"manifest\";\n\nexport type SealProofV1 = {\n version: typeof SEAL_PROOF_VERSION;\n type: typeof SEAL_PROOF_TYPE;\n algorithm: typeof SEAL_SIGNATURE_ALGORITHM;\n createdAt: string;\n subject: {\n kind: SealSubjectKind;\n path: string;\n hash: `sha256:${string}`;\n };\n signer: {\n publicKey: string;\n keyId: string;\n };\n signature: string;\n};\n\nexport type SealPublicKeyArtifact = {\n version: typeof SEAL_PROOF_VERSION;\n type: typeof SEAL_PUBLIC_KEY_TYPE;\n algorithm: typeof SEAL_SIGNATURE_ALGORITHM;\n publicKey: string;\n keyId: string;\n};\n\ntype SealProofSignableFields = Omit<SealProofV1, \"signature\">;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction hasOnlyKeys(\n value: Record<string, unknown>,\n allowed: string[]\n): boolean {\n return Object.keys(value).every((key) => allowed.includes(key));\n}\n\nfunction isSealHash(value: unknown): value is `sha256:${string}` {\n return typeof value === \"string\" && /^sha256:[0-9a-f]{64}$/.test(value);\n}\n\nfunction isIsoDate(value: string): boolean {\n return !Number.isNaN(Date.parse(value));\n}\n\nexport function getSealProofSignableFields(\n proof: SealProofV1 | SealProofSignableFields\n): SealProofSignableFields {\n return {\n version: proof.version,\n type: proof.type,\n algorithm: proof.algorithm,\n createdAt: proof.createdAt,\n subject: proof.subject,\n signer: proof.signer,\n };\n}\n\nexport function getSealProofSigningPayload(\n proof: SealProofV1 | SealProofSignableFields\n): string {\n return canonicalStringify(getSealProofSignableFields(proof));\n}\n\nexport async function createSealHash(\n bytes: Uint8Array | ArrayBuffer\n): Promise<`sha256:${string}`> {\n const hash = await hashBytes(bytes);\n return `sha256:${hash}`;\n}\n\nexport async function createSealProof(input: {\n createdAt?: string;\n signer: SealSignerInput;\n subject: SealProofV1[\"subject\"];\n}): Promise<SealProofV1> {\n const signer = await resolveSealSigner(input.signer);\n const fields: SealProofSignableFields = {\n version: SEAL_PROOF_VERSION,\n type: SEAL_PROOF_TYPE,\n algorithm: SEAL_SIGNATURE_ALGORITHM,\n createdAt: input.createdAt ?? new Date().toISOString(),\n subject: input.subject,\n signer: {\n publicKey: signer.publicKey,\n keyId: signer.keyId,\n },\n };\n\n const signature = await signSealUtf8(\n signer.identity,\n getSealProofSigningPayload(fields)\n );\n\n return {\n ...fields,\n signature,\n };\n}\n\nexport async function createSealPublicKeyArtifact(\n signer: SealSignerInput\n): Promise<SealPublicKeyArtifact> {\n const resolved = await resolveSealSigner(signer);\n return {\n version: SEAL_PROOF_VERSION,\n type: SEAL_PUBLIC_KEY_TYPE,\n algorithm: SEAL_SIGNATURE_ALGORITHM,\n publicKey: resolved.publicKey,\n keyId: resolved.keyId,\n };\n}\n\nexport function validateSealProofShape(value: unknown): {\n ok: boolean;\n errors: string[];\n proof: SealProofV1 | null;\n} {\n if (!isRecord(value)) {\n return { ok: false, errors: [\"Proof must be a JSON object.\"], proof: null };\n }\n\n const errors: string[] = [];\n\n if (\n !hasOnlyKeys(value, [\n \"version\",\n \"type\",\n \"algorithm\",\n \"createdAt\",\n \"subject\",\n \"signer\",\n \"signature\",\n ])\n ) {\n errors.push(\"Proof contains unsupported fields.\");\n }\n\n if (value.version !== SEAL_PROOF_VERSION) {\n errors.push(`Proof version must be ${SEAL_PROOF_VERSION}.`);\n }\n if (value.type !== SEAL_PROOF_TYPE) {\n errors.push(`Proof type must be ${SEAL_PROOF_TYPE}.`);\n }\n if (value.algorithm !== SEAL_SIGNATURE_ALGORITHM) {\n errors.push(`Proof algorithm must be ${SEAL_SIGNATURE_ALGORITHM}.`);\n }\n if (typeof value.createdAt !== \"string\" || !isIsoDate(value.createdAt)) {\n errors.push(\"Proof createdAt must be an ISO timestamp.\");\n }\n if (typeof value.signature !== \"string\" || value.signature.length === 0) {\n errors.push(\"Proof signature must be a non-empty base64url string.\");\n }\n if (!isRecord(value.subject)) {\n errors.push(\"Proof subject must be an object.\");\n }\n if (!isRecord(value.signer)) {\n errors.push(\"Proof signer must be an object.\");\n }\n\n if (errors.length > 0 || !isRecord(value.subject) || !isRecord(value.signer)) {\n return { ok: false, errors, proof: null };\n }\n\n if (!hasOnlyKeys(value.subject, [\"kind\", \"path\", \"hash\"])) {\n errors.push(\"Proof subject contains unsupported fields.\");\n }\n if (!hasOnlyKeys(value.signer, [\"publicKey\", \"keyId\"])) {\n errors.push(\"Proof signer contains unsupported fields.\");\n }\n if (value.subject.kind !== \"file\" && value.subject.kind !== \"manifest\") {\n errors.push(\"Proof subject kind must be file or manifest.\");\n }\n if (typeof value.subject.path !== \"string\" || value.subject.path.length === 0) {\n errors.push(\"Proof subject path must be a non-empty string.\");\n }\n if (!isSealHash(value.subject.hash)) {\n errors.push(\"Proof subject hash must be a sha256 hash.\");\n }\n if (\n typeof value.signer.publicKey !== \"string\" ||\n value.signer.publicKey.length === 0\n ) {\n errors.push(\"Proof signer publicKey must be a non-empty base64url string.\");\n }\n if (typeof value.signer.keyId !== \"string\" || value.signer.keyId.length === 0) {\n errors.push(\"Proof signer keyId must be a non-empty string.\");\n }\n\n if (errors.length > 0) {\n return { ok: false, errors, proof: null };\n }\n\n return {\n ok: true,\n errors: [],\n proof: value as SealProofV1,\n };\n}\n\nexport function parseSealProofJson(raw: string): {\n ok: boolean;\n errors: string[];\n proof: SealProofV1 | null;\n} {\n try {\n return validateSealProofShape(JSON.parse(raw));\n } catch {\n return {\n ok: false,\n errors: [\"Proof JSON is not valid JSON.\"],\n proof: null,\n };\n }\n}\n\nexport function validateSealPublicKeyShape(value: unknown): {\n ok: boolean;\n errors: string[];\n artifact: SealPublicKeyArtifact | null;\n} {\n if (!isRecord(value)) {\n return {\n ok: false,\n errors: [\"Public key artifact must be a JSON object.\"],\n artifact: null,\n };\n }\n\n const errors: string[] = [];\n\n if (!hasOnlyKeys(value, [\"version\", \"type\", \"algorithm\", \"publicKey\", \"keyId\"])) {\n errors.push(\"Public key artifact contains unsupported fields.\");\n }\n if (value.version !== SEAL_PROOF_VERSION) {\n errors.push(`Public key artifact version must be ${SEAL_PROOF_VERSION}.`);\n }\n if (value.type !== SEAL_PUBLIC_KEY_TYPE) {\n errors.push(`Public key artifact type must be ${SEAL_PUBLIC_KEY_TYPE}.`);\n }\n if (value.algorithm !== SEAL_SIGNATURE_ALGORITHM) {\n errors.push(`Public key artifact algorithm must be ${SEAL_SIGNATURE_ALGORITHM}.`);\n }\n if (typeof value.publicKey !== \"string\" || value.publicKey.length === 0) {\n errors.push(\"Public key artifact publicKey must be a non-empty base64url string.\");\n }\n if (typeof value.keyId !== \"string\" || value.keyId.length === 0) {\n errors.push(\"Public key artifact keyId must be a non-empty string.\");\n }\n\n if (errors.length > 0) {\n return { ok: false, errors, artifact: null };\n }\n\n return {\n ok: true,\n errors: [],\n artifact: value as SealPublicKeyArtifact,\n };\n}\n\nexport function parseSealPublicKeyJson(raw: string): {\n ok: boolean;\n errors: string[];\n artifact: SealPublicKeyArtifact | null;\n} {\n try {\n return validateSealPublicKeyShape(JSON.parse(raw));\n } catch {\n return {\n ok: false,\n errors: [\"Public key JSON is not valid JSON.\"],\n artifact: null,\n };\n }\n}\n\nexport async function verifySealProofSignature(proof: SealProofV1): Promise<{\n ok: boolean;\n errors: string[];\n}> {\n const validation = validateSealProofShape(proof);\n if (!validation.ok || !validation.proof) {\n return { ok: false, errors: validation.errors };\n }\n\n if (!(await verifyPublicKeyKeyId(proof.signer.publicKey, proof.signer.keyId))) {\n return {\n ok: false,\n errors: [\"Proof signer keyId does not match signer public key.\"],\n };\n }\n\n try {\n const valid = await verifySealUtf8(\n proof.signature,\n getSealProofSigningPayload(proof),\n proof.signer.publicKey\n );\n if (!valid) {\n return { ok: false, errors: [\"Invalid signature.\"] };\n }\n return { ok: true, errors: [] };\n } catch (caught) {\n return {\n ok: false,\n errors: [\"Invalid signature.\"],\n };\n }\n}\n\nexport async function verifySealProofAgainstBytes(\n proof: SealProofV1,\n bytes: Uint8Array | ArrayBuffer\n): Promise<{\n valid: boolean;\n hashMatch: boolean;\n signatureValid: boolean;\n keyId: string;\n algorithm: typeof SEAL_SIGNATURE_ALGORITHM;\n subjectHash: `sha256:${string}`;\n}> {\n const subjectHash = await createSealHash(bytes);\n const signatureCheck = await verifySealProofSignature(proof);\n const hashMatch = subjectHash === proof.subject.hash;\n const signatureValid = signatureCheck.ok;\n\n return {\n valid: hashMatch && signatureValid,\n hashMatch,\n signatureValid,\n keyId: proof.signer.keyId,\n algorithm: proof.algorithm,\n subjectHash,\n };\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ternent/seal-cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Deterministic artifact signing for Seal",
5
5
  "license": "ISC",
6
6
  "type": "module",
@@ -44,8 +44,8 @@
44
44
  "devDependencies": {
45
45
  "vite": "^2.9.18",
46
46
  "vitest": "^1.6.0",
47
- "@ternent/identity": "0.2.0",
48
- "ternent-utils": "1.1.1"
47
+ "ternent-utils": "1.1.1",
48
+ "@ternent/identity": "0.3.0"
49
49
  },
50
50
  "scripts": {
51
51
  "build": "vite build",
@@ -1,245 +0,0 @@
1
- const IDENTITY_FORMAT = "ternent-identity";
2
- const IDENTITY_VERSION = "2";
3
- const IDENTITY_ALGORITHM = "Ed25519";
4
- const ED25519_CONTEXT = "ternent-seal/v2";
5
- const ED25519_PKCS8_PREFIX = hexToBytes("302e020100300506032b657004220420");
6
- const ED25519_SPKI_PREFIX = hexToBytes("302a300506032b6570032100");
7
- hexToBytes("302e020100300506032b656e04220420");
8
- function getWebCrypto() {
9
- if (typeof globalThis.crypto !== "undefined") {
10
- return globalThis.crypto;
11
- }
12
- throw new Error("Web Crypto is not available in this runtime.");
13
- }
14
- function base64Encode(bytes) {
15
- const buffer = globalThis.Buffer;
16
- if (buffer) {
17
- return buffer.from(bytes).toString("base64");
18
- }
19
- let binary = "";
20
- for (const byte of bytes) {
21
- binary += String.fromCharCode(byte);
22
- }
23
- if (typeof btoa !== "undefined") {
24
- return btoa(binary);
25
- }
26
- throw new Error("Base64 encoding is not available in this runtime.");
27
- }
28
- function base64Decode(value) {
29
- const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
30
- const buffer = globalThis.Buffer;
31
- if (buffer) {
32
- return new Uint8Array(buffer.from(normalized, "base64"));
33
- }
34
- if (typeof atob !== "undefined") {
35
- const binary = atob(normalized);
36
- const bytes = new Uint8Array(binary.length);
37
- for (let index = 0; index < binary.length; index += 1) {
38
- bytes[index] = binary.charCodeAt(index);
39
- }
40
- return bytes;
41
- }
42
- throw new Error("Base64 decoding is not available in this runtime.");
43
- }
44
- function isRecord(value) {
45
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
46
- }
47
- function hexToBytes(value) {
48
- const bytes = new Uint8Array(value.length / 2);
49
- for (let index = 0; index < value.length; index += 2) {
50
- bytes[index / 2] = Number.parseInt(value.slice(index, index + 2), 16);
51
- }
52
- return bytes;
53
- }
54
- function concatBytes(...parts) {
55
- const length = parts.reduce((sum, part) => sum + part.length, 0);
56
- const output = new Uint8Array(length);
57
- let offset = 0;
58
- for (const part of parts) {
59
- output.set(part, offset);
60
- offset += part.length;
61
- }
62
- return output;
63
- }
64
- function ensureBytes(value, label) {
65
- const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);
66
- if (bytes.length === 0) {
67
- throw new Error(`${label} must not be empty.`);
68
- }
69
- return bytes;
70
- }
71
- function bytesToHex(bytes) {
72
- return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(
73
- ""
74
- );
75
- }
76
- function normalizeBase64Url(value) {
77
- return String(value || "").trim().replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
78
- }
79
- function base64UrlEncode(bytes) {
80
- return base64Encode(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
81
- }
82
- function base64UrlDecode(value) {
83
- const normalized = normalizeBase64Url(value);
84
- if (!normalized) {
85
- throw new Error("Base64url value is required.");
86
- }
87
- const pad = normalized.length % 4 === 0 ? "" : "=".repeat(4 - normalized.length % 4);
88
- return base64Decode(`${normalized}${pad}`);
89
- }
90
- function utf8Bytes(value) {
91
- return new TextEncoder().encode(value);
92
- }
93
- function combineContext(payload, context = ED25519_CONTEXT) {
94
- return concatBytes(utf8Bytes(context), new Uint8Array([0]), payload);
95
- }
96
- async function sha256(bytes) {
97
- const hash = await getWebCrypto().subtle.digest("SHA-256", bytes);
98
- return new Uint8Array(hash);
99
- }
100
- async function importEd25519PrivateKey(seedBytes) {
101
- return getWebCrypto().subtle.importKey(
102
- "pkcs8",
103
- concatBytes(ED25519_PKCS8_PREFIX, seedBytes),
104
- { name: "Ed25519" },
105
- true,
106
- ["sign"]
107
- );
108
- }
109
- async function importEd25519PublicKey(publicKeyBytes) {
110
- return getWebCrypto().subtle.importKey(
111
- "spki",
112
- concatBytes(ED25519_SPKI_PREFIX, publicKeyBytes),
113
- { name: "Ed25519" },
114
- true,
115
- ["verify"]
116
- );
117
- }
118
- function isSerializedIdentity(value) {
119
- return isRecord(value) && value.format === IDENTITY_FORMAT && value.version === IDENTITY_VERSION && value.algorithm === IDENTITY_ALGORITHM && typeof value.createdAt === "string" && typeof value.seed === "string" && typeof value.publicKey === "string" && typeof value.keyId === "string";
120
- }
121
- function toCanonicalIdentity(identity) {
122
- return {
123
- format: identity.format,
124
- version: identity.version,
125
- algorithm: identity.algorithm,
126
- createdAt: identity.createdAt,
127
- seed: identity.seed,
128
- publicKey: identity.publicKey,
129
- keyId: identity.keyId
130
- };
131
- }
132
- function resolveSeedBytes(input) {
133
- if (input instanceof Uint8Array || input instanceof ArrayBuffer) {
134
- const bytes = ensureBytes(input, "Seed");
135
- if (bytes.length !== 32) {
136
- throw new Error("Seed must be 32 bytes.");
137
- }
138
- return bytes;
139
- }
140
- if (typeof input === "string") {
141
- const bytes = base64UrlDecode(input);
142
- if (bytes.length !== 32) {
143
- throw new Error("Seed must be 32 bytes.");
144
- }
145
- return bytes;
146
- }
147
- if (isSerializedIdentity(input)) {
148
- return resolveSeedBytes(input.seed);
149
- }
150
- throw new Error("A valid identity or 32-byte seed is required.");
151
- }
152
- function resolvePublicKeyBytes(input) {
153
- if (input instanceof Uint8Array || input instanceof ArrayBuffer) {
154
- const bytes = ensureBytes(input, "Public key");
155
- if (bytes.length !== 32) {
156
- throw new Error("Public key must be 32 bytes.");
157
- }
158
- return bytes;
159
- }
160
- if (typeof input === "string") {
161
- const bytes = base64UrlDecode(input);
162
- if (bytes.length !== 32) {
163
- throw new Error("Public key must be 32 bytes.");
164
- }
165
- return bytes;
166
- }
167
- if (isSerializedIdentity(input)) {
168
- return resolvePublicKeyBytes(input.publicKey);
169
- }
170
- if (isRecord(input) && "publicKey" in input) {
171
- return resolvePublicKeyBytes(
172
- input.publicKey
173
- );
174
- }
175
- throw new Error("A valid 32-byte public key is required.");
176
- }
177
- async function deriveKeyId(publicKey) {
178
- return bytesToHex(await sha256(resolvePublicKeyBytes(publicKey)));
179
- }
180
- function parseIdentity(input) {
181
- const parsed = typeof input === "string" ? JSON.parse(input) : input;
182
- if (!isSerializedIdentity(parsed)) {
183
- throw new Error("Identity payload must be a ternent-identity v2 object.");
184
- }
185
- return toCanonicalIdentity(parsed);
186
- }
187
- function serializeIdentity(identity, pretty = true) {
188
- return `${JSON.stringify(parseIdentity(identity), null, pretty ? 2 : 0)}
189
- `;
190
- }
191
- async function signBytes(identityOrSeed, payload, options = {}) {
192
- const privateKey = await importEd25519PrivateKey(resolveSeedBytes(identityOrSeed));
193
- const signature = await getWebCrypto().subtle.sign(
194
- "Ed25519",
195
- privateKey,
196
- combineContext(ensureBytes(payload, "Payload"), options.context)
197
- );
198
- return base64UrlEncode(new Uint8Array(signature));
199
- }
200
- async function verifyBytes(publicKey, payload, signature, options = {}) {
201
- const verifyKey = await importEd25519PublicKey(resolvePublicKeyBytes(publicKey));
202
- return getWebCrypto().subtle.verify(
203
- "Ed25519",
204
- verifyKey,
205
- base64UrlDecode(signature),
206
- combineContext(ensureBytes(payload, "Payload"), options.context)
207
- );
208
- }
209
- async function signUtf8(identityOrSeed, value, options = {}) {
210
- return signBytes(identityOrSeed, utf8Bytes(value), options);
211
- }
212
- async function verifyUtf8(publicKey, value, signature, options = {}) {
213
- return verifyBytes(publicKey, utf8Bytes(value), signature, options);
214
- }
215
- const SEAL_SIGNATURE_CONTEXT = "ternent-seal/v2";
216
- function exportIdentityJson(identity) {
217
- return serializeIdentity(identity);
218
- }
219
- async function resolveSealSigner(input) {
220
- const identity = parseIdentity(input.identity);
221
- const keyId = await deriveKeyId(identity.publicKey);
222
- if (keyId !== identity.keyId) {
223
- throw new Error("Identity keyId does not match the signer public key.");
224
- }
225
- return {
226
- identity,
227
- publicKey: identity.publicKey,
228
- keyId
229
- };
230
- }
231
- async function signSealUtf8(identity, value) {
232
- return signUtf8(identity, value, {
233
- context: SEAL_SIGNATURE_CONTEXT
234
- });
235
- }
236
- async function verifySealUtf8(signature, value, publicKey) {
237
- return verifyUtf8(publicKey, value, signature, {
238
- context: SEAL_SIGNATURE_CONTEXT
239
- });
240
- }
241
- async function verifyPublicKeyKeyId(publicKey, keyId) {
242
- return await deriveKeyId(publicKey) === keyId;
243
- }
244
- export { SEAL_SIGNATURE_CONTEXT as S, verifySealUtf8 as a, exportIdentityJson as e, parseIdentity as p, resolveSealSigner as r, signSealUtf8 as s, verifyPublicKeyKeyId as v };
245
- //# sourceMappingURL=crypto-8a814dd3.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"crypto-8a814dd3.js","sources":["../../../identity-v2/src/index.ts","../../src/crypto.ts"],"sourcesContent":["const IDENTITY_FORMAT = \"ternent-identity\" as const;\nconst IDENTITY_VERSION = \"2\" as const;\nconst IDENTITY_ALGORITHM = \"Ed25519\" as const;\nconst ED25519_CONTEXT = \"ternent-seal/v2\";\n\nconst ED25519_PKCS8_PREFIX = hexToBytes(\"302e020100300506032b657004220420\");\nconst ED25519_SPKI_PREFIX = hexToBytes(\"302a300506032b6570032100\");\nconst X25519_PKCS8_PREFIX = hexToBytes(\"302e020100300506032b656e04220420\");\n\nconst CURVE25519_P = (1n << 255n) - 19n;\nconst BECH32_GENERATORS = [\n 0x3b6a57b2,\n 0x26508e6d,\n 0x1ea119fa,\n 0x3d4233dd,\n 0x2a1462b3,\n];\n\nfunction getWebCrypto(): Crypto {\n if (typeof globalThis.crypto !== \"undefined\") {\n return globalThis.crypto;\n }\n throw new Error(\"Web Crypto is not available in this runtime.\");\n}\n\nfunction base64Encode(bytes: Uint8Array): string {\n const buffer = (globalThis as typeof globalThis & {\n Buffer?: {\n from(input: Uint8Array): { toString(encoding: \"base64\"): string };\n from(input: string, encoding: \"base64\"): Uint8Array;\n };\n }).Buffer;\n\n if (buffer) {\n return buffer.from(bytes).toString(\"base64\");\n }\n\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n if (typeof btoa !== \"undefined\") {\n return btoa(binary);\n }\n throw new Error(\"Base64 encoding is not available in this runtime.\");\n}\n\nfunction base64Decode(value: string): Uint8Array {\n const normalized = value.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const buffer = (globalThis as typeof globalThis & {\n Buffer?: {\n from(input: Uint8Array): { toString(encoding: \"base64\"): string };\n from(input: string, encoding: \"base64\"): Uint8Array;\n };\n }).Buffer;\n\n if (buffer) {\n return new Uint8Array(buffer.from(normalized, \"base64\"));\n }\n\n if (typeof atob !== \"undefined\") {\n const binary = atob(normalized);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n return bytes;\n }\n\n throw new Error(\"Base64 decoding is not available in this runtime.\");\n}\n\nexport type SerializedIdentity = {\n format: typeof IDENTITY_FORMAT;\n version: typeof IDENTITY_VERSION;\n algorithm: typeof IDENTITY_ALGORITHM;\n createdAt: string;\n seed: string;\n publicKey: string;\n keyId: string;\n};\n\ntype SeedLike = SerializedIdentity | Uint8Array | ArrayBuffer | string;\ntype PublicKeyLike =\n | SerializedIdentity\n | Uint8Array\n | ArrayBuffer\n | string\n | { publicKey: string | Uint8Array | ArrayBuffer }\n | { seed: string | Uint8Array | ArrayBuffer };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction hexToBytes(value: string): Uint8Array {\n const bytes = new Uint8Array(value.length / 2);\n for (let index = 0; index < value.length; index += 2) {\n bytes[index / 2] = Number.parseInt(value.slice(index, index + 2), 16);\n }\n return bytes;\n}\n\nfunction concatBytes(...parts: Uint8Array[]): Uint8Array {\n const length = parts.reduce((sum, part) => sum + part.length, 0);\n const output = new Uint8Array(length);\n let offset = 0;\n for (const part of parts) {\n output.set(part, offset);\n offset += part.length;\n }\n return output;\n}\n\nfunction ensureBytes(\n value: Uint8Array | ArrayBuffer,\n label: string\n): Uint8Array {\n const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);\n if (bytes.length === 0) {\n throw new Error(`${label} must not be empty.`);\n }\n return bytes;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\n \"\"\n );\n}\n\nfunction normalizeBase64Url(value: string): string {\n return String(value || \"\")\n .trim()\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/g, \"\");\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n return base64Encode(bytes)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/g, \"\");\n}\n\nfunction base64UrlDecode(value: string): Uint8Array {\n const normalized = normalizeBase64Url(value);\n if (!normalized) {\n throw new Error(\"Base64url value is required.\");\n }\n const pad =\n normalized.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (normalized.length % 4));\n return base64Decode(`${normalized}${pad}`);\n}\n\nfunction utf8Bytes(value: string): Uint8Array {\n return new TextEncoder().encode(value);\n}\n\nfunction combineContext(\n payload: Uint8Array,\n context = ED25519_CONTEXT\n): Uint8Array {\n return concatBytes(utf8Bytes(context), new Uint8Array([0]), payload);\n}\n\nasync function sha256(bytes: Uint8Array): Promise<Uint8Array> {\n const hash = await getWebCrypto().subtle.digest(\"SHA-256\", bytes);\n return new Uint8Array(hash);\n}\n\nasync function sha512(bytes: Uint8Array): Promise<Uint8Array> {\n const hash = await getWebCrypto().subtle.digest(\"SHA-512\", bytes);\n return new Uint8Array(hash);\n}\n\nfunction toLittleEndianBigInt(bytes: Uint8Array): bigint {\n let result = 0n;\n for (let index = bytes.length - 1; index >= 0; index -= 1) {\n result = (result << 8n) + BigInt(bytes[index]);\n }\n return result;\n}\n\nfunction fromLittleEndianBigInt(value: bigint, length: number): Uint8Array {\n let remaining = value;\n const bytes = new Uint8Array(length);\n for (let index = 0; index < length; index += 1) {\n bytes[index] = Number(remaining & 0xffn);\n remaining >>= 8n;\n }\n return bytes;\n}\n\nfunction mod(value: bigint, modulus: bigint): bigint {\n const result = value % modulus;\n return result >= 0n ? result : result + modulus;\n}\n\nfunction modPow(base: bigint, exponent: bigint, modulus: bigint): bigint {\n let result = 1n;\n let factor = mod(base, modulus);\n let power = exponent;\n while (power > 0n) {\n if (power & 1n) {\n result = mod(result * factor, modulus);\n }\n factor = mod(factor * factor, modulus);\n power >>= 1n;\n }\n return result;\n}\n\nfunction modInverse(value: bigint, modulus: bigint): bigint {\n if (value === 0n) {\n throw new Error(\"Cannot invert zero in the field.\");\n }\n return modPow(value, modulus - 2n, modulus);\n}\n\nasync function importEd25519PrivateKey(seedBytes: Uint8Array): Promise<CryptoKey> {\n return getWebCrypto().subtle.importKey(\n \"pkcs8\",\n concatBytes(ED25519_PKCS8_PREFIX, seedBytes),\n { name: \"Ed25519\" },\n true,\n [\"sign\"]\n );\n}\n\nasync function importEd25519PublicKey(publicKeyBytes: Uint8Array): Promise<CryptoKey> {\n return getWebCrypto().subtle.importKey(\n \"spki\",\n concatBytes(ED25519_SPKI_PREFIX, publicKeyBytes),\n { name: \"Ed25519\" },\n true,\n [\"verify\"]\n );\n}\n\nasync function exportEd25519PublicKeyFromSeed(\n seedBytes: Uint8Array\n): Promise<Uint8Array> {\n const privateKey = await importEd25519PrivateKey(seedBytes);\n const jwk = (await getWebCrypto().subtle.exportKey(\n \"jwk\",\n privateKey\n )) as JsonWebKey;\n if (!jwk.x) {\n throw new Error(\"Unable to derive Ed25519 public key.\");\n }\n return base64UrlDecode(jwk.x);\n}\n\nasync function exportX25519PublicKeyFromScalar(\n scalarBytes: Uint8Array\n): Promise<Uint8Array> {\n const privateKey = await getWebCrypto().subtle.importKey(\n \"pkcs8\",\n concatBytes(X25519_PKCS8_PREFIX, scalarBytes),\n { name: \"X25519\" },\n true,\n [\"deriveBits\"]\n );\n const jwk = (await getWebCrypto().subtle.exportKey(\n \"jwk\",\n privateKey\n )) as JsonWebKey;\n if (!jwk.x) {\n throw new Error(\"Unable to derive X25519 public key.\");\n }\n return base64UrlDecode(jwk.x);\n}\n\nfunction isSerializedIdentity(value: unknown): value is SerializedIdentity {\n return (\n isRecord(value) &&\n value.format === IDENTITY_FORMAT &&\n value.version === IDENTITY_VERSION &&\n value.algorithm === IDENTITY_ALGORITHM &&\n typeof value.createdAt === \"string\" &&\n typeof value.seed === \"string\" &&\n typeof value.publicKey === \"string\" &&\n typeof value.keyId === \"string\"\n );\n}\n\nfunction toCanonicalIdentity(identity: SerializedIdentity): SerializedIdentity {\n return {\n format: identity.format,\n version: identity.version,\n algorithm: identity.algorithm,\n createdAt: identity.createdAt,\n seed: identity.seed,\n publicKey: identity.publicKey,\n keyId: identity.keyId,\n };\n}\n\nfunction resolveSeedBytes(input: SeedLike): Uint8Array {\n if (input instanceof Uint8Array || input instanceof ArrayBuffer) {\n const bytes = ensureBytes(input, \"Seed\");\n if (bytes.length !== 32) {\n throw new Error(\"Seed must be 32 bytes.\");\n }\n return bytes;\n }\n if (typeof input === \"string\") {\n const bytes = base64UrlDecode(input);\n if (bytes.length !== 32) {\n throw new Error(\"Seed must be 32 bytes.\");\n }\n return bytes;\n }\n if (isSerializedIdentity(input)) {\n return resolveSeedBytes(input.seed);\n }\n throw new Error(\"A valid identity or 32-byte seed is required.\");\n}\n\nfunction resolvePublicKeyBytes(input: PublicKeyLike): Uint8Array {\n if (input instanceof Uint8Array || input instanceof ArrayBuffer) {\n const bytes = ensureBytes(input, \"Public key\");\n if (bytes.length !== 32) {\n throw new Error(\"Public key must be 32 bytes.\");\n }\n return bytes;\n }\n if (typeof input === \"string\") {\n const bytes = base64UrlDecode(input);\n if (bytes.length !== 32) {\n throw new Error(\"Public key must be 32 bytes.\");\n }\n return bytes;\n }\n if (isSerializedIdentity(input)) {\n return resolvePublicKeyBytes(input.publicKey);\n }\n if (isRecord(input) && \"publicKey\" in input) {\n return resolvePublicKeyBytes(\n input.publicKey as string | Uint8Array | ArrayBuffer\n );\n }\n throw new Error(\"A valid 32-byte public key is required.\");\n}\n\nasync function resolveX25519PrivateKeyBytes(input: SeedLike): Promise<Uint8Array> {\n const seedBytes = resolveSeedBytes(input);\n const hashed = await sha512(seedBytes);\n const scalar = hashed.slice(0, 32);\n scalar[0] &= 248;\n scalar[31] &= 127;\n scalar[31] |= 64;\n return scalar;\n}\n\nfunction convertEd25519PublicKeyToX25519PublicKeyBytes(\n publicKeyBytes: Uint8Array\n): Uint8Array {\n const yBytes = publicKeyBytes.slice();\n yBytes[31] &= 0x7f;\n const y = toLittleEndianBigInt(yBytes);\n const numerator = mod(1n + y, CURVE25519_P);\n const denominator = mod(1n - y, CURVE25519_P);\n const u = mod(\n numerator * modInverse(denominator, CURVE25519_P),\n CURVE25519_P\n );\n return fromLittleEndianBigInt(u, 32);\n}\n\nasync function resolveX25519PublicKeyBytes(\n input: PublicKeyLike\n): Promise<Uint8Array> {\n if (isRecord(input) && \"seed\" in input) {\n return exportX25519PublicKeyFromScalar(\n await resolveX25519PrivateKeyBytes(\n input.seed as string | Uint8Array | ArrayBuffer\n )\n );\n }\n if (isSerializedIdentity(input)) {\n return exportX25519PublicKeyFromScalar(\n await resolveX25519PrivateKeyBytes(input)\n );\n }\n if (\n typeof input === \"string\" ||\n input instanceof Uint8Array ||\n input instanceof ArrayBuffer\n ) {\n return convertEd25519PublicKeyToX25519PublicKeyBytes(\n resolvePublicKeyBytes(input)\n );\n }\n if (isRecord(input) && \"publicKey\" in input) {\n return convertEd25519PublicKeyToX25519PublicKeyBytes(\n resolvePublicKeyBytes(\n input.publicKey as string | Uint8Array | ArrayBuffer\n )\n );\n }\n throw new Error(\"A valid identity seed or Ed25519 public key is required.\");\n}\n\nfunction bech32Polymod(values: number[]): number {\n let checksum = 1;\n for (const value of values) {\n const top = checksum >>> 25;\n checksum = ((checksum & 0x1ffffff) << 5) ^ value;\n for (let bit = 0; bit < 5; bit += 1) {\n if ((top >>> bit) & 1) {\n checksum ^= BECH32_GENERATORS[bit];\n }\n }\n }\n return checksum;\n}\n\nfunction bech32HrpExpand(hrp: string): number[] {\n const output: number[] = [];\n for (let index = 0; index < hrp.length; index += 1) {\n output.push(hrp.charCodeAt(index) >> 5);\n }\n output.push(0);\n for (let index = 0; index < hrp.length; index += 1) {\n output.push(hrp.charCodeAt(index) & 31);\n }\n return output;\n}\n\nfunction convertBits(data: Uint8Array, fromBits: number, toBits: number): number[] {\n let value = 0;\n let bits = 0;\n const maxValue = (1 << toBits) - 1;\n const output: number[] = [];\n for (const byte of data) {\n value = (value << fromBits) | byte;\n bits += fromBits;\n while (bits >= toBits) {\n bits -= toBits;\n output.push((value >> bits) & maxValue);\n }\n }\n if (bits > 0) {\n output.push((value << (toBits - bits)) & maxValue);\n }\n return output;\n}\n\nfunction bech32CreateChecksum(hrp: string, data: number[]): number[] {\n const values = [...bech32HrpExpand(hrp), ...data, 0, 0, 0, 0, 0, 0];\n const polymod = bech32Polymod(values) ^ 1;\n const checksum: number[] = [];\n for (let index = 0; index < 6; index += 1) {\n checksum.push((polymod >> (5 * (5 - index))) & 31);\n }\n return checksum;\n}\n\nfunction bech32Encode(hrp: string, data: Uint8Array): string {\n const charset = \"qpzry9x8gf2tvdw0s3jn54khce6mua7l\";\n const words = convertBits(data, 8, 5);\n const combined = [...words, ...bech32CreateChecksum(hrp, words)];\n return `${hrp}1${combined.map((value) => charset[value]).join(\"\")}`;\n}\n\nexport function getDefaultSignatureContext(): string {\n return ED25519_CONTEXT;\n}\n\nexport async function createIdentity(\n createdAt = new Date().toISOString()\n): Promise<SerializedIdentity> {\n const seedBytes = getWebCrypto().getRandomValues(new Uint8Array(32));\n const publicKeyBytes = await exportEd25519PublicKeyFromSeed(seedBytes);\n return {\n format: IDENTITY_FORMAT,\n version: IDENTITY_VERSION,\n algorithm: IDENTITY_ALGORITHM,\n createdAt,\n seed: base64UrlEncode(seedBytes),\n publicKey: base64UrlEncode(publicKeyBytes),\n keyId: await deriveKeyId(publicKeyBytes),\n };\n}\n\nexport async function derivePublicKey(\n seed: string | Uint8Array | ArrayBuffer\n): Promise<string> {\n return base64UrlEncode(\n await exportEd25519PublicKeyFromSeed(resolveSeedBytes(seed))\n );\n}\n\nexport async function deriveKeyId(\n publicKey: string | Uint8Array | ArrayBuffer\n): Promise<string> {\n return bytesToHex(await sha256(resolvePublicKeyBytes(publicKey)));\n}\n\nexport function parseIdentity(input: string | SerializedIdentity): SerializedIdentity {\n const parsed = typeof input === \"string\" ? JSON.parse(input) : input;\n if (!isSerializedIdentity(parsed)) {\n throw new Error(\"Identity payload must be a ternent-identity v2 object.\");\n }\n return toCanonicalIdentity(parsed);\n}\n\nexport function serializeIdentity(\n identity: SerializedIdentity,\n pretty = true\n): string {\n return `${JSON.stringify(parseIdentity(identity), null, pretty ? 2 : 0)}\\n`;\n}\n\nexport async function validateIdentity(\n identity: SerializedIdentity\n): Promise<SerializedIdentity> {\n const parsed = parseIdentity(identity);\n const derivedPublicKey = await derivePublicKey(parsed.seed);\n if (derivedPublicKey !== normalizeBase64Url(parsed.publicKey)) {\n throw new Error(\"Identity publicKey does not match the stored seed.\");\n }\n const derivedKeyId = await deriveKeyId(parsed.publicKey);\n if (derivedKeyId !== parsed.keyId) {\n throw new Error(\"Identity keyId does not match the public key.\");\n }\n return parsed;\n}\n\nexport async function signBytes(\n identityOrSeed: SeedLike,\n payload: Uint8Array | ArrayBuffer,\n options: { context?: string } = {}\n): Promise<string> {\n const privateKey = await importEd25519PrivateKey(resolveSeedBytes(identityOrSeed));\n const signature = await getWebCrypto().subtle.sign(\n \"Ed25519\",\n privateKey,\n combineContext(ensureBytes(payload, \"Payload\"), options.context)\n );\n return base64UrlEncode(new Uint8Array(signature));\n}\n\nexport async function verifyBytes(\n publicKey: string | Uint8Array | ArrayBuffer,\n payload: Uint8Array | ArrayBuffer,\n signature: string,\n options: { context?: string } = {}\n): Promise<boolean> {\n const verifyKey = await importEd25519PublicKey(resolvePublicKeyBytes(publicKey));\n return getWebCrypto().subtle.verify(\n \"Ed25519\",\n verifyKey,\n base64UrlDecode(signature),\n combineContext(ensureBytes(payload, \"Payload\"), options.context)\n );\n}\n\nexport async function signUtf8(\n identityOrSeed: SeedLike,\n value: string,\n options: { context?: string } = {}\n): Promise<string> {\n return signBytes(identityOrSeed, utf8Bytes(value), options);\n}\n\nexport async function verifyUtf8(\n publicKey: string | Uint8Array | ArrayBuffer,\n value: string,\n signature: string,\n options: { context?: string } = {}\n): Promise<boolean> {\n return verifyBytes(publicKey, utf8Bytes(value), signature, options);\n}\n\nexport async function deriveX25519PrivateKey(input: SeedLike): Promise<string> {\n return base64UrlEncode(await resolveX25519PrivateKeyBytes(input));\n}\n\nexport async function deriveX25519PublicKey(input: PublicKeyLike): Promise<string> {\n return base64UrlEncode(await resolveX25519PublicKeyBytes(input));\n}\n\nexport async function convertEd25519PublicKeyToX25519PublicKey(\n publicKey: string | Uint8Array | ArrayBuffer\n): Promise<string> {\n return base64UrlEncode(\n convertEd25519PublicKeyToX25519PublicKeyBytes(resolvePublicKeyBytes(publicKey))\n );\n}\n\nexport async function deriveAgeRecipient(input: PublicKeyLike): Promise<string> {\n return bech32Encode(\"age\", await resolveX25519PublicKeyBytes(input));\n}\n\nexport async function deriveAgeSecretKey(input: SeedLike): Promise<string> {\n return bech32Encode(\n \"age-secret-key-\",\n await resolveX25519PrivateKeyBytes(input)\n ).toUpperCase();\n}\n","import {\n deriveKeyId,\n parseIdentity,\n serializeIdentity,\n signUtf8,\n verifyUtf8,\n type SerializedIdentity,\n} from \"@ternent/identity\";\n\nexport type SealSignerInput = {\n identity: SerializedIdentity;\n};\n\nexport type ResolvedSealSigner = {\n identity: SerializedIdentity;\n publicKey: string;\n keyId: string;\n};\n\nexport const SEAL_SIGNATURE_CONTEXT = \"ternent-seal/v2\";\n\nexport function exportIdentityJson(identity: SerializedIdentity): string {\n return serializeIdentity(identity);\n}\n\nexport async function resolveSealSigner(\n input: SealSignerInput\n): Promise<ResolvedSealSigner> {\n const identity = parseIdentity(input.identity);\n const keyId = await deriveKeyId(identity.publicKey);\n if (keyId !== identity.keyId) {\n throw new Error(\"Identity keyId does not match the signer public key.\");\n }\n\n return {\n identity,\n publicKey: identity.publicKey,\n keyId,\n };\n}\n\nexport async function signSealUtf8(\n identity: SerializedIdentity,\n value: string\n): Promise<string> {\n return signUtf8(identity, value, {\n context: SEAL_SIGNATURE_CONTEXT,\n });\n}\n\nexport async function verifySealUtf8(\n signature: string,\n value: string,\n publicKey: string\n): Promise<boolean> {\n return verifyUtf8(publicKey, value, signature, {\n context: SEAL_SIGNATURE_CONTEXT,\n });\n}\n\nexport async function verifyPublicKeyKeyId(\n publicKey: string,\n keyId: string\n): Promise<boolean> {\n return (await deriveKeyId(publicKey)) === keyId;\n}\n"],"names":[],"mappings":"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"}