Skip to content

Commit

Permalink
feat: Add lto-did module
Browse files Browse the repository at this point in the history
  • Loading branch information
nklomp committed Nov 22, 2021
1 parent 42a5b65 commit 236ca01
Show file tree
Hide file tree
Showing 7 changed files with 353 additions and 0 deletions.
3 changes: 3 additions & 0 deletions packages/lto-did-provider/api-extractor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../include/api-extractor-base.json"
}
51 changes: 51 additions & 0 deletions packages/lto-did-provider/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "@sphereon/ssi-sdk-lto-did-provider",
"description": "Veramo LTO Network DID provider",
"version": "0.0.1",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc"
},
"private": true,
"dependencies": {
"@sphereon/lto-did-ts": "^0.1.4",
"lto-api": "^0.5.14",
"@sphereon/did-uni-client": "^0.3.3",
"@sphereon/ssi-sdk-core": "^0.0.1",
"@veramo/core": "^3.1.0",
"@veramo/did-manager": "^3.1.0",
"@veramo/kms-local": "^3.1.0",
"debug": "^4.1.1",
"did-jwt-vc": "^2.1.7",
"events": "^3.3.0",
"micro-base58": "^0.5.1",
"z-schema": "^5.0.2"
},
"devDependencies": {
"@types/debug": "^4.1.7",
"@types/jest": "^27.0.3",
"@types/uuid": "^8.3.3",
"did-resolver": "^3.1.3",
"typescript": "^4.5.2"
},
"files": [
"dist/**/*",
"src/**/*",
"plugin.schema.json",
"README.md",
"LICENSE"
],
"publishConfig": {
"access": "public"
},
"repository": "[email protected]:Sphereon-OpenSource/ssi-sdk.git",
"author": "Sphereon <[email protected]>",
"license": "Apache-2.0",
"keywords": [
"SSI",
"LTO Network",
"DID",
"Veramo"
]
}
92 changes: 92 additions & 0 deletions packages/lto-did-provider/src/__tests__/lto-did-provider.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { LtoDidProvider } from '../lto-did-provider'
import { IDidConnectionMode, IRequiredContext } from '../types/lto-provider-types'
import { Network } from '@sphereon/lto-did-ts'
import { IKeyManager, IKeyManagerCreateArgs, MinimalImportableKey, ManagedKeyInfo, IIdentifier } from '@veramo/core'
import { DIDManager, MemoryDIDStore } from '@veramo/did-manager'
import { KeyManagementSystem } from '@veramo/kms-local'
// import { IIdentifier } from '@veramo/core/src/types/IIdentifier'

const sponsorPrivateKeyBase58 = '5gqCU5NbwU4gc62be39LXDDALKj8opj1KZszx7ULJc2k33kk52prn8D1H2pPPwm6QVKvkuo72YJSoUhzzmAFmDH8'
// const kms = new KeyManagementSystem(new MemoryPrivateKeyStore())
const memoryDIDStore = new MemoryDIDStore()

const ltoDIDProvider = new LtoDidProvider({
defaultKms: 'local',
connectionMode: IDidConnectionMode.NODE,
sponsorPrivateKeyBase58,
network: Network.TESTNET,
})

const didManager = new DIDManager({
providers: { 'did:lto': ltoDIDProvider },
defaultProvider: 'did:lto',
store: memoryDIDStore,
})

const PRIVATE_KEY_HEX = 'ea6aaeebe17557e0fe256bfce08e8224a412ea1e25a5ec8b5d69618a58bad89e89a4661e446b46401325a38d3b20582d1dd277eb448a3181012a671b7ae15837'
const LTO_DID = 'did:lto:3MzYSqyo8GBMsY8u8F2WEuoVXYuq6hnKzyj'
const LTO_KID = `${LTO_DID}#key`

const PUBLIC_KEY_HEX = '89a4661e446b46401325a38d3b20582d1dd277eb448a3181012a671b7ae15837'

describe('@sphereon/lto-did-provider', () => {
const mockContext = {
agent: {
keyManagerCreate(_args: IKeyManagerCreateArgs): Promise<ManagedKeyInfo> {
return Promise.resolve({ publicKeyHex: 'aabbcc', kid: 'testKid' } as ManagedKeyInfo)
},
keyManagerImport(args: MinimalImportableKey): Promise<ManagedKeyInfo> {
return Promise.resolve({
publicKeyHex: args.publicKeyHex || 'aabbcc',
type: args.type,
kms: args.kms,
kid: args.kid || 'testKid',
})
},
} as IKeyManager,
} as IRequiredContext

it('should create identifier', async () => {

const restResponse = {
data: {
didIdentifier: 'TestDID',
},
}

// jest.spyOn(fetch, '').mockResolvedValueOnce(Promise.resolve(restResponse));
const identifier = await ltoDIDProvider.createIdentifier(
{
options: {
privateKeyHex:
PRIVATE_KEY_HEX,
},
},
mockContext,
)


/*expect(provider).not.toHaveProperty('resolveDid')
expect(axios.post).toHaveBeenCalledWith(`${AppConfig.credencoBackendUrl}/did`, {"publicKeyBase58": "0xmyPublicKey"}, {"headers": {"Content-Type": "application/json"}});*/
assertExpectedIdentifier(identifier)

})

it('should properly import identifier using DID manager', async () => {
const importedIdentifier = await didManager.didManagerImport({did: LTO_DID, provider: 'did:lto', controllerKeyId: LTO_KID, keys: [ {kid: LTO_KID, kms: 'local', type: 'Ed25519', privateKeyHex: PRIVATE_KEY_HEX, publicKeyHex: PUBLIC_KEY_HEX}]}, mockContext)
assertExpectedIdentifier(importedIdentifier)
assertExpectedIdentifier(await didManager.didManagerGet({ did: LTO_DID }))
})
})

function assertExpectedIdentifier(identifier: Omit<IIdentifier, 'provider'>) {
expect(identifier).toHaveProperty('did', LTO_DID)
expect(identifier).toHaveProperty('controllerKeyId', LTO_KID)
expect(identifier).toHaveProperty('keys', [{
kid: LTO_KID,
kms: 'local',
type: 'Ed25519',
publicKeyHex: PUBLIC_KEY_HEX,
}])
expect(identifier).toHaveProperty('services', [])
}
2 changes: 2 additions & 0 deletions packages/lto-did-provider/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './types/lto-provider-types'
export { LtoDidProvider } from './lto-did-provider'
172 changes: 172 additions & 0 deletions packages/lto-did-provider/src/lto-did-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { AbstractIdentifierProvider } from '@veramo/did-manager'
import { IAgentContext, IIdentifier, IKey, IKeyManager, IService, TKeyType } from '@veramo/core'
import { Account, LTO } from 'lto-api'
import { DIDService, hexToBase58, base58ToHex } from '@sphereon/lto-did-ts'
import Debug from 'debug'
import { ICreateIdentifierOpts, ILtoDidProviderOpts, IRequiredContext, IDidConnectionMode } from './types/lto-provider-types'
import { UniRegistrar } from '@sphereon/did-uni-client'
const debug = Debug('veramo:did-provider-lto')

export class LtoDidProvider extends AbstractIdentifierProvider {
private readonly defaultKms: string
private readonly opts: ILtoDidProviderOpts
private readonly uniRegistrar?: UniRegistrar

constructor(opts: ILtoDidProviderOpts) {
super()
this.defaultKms = opts.defaultKms
this.opts = { ...opts }
this.uniRegistrar = this.isUniRegistrarMode()
? this.opts.registrarUrl
? new UniRegistrar().setBaseURL(this.opts.registrarUrl)
: new UniRegistrar()
: undefined
}

async createIdentifier(
{
kms,
options,
}: {
kms?: string
options?: ICreateIdentifierOpts
},
context: IRequiredContext
): Promise<Omit<IIdentifier, 'provider'>> {
if (this.isUniRegistrarMode()) {
return await this.createIdentifierUsingUniRegistrar(context, kms, options)
} else {
return await this.createIdentifierUsingNodeRPC(context, kms, options)
}
}

private isUniRegistrarMode() {
return this.opts?.connectionMode === IDidConnectionMode.UNI
}

private async createIdentifierUsingUniRegistrar(
_context: IAgentContext<IKeyManager>,
_kms?: string,
_options?: ICreateIdentifierOpts
): Promise<Omit<IIdentifier, 'provider'>> {
if (!this.uniRegistrar) {
throw new Error(`Uni registrar mode used, but no registrar is present`)
}

throw new Error('Not implemented yet')

/*const registrarOpts = {
/!*didVersion: 'FACTOM_V1_JSON',
managementKeys,
didKeys,
tags,
nonce,
network: this.network,*!/
}
// todo: add services
const request = new DIDRegistrationRequestBuilder().withOptions(registrarOpts).build()
const registrationResult = await this.uniRegistrar.create('lto', request)
const identifier: Omit<IIdentifier, 'provider'> = {
did: registrationResult.didState.identifier as string,
controllerKeyId: keys.reverse()[0].kid,
keys,
services: [],
}
debug('Created', identifier.did)
return identifier*/
}

private async createIdentifierUsingNodeRPC(
context: IAgentContext<IKeyManager>,
kms?: string,
options?: ICreateIdentifierOpts
): Promise<Omit<IIdentifier, 'provider'>> {
const ltoAPI = new LTO(this.opts?.network)
const providedPrivateKeyBase58 = options?.privateKeyHex ? hexToBase58(options?.privateKeyHex) : undefined
const didAccount = providedPrivateKeyBase58 ? ltoAPI.createAccountFromPrivateKey(providedPrivateKeyBase58) : ltoAPI.createAccount()
const didService = this.didService(didAccount)
const didKey = await this.createKey(context, { kms }, didAccount, didAccount.sign.privateKey ? base58ToHex(didAccount.getPrivateSignKey()) : undefined)

return didService
.createDID({
verificationMethods: undefined,
_didAccount: didAccount
// We add the verification methods ourselves below so that we can create/import the keys into Veramo's keystore
})
.then((did) => {
const keys = [didKey]

if (options?.verificationMethods) {
options.verificationMethods.map(async (verificationMethod) => {
const account = ltoAPI.createAccount()
const privateKeyBase58 = account.getPrivateSignKey()
const verificationKey = await this.createKey(context, { kms }, account, base58ToHex(privateKeyBase58))
await didService.addVerificationMethod({
verificationMethodPrivateKeyBase58: privateKeyBase58,
verificationMethod,
createVerificationDID: true,
})
keys.push(verificationKey)
})
}

// The #key constant for the controller key is set in stone on the LTO Network
const identifier: Omit<IIdentifier, 'provider'> = {
did,
controllerKeyId: `${did}#key`,
keys,
services: [],
}
debug('Created', identifier.did)
return identifier
})
}

async deleteIdentifier(args: IIdentifier, context: IAgentContext<IKeyManager>): Promise<boolean> {
return Promise.resolve(false)
}

addKey(args: { identifier: IIdentifier; key: IKey; options?: any }, context: IAgentContext<IKeyManager>): Promise<any> {
return Promise.resolve(undefined)
}

removeKey(args: { identifier: IIdentifier; kid: string; options?: any }, context: IAgentContext<IKeyManager>): Promise<any> {
return Promise.resolve(undefined)
}

addService(args: { identifier: IIdentifier; service: IService; options?: any }, context: IAgentContext<IKeyManager>): Promise<any> {
return Promise.resolve(undefined)
}

removeService(args: { identifier: IIdentifier; id: string; options?: any }, context: IAgentContext<IKeyManager>): Promise<any> {
return Promise.resolve(undefined)
}

private didService(didAccount: Account) {
return new DIDService({ ...this.opts, didPrivateKeyBase58: didAccount.getPrivateSignKey() })
}

private async createKey(
context: IAgentContext<IKeyManager>,
{ kms }: { kms?: string; alias?: string; options?: any },
didAccount?: Account,
privateKeyHex?: string,
keyType?: TKeyType
): Promise<IKey> {
const key = privateKeyHex
? await context.agent.keyManagerImport({
kms: kms || this.defaultKms,
publicKeyHex: didAccount ? base58ToHex(didAccount.getPublicSignKey()) : undefined,
kid: didAccount ? `did:lto:${didAccount.address}#key` : undefined,
type: keyType || 'Ed25519',
privateKeyHex,
})
: await context.agent.keyManagerCreate({
kms: kms || this.defaultKms,
type: keyType || 'Ed25519',
})
return key
}
}
24 changes: 24 additions & 0 deletions packages/lto-did-provider/src/types/lto-provider-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { LtoVerificationMethod, Network } from '@sphereon/lto-did-ts'
import { IAgentContext, IKeyManager } from '@veramo/core'

export interface ILtoDidProviderOpts {
connectionMode: IDidConnectionMode
defaultKms: string
network?: Network | string
rpcUrl?: string
uniResolverUrl?: string
sponsorPrivateKeyBase58?: string
registrarUrl?: string
}

export interface ICreateIdentifierOpts {
verificationMethods?: LtoVerificationMethod[] | undefined
privateKeyHex?: string
}

export type IRequiredContext = IAgentContext<IKeyManager>

export enum IDidConnectionMode {
UNI = 'UNI',
NODE = 'NODE',
}
9 changes: 9 additions & 0 deletions packages/lto-did-provider/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../tsconfig-base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"declarationDir": "dist"
},
"references": [{ "path": "../ssi-sdk-core" }]
}

0 comments on commit 236ca01

Please sign in to comment.