@aporthq/aport-agent-guardrails 1.0.8
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/LICENSE +217 -0
- package/README.md +481 -0
- package/bin/agent-guardrails +133 -0
- package/bin/aport-create-passport.sh +444 -0
- package/bin/aport-cursor-hook.sh +90 -0
- package/bin/aport-guardrail-api.sh +108 -0
- package/bin/aport-guardrail-bash.sh +394 -0
- package/bin/aport-guardrail-v2.sh +5 -0
- package/bin/aport-guardrail.sh +5 -0
- package/bin/aport-resolve-paths.sh +71 -0
- package/bin/aport-status.sh +276 -0
- package/bin/frameworks/crewai.sh +49 -0
- package/bin/frameworks/cursor.sh +95 -0
- package/bin/frameworks/langchain.sh +48 -0
- package/bin/frameworks/n8n.sh +36 -0
- package/bin/frameworks/openclaw.sh +19 -0
- package/bin/lib/allowlist.sh +18 -0
- package/bin/lib/common.sh +28 -0
- package/bin/lib/config.sh +46 -0
- package/bin/lib/constants.sh +232 -0
- package/bin/lib/detect.sh +65 -0
- package/bin/lib/error.sh +269 -0
- package/bin/lib/passport.sh +19 -0
- package/bin/lib/templates/.gitkeep +1 -0
- package/bin/lib/templates/config.yaml +6 -0
- package/bin/lib/validation.sh +206 -0
- package/bin/openclaw +660 -0
- package/docs/ADDING_A_FRAMEWORK.md +87 -0
- package/docs/AGENTS.md.example +40 -0
- package/docs/CODE_REVIEW.md +192 -0
- package/docs/DEPLOYMENT_READINESS.md +81 -0
- package/docs/FAQ_SECURITY_SCANNERS.md +373 -0
- package/docs/FRAMEWORK_ROADMAP.md +41 -0
- package/docs/HOSTED_PASSPORT_SETUP.md +362 -0
- package/docs/IMPLEMENTING_YOUR_OWN_EVALUATOR.md +433 -0
- package/docs/OPENCLAW_COMPATIBILITY.md +73 -0
- package/docs/OPENCLAW_LOCAL_INTEGRATION.md +596 -0
- package/docs/OPENCLAW_TOOLS_AND_POLICIES.md +54 -0
- package/docs/QUICKSTART.md +470 -0
- package/docs/QUICKSTART_OPENCLAW_PLUGIN.md +470 -0
- package/docs/README.md +28 -0
- package/docs/RELEASE.md +87 -0
- package/docs/REPO_LAYOUT.md +47 -0
- package/docs/SKILLS_ECOSYSTEM_ANALYSIS_FEB17.md +1260 -0
- package/docs/TOOL_POLICY_MAPPING.md +46 -0
- package/docs/UPGRADE.md +46 -0
- package/docs/VERIFICATION_METHODS.md +97 -0
- package/docs/assets/README.md +8 -0
- package/docs/assets/porter.svg +54 -0
- package/docs/development/ERROR_CODES.md +616 -0
- package/docs/frameworks/GITHUB_ISSUE_PROPOSALS.md +1105 -0
- package/docs/frameworks/crewai.md +114 -0
- package/docs/frameworks/cursor.md +159 -0
- package/docs/frameworks/langchain.md +72 -0
- package/docs/frameworks/n8n.md +40 -0
- package/docs/frameworks/openclaw.md +40 -0
- package/docs/launch/ADD_APORT_AWESOME_LISTS_INSTRUCTIONS.md +146 -0
- package/docs/launch/ANNOUNCEMENT_GUIDE.md +266 -0
- package/docs/launch/AWESOME_REPOS.md +53 -0
- package/docs/launch/CURSOR_VSCODE_HOOKS_RESEARCH.md +77 -0
- package/docs/launch/DEMO_TERMINAL_OUTPUT.txt +48 -0
- package/docs/launch/DRY_AND_PLAN_CHECKLIST.md +47 -0
- package/docs/launch/EVIDENCE_README.md +61 -0
- package/docs/launch/EVIDENCE_TERMINAL_CAPTURE.txt +10 -0
- package/docs/launch/FRAMEWORK_SUPPORT_PLAN.md +1640 -0
- package/docs/launch/LAUNCH_READINESS_CHECKLIST.md +237 -0
- package/docs/launch/LAUNCH_STRATEGY_SUMMARY.md +464 -0
- package/docs/launch/OPENCLAW_FEEDBACK_AND_FIXES.md +85 -0
- package/docs/launch/POST_1_VALENTINE_IMPROVED.md +233 -0
- package/docs/launch/POST_2_GUARDRAIL_IMPROVED.md +369 -0
- package/docs/launch/PRE_LAUNCH_FIXES.md +766 -0
- package/docs/launch/QUICK_LAUNCH_CHECKLIST.md +400 -0
- package/docs/launch/READINESS_SUMMARY.md +262 -0
- package/docs/launch/README.md +68 -0
- package/docs/launch/USER_STORIES.md +327 -0
- package/docs/launch/scripts/add-aport-awesome-pr.sh +69 -0
- package/docs/operations/MONITORING.md +588 -0
- package/docs/reviews/2026-02-18-staff-review.md +268 -0
- package/extensions/openclaw-aport/README.md +415 -0
- package/extensions/openclaw-aport/index.js +625 -0
- package/extensions/openclaw-aport/openclaw-aport.js +7 -0
- package/extensions/openclaw-aport/openclaw.plugin.json +46 -0
- package/extensions/openclaw-aport/package.json +36 -0
- package/extensions/openclaw-aport/test.js +307 -0
- package/external/aport-policies/README.md +363 -0
- package/external/aport-policies/agent.session.create.v1/README.md +345 -0
- package/external/aport-policies/agent.session.create.v1/policy.json +162 -0
- package/external/aport-policies/agent.tool.register.v1/README.md +361 -0
- package/external/aport-policies/agent.tool.register.v1/policy.json +172 -0
- package/external/aport-policies/code.release.publish.v1/README.md +51 -0
- package/external/aport-policies/code.release.publish.v1/policy.json +121 -0
- package/external/aport-policies/code.repository.merge.v1/README.md +287 -0
- package/external/aport-policies/code.repository.merge.v1/express.example.js +332 -0
- package/external/aport-policies/code.repository.merge.v1/fastapi.example.py +370 -0
- package/external/aport-policies/code.repository.merge.v1/policy.json +162 -0
- package/external/aport-policies/data.export.create.v1/README.md +226 -0
- package/external/aport-policies/data.export.create.v1/express.example.js +172 -0
- package/external/aport-policies/data.export.create.v1/fastapi.example.py +165 -0
- package/external/aport-policies/data.export.create.v1/policy.json +133 -0
- package/external/aport-policies/data.report.ingest.v1/README.md +134 -0
- package/external/aport-policies/data.report.ingest.v1/express.example.js +105 -0
- package/external/aport-policies/data.report.ingest.v1/minimal-example.js +68 -0
- package/external/aport-policies/data.report.ingest.v1/policy.json +174 -0
- package/external/aport-policies/finance.crypto.trade.v1/README.md +146 -0
- package/external/aport-policies/finance.crypto.trade.v1/express.example.js +109 -0
- package/external/aport-policies/finance.crypto.trade.v1/minimal-example.js +65 -0
- package/external/aport-policies/finance.crypto.trade.v1/policy.json +176 -0
- package/external/aport-policies/finance.payment.charge.v1/README.md +326 -0
- package/external/aport-policies/finance.payment.charge.v1/express.example.js +250 -0
- package/external/aport-policies/finance.payment.charge.v1/fastapi.example.py +227 -0
- package/external/aport-policies/finance.payment.charge.v1/minimal-example.js +64 -0
- package/external/aport-policies/finance.payment.charge.v1/policy.json +224 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/contexts.jsonl +12 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/expected.jsonl +12 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/passport.instance.json +42 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/passport.template.json +40 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/payments-charge-policy.test.js +817 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/test_payments_charge_policy.py +486 -0
- package/external/aport-policies/finance.payment.payout.v1/README.md +78 -0
- package/external/aport-policies/finance.payment.payout.v1/policy.json +181 -0
- package/external/aport-policies/finance.payment.refund.v1/README.md +275 -0
- package/external/aport-policies/finance.payment.refund.v1/express.example.js +167 -0
- package/external/aport-policies/finance.payment.refund.v1/fastapi.example.py +136 -0
- package/external/aport-policies/finance.payment.refund.v1/minimal-example.js +183 -0
- package/external/aport-policies/finance.payment.refund.v1/policy.json +216 -0
- package/external/aport-policies/finance.payment.refund.v1/tests/refunds-policy.test.js +924 -0
- package/external/aport-policies/finance.payment.refund.v1/tests/test_refunds_policy.py +778 -0
- package/external/aport-policies/finance.transaction.execute.v1/README.md +309 -0
- package/external/aport-policies/finance.transaction.execute.v1/express.example.js +261 -0
- package/external/aport-policies/finance.transaction.execute.v1/fastapi.example.py +231 -0
- package/external/aport-policies/finance.transaction.execute.v1/minimal-example.js +78 -0
- package/external/aport-policies/finance.transaction.execute.v1/policy.json +189 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/contexts.jsonl +12 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/expected.jsonl +12 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/passport.instance.json +42 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/passport.template.json +42 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/test_transactions_policy.py +214 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/transactions-policy.test.js +306 -0
- package/external/aport-policies/governance.data.access.v1/README.md +292 -0
- package/external/aport-policies/governance.data.access.v1/express.example.js +321 -0
- package/external/aport-policies/governance.data.access.v1/fastapi.example.py +279 -0
- package/external/aport-policies/governance.data.access.v1/minimal-example.js +65 -0
- package/external/aport-policies/governance.data.access.v1/policy.json +208 -0
- package/external/aport-policies/governance.data.access.v1/tests/contexts.jsonl +12 -0
- package/external/aport-policies/governance.data.access.v1/tests/data-access-policy.test.js +308 -0
- package/external/aport-policies/governance.data.access.v1/tests/expected.jsonl +12 -0
- package/external/aport-policies/governance.data.access.v1/tests/passport.instance.json +56 -0
- package/external/aport-policies/governance.data.access.v1/tests/passport.template.json +56 -0
- package/external/aport-policies/governance.data.access.v1/tests/test_data_access_policy.py +214 -0
- package/external/aport-policies/legal.contract.review.v1/README.md +109 -0
- package/external/aport-policies/legal.contract.review.v1/policy.json +378 -0
- package/external/aport-policies/legal.contract.review.v1/tests/legal-contract-review-policy.test.js +609 -0
- package/external/aport-policies/legal.contract.review.v1/tests/passport.template.json +49 -0
- package/external/aport-policies/mcp.tool.execute.v1/README.md +301 -0
- package/external/aport-policies/mcp.tool.execute.v1/policy.json +141 -0
- package/external/aport-policies/messaging.message.send.v1/README.md +230 -0
- package/external/aport-policies/messaging.message.send.v1/express.example.js +183 -0
- package/external/aport-policies/messaging.message.send.v1/fastapi.example.py +193 -0
- package/external/aport-policies/messaging.message.send.v1/policy.json +144 -0
- package/external/aport-policies/policy-template.json +107 -0
- package/external/aport-policies/system.command.execute.v1/README.md +275 -0
- package/external/aport-policies/system.command.execute.v1/policy.json +146 -0
- package/external/aport-spec/CONTRIBUTING.md +273 -0
- package/external/aport-spec/LICENSE +21 -0
- package/external/aport-spec/README.md +168 -0
- package/external/aport-spec/conformance/README.md +294 -0
- package/external/aport-spec/conformance/cases/data.export.v1/contexts/allow_users.json +6 -0
- package/external/aport-spec/conformance/cases/data.export.v1/contexts/deny_pii.json +6 -0
- package/external/aport-spec/conformance/cases/data.export.v1/expected/allow_users.decision.json +19 -0
- package/external/aport-spec/conformance/cases/data.export.v1/expected/deny_pii.decision.json +19 -0
- package/external/aport-spec/conformance/cases/data.export.v1/passports/template.json +29 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/allow_50usd.json +9 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_150usd.json +9 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_currency.json +9 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/allow_50usd.decision.json +19 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_150usd.decision.json +19 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_currency.decision.json +19 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/passports/template.json +42 -0
- package/external/aport-spec/conformance/package.json +44 -0
- package/external/aport-spec/conformance/pnpm-lock.yaml +642 -0
- package/external/aport-spec/conformance/src/cases.ts +371 -0
- package/external/aport-spec/conformance/src/ed25519.ts +167 -0
- package/external/aport-spec/conformance/src/jcs.ts +85 -0
- package/external/aport-spec/conformance/src/runner.ts +533 -0
- package/external/aport-spec/conformance/src/validators.ts +185 -0
- package/external/aport-spec/conformance/test-runner.js +315 -0
- package/external/aport-spec/conformance/tsconfig.json +21 -0
- package/external/aport-spec/error-schema.json +192 -0
- package/external/aport-spec/index.json +12 -0
- package/external/aport-spec/integrations/clawmoat/README.md +12 -0
- package/external/aport-spec/integrations/shield/README.md +245 -0
- package/external/aport-spec/integrations/shield/adapters/index.js +116 -0
- package/external/aport-spec/integrations/shield/adapters/system-command-execute.js +133 -0
- package/external/aport-spec/integrations/shield/test/README.md +58 -0
- package/external/aport-spec/integrations/shield/test/shield.md +40 -0
- package/external/aport-spec/integrations/shield/test/test-shield-to-verify.js +274 -0
- package/external/aport-spec/metrics-schema.json +504 -0
- package/external/aport-spec/oap/CHANGELOG.md +54 -0
- package/external/aport-spec/oap/VERSION.md +40 -0
- package/external/aport-spec/oap/capability-registry.md +229 -0
- package/external/aport-spec/oap/conformance.md +257 -0
- package/external/aport-spec/oap/decision-schema.json +114 -0
- package/external/aport-spec/oap/examples/context.refund.usd.50.json +9 -0
- package/external/aport-spec/oap/examples/decision.allow.sample.json +20 -0
- package/external/aport-spec/oap/examples/decision.deny.sample.json +23 -0
- package/external/aport-spec/oap/examples/passport.instance.v1.json +50 -0
- package/external/aport-spec/oap/examples/passport.template.v1.json +71 -0
- package/external/aport-spec/oap/oap-spec.md +426 -0
- package/external/aport-spec/oap/passport-schema.json +396 -0
- package/external/aport-spec/oap/security.md +213 -0
- package/external/aport-spec/oap/vc/context-oap-v1.jsonld +137 -0
- package/external/aport-spec/oap/vc/examples/oap-decision-vc.json +37 -0
- package/external/aport-spec/oap/vc/examples/oap-passport-vc.json +68 -0
- package/external/aport-spec/oap/vc/tools/INTEGRATION.md +375 -0
- package/external/aport-spec/oap/vc/tools/README.md +278 -0
- package/external/aport-spec/oap/vc/tools/examples/decision-to-vc.js +66 -0
- package/external/aport-spec/oap/vc/tools/examples/passport-to-vc.js +83 -0
- package/external/aport-spec/oap/vc/tools/examples/vc-to-decision.js +77 -0
- package/external/aport-spec/oap/vc/tools/examples/vc-to-passport.js +94 -0
- package/external/aport-spec/oap/vc/tools/package.json +38 -0
- package/external/aport-spec/oap/vc/tools/pnpm-lock.yaml +472 -0
- package/external/aport-spec/oap/vc/tools/src/cli.ts +226 -0
- package/external/aport-spec/oap/vc/tools/src/crypto-utils.ts +427 -0
- package/external/aport-spec/oap/vc/tools/src/index.ts +653 -0
- package/external/aport-spec/oap/vc/tools/src/test.ts +148 -0
- package/external/aport-spec/oap/vc/tools/src/vp.ts +382 -0
- package/external/aport-spec/oap/vc/tools/test-simple.js +214 -0
- package/external/aport-spec/oap/vc/tools/tsconfig.json +19 -0
- package/external/aport-spec/oap/vc/vc-mapping.md +443 -0
- package/external/aport-spec/passport-schema.json +586 -0
- package/external/aport-spec/rate-limiting.md +136 -0
- package/external/aport-spec/transport-profile.md +325 -0
- package/external/aport-spec/webhook-spec.md +314 -0
- package/package.json +70 -0
- package/skills/aport-agent-guardrail/SKILL.md +314 -0
- package/src/evaluator.js +252 -0
- package/src/server/index.js +72 -0
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Open Agent Passport VC Conversion Tools
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to convert between OAP objects and Verifiable Credentials
|
|
5
|
+
* for interoperability with VC/DID ecosystems.
|
|
6
|
+
*
|
|
7
|
+
* Note: This module is designed to work in both Node.js and Cloudflare Workers environments.
|
|
8
|
+
* File system operations are skipped in Workers since they're not available there.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
canonicalizeJsonLd,
|
|
13
|
+
signEd25519,
|
|
14
|
+
verifyEd25519,
|
|
15
|
+
bytesToBase64url,
|
|
16
|
+
publicKeyToBytes,
|
|
17
|
+
} from "./crypto-utils";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Type Definitions
|
|
21
|
+
*
|
|
22
|
+
* These interfaces define the structure of OAP objects and W3C Verifiable Credentials.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Open Agent Passport (OAP) structure
|
|
27
|
+
*
|
|
28
|
+
* Represents an agent's identity, capabilities, and authorization metadata.
|
|
29
|
+
*/
|
|
30
|
+
export interface OAPPassport {
|
|
31
|
+
agent_id: string;
|
|
32
|
+
kind: "template" | "instance";
|
|
33
|
+
spec_version: string;
|
|
34
|
+
parent_agent_id?: string;
|
|
35
|
+
owner_id: string;
|
|
36
|
+
owner_type: "org" | "user";
|
|
37
|
+
assurance_level: "L0" | "L1" | "L2" | "L3" | "L4KYC" | "L4FIN";
|
|
38
|
+
status: "draft" | "active" | "suspended" | "revoked";
|
|
39
|
+
capabilities: Array<{ id: string; params?: Record<string, any> }>;
|
|
40
|
+
limits: Record<string, any>;
|
|
41
|
+
regions: string[];
|
|
42
|
+
metadata: Record<string, any>;
|
|
43
|
+
created_at: string;
|
|
44
|
+
updated_at: string;
|
|
45
|
+
version: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Open Agent Passport (OAP) Decision structure
|
|
50
|
+
*
|
|
51
|
+
* Represents a policy verification decision with authorization result and metadata.
|
|
52
|
+
*/
|
|
53
|
+
export interface OAPDecision {
|
|
54
|
+
decision_id: string;
|
|
55
|
+
policy_id: string;
|
|
56
|
+
agent_id: string;
|
|
57
|
+
owner_id: string;
|
|
58
|
+
assurance_level: string;
|
|
59
|
+
allow: boolean;
|
|
60
|
+
reasons: Array<{ code: string; message?: string }>;
|
|
61
|
+
created_at: string;
|
|
62
|
+
expires_in: number;
|
|
63
|
+
passport_digest: string;
|
|
64
|
+
signature: string;
|
|
65
|
+
kid: string;
|
|
66
|
+
decision_token?: string;
|
|
67
|
+
remaining_daily_cap?: Record<string, number>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* W3C Verifiable Credential structure
|
|
72
|
+
*
|
|
73
|
+
* Compliant with W3C VC Data Model 1.1. The proof contains the cryptographic
|
|
74
|
+
* signature in JWS format.
|
|
75
|
+
*/
|
|
76
|
+
export interface VerifiableCredential {
|
|
77
|
+
"@context": string[];
|
|
78
|
+
type: string[];
|
|
79
|
+
credentialSubject: any;
|
|
80
|
+
issuer: string;
|
|
81
|
+
issuanceDate: string;
|
|
82
|
+
expirationDate: string;
|
|
83
|
+
proof: {
|
|
84
|
+
type: string;
|
|
85
|
+
created: string;
|
|
86
|
+
verificationMethod: string;
|
|
87
|
+
proofPurpose: string;
|
|
88
|
+
jws: string;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Registry key configuration for signing Verifiable Credentials
|
|
94
|
+
*
|
|
95
|
+
* Contains issuer information and cryptographic keys for signing VCs.
|
|
96
|
+
*/
|
|
97
|
+
export interface RegistryKey {
|
|
98
|
+
issuer: string;
|
|
99
|
+
kid: string;
|
|
100
|
+
privateKey?: string;
|
|
101
|
+
publicKey?: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Note: Schema validation is not currently implemented but may be added in the future
|
|
105
|
+
// for additional validation beyond the basic structure checks in isValidOAPPassport/isValidOAPDecision
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Convert OAP Passport to Verifiable Credential
|
|
109
|
+
*
|
|
110
|
+
* Exports an Open Agent Passport (OAP) as a W3C Verifiable Credential (VC),
|
|
111
|
+
* compliant with W3C VC Data Model 1.1. The credential is cryptographically
|
|
112
|
+
* signed using Ed25519 and includes a DID-based verification method.
|
|
113
|
+
*
|
|
114
|
+
* @param passport - The OAP Passport to convert
|
|
115
|
+
* @param registryKey - Registry key containing issuer info and private key for signing
|
|
116
|
+
* @returns Promise resolving to a Verifiable Credential
|
|
117
|
+
* @throws Error if passport structure is invalid or signing fails
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* const vc = await exportPassportToVC(passport, {
|
|
122
|
+
* issuer: "https://aport.io",
|
|
123
|
+
* kid: "ap_registry_ed25519_2024",
|
|
124
|
+
* privateKey: process.env.REGISTRY_PRIVATE_KEY
|
|
125
|
+
* });
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* @see https://www.w3.org/TR/vc-data-model/
|
|
129
|
+
*/
|
|
130
|
+
export async function exportPassportToVC(
|
|
131
|
+
passport: OAPPassport,
|
|
132
|
+
registryKey: RegistryKey
|
|
133
|
+
): Promise<VerifiableCredential> {
|
|
134
|
+
// Validate passport structure
|
|
135
|
+
if (!isValidOAPPassport(passport)) {
|
|
136
|
+
throw new Error("Invalid OAP passport structure");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Generate DID if not present in passport
|
|
140
|
+
// DID format: did:web:aport.io:api:agents:{agent_id}
|
|
141
|
+
let did: string;
|
|
142
|
+
if ((passport as any).did) {
|
|
143
|
+
did = (passport as any).did;
|
|
144
|
+
} else {
|
|
145
|
+
// Generate DID from agent_id
|
|
146
|
+
// This matches the DID resolution endpoint: /api/agents/{agent_id}/did.json
|
|
147
|
+
const baseUrl = registryKey.issuer
|
|
148
|
+
.replace(/^https?:\/\//, "")
|
|
149
|
+
.replace(/\/$/, "");
|
|
150
|
+
did = `did:web:${baseUrl}:api:agents:${passport.agent_id}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Use DID as issuer (W3C VC spec prefers DIDs over URLs)
|
|
154
|
+
const issuer = did;
|
|
155
|
+
|
|
156
|
+
// Construct verification method using DID (W3C VC best practice)
|
|
157
|
+
// DID-based verificationMethod resolves via DID Document
|
|
158
|
+
// DID Document is at: https://aport.io/api/agents/{agent_id}/did.json
|
|
159
|
+
// Public key is in the DID Document's verificationMethod array with id: {did}#key-1
|
|
160
|
+
const verificationMethod = `${did}#key-1`;
|
|
161
|
+
|
|
162
|
+
// Create VC structure (without proof first, proof is added after signing)
|
|
163
|
+
const vcWithoutProof: Omit<VerifiableCredential, "proof"> = {
|
|
164
|
+
"@context": [
|
|
165
|
+
"https://www.w3.org/2018/credentials/v1",
|
|
166
|
+
"https://raw.githubusercontent.com/aporthq/aport-spec/refs/heads/main/oap/vc/context-oap-v1.jsonld",
|
|
167
|
+
],
|
|
168
|
+
type: ["VerifiableCredential", "OAPPassportCredential"],
|
|
169
|
+
credentialSubject: {
|
|
170
|
+
// Map all OAP passport fields
|
|
171
|
+
agent_id: passport.agent_id,
|
|
172
|
+
kind: passport.kind,
|
|
173
|
+
spec_version: passport.spec_version,
|
|
174
|
+
parent_agent_id: passport.parent_agent_id,
|
|
175
|
+
owner_id: passport.owner_id,
|
|
176
|
+
owner_type: passport.owner_type,
|
|
177
|
+
assurance_level: passport.assurance_level,
|
|
178
|
+
status: passport.status,
|
|
179
|
+
capabilities: passport.capabilities,
|
|
180
|
+
limits: passport.limits,
|
|
181
|
+
regions: passport.regions,
|
|
182
|
+
metadata: passport.metadata,
|
|
183
|
+
created_at: passport.created_at,
|
|
184
|
+
updated_at: passport.updated_at,
|
|
185
|
+
version: passport.version,
|
|
186
|
+
did: did, // Include DID in credential subject
|
|
187
|
+
},
|
|
188
|
+
issuer: issuer,
|
|
189
|
+
issuanceDate: passport.created_at,
|
|
190
|
+
expirationDate: computeExpirationDate(passport),
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// Sign the credential (signs the credential without proof)
|
|
194
|
+
const jws = await signCredential(
|
|
195
|
+
vcWithoutProof as VerifiableCredential,
|
|
196
|
+
registryKey
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// Add proof to complete the VC
|
|
200
|
+
const vc: VerifiableCredential = {
|
|
201
|
+
...vcWithoutProof,
|
|
202
|
+
proof: {
|
|
203
|
+
type: "Ed25519Signature2020",
|
|
204
|
+
created: passport.created_at,
|
|
205
|
+
verificationMethod: verificationMethod,
|
|
206
|
+
proofPurpose: "assertionMethod",
|
|
207
|
+
jws: jws,
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
return vc;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Convert OAP Decision to Verifiable Credential
|
|
216
|
+
*
|
|
217
|
+
* Exports an OAP Decision as a W3C Verifiable Credential, representing a
|
|
218
|
+
* policy verification decision receipt. The credential is cryptographically
|
|
219
|
+
* signed using Ed25519.
|
|
220
|
+
*
|
|
221
|
+
* @param decision - The OAP Decision to convert
|
|
222
|
+
* @param registryKey - Registry key containing issuer info and private key for signing
|
|
223
|
+
* @returns Promise resolving to a Verifiable Credential
|
|
224
|
+
* @throws Error if decision structure is invalid or signing fails
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```typescript
|
|
228
|
+
* const vc = await exportDecisionToVC(decision, {
|
|
229
|
+
* issuer: "https://aport.io",
|
|
230
|
+
* kid: "ap_registry_ed25519_2024",
|
|
231
|
+
* privateKey: process.env.REGISTRY_PRIVATE_KEY
|
|
232
|
+
* });
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
export async function exportDecisionToVC(
|
|
236
|
+
decision: OAPDecision,
|
|
237
|
+
registryKey: RegistryKey
|
|
238
|
+
): Promise<VerifiableCredential> {
|
|
239
|
+
// Validate decision structure
|
|
240
|
+
if (!isValidOAPDecision(decision)) {
|
|
241
|
+
throw new Error("Invalid OAP decision structure");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Create VC structure (without proof first)
|
|
245
|
+
const vcWithoutProof: Omit<VerifiableCredential, "proof"> = {
|
|
246
|
+
"@context": [
|
|
247
|
+
"https://www.w3.org/2018/credentials/v1",
|
|
248
|
+
"https://raw.githubusercontent.com/aporthq/aport-spec/refs/heads/main/oap/vc/context-oap-v1.jsonld",
|
|
249
|
+
],
|
|
250
|
+
type: ["VerifiableCredential", "OAPDecisionReceipt"],
|
|
251
|
+
credentialSubject: {
|
|
252
|
+
// Map all OAP decision fields
|
|
253
|
+
decision_id: decision.decision_id,
|
|
254
|
+
policy_id: decision.policy_id,
|
|
255
|
+
agent_id: decision.agent_id,
|
|
256
|
+
owner_id: decision.owner_id,
|
|
257
|
+
assurance_level: decision.assurance_level,
|
|
258
|
+
allow: decision.allow,
|
|
259
|
+
reasons: decision.reasons,
|
|
260
|
+
created_at: decision.created_at,
|
|
261
|
+
expires_in: decision.expires_in,
|
|
262
|
+
passport_digest: decision.passport_digest,
|
|
263
|
+
signature: decision.signature,
|
|
264
|
+
kid: decision.kid,
|
|
265
|
+
decision_token: decision.decision_token,
|
|
266
|
+
remaining_daily_cap: decision.remaining_daily_cap,
|
|
267
|
+
},
|
|
268
|
+
issuer: registryKey.issuer,
|
|
269
|
+
issuanceDate: decision.created_at,
|
|
270
|
+
expirationDate: new Date(
|
|
271
|
+
new Date(decision.created_at).getTime() + decision.expires_in * 1000
|
|
272
|
+
).toISOString(),
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// Sign the credential
|
|
276
|
+
const jws = await signCredential(
|
|
277
|
+
vcWithoutProof as VerifiableCredential,
|
|
278
|
+
registryKey
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// Add proof to complete the VC
|
|
282
|
+
const vc: VerifiableCredential = {
|
|
283
|
+
...vcWithoutProof,
|
|
284
|
+
proof: {
|
|
285
|
+
type: "Ed25519Signature2020",
|
|
286
|
+
created: decision.created_at,
|
|
287
|
+
// For decisions, use registry's well-known endpoint
|
|
288
|
+
// This should resolve to: https://aport.io/.well-known/oap/keys.json
|
|
289
|
+
verificationMethod: `${registryKey.issuer}/.well-known/oap/keys.json#${registryKey.kid}`,
|
|
290
|
+
proofPurpose: "assertionMethod",
|
|
291
|
+
jws: jws,
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
return vc;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Convert Verifiable Credential to OAP Passport
|
|
300
|
+
*
|
|
301
|
+
* Imports a W3C Verifiable Credential back to an OAP Passport format.
|
|
302
|
+
* Verifies the credential's signature before conversion.
|
|
303
|
+
*
|
|
304
|
+
* @param vc - The Verifiable Credential to convert
|
|
305
|
+
* @param publicKey - Optional public key for signature verification. If not provided,
|
|
306
|
+
* verification will fail unless DID resolution is implemented.
|
|
307
|
+
* @returns Promise resolving to an OAP Passport
|
|
308
|
+
* @throws Error if VC structure is invalid, signature is invalid, or passport structure is invalid
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```typescript
|
|
312
|
+
* const passport = await importVCToPassport(vc, publicKey);
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
export async function importVCToPassport(
|
|
316
|
+
vc: VerifiableCredential,
|
|
317
|
+
publicKey?: string | Uint8Array
|
|
318
|
+
): Promise<OAPPassport> {
|
|
319
|
+
// Validate VC structure
|
|
320
|
+
if (!isValidVC(vc)) {
|
|
321
|
+
throw new Error("Invalid VC structure");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Verify signature
|
|
325
|
+
const isValid = await verifyCredentialSignature(vc, publicKey);
|
|
326
|
+
if (!isValid) {
|
|
327
|
+
throw new Error("Invalid VC signature");
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Extract passport from credential subject
|
|
331
|
+
const passport = vc.credentialSubject;
|
|
332
|
+
|
|
333
|
+
// Validate OAP passport structure
|
|
334
|
+
if (!isValidOAPPassport(passport)) {
|
|
335
|
+
throw new Error("Invalid OAP passport structure");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return passport;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Convert Verifiable Credential to OAP Decision
|
|
343
|
+
*
|
|
344
|
+
* Imports a W3C Verifiable Credential back to an OAP Decision format.
|
|
345
|
+
* Verifies the credential's signature before conversion.
|
|
346
|
+
*
|
|
347
|
+
* @param vc - The Verifiable Credential to convert
|
|
348
|
+
* @param publicKey - Optional public key for signature verification. If not provided,
|
|
349
|
+
* verification will fail unless DID resolution is implemented.
|
|
350
|
+
* @returns Promise resolving to an OAP Decision
|
|
351
|
+
* @throws Error if VC structure is invalid, signature is invalid, or decision structure is invalid
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* ```typescript
|
|
355
|
+
* const decision = await importVCToDecision(vc, publicKey);
|
|
356
|
+
* ```
|
|
357
|
+
*/
|
|
358
|
+
export async function importVCToDecision(
|
|
359
|
+
vc: VerifiableCredential,
|
|
360
|
+
publicKey?: string | Uint8Array
|
|
361
|
+
): Promise<OAPDecision> {
|
|
362
|
+
// Validate VC structure
|
|
363
|
+
if (!isValidVC(vc)) {
|
|
364
|
+
throw new Error("Invalid VC structure");
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Verify signature
|
|
368
|
+
const isValid = await verifyCredentialSignature(vc, publicKey);
|
|
369
|
+
if (!isValid) {
|
|
370
|
+
throw new Error("Invalid VC signature");
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Extract decision from credential subject
|
|
374
|
+
const decision = vc.credentialSubject;
|
|
375
|
+
|
|
376
|
+
// Validate OAP decision structure
|
|
377
|
+
if (!isValidOAPDecision(decision)) {
|
|
378
|
+
throw new Error("Invalid OAP decision structure");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return decision;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Validation Functions
|
|
386
|
+
*/
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Validates that an object conforms to the Verifiable Credential structure
|
|
390
|
+
*
|
|
391
|
+
* @param vc - Object to validate
|
|
392
|
+
* @returns true if the object has all required VC fields
|
|
393
|
+
*/
|
|
394
|
+
export function isValidVC(vc: any): boolean {
|
|
395
|
+
return (
|
|
396
|
+
vc &&
|
|
397
|
+
vc["@context"] &&
|
|
398
|
+
vc.type &&
|
|
399
|
+
vc.credentialSubject &&
|
|
400
|
+
vc.issuer &&
|
|
401
|
+
vc.issuanceDate &&
|
|
402
|
+
vc.proof
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Validates that an object conforms to the OAP Passport structure
|
|
408
|
+
*
|
|
409
|
+
* @param passport - Object to validate
|
|
410
|
+
* @returns true if the object has all required passport fields
|
|
411
|
+
*/
|
|
412
|
+
export function isValidOAPPassport(passport: any): boolean {
|
|
413
|
+
return (
|
|
414
|
+
passport &&
|
|
415
|
+
(passport.agent_id || passport.id) &&
|
|
416
|
+
passport.kind &&
|
|
417
|
+
(passport.spec_version || passport.version) &&
|
|
418
|
+
passport.owner_id &&
|
|
419
|
+
passport.owner_type &&
|
|
420
|
+
passport.assurance_level &&
|
|
421
|
+
passport.status &&
|
|
422
|
+
passport.capabilities &&
|
|
423
|
+
passport.limits &&
|
|
424
|
+
passport.regions &&
|
|
425
|
+
passport.created_at &&
|
|
426
|
+
passport.updated_at
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Validates that an object conforms to the OAP Decision structure
|
|
432
|
+
*
|
|
433
|
+
* @param decision - Object to validate
|
|
434
|
+
* @returns true if the object has all required decision fields
|
|
435
|
+
*/
|
|
436
|
+
export function isValidOAPDecision(decision: any): boolean {
|
|
437
|
+
return (
|
|
438
|
+
decision &&
|
|
439
|
+
decision.decision_id &&
|
|
440
|
+
decision.policy_id &&
|
|
441
|
+
decision.agent_id &&
|
|
442
|
+
decision.owner_id &&
|
|
443
|
+
decision.assurance_level &&
|
|
444
|
+
typeof decision.allow === "boolean" &&
|
|
445
|
+
decision.reasons &&
|
|
446
|
+
decision.created_at &&
|
|
447
|
+
decision.expires_in &&
|
|
448
|
+
decision.passport_digest &&
|
|
449
|
+
decision.signature &&
|
|
450
|
+
decision.kid
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Helper Functions
|
|
456
|
+
*/
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Computes the expiration date for a passport-based VC
|
|
460
|
+
*
|
|
461
|
+
* Uses passport.expires_at if set, otherwise checks never_expires flag,
|
|
462
|
+
* or defaults to 1 year from creation.
|
|
463
|
+
*
|
|
464
|
+
* @param passport - Passport object with expiration metadata
|
|
465
|
+
* @returns ISO 8601 timestamp string
|
|
466
|
+
*/
|
|
467
|
+
function computeExpirationDate(passport: OAPPassport | any): string {
|
|
468
|
+
// Use native expiry if set
|
|
469
|
+
if (passport.expires_at) {
|
|
470
|
+
return passport.expires_at;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Check never_expires flag
|
|
474
|
+
if (passport.never_expires) {
|
|
475
|
+
// W3C VC spec requires expirationDate, use far future for perpetual credentials
|
|
476
|
+
return new Date("2999-12-31T23:59:59Z").toISOString();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Default: 1 year from creation
|
|
480
|
+
const created = new Date(passport.created_at);
|
|
481
|
+
const expiration = new Date(created.getTime() + 365 * 24 * 60 * 60 * 1000);
|
|
482
|
+
return expiration.toISOString();
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Sign a credential using Ed25519
|
|
487
|
+
*
|
|
488
|
+
* @param vc - The Verifiable Credential to sign (without proof)
|
|
489
|
+
* @param registryKey - Registry key containing private key
|
|
490
|
+
* @returns JWS in compact format: <header>.<payload>.<signature>
|
|
491
|
+
*
|
|
492
|
+
* Note: JWS is safe to expose publicly - it's a cryptographic signature meant for verification.
|
|
493
|
+
* Anyone can verify it using the public key from verificationMethod.
|
|
494
|
+
*
|
|
495
|
+
* According to W3C VC spec, we sign the credential WITHOUT the proof, then add the proof.
|
|
496
|
+
*
|
|
497
|
+
* @see https://www.w3.org/TR/vc-data-model/#proofs-signatures
|
|
498
|
+
* @see https://w3c-ccg.github.io/lds-ed25519-2020/#ed25519signature2020
|
|
499
|
+
*/
|
|
500
|
+
async function signCredential(
|
|
501
|
+
vc: VerifiableCredential,
|
|
502
|
+
registryKey: RegistryKey
|
|
503
|
+
): Promise<string> {
|
|
504
|
+
if (!registryKey.privateKey) {
|
|
505
|
+
throw new Error(
|
|
506
|
+
"REGISTRY_PRIVATE_KEY is required for VC signing. Set it in environment variables."
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Step 1: Create credential without proof (proof is added after signing)
|
|
511
|
+
const credentialWithoutProof = {
|
|
512
|
+
"@context": vc["@context"],
|
|
513
|
+
type: vc.type,
|
|
514
|
+
credentialSubject: vc.credentialSubject,
|
|
515
|
+
issuer: vc.issuer,
|
|
516
|
+
issuanceDate: vc.issuanceDate,
|
|
517
|
+
expirationDate: vc.expirationDate,
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// Step 2: Canonicalize the JSON-LD representation
|
|
521
|
+
const canonicalMessage = await canonicalizeJsonLd(credentialWithoutProof);
|
|
522
|
+
|
|
523
|
+
// Step 3: Sign using Ed25519
|
|
524
|
+
const signatureBytes = await signEd25519(
|
|
525
|
+
canonicalMessage,
|
|
526
|
+
registryKey.privateKey
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
// Step 4: Format as JWS (compact format)
|
|
530
|
+
// JWS header for Ed25519
|
|
531
|
+
const header = {
|
|
532
|
+
alg: "EdDSA",
|
|
533
|
+
b64: false,
|
|
534
|
+
crit: ["b64"],
|
|
535
|
+
};
|
|
536
|
+
const headerB64 = bytesToBase64url(
|
|
537
|
+
new TextEncoder().encode(JSON.stringify(header))
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
// For Ed25519Signature2020, we use detached payload
|
|
541
|
+
// The signature is over the canonicalized credential
|
|
542
|
+
const signatureB64 = bytesToBase64url(signatureBytes);
|
|
543
|
+
|
|
544
|
+
// Return compact JWS format: header..signature (detached payload)
|
|
545
|
+
// Note: For Ed25519Signature2020, the payload is the canonicalized credential
|
|
546
|
+
return `${headerB64}..${signatureB64}`;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Verify a Verifiable Credential's Ed25519 signature
|
|
551
|
+
*
|
|
552
|
+
* @param vc - The Verifiable Credential to verify
|
|
553
|
+
* @param publicKey - Optional public key for signature verification. If not provided,
|
|
554
|
+
* verification will fail unless DID resolution is implemented.
|
|
555
|
+
* @returns Promise<boolean> - true if signature is valid, false otherwise
|
|
556
|
+
*
|
|
557
|
+
* @see https://www.w3.org/TR/vc-data-model/#proofs-signatures
|
|
558
|
+
* @see https://w3c-ccg.github.io/lds-ed25519-2020/#ed25519signature2020
|
|
559
|
+
*/
|
|
560
|
+
export async function verifyCredentialSignature(
|
|
561
|
+
vc: VerifiableCredential,
|
|
562
|
+
publicKey?: string | Uint8Array
|
|
563
|
+
): Promise<boolean> {
|
|
564
|
+
if (!vc.proof || !vc.proof.jws) {
|
|
565
|
+
return false;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
try {
|
|
569
|
+
// Step 1: Recreate credential without proof
|
|
570
|
+
const credentialWithoutProof = {
|
|
571
|
+
"@context": vc["@context"],
|
|
572
|
+
type: vc.type,
|
|
573
|
+
credentialSubject: vc.credentialSubject,
|
|
574
|
+
issuer: vc.issuer,
|
|
575
|
+
issuanceDate: vc.issuanceDate,
|
|
576
|
+
expirationDate: vc.expirationDate,
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
// Step 2: Canonicalize the JSON-LD representation (same as signing)
|
|
580
|
+
const canonicalMessage = await canonicalizeJsonLd(credentialWithoutProof);
|
|
581
|
+
|
|
582
|
+
// Step 3: Extract signature from JWS
|
|
583
|
+
const jwsParts = vc.proof.jws.split(".");
|
|
584
|
+
if (jwsParts.length !== 3) {
|
|
585
|
+
// Invalid JWS format - expected header..signature (detached payload)
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const signatureB64 = jwsParts[2];
|
|
590
|
+
const signatureBytes = base64urlToBytes(signatureB64);
|
|
591
|
+
|
|
592
|
+
// Step 4: Get public key
|
|
593
|
+
let publicKeyBytes: Uint8Array;
|
|
594
|
+
|
|
595
|
+
if (publicKey) {
|
|
596
|
+
// Use provided public key
|
|
597
|
+
publicKeyBytes = publicKeyToBytes(publicKey);
|
|
598
|
+
} else {
|
|
599
|
+
// Public key resolution from verificationMethod is not yet implemented
|
|
600
|
+
// This would require DID Document resolution, which is environment-dependent
|
|
601
|
+
// For now, require public key to be provided explicitly
|
|
602
|
+
throw new Error(
|
|
603
|
+
"Public key is required for verification. Provide it explicitly or implement DID resolution to fetch from verificationMethod."
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Step 5: Verify signature
|
|
608
|
+
return await verifyEd25519(
|
|
609
|
+
canonicalMessage,
|
|
610
|
+
signatureBytes,
|
|
611
|
+
publicKeyBytes
|
|
612
|
+
);
|
|
613
|
+
} catch (error) {
|
|
614
|
+
// Return false on any verification error (invalid signature, malformed data, etc.)
|
|
615
|
+
// Error details are not exposed to prevent information leakage
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Convert base64url string to Uint8Array
|
|
622
|
+
*
|
|
623
|
+
* Handles both Node.js (Buffer) and browser/Workers (atob) environments.
|
|
624
|
+
*
|
|
625
|
+
* @param base64url - Base64url-encoded string
|
|
626
|
+
* @returns Decoded bytes
|
|
627
|
+
* @internal
|
|
628
|
+
*/
|
|
629
|
+
function base64urlToBytes(base64url: string): Uint8Array {
|
|
630
|
+
// Convert base64url to base64
|
|
631
|
+
let base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
|
|
632
|
+
|
|
633
|
+
// Add padding if needed
|
|
634
|
+
while (base64.length % 4) {
|
|
635
|
+
base64 += "=";
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Decode base64
|
|
639
|
+
if (typeof Buffer !== "undefined") {
|
|
640
|
+
return Buffer.from(base64, "base64");
|
|
641
|
+
} else {
|
|
642
|
+
// For Cloudflare Workers, use atob
|
|
643
|
+
const binary = atob(base64);
|
|
644
|
+
const bytes = new Uint8Array(binary.length);
|
|
645
|
+
for (let i = 0; i < binary.length; i++) {
|
|
646
|
+
bytes[i] = binary.charCodeAt(i);
|
|
647
|
+
}
|
|
648
|
+
return bytes;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Export Verifiable Presentation functions
|
|
653
|
+
export * from "./vp";
|