Skip to content

Commit

Permalink
feat: use did keys for oob and did exchange protocols (#700)
Browse files Browse the repository at this point in the history
* Use instance of Key in message context

Signed-off-by: Jakub Koci <[email protected]>
  • Loading branch information
jakubkoci committed Apr 21, 2022
1 parent 3576024 commit 13da519
Show file tree
Hide file tree
Showing 28 changed files with 261 additions and 189 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/agent/Dispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ class Dispatcher {
this.logger.error(`Error handling message with type ${message.type}`, {
message: message.toJSON(),
error,
senderVerkey: messageContext.senderVerkey,
recipientVerkey: messageContext.recipientVerkey,
senderKey: messageContext.senderKey,
recipientKey: messageContext.recipientKey,
connectionId: messageContext.connection?.id,
})

Expand Down
42 changes: 28 additions & 14 deletions packages/core/src/agent/EnvelopeService.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import type { Logger } from '../logger'
import type { DecryptedMessageContext, EncryptedMessage } from '../types'
import type { EncryptedMessage, PlaintextMessage } from '../types'
import type { AgentMessage } from './AgentMessage'

import { inject, scoped, Lifecycle } from 'tsyringe'

import { InjectionSymbols } from '../constants'
import { KeyType } from '../crypto'
import { Key } from '../modules/dids'
import { ForwardMessage } from '../modules/routing/messages'
import { Wallet } from '../wallet/Wallet'

import { AgentConfig } from './AgentConfig'

export interface EnvelopeKeys {
recipientKeys: string[]
routingKeys: string[]
senderKey: string | null
recipientKeys: Key[]
routingKeys: Key[]
senderKey: Key | null
}

@scoped(Lifecycle.ContainerScoped)
class EnvelopeService {
export class EnvelopeService {
private wallet: Wallet
private logger: Logger
private config: AgentConfig
Expand All @@ -29,38 +31,50 @@ class EnvelopeService {
}

public async packMessage(payload: AgentMessage, keys: EnvelopeKeys): Promise<EncryptedMessage> {
const { routingKeys, senderKey } = keys
let recipientKeys = keys.recipientKeys
const { recipientKeys, routingKeys, senderKey } = keys
let recipientKeysBase58 = recipientKeys.map((key) => key.publicKeyBase58)
const routingKeysBase58 = routingKeys.map((key) => key.publicKeyBase58)
const senderKeyBase58 = senderKey && senderKey.publicKeyBase58

// pass whether we want to use legacy did sov prefix
const message = payload.toJSON({ useLegacyDidSovPrefix: this.config.useLegacyDidSovPrefix })

this.logger.debug(`Pack outbound message ${message['@type']}`)

let encryptedMessage = await this.wallet.pack(message, recipientKeys, senderKey ?? undefined)
let encryptedMessage = await this.wallet.pack(message, recipientKeysBase58, senderKeyBase58 ?? undefined)

// If the message has routing keys (mediator) pack for each mediator
for (const routingKey of routingKeys) {
for (const routingKeyBase58 of routingKeysBase58) {
const forwardMessage = new ForwardMessage({
// Forward to first recipient key
to: recipientKeys[0],
to: recipientKeysBase58[0],
message: encryptedMessage,
})
recipientKeys = [routingKey]
recipientKeysBase58 = [routingKeyBase58]
this.logger.debug('Forward message created', forwardMessage)

const forwardJson = forwardMessage.toJSON({ useLegacyDidSovPrefix: this.config.useLegacyDidSovPrefix })

// Forward messages are anon packed
encryptedMessage = await this.wallet.pack(forwardJson, [routingKey], undefined)
encryptedMessage = await this.wallet.pack(forwardJson, [routingKeyBase58], undefined)
}

return encryptedMessage
}

public async unpackMessage(encryptedMessage: EncryptedMessage): Promise<DecryptedMessageContext> {
return this.wallet.unpack(encryptedMessage)
const decryptedMessage = await this.wallet.unpack(encryptedMessage)
const { recipientKey, senderKey, plaintextMessage } = decryptedMessage
return {
recipientKey: recipientKey ? Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519) : undefined,
senderKey: senderKey ? Key.fromPublicKeyBase58(senderKey, KeyType.Ed25519) : undefined,
plaintextMessage,
}
}
}

export { EnvelopeService }
export interface DecryptedMessageContext {
plaintextMessage: PlaintextMessage
senderKey?: Key
recipientKey?: Key
}
20 changes: 14 additions & 6 deletions packages/core/src/agent/MessageReceiver.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import type { Logger } from '../logger'
import type { ConnectionRecord } from '../modules/connections'
import type { InboundTransport } from '../transport'
import type { DecryptedMessageContext, PlaintextMessage, EncryptedMessage } from '../types'
import type { PlaintextMessage, EncryptedMessage } from '../types'
import type { AgentMessage } from './AgentMessage'
import type { DecryptedMessageContext } from './EnvelopeService'
import type { TransportSession } from './TransportService'

import { Lifecycle, scoped } from 'tsyringe'

import { AriesFrameworkError } from '../error'
import { ConnectionsModule } from '../modules/connections'
import { DidKey } from '../modules/dids'
import { OutOfBandService } from '../modules/oob/OutOfBandService'
import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports'
import { isValidJweStructure } from '../utils/JWE'
Expand Down Expand Up @@ -90,12 +92,13 @@ export class MessageReceiver {
const { plaintextMessage, senderKey, recipientKey } = decryptedMessage

this.logger.info(
`Received message with type '${plaintextMessage['@type']}', recipient key ${recipientKey} and sender key ${senderKey}`,
`Received message with type '${plaintextMessage['@type']}', recipient key ${recipientKey?.fingerprint} and sender key ${senderKey?.fingerprint}`,
plaintextMessage
)

const connection = await this.findConnectionByMessageKeys(decryptedMessage)
const outOfBand = (recipientKey && (await this.outOfBandService.findByRecipientKey(recipientKey))) || undefined
const outOfBand =
(recipientKey && (await this.outOfBandService.findByRecipientKey(new DidKey(recipientKey).did))) || undefined

const message = await this.transformAndValidate(plaintextMessage, connection)

Expand Down Expand Up @@ -125,8 +128,8 @@ export class MessageReceiver {
// To prevent unwanted usage of unready connections. Connections can still be retrieved from
// Storage if the specific protocol allows an unready connection to be used.
connection: connection?.isReady ? connection : undefined,
senderVerkey: senderKey,
recipientVerkey: recipientKey,
senderKey: senderKey,
recipientKey: recipientKey,
})
await this.dispatcher.dispatch(messageContext)
}
Expand Down Expand Up @@ -184,8 +187,13 @@ export class MessageReceiver {
// We only fetch connections that are sent in AuthCrypt mode
if (!recipientKey || !senderKey) return null

const senderDidKey = new DidKey(senderKey)
const recipientDidKey = new DidKey(recipientKey)
// Try to find the did records that holds the sender and recipient keys
return this.connectionsModule.findByKeys({ senderKey, recipientKey })
return this.connectionsModule.findByKeys({
senderKey: senderDidKey.did,
recipientKey: recipientDidKey.did,
})
}

/**
Expand Down
15 changes: 9 additions & 6 deletions packages/core/src/agent/MessageSender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator'
import { AriesFrameworkError } from '../error'
import { Logger } from '../logger'
import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type'
import { stringToInstanceOfKey } from '../modules/dids/helpers'
import { DidResolverService } from '../modules/dids/services/DidResolverService'
import { MessageRepository } from '../storage/MessageRepository'
import { MessageValidator } from '../utils/MessageValidator'
Expand Down Expand Up @@ -229,10 +230,11 @@ export class MessageSender {
if (queueService) {
this.logger.debug(`Queue message for connection ${connection.id} (${connection.theirLabel})`)

// TODO We should add a method to return instances of Key rather than keys as a string
const keys = {
recipientKeys: queueService.recipientKeys,
routingKeys: queueService.routingKeys || [],
senderKey: firstOurAuthenticationKey,
recipientKeys: queueService.recipientKeys.map(stringToInstanceOfKey),
routingKeys: queueService.routingKeys?.map(stringToInstanceOfKey) || [],
senderKey: stringToInstanceOfKey(firstOurAuthenticationKey),
}

const encryptedMessage = await this.envelopeService.packMessage(payload, keys)
Expand Down Expand Up @@ -268,10 +270,11 @@ export class MessageSender {

this.logger.debug(`Sending outbound message to service:`, { messageId: message.id, service })

// TODO We should add a method to return instances of Key rather than keys as a string
const keys = {
recipientKeys: service.recipientKeys,
routingKeys: service.routingKeys || [],
senderKey,
recipientKeys: service.recipientKeys.map(stringToInstanceOfKey),
routingKeys: service.routingKeys?.map(stringToInstanceOfKey) || [],
senderKey: stringToInstanceOfKey(senderKey),
}

// Set return routing for message if requested
Expand Down
17 changes: 10 additions & 7 deletions packages/core/src/agent/__tests__/MessageSender.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import type { OutboundMessage, EncryptedMessage } from '../../types'
import { TestMessage } from '../../../tests/TestMessage'
import { getAgentConfig, getMockConnection, mockFunction } from '../../../tests/helpers'
import testLogger from '../../../tests/logger'
import { KeyType } from '../../crypto'
import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator'
import { DidDocument, VerificationMethod } from '../../modules/dids'
import { Key, DidDocument, VerificationMethod } from '../../modules/dids'
import { DidCommService } from '../../modules/dids/domain/service/DidCommService'
import { DidResolverService } from '../../modules/dids/services/DidResolverService'
import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository'
Expand Down Expand Up @@ -63,11 +64,13 @@ describe('MessageSender', () => {
const inboundMessage = new TestMessage()
inboundMessage.setReturnRouting(ReturnRouteTypes.all)

const recipientKey = Key.fromPublicKeyBase58('8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K', KeyType.Ed25519)
const senderKey = Key.fromPublicKeyBase58('79CXkde3j8TNuMXxPdV7nLUrT2g7JAEjH5TreyVY7GEZ', KeyType.Ed25519)
const session = new DummyTransportSession('session-123')
session.keys = {
recipientKeys: ['verkey'],
recipientKeys: [recipientKey],
routingKeys: [],
senderKey: 'senderKey',
senderKey: senderKey,
}
session.inboundMessage = inboundMessage
session.send = jest.fn()
Expand All @@ -83,12 +86,12 @@ describe('MessageSender', () => {
const firstDidCommService = new DidCommService({
id: `<did>;indy`,
serviceEndpoint: 'https://www.first-endpoint.com',
recipientKeys: ['verkey'],
recipientKeys: [recipientKey.publicKeyBase58],
})
const secondDidCommService = new DidCommService({
id: `<did>;indy`,
serviceEndpoint: 'https://www.second-endpoint.com',
recipientKeys: ['verkey'],
recipientKeys: [recipientKey.publicKeyBase58],
})

let messageSender: MessageSender
Expand Down Expand Up @@ -362,9 +365,9 @@ describe('MessageSender', () => {
const endpoint = 'https://example.com'

const keys = {
recipientKeys: ['service.recipientKeys'],
recipientKeys: [recipientKey],
routingKeys: [],
senderKey: 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d',
senderKey: senderKey,
}
const result = await messageSender.packMessage({ message, keys, endpoint })

Expand Down
13 changes: 7 additions & 6 deletions packages/core/src/agent/models/InboundMessageContext.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import type { ConnectionRecord } from '../../modules/connections'
import type { Key } from '../../modules/dids'
import type { AgentMessage } from '../AgentMessage'

import { AriesFrameworkError } from '../../error'

export interface MessageContextParams {
connection?: ConnectionRecord
senderVerkey?: string
recipientVerkey?: string
senderKey?: Key
recipientKey?: Key
}

export class InboundMessageContext<T extends AgentMessage = AgentMessage> {
public message: T
public connection?: ConnectionRecord
public senderVerkey?: string
public recipientVerkey?: string
public senderKey?: Key
public recipientKey?: Key

public constructor(message: T, context: MessageContextParams = {}) {
this.message = message
this.recipientVerkey = context.recipientVerkey
this.senderVerkey = context.senderVerkey
this.recipientKey = context.recipientKey
this.senderKey = context.senderKey
this.connection = context.connection
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,7 @@ describe('BasicMessageService', () => {
content: 'message',
})

const messageContext = new InboundMessageContext(basicMessage, {
senderVerkey: 'senderKey',
recipientVerkey: 'recipientKey',
})
const messageContext = new InboundMessageContext(basicMessage)

await basicMessageService.save(messageContext, mockConnectionRecord)

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/modules/connections/ConnectionsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,9 @@ export class ConnectionsModule {
}

public async findByKeys({ senderKey, recipientKey }: { senderKey: string; recipientKey: string }) {
const theirDidRecord = await this.didRepository.findByVerkey(senderKey)
const theirDidRecord = await this.didRepository.findByRecipientKey(senderKey)
if (theirDidRecord) {
const ourDidRecord = await this.didRepository.findByVerkey(recipientKey)
const ourDidRecord = await this.didRepository.findByRecipientKey(recipientKey)
if (ourDidRecord) {
const connectionRecord = await this.connectionService.findSingleByQuery({
did: ourDidRecord.id,
Expand Down
18 changes: 13 additions & 5 deletions packages/core/src/modules/connections/DidExchangeProtocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ 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 { didKeyToVerkey, verkeyToDidKey } from '../dids/helpers'
import { DidKey } from '../dids/methods/key/DidKey'
import { DidPeer, PeerDidNumAlgo } from '../dids/methods/peer/DidPeer'
import { DidRecord, DidRepository } from '../dids/repository'
Expand Down Expand Up @@ -100,7 +101,7 @@ export class DidExchangeProtocol {

// Create sign attachment containing didDoc
if (peerDid.numAlgo === PeerDidNumAlgo.GenesisDoc) {
const didDocAttach = await this.createSignedAttachment(didDocument, [verkey])
const didDocAttach = await this.createSignedAttachment(didDocument, [verkey].map(didKeyToVerkey))
message.didDoc = didDocAttach
}

Expand Down Expand Up @@ -175,7 +176,7 @@ export class DidExchangeProtocol {
tags: {
// We need to save the recipientKeys, so we can find the associated did
// of a key when we receive a message from another connection.
recipientKeys: didDocument.recipientKeys,
recipientKeys: didDocument.recipientKeys.map(verkeyToDidKey),
},
})

Expand Down Expand Up @@ -236,7 +237,14 @@ export class DidExchangeProtocol {
if (peerDid.numAlgo === PeerDidNumAlgo.GenesisDoc) {
const didDocAttach = await this.createSignedAttachment(
didDocument,
Array.from(new Set(services.map((s) => s.recipientKeys).reduce((acc, curr) => acc.concat(curr), [])))
Array.from(
new Set(
services
.map((s) => s.recipientKeys)
.reduce((acc, curr) => acc.concat(curr), [])
.map(didKeyToVerkey)
)
)
)
message.didDoc = didDocAttach
}
Expand Down Expand Up @@ -293,7 +301,7 @@ export class DidExchangeProtocol {
tags: {
// We need to save the recipientKeys, so we can find the associated did
// of a key when we receive a message from another connection.
recipientKeys: didDocument.recipientKeys,
recipientKeys: didDocument.recipientKeys.map(verkeyToDidKey),
},
})

Expand Down Expand Up @@ -389,7 +397,7 @@ export class DidExchangeProtocol {
tags: {
// We need to save the recipientKeys, so we can find the associated did
// of a key when we receive a message from another connection.
recipientKeys: peerDid.didDocument.recipientKeys,
recipientKeys: peerDid.didDocument.recipientKeys.map(verkeyToDidKey),
},
})

Expand Down
Loading

0 comments on commit 13da519

Please sign in to comment.