Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(indy-vdr): resolver and registrar for did:indy #1253

Merged
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
213187f
Created the indy-vdr package
Nov 24, 2022
6998abe
feat: create IndyVdrPool
Nov 28, 2022
a7f05ca
feat: create IndyVdrPool
Nov 28, 2022
f2e2455
removed e-2-e test
Nov 28, 2022
552d3a3
feat: create IndyVdrPool Service
Dec 14, 2022
3cb4259
feat: created the IndyVdrNotFoundError
Dec 15, 2022
8794d1e
refactor: IndyVdr package
Dec 20, 2022
c0f4764
test: add indy-vdr tests
TimoGlastra Dec 20, 2022
d11da2a
refactor: IndyVdr package
Dec 20, 2022
3854c35
refactor: IndyVdr package
Dec 20, 2022
6ec514e
test(indy-vdr): add basic tests
karimStekelenburg Jan 17, 2023
8ff1dc9
fix(indy-vdr): fix import
karimStekelenburg Jan 17, 2023
798406c
test(indy-vdr): start credential definition
Jan 20, 2023
b51d60a
feat: created IndyNotCOnfiguredError
Jan 24, 2023
139eacf
test(IndyVdr): schema && credential definition
Jan 24, 2023
3004279
merge main
Jan 24, 2023
62b7ae3
feat: IndyVdr Package
Jan 24, 2023
e113e1e
import initial sov resolver
genaris Jan 25, 2023
06c58c5
test: add tests for SOV resolver
genaris Jan 26, 2023
dc04650
Merge branch 'main' into feat/indy-vdr-resolver
genaris Jan 26, 2023
1958381
fix: did resolution result
genaris Jan 27, 2023
7265465
feat: initial changes for did:indy
genaris Jan 27, 2023
d415b40
remove unused file
genaris Jan 27, 2023
ec6230a
export types
genaris Jan 27, 2023
db0963e
test: fix pool mock and increase timeout
genaris Jan 27, 2023
f41e01d
test: increase timeout
genaris Jan 27, 2023
474b705
fix: resolve PR feedback
genaris Jan 27, 2023
6cb9ed7
Merge branch 'feat/indy-vdr-resolver' into feat/indy-vdr-didindy-reso…
genaris Jan 27, 2023
9d10bd4
remove injectable decorator
genaris Jan 27, 2023
cdf7513
Merge branch 'feat/indy-vdr-resolver' into feat/indy-vdr-didindy-reso…
genaris Jan 27, 2023
5ed0d92
feat: add initial classes for did:indy resolver and registrar
genaris Jan 28, 2023
e73d017
Merge branch 'main' into feat/indy-vdr-didindy-resolver
genaris Jan 31, 2023
3551c0f
add did:indy resolver tests and fixes
genaris Feb 1, 2023
b22b2b5
add/move tests for utility methods and initial registrar for did:indy
genaris Feb 2, 2023
81d3b5d
fix: return null
genaris Feb 6, 2023
d1814e1
Merge branch 'main' into feat/indy-vdr-didindy-resolver
genaris Feb 10, 2023
9d19ab5
fix imports and remove did from tags
genaris Feb 10, 2023
47e1c77
Merge remote-tracking branch 'upstream/main' into feat/indy-vdr-didin…
genaris Feb 13, 2023
ab4bd20
feat: add tests using askar as backend
genaris Feb 14, 2023
81cff62
Merge remote-tracking branch 'upstream/main' into feat/indy-vdr-didin…
genaris Feb 14, 2023
86b96db
did registrar and resolver tests using askar
genaris Feb 14, 2023
fa7da5c
fix: check did and initial verkey
genaris Feb 14, 2023
22adc13
remove unused import
genaris Feb 14, 2023
ab6ea44
remove lodash dependency, fix registrar return interface, use askar o…
genaris Feb 14, 2023
0c5faae
Merge remote-tracking branch 'upstream/main' into feat/indy-vdr-didin…
genaris Feb 14, 2023
8c30288
fix: remove usage of askar in indy-vdr. Revert changes in AskarWallet
genaris Feb 14, 2023
693ddd3
accept abbreviated verkeys in did:indy resolver
genaris Feb 14, 2023
6262190
revert file
genaris Feb 14, 2023
86c576d
fix: remove unused import
genaris Feb 14, 2023
2cfba55
fix: remove useless didDocumentMetadata
genaris Feb 14, 2023
22abbff
fix: expose did:indy registrar and resolver
genaris Feb 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions packages/askar/src/wallet/AskarWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,13 +355,17 @@ export class AskarWallet implements Wallet {
* @throws {WalletError} When an unsupported keytype is requested
* @throws {WalletError} When the key could not be created
*/
public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise<Key> {
public async createKey({ seed, secretKey, keyType }: AskarWalletCreateKeyOptions): Promise<Key> {
try {
if (keyTypeSupportedByAskar(keyType)) {
const algorithm = keyAlgFromString(keyType)

// Create key from seed
const key = seed ? AskarKey.fromSeed({ seed: Buffer.from(seed), algorithm }) : AskarKey.generate(algorithm)
const key = secretKey
? AskarKey.fromSecretBytes({ secretKey, algorithm })
: seed
? AskarKey.fromSeed({ seed: Buffer.from(seed), algorithm })
: AskarKey.generate(algorithm)

// Store key
await this.session.insertKey({ key, name: encodeToBase58(key.publicBytes) })
Expand Down Expand Up @@ -399,7 +403,6 @@ export class AskarWallet implements Wallet {
if (!TypedArrayEncoder.isTypedArray(data)) {
throw new WalletError(`Currently not supporting signing of multiple messages`)
}

const keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 })

if (!keyEntry) {
Expand Down Expand Up @@ -749,3 +752,7 @@ export class AskarWallet implements Wallet {
}
}
}

export interface AskarWalletCreateKeyOptions extends WalletCreateKeyOptions {
secretKey?: Uint8Array
}
8 changes: 7 additions & 1 deletion packages/indy-vdr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,17 @@
"dependencies": {
"@aries-framework/anoncreds": "0.3.3",
"@aries-framework/core": "0.3.3",
"@hyperledger/indy-vdr-shared": "^0.1.0-dev.4"
"@hyperledger/indy-vdr-shared": "^0.1.0-dev.4",
"lodash": "^4.17.21"
},
"devDependencies": {
"@aries-framework/askar": "0.3.3",
"@hyperledger/aries-askar-nodejs": "^0.1.0-dev.1",
"@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.4",
"@stablelib/ed25519": "^1.0.2",
"@types/lodash": "^4.14.191",
"rimraf": "^4.0.7",
"rxjs": "^7.2.0",
"typescript": "~4.9.4"
}
}
329 changes: 329 additions & 0 deletions packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,329 @@
import type { CommEndpointType, IndyEndpointAttrib } from './didSovUtil'
import type { IndyVdrPool } from '../pool'
import type {
AgentContext,
DidRegistrar,
DidCreateOptions,
DidCreateResult,
DidDeactivateResult,
DidUpdateResult,
DidDocumentService,
} from '@aries-framework/core'

import {
Hasher,
TypedArrayEncoder,
Key,
KeyType,
DidDocumentRole,
DidRecord,
DidRepository,
} from '@aries-framework/core'
import { AttribRequest, NymRequest } from '@hyperledger/indy-vdr-shared'

import { IndyVdrError } from '../error'
import { IndyVdrPoolService } from '../pool/IndyVdrPoolService'

import {
createKeyAgreementKey,
deepObjectDiff,
indyDidDocumentFromDid,
parseIndyDid,
isSelfCertifiedIndyDid,
} from './didIndyUtil'
import { endpointsAttribFromServices } from './didSovUtil'

export class IndyVdrIndyDidRegistrar implements DidRegistrar {
public readonly supportedMethods = ['indy']

public async create(agentContext: AgentContext, options: IndyVdrDidCreateOptions): Promise<DidCreateResult> {
const seed = options.secret?.seed
if (seed && (typeof seed !== 'string' || seed.length !== 32)) {
return {
didDocumentMetadata: {},
didRegistrationMetadata: {},
didState: {
state: 'failed',
reason: 'Invalid seed provided',
},
}
}

const { alias, role, submitterDid, submitterVerkey, services, useEndpointAttrib } = options.options
let verkey = options.options.verkey
let did = options.did
let id

if (seed && did) {
return {
didDocumentMetadata: {},
didRegistrationMetadata: {},
didState: {
state: 'failed',
reason: `Only one of 'seed' and 'did' must be provided`,
},
}
}

try {
const { namespace, id: submitterId } = parseIndyDid(submitterDid)

if (did) {
id = parseIndyDid(did).id
if (!verkey) {
return {
didDocumentMetadata: {},
didRegistrationMetadata: {},
didState: {
state: 'failed',
reason: 'If a did is defined, a matching verkey must be provided',
},
}
}
if (!isSelfCertifiedIndyDid(did, verkey)) {
throw new Error(`Initial verkey ${verkey} does not match did ˇ${did}`)
}
} else {
// Create a new key and calculate did according to the rules for indy did method
const key = await agentContext.wallet.createKey({ seed, keyType: KeyType.Ed25519 })
const buffer = Hasher.hash(key.publicKey, 'sha2-256')

id = TypedArrayEncoder.toBase58(buffer.slice(0, 16))
verkey = key.publicKeyBase58
did = `did:indy:${namespace}:${id}`
}

// Create base did document
const didDocumentBuilder = indyDidDocumentFromDid(did, verkey)
let diddocContent

// Add services if object was passed
if (services) {
services.forEach((item) => didDocumentBuilder.addService(item))

const commTypes: CommEndpointType[] = ['endpoint', 'did-communication', 'DIDComm']
const serviceTypes = new Set(services.map((item) => item.type))

const keyAgreementId = `${did}#key-agreement-1`

// If there is at least a communication service, add the key agreement key
if (commTypes.some((type) => serviceTypes.has(type))) {
didDocumentBuilder
.addVerificationMethod({
controller: did,
id: keyAgreementId,
publicKeyBase58: createKeyAgreementKey(verkey),
type: 'X25519KeyAgreementKey2019',
})
.addKeyAgreement(keyAgreementId)
}

if (!useEndpointAttrib) {
// create diddocContent parameter based on the diff between the base and the resulting DID Document
diddocContent = deepObjectDiff(
didDocumentBuilder.build().toJSON(),
indyDidDocumentFromDid(did, verkey).build().toJSON()
)
}
}

// Build did document
const didDocument = didDocumentBuilder.build()

// If there are services and we are using legacy indy endpoint attrib, make sure they are suitable before registering the DID
if (services && useEndpointAttrib) {
endpointsAttribFromServices(services)
genaris marked this conversation as resolved.
Show resolved Hide resolved
}

const pool = agentContext.dependencyManager.resolve(IndyVdrPoolService).getPoolForNamespace(namespace)
await this.registerPublicDid(
agentContext,
pool,
submitterId,
submitterVerkey,
id,
verkey,
alias,
role,
diddocContent
)

if (services && useEndpointAttrib) {
await this.setEndpointsForDid(agentContext, pool, verkey, id, endpointsAttribFromServices(services))
}

// Save the did so we know we created it and can issue with it
const didRecord = new DidRecord({
id: did,
did,
role: DidDocumentRole.Created,
tags: {
recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint),
},
})

const didRepository = agentContext.dependencyManager.resolve(DidRepository)
await didRepository.save(agentContext, didRecord)

return {
didDocumentMetadata: {
did,
},
didRegistrationMetadata: {
namespace,
genaris marked this conversation as resolved.
Show resolved Hide resolved
},
didState: {
state: 'finished',
did,
didDocument,
secret: {
// FIXME: the uni-registrar creates the seed in the registrar method
// if it doesn't exist so the seed can always be returned. Currently
// we can only return it if the seed was passed in by the user. Once
// we have a secure method for generating seeds we should use the same
// approach
seed: options.secret?.seed,
},
},
}
} catch (error) {
return {
didDocumentMetadata: {},
didRegistrationMetadata: {},
didState: {
state: 'failed',
reason: `unknownError: ${error.message}`,
},
}
}
}

public async update(): Promise<DidUpdateResult> {
return {
didDocumentMetadata: {},
didRegistrationMetadata: {},
didState: {
state: 'failed',
reason: `notImplemented: updating did:indy not implemented yet`,
},
}
}

public async deactivate(): Promise<DidDeactivateResult> {
return {
didDocumentMetadata: {},
didRegistrationMetadata: {},
didState: {
state: 'failed',
reason: `notImplemented: deactivating did:indy not implemented yet`,
},
}
}

private async registerPublicDid(
agentContext: AgentContext,
pool: IndyVdrPool,
submitterDid: string,
submitterVerkey: string,
targetDid: string,
verkey: string,
alias?: string,
role?: string,
diddocContent?: Record<string, unknown>
) {
try {
agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool}'`)

// FIXME: Add diddocContent when supported by indy-vdr
if (diddocContent) {
throw new IndyVdrError('diddocContent is not yet supported')
}

const request = new NymRequest({ submitterDid, dest: targetDid, verkey, alias })

const signingKey = Key.fromPublicKeyBase58(submitterVerkey, KeyType.Ed25519)

const response = await pool.submitWriteRequest(agentContext, request, signingKey)

agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.indyNamespace}'`, {
response,
})

return targetDid
} catch (error) {
agentContext.config.logger.error(
`Error registering public did '${targetDid}' on ledger '${pool.indyNamespace}'`,
{
error,
submitterDid,
targetDid,
verkey,
alias,
role,
pool: pool.indyNamespace,
}
)

throw error
}
}

private async setEndpointsForDid(
agentContext: AgentContext,
pool: IndyVdrPool,
submitterVerkey: string,
did: string,
endpoints: IndyEndpointAttrib
): Promise<void> {
try {
agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, endpoints)

const request = new AttribRequest({
submitterDid: did,
targetDid: did,
raw: JSON.stringify({ endpoint: endpoints }),
})

const signingKey = Key.fromPublicKeyBase58(submitterVerkey, KeyType.Ed25519)

const response = await pool.submitWriteRequest(agentContext, request, signingKey)
agentContext.config.logger.debug(
`Successfully set endpoints for did '${did}' on ledger '${pool.indyNamespace}'`,
{
response,
endpoints,
}
)
} catch (error) {
agentContext.config.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, {
error,
did,
endpoints,
})

throw new IndyVdrError(error)
}
}
}

export interface IndyVdrDidCreateOptions extends DidCreateOptions {
method: 'indy'
did?: string
didDocument?: never // Not yet supported
options: {
alias?: string
role?: string
services?: DidDocumentService[]
useEndpointAttrib?: boolean
submitterDid: string
submitterVerkey: string
verkey?: string
}
secret?: {
seed?: string
}
}

// TODO: Add Update and Deactivate
export type IndyVdrIndyDidUpdateOptions = never
export type IndyVdrIndyDidDeactivateOptions = never
Loading