@ternent/core 0.0.1
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/.changeset/README.md +8 -0
- package/.changeset/config.json +17 -0
- package/.github/workflows/deploy-armour.yml +42 -0
- package/.github/workflows/deploy-identity.yml +42 -0
- package/.github/workflows/deploy-seal.yml +42 -0
- package/.github/workflows/deploy-ui.yml +42 -0
- package/.github/workflows/deploy-utils.yml +42 -0
- package/.github/workflows/release-create.yml +59 -0
- package/.github/workflows/release-publish.yml +54 -0
- package/.nvmrc +1 -0
- package/.ops/publish.mjs +31 -0
- package/package.json +16 -0
- package/packages/README.md +0 -0
- package/packages/armour/CHANGELOG.md +66 -0
- package/packages/armour/CLAUDE.md +8 -0
- package/packages/armour/README.md +103 -0
- package/packages/armour/SPEC.md +92 -0
- package/packages/armour/package.json +45 -0
- package/packages/armour/src/constants.ts +5 -0
- package/packages/armour/src/deps.d.ts +56 -0
- package/packages/armour/src/errors.ts +172 -0
- package/packages/armour/src/files.ts +73 -0
- package/packages/armour/src/identity.ts +72 -0
- package/packages/armour/src/index.ts +56 -0
- package/packages/armour/src/init.ts +10 -0
- package/packages/armour/src/passphrase.ts +33 -0
- package/packages/armour/src/recipients.ts +73 -0
- package/packages/armour/src/text.ts +68 -0
- package/packages/armour/src/types.ts +93 -0
- package/packages/armour/test/armour.test.ts +270 -0
- package/packages/armour/tsconfig.build.json +12 -0
- package/packages/armour/tsconfig.json +12 -0
- package/packages/armour/vite.config.ts +29 -0
- package/packages/concord/CHANGELOG.md +83 -0
- package/packages/concord/CLAUDE.md +9 -0
- package/packages/concord/README.md +146 -0
- package/packages/concord/SPEC.md +287 -0
- package/packages/concord/package.json +51 -0
- package/packages/concord/src/app.ts +717 -0
- package/packages/concord/src/errors.ts +9 -0
- package/packages/concord/src/index.ts +20 -0
- package/packages/concord/src/types.ts +127 -0
- package/packages/concord/test/concord.test.ts +978 -0
- package/packages/concord/tsconfig.json +12 -0
- package/packages/concord/vite.browser.config.ts +27 -0
- package/packages/concord/vite.config.ts +35 -0
- package/packages/concord/vite.config.ts.timestamp-1774262297922-ffd76e35ea668.mjs +83 -0
- package/packages/identity/CHANGELOG.md +47 -0
- package/packages/identity/README.md +236 -0
- package/packages/identity/package.json +41 -0
- package/packages/identity/src/index.ts +538 -0
- package/packages/identity/test/identity.test.ts +172 -0
- package/packages/identity/tsconfig.build.json +12 -0
- package/packages/identity/vite.config.ts +17 -0
- package/packages/ledger/CHANGELOG.md +69 -0
- package/packages/ledger/CLAUDE.md +9 -0
- package/packages/ledger/SPEC.md +304 -0
- package/packages/ledger/package.json +48 -0
- package/packages/ledger/src/index.ts +2 -0
- package/packages/ledger/src/ledger.ts +1286 -0
- package/packages/ledger/src/seal-cli.d.ts +25 -0
- package/packages/ledger/src/types.ts +294 -0
- package/packages/ledger/test/ledger.test.ts +838 -0
- package/packages/ledger/tsconfig.json +12 -0
- package/packages/ledger/vite.browser.config.ts +27 -0
- package/packages/ledger/vite.config.ts +39 -0
- package/packages/seal/CHANGELOG.md +137 -0
- package/packages/seal/CLAUDE.md +8 -0
- package/packages/seal/README.md +258 -0
- package/packages/seal/bin/seal +6 -0
- package/packages/seal/package.json +59 -0
- package/packages/seal/src/artifact.ts +380 -0
- package/packages/seal/src/cli.ts +372 -0
- package/packages/seal/src/commands/identity.ts +52 -0
- package/packages/seal/src/commands/manifest.ts +71 -0
- package/packages/seal/src/commands/publicKey.ts +7 -0
- package/packages/seal/src/commands/sign.ts +56 -0
- package/packages/seal/src/commands/verify.ts +54 -0
- package/packages/seal/src/crypto.ts +85 -0
- package/packages/seal/src/errors.ts +88 -0
- package/packages/seal/src/index.ts +5 -0
- package/packages/seal/src/manifest.ts +114 -0
- package/packages/seal/src/node.ts +18 -0
- package/packages/seal/src/proof.ts +344 -0
- package/packages/seal/test/artifact.test.ts +86 -0
- package/packages/seal/test/cli.test.ts +208 -0
- package/packages/seal/test/crypto.test.ts +21 -0
- package/packages/seal/test/manifest.test.ts +32 -0
- package/packages/seal/test/proof.test.ts +60 -0
- package/packages/seal/tsconfig.json +12 -0
- package/packages/seal/vite.config.ts +54 -0
- package/packages/ui/CHANGELOG.md +393 -0
- package/packages/ui/README.md +57 -0
- package/packages/ui/jsconfig.json +19 -0
- package/packages/ui/package.json +64 -0
- package/packages/ui/scripts/check-tokens.js +56 -0
- package/packages/ui/scripts/generate-theme-css.mjs +85 -0
- package/packages/ui/src/design-system/base.css +8 -0
- package/packages/ui/src/design-system/docs/ACCESSIBILITY_RULES.md +186 -0
- package/packages/ui/src/design-system/docs/AI_SYSTEM.md +281 -0
- package/packages/ui/src/design-system/docs/PATTERN_RULES.md +83 -0
- package/packages/ui/src/design-system/docs/PRIMITIVE_RULES.md +258 -0
- package/packages/ui/src/design-system/docs/TOKEN_RULES.md +235 -0
- package/packages/ui/src/design-system/docs/VISUAL_DIRECTION.md +68 -0
- package/packages/ui/src/design-system/foundation.js +420 -0
- package/packages/ui/src/design-system/tokens.css +140 -0
- package/packages/ui/src/design-system/tokens.js +327 -0
- package/packages/ui/src/design-system/utils.js +246 -0
- package/packages/ui/src/main.js +4 -0
- package/packages/ui/src/patterns/FeatureCard/FeatureCard.spec.md +24 -0
- package/packages/ui/src/patterns/FeatureCard/FeatureCard.types.ts +8 -0
- package/packages/ui/src/patterns/FeatureCard/FeatureCard.vue +175 -0
- package/packages/ui/src/patterns/FormField/FormField.spec.md +65 -0
- package/packages/ui/src/patterns/FormField/FormField.types.ts +11 -0
- package/packages/ui/src/patterns/FormField/FormField.vue +87 -0
- package/packages/ui/src/patterns/IdentityGlyph/IdentityGlyph.vue +61 -0
- package/packages/ui/src/patterns/IdentityGlyph/IdentityHandle.vue +58 -0
- package/packages/ui/src/patterns/IdentityGlyph/identityGlyph.types.ts +36 -0
- package/packages/ui/src/patterns/IdentityGlyph/identityGlyph.utils.ts +585 -0
- package/packages/ui/src/patterns/IdentityGlyph/index.ts +5 -0
- package/packages/ui/src/patterns/KeyValueList/KeyValueList.spec.md +28 -0
- package/packages/ui/src/patterns/KeyValueList/KeyValueList.types.ts +16 -0
- package/packages/ui/src/patterns/KeyValueList/KeyValueList.vue +50 -0
- package/packages/ui/src/patterns/LandingPage/LandingIcon.vue +90 -0
- package/packages/ui/src/patterns/LandingPage/LandingPage.spec.md +24 -0
- package/packages/ui/src/patterns/LandingPage/LandingPage.types.ts +212 -0
- package/packages/ui/src/patterns/LandingPage/LandingPage.vue +599 -0
- package/packages/ui/src/patterns/ListWorkspaceLayout/ListWorkspaceLayout.test.ts +33 -0
- package/packages/ui/src/patterns/ListWorkspaceLayout/ListWorkspaceLayout.vue +44 -0
- package/packages/ui/src/patterns/Logo/Logo.spec.md +22 -0
- package/packages/ui/src/patterns/Logo/Logo.vue +160 -0
- package/packages/ui/src/patterns/PageSurface/PageSurface.spec.md +15 -0
- package/packages/ui/src/patterns/PageSurface/PageSurface.vue +85 -0
- package/packages/ui/src/patterns/PanelChrome/PanelChrome.spec.md +39 -0
- package/packages/ui/src/patterns/PanelChrome/PanelChrome.types.ts +1 -0
- package/packages/ui/src/patterns/PanelChrome/PanelChrome.vue +187 -0
- package/packages/ui/src/patterns/PreviewPanel/PreviewPanel.spec.md +31 -0
- package/packages/ui/src/patterns/PreviewPanel/PreviewPanel.types.ts +23 -0
- package/packages/ui/src/patterns/PreviewPanel/PreviewPanel.vue +354 -0
- package/packages/ui/src/patterns/RecordList/RecordList.spec.md +35 -0
- package/packages/ui/src/patterns/RecordList/RecordList.test.ts +42 -0
- package/packages/ui/src/patterns/RecordList/RecordList.types.ts +9 -0
- package/packages/ui/src/patterns/RecordList/RecordList.utils.ts +5 -0
- package/packages/ui/src/patterns/RecordList/RecordList.vue +134 -0
- package/packages/ui/src/patterns/SectionClarifier/SectionClarifier.vue +85 -0
- package/packages/ui/src/patterns/SectionIntro/SectionIntro.spec.md +25 -0
- package/packages/ui/src/patterns/SectionIntro/SectionIntro.types.ts +7 -0
- package/packages/ui/src/patterns/SectionIntro/SectionIntro.vue +141 -0
- package/packages/ui/src/patterns/SidebarNav/SidebarNav.spec.md +34 -0
- package/packages/ui/src/patterns/SidebarNav/SidebarNav.types.ts +17 -0
- package/packages/ui/src/patterns/SidebarNav/SidebarNav.vue +110 -0
- package/packages/ui/src/patterns/SplitView/SplitView.spec.md +28 -0
- package/packages/ui/src/patterns/SplitView/SplitView.test.ts +22 -0
- package/packages/ui/src/patterns/SplitView/SplitView.types.ts +3 -0
- package/packages/ui/src/patterns/SplitView/SplitView.utils.ts +13 -0
- package/packages/ui/src/patterns/SplitView/SplitView.vue +39 -0
- package/packages/ui/src/patterns/StepList/StepList.spec.md +15 -0
- package/packages/ui/src/patterns/StepList/StepList.types.ts +4 -0
- package/packages/ui/src/patterns/StepList/StepList.vue +91 -0
- package/packages/ui/src/patterns/Verification/VerificationBadge.vue +97 -0
- package/packages/ui/src/patterns/Verification/VerificationComponents.test.ts +153 -0
- package/packages/ui/src/patterns/Verification/VerificationDetailsPanel.vue +270 -0
- package/packages/ui/src/patterns/Verification/VerificationSummary.vue +171 -0
- package/packages/ui/src/patterns/Verification/index.ts +6 -0
- package/packages/ui/src/patterns/Verification/verification.types.ts +8 -0
- package/packages/ui/src/patterns/Verification/verification.utils.test.ts +37 -0
- package/packages/ui/src/patterns/Verification/verification.utils.ts +75 -0
- package/packages/ui/src/patterns/index.ts +25 -0
- package/packages/ui/src/primitives/Accordian/Accordian.vue +11 -0
- package/packages/ui/src/primitives/Accordian/AccordianItem.vue +14 -0
- package/packages/ui/src/primitives/Accordion/Accordion.props.ts +21 -0
- package/packages/ui/src/primitives/Accordion/Accordion.spec.md +50 -0
- package/packages/ui/src/primitives/Accordion/Accordion.types.ts +4 -0
- package/packages/ui/src/primitives/Accordion/Accordion.variants.ts +12 -0
- package/packages/ui/src/primitives/Accordion/Accordion.vue +71 -0
- package/packages/ui/src/primitives/Accordion/AccordionItem.props.ts +14 -0
- package/packages/ui/src/primitives/Accordion/AccordionItem.vue +40 -0
- package/packages/ui/src/primitives/Badge/Badge.props.ts +17 -0
- package/packages/ui/src/primitives/Badge/Badge.spec.md +17 -0
- package/packages/ui/src/primitives/Badge/Badge.types.ts +15 -0
- package/packages/ui/src/primitives/Badge/Badge.variants.ts +48 -0
- package/packages/ui/src/primitives/Badge/Badge.vue +31 -0
- package/packages/ui/src/primitives/Button/Button.props.ts +29 -0
- package/packages/ui/src/primitives/Button/Button.spec.md +139 -0
- package/packages/ui/src/primitives/Button/Button.types.ts +19 -0
- package/packages/ui/src/primitives/Button/Button.variants.ts +72 -0
- package/packages/ui/src/primitives/Button/Button.vue +90 -0
- package/packages/ui/src/primitives/Card/Card.props.ts +17 -0
- package/packages/ui/src/primitives/Card/Card.spec.md +29 -0
- package/packages/ui/src/primitives/Card/Card.types.ts +12 -0
- package/packages/ui/src/primitives/Card/Card.variants.ts +27 -0
- package/packages/ui/src/primitives/Card/Card.vue +37 -0
- package/packages/ui/src/primitives/Checkbox/Checkbox.props.ts +21 -0
- package/packages/ui/src/primitives/Checkbox/Checkbox.spec.md +51 -0
- package/packages/ui/src/primitives/Checkbox/Checkbox.types.ts +4 -0
- package/packages/ui/src/primitives/Checkbox/Checkbox.variants.ts +34 -0
- package/packages/ui/src/primitives/Checkbox/Checkbox.vue +92 -0
- package/packages/ui/src/primitives/Dialog/Dialog.props.ts +29 -0
- package/packages/ui/src/primitives/Dialog/Dialog.spec.md +52 -0
- package/packages/ui/src/primitives/Dialog/Dialog.types.ts +3 -0
- package/packages/ui/src/primitives/Dialog/Dialog.variants.ts +27 -0
- package/packages/ui/src/primitives/Dialog/Dialog.vue +78 -0
- package/packages/ui/src/primitives/Drawer/Drawer.props.ts +33 -0
- package/packages/ui/src/primitives/Drawer/Drawer.spec.md +50 -0
- package/packages/ui/src/primitives/Drawer/Drawer.types.ts +5 -0
- package/packages/ui/src/primitives/Drawer/Drawer.variants.ts +35 -0
- package/packages/ui/src/primitives/Drawer/Drawer.vue +88 -0
- package/packages/ui/src/primitives/FieldMessage/FieldMessage.props.ts +17 -0
- package/packages/ui/src/primitives/FieldMessage/FieldMessage.spec.md +35 -0
- package/packages/ui/src/primitives/FieldMessage/FieldMessage.types.ts +5 -0
- package/packages/ui/src/primitives/FieldMessage/FieldMessage.variants.ts +14 -0
- package/packages/ui/src/primitives/FieldMessage/FieldMessage.vue +40 -0
- package/packages/ui/src/primitives/FileInput/FileInput.props.ts +41 -0
- package/packages/ui/src/primitives/FileInput/FileInput.types.ts +6 -0
- package/packages/ui/src/primitives/FileInput/FileInput.variants.ts +46 -0
- package/packages/ui/src/primitives/FileInput/FileInput.vue +163 -0
- package/packages/ui/src/primitives/Input/Input.props.ts +29 -0
- package/packages/ui/src/primitives/Input/Input.spec.md +79 -0
- package/packages/ui/src/primitives/Input/Input.types.ts +13 -0
- package/packages/ui/src/primitives/Input/Input.variants.ts +54 -0
- package/packages/ui/src/primitives/Input/Input.vue +99 -0
- package/packages/ui/src/primitives/Label/Label.props.ts +25 -0
- package/packages/ui/src/primitives/Label/Label.spec.md +31 -0
- package/packages/ui/src/primitives/Label/Label.types.ts +3 -0
- package/packages/ui/src/primitives/Label/Label.variants.ts +17 -0
- package/packages/ui/src/primitives/Label/Label.vue +38 -0
- package/packages/ui/src/primitives/Menu/Menu.props.ts +17 -0
- package/packages/ui/src/primitives/Menu/Menu.spec.md +38 -0
- package/packages/ui/src/primitives/Menu/Menu.types.ts +10 -0
- package/packages/ui/src/primitives/Menu/Menu.variants.ts +10 -0
- package/packages/ui/src/primitives/Menu/Menu.vue +57 -0
- package/packages/ui/src/primitives/Popover/Popover.props.ts +25 -0
- package/packages/ui/src/primitives/Popover/Popover.spec.md +49 -0
- package/packages/ui/src/primitives/Popover/Popover.types.ts +3 -0
- package/packages/ui/src/primitives/Popover/Popover.variants.ts +18 -0
- package/packages/ui/src/primitives/Popover/Popover.vue +74 -0
- package/packages/ui/src/primitives/RadioGroup/RadioGroup.props.ts +29 -0
- package/packages/ui/src/primitives/RadioGroup/RadioGroup.spec.md +50 -0
- package/packages/ui/src/primitives/RadioGroup/RadioGroup.types.ts +12 -0
- package/packages/ui/src/primitives/RadioGroup/RadioGroup.variants.ts +48 -0
- package/packages/ui/src/primitives/RadioGroup/RadioGroup.vue +87 -0
- package/packages/ui/src/primitives/Separator/Separator.props.ts +9 -0
- package/packages/ui/src/primitives/Separator/Separator.spec.md +15 -0
- package/packages/ui/src/primitives/Separator/Separator.types.ts +3 -0
- package/packages/ui/src/primitives/Separator/Separator.variants.ts +8 -0
- package/packages/ui/src/primitives/Separator/Separator.vue +23 -0
- package/packages/ui/src/primitives/Skeleton/Skeleton.props.ts +21 -0
- package/packages/ui/src/primitives/Skeleton/Skeleton.spec.md +18 -0
- package/packages/ui/src/primitives/Skeleton/Skeleton.types.ts +5 -0
- package/packages/ui/src/primitives/Skeleton/Skeleton.variants.ts +18 -0
- package/packages/ui/src/primitives/Skeleton/Skeleton.vue +37 -0
- package/packages/ui/src/primitives/Spinner/Spinner.props.ts +13 -0
- package/packages/ui/src/primitives/Spinner/Spinner.spec.md +16 -0
- package/packages/ui/src/primitives/Spinner/Spinner.types.ts +5 -0
- package/packages/ui/src/primitives/Spinner/Spinner.variants.ts +15 -0
- package/packages/ui/src/primitives/Spinner/Spinner.vue +33 -0
- package/packages/ui/src/primitives/SplitButton/SplitButton.vue +108 -0
- package/packages/ui/src/primitives/Switch/Switch.props.ts +21 -0
- package/packages/ui/src/primitives/Switch/Switch.spec.md +49 -0
- package/packages/ui/src/primitives/Switch/Switch.types.ts +3 -0
- package/packages/ui/src/primitives/Switch/Switch.variants.ts +34 -0
- package/packages/ui/src/primitives/Switch/Switch.vue +71 -0
- package/packages/ui/src/primitives/Tabs/Tabs.props.ts +25 -0
- package/packages/ui/src/primitives/Tabs/Tabs.spec.md +48 -0
- package/packages/ui/src/primitives/Tabs/Tabs.types.ts +11 -0
- package/packages/ui/src/primitives/Tabs/Tabs.variants.ts +28 -0
- package/packages/ui/src/primitives/Tabs/Tabs.vue +59 -0
- package/packages/ui/src/primitives/Textarea/Textarea.props.ts +33 -0
- package/packages/ui/src/primitives/Textarea/Textarea.spec.md +59 -0
- package/packages/ui/src/primitives/Textarea/Textarea.types.ts +5 -0
- package/packages/ui/src/primitives/Textarea/Textarea.variants.ts +27 -0
- package/packages/ui/src/primitives/Textarea/Textarea.vue +74 -0
- package/packages/ui/src/primitives/Tooltip/Tooltip.props.ts +21 -0
- package/packages/ui/src/primitives/Tooltip/Tooltip.spec.md +45 -0
- package/packages/ui/src/primitives/Tooltip/Tooltip.types.ts +3 -0
- package/packages/ui/src/primitives/Tooltip/Tooltip.variants.ts +4 -0
- package/packages/ui/src/primitives/Tooltip/Tooltip.vue +31 -0
- package/packages/ui/src/primitives/TreeView/TreeView.types.ts +10 -0
- package/packages/ui/src/primitives/TreeView/TreeView.vue +113 -0
- package/packages/ui/src/primitives/TreeView/TreeViewNode.vue +190 -0
- package/packages/ui/src/primitives/index.ts +29 -0
- package/packages/ui/src/style.css +7 -0
- package/packages/ui/src/style.js +1 -0
- package/packages/ui/src/themes/armour.css +147 -0
- package/packages/ui/src/themes/aurora.css +147 -0
- package/packages/ui/src/themes/citrine-ash.css +147 -0
- package/packages/ui/src/themes/concord.css +147 -0
- package/packages/ui/src/themes/garnet-honey.css +147 -0
- package/packages/ui/src/themes/harbor-rose.css +147 -0
- package/packages/ui/src/themes/ledger.css +147 -0
- package/packages/ui/src/themes/neon-noir.css +74 -0
- package/packages/ui/src/themes/obsidian-iris.css +147 -0
- package/packages/ui/src/themes/pixpax.css +147 -0
- package/packages/ui/src/themes/print.css +147 -0
- package/packages/ui/src/themes/prism.css +147 -0
- package/packages/ui/src/themes/proof.css +145 -0
- package/packages/ui/src/themes/semanticThemeContract.js +2256 -0
- package/packages/ui/src/themes/spruce-ink.css +147 -0
- package/packages/ui/src/themes/sunset.css +147 -0
- package/packages/ui/tailwind.config.js +64 -0
- package/packages/ui/vite.config.js +35 -0
- package/packages/ui/vite.config.js.timestamp-1780697224943-89fbc929987bc.mjs +38 -0
- package/packages/utils/CHANGELOG.md +111 -0
- package/packages/utils/README.md +3 -0
- package/packages/utils/package.json +46 -0
- package/packages/utils/src/index.test.js +39 -0
- package/packages/utils/src/index.ts +289 -0
- package/packages/utils/tsconfig.build.json +12 -0
- package/packages/utils/vite.config.js +28 -0
- package/pnpm-workspace.yaml +8 -0
- package/scripts/vite/package-lib-config.ts +59 -0
- package/tsconfig.json +24 -0
- package/tsconfig.node.json +9 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { createManifestArtifact } from "./commands/manifest";
|
|
5
|
+
import { createIdentityArtifact } from "./commands/identity";
|
|
6
|
+
import { createProofArtifact, createRecipientArtifact } from "./commands/sign";
|
|
7
|
+
import { createPublicKeyArtifact } from "./commands/publicKey";
|
|
8
|
+
import { verifyArtifactProof, verifyProofArtifact } from "./commands/verify";
|
|
9
|
+
import { resolveSealIdentityFromEnv } from "./node";
|
|
10
|
+
import {
|
|
11
|
+
EXIT_FAILURE,
|
|
12
|
+
EXIT_HASH_MISMATCH,
|
|
13
|
+
EXIT_INVALID_PROOF,
|
|
14
|
+
EXIT_KEY_CONFIG,
|
|
15
|
+
EXIT_SIGNATURE_INVALID,
|
|
16
|
+
EXIT_SUCCESS,
|
|
17
|
+
SealCliError,
|
|
18
|
+
getExitCode,
|
|
19
|
+
} from "./errors";
|
|
20
|
+
|
|
21
|
+
type ProcessEnvLike = Record<string, string | undefined>;
|
|
22
|
+
|
|
23
|
+
declare const process: {
|
|
24
|
+
argv: string[];
|
|
25
|
+
env: ProcessEnvLike;
|
|
26
|
+
stdout: { write: (value: string) => void };
|
|
27
|
+
stderr: { write: (value: string) => void };
|
|
28
|
+
exitCode?: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type ParsedArgs = {
|
|
32
|
+
_: string[];
|
|
33
|
+
flags: Record<string, string | boolean | string[]>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type OutputWriter = {
|
|
37
|
+
stdout: (value: string) => void;
|
|
38
|
+
stderr: (value: string) => void;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function parseArgs(argv: string[]): ParsedArgs {
|
|
42
|
+
const result: ParsedArgs = { _: [], flags: {} };
|
|
43
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
44
|
+
const arg = argv[index];
|
|
45
|
+
if (!arg.startsWith("--")) {
|
|
46
|
+
result._.push(arg);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const [rawKey, inlineValue] = arg.slice(2).split("=");
|
|
51
|
+
const key = rawKey.trim();
|
|
52
|
+
const next = argv[index + 1];
|
|
53
|
+
const hasNextValue = inlineValue === undefined && next && !next.startsWith("--");
|
|
54
|
+
const value = inlineValue ?? (hasNextValue ? next : undefined);
|
|
55
|
+
|
|
56
|
+
if (hasNextValue) {
|
|
57
|
+
index += 1;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (value === undefined) {
|
|
61
|
+
result.flags[key] = true;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const existing = result.flags[key];
|
|
66
|
+
if (existing === undefined) {
|
|
67
|
+
result.flags[key] = value;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (Array.isArray(existing)) {
|
|
72
|
+
existing.push(value);
|
|
73
|
+
result.flags[key] = existing;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
result.flags[key] = [String(existing), value];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getFlag(flags: ParsedArgs["flags"], key: string): string | undefined {
|
|
84
|
+
const value = flags[key];
|
|
85
|
+
if (Array.isArray(value)) {
|
|
86
|
+
return value[value.length - 1];
|
|
87
|
+
}
|
|
88
|
+
return typeof value === "string" ? value : undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getFlags(flags: ParsedArgs["flags"], key: string): string[] {
|
|
92
|
+
const value = flags[key];
|
|
93
|
+
if (Array.isArray(value)) {
|
|
94
|
+
return value.map(String);
|
|
95
|
+
}
|
|
96
|
+
if (typeof value === "string") {
|
|
97
|
+
return [value];
|
|
98
|
+
}
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function hasFlag(flags: ParsedArgs["flags"], key: string): boolean {
|
|
103
|
+
return flags[key] === true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function requireFlag(flags: ParsedArgs["flags"], key: string): string {
|
|
107
|
+
const value = getFlag(flags, key);
|
|
108
|
+
if (!value) {
|
|
109
|
+
throw new SealCliError(`Missing --${key}`, EXIT_FAILURE);
|
|
110
|
+
}
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function parseMnemonicWordCount(flags: ParsedArgs["flags"]): 12 | 24 | undefined {
|
|
115
|
+
const words = getFlag(flags, "words");
|
|
116
|
+
if (!words) {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
if (words === "12" || words === "24") {
|
|
120
|
+
return Number(words) as 12 | 24;
|
|
121
|
+
}
|
|
122
|
+
throw new SealCliError("Mnemonic word count must be 12 or 24.", EXIT_FAILURE);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function writeOutputFile(filePath: string, content: string): Promise<void> {
|
|
126
|
+
const resolvedPath = resolve(filePath);
|
|
127
|
+
await mkdir(dirname(resolvedPath), { recursive: true });
|
|
128
|
+
await writeFile(resolvedPath, content, "utf8");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function outputResult(writer: OutputWriter, json: boolean, quiet: boolean, value: unknown): void {
|
|
132
|
+
if (quiet) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (typeof value === "string") {
|
|
137
|
+
writer.stdout(`${value}\n`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (json) {
|
|
142
|
+
writer.stdout(`${JSON.stringify(value, null, 2)}\n`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
writer.stdout(`${JSON.stringify(value, null, 2)}\n`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function outputError(writer: OutputWriter, json: boolean, error: unknown): number {
|
|
150
|
+
const exitCode = getExitCode(error);
|
|
151
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
152
|
+
if (json) {
|
|
153
|
+
writer.stderr(`${JSON.stringify({ error: message, exitCode }, null, 2)}\n`);
|
|
154
|
+
} else {
|
|
155
|
+
writer.stderr(`${message}\n`);
|
|
156
|
+
}
|
|
157
|
+
return exitCode;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export async function runCli(
|
|
161
|
+
argv: string[],
|
|
162
|
+
params: {
|
|
163
|
+
env?: ProcessEnvLike;
|
|
164
|
+
writer?: OutputWriter;
|
|
165
|
+
} = {},
|
|
166
|
+
): Promise<number> {
|
|
167
|
+
const parsed = parseArgs(argv);
|
|
168
|
+
const env = params.env ?? process.env;
|
|
169
|
+
const writer: OutputWriter = params.writer ?? {
|
|
170
|
+
stdout: (value) => process.stdout.write(value),
|
|
171
|
+
stderr: (value) => process.stderr.write(value),
|
|
172
|
+
};
|
|
173
|
+
const json = hasFlag(parsed.flags, "json");
|
|
174
|
+
const quiet = hasFlag(parsed.flags, "quiet");
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const [command, subcommand] = parsed._;
|
|
178
|
+
|
|
179
|
+
if (command === "identity" && subcommand === "create") {
|
|
180
|
+
const mnemonicOutPath = getFlag(parsed.flags, "mnemonic-out");
|
|
181
|
+
const artifact = await createIdentityArtifact({
|
|
182
|
+
withMnemonic: Boolean(mnemonicOutPath),
|
|
183
|
+
words: parseMnemonicWordCount(parsed.flags),
|
|
184
|
+
passphrase: getFlag(parsed.flags, "passphrase"),
|
|
185
|
+
});
|
|
186
|
+
const outPath = getFlag(parsed.flags, "out");
|
|
187
|
+
if (outPath) {
|
|
188
|
+
await writeOutputFile(outPath, artifact.content);
|
|
189
|
+
}
|
|
190
|
+
if (mnemonicOutPath) {
|
|
191
|
+
await writeOutputFile(mnemonicOutPath, artifact.mnemonicContent || "");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (outPath) {
|
|
195
|
+
outputResult(
|
|
196
|
+
writer,
|
|
197
|
+
json,
|
|
198
|
+
quiet,
|
|
199
|
+
json
|
|
200
|
+
? artifact.mnemonic
|
|
201
|
+
? {
|
|
202
|
+
identity: artifact.identity,
|
|
203
|
+
mnemonic: artifact.mnemonic,
|
|
204
|
+
mnemonicFile: mnemonicOutPath || null,
|
|
205
|
+
}
|
|
206
|
+
: artifact.identity
|
|
207
|
+
: outPath,
|
|
208
|
+
);
|
|
209
|
+
} else {
|
|
210
|
+
outputResult(
|
|
211
|
+
writer,
|
|
212
|
+
true,
|
|
213
|
+
quiet,
|
|
214
|
+
artifact.mnemonic
|
|
215
|
+
? {
|
|
216
|
+
identity: artifact.identity,
|
|
217
|
+
mnemonic: artifact.mnemonic,
|
|
218
|
+
mnemonicFile: mnemonicOutPath || null,
|
|
219
|
+
}
|
|
220
|
+
: artifact.identity,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
return EXIT_SUCCESS;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (command === "manifest" && subcommand === "create") {
|
|
227
|
+
const artifact = await createManifestArtifact(requireFlag(parsed.flags, "input"));
|
|
228
|
+
const outPath = getFlag(parsed.flags, "out");
|
|
229
|
+
if (outPath) {
|
|
230
|
+
await writeOutputFile(outPath, artifact.content);
|
|
231
|
+
outputResult(writer, json, quiet, json ? artifact.manifest : outPath);
|
|
232
|
+
} else {
|
|
233
|
+
outputResult(writer, true, quiet, artifact.manifest);
|
|
234
|
+
}
|
|
235
|
+
return EXIT_SUCCESS;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (command === "sign") {
|
|
239
|
+
const identity = await resolveSealIdentityFromEnv(env);
|
|
240
|
+
const recipients = getFlags(parsed.flags, "recipient");
|
|
241
|
+
const inputPath = requireFlag(parsed.flags, "input");
|
|
242
|
+
const outPath = getFlag(parsed.flags, "out");
|
|
243
|
+
if (recipients.length > 0) {
|
|
244
|
+
const artifact = await createRecipientArtifact({
|
|
245
|
+
inputPath,
|
|
246
|
+
identity,
|
|
247
|
+
recipients,
|
|
248
|
+
});
|
|
249
|
+
if (outPath) {
|
|
250
|
+
await writeOutputFile(outPath, artifact.content);
|
|
251
|
+
outputResult(writer, json, quiet, json ? artifact.artifact : outPath);
|
|
252
|
+
} else {
|
|
253
|
+
outputResult(writer, true, quiet, artifact.artifact);
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
const artifact = await createProofArtifact({
|
|
257
|
+
inputPath,
|
|
258
|
+
identity,
|
|
259
|
+
});
|
|
260
|
+
if (outPath) {
|
|
261
|
+
await writeOutputFile(outPath, artifact.content);
|
|
262
|
+
outputResult(writer, json, quiet, json ? artifact.proof : outPath);
|
|
263
|
+
} else {
|
|
264
|
+
outputResult(writer, true, quiet, artifact.proof);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return EXIT_SUCCESS;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (command === "verify") {
|
|
271
|
+
const artifactPath = getFlag(parsed.flags, "artifact");
|
|
272
|
+
if (artifactPath) {
|
|
273
|
+
const { result } = await verifyArtifactProof({ artifactPath });
|
|
274
|
+
outputResult(
|
|
275
|
+
writer,
|
|
276
|
+
json,
|
|
277
|
+
quiet,
|
|
278
|
+
json
|
|
279
|
+
? result
|
|
280
|
+
: [
|
|
281
|
+
`valid=${result.valid}`,
|
|
282
|
+
`hashMatch=${result.hashMatch}`,
|
|
283
|
+
`signatureValid=${result.signatureValid}`,
|
|
284
|
+
`encrypted=${result.encrypted}`,
|
|
285
|
+
`payloadScheme=${result.payloadScheme}`,
|
|
286
|
+
`payloadMode=${result.payloadMode}`,
|
|
287
|
+
`keyId=${result.keyId}`,
|
|
288
|
+
`algorithm=${result.algorithm}`,
|
|
289
|
+
`subjectHash=${result.subjectHash}`,
|
|
290
|
+
].join("\n"),
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
if (!result.hashMatch) {
|
|
294
|
+
return EXIT_HASH_MISMATCH;
|
|
295
|
+
}
|
|
296
|
+
if (!result.signatureValid) {
|
|
297
|
+
return EXIT_SIGNATURE_INVALID;
|
|
298
|
+
}
|
|
299
|
+
return EXIT_SUCCESS;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const proofPath = requireFlag(parsed.flags, "proof");
|
|
303
|
+
const inputPath = requireFlag(parsed.flags, "input");
|
|
304
|
+
const { result } = await verifyProofArtifact({ proofPath, inputPath });
|
|
305
|
+
outputResult(
|
|
306
|
+
writer,
|
|
307
|
+
json,
|
|
308
|
+
quiet,
|
|
309
|
+
json
|
|
310
|
+
? result
|
|
311
|
+
: [
|
|
312
|
+
`valid=${result.valid}`,
|
|
313
|
+
`hashMatch=${result.hashMatch}`,
|
|
314
|
+
`signatureValid=${result.signatureValid}`,
|
|
315
|
+
`keyId=${result.keyId}`,
|
|
316
|
+
`algorithm=${result.algorithm}`,
|
|
317
|
+
`subjectHash=${result.subjectHash}`,
|
|
318
|
+
].join("\n"),
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
if (!result.hashMatch) {
|
|
322
|
+
return EXIT_HASH_MISMATCH;
|
|
323
|
+
}
|
|
324
|
+
if (!result.signatureValid) {
|
|
325
|
+
return EXIT_SIGNATURE_INVALID;
|
|
326
|
+
}
|
|
327
|
+
return EXIT_SUCCESS;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (command === "public-key") {
|
|
331
|
+
const artifact = await createPublicKeyArtifact({
|
|
332
|
+
identity: await resolveSealIdentityFromEnv(env),
|
|
333
|
+
});
|
|
334
|
+
outputResult(writer, true, quiet, artifact);
|
|
335
|
+
return EXIT_SUCCESS;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
throw new SealCliError(
|
|
339
|
+
"Usage: seal identity create [--out <path>] [--words 12|24] [--passphrase <value>] [--mnemonic-out <path>] [--json] [--quiet]\n" +
|
|
340
|
+
" seal manifest create --input <path> [--out <path>] [--json] [--quiet]\n" +
|
|
341
|
+
" seal sign --input <path> [--recipient <age...>] [--out <path>] [--json] [--quiet]\n" +
|
|
342
|
+
" seal verify --proof <proof.json> --input <path> [--json] [--quiet]\n" +
|
|
343
|
+
" seal verify --artifact <artifact.json> [--json] [--quiet]\n" +
|
|
344
|
+
" seal public-key [--json] [--quiet]",
|
|
345
|
+
EXIT_FAILURE,
|
|
346
|
+
);
|
|
347
|
+
} catch (error) {
|
|
348
|
+
if (error instanceof Error && error.message.includes("Proof")) {
|
|
349
|
+
return outputError(writer, json, new SealCliError(error.message, EXIT_INVALID_PROOF));
|
|
350
|
+
}
|
|
351
|
+
if (
|
|
352
|
+
error instanceof Error &&
|
|
353
|
+
(error.message.includes("public key") ||
|
|
354
|
+
error.message.includes("SEAL_IDENTITY") ||
|
|
355
|
+
error.message.includes("identity"))
|
|
356
|
+
) {
|
|
357
|
+
return outputError(writer, json, new SealCliError(error.message, EXIT_KEY_CONFIG));
|
|
358
|
+
}
|
|
359
|
+
return outputError(writer, json, error);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const isDirectRun =
|
|
364
|
+
typeof process !== "undefined" &&
|
|
365
|
+
process.argv[1] &&
|
|
366
|
+
fileURLToPath(import.meta.url) === resolve(process.argv[1]);
|
|
367
|
+
|
|
368
|
+
if (isDirectRun) {
|
|
369
|
+
runCli(process.argv.slice(2)).then((exitCode) => {
|
|
370
|
+
process.exitCode = exitCode;
|
|
371
|
+
});
|
|
372
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createSealIdentity, createSealMnemonicIdentity, exportIdentityJson } from "../crypto";
|
|
2
|
+
|
|
3
|
+
function formatMnemonicBackup(input: { mnemonic: string; passphrase?: string }): string {
|
|
4
|
+
const lines = ["Seal seed phrase backup", "", "Mnemonic:", input.mnemonic];
|
|
5
|
+
if (String(input.passphrase || "").trim()) {
|
|
6
|
+
lines.push("", "Passphrase:", String(input.passphrase));
|
|
7
|
+
}
|
|
8
|
+
lines.push(
|
|
9
|
+
"",
|
|
10
|
+
"Store this file securely. Anyone with this recovery material can recreate the signer.",
|
|
11
|
+
);
|
|
12
|
+
return `${lines.join("\n")}\n`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function createIdentityArtifact(
|
|
16
|
+
input: {
|
|
17
|
+
withMnemonic?: boolean;
|
|
18
|
+
words?: 12 | 24;
|
|
19
|
+
passphrase?: string;
|
|
20
|
+
} = {},
|
|
21
|
+
): Promise<{
|
|
22
|
+
identity: import("@ternent/identity").SerializedIdentity;
|
|
23
|
+
content: string;
|
|
24
|
+
mnemonic: string | null;
|
|
25
|
+
mnemonicContent: string | null;
|
|
26
|
+
}> {
|
|
27
|
+
const useMnemonic =
|
|
28
|
+
Boolean(input.withMnemonic) ||
|
|
29
|
+
input.words === 12 ||
|
|
30
|
+
input.words === 24 ||
|
|
31
|
+
Boolean(input.passphrase);
|
|
32
|
+
const created = useMnemonic
|
|
33
|
+
? await createSealMnemonicIdentity({
|
|
34
|
+
words: input.words,
|
|
35
|
+
passphrase: input.passphrase,
|
|
36
|
+
})
|
|
37
|
+
: {
|
|
38
|
+
identity: await createSealIdentity(),
|
|
39
|
+
mnemonic: null,
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
identity: created.identity,
|
|
43
|
+
content: exportIdentityJson(created.identity),
|
|
44
|
+
mnemonic: created.mnemonic,
|
|
45
|
+
mnemonicContent: created.mnemonic
|
|
46
|
+
? formatMnemonicBackup({
|
|
47
|
+
mnemonic: created.mnemonic,
|
|
48
|
+
passphrase: input.passphrase,
|
|
49
|
+
})
|
|
50
|
+
: null,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { basename, join, relative, resolve } from "node:path";
|
|
3
|
+
import { createSealHash } from "../proof";
|
|
4
|
+
import {
|
|
5
|
+
SEAL_MANIFEST_TYPE,
|
|
6
|
+
SEAL_MANIFEST_VERSION,
|
|
7
|
+
stringifySealManifest,
|
|
8
|
+
type SealManifestV1,
|
|
9
|
+
} from "../manifest";
|
|
10
|
+
|
|
11
|
+
function normalizeRelativePath(value: string): string {
|
|
12
|
+
return value.split("\\").join("/");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function collectFiles(
|
|
16
|
+
rootPath: string,
|
|
17
|
+
currentPath: string,
|
|
18
|
+
files: Record<string, SealManifestV1["files"][string]>,
|
|
19
|
+
): Promise<void> {
|
|
20
|
+
const entries = await readdir(currentPath, { withFileTypes: true });
|
|
21
|
+
const sorted = entries
|
|
22
|
+
.filter((entry) => entry.name !== ".DS_Store")
|
|
23
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
24
|
+
|
|
25
|
+
for (const entry of sorted) {
|
|
26
|
+
const entryPath = join(currentPath, entry.name);
|
|
27
|
+
if (entry.isDirectory()) {
|
|
28
|
+
await collectFiles(rootPath, entryPath, files);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (!entry.isFile()) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const bytes = await readFile(entryPath);
|
|
35
|
+
const relativePath = normalizeRelativePath(relative(rootPath, entryPath));
|
|
36
|
+
files[relativePath] = await createSealHash(bytes);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function createManifestArtifact(inputPath: string): Promise<{
|
|
41
|
+
manifest: SealManifestV1;
|
|
42
|
+
content: string;
|
|
43
|
+
}> {
|
|
44
|
+
const resolvedInput = resolve(inputPath);
|
|
45
|
+
const inputStat = await stat(resolvedInput);
|
|
46
|
+
const root = basename(resolvedInput);
|
|
47
|
+
const files: SealManifestV1["files"] = {};
|
|
48
|
+
|
|
49
|
+
if (inputStat.isDirectory()) {
|
|
50
|
+
await collectFiles(resolvedInput, resolvedInput, files);
|
|
51
|
+
} else if (inputStat.isFile()) {
|
|
52
|
+
const bytes = await readFile(resolvedInput);
|
|
53
|
+
files[root] = await createSealHash(bytes);
|
|
54
|
+
} else {
|
|
55
|
+
throw new Error("Manifest input must be a file or directory.");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const manifest: SealManifestV1 = {
|
|
59
|
+
version: SEAL_MANIFEST_VERSION,
|
|
60
|
+
type: SEAL_MANIFEST_TYPE,
|
|
61
|
+
root,
|
|
62
|
+
files: Object.fromEntries(
|
|
63
|
+
Object.entries(files).sort(([left], [right]) => left.localeCompare(right)),
|
|
64
|
+
),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
manifest,
|
|
69
|
+
content: `${stringifySealManifest(manifest)}\n`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { createSealArtifact, type SealArtifactV1 } from "../artifact";
|
|
4
|
+
import { parseSealManifestJson } from "../manifest";
|
|
5
|
+
import { createSealProof, createSealHash, type SealProofV1 } from "../proof";
|
|
6
|
+
|
|
7
|
+
export async function createProofArtifact(params: {
|
|
8
|
+
inputPath: string;
|
|
9
|
+
identity: import("@ternent/identity").SerializedIdentity;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
proof: SealProofV1;
|
|
12
|
+
content: string;
|
|
13
|
+
}> {
|
|
14
|
+
const bytes = await readFile(params.inputPath);
|
|
15
|
+
const raw = new TextDecoder().decode(bytes);
|
|
16
|
+
const parsedManifest = parseSealManifestJson(raw);
|
|
17
|
+
const proof = await createSealProof({
|
|
18
|
+
signer: {
|
|
19
|
+
identity: params.identity,
|
|
20
|
+
},
|
|
21
|
+
subject: {
|
|
22
|
+
kind: parsedManifest.ok ? "manifest" : "file",
|
|
23
|
+
path: basename(params.inputPath),
|
|
24
|
+
hash: await createSealHash(bytes),
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
proof,
|
|
30
|
+
content: `${JSON.stringify(proof, null, 2)}\n`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function createRecipientArtifact(params: {
|
|
35
|
+
inputPath: string;
|
|
36
|
+
identity: import("@ternent/identity").SerializedIdentity;
|
|
37
|
+
recipients: string[];
|
|
38
|
+
}): Promise<{
|
|
39
|
+
artifact: SealArtifactV1;
|
|
40
|
+
content: string;
|
|
41
|
+
}> {
|
|
42
|
+
const bytes = await readFile(params.inputPath);
|
|
43
|
+
const artifact = await createSealArtifact({
|
|
44
|
+
signer: {
|
|
45
|
+
identity: params.identity,
|
|
46
|
+
},
|
|
47
|
+
subjectPath: basename(params.inputPath),
|
|
48
|
+
payload: bytes,
|
|
49
|
+
recipients: params.recipients,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
artifact,
|
|
54
|
+
content: `${JSON.stringify(artifact, null, 2)}\n`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import {
|
|
3
|
+
parseSealArtifactJson,
|
|
4
|
+
verifySealArtifact,
|
|
5
|
+
type SealArtifactV1,
|
|
6
|
+
type VerifySealArtifactResult,
|
|
7
|
+
} from "../artifact";
|
|
8
|
+
import { parseSealProofJson, verifySealProofAgainstBytes, type SealProofV1 } from "../proof";
|
|
9
|
+
|
|
10
|
+
export type VerifyArtifactResult = {
|
|
11
|
+
valid: boolean;
|
|
12
|
+
hashMatch: boolean;
|
|
13
|
+
signatureValid: boolean;
|
|
14
|
+
keyId: string;
|
|
15
|
+
algorithm: "Ed25519";
|
|
16
|
+
subjectHash: `sha256:${string}`;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export async function verifyProofArtifact(params: {
|
|
20
|
+
proofPath: string;
|
|
21
|
+
inputPath: string;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
proof: SealProofV1;
|
|
24
|
+
result: VerifyArtifactResult;
|
|
25
|
+
}> {
|
|
26
|
+
const rawProof = await readFile(params.proofPath, "utf8");
|
|
27
|
+
const parsed = parseSealProofJson(rawProof);
|
|
28
|
+
if (!parsed.ok || !parsed.proof) {
|
|
29
|
+
throw new Error(parsed.errors.join(" "));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const subjectBytes = await readFile(params.inputPath);
|
|
33
|
+
const result = await verifySealProofAgainstBytes(parsed.proof, subjectBytes);
|
|
34
|
+
return {
|
|
35
|
+
proof: parsed.proof,
|
|
36
|
+
result,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function verifyArtifactProof(params: { artifactPath: string }): Promise<{
|
|
41
|
+
artifact: SealArtifactV1;
|
|
42
|
+
result: VerifySealArtifactResult;
|
|
43
|
+
}> {
|
|
44
|
+
const rawArtifact = await readFile(params.artifactPath, "utf8");
|
|
45
|
+
const parsed = parseSealArtifactJson(rawArtifact);
|
|
46
|
+
if (!parsed.ok || !parsed.artifact) {
|
|
47
|
+
throw new Error(parsed.errors.join(" "));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
artifact: parsed.artifact,
|
|
52
|
+
result: await verifySealArtifact(parsed.artifact),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createIdentity,
|
|
3
|
+
createIdentityFromMnemonic,
|
|
4
|
+
createMnemonicIdentity,
|
|
5
|
+
deriveKeyId,
|
|
6
|
+
parseIdentity,
|
|
7
|
+
serializeIdentity,
|
|
8
|
+
signUtf8,
|
|
9
|
+
verifyUtf8,
|
|
10
|
+
type SerializedIdentity,
|
|
11
|
+
} from "@ternent/identity";
|
|
12
|
+
|
|
13
|
+
export type SealSignerInput = {
|
|
14
|
+
identity: SerializedIdentity;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ResolvedSealSigner = {
|
|
18
|
+
identity: SerializedIdentity;
|
|
19
|
+
publicKey: string;
|
|
20
|
+
keyId: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const SEAL_SIGNATURE_CONTEXT = "ternent-seal/v2";
|
|
24
|
+
|
|
25
|
+
export async function createSealIdentity(
|
|
26
|
+
createdAt = new Date().toISOString(),
|
|
27
|
+
): Promise<SerializedIdentity> {
|
|
28
|
+
return createIdentity(createdAt);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function createSealIdentityFromMnemonic(input: {
|
|
32
|
+
mnemonic: string;
|
|
33
|
+
passphrase?: string;
|
|
34
|
+
createdAt?: string;
|
|
35
|
+
}): Promise<SerializedIdentity> {
|
|
36
|
+
return createIdentityFromMnemonic(input);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function createSealMnemonicIdentity(
|
|
40
|
+
input: {
|
|
41
|
+
words?: 12 | 24;
|
|
42
|
+
passphrase?: string;
|
|
43
|
+
createdAt?: string;
|
|
44
|
+
} = {},
|
|
45
|
+
): Promise<{ identity: SerializedIdentity; mnemonic: string }> {
|
|
46
|
+
return createMnemonicIdentity(input);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function exportIdentityJson(identity: SerializedIdentity): string {
|
|
50
|
+
return serializeIdentity(identity);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function resolveSealSigner(input: SealSignerInput): Promise<ResolvedSealSigner> {
|
|
54
|
+
const identity = parseIdentity(input.identity);
|
|
55
|
+
const keyId = await deriveKeyId(identity.publicKey);
|
|
56
|
+
if (keyId !== identity.keyId) {
|
|
57
|
+
throw new Error("Identity keyId does not match the signer public key.");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
identity,
|
|
62
|
+
publicKey: identity.publicKey,
|
|
63
|
+
keyId,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function signSealUtf8(identity: SerializedIdentity, value: string): Promise<string> {
|
|
68
|
+
return signUtf8(identity, value, {
|
|
69
|
+
context: SEAL_SIGNATURE_CONTEXT,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function verifySealUtf8(
|
|
74
|
+
signature: string,
|
|
75
|
+
value: string,
|
|
76
|
+
publicKey: string,
|
|
77
|
+
): Promise<boolean> {
|
|
78
|
+
return verifyUtf8(publicKey, value, signature, {
|
|
79
|
+
context: SEAL_SIGNATURE_CONTEXT,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function verifyPublicKeyKeyId(publicKey: string, keyId: string): Promise<boolean> {
|
|
84
|
+
return (await deriveKeyId(publicKey)) === keyId;
|
|
85
|
+
}
|