From d279e482e7a00958b9afb8f556dcbc20dcf8e65d Mon Sep 17 00:00:00 2001 From: jakubkoci Date: Wed, 13 Apr 2022 12:31:27 +0200 Subject: [PATCH] feat: find existing connection based on invitation did (#698) * Extract creation of did doc from services * Create peer did from service and store it as invitation did Signed-off-by: Jakub Koci --- .../modules/connections/ConnectionsModule.ts | 4 + .../connections/DidExchangeProtocol.ts | 62 ++-------- .../repository/ConnectionRecord.ts | 4 + .../connections/services/ConnectionService.ts | 14 ++- .../dids/domain/createPeerDidFromServices.ts | 61 +++++++++ ...connection based on invitation did (#698)) | 117 ++++++++++++++++++ .../didPeer1zQmR-did-comm-service.json | 21 ++++ .../methods/peer/__tests__/didPeer.test.ts | 40 ------ .../dids/methods/peer/peerDidNumAlgo2.ts | 28 ++++- .../core/src/modules/oob/OutOfBandModule.ts | 30 ++--- .../modules/oob/messages/OutOfBandMessage.ts | 11 ++ packages/core/tests/helpers.ts | 2 +- 12 files changed, 284 insertions(+), 110 deletions(-) create mode 100644 packages/core/src/modules/dids/domain/createPeerDidFromServices.ts create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts~9399a710 (feat: find existing connection based on invitation did (#698)) create mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR-did-comm-service.json delete mode 100644 packages/core/src/modules/dids/methods/peer/__tests__/didPeer.test.ts diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index 0ef1b01876..a764a2dee0 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -260,6 +260,10 @@ export class ConnectionsModule { return this.connectionService.findByTheirDid(did) } + public async findByInvitationDid(invitationDid: string): Promise { + return this.connectionService.findByInvitationDid(invitationDid) + } + private registerHandlers(dispatcher: Dispatcher) { dispatcher.registerHandler( new ConnectionRequestHandler( diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index d7e24cff62..4e7228b689 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -4,7 +4,6 @@ import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionRecord } from './repository' import type { Routing } from './services/ConnectionService' -import { convertPublicKeyToX25519 } from '@stablelib/ed25519' import { Lifecycle, scoped } from 'tsyringe' import { AgentConfig } from '../../agent/AgentConfig' @@ -14,12 +13,10 @@ import { Attachment, AttachmentData } from '../../decorators/attachment/Attachme import { AriesFrameworkError } from '../../error' import { JsonEncoder } from '../../utils/JsonEncoder' import { JsonTransformer } from '../../utils/JsonTransformer' -import { uuid } from '../../utils/uuid' -import { DidCommService, DidDocument, DidDocumentBuilder, Key } from '../dids' +import { DidCommService, DidDocument, Key } from '../dids' import { DidDocumentRole } from '../dids/domain/DidDocumentRole' +import { createDidDocumentFromServices } from '../dids/domain/createPeerDidFromServices' import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' -import { getEd25519VerificationMethod } from '../dids/domain/key-type/ed25519' -import { getX25519VerificationMethod } from '../dids/domain/key-type/x25519' import { DidKey } from '../dids/methods/key/DidKey' import { DidPeer, PeerDidNumAlgo } from '../dids/methods/peer/DidPeer' import { DidRecord, DidRepository } from '../dids/repository' @@ -72,6 +69,11 @@ export class DidExchangeProtocol { const { alias, goal, goalCode, routing, autoAcceptConnection } = params const { did, mediatorId } = routing + + // TODO: We should store only one did that we'll use to send the request message with success. + // We take just the first one for now. + const [invitationDid] = outOfBandMessage.invitationDids + const connectionRecord = await this.connectionService.createConnection({ protocol: HandshakeProtocol.DidExchange, role: DidExchangeRole.Requester, @@ -83,6 +85,7 @@ export class DidExchangeProtocol { mediatorId, autoAcceptConnection: outOfBandRecord.autoAcceptConnection, outOfBandId: outOfBandRecord.id, + invitationDid, }) DidExchangeStateMachine.assertCreateMessageState(DidExchangeRequestMessage.type, connectionRecord) @@ -374,54 +377,7 @@ export class DidExchangeProtocol { } private async createPeerDidDoc(services: DidCommService[]) { - const didDocumentBuilder = new DidDocumentBuilder('') - - // We need to all reciepient and routing keys from all services but we don't want to duplicated items - const recipientKeys = new Set(services.map((s) => s.recipientKeys).reduce((acc, curr) => acc.concat(curr), [])) - const routingKeys = new Set( - services - .map((s) => s.routingKeys) - .filter((r): r is string[] => r !== undefined) - .reduce((acc, curr) => acc.concat(curr), []) - ) - - for (const recipientKey of recipientKeys) { - const publicKeyBase58 = recipientKey - const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) - const x25519Key = Key.fromPublicKey(convertPublicKeyToX25519(ed25519Key.publicKey), KeyType.X25519) - - const ed25519VerificationMethod = getEd25519VerificationMethod({ - id: uuid(), - key: ed25519Key, - controller: '#id', - }) - const x25519VerificationMethod = getX25519VerificationMethod({ - id: uuid(), - key: x25519Key, - controller: '#id', - }) - - // We should not add duplicated keys for services - didDocumentBuilder.addAuthentication(ed25519VerificationMethod).addKeyAgreement(x25519VerificationMethod) - } - - for (const routingKey of routingKeys) { - const publicKeyBase58 = routingKey - const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) - const verificationMethod = getEd25519VerificationMethod({ - id: uuid(), - key: ed25519Key, - controller: '#id', - }) - didDocumentBuilder.addVerificationMethod(verificationMethod) - } - - services.forEach((service) => { - didDocumentBuilder.addService(service) - }) - - const didDocument = didDocumentBuilder.build() - + const didDocument = createDidDocumentFromServices(services) const peerDid = DidPeer.fromDidDocument(didDocument, PeerDidNumAlgo.GenesisDoc) const didRecord = new DidRecord({ diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index 80292991b8..ed5e20f790 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -24,6 +24,7 @@ export interface ConnectionRecordProps { errorMessage?: string protocol?: HandshakeProtocol outOfBandId?: string + invitationDid?: string } export type CustomConnectionTags = TagsBase @@ -59,6 +60,7 @@ export class ConnectionRecord public errorMessage?: string public protocol?: HandshakeProtocol public outOfBandId?: string + public invitationDid?: string public static readonly type = 'ConnectionRecord' public readonly type = ConnectionRecord.type @@ -70,6 +72,7 @@ export class ConnectionRecord this.id = props.id ?? uuid() this.createdAt = props.createdAt ?? new Date() this.did = props.did + this.invitationDid = props.invitationDid this.theirDid = props.theirDid this.theirLabel = props.theirLabel this.state = props.state @@ -97,6 +100,7 @@ export class ConnectionRecord did: this.did, theirDid: this.theirDid, outOfBandId: this.outOfBandId, + invitationDid: this.invitationDid, } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 2b3a149a4d..489947d99f 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -98,6 +98,11 @@ export class ConnectionService { const { did, mediatorId } = config.routing const didDoc = this.createDidDoc(config.routing) + + // TODO: We should store only one did that we'll use to send the request message with success. + // We take just the first one for now. + const [invitationDid] = outOfBandMessage.invitationDids + const connectionRecord = await this.createConnection({ protocol: HandshakeProtocol.Connections, role: ConnectionRole.Invitee, @@ -108,8 +113,9 @@ export class ConnectionService { mediatorId, autoAcceptConnection: config?.autoAcceptConnection, multiUseInvitation: false, + outOfBandId: outOfBandRecord.id, + invitationDid, }) - connectionRecord.outOfBandId = outOfBandRecord.id const routing = config.routing const { did: peerDid } = await this.createDid({ @@ -631,6 +637,10 @@ export class ConnectionService { return this.connectionRepository.findSingleByQuery({ outOfBandId }) } + public async findByInvitationDid(invitationDid: string) { + return this.connectionRepository.findByQuery({ invitationDid }) + } + public async createConnection(options: { role?: ConnectionRole | DidExchangeRole state?: ConnectionState | DidExchangeState @@ -644,6 +654,7 @@ export class ConnectionService { imageUrl?: string protocol?: HandshakeProtocol outOfBandId?: string + invitationDid?: string }): Promise { const connectionRecord = new ConnectionRecord({ did: options.did, @@ -658,6 +669,7 @@ export class ConnectionService { mediatorId: options.mediatorId, protocol: options.protocol, outOfBandId: options.outOfBandId, + invitationDid: options.invitationDid, }) await this.connectionRepository.save(connectionRecord) return connectionRecord diff --git a/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts b/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts new file mode 100644 index 0000000000..22bf3e16e3 --- /dev/null +++ b/packages/core/src/modules/dids/domain/createPeerDidFromServices.ts @@ -0,0 +1,61 @@ +import type { DidCommService } from '.' + +import { convertPublicKeyToX25519 } from '@stablelib/ed25519' + +import { KeyType } from '../../../crypto' +import { uuid } from '../../../utils/uuid' + +import { DidDocumentBuilder } from './DidDocumentBuilder' +import { Key } from './Key' +import { getEd25519VerificationMethod } from './key-type/ed25519' +import { getX25519VerificationMethod } from './key-type/x25519' + +export function createDidDocumentFromServices(services: DidCommService[]) { + const didDocumentBuilder = new DidDocumentBuilder('') + + // We need to all reciepient and routing keys from all services but we don't want to duplicated items + const recipientKeys = new Set(services.map((s) => s.recipientKeys).reduce((acc, curr) => acc.concat(curr), [])) + const routingKeys = new Set( + services + .map((s) => s.routingKeys) + .filter((r): r is string[] => r !== undefined) + .reduce((acc, curr) => acc.concat(curr), []) + ) + + for (const recipientKey of recipientKeys) { + const publicKeyBase58 = recipientKey + const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) + const x25519Key = Key.fromPublicKey(convertPublicKeyToX25519(ed25519Key.publicKey), KeyType.X25519) + + const ed25519VerificationMethod = getEd25519VerificationMethod({ + id: uuid(), + key: ed25519Key, + controller: '#id', + }) + const x25519VerificationMethod = getX25519VerificationMethod({ + id: uuid(), + key: x25519Key, + controller: '#id', + }) + + // We should not add duplicated keys for services + didDocumentBuilder.addAuthentication(ed25519VerificationMethod).addKeyAgreement(x25519VerificationMethod) + } + + for (const routingKey of routingKeys) { + const publicKeyBase58 = routingKey + const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) + const verificationMethod = getEd25519VerificationMethod({ + id: uuid(), + key: ed25519Key, + controller: '#id', + }) + didDocumentBuilder.addVerificationMethod(verificationMethod) + } + + services.forEach((service) => { + didDocumentBuilder.addService(service) + }) + + return didDocumentBuilder.build() +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts~9399a710 (feat: find existing connection based on invitation did (#698)) b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts~9399a710 (feat: find existing connection based on invitation did (#698)) new file mode 100644 index 0000000000..c4aa250dfb --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/DidPeer.test.ts~9399a710 (feat: find existing connection based on invitation did (#698)) @@ -0,0 +1,117 @@ +import { JsonTransformer } from '../../../../../utils' +import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.json' +import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' +import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' +import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json' +import didKeyX25519 from '../../../__tests__/__fixtures__/didKeyX25519.json' +import { DidDocument, Key } from '../../../domain' +import { DidPeer, PeerDidNumAlgo } from '../DidPeer' +import { serviceToNumAlgo2Did } from '../peerDidNumAlgo2' + +import didPeer1zQmRDidCommServices from './__fixtures__/didPeer1zQmR-did-comm-service.json' +import didPeer1zQmR from './__fixtures__/didPeer1zQmR.json' +import didPeer1zQmZ from './__fixtures__/didPeer1zQmZ.json' +import didPeer2Ez6L from './__fixtures__/didPeer2Ez6L.json' + +describe('DidPeer', () => { + test('transforms a key correctly into a peer did method 0 did document', async () => { + const didDocuments = [didKeyEd25519, didKeyBls12381g1, didKeyX25519, didKeyBls12381g1g2, didKeyBls12381g2] + + for (const didDocument of didDocuments) { + const key = Key.fromFingerprint(didDocument.id.split(':')[2]) + + const didPeer = DidPeer.fromKey(key) + const expectedDidPeerDocument = JSON.parse( + JSON.stringify(didDocument).replace(new RegExp('did:key:', 'g'), 'did:peer:0') + ) + + expect(didPeer.didDocument.toJSON()).toMatchObject(expectedDidPeerDocument) + } + }) + + test('transforms a method 2 did correctly into a did document', () => { + expect(DidPeer.fromDid(didPeer2Ez6L.id).didDocument.toJSON()).toMatchObject(didPeer2Ez6L) + }) + + test('transforms a method 0 did correctly into a did document', () => { + const didDocuments = [didKeyEd25519, didKeyBls12381g1, didKeyX25519, didKeyBls12381g1g2, didKeyBls12381g2] + + for (const didDocument of didDocuments) { + const didPeer = DidPeer.fromDid(didDocument.id.replace('did:key:', 'did:peer:0')) + const expectedDidPeerDocument = JSON.parse( + JSON.stringify(didDocument).replace(new RegExp('did:key:', 'g'), 'did:peer:0') + ) + + expect(didPeer.didDocument.toJSON()).toMatchObject(expectedDidPeerDocument) + } + }) + + test('transforms a did document into a valid method 2 did', () => { + const didPeer2 = DidPeer.fromDidDocument( + JsonTransformer.fromJSON(didPeer2Ez6L, DidDocument), + PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc + ) + + expect(didPeer2.did).toBe(didPeer2Ez6L.id) + }) + + test('transforms a did comm service into a valid method 2 did', () => { + const didDocument = JsonTransformer.fromJSON(didPeer1zQmRDidCommServices, DidDocument) + const peerDid = serviceToNumAlgo2Did(didDocument.didCommServices[0]) + const peerDidInstance = DidPeer.fromDid(peerDid) + + // TODO the following `console.log` statement throws an error "TypeError: Cannot read property 'toLowerCase' + // of undefined" because of this: + // + // `service.id = `${did}#${service.type.toLowerCase()}-${serviceIndex++}`` + + // console.log(peerDidInstance.didDocument) + + expect(peerDid).toBe( + 'did:peer:2.Ez6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH.SeyJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCJ9' + ) + expect(peerDid).toBe(peerDidInstance.did) + }) + + test('transforms a did document into a valid method 1 did', () => { + const didPeer1 = DidPeer.fromDidDocument( + JsonTransformer.fromJSON(didPeer1zQmR, DidDocument), + PeerDidNumAlgo.GenesisDoc + ) + + expect(didPeer1.did).toBe(didPeer1zQmR.id) + }) + + // FIXME: we need some input data from AFGO for this test to succeed (we create a hash of the document, so any inconsistency is fatal) + xtest('transforms a did document from aries-framework-go into a valid method 1 did', () => { + const didPeer1 = DidPeer.fromDidDocument( + JsonTransformer.fromJSON(didPeer1zQmZ, DidDocument), + PeerDidNumAlgo.GenesisDoc + ) + + expect(didPeer1.did).toBe(didPeer1zQmZ.id) + }) + + test('extracts the numAlgo from the peer did', async () => { + // NumAlgo 0 + const key = Key.fromFingerprint(didKeyEd25519.id.split(':')[2]) + const didPeerNumAlgo0 = DidPeer.fromKey(key) + + expect(didPeerNumAlgo0.numAlgo).toBe(PeerDidNumAlgo.InceptionKeyWithoutDoc) + expect(DidPeer.fromDid('did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL').numAlgo).toBe( + PeerDidNumAlgo.InceptionKeyWithoutDoc + ) + + // NumAlgo 1 + const peerDidNumAlgo1 = 'did:peer:1zQmZMygzYqNwU6Uhmewx5Xepf2VLp5S4HLSwwgf2aiKZuwa' + expect(DidPeer.fromDid(peerDidNumAlgo1).numAlgo).toBe(PeerDidNumAlgo.GenesisDoc) + + // NumAlgo 2 + const peerDidNumAlgo2 = + 'did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' + expect(DidPeer.fromDid(peerDidNumAlgo2).numAlgo).toBe(PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) + expect(DidPeer.fromDidDocument(JsonTransformer.fromJSON(didPeer2Ez6L, DidDocument)).numAlgo).toBe( + PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc + ) + }) +}) diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR-did-comm-service.json b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR-did-comm-service.json new file mode 100644 index 0000000000..d45bac0a5f --- /dev/null +++ b/packages/core/src/modules/dids/methods/peer/__tests__/__fixtures__/didPeer1zQmR-did-comm-service.json @@ -0,0 +1,21 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "id": "did:peer:1zQmRYBx1pL86DrsxoJ2ZD3w42d7Ng92ErPgFsCSqg8Q1h4i", + "keyAgreement": [ + { + "id": "#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7" + } + ], + "service": [ + { + "id": "#service-0", + "type": "did-communication", + "serviceEndpoint": "https://example.com/endpoint", + "recipientKeys": ["did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"], + "routingKeys": ["ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7"], + "accept": ["didcomm/v2", "didcomm/aip2;env=rfc587"] + } + ] +} diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/didPeer.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/didPeer.test.ts deleted file mode 100644 index 99716995f5..0000000000 --- a/packages/core/src/modules/dids/methods/peer/__tests__/didPeer.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { isValidPeerDid, getNumAlgoFromPeerDid, PeerDidNumAlgo } from '../didPeer' - -describe('didPeer', () => { - test('isValidPeerDid', () => { - expect(isValidPeerDid('did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL')).toBe(true) - expect(isValidPeerDid('did:peer:1zQmZMygzYqNwU6Uhmewx5Xepf2VLp5S4HLSwwgf2aiKZuwa')).toBe(true) - expect( - isValidPeerDid( - 'did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' - ) - ).toBe(true) - - expect( - isValidPeerDid( - 'did:peer:4.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' - ) - ).toBe(false) - }) - - describe('getNumAlgoFromPeerDid', () => { - test('extracts the numAlgo from the peer did', async () => { - // NumAlgo 0 - expect(getNumAlgoFromPeerDid('did:peer:0z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL')).toBe( - PeerDidNumAlgo.InceptionKeyWithoutDoc - ) - - // NumAlgo 1 - expect(getNumAlgoFromPeerDid('did:peer:1zQmZMygzYqNwU6Uhmewx5Xepf2VLp5S4HLSwwgf2aiKZuwa')).toBe( - PeerDidNumAlgo.GenesisDoc - ) - - // NumAlgo 2 - expect( - getNumAlgoFromPeerDid( - 'did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0' - ) - ).toBe(PeerDidNumAlgo.MultipleInceptionKeyWithoutDoc) - }) - }) -}) diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index c873a9b508..f166eb6ff6 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -1,11 +1,13 @@ import type { JsonObject } from '../../../../types' -import type { DidDocument, VerificationMethod } from '../../domain' +import type { DidCommService, DidDocument, VerificationMethod } from '../../domain' +import { KeyType } from '../../../../crypto' import { JsonEncoder, JsonTransformer } from '../../../../utils' import { DidDocumentService, Key } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' import { getKeyDidMappingByKeyType, getKeyDidMappingByVerificationMethod } from '../../domain/key-type' import { parseDid } from '../../domain/parse' +import { DidKey } from '../key' enum DidPeerPurpose { Assertion = 'A', @@ -145,6 +147,30 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { return did } +export function serviceToNumAlgo2Did(service: DidCommService) { + let did = 'did:peer:2' + let didKey + const [recipientKey] = service.recipientKeys + if (recipientKey.startsWith('did:key')) { + didKey = DidKey.fromDid(recipientKey) + } else { + const publicKeyBase58 = recipientKey + const ed25519Key = Key.fromPublicKeyBase58(publicKeyBase58, KeyType.Ed25519) + didKey = new DidKey(ed25519Key) + } + + const encoded = `.${DidPeerPurpose.Encryption}${didKey.key.fingerprint}` + did += encoded + + const serviceOnlyWithEndpoint = { + serviceEndpoint: service.serviceEndpoint, + } + const encodedServices = JsonEncoder.toBase64URL(abbreviateServiceJson(serviceOnlyWithEndpoint)) + did += `.${DidPeerPurpose.Service}${encodedServices}` + + return did +} + function expandServiceAbbreviations(service: JsonObject) { const expand = (abbreviated: string) => didPeerExpansions[abbreviated] ?? abbreviated diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 7b77364bcd..c4de942895 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -22,6 +22,7 @@ import { ConnectionsModule, } from '../../modules/connections' import { DidCommService, DidsModule } from '../dids' +import { serviceToNumAlgo2Did } from '../dids/methods/peer/peerDidNumAlgo2' import { MediationRecipientService } from '../routing' import { OutOfBandService } from './OutOfBandService' @@ -459,25 +460,26 @@ export class OutOfBandModule { } private async findExistingConnection(services: Array) { - this.logger.debug('Searching for an existing connection for given services.', { services }) - for (const service of services) { - if (typeof service === 'string') { + this.logger.debug('Searching for an existing connection for out-of-band invitation services.', { services }) + for (const didOrService of services) { + if (typeof didOrService === 'string') { // TODO await this.connectionsModule.findByTheirDid() throw new AriesFrameworkError('Dids are not currently supported in out-of-band message services attribute.') } - let existingConnection - for (const recipientKey of service.recipientKeys) { - const theirDidRecords = await this.dids.findMultipleByVerkey(recipientKey) - - for (const theirDidRecord of theirDidRecords) { - // TODO Encode the key and endpoint of the service block in a Peer DID numalgo 2 and using that DID instead of a service block - existingConnection = await this.connectionsModule.findByDid(theirDidRecord.id) - // TODO what if we have more connections? - if (existingConnection) return existingConnection - } + const did = serviceToNumAlgo2Did(didOrService) + const connections = await this.connectionsModule.findByInvitationDid(did) + this.logger.debug(`Retrieved ${connections.length} connections for invitation did ${did}`) + + if (connections.length === 1) { + const [firstConnection] = connections + return firstConnection + } else if (connections.length > 1) { + this.logger.warn(`There is more than one connection created from invitationDid ${did}. Taking the first one.`) + const [firstConnection] = connections + return firstConnection } - return existingConnection + return null } } diff --git a/packages/core/src/modules/oob/messages/OutOfBandMessage.ts b/packages/core/src/modules/oob/messages/OutOfBandMessage.ts index 733f663158..65a0b28bc4 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandMessage.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandMessage.ts @@ -12,6 +12,7 @@ import { AriesFrameworkError } from '../../../error' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { MessageValidator } from '../../../utils/MessageValidator' +import { serviceToNumAlgo2Did } from '../../dids/methods/peer/peerDidNumAlgo2' interface OutOfBandMessageOptions { id?: string @@ -84,6 +85,16 @@ export class OutOfBandMessage extends AgentMessage { return invitation } + public get invitationDids() { + const dids = this.services.map((didOrService) => { + if (typeof didOrService === 'string') { + return didOrService + } + return serviceToNumAlgo2Did(didOrService) + }) + return dids + } + @Equals(OutOfBandMessage.type) public readonly type = OutOfBandMessage.type public static readonly type = `https://didcomm.org/out-of-band/1.1/invitation` diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index e166fe3fcf..0e5fdd991a 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -291,7 +291,7 @@ export function getMockOutOfBand({ id: `#inline-0`, priority: 0, serviceEndpoint: serviceEndpoint ?? 'http://example.com', - recipientKeys: recipientKeys || [], + recipientKeys: recipientKeys || ['ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7'], routingKeys: [], }), ],