UNPKG

@sphereon/ssi-sdk.ebsi-support

Version:

460 lines (421 loc) • 14.2 kB
import { W3CVerifiableCredential } from '@sphereon/ssi-types' import { IAgentContext, IIdentifier, IKeyManager, MinimalImportableKey, TKeyType } from '@veramo/core' import { IService } from '@veramo/core' import { DIDDocument } from 'did-resolver' import { AccessListish, BigNumberish, BytesLike } from 'ethers' import { ApiOpts, EbsiEnvironment } from '../types/IEbsiSupport' import { CredentialRole } from '@sphereon/ssi-types' export type IContext = IAgentContext<IKeyManager> /** * The type of the DID to be created * @readonly * @enum {string} */ export type EbsiDIDType = 'NATURAL_PERSON' | 'LEGAL_ENTITY' /** * The DID method to use * @readonly * @enum {string} */ export type EbsiDIDPrefix = 'did:ebsi:' | 'did:key:' /** * @typedef EbsiDidSpecInfo * @type {object} * @property {EbsiDIDType} type - The type of the DID * @property {EbsiDIDPrefix} method - The method of the DID * @property {number} version - The version of the specs * @property {number} didLength - The length of the DID * @property {number} privateKeyLength The private key length */ export interface EbsiDidSpecInfo { type: EbsiDIDType method: EbsiDIDPrefix version?: number didLength?: number privateKeyLength?: number } export const EBSI_DID_SPEC_INFOS: Record<string, EbsiDidSpecInfo> = { V1: { type: 'LEGAL_ENTITY', method: 'did:ebsi:', version: 0x01, didLength: 16, privateKeyLength: 32, }, KEY: { type: 'NATURAL_PERSON', method: 'did:key:', }, } /** * A minimal importable key with restricted types to choose from and purposes of the public key * @typedef IKeyOpts * @extends MinimalImportableKey * @property {EbsiKeyType} type * @property {EbsiPublicKeyPurpose[]} purposes */ export interface IKeyOpts extends WithRequiredProperty<Partial<MinimalImportableKey>, 'privateKeyHex'> { type?: EbsiKeyType purposes?: EbsiPublicKeyPurpose[] } // Needed to make a single property required type WithRequiredProperty<Type, Key extends keyof Type> = Type & { [Property in Key]-?: Type[Property] } export type RpcMethodArgs = { params: RPCParams[] rpcId: number accessToken: string rpcMethod: EbsiRpcMethod apiOpts?: ApiOpts doNotThrowErrors?: boolean } export type EbsiCreateIdentifierOpts = { methodSpecificId?: string rpcId?: number secp256k1Key?: IKeyOpts secp256r1Key?: IKeyOpts did?: string keys?: IKeyOpts[] // additional importable keys, but only in case execute ledger is true executeLedgerOperation?: boolean // Whether to persist on the EBSI ledger. By default looks at whether access token opts are set or not baseDocument?: string notBefore?: number notAfter?: number accessTokenOpts: EbsiAccessTokenOpts services?: IService[] } /** * @typedef ICreateIdentifierArgs * @type {object} * @property {string} kms - The kms to use * @property {string} alias - The alias of the DID * @property {EbsiDidSpecInfo} type * @property {string} options.methodSpecificId - method specific id for import * @property {IKeyOpts} secp256k1Key - The options to create the key * @property {IKeyOpts} secp256r1Key - The options to create the key */ export interface ICreateIdentifierArgs { kms?: string alias?: string type?: EbsiDidSpecInfo options: EbsiCreateIdentifierOpts } /** * The Ebsi allowed key types - Secp256k1 and Secp256r1 * @readonly * @enum {string} */ export type EbsiKeyType = Extract<TKeyType, 'Secp256k1' | 'Secp256r1'> /** * The purpose of the public keys * @readonly * @enum {string} */ export enum EbsiPublicKeyPurpose { Authentication = 'authentication', AssertionMethod = 'assertionMethod', CapabilityInvocation = 'capabilityInvocation', } /** * @typedef InsertDidDocumentParams * @type {object} * @property {string} from - Ethereum address of the signer * @property {string} did - DID to insert. It must be for a legal entity (DID v1) * @property {string} BASE_CONTEXT_DOC - JSON string containing the @context of the DID document * @property {string} vMethodId - Thumbprint of the public key * @property {string} publicKey - Public key for secp256k1 in uncompressed format prefixed with "0x04" * @property {boolean} isSecp256k1 - It must be true * @property {number} notBefore - Capability invocation is valid from this time * @property {number} notAfter - Expiration of the capability invocation */ export type InsertDidDocumentParams = { from: string did: string baseDocument: string vMethodId: string publicKey: string isSecp256k1: boolean notBefore: number notAfter: number } /** * @typedef UpdateBaseDocumentParams * @type {object} * @property {string} from - Ethereum address of the signer * @property {string} did - Existing DID * @property {string} BASE_CONTEXT_DOC - JSON string containing the @context of the DID document */ export type UpdateBaseDocumentParams = Pick<InsertDidDocumentParams, 'from' | 'did' | 'baseDocument'> /** * @typedef UpdateIdentifierParams * @type {object} * @property {string} did - A DID * @property {Partial<DIDDocument>} document - The partial DID document * @property {{ [p: string]: any }} [options] - Any additional options */ export type UpdateIdentifierParams = { did: string document: Partial<DIDDocument> options?: { [p: string]: any } } export type AddServiceParams = { from: string did: string service: IService } /** * @typedef AddVerificationMethodParams * @type {object} * @property {string} from - Ethereum address of the signer * @property {string} did - Existing DID * @property {string} vMethodId - New verification method id * @property {boolean} isSecp256k1 - Boolean defining if the public key is for secp256k1 curve or not * @property {string} publicKey - Public key as hex string. For an ES256K key, it must be in uncompressed format * prefixed with "0x04". For other algorithms, it must be the JWK transformed to string and then to hex format. */ export type AddVerificationMethodParams = Pick<InsertDidDocumentParams, 'from' | 'did' | 'vMethodId' | 'isSecp256k1' | 'publicKey'> /** * @typedef AddVerificationMethodRelationshipParams * @type {object} * @property {string} from - Ethereum address of the signer * @property {string} did - Existing DID * @property {string} name - Name of the verification relationship * @property {string} vMethodId - Reference to the verification method * @property {number} notBefore - Verification relationship is valid from this time * @property {number} notAfter - Expiration of the verification relationship */ export type AddVerificationMethodRelationshipParams = Pick<InsertDidDocumentParams, 'from' | 'did' | 'vMethodId' | 'notBefore' | 'notAfter'> & { name: string } /** * @typedef UnsignedTransaction * @type {object} * @property {string} from - The sending address. * @property {string} to - The receiving address (if EOA, the transaction will transfer value. If a smart contract * account, the transaction will use contract code). * @property {string} data - Can contain code or a message to the recipient. * @property {string} nonce - A number used to track ordering of transactions and prevent replay attacks * @property {string} chainId - The Ethereum Network ID (ex: 1 - Ethereum Mainnet). * @property {string} gasLimit - The maximum amount of gas units that can be used. * @property {string} gasPrice - Gas price provided by the sender in Wei. * @property {string} value - The amount of ETH to be sent from the sending address (denominated in Wei) */ export type UnsignedTransaction = { to?: string nonce?: number gasLimit?: BigNumberish gasPrice?: BigNumberish data?: BytesLike value?: BigNumberish chainId?: number // Typed-Transaction features type?: number | null // EIP-2930; Type 1 & EIP-1559; Type 2 accessList?: AccessListish // EIP-1559; Type 2 maxPriorityFeePerGas?: BigNumberish maxFeePerGas?: BigNumberish /*from: string to: string data: string nonce: string chainId: string gasLimit: string gasPrice: string value: string*/ } /** * @typedef SendSignedTransactionParams * @type {object} * @property {string} protocol - Example: eth * @property {UnsignedTransaction} unsignedTransaction - The unsigned transaction * @property {string} r - ECDSA signature r * @property {string} s - ECDSA signature s * @property {string} v - ECDSA recovery id * @property {string} signedRawTransaction - The signed raw transaction */ export type SendSignedTransactionParams = { protocol: string unsignedTransaction: UnsignedTransaction r: string s: string v: string signedRawTransaction: string } /** * @typedef RpcOkResponse * @type {object} * @property {string} JSON_RPC_VERSION - Must be exactly "2.0" * @property {number} id - Same identifier established by the client in the call * @property {object} result - Result of the transaction */ export type RpcOkResponse = { jsonrpc: string id: number result: any } export type RpcErrorResponse = { jsonrpc: string id: number error: { code: number message: string } } /** * @typedef ResponseNot200 * @type {object} * @property {URL | string} type - An absolute URI that identifies the problem type. When dereferenced, * it SHOULD provide human-readable documentation for the problem type. * @property {string} title - A short summary of the problem type. * @property {number} status - The HTTP status code generated by the origin server for this occurrence of the problem. * @property {string} detail - A human-readable explanation specific to this occurrence of the problem. * @property {URL | string} instance An absolute URI that identifies the specific occurrence of the problem. * It may or may not yield further information if dereferenced. */ export type ResponseNot200 = { type: URL | string id?: number title: string error?: { code: number message: string } status: number detail: string instance: URL | string } /** * @typedef GetDidDocumentParams * @type {object} * @property {string} did * @property {string} validAt */ export type GetDidDocumentParams = { did: string validAt?: string } /** * @typedef GetDidDocumentsParams * @type {object} * @property {string} offset Originally page[after] Cursor that points to the end of the page of data that has been returned. * @property {number} size Originally page[size] Defines the maximum number of objects that may be returned. * @property {string} controller Filter by controller DID. */ export type GetDidDocumentsParams = { offset?: string size?: number controller?: string } /** * Result of listing dids * @typedef {Item} * @type {object} * @property {string} did - The DID * @property {string} href - The referrer of the DID */ export type Item = { did: string href: string } /** * The links related to pagination * @typedef Links * @type {object} * @property {string} first - The link to the first page * @property {string} prev - The link ot the previous page * @property {string} next - The link to the next page * @property {string} last - The link to the last page */ export type Links = { first: string prev: string next: string last: string } /** * @typedef GetDidDocumentResponse * @type {object} * @property {string} self - Absolute path to the collection (consult) * @property {Item[]} items - List of DIDs and their referrers * @property {number} total - Total number of items across all pages. * @property {pageSize} number - Maximum number of items per page. For the last page, its value should be independent of the number of actually returned items. * @property {Links} links - The links related to pagination */ export type GetDidDocumentsResponse = { self: string items: Item[] total: number pageSize: number links: Links } export type EbsiAccessTokenOpts = { attestationToOnboard?: W3CVerifiableCredential attestationToOnboardCredentialRole: CredentialRole jwksUri?: string redirectUri: string credentialIssuer: string clientId: string environment: EbsiEnvironment } /** * @typedef CreateEbsiDidParams * @type {object} * @property {Omit<IIdentifier, 'provider'>} identifier An identifier without the provider * @property {ManagedKeyInfo} secp256k1ManagedKeyInfo A Secp256k1 managed key * @property {ManagedKeyInfo} secp256r1ManagedKeyInfo A Secp256r1 managed key * @property {number} id A client created id * @property {string} from The wallet eth like address * @property {string} [baseDocument] The base DID document * @property {number} notBefore Date of issuance of the identifier * @property {number} notAfter Date of expiration of the identifier * @property {ApiOpts} [apiOpts] The EBSI API options */ export type CreateEbsiDidParams = { identifier: IIdentifier rpcId?: number notBefore?: number notAfter?: number baseDocument?: string accessTokenOpts: EbsiAccessTokenOpts } export interface CreateEbsiDidOnLedgerResult { identifier: IIdentifier addVerificationMethod: EbsiRPCResponse insertDidDoc: EbsiRPCResponse addAssertionMethodRelationship: EbsiRPCResponse addAuthenticationRelationship: EbsiRPCResponse } /** * @constant JSON_RPC_VERSION */ export const JSON_RPC_VERSION = '2.0' /** * @constant BASE_CONTEXT_DOC */ export const BASE_CONTEXT_DOC = JSON.stringify({ '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'] }) export interface EbsiDidRegistryAPIEndpoints { mutate: string query: string } /** * The EBSI RPC operations * @readonly * @enum {string} */ export enum EbsiRpcMethod { INSERT_DID_DOCUMENT = 'insertDidDocument', UPDATE_DID_DOCUMENT = 'updateBaseDocument', ADD_VERIFICATION_METHOD = 'addVerificationMethod', ADD_VERIFICATION_RELATIONSHIP = 'addVerificationRelationship', ADD_SERVICE = 'addService', SEND_SIGNED_TRANSACTION = 'sendSignedTransaction', } export type RPCParams = | InsertDidDocumentParams | UpdateBaseDocumentParams | AddVerificationMethodParams | AddVerificationMethodRelationshipParams | SendSignedTransactionParams | AddServiceParams export type EbsiRPCResponse = RpcOkResponse | (RpcErrorResponse & { nonce: string })