@europeum-ebsi/cli 0.0.0 → 1.0.0-rc.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/LICENSE.txt +289 -289
- package/LICENSES.txt +538 -0
- package/README.md +1140 -98
- package/dist/api/client.d.ts +4 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +14 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/fetch.d.ts +13 -0
- package/dist/api/fetch.d.ts.map +1 -0
- package/dist/api/fetch.js +30 -0
- package/dist/api/fetch.js.map +1 -0
- package/dist/api/params.d.ts +4 -0
- package/dist/api/params.d.ts.map +1 -0
- package/dist/api/params.js +9 -0
- package/dist/api/params.js.map +1 -0
- package/dist/auth/dcql.d.ts +5 -0
- package/dist/auth/dcql.d.ts.map +1 -0
- package/dist/auth/dcql.js +41 -0
- package/dist/auth/dcql.js.map +1 -0
- package/dist/auth/presentation.d.ts +30 -0
- package/dist/auth/presentation.d.ts.map +1 -0
- package/dist/auth/presentation.js +99 -0
- package/dist/auth/presentation.js.map +1 -0
- package/dist/auth/token.d.ts +11 -0
- package/dist/auth/token.d.ts.map +1 -0
- package/dist/auth/token.js +116 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +96 -10
- package/dist/cli.js.map +1 -1
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +99 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/config-cmd.d.ts +3 -0
- package/dist/commands/config-cmd.d.ts.map +1 -0
- package/dist/commands/config-cmd.js +86 -0
- package/dist/commands/config-cmd.js.map +1 -0
- package/dist/commands/contracts-registry.d.ts +3 -0
- package/dist/commands/contracts-registry.d.ts.map +1 -0
- package/dist/commands/contracts-registry.js +132 -0
- package/dist/commands/contracts-registry.js.map +1 -0
- package/dist/commands/did-registry.d.ts +3 -0
- package/dist/commands/did-registry.d.ts.map +1 -0
- package/dist/commands/did-registry.js +110 -0
- package/dist/commands/did-registry.js.map +1 -0
- package/dist/commands/faucet.d.ts +3 -0
- package/dist/commands/faucet.d.ts.map +1 -0
- package/dist/commands/faucet.js +78 -0
- package/dist/commands/faucet.js.map +1 -0
- package/dist/commands/health.d.ts +3 -0
- package/dist/commands/health.d.ts.map +1 -0
- package/dist/commands/health.js +20 -0
- package/dist/commands/health.js.map +1 -0
- package/dist/commands/issuers-registry.d.ts +3 -0
- package/dist/commands/issuers-registry.d.ts.map +1 -0
- package/dist/commands/issuers-registry.js +340 -0
- package/dist/commands/issuers-registry.js.map +1 -0
- package/dist/commands/ledger.d.ts +3 -0
- package/dist/commands/ledger.d.ts.map +1 -0
- package/dist/commands/ledger.js +549 -372
- package/dist/commands/ledger.js.map +1 -1
- package/dist/commands/onboarding.d.ts +3 -0
- package/dist/commands/onboarding.d.ts.map +1 -0
- package/dist/commands/onboarding.js +301 -0
- package/dist/commands/onboarding.js.map +1 -0
- package/dist/commands/openapi.d.ts +3 -0
- package/dist/commands/openapi.d.ts.map +1 -0
- package/dist/commands/openapi.js +37 -0
- package/dist/commands/openapi.js.map +1 -0
- package/dist/commands/policies-registry.d.ts +3 -0
- package/dist/commands/policies-registry.d.ts.map +1 -0
- package/dist/commands/policies-registry.js +198 -0
- package/dist/commands/policies-registry.js.map +1 -0
- package/dist/commands/registry.d.ts +27 -0
- package/dist/commands/registry.d.ts.map +1 -0
- package/dist/commands/registry.js +62 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/commands/schemas-registry.d.ts +3 -0
- package/dist/commands/schemas-registry.d.ts.map +1 -0
- package/dist/commands/schemas-registry.js +231 -0
- package/dist/commands/schemas-registry.js.map +1 -0
- package/dist/commands/shared/args.d.ts +2 -0
- package/dist/commands/shared/args.d.ts.map +1 -0
- package/dist/commands/shared/args.js +4 -0
- package/dist/commands/shared/args.js.map +1 -0
- package/dist/commands/shared/wallet-opts.d.ts +9 -0
- package/dist/commands/shared/wallet-opts.d.ts.map +1 -0
- package/dist/commands/shared/wallet-opts.js +44 -0
- package/dist/commands/shared/wallet-opts.js.map +1 -0
- package/dist/commands/timestamp.d.ts +3 -0
- package/dist/commands/timestamp.d.ts.map +1 -0
- package/dist/commands/timestamp.js +250 -0
- package/dist/commands/timestamp.js.map +1 -0
- package/dist/commands/track-and-trace.d.ts +3 -0
- package/dist/commands/track-and-trace.d.ts.map +1 -0
- package/dist/commands/track-and-trace.js +235 -0
- package/dist/commands/track-and-trace.js.map +1 -0
- package/dist/commands/wallet.d.ts +3 -0
- package/dist/commands/wallet.d.ts.map +1 -0
- package/dist/commands/wallet.js +123 -0
- package/dist/commands/wallet.js.map +1 -0
- package/dist/config/config.d.ts +45 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +111 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/ebsi-env.d.ts +4 -0
- package/dist/config/ebsi-env.d.ts.map +1 -0
- package/dist/config/ebsi-env.js +37 -0
- package/dist/config/ebsi-env.js.map +1 -0
- package/dist/crypto/wallet.d.ts +34 -0
- package/dist/crypto/wallet.d.ts.map +1 -0
- package/dist/crypto/wallet.js +100 -0
- package/dist/crypto/wallet.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -6
- package/dist/index.js.map +1 -1
- package/dist/ledger/read.d.ts +11 -0
- package/dist/ledger/read.d.ts.map +1 -0
- package/dist/ledger/read.js +24 -0
- package/dist/ledger/read.js.map +1 -0
- package/dist/ledger/registries.d.ts +25 -0
- package/dist/ledger/registries.d.ts.map +1 -0
- package/dist/ledger/registries.js +111 -0
- package/dist/ledger/registries.js.map +1 -0
- package/dist/ledger/write.d.ts +43 -0
- package/dist/ledger/write.d.ts.map +1 -0
- package/dist/ledger/write.js +119 -0
- package/dist/ledger/write.js.map +1 -0
- package/dist/onboarding/vc.d.ts +18 -0
- package/dist/onboarding/vc.d.ts.map +1 -0
- package/dist/onboarding/vc.js +72 -0
- package/dist/onboarding/vc.js.map +1 -0
- package/dist/output/format.d.ts +7 -0
- package/dist/output/format.d.ts.map +1 -0
- package/dist/output/format.js +94 -0
- package/dist/output/format.js.map +1 -0
- package/dist/tui/App.d.ts +3 -0
- package/dist/tui/App.d.ts.map +1 -0
- package/dist/tui/App.js +99 -0
- package/dist/tui/App.js.map +1 -0
- package/dist/tui/CommandPalette.d.ts +8 -0
- package/dist/tui/CommandPalette.d.ts.map +1 -0
- package/dist/tui/CommandPalette.js +105 -0
- package/dist/tui/CommandPalette.js.map +1 -0
- package/package.json +66 -108
- package/src/api/client.ts +26 -0
- package/src/api/fetch.ts +53 -0
- package/src/api/params.ts +17 -0
- package/src/auth/dcql.ts +84 -0
- package/src/auth/presentation.ts +255 -0
- package/src/auth/token.ts +244 -0
- package/src/cli.ts +164 -0
- package/src/commands/auth.ts +119 -0
- package/src/commands/config-cmd.ts +122 -0
- package/src/commands/contracts-registry.ts +156 -0
- package/src/commands/did-registry.ts +126 -0
- package/src/commands/faucet.ts +112 -0
- package/src/commands/health.ts +21 -0
- package/src/commands/issuers-registry.ts +385 -0
- package/src/commands/ledger.ts +777 -0
- package/src/commands/onboarding.ts +411 -0
- package/src/commands/openapi.ts +42 -0
- package/src/commands/policies-registry.ts +218 -0
- package/src/commands/registry.ts +152 -0
- package/src/commands/schemas-registry.ts +255 -0
- package/src/commands/shared/args.ts +14 -0
- package/src/commands/shared/wallet-opts.ts +85 -0
- package/src/commands/timestamp.ts +272 -0
- package/src/commands/track-and-trace.ts +263 -0
- package/src/commands/wallet.ts +175 -0
- package/src/config/config.ts +275 -0
- package/src/config/ebsi-env.ts +67 -0
- package/src/crypto/wallet.ts +291 -0
- package/src/index.ts +24 -0
- package/src/ledger/read.ts +70 -0
- package/src/ledger/registries.ts +285 -0
- package/src/ledger/write.ts +381 -0
- package/src/onboarding/vc.ts +205 -0
- package/src/output/format.ts +157 -0
- package/src/tui/App.tsx +282 -0
- package/src/tui/CommandPalette.tsx +223 -0
- package/bin/cli.js +0 -13
- package/dist/abi/pilot/DidRegistry.js +0 -568
- package/dist/abi/pilot/DidRegistry.js.map +0 -1
- package/dist/abi/pilot/Ptr.js +0 -666
- package/dist/abi/pilot/Ptr.js.map +0 -1
- package/dist/abi/pilot/SchemaSCRegistry.js +0 -264
- package/dist/abi/pilot/SchemaSCRegistry.js.map +0 -1
- package/dist/abi/pilot/Tcr.js +0 -715
- package/dist/abi/pilot/Tcr.js.map +0 -1
- package/dist/abi/pilot/Timestamp.js +0 -658
- package/dist/abi/pilot/Timestamp.js.map +0 -1
- package/dist/abi/pilot/Tir.js +0 -428
- package/dist/abi/pilot/Tir.js.map +0 -1
- package/dist/abi/pilot/TnT.js +0 -842
- package/dist/abi/pilot/TnT.js.map +0 -1
- package/dist/abi/pilot/Tpr.js +0 -492
- package/dist/abi/pilot/Tpr.js.map +0 -1
- package/dist/abi/test/DidRegistry.js +0 -568
- package/dist/abi/test/DidRegistry.js.map +0 -1
- package/dist/abi/test/Ptr.js +0 -666
- package/dist/abi/test/Ptr.js.map +0 -1
- package/dist/abi/test/SchemaSCRegistry.js +0 -300
- package/dist/abi/test/SchemaSCRegistry.js.map +0 -1
- package/dist/abi/test/Tcr.js +0 -715
- package/dist/abi/test/Tcr.js.map +0 -1
- package/dist/abi/test/Timestamp.js +0 -658
- package/dist/abi/test/Timestamp.js.map +0 -1
- package/dist/abi/test/Tir.js +0 -428
- package/dist/abi/test/Tir.js.map +0 -1
- package/dist/abi/test/TnT.js +0 -842
- package/dist/abi/test/TnT.js.map +0 -1
- package/dist/abi/test/Tpr.js +0 -492
- package/dist/abi/test/Tpr.js.map +0 -1
- package/dist/app.js +0 -659
- package/dist/app.js.map +0 -1
- package/dist/buildParam/didr.js +0 -374
- package/dist/buildParam/didr.js.map +0 -1
- package/dist/buildParam/index.js +0 -53
- package/dist/buildParam/index.js.map +0 -1
- package/dist/buildParam/ptr.js +0 -80
- package/dist/buildParam/ptr.js.map +0 -1
- package/dist/buildParam/tcr.js +0 -36
- package/dist/buildParam/tcr.js.map +0 -1
- package/dist/buildParam/timestamp.js +0 -350
- package/dist/buildParam/timestamp.js.map +0 -1
- package/dist/buildParam/tir.js +0 -110
- package/dist/buildParam/tir.js.map +0 -1
- package/dist/buildParam/tnt.js +0 -214
- package/dist/buildParam/tnt.js.map +0 -1
- package/dist/buildParam/tpr.js +0 -91
- package/dist/buildParam/tpr.js.map +0 -1
- package/dist/buildParam/tsr.js +0 -115
- package/dist/buildParam/tsr.js.map +0 -1
- package/dist/commands/authorisation.js +0 -144
- package/dist/commands/authorisation.js.map +0 -1
- package/dist/commands/compute.js +0 -680
- package/dist/commands/compute.js.map +0 -1
- package/dist/commands/conformance.js +0 -813
- package/dist/commands/conformance.js.map +0 -1
- package/dist/commands/hardwarewallet.js +0 -41
- package/dist/commands/hardwarewallet.js.map +0 -1
- package/dist/commands/index.js +0 -8
- package/dist/commands/index.js.map +0 -1
- package/dist/commands/ptr.js +0 -54
- package/dist/commands/ptr.js.map +0 -1
- package/dist/commands/tcr.js +0 -50
- package/dist/commands/tcr.js.map +0 -1
- package/dist/commands/tir.js +0 -64
- package/dist/commands/tir.js.map +0 -1
- package/dist/commands/tnl.js +0 -81
- package/dist/commands/tnl.js.map +0 -1
- package/dist/commands/tsr.js +0 -45
- package/dist/commands/tsr.js.map +0 -1
- package/dist/commands/view.js +0 -53
- package/dist/commands/view.js.map +0 -1
- package/dist/config.js +0 -497
- package/dist/config.js.map +0 -1
- package/dist/interfaces/context.js +0 -2
- package/dist/interfaces/context.js.map +0 -1
- package/dist/interfaces/contracts/ProxyFactory.js +0 -2
- package/dist/interfaces/contracts/ProxyFactory.js.map +0 -1
- package/dist/interfaces/contracts/ProxyTemplateRegistry.js +0 -2
- package/dist/interfaces/contracts/ProxyTemplateRegistry.js.map +0 -1
- package/dist/interfaces/contracts/SchemaSCRegistry.js +0 -2
- package/dist/interfaces/contracts/SchemaSCRegistry.js.map +0 -1
- package/dist/interfaces/contracts/common.js +0 -2
- package/dist/interfaces/contracts/common.js.map +0 -1
- package/dist/interfaces/index.js +0 -5
- package/dist/interfaces/index.js.map +0 -1
- package/dist/interfaces/jsonrpc.interface.js +0 -2
- package/dist/interfaces/jsonrpc.interface.js.map +0 -1
- package/dist/interfaces/paginated-list.interface.js +0 -2
- package/dist/interfaces/paginated-list.interface.js.map +0 -1
- package/dist/interfaces/unsigned-transaction.interface.js +0 -2
- package/dist/interfaces/unsigned-transaction.interface.js.map +0 -1
- package/dist/interfaces/utils.interface.js +0 -2
- package/dist/interfaces/utils.interface.js.map +0 -1
- package/dist/programs/migrateDid.js +0 -159
- package/dist/programs/migrateDid.js.map +0 -1
- package/dist/programs/migrateDids.js +0 -232
- package/dist/programs/migrateDids.js.map +0 -1
- package/dist/programs/migrateTsr.js +0 -201
- package/dist/programs/migrateTsr.js.map +0 -1
- package/dist/scripts/accreditAndAuthorize/conformance/step1 +0 -34
- package/dist/scripts/accreditAndAuthorize/conformance/step2 +0 -18
- package/dist/scripts/accreditAndAuthorize/conformance/step3 +0 -9
- package/dist/scripts/accreditAndAuthorize/conformance/step4 +0 -27
- package/dist/scripts/accreditAndAuthorize/conformance/step5 +0 -13
- package/dist/scripts/accreditAndAuthorize/conformance/step6 +0 -14
- package/dist/scripts/accreditAndAuthorize/conformance/step7 +0 -7
- package/dist/scripts/accreditAndAuthorize/test/step1 +0 -34
- package/dist/scripts/accreditAndAuthorize/test/step2 +0 -18
- package/dist/scripts/accreditAndAuthorize/test/step3 +0 -9
- package/dist/scripts/accreditAndAuthorize/test/step4 +0 -27
- package/dist/scripts/accreditAndAuthorize/test/step5 +0 -13
- package/dist/scripts/accreditAndAuthorize/test/step6 +0 -14
- package/dist/scripts/accreditAndAuthorize/test/step7 +0 -7
- package/dist/scripts/accreditTI +0 -21
- package/dist/scripts/assets/Nodes.json +0 -27
- package/dist/scripts/assets/vcdm1.1/CTRevocableCredential.json +0 -30
- package/dist/scripts/assets/vcdm1.1/CredentialToAttestVerifiableAuthorisationForTrustChain.json +0 -29
- package/dist/scripts/assets/vcdm1.1/Nodes.json +0 -27
- package/dist/scripts/assets/vcdm1.1/TrustedNodesList.json +0 -35
- package/dist/scripts/assets/vcdm1.1/VerifiableAccreditationToAccredit.json +0 -35
- package/dist/scripts/assets/vcdm1.1/VerifiableAccreditationToAttest.json +0 -33
- package/dist/scripts/assets/vcdm1.1/VerifiableAuthorisationForTrustChain.json +0 -22
- package/dist/scripts/assets/vcdm1.1/VerifiableAuthorisationToInvoke.json +0 -24
- package/dist/scripts/assets/vcdm1.1/VerifiableAuthorisationToOnboard.json +0 -24
- package/dist/scripts/assets/vcdm2.0/CredentialToAttestVerifiableAuthorisationForTrustChain.json +0 -29
- package/dist/scripts/assets/vcdm2.0/TrustedNodesList.json +0 -36
- package/dist/scripts/assets/vcdm2.0/VerifiableAccreditationToAccredit.json +0 -40
- package/dist/scripts/assets/vcdm2.0/VerifiableAccreditationToAttest.json +0 -38
- package/dist/scripts/assets/vcdm2.0/VerifiableAuthorisationForTrustChain.json +0 -30
- package/dist/scripts/assets/vcdm2.0/VerifiableAuthorisationToInvoke.json +0 -24
- package/dist/scripts/assets/vcdm2.0/VerifiableAuthorisationToOnboard.json +0 -23
- package/dist/scripts/bootstrap/0a-loadTPROperator +0 -5
- package/dist/scripts/bootstrap/0b-loadAdmin +0 -8
- package/dist/scripts/bootstrap/1-populateTPR +0 -92
- package/dist/scripts/bootstrap/2-populateDID +0 -6
- package/dist/scripts/bootstrap/3-populateTimestamp +0 -9
- package/dist/scripts/bootstrap/4-populateTIR +0 -8
- package/dist/scripts/bootstrap/5-populateTSR +0 -85
- package/dist/scripts/bootstrap/6-setupConformanceIssuer +0 -35
- package/dist/scripts/bootstrap/README.md +0 -218
- package/dist/scripts/issueVcInvoke +0 -10
- package/dist/scripts/issueVcOnboard +0 -9
- package/dist/scripts/issueVcRootTAO +0 -11
- package/dist/scripts/issueVcTAO +0 -12
- package/dist/scripts/issueVcTI +0 -12
- package/dist/scripts/issueVcTnl +0 -32
- package/dist/scripts/issue_CTRevocableCredential +0 -12
- package/dist/scripts/issue_SelfAttestationSupportOffice +0 -10
- package/dist/scripts/issue_VerifiableAccreditationToAccredit +0 -11
- package/dist/scripts/issue_VerifiableAccreditationToAttest +0 -11
- package/dist/scripts/issue_VerifiableAuthorisationForTrustChain +0 -10
- package/dist/scripts/issue_VerifiableAuthorisationToOnboard +0 -9
- package/dist/scripts/preregisterIssuer +0 -6
- package/dist/scripts/registerDidDocument +0 -15
- package/dist/scripts/registerIssuer +0 -6
- package/dist/scripts/updateVcRootTAO +0 -10
- package/dist/scripts/updateVcTAO +0 -11
- package/dist/scripts/updateVcTI +0 -11
- package/dist/scripts/vcdm2.0/issueSelfAttestationSupportOffice +0 -10
- package/dist/scripts/vcdm2.0/issueVcInvoke +0 -12
- package/dist/scripts/vcdm2.0/issueVcOnboard +0 -11
- package/dist/scripts/vcdm2.0/issueVcRootTAO +0 -14
- package/dist/scripts/vcdm2.0/issueVcTAO +0 -15
- package/dist/scripts/vcdm2.0/issueVcTI +0 -15
- package/dist/scripts/vcdm2.0/issueVcTnl +0 -34
- package/dist/scripts/vcdm2.0/registerDidDocument +0 -15
- package/dist/scripts/vcdm2.0/updateVcRootTAO +0 -13
- package/dist/scripts/vcdm2.0/updateVcTAO +0 -14
- package/dist/scripts/vcdm2.0/updateVcTI +0 -14
- package/dist/scripts/vcdm2.0/verifyVcTnl +0 -10
- package/dist/scripts/verifyVcTnl +0 -10
- package/dist/scripts/wct/accreditAndAuthorize +0 -133
- package/dist/scripts/wct/accreditAndAuthorize2 +0 -89
- package/dist/scripts/wct/holderWallet +0 -39
- package/dist/scripts/wct/holderWallet2 +0 -39
- package/dist/scripts/wct/issueToHolder +0 -38
- package/dist/scripts/wct/issueToHolder2 +0 -38
- package/dist/scripts/wct/pda1 +0 -3
- package/dist/scripts/wct/verifier +0 -8
- package/dist/scripts/wct/verifier2 +0 -7
- package/dist/utils/Client.js +0 -155
- package/dist/utils/Client.js.map +0 -1
- package/dist/utils/HardwareWallet.js +0 -326
- package/dist/utils/HardwareWallet.js.map +0 -1
- package/dist/utils/http.js +0 -132
- package/dist/utils/http.js.map +0 -1
- package/dist/utils/index.js +0 -8
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/jsonrpc.js +0 -38
- package/dist/utils/jsonrpc.js.map +0 -1
- package/dist/utils/print.js +0 -56
- package/dist/utils/print.js.map +0 -1
- package/dist/utils/utils.js +0 -427
- package/dist/utils/utils.js.map +0 -1
- package/dist/utils/vcdm.js +0 -47
- package/dist/utils/vcdm.js.map +0 -1
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import envPaths from "env-paths";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import type { KeyAlgorithm } from "../crypto/wallet.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Supported EBSI environments
|
|
9
|
+
*/
|
|
10
|
+
export type EbsiEnv = "local" | "pilot" | "preprod" | "prod" | "test";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* All known EBSI environment names
|
|
14
|
+
*/
|
|
15
|
+
export const EBSI_ENVS: EbsiEnv[] = [
|
|
16
|
+
"local",
|
|
17
|
+
"test",
|
|
18
|
+
"pilot",
|
|
19
|
+
"preprod",
|
|
20
|
+
"prod",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Default base URLs (including /v1) for the environments that have a single
|
|
25
|
+
* well-known API host.
|
|
26
|
+
*
|
|
27
|
+
* `preprod` and `prod` are deliberately absent: every node operator serves the
|
|
28
|
+
* API under a domain of their own choosing (`api-preprod.ebsi.<domain>` /
|
|
29
|
+
* `api.ebsi.<domain>`), so those environments must always be paired with an
|
|
30
|
+
* explicit `--base-url` / `EBSI_API_BASE_URL`.
|
|
31
|
+
*/
|
|
32
|
+
export const ENV_BASE_URLS: Partial<Record<EbsiEnv, string>> = {
|
|
33
|
+
local: "http://localhost:3000/v1",
|
|
34
|
+
pilot: "https://api-pilot.ebsi.eu/v1",
|
|
35
|
+
test: "https://api-test.testnode02.ebsi.eu/v1",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export interface CliConfig {
|
|
39
|
+
baseUrl: string;
|
|
40
|
+
/**
|
|
41
|
+
* EBSI DID (did:ebsi:...) associated with the private key(s).
|
|
42
|
+
*/
|
|
43
|
+
did?: string;
|
|
44
|
+
/**
|
|
45
|
+
* EBSI environment the CLI targets. Always one of `EBSI_ENVS`, even when the
|
|
46
|
+
* base URL is overridden: it names the EBSI network credentials and URIs
|
|
47
|
+
* are minted for.
|
|
48
|
+
*/
|
|
49
|
+
env: EbsiEnv;
|
|
50
|
+
/**
|
|
51
|
+
* Secp256k1 private key hex (0x-prefixed). Mandatory for anything that
|
|
52
|
+
* signs: always signs Ethereum transactions, and signs VCs/VPs unless
|
|
53
|
+
* `signAlg` is "ES256".
|
|
54
|
+
*/
|
|
55
|
+
es256kPrivateKey?: string;
|
|
56
|
+
/**
|
|
57
|
+
* P-256 private key hex, for signing VCs/VPs. Optional — used instead of
|
|
58
|
+
* the mandatory secp256k1 key only when `signAlg` (or an explicit choice)
|
|
59
|
+
* picks ES256.
|
|
60
|
+
*/
|
|
61
|
+
es256PrivateKey?: string;
|
|
62
|
+
output: OutputFormat;
|
|
63
|
+
/**
|
|
64
|
+
* Which key signs VCs/VPs when both `es256kPrivateKey` and
|
|
65
|
+
* `es256PrivateKey` are configured. Required in that case — see
|
|
66
|
+
* `WalletDetails.signingKey()` in `crypto/wallet.ts`.
|
|
67
|
+
*/
|
|
68
|
+
signAlg?: KeyAlgorithm;
|
|
69
|
+
/**
|
|
70
|
+
* Bearer access token attached to every request as `Authorization: Bearer <token>`.
|
|
71
|
+
*/
|
|
72
|
+
token?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type OutputFormat = "json" | "pretty" | "table";
|
|
76
|
+
export const OUTPUT_FORMATS: OutputFormat[] = ["pretty", "json", "table"];
|
|
77
|
+
|
|
78
|
+
export const SIGN_ALGS: KeyAlgorithm[] = ["ES256", "ES256K"];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Persisted config file shape
|
|
82
|
+
*/
|
|
83
|
+
interface PersistedConfig {
|
|
84
|
+
baseUrl?: string;
|
|
85
|
+
did?: string;
|
|
86
|
+
env?: EbsiEnv;
|
|
87
|
+
es256kPrivateKey?: string;
|
|
88
|
+
es256PrivateKey?: string;
|
|
89
|
+
output?: OutputFormat;
|
|
90
|
+
signAlg?: KeyAlgorithm;
|
|
91
|
+
token?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const APP_NAME = "ebsi-cli";
|
|
95
|
+
const paths = envPaths(APP_NAME, { suffix: "" });
|
|
96
|
+
const CONFIG_FILE = path.join(paths.config, "config.json");
|
|
97
|
+
|
|
98
|
+
export function getConfigFilePath(): string {
|
|
99
|
+
return CONFIG_FILE;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function getPersistedConfigValue(
|
|
103
|
+
key: keyof PersistedConfig,
|
|
104
|
+
): string | undefined {
|
|
105
|
+
const persisted = readPersistedConfig();
|
|
106
|
+
const value = persisted[key];
|
|
107
|
+
return value ?? undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Resolves the active CLI config.
|
|
112
|
+
*
|
|
113
|
+
* The environment and the base URL are resolved separately: `env` always
|
|
114
|
+
* names the EBSI network (used for credentials and `ebsi:` URIs), while
|
|
115
|
+
* `baseUrl` only says which API host to call. Overriding one never affects
|
|
116
|
+
* the other.
|
|
117
|
+
*
|
|
118
|
+
* Resolution order (highest wins):
|
|
119
|
+
* env: --env flag > EBSI_ENV > persisted config > "test"
|
|
120
|
+
* baseUrl: --base-url flag > EBSI_API_BASE_URL > ENV_BASE_URLS[env]
|
|
121
|
+
*
|
|
122
|
+
* Throws when the resolved env has no default URL (`preprod`, `prod`) and
|
|
123
|
+
* none was supplied.
|
|
124
|
+
*/
|
|
125
|
+
export function resolveConfig(
|
|
126
|
+
overrides: {
|
|
127
|
+
baseUrl?: string;
|
|
128
|
+
did?: string;
|
|
129
|
+
env?: string;
|
|
130
|
+
es256Key?: string;
|
|
131
|
+
es256kKey?: string;
|
|
132
|
+
output?: string;
|
|
133
|
+
signAlg?: string;
|
|
134
|
+
token?: string;
|
|
135
|
+
},
|
|
136
|
+
opts: { allowLegacyPrivateKey?: boolean } = {},
|
|
137
|
+
): CliConfig {
|
|
138
|
+
const persisted = readPersistedConfig();
|
|
139
|
+
|
|
140
|
+
if (!opts.allowLegacyPrivateKey) {
|
|
141
|
+
assertNotLegacyPrivateKey(persisted);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Output format
|
|
145
|
+
const output = (overrides.output ??
|
|
146
|
+
process.env["EBSI_OUTPUT"] ??
|
|
147
|
+
persisted.output ??
|
|
148
|
+
"pretty") as OutputFormat;
|
|
149
|
+
|
|
150
|
+
// Bearer token: flag → env var → persisted
|
|
151
|
+
const token =
|
|
152
|
+
overrides.token ?? process.env["EBSI_ACCESS_TOKEN"] ?? persisted.token;
|
|
153
|
+
|
|
154
|
+
// Secp256k1 key: flag → env var → persisted
|
|
155
|
+
const es256kPrivateKey =
|
|
156
|
+
overrides.es256kKey ??
|
|
157
|
+
process.env["EBSI_ES256K_PRIVATE_KEY"] ??
|
|
158
|
+
persisted.es256kPrivateKey;
|
|
159
|
+
|
|
160
|
+
// P-256 key (optional): flag → env var → persisted
|
|
161
|
+
const es256PrivateKey =
|
|
162
|
+
overrides.es256Key ??
|
|
163
|
+
process.env["EBSI_ES256_PRIVATE_KEY"] ??
|
|
164
|
+
persisted.es256PrivateKey;
|
|
165
|
+
|
|
166
|
+
// Which key signs VCs/VPs when both are configured: flag → env var → persisted
|
|
167
|
+
const signAlg = validateSignAlg(
|
|
168
|
+
overrides.signAlg ?? process.env["EBSI_SIGN_ALG"] ?? persisted.signAlg,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// DID: flag → env var → persisted
|
|
172
|
+
const did = overrides.did ?? process.env["EBSI_DID"] ?? persisted.did;
|
|
173
|
+
|
|
174
|
+
// Env selection (flag > env var > persisted > default)
|
|
175
|
+
const envName = overrides.env ?? process.env["EBSI_ENV"] ?? persisted.env;
|
|
176
|
+
const env = isEbsiEnv(envName) ? envName : "test";
|
|
177
|
+
|
|
178
|
+
// Base URL: flag > env var > env default
|
|
179
|
+
const baseUrl =
|
|
180
|
+
overrides.baseUrl ?? process.env["EBSI_API_BASE_URL"] ?? ENV_BASE_URLS[env];
|
|
181
|
+
if (baseUrl === undefined) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`The "${env}" environment has no default API base URL: ` +
|
|
184
|
+
"pass --base-url <url> or set EBSI_API_BASE_URL.",
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
baseUrl,
|
|
190
|
+
env,
|
|
191
|
+
output,
|
|
192
|
+
...(token !== undefined && { token }),
|
|
193
|
+
...(es256kPrivateKey !== undefined && { es256kPrivateKey }),
|
|
194
|
+
...(es256PrivateKey !== undefined && { es256PrivateKey }),
|
|
195
|
+
...(signAlg !== undefined && { signAlg }),
|
|
196
|
+
...(did !== undefined && { did }),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function setPersistedConfigValue(
|
|
201
|
+
key: keyof PersistedConfig,
|
|
202
|
+
value: string,
|
|
203
|
+
): void {
|
|
204
|
+
const persisted: PersistedConfig & { privateKey?: string } =
|
|
205
|
+
readPersistedConfig();
|
|
206
|
+
// Validated by callers
|
|
207
|
+
(persisted as Record<string, string>)[key] = value;
|
|
208
|
+
// Drop the pre-rename `privateKey` field on any write, so a single
|
|
209
|
+
// `ebsi config set <key> <value>` call (now reachable even with a legacy
|
|
210
|
+
// key present — see `allowLegacyPrivateKey`) permanently clears the
|
|
211
|
+
// condition `assertNotLegacyPrivateKey` checks for, instead of the user
|
|
212
|
+
// being stuck re-triggering it on every subsequent command.
|
|
213
|
+
delete persisted.privateKey;
|
|
214
|
+
writePersistedConfig(persisted);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function writePersistedConfig(config: PersistedConfig): void {
|
|
218
|
+
mkdirSync(paths.config, { recursive: true });
|
|
219
|
+
writeFileSync(
|
|
220
|
+
CONFIG_FILE,
|
|
221
|
+
JSON.stringify(config, undefined, 2) + "\n",
|
|
222
|
+
"utf8",
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* `EBSI_PRIVATE_KEY` / the persisted `privateKey` entry were renamed to
|
|
228
|
+
* `EBSI_ES256K_PRIVATE_KEY` / `es256kPrivateKey`. Rather than silently
|
|
229
|
+
* ignoring a key the user still has configured under the old name, fail
|
|
230
|
+
* loudly so a stale env var or config file doesn't look like "no key found".
|
|
231
|
+
*/
|
|
232
|
+
function assertNotLegacyPrivateKey(
|
|
233
|
+
persisted: PersistedConfig & { privateKey?: string },
|
|
234
|
+
): void {
|
|
235
|
+
if (
|
|
236
|
+
process.env["EBSI_PRIVATE_KEY"] === undefined &&
|
|
237
|
+
persisted.privateKey === undefined
|
|
238
|
+
) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
throw new Error(
|
|
242
|
+
"EBSI_PRIVATE_KEY has been renamed to EBSI_ES256K_PRIVATE_KEY. " +
|
|
243
|
+
"The CLI now takes a mandatory secp256k1 key (EBSI_ES256K_PRIVATE_KEY, " +
|
|
244
|
+
"signs Ethereum transactions) and an optional P-256 key " +
|
|
245
|
+
"(EBSI_ES256_PRIVATE_KEY, signs VCs/VPs). " +
|
|
246
|
+
"If a persisted config file still has a `privateKey` entry, run " +
|
|
247
|
+
"`ebsi config set es256kPrivateKey <hex>` to migrate it (allowed even " +
|
|
248
|
+
"while this error is otherwise showing) — it also clears the old entry. " +
|
|
249
|
+
"Otherwise, unset the EBSI_PRIVATE_KEY environment variable.",
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function isEbsiEnv(value: string | undefined): value is EbsiEnv {
|
|
254
|
+
return EBSI_ENVS.includes(value as EbsiEnv);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function readPersistedConfig(): PersistedConfig {
|
|
258
|
+
if (!existsSync(CONFIG_FILE)) return {};
|
|
259
|
+
try {
|
|
260
|
+
return JSON.parse(readFileSync(CONFIG_FILE, "utf8")) as PersistedConfig;
|
|
261
|
+
} catch {
|
|
262
|
+
return {};
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function validateSignAlg(value: string | undefined): KeyAlgorithm | undefined {
|
|
267
|
+
if (value === undefined) return undefined;
|
|
268
|
+
const normalized = value.toUpperCase();
|
|
269
|
+
if (!SIGN_ALGS.includes(normalized as KeyAlgorithm)) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
`Invalid sign algorithm "${value}". Valid values: ${SIGN_ALGS.join(", ")}`,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
return normalized as KeyAlgorithm;
|
|
275
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { EbsiEnvConfiguration } from "@europeum-ebsi/verifiable-credential";
|
|
2
|
+
|
|
3
|
+
import type { CliConfig, EbsiEnv } from "./config.ts";
|
|
4
|
+
|
|
5
|
+
const EBSI_SERVICES = [
|
|
6
|
+
"did-registry",
|
|
7
|
+
"track-and-trace",
|
|
8
|
+
"contracts-registry",
|
|
9
|
+
"issuers-registry",
|
|
10
|
+
"policies-registry",
|
|
11
|
+
"schemas-registry",
|
|
12
|
+
] as const;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Builds an `EbsiEnvConfiguration` from the CLI config.
|
|
16
|
+
*
|
|
17
|
+
* Shared by the VP signing path (`auth/token.ts`) and the onboarding VC
|
|
18
|
+
* builder (`onboarding/vc.ts`), so a credential and the presentation carrying
|
|
19
|
+
* it can never disagree about which EBSI network they belong to.
|
|
20
|
+
*
|
|
21
|
+
* When callers pass `skipValidation: true` the lib skips DID resolution, so
|
|
22
|
+
* the config only needs to be structurally valid.
|
|
23
|
+
*/
|
|
24
|
+
export function buildEbsiEnvConfig(config: CliConfig): EbsiEnvConfiguration {
|
|
25
|
+
const url = new URL(config.baseUrl);
|
|
26
|
+
// The CLI always uses /v1; cast to the ApiVersion literal type expected by EbsiEnvConfiguration
|
|
27
|
+
const version = (url.pathname.split("/").find(Boolean) ??
|
|
28
|
+
"v1") as `v${number}`;
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
hosts: [url.hostname],
|
|
32
|
+
// Production URIs carry no network segment (mirrors the API's own
|
|
33
|
+
// `isOptional: NETWORK === "production"` in `api/src/config/configuration.ts`).
|
|
34
|
+
network: {
|
|
35
|
+
isOptional: config.env === "prod",
|
|
36
|
+
name: resolveNetworkName(config.env),
|
|
37
|
+
},
|
|
38
|
+
scheme: "ebsi",
|
|
39
|
+
services: [...EBSI_SERVICES],
|
|
40
|
+
version,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Maps the configured environment label onto the EBSI network name the API
|
|
46
|
+
* expects in `ebsi:` URIs. Most labels are used verbatim; the exceptions:
|
|
47
|
+
*
|
|
48
|
+
* - "prod" is the network the API calls "production" (`NETWORK=production`).
|
|
49
|
+
* - "local" is an API base URL, not an EBSI network: the local stack runs the
|
|
50
|
+
* API with `NETWORK=test` (see `api/.env.default.local`), so every `ebsi:`
|
|
51
|
+
* URI it accepts — and every one it mints — names "test". Emitting "local"
|
|
52
|
+
* makes the API reject the CLI's own credentials with
|
|
53
|
+
* "Invalid URI: network must be 'test'".
|
|
54
|
+
*/
|
|
55
|
+
function resolveNetworkName(env: EbsiEnv): string {
|
|
56
|
+
switch (env) {
|
|
57
|
+
case "local": {
|
|
58
|
+
return "test";
|
|
59
|
+
}
|
|
60
|
+
case "prod": {
|
|
61
|
+
return "production";
|
|
62
|
+
}
|
|
63
|
+
default: {
|
|
64
|
+
return env;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import type { EbsiIssuer } from "@europeum-ebsi/verifiable-credential";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
bytesToBase64url,
|
|
5
|
+
ES256KSigner,
|
|
6
|
+
ES256Signer,
|
|
7
|
+
hexToBytes,
|
|
8
|
+
} from "@europeum-ebsi/did-jwt";
|
|
9
|
+
import { EbsiWallet } from "@europeum-ebsi/wallet";
|
|
10
|
+
import { ethers } from "ethers";
|
|
11
|
+
import { calculateJwkThumbprint } from "jose";
|
|
12
|
+
|
|
13
|
+
export interface DeriveWalletKeys {
|
|
14
|
+
/**
|
|
15
|
+
* secp256k1 private key hex (0x-prefixed), mandatory.
|
|
16
|
+
*/
|
|
17
|
+
es256kPrivateKey: string;
|
|
18
|
+
/**
|
|
19
|
+
* P-256 private key hex, optional. Enables `--sign-alg ES256`.
|
|
20
|
+
*/
|
|
21
|
+
es256PrivateKey?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Which curve signs VCs/VPs when both keys are given. See
|
|
24
|
+
* `WalletDetails.signingKey`.
|
|
25
|
+
*/
|
|
26
|
+
signAlg?: KeyAlgorithm;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The two curves the CLI's wallet signs over: ES256K (secp256k1, the ledger
|
|
31
|
+
* key) and ES256 (P-256, VCs/VPs only). Not imported from
|
|
32
|
+
* `@europeum-ebsi/wallet` -- its `KeyAlgorithm` isn't re-exported through
|
|
33
|
+
* that package's public entry point.
|
|
34
|
+
*/
|
|
35
|
+
export type KeyAlgorithm = "ES256" | "ES256K";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* One curve's signing identity: everything needed to sign a JWT (VC or VP)
|
|
39
|
+
* as this key, plus the JWK thumbprint the DID document's verification
|
|
40
|
+
* method id is keyed by.
|
|
41
|
+
*/
|
|
42
|
+
export interface KeyMaterial {
|
|
43
|
+
alg: KeyAlgorithm;
|
|
44
|
+
issuer: EbsiIssuer;
|
|
45
|
+
/**
|
|
46
|
+
* DID key id: `${did}#${thumbprint}`
|
|
47
|
+
*/
|
|
48
|
+
kid: string;
|
|
49
|
+
publicKeyJwk: JwkEcKey;
|
|
50
|
+
/**
|
|
51
|
+
* RFC 7638 JWK thumbprint (SHA-256 of canonical JWK, base64url). This is
|
|
52
|
+
* the verification method id the DID document registers for this key (see
|
|
53
|
+
* `generateDidParams`'s `vMethodId` in `contracts/deploy-toolkit`).
|
|
54
|
+
*/
|
|
55
|
+
thumbprint: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface WalletDetails {
|
|
59
|
+
/**
|
|
60
|
+
* Ethereum address derived from the secp256k1 key (the on-chain controller
|
|
61
|
+
* address).
|
|
62
|
+
*/
|
|
63
|
+
address: string;
|
|
64
|
+
/**
|
|
65
|
+
* EBSI DID (did:ebsi:...)
|
|
66
|
+
*/
|
|
67
|
+
did: string;
|
|
68
|
+
/**
|
|
69
|
+
* The P-256 key material, when this wallet was also given one — for
|
|
70
|
+
* signing verifiable credentials/presentations alongside the secp256k1
|
|
71
|
+
* ledger key.
|
|
72
|
+
*/
|
|
73
|
+
es256?: KeyMaterial;
|
|
74
|
+
/**
|
|
75
|
+
* The secp256k1 (ES256K) key material — the ledger key, required by every
|
|
76
|
+
* wallet.
|
|
77
|
+
*/
|
|
78
|
+
es256k: KeyMaterial;
|
|
79
|
+
/**
|
|
80
|
+
* Picks the key that signs VCs/VPs:
|
|
81
|
+
* - `signAlg` names a curve → that curve's key (throws if it isn't configured)
|
|
82
|
+
* - no `signAlg`, no ES256 key → the ES256K key (today's behaviour)
|
|
83
|
+
* - no `signAlg`, both keys configured → throws — the caller must choose
|
|
84
|
+
*
|
|
85
|
+
* A method rather than a field so commands that never sign a VC/VP (`wallet
|
|
86
|
+
* info`, a plain `ledger read`, ...) keep working with both keys configured
|
|
87
|
+
* and no `--sign-alg`.
|
|
88
|
+
*/
|
|
89
|
+
signingKey(): KeyMaterial;
|
|
90
|
+
/**
|
|
91
|
+
* ethers Wallet for Ethereum transaction signing — always the secp256k1
|
|
92
|
+
* key; a P-256 key cannot sign Ethereum transactions.
|
|
93
|
+
*/
|
|
94
|
+
wallet: ethers.Wallet;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* A JWK EC public key, as returned by `EbsiWallet.formatPublicKey(..., "jwk", ...)`.
|
|
99
|
+
* Declared locally for the same reason as `KeyAlgorithm` above -- only the
|
|
100
|
+
* fields this module actually uses.
|
|
101
|
+
*/
|
|
102
|
+
interface JwkEcKey {
|
|
103
|
+
crv: "P-256" | "secp256k1";
|
|
104
|
+
kty: "EC";
|
|
105
|
+
x: string;
|
|
106
|
+
y: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Derives a P-256 (ES256) `EbsiIssuer` for a DID that already exists.
|
|
111
|
+
*
|
|
112
|
+
* Kept as a thin wrapper around `deriveEs256KeyMaterial` for the RTAO issuing
|
|
113
|
+
* flow (`onboarding/vc.ts`), which signs as a *different* identity than the
|
|
114
|
+
* CLI's own wallet and has always taken its P-256 key separately (see
|
|
115
|
+
* `--rtao-es256-key`).
|
|
116
|
+
*/
|
|
117
|
+
export async function deriveEs256Issuer(
|
|
118
|
+
privateKeyHex: string,
|
|
119
|
+
did: string,
|
|
120
|
+
): Promise<EbsiIssuer<"ES256">> {
|
|
121
|
+
const { issuer } = await deriveEs256KeyMaterial(privateKeyHex, did);
|
|
122
|
+
return issuer as EbsiIssuer<"ES256">;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Derives wallet details from a mandatory secp256k1 private key, and an
|
|
127
|
+
* optional P-256 private key alongside it.
|
|
128
|
+
*
|
|
129
|
+
* The secp256k1 key always serves dual purpose (mirrors API's `createUser()`
|
|
130
|
+
* pattern): an `ethers.Wallet` for on-chain transaction signing, and an
|
|
131
|
+
* ES256K JWT signer for VC/VP signing. The P-256 key, when given, is only
|
|
132
|
+
* ever a JWT signer.
|
|
133
|
+
*
|
|
134
|
+
* If `overrideDid` is not provided a fresh random EBSI DID is generated.
|
|
135
|
+
*
|
|
136
|
+
* @throws If either key is malformed (not 32 bytes of hex, or not a valid
|
|
137
|
+
* private key for its curve) — validated via `EbsiWallet`'s constructor.
|
|
138
|
+
*/
|
|
139
|
+
export async function deriveWallet(
|
|
140
|
+
keys: DeriveWalletKeys,
|
|
141
|
+
overrideDid?: string,
|
|
142
|
+
): Promise<WalletDetails> {
|
|
143
|
+
const { es256kPrivateKey, es256PrivateKey, signAlg } = keys;
|
|
144
|
+
|
|
145
|
+
// Validates both keys (32 bytes of hex, valid scalar for their curve)
|
|
146
|
+
// before any derivation happens. EbsiWallet strips a leading "0x" itself.
|
|
147
|
+
new EbsiWallet({
|
|
148
|
+
ES256KPrivateKey: es256kPrivateKey,
|
|
149
|
+
...(es256PrivateKey !== undefined && {
|
|
150
|
+
ES256PrivateKey: es256PrivateKey,
|
|
151
|
+
}),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const { publicKeyJwk, wallet } = deriveEs256kPublicMaterial(es256kPrivateKey);
|
|
155
|
+
const thumbprint = await calculateJwkThumbprint(publicKeyJwk);
|
|
156
|
+
const did = overrideDid ?? EbsiWallet.createDid();
|
|
157
|
+
const kid = `${did}#${thumbprint}`;
|
|
158
|
+
// hexToBytes handles "0x" prefix
|
|
159
|
+
const pkBytes = hexToBytes(wallet.privateKey);
|
|
160
|
+
|
|
161
|
+
const es256k: KeyMaterial = {
|
|
162
|
+
alg: "ES256K",
|
|
163
|
+
issuer: { alg: "ES256K", did, kid, signer: ES256KSigner(pkBytes) },
|
|
164
|
+
kid,
|
|
165
|
+
publicKeyJwk,
|
|
166
|
+
thumbprint,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const es256 =
|
|
170
|
+
es256PrivateKey === undefined
|
|
171
|
+
? undefined
|
|
172
|
+
: await deriveEs256KeyMaterial(es256PrivateKey, did);
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
address: wallet.address,
|
|
176
|
+
did,
|
|
177
|
+
...(es256 !== undefined && { es256 }),
|
|
178
|
+
es256k,
|
|
179
|
+
signingKey: () => pickSigningKey(es256k, es256, signAlg),
|
|
180
|
+
wallet,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Generate a new random private key (0x-prefixed hex string).
|
|
186
|
+
* @param algorithm - Curve to generate on ("ES256K", secp256k1, default; or
|
|
187
|
+
* "ES256", P-256)
|
|
188
|
+
*/
|
|
189
|
+
export function generatePrivateKey(algorithm: KeyAlgorithm = "ES256K"): string {
|
|
190
|
+
if (algorithm === "ES256") {
|
|
191
|
+
const { privateKey } = EbsiWallet.generateKeyPair({ algorithm: "ES256" });
|
|
192
|
+
return addPrefix(privateKey);
|
|
193
|
+
}
|
|
194
|
+
return ethers.Wallet.createRandom().privateKey;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function addPrefix(hex: string): string {
|
|
198
|
+
return hex.startsWith("0x") ? hex : `0x${hex}`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Derives a P-256 (ES256) `KeyMaterial` for a DID that already exists.
|
|
203
|
+
*
|
|
204
|
+
* Unlike the secp256k1 derivation this has no ledger counterpart -- a P-256
|
|
205
|
+
* key can't sign Ethereum transactions.
|
|
206
|
+
*/
|
|
207
|
+
async function deriveEs256KeyMaterial(
|
|
208
|
+
privateKeyHex: string,
|
|
209
|
+
did: string,
|
|
210
|
+
): Promise<KeyMaterial> {
|
|
211
|
+
const publicKeyHex = EbsiWallet.getPublicKeyHex(privateKeyHex, "ES256");
|
|
212
|
+
const publicKeyJwk = EbsiWallet.formatPublicKey(publicKeyHex, "jwk", "ES256");
|
|
213
|
+
const thumbprint = await calculateJwkThumbprint(publicKeyJwk, "sha256");
|
|
214
|
+
const kid = `${did}#${thumbprint}`;
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
alg: "ES256",
|
|
218
|
+
issuer: {
|
|
219
|
+
alg: "ES256",
|
|
220
|
+
did,
|
|
221
|
+
kid,
|
|
222
|
+
// hexToBytes handles "0x" prefix
|
|
223
|
+
signer: ES256Signer(hexToBytes(privateKeyHex)),
|
|
224
|
+
},
|
|
225
|
+
kid,
|
|
226
|
+
publicKeyJwk,
|
|
227
|
+
thumbprint,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Derives the secp256k1 public key JWK, plus the `ethers.Wallet` used for
|
|
233
|
+
* Ethereum transaction signing.
|
|
234
|
+
*/
|
|
235
|
+
function deriveEs256kPublicMaterial(privateKeyHex: string): {
|
|
236
|
+
publicKeyJwk: JwkEcKey;
|
|
237
|
+
wallet: ethers.Wallet;
|
|
238
|
+
} {
|
|
239
|
+
const wallet = new ethers.Wallet(privateKeyHex);
|
|
240
|
+
|
|
241
|
+
// ethers returns "0x" + "04" (uncompressed prefix) + x(32 bytes=64 hex) + y(32 bytes=64 hex)
|
|
242
|
+
const uncompressed = ethers.SigningKey.computePublicKey(
|
|
243
|
+
wallet.signingKey.publicKey,
|
|
244
|
+
false,
|
|
245
|
+
);
|
|
246
|
+
const xHex = uncompressed.slice(4, 68);
|
|
247
|
+
const yHex = uncompressed.slice(68);
|
|
248
|
+
|
|
249
|
+
const publicKeyJwk: JwkEcKey = {
|
|
250
|
+
crv: "secp256k1",
|
|
251
|
+
kty: "EC",
|
|
252
|
+
x: bytesToBase64url(hexToBytes(xHex)),
|
|
253
|
+
y: bytesToBase64url(hexToBytes(yHex)),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
return { publicKeyJwk, wallet };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Implements `WalletDetails.signingKey`'s selection rule — see its doc
|
|
261
|
+
* comment for the full contract.
|
|
262
|
+
*/
|
|
263
|
+
function pickSigningKey(
|
|
264
|
+
es256k: KeyMaterial,
|
|
265
|
+
es256: KeyMaterial | undefined,
|
|
266
|
+
signAlg: KeyAlgorithm | undefined,
|
|
267
|
+
): KeyMaterial {
|
|
268
|
+
if (signAlg === "ES256") {
|
|
269
|
+
if (!es256) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
"No P-256 key configured. Pass --es256-key <hex>, set " +
|
|
272
|
+
"EBSI_ES256_PRIVATE_KEY, or run `ebsi config set es256PrivateKey <hex>`.",
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
return es256;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (signAlg === "ES256K") {
|
|
279
|
+
return es256k;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (!es256) {
|
|
283
|
+
return es256k;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
throw new Error(
|
|
287
|
+
"Two signing keys are configured (ES256K and ES256). Choose which one " +
|
|
288
|
+
"signs VCs/VPs with --sign-alg <ES256|ES256K>, EBSI_SIGN_ALG, or " +
|
|
289
|
+
"`ebsi config set signAlg <alg>`.",
|
|
290
|
+
);
|
|
291
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* EBSI CLI entry point.
|
|
4
|
+
*
|
|
5
|
+
* - With subcommand arguments: runs in non-interactive Commander mode.
|
|
6
|
+
* - Without arguments (or with `--interactive`): launches the Ink TUI.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
const isInteractive = args.length === 0 || args[0] === "--interactive";
|
|
11
|
+
|
|
12
|
+
if (isInteractive) {
|
|
13
|
+
// Launch Ink interactive TUI
|
|
14
|
+
const { render } = await import("ink");
|
|
15
|
+
// eslint-disable-next-line unicorn/no-await-expression-member
|
|
16
|
+
const React = (await import("react")).default;
|
|
17
|
+
const { App } = await import("./tui/App.tsx");
|
|
18
|
+
render(React.createElement(App, {}));
|
|
19
|
+
} else {
|
|
20
|
+
// Non-interactive Commander mode
|
|
21
|
+
const { buildProgram } = await import("./cli.ts");
|
|
22
|
+
const program = buildProgram();
|
|
23
|
+
await program.parseAsync(process.argv);
|
|
24
|
+
}
|